我的情况是,我的语言包含一些在构建时未知但在运行时已知的单词,导致需要不断重建/重新部署程序以考虑新单词。如果在Antlr中有可能从配置文件中生成一些令牌,我就会徘徊?
例如,在一个简化的例子中,如果我有一个规则
rule : WORDS+;
WORDS : 'abc';
我的语言在runntime遇到'bcd',我希望能够修改配置文件以将bcd定义为单词,而不是必须重建然后重新部署。
答案 0 :(得分:18)
您可以为lexer类添加某种集合。此集合将包含所有运行时单词。然后在规则中添加一些可能与这些运行时单词匹配的自定义代码,并在集合中存在令牌时更改令牌的类型。
假设您要解析输入:
"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
这是另一个针对您的问题量身定制的演示(我最初过快地浏览了您的问题,但我会留下我的第一个演示,因为它可能会派上用场)。其中没有太多评论,但我的猜测是你不会有问题把握所发生的事情(如果没有,请不要犹豫要求澄清!)。
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"
在运行时单词列表中并且词法分析器遇到"stacker"
,那么您可能不想创建"stack"
- 令牌,离开"er"
悬空。在这种情况下,您需要检查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 ... ;
}