5

I have here an only slightly modified version of the JISON calculator example:

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

/* lexical grammar */
%lex
%%

\s+                   /* skip whitespace */
[0-9]+("."[0-9]+)?\b  return 'NUMBER'
"*"                   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 '!'
        {{
          $$ = fact($1);
        }}
    | e '%'
        {$$ = $1/100;}
    | '-' e %prec UMINUS
        {$$ = -$2;}
    | '(' e ')'
        {$$ = $2;}
    | NUMBER
        {$$ = Number(yytext);}
    | E
        {$$ = Math.E;}
    | PI
        {$$ = Math.PI;}
    ;

%%
/*why doesn't this work at runtime?
I see other examples defining declarations this way but I must be doing something wrong
I couldn't find a syntactically valid way of putting this declaration anywhere but here,
which is probably the issue*/
function fact(n) {
  var tot=1;
  for(var i=2;i<=n;++i) {
    tot*=i;
  }
  return tot;
}

Note the slight differences in the ! operator's definition. I'm trying to externally define the fact function rather than doing it inline.

As of now, it tells me at runtime fact is not defined. How can I fix this? Also, why does the calculator example use two braces around the factorial definition, {{ /*like so*/ }}?

2 Answers2

3

To call a function defined under your productions (such as fact), you can use the mod-brace notation %{ and %} for multiline semantic actions:

e
    : e '+' e

    ...

    | e '!'
        %{
            // the %{ tells jison this is a multi-line js eval statement
            $$ = fact($1);
        %}
    ;

As a final solution, try this:

/* lexical grammar */
%lex
%%

\s+                   /* skip whitespace */
[0-9]+("."[0-9]+)?\b  return 'NUMBER'
"*"                   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 '!'
        %{
          $$ = fact($1);
        %}
    | e '%'
        {$$ = $1/100;}
    | '-' e %prec UMINUS
        {$$ = -$2;}
    | '(' e ')'
        {$$ = $2;}
    | NUMBER
        {$$ = Number(yytext);}
    | E
        {$$ = Math.E;}
    | PI
        {$$ = Math.PI;}
    ;

%%

function fact(n) {
  var tot=1;
  for(var i=2;i<=n;++i) {
    tot*=i;
  }
  return tot;
}
Blake Regalia
  • 2,677
  • 2
  • 20
  • 29
  • This solution doesn't actually work. If you copy and paste that full grammar into Jison, and try and parse `2!` it gives you `fact is not defined`. I'm struggling with this same issue because there appears to be zero documentation regarding this feature. – Owen Allen Oct 21 '14 at 07:19
  • @Nucleon got the same problem and figured out that it is not working in the online tool of jison but it is working local in an node.js environment. Maybe this info helps you! Unfortunately as you mentioned no documentation found... – chaosbohne Nov 28 '14 at 09:33
  • @Nucleon and @chaosbohne, see my answer below -- the API for Jison has changed since the original answer was accepted. Multiline lexical actions are delimited by `{% %}` while multiline **parser** actions (those referenced in the question) are delimited by `{{ }}` – Clark Mar 16 '15 at 00:16
2

As noted by @Nucleon and @chaosbohne, the accepted answer is incorrect.

Directly from the Jison wiki (https://github.com/zaach/jison/wiki/Deviations-From-Flex-Bison)

Within lexer actions use %{ ... %} delimiters if you want to use block-style statements, e.g.:

.*  %{
  if (true) {
    console.log('test');
  }
  // ...
%}

Within parser actions use {{ .. }} delimiters for the same purpose.

So, if you are in the lexer, for multiline actions, use %{ action %}. In the parser, for multiline actions, use {{ action }}. If you are you are writing a single-line action, single braces {} work fine in the lexer and parser.

Clark
  • 416
  • 3
  • 10