2015-04-02 5 views
1

В ANTLR4 сгенерированный лексер в Java содержит общедоступное поле для каждого токена, где тип поля является простым «int». Есть ли причина, почему ANTLR4 не использует перечисления вместо этого, или есть ли возможность использовать его перечислениями?Есть ли способ сделать ANTLR4 использовать перечисления для генерируемых токенов?

Это упрощенный пример с верхней части моей головы

x.g4

A: 'a'; 
B: 'b'; 

XLexer.java

public class XLexer extends Lexer{ 
    public static final int A = 1, B = 2; 
} 

Я предпочел бы для XLexer вместо этого содержать

public class XLexer extends Lexer{ 
    public static enum Token{ 
    A(1), B(2) 
    } 
} 

Это полезно для целей отладки при сбрасывании токенов. В настоящее время имя токена не печатается, вместо этого предоставляется только целочисленное представление.

[@-1,0:0='a',<1>,1:0] 

Более читаемый вариант будет иметь < > вместо < 1>

[@-1,0:0='a',<A>,1:0] 
+1

Он обсуждался ранее здесь: http://www.antlr3.org/pipermail/antlr-interest/2008- May/028432.html –

+0

В свете этого обсуждения его, вероятно, проще всего для сгенерированного класса lexer содержать массив, который отображает целочисленные значения токенов на имя строки, например, уже сделано для имен режимов и имен правил. Существует массив tokenNames, но он содержит, казалось бы, случайный набор символов. Возможно, это всего лишь ошибка. – jonr

ответ

1

Вот мой текущий обходной путь. Я создаю пользовательский маркер и обеспечить TokenFactory к XLexer через

lexer.setTokenFactory(new MyTokenFactory()); 

И я переопределять метод ToString() в моем классе лексем.

public class MyToken extends Token{ 
    @Override 
    public String toString(){ 
    StringBuilder out = new StringBuilder(); 

    out.append("["); 
    out.append("'").append(getText()).append("'"); 
    out.append(" type ").append(getName()); //getName() is implemented by this class 

    int start = getCharPositionInLine(); 
    int end = start + getText().length(); 
    out.append(" at ").append(getLine()).append(":").append(start).append("-").append(end); 
    out.append("]"); 

    return out.toString(); 
} 

где вместо того чтобы показывать целое число для типа класс использует GetName(), чтобы преобразовать целое число в строку.

// inside the token class 
private String getName(){ 
    switch (getType()){ 
    case XLexer.A: return "A"; 
    case XLexer.B: return "B"; 
    default: throw new RuntimeException("unknown token " + getType()); 
    } 
} 

Это производит следующий вывод

['A' type A at 1:5-6] 

Это решение несколько хрупким в этом GetName() должен быть обновлен, чтобы оставаться в синхронизации с текущими лексем, определенных в файле g4. Невозможно реализовать это свойство, поскольку компилятор не может знать, обрабатываются ли все типы токенов в коммутаторе внутри getName().

1

Причина, почему ANTLR4 использует int сек вместо enumsявляются простота и производительность.

Для целей отладки, вы можете изменить строковое представление маркеров следующим образом:

  • Создать собственную реализацию лексемы, простираясь CommonToken. Определите метод toString() по своему усмотрению.

  • Создать реализацию TokenFactory, которая возвращает маркеры вашего пользовательского типа.

  • Установить маркерный завод for lexer и for parser.

Смотрите также:


EDIT, решение проблемы вы mentio в вашем ответе.

Чтобы избежать сохранения символических имен в синхронизации с .g4 вручную, вы можете построить отображение XLexerdynamically using reflection.

+0

Можете ли вы сказать больше о простоте и производительности, и как перечисление не удовлетворяет этим свойствам? – jonr

+0

Отражение не будет работать, поскольку в классе XLexer есть несколько видов полей, которые объявляются как «public static final int». Только подмножество из них - это типы токенов. Например, режимы лексера становятся int-полями и имеют значения, которые перекрывают значения токена. Предположим, что в грамматике g4 выше названного ZZ существует дополнительный режим лексера. Класс XLexer будет иметь «int A = 1; int ZZ = 1; – jonr

+0

@jonr, как я понимаю, вы широко используете ANTLR и определяете сложные грамматики. Вы попробовали [ANTLRWorks2] (http://tunnelvisionlabs.com/products/demo/antlrworks) для отладки? –

4

Чтобы преобразовать тип маркера INT его символическое значение, просто используйте

String tokenName = YourLexer.VOCABULARY.getSymbolicName(type); 
+1

Это работает, но только в версии 4.5 (последняя версия). – jonr

Смежные вопросы