Ответ 1
Вы можете добавить какую-то коллекцию в свой класс lexer. Эта коллекция будет содержать все слова во время выполнения. Затем вы добавляете какой-либо пользовательский код внутри правила, который может соответствовать этим словам времени выполнения и изменять тип токена, если он присутствует в коллекции.
Demo
Предположим, вы хотите проанализировать ввод:
"foo bar baz"
и во время выполнения слова "foo"
и "baz"
должны стать специальными словами во время выполнения. Следующая грамматика показывает, как это решить:
grammar RuntimeWords;
tokens {
RUNTIME_WORD;
}
@lexer::members {
private java.util.Set<String> runtimeWords;
public RuntimeWordsLexer(CharStream input, java.util.Set<String> words) {
super(input);
runtimeWords = words;
}
}
parse
: (w=. {System.out.printf("\%-15s :: \%s \n", tokenNames[$w.type], $w.text);})+ EOF
;
Word
: ('a'..'z' | 'A'..'Z')+
{
if(runtimeWords.contains(getText())) {
$type = RUNTIME_WORD;
}
}
;
Space
: ' ' {skip();}
;
И немного тестового класса:
import org.antlr.runtime.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
Set<String> words = new HashSet<String>(Arrays.asList("foo", "baz"));
ANTLRStringStream in = new ANTLRStringStream("foo bar baz");
RuntimeWordsLexer lexer = new RuntimeWordsLexer(in, words);
CommonTokenStream tokens = new CommonTokenStream(lexer);
RuntimeWordsParser parser = new RuntimeWordsParser(tokens);
parser.parse();
}
}
который будет выдавать следующий результат:
RUNTIME_WORD :: foo
Word :: bar
RUNTIME_WORD :: baz
Демо II
Вот еще одна демонстрация, которая больше подходит для вашей проблемы (я сначала сфотографировал ваш вопрос, но я оставлю свою первую демо-версию на месте, потому что это может пригодиться кому-то). Там не так много комментариев, но я предполагаю, что у вас не будет проблем с пониманием того, что происходит (если нет, не стесняйтесь просить разъяснений!).
grammar RuntimeWords;
@lexer::members {
private java.util.Set<String> runtimeWords;
public RuntimeWordsLexer(CharStream input, java.util.Set<String> words) {
super(input);
runtimeWords = words;
}
private boolean runtimeWordAhead() {
for(String word : runtimeWords) {
if(ahead(word)) {
return true;
}
}
return false;
}
private boolean ahead(String word) {
for(int i = 0; i < word.length(); i++) {
if(input.LA(i+1) != word.charAt(i)) {
return false;
}
}
return true;
}
}
parse
: (w=. {System.out.printf("\%-15s :: \%s \n", tokenNames[$w.type], $w.text);})+ EOF
;
Word
: {runtimeWordAhead()}?=> ('a'..'z' | 'A'..'Z')+
| 'abc'
;
Space
: ' ' {skip();}
;
и класс:
import org.antlr.runtime.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
Set<String> words = new HashSet<String>(Arrays.asList("BBB", "CDEFG"));
ANTLRStringStream in = new ANTLRStringStream("BBB abc CDEFG");
RuntimeWordsLexer lexer = new RuntimeWordsLexer(in, words);
CommonTokenStream tokens = new CommonTokenStream(lexer);
RuntimeWordsParser parser = new RuntimeWordsParser(tokens);
parser.parse();
}
}
будет производить:
Word :: BBB
Word :: abc
Word :: CDEFG
Будьте осторожны, если некоторые из ваших слов времени запуска начинаются с другого. Например, если ваши слова во время выполнения содержат "stack"
и "stacker"
, вам нужно сначала проверить более длинное слово! Сортировка набора в зависимости от длины строк должна быть в порядке.
Одно последнее предостережение: если в вашем списке времени выполнения есть только "stack"
, а встреча lexer "stacker"
, то вы, вероятно, не хотите создавать "stack"
-token и оставлять "er"
зависанием. В этом случае вам нужно будет проверить, не является ли символ после последнего char в word
не буквой:
private boolean ahead(String word) {
for(int i = 0; i < word.length(); i++) {
if(input.LA(i+1) != word.charAt(i)) {
return false;
}
}
// charAfterWord = input.LA(word.length())
// assert charAfterWord != letter
// note that charAfterWord could also be EOF
return ... ;
}