Как получить номер строки в ANTLR3 tree-parser @init - PullRequest
3 голосов
/ 01 декабря 2011

В ANTLR, версия 3, как можно получить номер строки в действии @init высокоуровневого правила синтаксического анализа дерева?

Например, в приведенном ниже действии @init я бынравится вставлять номер строки вместе с текстом предложения.

sentence
    @init { myNodeVisitor.pushScriptContext( new MyScriptContext( $sentence.text )); }
    : assignCommand 
    | actionCommand;
    finally {
        m_nodeVisitor.popScriptContext();
    }

Мне нужно нажать контекст до выполнения действий, связанных с символами в правилах.

Некоторые вещи, которые не работают:

  • Использование $sentence.line - это не определено, даже если $sentence.text есть.
  • ПеремещениеПерефразировать толчок в действие правила.Помещенный перед правилом, токен в правиле недоступен.Помещенное после правила, действие происходит после действий, связанных с символами правила.
  • Использование этого выражения в действии @init, которое компилирует, но возвращает значение 0: getTreeNodeStream().getTreeAdaptor().getToken( $sentence.start ).getLine(). РЕДАКТИРОВАТЬ: На самом деле, это работает, если $ termince.start является либо реальным токеном, либо воображаемым со ссылкой - см. Ответ Барта Киерса ниже.

Похоже, что если бы я мог легко получить в правиле @init сопоставленный текст и первый сопоставленный токен, то также должен быть простой способ получить номер строки.

1 Ответ

7 голосов
/ 02 декабря 2011

Вы можете посмотреть на 1 шаг вперед в токене / древовидной структуре древовидной грамматики, используя следующее: CommonTree ahead = (CommonTree)input.LT(1), которое вы можете поместить в раздел @init.

Каждая CommonTree (реализация по умолчанию Tree в ANTLR) имеет метод getToken(), который возвращает Token, связанный с этим деревом.И у каждого Token есть метод getLine(), который, что неудивительно, возвращает номер строки этого токена.

Итак, если вы сделаете следующее:

sentence
@init {
  CommonTree ahead = (CommonTree)input.LT(1);
  int line = ahead.getToken().getLine();
  System.out.println("line=" + line);
}
  :  assignCommand 
  |  actionCommand
  ;

, вам следуетбыть в состоянии видеть некоторые правильные номера печатаемых строк.Я говорю немного , потому что это не пойдет как запланировано в всех случаях.Позвольте мне продемонстрировать, используя простой пример грамматики:

grammar ASTDemo;

options { 
  output=AST;
}

tokens {
  ROOT;
  ACTION;
}

parse
  :  sentence+ EOF -> ^(ROOT sentence+)
  ;

sentence
  :  assignCommand 
  |  actionCommand
  ;

assignCommand
  :  ID ASSIGN NUMBER -> ^(ASSIGN ID NUMBER)
  ;

actionCommand
  :  action ID -> ^(ACTION action ID)
  ;

action
  :  START
  |  STOP
  ;

ASSIGN : '=';
START  : 'start';
STOP   : 'stop';
ID     : ('a'..'z' | 'A'..'Z')+;
NUMBER : '0'..'9'+;
SPACE  : (' ' | '\t' | '\r' | '\n')+ {skip();};

, чья древовидная грамматика выглядит так:

tree grammar ASTDemoWalker;

options {
  output=AST;
  tokenVocab=ASTDemo;
  ASTLabelType=CommonTree;
}


walk
  :  ^(ROOT sentence+)
  ;

sentence
@init {
  CommonTree ahead = (CommonTree)input.LT(1);
  int line = ahead.getToken().getLine();
  System.out.println("line=" + line);
}
  :  assignCommand 
  |  actionCommand
  ;

assignCommand
  :  ^(ASSIGN ID NUMBER)
  ;

actionCommand
  :  ^(ACTION action ID)
  ;

action
  :  START
  |  STOP
  ;

И если вы запустите следующий тестовый класс:

import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;

public class Main {
  public static void main(String[] args) throws Exception {
    String src = "\n\n\nABC = 123\n\nstart ABC";
    ASTDemoLexer lexer = new ASTDemoLexer(new ANTLRStringStream(src));
    ASTDemoParser parser = new ASTDemoParser(new CommonTokenStream(lexer));
    CommonTree root = (CommonTree)parser.parse().getTree();
    ASTDemoWalker walker = new ASTDemoWalker(new CommonTreeNodeStream(root));
    walker.walk();
  }
}

вы увидите следующее:

line=4
line=0

Как видите, "ABC = 123" дал ожидаемый результат (строка 4), а "start ABC" - нет (строка 0).Это связано с тем, что корнем правила action является токен ACTION, и этот токен никогда не определяется в лексере, только в блоке tokens{...}.И поскольку он на самом деле не существует во входных данных, по умолчанию к нему прикреплена строка 0.Если вы хотите изменить номер строки, вам необходимо предоставить «опорный» токен в качестве параметра для этого так называемого мнимого ACTION токена, который он использует для копирования атрибутов в себя.

Таким образом, если вы измените правило actionCommand в объединенной грамматике на:

actionCommand
  :  ref=action ID -> ^(ACTION[$ref.start] action ID)
  ;

номер строки будет таким, как ожидалось (строка 6).

Обратите внимание, что каждое правило синтаксического анализатора имеет атрибут start и end, который является ссылкой на первый и последний токен соответственно.Если action было правилом лексера (скажем, FOO), то вы могли бы опустить .start из него:

actionCommand
  :  ref=FOO ID -> ^(ACTION[$ref] action ID)
  ;

Теперь токен ACTION скопировал все атрибуты из любого $ref указывает на, кроме типа токена, который, конечно, int ACTION.Но это также означает, что он скопировал атрибут text, поэтому в моем примере AST, созданный ref=action ID -> ^(ACTION[$ref.start] action ID), может выглядеть следующим образом:

            [text=START,type=ACTION]
                  /         \
                 /           \
                /             \
   [text=START,type=START]  [text=ABC,type=ID]

Конечно, это правильный AST, потому что типыузлы уникальны, но это затрудняет отладку, поскольку ACTION и START имеют один и тот же атрибут .text.

Вы можете скопировать все атрибуты в мнимый токен, кроме .text и .type, указав второй строковый параметр, например:

actionCommand
  :  ref=action ID -> ^(ACTION[$ref.start, "Action"] action ID)
  ;

Иесли теперь вы снова запустите тот же тестовый класс, вы увидите следующее:

line=4
line=6

И если вы посмотрите на сгенерированное дерево, оно будет выглядеть так:

[type=ROOT, text='ROOT']
  [type=ASSIGN, text='=']
    [type=ID, text='ABC']
    [type=NUMBER, text='123']
  [type=ACTION, text='Action']
    [type=START, text='start']
    [type=ID, text='ABC']
...