1

Jison parsers return the calculated result:

calculator.parse("2^3"); // returns 8
calculator.parse("x^2"); // gives a parse err

I would like that it return the symbolic expression:

calculator.parse("x^2");
// should return
// "Math.pow(x,2)"

And

calculator.parse("x^(x+1)");
// should return
// "Math.pow(x,x+1)"

And

calculator.parse("cos(x)");
// should return
// "Math.cos(x)"
marco alves
  • 1,707
  • 2
  • 18
  • 28

3 Answers3

2

If what you need is simple enough, you might get by by modifying the calculator. For instance:

  1. Add an IDENTIFIER token after NUMBER in the list of tokens:

    [a-z]                 return 'IDENTIFIER'
    

    This allows a single lower case letter to serve as identifie.

  2. Modify the e '^' e rule to return a string rather than the computed value:

    | e '^' e
        {$$ = "Math.pow(" + $1 + "," + $3 + ");"}
    
  3. Add a new rule to the list of rules for e:

    | IDENTIFIER
    

    (No explicit action needed.)

With these changes parsing, x^2 results in "Math.pow(x,2);"

To support the operators, the other rules would have to be modified like the one for e '^' e to return strings rather than the result of the math.

This is extremely primitive and won't optimize things that could be optimized. For instance, 1^2 will be output as "Math.pow(1, 2)" when it could be optimized to 1.

Louis
  • 146,715
  • 28
  • 274
  • 320
  • Thanks. Will try that out and report here. – marco alves Oct 09 '14 at 12:26
  • It worked! Now i'm thinking that it would be good to handle the case x^(y+3). Any thing you recommend reading to tackle this case? – marco alves Oct 09 '14 at 15:05
  • 1
    Yes, I've mentioned in my answer that "To support the operators, the other rules would have to be modified like the one for `e '^' e` to return strings rather than the result of the math." It's the same kind of transformation as the one I made for `e '^' e`. – Louis Oct 09 '14 at 15:07
  • Ok. So if i want x^(y+3), then I have to include `e '^' (e)` as rule, or something similar. I'll try that. – marco alves Oct 09 '14 at 16:21
  • To make `x^(y+3)` work, all I had to do was make the `+` rule return a string like `{$$ = $1 + " + " + $3;}` – marco alves Oct 09 '14 at 16:35
1

(Based on @Louis answer and comments)

Here is the code for a basic symbolic calculator using jison:

/* description: Parses and executes mathematical expressions. */

/* lexical grammar */
%lex
%%

\s+                   /* skip whitespace */ 
(acos|asin|atan|atan2|cos|log|sin|sqrt|tan) return 'FUNCTION'
[0-9]+("."[0-9]+)?\b  return 'NUMBER'
[a-z]                 return 'IDENTIFIER'
"|"                   return '|'
"*"                   return '*'
"/"                   return '/'
"-"                   return '-'
"+"                   return '+'
"^"                   return '^'
"!"                   return '!'
"%"                   return '%'
"("                   return '('
")"                   return ')'
"PI"                  return 'PI'
"E"                   return 'E'
<<EOF>>               return 'EOF'
.                     return 'INVALID'

/lex

/* operator associations and precedence */

%left '+' '-'
%left '*' '/'
%left '^'
%right '!'
%right '%'
%left UMINUS

%start expressions

%% /* language grammar */

expressions
    : e EOF
        { typeof console !== 'undefined' ? console.log($1) : print($1);
          return $1; }
    ;

e
    : e '+' e
        {$$ = $1 + " + " + $3;}
    | e '-' e
        {$$ = $1 + "-" + $3;}
    | e '*' e
        {$$ = $1 + "*" + $3;}
    | e '/' e
        {$$ = $1 + "/" + $3;}
    | e '^' e
        {$$ = "Math.pow(" + $1 + ", " + $3 + ");"}
    | e '!'
        {{
          $$ = (function fact (n) { return n==0 ? 1 : fact(n-1) * n })($1);
        }}
    | e '%'
        {$$ = $1/100;}
    | '-' e %prec UMINUS
        {$$ = -$2;}
    | '(' e ')'
        {$$ = $2;}
    | FUNCTION '(' e ')'
        {$$ = "Math." + $1 + "(" + $3 + ")";}
    | '|' e '|'
        {$$ = "Math.abs(" + $2 + ")";}
    | NUMBER
        {$$ = Number(yytext);}
    | E
        {$$ = Math.E;}
    | PI
        {$$ = Math.PI;}
    | IDENTIFIER
    | FUNCTION
    ;
marco alves
  • 1,707
  • 2
  • 18
  • 28
0

No.

Not an easy way. Depending on what you call 'easy'. You need to define tokens. And then symbolic manipulation of those tokens. And stuff.

I don't think that calculator would be a good starting point, and its such a trivial thing anyway that throwing it away and writing your JS symbolic parser grammar from scratch wouldn't be too much more of a problem.

Just to save some google time for everyone, this is what we are talking about: http://zaach.github.io/jison/demos/calc/

Spacedman
  • 92,590
  • 12
  • 140
  • 224