Можно ли получить список из метки, определенной парсером в antlr4? - PullRequest
0 голосов
/ 28 января 2019

Возьми эту фиктивную antlr4-грамматику:

grammar testingGrammar;
@header{package gen;}

dsopt_rename: 'rename' (OLDN=ID '=' NEWN=ID)+;
ID: [a-zA-Z_];

Моя цель - Java.Я хочу получить два списка: oldNames и newNames;можно сделать так:

@Override
public DsOption visitDsopt_rename(Dsopt_renameContext ctx) {    
    LinkedList<String> oldNames = new LinkedList<String>();
    LinkedList<String> newNames = new LinkedList<String>();
    for (int i=0; i < ctx.ID().size(); ++i) {
        LinkedList<String> rename = (i%2 == 1) ? oldNames : newNames;
        rename.add(ctx.ID(i).getText());
    }
    return new DsOptRename(oldNames, newNames);
}

Я бы предпочел следующее - также известное как "второй подход" - (если бы это сработало):

@Override
public DsOption visitDsopt_rename(Dsopt_renameContext ctx) {    
    LinkedList<String> oldNames = new LinkedList<String>();
    LinkedList<String> newNames = new LinkedList<String>();
    ctx.OLDN().forEach(e -> oldNames.add(e.getText()));
    ctx.NEWN().forEach(e -> oldNames.add(e.getText()));
    return new DsOptRename(oldNames, newNames);
}

Очевидно, метки ctx.OLDN (без скобок) и ctx.NEWN просто сохраняют первую итерацию списка, а не весь список (в то время как, например, ID хранит весь список).

Первый вопрос: 1. Возможно исправитьвторой код, чтобы выполнить работу, используя второй подход (т.е. не касаясь грамматики)?Имейте в виду, что этот пример был достаточно легким, чтобы первый код работал нормально, но если бы у меня было правило, например «пример: (ID ID? ID) +;»потребуется другой подход;возможно, это невозможно исправить, потому что этот подход не должен работать в первую очередь (правило должно быть определено по-другому).

Каков лучший способ изменить грамматику, чтобы сделать это;Я думаю:
grammar testingGrammar;
@header{package gen;}

dsopt_rename: 'rename' (oldn '=' newn)+;
oldn: ID;
newn: ID;
ID: [a-zA-Z_];

но это, вероятно, подвержено ошибкам, потому что oldn и newn могут совпадать непреднамеренно.

Спасибо за ваше время!

1 Ответ

0 голосов
/ 28 января 2019

Используйте нотацию +=, чтобы собрать свои токены:

grammar testingGrammar;

dsopt_rename
 : 'rename' ( lhs+=ID '=' rhs+=ID )+
 ;

ID     : [a-zA-Z_];
SPACES : [ \t\r\n]+ -> skip;

Проверьте это следующим образом:

String source = "rename a = A b = B c = C";

testingGrammarLexer lexer = new testingGrammarLexer(CharStreams.fromString(source));
testingGrammarParser parser = new testingGrammarParser(new CommonTokenStream(lexer));

testingGrammarParser.Dsopt_renameContext ctx = parser.dsopt_rename();

List<Token> lhsTokens = ctx.lhs;
List<Token> rhsTokens = ctx.rhs;

System.out.printf("lhsTokens=%s\nrhsTokens=%s\n", lhsTokens, rhsTokens);

, который напечатает:

lhsTokens=[[@1,7:7='a',<3>,1:7], [@4,13:13='b',<3>,1:13], [@7,19:19='c',<3>,1:19]]
rhsTokens=[[@3,11:11='A',<3>,1:11], [@6,17:17='B',<3>,1:17], [@9,23:23='C',<3>,1:23]]

Дополнительная информация: https://github.com/antlr/antlr4/blob/master/doc/parser-rules.md#rule-element-labels

...