如何检测来源/调试Lexer的空值

时间:2019-05-12 03:10:48

标签: javascript antlr antlr4

我已经为ANTLR4实现了基本语法,以构建JavaScript解释器 从简单的Java风格的语言开始。

当我运行脚本以转换代码(解析/生成/转换)时,我得到 TypeError: ctx.INT is not a function,来自transpiler文件。

这是我要编译的脚本

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;

这是捕获错误的地方

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";
}

这是我的语法文件

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;

我注意到的是,在Lexer文件中有一些'null'值出现,我怀疑这是INT()为null而不是它的原因

Lexer文件

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";

我想要实现的是基本的计算器操作,变量分配和向控制台的打印,但是语法不同,这些语法会转换为JavaScript。

1 个答案:

答案 0 :(得分:2)

当您定义带有标记替代项的解析器规则时,每个替代项都会获得自己的子类,只有该子类才会具有与该替代项的子规则相对应的getter方法。因此,根据您的语法,您拥有ExprContext的以下子类:

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

其中只有IntContext具有INT()方法。因此,除非您首先确保它实际上是INT(),否则不要在ExprContext上调用IntContext

由ANTLR生成的侦听器和访问者将为每个带标签的替代项提供enterexitvisit方法,因此您可以确保与{ {1}}就是您定义IntContextvisitIntContext(ctx)等而不是visitStringContext(ctx),然后您就会知道visitExprContext(ctx)只能具有与之对应的类型替代。