ctx в ANTLR4 javascript посетитель - PullRequest
1 голос
/ 16 марта 2020

Использование ANTLR4 v4.8

Я нахожусь в процессе написания транспилером, исследуя использование ANTLR (javascript цель с посетителем).

Грамматика -> lex / parse в порядке и я сейчас сижу на дереве разбора.

Грамматика

grammar Mygrammar;

/*
 * parser rules
 */

progm   : stmt+;

stmt
: progdecl
| print
;

progdecl : PROGDECLKW ID '..';
print    : WRITEKW STRLIT '..';

/*
 * lexer rules
 */

PROGDECLKW  : 'DECLAREPROGRAM';
WRITEKW     : 'PRINT';

// Literal
STRLIT             : '\'' .*? '\'' ;

// Identifier 
ID              : [a-zA-Z0-9]+;

// skip
LINE_COMMENT    : '*' .*? '\n' -> skip;
TERMINATOR      : [\r\n]+ -> skip;
WS              : [ \t\n\r]+ -> skip;

hw.mg

***************
* Hello world
***************

DECLAREPROGRAM  hw..

PRINT 'Hello World!'..

index. js

...
const myVisitor = require('./src/myVisitor').myVisitor;

const input = './src_sample/hw.mg';
const chars = new antlr4.FileStream(input);
...
parser.buildParseTrees = true;

const myVisit = new myVisitor();
myVisit.visitPrint(parser.print());

Использование посетителя не показалось простым, и этот пост SO помогает в некоторой степени.

При использовании context . Есть ли хороший способ отслеживать ctx, когда я нажимаю на каждый узел?
Использование myVisit.visit(tree) в качестве начального контекста - это нормально. Когда я начинаю посещать каждый узел, использование не root context
myVisit.visitPrint(parser.print()) выдает ошибку.

Ошибка:

PrintContext {
  parentCtx: null,
  invokingState: -1,
  ruleIndex: 3,
  children: null,
  start: CommonToken {
    source: [ [MygrammarLexer], [FileStream] ],
    type: -1,
    channel: 0,
    start: 217,

вместе с exception: InputMismatchException [Error]
Я полагаю, это потому, что children означает null вместо того, чтобы быть заполненным.
Что, в свою очередь, связано с line 9:0 mismatched input '<EOF>' expecting {'DECLAREPROGRAM', 'PRINT'}

Вопрос:
Это единственный способ передать контекст или я делаю это неправильно? Если использование правильное, то я склоняюсь к тому, чтобы рассматривать это как ошибку.

edit 17.3 - добавлена ​​грамматика и источник

1 Ответ

2 голосов
/ 17 марта 2020

Когда вы вызываете parser.print(), но подаете на него ввод:

***************
* Hello world
***************

DECLAREPROGRAM  hw..

PRINT 'Hello World!'..

, оно не будет работать. Для print() парсер ожидает ввод, подобный этому PRINT 'Hello World!'... Для всего ввода вам нужно будет вызвать prog() вместо этого. Кроме того, имеет смысл «привязать» ваше правило запуска с помощью токена EOF, который заставит ANTLR потреблять весь ввод:

progm : stmt+ EOF;

Если вы хотите выполнить синтаксический анализ и посетить все дерево синтаксического анализа (используя prog()), но интересует только узел / контекст print, тогда лучше использовать слушателя вместо посетителя. Посмотрите на этой странице, как использовать слушателя: https://github.com/antlr/antlr4/blob/master/doc/javascript-target.md

EDIT

Вот как работает слушатель (демонстрация Python, так как у меня нет JS настроен правильно):

import antlr4

from playground.MygrammarLexer import MygrammarLexer
from playground.MygrammarParser import MygrammarParser
from playground.MygrammarListener import MygrammarListener


class PrintPreprocessor(MygrammarListener):
    def enterPrint_(self, ctx: MygrammarParser.Print_Context):
        print("Entered print: `{}`".format(ctx.getText()))


if __name__ == '__main__':
    source = """
        ***************
        * Hello world
        ***************

        DECLAREPROGRAM  hw..

        PRINT 'Hello World!'..
    """
    lexer = MygrammarLexer(antlr4.InputStream(source))
    parser = MygrammarParser(antlr4.CommonTokenStream(lexer))
    antlr4.ParseTreeWalker().walk(PrintPreprocessor(), parser.progm())

При выполнении кода выше будет напечатано следующее:

Entered print: `PRINT'Hello World!'..`

Итак, вкратце: этот слушатель принимает все дерево разбора ваш ввод, но только «слушает», когда мы вводим правило синтаксического анализатора print.

Обратите внимание, что я переименовал print в print_, потому что print защищен в цели Python.

...