File size: 1,820 Bytes
09aeaeb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | /** Bounded arithmetic grammar, never JavaScript evaluation. IEEE-754 semantics. */
export function arithmeticExpression(input) {
if(typeof input!=='string'||input.length>1000)throw Error('EXPRESSION_BUDGET');
const tokens=input.match(/Infinity|NaN|(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?|\*\*|[()+*/%-]|\S/g)||[];
if(tokens.length>256)throw Error('TOKEN_BUDGET');
let at=0,depth=0;
const take=()=>tokens[at++];
function atom(){
if(++depth>32)throw Error('DEPTH_BUDGET');
let value;
if(tokens[at]==='('){take();value=sum();if(take()!==')')throw Error('MISSING_PAREN');}
else {
const token=take();
if(!token||! /^(?:Infinity|NaN|(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?)$/.test(token))throw Error('EXPECTED_NUMBER');
value=Number(token);
}
depth--;return value;
}
function power(){let v=atom();if(tokens[at]==='**'){take();v=v**unary();}return v;}
function unary(){
if(tokens[at]==='+'||tokens[at]==='-'){
const sign=take();
if(tokens[at]===sign)throw Error('INCREMENT_NOT_ARITHMETIC');
// JavaScript forbids a unary expression as the unparenthesized left of **.
if(tokens[at]!=='(' && tokens[at+1]==='**')throw Error('UNARY_EXPONENT_BASE');
if(++depth>32)throw Error('DEPTH_BUDGET');
const value=unary();depth--;
return sign==='-'?-value:value;
}
return power();
}
function product(){let v=unary();while(['*','/','%'].includes(tokens[at])){const op=take(),r=unary();v=op==='*'?v*r:op==='/'?v/r:v%r;}return v;}
function sum(){let v=product();while(['+','-'].includes(tokens[at])){const op=take();if(tokens[at]===op)throw Error('INCREMENT_NOT_ARITHMETIC');const r=product();v=op==='+'?v+r:v-r;}return v;}
const value=sum();if(at!==tokens.length)throw Error('UNSUPPORTED_EXPRESSION');return value;
}
|