我的情况是,我的语言包含一些在构建时未知但在运行时已知的单词,导致需要不断重建/重新部署程序以考虑新单词.如果在Antlr中有可能从配置文件中生成一些令牌,我就会徘徊?
例如,如果我有一个规则,在一个简化的例子中
rule : WORDS+;
WORDS : 'abc';
Run Code Online (Sandbox Code Playgroud)
我的语言在runntime遇到'bcd',我希望能够修改配置文件以将bcd定义为单词,而不是必须重建然后重新部署.
Bar*_*ers 18
您可以为lexer类添加某种集合.此集合将包含所有运行时单词.然后在规则中添加一些可能与这些运行时单词匹配的自定义代码,并在集合中存在时更改令牌的类型.
假设您要解析输入:
"foo bar baz"
Run Code Online (Sandbox Code Playgroud)
并且在运行时,单词"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();}
;
Run Code Online (Sandbox Code Playgroud)
还有一点测试类:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
这将产生以下输出:
RUNTIME_WORD :: foo
Word :: bar
RUNTIME_WORD :: baz
Run Code Online (Sandbox Code Playgroud)
这是另一个针对您的问题量身定制的演示(我最初过快地浏览了您的问题,但我会留下我的第一个演示,因为它可能会派上用场).其中没有太多评论,但我的猜测是你不会有问题把握所发生的事情(如果没有,请不要犹豫要求澄清!).
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();}
;
Run Code Online (Sandbox Code Playgroud)
和班级:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
将产生:
Word :: BBB
Word :: abc
Word :: CDEFG
Run Code Online (Sandbox Code Playgroud)
如果某些运行时单词以另一个运行时单词开头,请小心.例如,如果运行时单词包含"stack"和"stacker",则需要首先检查较长的单词!根据字符串的长度对集合进行排序应该是有序的.
最后一个警告:如果只是"stack"在你的运行时单词列表和词法分析器遇到"stacker",那么你可能不想创建一个"stack"-token并留下"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 ... ;
}
Run Code Online (Sandbox Code Playgroud)