我可以在运行时添加Antlr令牌吗?

我可以在运行时添加Antlr令牌吗?,antlr,antlr3,Antlr,Antlr3,我的语言包含一些在构建时未知但在运行时已知的单词,因此需要不断地重新构建/重新部署程序以考虑新单词。我想知道在Antlr中是否可以从配置文件生成一些令牌 e、 在一个简化的例子中,如果我有一个规则 rule : WORDS+; WORDS : 'abc'; 我的语言在运行时遇到了“bcd”,我希望能够修改配置文件,将bcd定义为单词,而不必重新构建然后重新部署 您可以向lexer类添加某种集合。此集合将保存所有运行时单词。然后,在规则中添加一些可能与这些运行时单词匹配的自定义代码,并更改令牌

我的语言包含一些在构建时未知但在运行时已知的单词,因此需要不断地重新构建/重新部署程序以考虑新单词。我想知道在Antlr中是否可以从配置文件生成一些令牌

e、 在一个简化的例子中,如果我有一个规则

rule : WORDS+;

WORDS : 'abc';

我的语言在运行时遇到了“bcd”,我希望能够修改配置文件,将bcd定义为单词,而不必重新构建然后重新部署

您可以向lexer类添加某种集合。此集合将保存所有运行时单词。然后,在规则中添加一些可能与这些运行时单词匹配的自定义代码,并更改令牌的类型(如果它存在于集合中)

演示 假设您想要解析输入:

"foo bar baz"
在运行时,单词
“foo”
“baz”
应该成为特殊的运行时单词。以下语法说明了如何解决此问题:

语法RuntimeWords;
代币{
英语单词;
}
@lexer::成员{
private java.util.Set runtimeWords;
公共运行时WordsLexer(CharStream输入,java.util.Set字){
超级(输入);
runtimeWords=单词;
}
}
作语法分析
:(w=.{System.out.printf(\%-15s::\%s\n),标记名[$w.type],$w.text);})+EOF
;
单词
:('a'..'z'|'a'..'z')+
{
if(runtimeWords.contains(getText())){
$type=RUNTIME\u单词;
}
}
;
空间
:''{skip();}
;
还有一个小测试班:

import org.antlr.runtime.*;
导入java.util.*;
公共班机{
公共静态void main(字符串[]args)引发异常{
Set words=newhashset(Arrays.asList(“foo”、“baz”);
ANTLSTRINGSTREAM in=新的ANTLSTRINGSTREAM(“foo-bar-baz”);
RuntimeWordsLexer lexer=新的RuntimeWordsLexer(in,words);
CommonTokenStream令牌=新的CommonTokenStream(lexer);
RuntimeWordsParser解析器=新的RuntimeWordsParser(令牌);
parser.parse();
}
}
将产生以下输出:

RUNTIME_WORD    :: foo 
Word            :: bar 
RUNTIME_WORD    :: baz
演示II 这是另一个更适合您的问题的演示(起初我浏览您的问题太快了,但我将保留我的第一个演示,因为它可能会对某些人有用)。里面没有太多的评论,但我的猜测是,您在理解发生了什么方面不会有问题(如果没有,请毫不犹豫地要求澄清!)

语法RuntimeWords;
@lexer::成员{
private java.util.Set runtimeWords;
公共运行时WordsLexer(CharStream输入,java.util.Set字){
超级(输入);
runtimeWords=单词;
}
专用布尔值runtimeWordAhead(){
for(字符串字:运行时字){
如果(前面(字)){
返回true;
}
}
返回false;
}
专用布尔前置(字符串字){
for(int i=0;i('a'..'z'|'a'..'z')+
|“abc”
;
空间
:''{skip();}
;
班级:

import org.antlr.runtime.*;
导入java.util.*;
公共班机{
公共静态void main(字符串[]args)引发异常{
Set words=newhashset(Arrays.asList(“BBB”、“CDEFG”);
AntlStringStream in=新的AntlStringStream(“BBB abc CDEFG”);
RuntimeWordsLexer lexer=新的RuntimeWordsLexer(in,words);
CommonTokenStream令牌=新的CommonTokenStream(lexer);
RuntimeWordsParser解析器=新的RuntimeWordsParser(令牌);
parser.parse();
}
}
将产生:

Word            :: BBB 
Word            :: abc 
Word            :: CDEFG 
如果一些运行时单词以另一个单词开头,请小心。例如,如果您的运行时单词包含
“stack”
“stacker”
,则您希望先检查较长的单词!根据字符串的长度对集合进行排序应该是有序的

最后一句警告:如果运行时单词列表中只有
“stack”
,并且lexer遇到
“stacker”
,那么您可能不想创建
“stack”
-令牌并让
“er”
挂起。在这种情况下,您需要检查
单词中最后一个字符之后的字符是否不是字母:

private boolean-ahead(字符串字){
for(int i=0;i
这是一个非常好的答案,我只是希望我能投更多的票。@Richard,你的好话比任何数量的投票都更有价值。不客气。优秀的写作+1;您不仅解决了这个问题,还提供了一些非常有价值的见解,说明如何从架构上解决这类问题-谢谢@BartKiers,在这两个演示中,我在Lexer类中都有一个错误:public RuntimeWordsLexer(CharStream input,java.util.Set words){super(input);runtimeWords=words;}。它告诉我“缺少此方法的返回类型”和“构造函数调用必须是构造函数中的第一条语句”。你能帮我一下吗?@user2144555我猜你给语法命名的不是
RuntimeWords
,在这种情况下,构造函数
RuntimeWordsLexer
被视为一个普通方法,它缺少返回类型。你需要复制粘贴我上面写的东西。如果你遇到更多的问题,最好创建一个你自己的问题:这些评论框不适合广泛的问答。祝你好运