Как я могу изменить текст маркеров в CommonTokenStream с ANTLR?

Я пытаюсь узнать, что ANTLR и в то же время используют его для текущего проекта.

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

Теперь, я хотел бы смочь изменить текст определенных маркеров в этом потоке и отобразить теперь измененный исходный код.

Например, я попробовал:

import org.antlr.runtime.*;
import java.util.*;

public class LexerTest
{
    public static final int IDENTIFIER_TYPE = 4;

    public static void main(String[] args)
    {
    String input = "public static void main(String[] args) { int myVar = 0; }";
    CharStream cs = new ANTLRStringStream(input);


        JavaLexer lexer = new JavaLexer(cs);
        CommonTokenStream tokens = new CommonTokenStream();
        tokens.setTokenSource(lexer);

        int size = tokens.size();
        for(int i = 0; i < size; i++)
        {
            Token token = (Token) tokens.get(i);
            if(token.getType() == IDENTIFIER_TYPE)
            {
                token.setText("V");
            }
        }
        System.out.println(tokens.toString());
    }  
}

Я пытаюсь установить весь текст маркера Идентификатора на строковый литерал "V".

  1. Почему мои изменения к тексту маркера, не отраженному, когда я называю tokens.toString ()?

  2. Как я, предполагают для знания различных идентификаторов Типа маркера? Я шел через с моим отладчиком и видел, что идентификатор для маркеров ИДЕНТИФИКАТОРА равнялся "4" (следовательно моя константа наверху). Но как я знал бы это иначе? Есть ли некоторый другой способ отобразить идентификаторы типа маркера на маркерное имя?


Править:

Одна вещь, которая важна для меня, я хочу для маркеров иметь их исходный запуск и положения конечного символа. Таким образом, я не хочу, чтобы они отразили свои новые положения с именами переменной, изменившими на "V". Это так, я знаю, где маркеры были в тексте первоисточника.

5
задан Brian Tompsett - 汤莱恩 25 June 2016 в 09:36
поделиться

2 ответа

У ANTLR есть способ сделать это в файле грамматики.

Допустим, вы разбираете строку, состоящую из чисел и строк, разделенных запятыми. Грамматика будет выглядеть так:

grammar Foo;

parse
  :  value ( ',' value )* EOF
  ;

value
  :  Number
  |  String
  ;

String
  :  '"' ( ~( '"' | '\\' ) | '\\\\' | '\\"' )* '"'
  ;

Number
  :  '0'..'9'+
  ;

Space
  :  ( ' ' | '\t' ) {skip();}
  ;

Все это должно быть вам знакомо. Допустим, вы хотите заключить в квадратные скобки все целочисленные значения. Вот как это сделать:

grammar Foo;

options {output=template; rewrite=true;} 

parse
  :  value ( ',' value )* EOF
  ;

value
  :  n=Number -> template(num={$n.text}) "[<num>]" 
  |  String
  ;

String
  :  '"' ( ~( '"' | '\\' ) | '\\\\' | '\\"' )* '"'
  ;

Number
  :  '0'..'9'+
  ;

Space
  :  ( ' ' | '\t' ) {skip();}
  ;

Как видите, я добавил несколько параметров вверху и добавил правило перезаписи (все после -> ) после Число в правиле синтаксического анализатора значения .

Теперь, чтобы проверить все это, скомпилируйте и запустите этот класс:

import org.antlr.runtime.*;

public class FooTest {
  public static void main(String[] args) throws Exception {
    String text = "12, \"34\", 56, \"a\\\"b\", 78";
    System.out.println("parsing: "+text);
    ANTLRStringStream in = new ANTLRStringStream(text);
    FooLexer lexer = new FooLexer(in);
    CommonTokenStream tokens = new TokenRewriteStream(lexer); // Note: a TokenRewriteStream!
    FooParser parser = new FooParser(tokens);
    parser.parse();
    System.out.println("tokens: "+tokens.toString());
  }
}

, который производит:

parsing: 12, "34", 56, "a\"b", 78
tokens: [12],"34",[56],"a\"b",[78]
5
ответ дан 13 December 2019 в 22:07
поделиться

Другой приведенный пример изменения текста в лексере хорошо работает, если вы хотите глобально заменить текст во всех ситуациях, однако вы часто хотите только заменить текст токена в определенных ситуациях.

Использование TokenRewriteStream позволяет гибко изменять текст только в определенных контекстах.

Это можно сделать с помощью подкласса класса потока токенов, который вы использовали. Вместо использования класса CommonTokenStream вы можете использовать TokenRewriteStream .

Итак, TokenRewriteStream потребляет лексер, а затем вы запускаете свой синтаксический анализатор.

В вашей грамматике обычно вы делаете замену следующим образом:

/** Convert "int foo() {...}" into "float foo();" */
function
:
{
    RefTokenWithIndex t(LT(1));  // copy the location of the token you want to replace
    engine.replace(t, "float");
}
type id:ID LPAREN (formalParameter (COMMA formalParameter)*)? RPAREN
    block[true]
;

Здесь мы заменили токен int, который мы сопоставили с текстом float. Информация о местоположении сохраняется, но текст, который она «соответствует», был изменен.

Чтобы проверить поток токенов после того, как вы использовали бы тот же код, что и раньше.

2
ответ дан 13 December 2019 в 22:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: