Search code examples
programming-languagesbisonjison

How to get line number from an AST node (Jison)


I'm using Jison to build a simple calculator language, which includes variables. I want these variables to work similar to JavaScript, that is you have to initialise it with the var keyword the first time. In my language, I want to show an error if a variable gets re-initialise.

var myVar = 4
var myVar = 3
// Error, cannot reinitialise variable myVar on line 2

My question is, how do I get the line number for an AST node? In my grammer file, I can pass the line number from the parser to my AssignVariable object, but I'm wondering if there is a better way to do this?

stmt
    : 'PRINT' expr
        { $$ = new yy.Print($2) }
    | 'VAR' 'IDENTIFIER' 'ASSIGN' expr
        { $$ = new yy.AssignVariable($2, $4, $3); $$.lineNo = yylineno }
    | 'IDENTIFIER' 'ASSIGN' expr
        { $$ = new yy.SetVariable($1, $3, $2) }
    ;

I will also need the line number for other nodes in my compiler for other types of error checking.

A more high-level takeaway from this question could be: What's the best way to detect and handle compile time errors using Jison (or similar)?


Solution

  • I was able to inject the line number into my AST nodes by monkey-patching the generated parser in my compiler. This was suggested by Zach. facepalms

    // load in your generated parser
    var parser = require('./parser');
    
    // store the current performAction function
    parser._performAction = parser.performAction;
    
    // override performAction
    parser.performAction = function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
        // invoke the original performAction
        var ret = parser._performAction.call(this, yytext, yyleng, yylineno, yy, yystate, $$, _$);
        // Do your own stuff
        if (this.$._type) {
            this.$.lineNo = yylineno;
        }
        return ret;
    }