import { IData , IOperation , IOperations , IVars , IOptions } from './interfaces' ; const ops = { '+' : { op : '+' , precedence : 10 , assoc : 'L' , exec : function ( l , r ) { return l + r ; } } , '-' : { op : '-' , precedence : 10 , assoc : 'L' , exec : function ( l , r ) { return l - r ; } } , '*' : { op : '*' , precedence : 20 , assoc : 'L' , exec : function ( l , r ) { return l * r ; } } , '/' : { op : '/' , precedence : 20 , assoc : 'L' , exec : function ( l , r ) { return l / r ; } } , '**' : { op : '**' , precedence : 30 , assoc : 'R' , exec : function ( l , r ) { return Math . pow ( l , r ) ; } } } ; export const vars = { e : Math . exp ( 1 ) , pi : Math . atan2 ( 1 , 1 ) * 4 } ; function parseVal ( r ) { const startOffset = r . offset ; let value ; let match ; value = 0 ; while ( '0123456789' . indexOf ( r . string . substr ( r . offset , 1 ) ) >= 0 && r . offset < r . string . length ) { r . offset ++ ; } if ( r . string . substr ( r . offset , 1 ) == '.' ) { r . offset ++ ; while ( '0123456789' . indexOf ( r . string . substr ( r . offset , 1 ) ) >= 0 && r . offset < r . string . length ) { r . offset ++ ; } } if ( r . offset > startOffset ) { return parseFloat ( r . string . substr ( startOffset , r . offset - startOffset ) ) ; } else if ( r . string . substr ( r . offset , 1 ) == '+' ) { r . offset ++ ; return parseVal ( r ) ; } else if ( r . string . substr ( r . offset , 1 ) == '-' ) { r . offset ++ ; return negate ( parseVal ( r ) ) ; } else if ( r . string . substr ( r . offset , 1 ) == '(' ) { r . offset ++ ; value = parseExpr ( r ) ; if ( r . string . substr ( r . offset , 1 ) == ')' ) { r . offset ++ ; return value ; } r . error = "Parsing error: ')' expected" ; throw new Error ( 'parseError' ) ; } else if ( ( match = /^[a-z_][a-z0-9_]*/i . exec ( r . string . substr ( r . offset ) ) ) != null ) { const name = match [ 0 ] ; r . offset += name . length ; if ( name in vars ) { return vars [ name ] ; } r . error = "Semantic error: unknown variable '" + name + "'" ; throw new Error ( 'unknownVar' ) ; } else { if ( r . string . length == r . offset ) { r . error = 'Parsing error at end of string: value expected' ; throw new Error ( 'valueMissing' ) ; } else { r . error = "Parsing error: unrecognized value" ; throw new Error ( 'valueNotParsed' ) ; } } } function negate ( value ) { return - value ; } function parseOp ( r ) { if ( r . string . substr ( r . offset , 2 ) == '**' ) { r . offset += 2 ; return ops [ '**' ] ; } if ( '+-*/' . indexOf ( r . string . substr ( r . offset , 1 ) ) >= 0 ) { return ops [ r . string . substr ( r . offset ++ , 1 ) ] ; } return null ; } function parseExpr ( r ) { const stack = [ { precedence : 0 , assoc : 'L' } ] ; let value = parseVal ( r ) ; for ( ; ; ) { const op = parseOp ( r ) || { precedence : 0 , assoc : 'L' } ; while ( op . precedence < stack [ stack . length - 1 ] . precedence || ( op . precedence == stack [ stack . length - 1 ] . precedence && op . assoc == 'L' ) ) { const tos = stack . pop ( ) ; if ( ! tos . exec ) { return value ; } value = tos . exec ( tos . value , value ) ; } stack . push ( { op : op . op , precedence : op . precedence , assoc : op . assoc , exec : op . exec , value : value } ) ; value = parseVal ( r ) ; } } export function parse ( str , opts ) { const r = { string : str , offset : 0 } ; try { const value = parseExpr ( r ) ; if ( r . offset < r . string . length ) { r . error = 'Syntax error: junk found at offset ' + r . offset ; throw new Error ( 'trailingJunk' ) ; } return value ; } catch ( e ) { if ( opts . showParseError ) { alert ( `${ r . error } (${ e }):\n${ r . string . substr ( 0 , r . offset ) }<*>${ r . string . substr ( r . offset ) }` ) ; } return null ; } }