Я пытаюсь узнать, что 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".
Почему мои изменения к тексту маркера, не отраженному, когда я называю tokens.toString ()?
Как я, предполагают для знания различных идентификаторов Типа маркера? Я шел через с моим отладчиком и видел, что идентификатор для маркеров ИДЕНТИФИКАТОРА равнялся "4" (следовательно моя константа наверху). Но как я знал бы это иначе? Есть ли некоторый другой способ отобразить идентификаторы типа маркера на маркерное имя?
Править:
Одна вещь, которая важна для меня, я хочу для маркеров иметь их исходный запуск и положения конечного символа. Таким образом, я не хочу, чтобы они отразили свои новые положения с именами переменной, изменившими на "V". Это так, я знаю, где маркеры были в тексте первоисточника.
У 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]
Другой приведенный пример изменения текста в лексере хорошо работает, если вы хотите глобально заменить текст во всех ситуациях, однако вы часто хотите только заменить текст токена в определенных ситуациях.
Использование 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. Информация о местоположении сохраняется, но текст, который она «соответствует», был изменен.
Чтобы проверить поток токенов после того, как вы использовали бы тот же код, что и раньше.