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;
}