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

pro*_*ach 10 antlr antlr3

我的情况是,我的语言包含一些在构建时未知但在运行时已知的单词,导致需要不断重建/重新部署程序以考虑新单词.如果在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)

演示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();}
  ;
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)