1
votes

I've implemented a basic grammar for ANTLR4 to build a JavaScript interpreter from a Simple Javascript-esque language.

When I run my script to transform the code (parsing/generation/transpilation) I get TypeError: ctx.INT is not a function which comes from the transpiler file.

This is my script to compile

var fs = require('fs');
var antlr4 = require('antlr4');
var buildAst = require('./build-ast');
var mylangTranspiler = require('./mylang-transpiler');


function runScript(inputText) {
    var ast = buildAst(inputText);
    var transpiler = new mylangTranspiler();
    antlr4.tree.ParseTreeWalker.DEFAULT.walk(transpiler, ast);
    // eval(transpiler.output);

    fs.writeFile("./main.js", transpiler.output, function(err) {
        if(err) {
            return console.log(err);
        }

        console.log("The file was saved!");
    });
}

var contents = fs.readFileSync('./tests/test.mylang', 'utf8');
console.log(contents);

runScript(contents);

module.exports = runScript;

This is the place where the error gets caught

mylangTranspiler.prototype.resolveExpr = function(ctx) {
    console.log(ctx);
    if (ctx.INT() != null) {
        return ctx.INT().getText();
    } else if (ctx.ID() != null) {
        return ctx.ID().getText();
    } else if (ctx.STRING() != null) {

        return ctx.STRING().getText().replace(/\$\{([^\}]+)\}/g, '" + $1 + "');
    } else return "undefined";
}

This is my grammar file

grammar mylang;

compilationUnit: stmt*;

stmt
    : assignStmt
    | invocationStmt
    ;

assignStmt: SET ID TO expr;
invocationStmt: ID ((expr COMMA)* expr)?;

expr: expr (MUL | DIV) expr  # MulDiv
    | expr (ADD | SUB) expr  # AddSub
    | LPAREN expr RPAREN     # Parens
    | ID                     # ID
    | INT                    # Int
    | STRING                 # String
    ;

INT: [0-9]+;
STRING : '"' .*? '"' ;
ID: [a-zA-Z_] [a-zA-Z0-9_]*;

COMMA: ',';
SAY: 'show';
SET: 'set';
TO: 'to';

MUL: 'times';
DIV: 'by';
ADD: 'add';
SUB: 'sub';
LPAREN: '(';
RPAREN: ')';

WS : [ \t\r\n]+ -> skip;

What i've noticed is that in the Lexer file there are some 'null' value occurencies which i suspect is the reason INT() is null while it shouldnt

Lexer file

mylangLexer.prototype.channelNames = [ "DEFAULT_TOKEN_CHANNEL", "HIDDEN" ];

mylangLexer.prototype.modeNames = [ "DEFAULT_MODE" ];

mylangLexer.prototype.literalNames = [ null, null, null, null, "','", "'show'", 
                                        "'set'", "'to'", "'times'", "'by'", 
                                        "'add'", "'sub'", "'('", "')'" ];

mylangLexer.prototype.symbolicNames = [ null, "INT", "STRING", "ID", "COMMA", 
                                         "SAY", "SET", "TO", "MUL", "DIV", 
                                         "ADD", "SUB", "LPAREN", "RPAREN", 
                                         "WS" ];

mylangLexer.prototype.ruleNames = [ "INT", "STRING", "ID", "COMMA", "SAY", 
                                     "SET", "TO", "MUL", "DIV", "ADD", "SUB", 
                                     "LPAREN", "RPAREN", "WS" ];

mylangLexer.prototype.grammarFileName = "mylang.g4";

What im trying to achieve is basic calculator operations, variable assignments and printing to the console but with a different syntax which will get transpiled to JavaScript.

1

1 Answers

2
votes

When you define a parser rule with labeled alternatives, each alternative will get its own subclass and only that subclass will have the getter methods corresponding to that alternative's subrules. So given your grammar, you have the following subclasses of ExprContext:

  • MulDivContext
  • AddSubContext
  • ParensContext
  • IDContext
  • IntContext
  • StringContext

And of those, only IntContext has an INT() method. So you should not call INT() on an ExprContext unless you first make sure that it's actually an IntContext.

ANTLR-generated listeners and visitors will have enter and exit or visit methods for each labeled alternative, so the way you'd make sure that you're dealing with an IntContext would be that you define visitIntContext(ctx), visitStringContext(ctx) etc. instead of visitExprContext(ctx) and then you'd know that ctx can only ever have the type corresponding to that alternative.