使用ANTLR代码替换DSL



我正在处理的DSL允许用户定义'完整的文本替换'变量。解析代码时,我们需要查找变量的值并从该代码再次解析。

替代可能非常简单(单个常数)或整个语句或代码块。这是一种模拟语法,我希望可以说明我的观点。

grammar a;
entry
  : (set_variable
  | print_line)*
  ;
set_variable
  : 'SET' ID '=' STRING_CONSTANT ';'
  ;
print_line
  : 'PRINT' ID ';'
  ;
STRING_CONSTANT: ''' ('''' | ~('''))* ''' ;
ID: [a-z][a-zA-Z0-9_]* ;
VARIABLE: '&' ID;
BLANK: [ tnr]+ -> channel(HIDDEN) ;

然后连续执行的以下语句应有效;

SET foo = 'Hello world!';
PRINT foo;            
SET bar = 'foo;'
PRINT &bar                    // should be interpreted as 'PRINT foo;'
SET baz = 'PRINT foo; PRINT'; // one complete statement and one incomplete statement
&baz foo;                     // should be interpreted as 'PRINT foo; PRINT foo;'

任何时候发现了可变令牌,我们立即改用解释该变量的值。如上所述,这可能意味着您以一种无效的方式设置代码,充满了只有在值恰到好处才能完成的半说。可以在文本中的任何时刻重新定义变量。

严格来说,当前的语言定义不是不遵守 nesting& vars彼此之间的嵌套,但是当前的解析无法解决此问题,如果不允许这样做,我就不会感到沮丧。

目前,我正在使用访客建造一名口译员,但是我坚持了。

如何构建一个允许我这样做的Lexer/Parser/解释器?感谢您的帮助!

,所以我找到了一个解决方案。我认为它可能会更好 - 因为它可能会进行大量的数组复制 - 但至少现在可以使用。

编辑:我以前错了,我的解决方案会消耗任何&它发现了,包括在有效位置(例如内部字符串常量)的那些。这似乎是一个更好的解决方案:

首先,我扩展了输入流,以便能够在A&时重写输入蒸汽。遇到。不幸的是,这涉及复制数组,我将来可以解决该数组:

MacroinputStream.java

    package preprocessor;
    import org.antlr.v4.runtime.ANTLRInputStream;
    public class MacroInputStream extends ANTLRInputStream {
      private HashMap<String, String> map;
      public MacroInputStream(String s, HashMap<String, String> map) {
        super(s);
        this.map = map;
      }
      public void rewrite(int startIndex, int stopIndex, String replaceText) {
        int length = stopIndex-startIndex+1;
        char[] replData = replaceText.toCharArray();
        if (replData.length == length) {
          for (int i = 0; i < length; i++) data[startIndex+i] = replData[i];
        } else {
          char[] newData = new char[data.length+replData.length-length];
          System.arraycopy(data, 0, newData, 0, startIndex);
          System.arraycopy(replData, 0, newData, startIndex, replData.length);
          System.arraycopy(data, stopIndex+1, newData, startIndex+replData.length, data.length-(stopIndex+1));
          data = newData;
          n = data.length;
        }
      }
    }

其次,我扩展了Lexer,因此当遇到变量令牌时,上面的重写方法称为:

MACROGRAMMARLEXER.JAVA

package language;
import language.DSL_GrammarLexer;
import org.antlr.v4.runtime.Token;
import java.util.HashMap;
public class MacroGrammarLexer extends MacroGrammarLexer{
  private HashMap<String, String> map;
  public DSL_GrammarLexerPre(MacroInputStream input, HashMap<String, String> map) {
    super(input);
    this.map = map;
    // TODO Auto-generated constructor stub
  }
  private MacroInputStream getInput() {
    return (MacroInputStream) _input;
  }
  @Override
  public Token nextToken() {
    Token t = super.nextToken();
    if (t.getType() == VARIABLE) {
      System.out.println("Encountered token " + t.getText()+" ===> rewriting!!!");
      getInput().rewrite(t.getStartIndex(), t.getStopIndex(),
          map.get(t.getText().substring(1)));
      getInput().seek(t.getStartIndex()); // reset input stream to previous
      return super.nextToken();
    }
    return t;   
  }   
}

最后,我修改了生成的解析器以在解析时设置变量:

dsl_grammarparser.java

    ...
    ...
    HashMap<String, String> map;  // same map as before, passed as a new argument.
    ...
    ...
public final SetContext set() throws RecognitionException {
  SetContext _localctx = new SetContext(_ctx, getState());
    enterRule(_localctx, 130, RULE_set);
    try {
        enterOuterAlt(_localctx, 1);
        {
        String vname = null; String vval = null;              // set up variables
        setState(1215); match(SET);
        setState(1216); vname = variable_name().getText();    // set vname
        setState(1217); match(EQUALS);
        setState(1218); vval = string_constant().getText();   // set vval
        System.out.println("Found SET " + vname +" = " + vval+";");
            map.put(vname, vval);
        }
    }
    catch (RecognitionException re) {
        _localctx.exception = re;
        _errHandler.reportError(this, re);
        _errHandler.recover(this, re);
    }
    finally {
        exitRule();
    }
    return _localctx;
}
    ...
    ...

不幸的是,此方法是final,因此这会使维护更加困难,但是现在可以使用。

处理您的要求的标准模式是实现符号表。最简单的形式是关键:Value Store。在您的访问者中,添加遇到的var声明,并在遇到VAR引用时读取值。

如所述,您的DSL并未在声明的变量上定义范围的范围。如果您确实需要示波器变量,请使用键的堆栈:值存储,在示波器条目和退出上推出并弹出。

请参阅此相关的stackoverflow答案。

单独,由于您的字符串可能包含命令,因此您可以简单地将内容分析为初始分析的一部分。也就是说,使用包含完整有效内容的规则扩展您的语法:

set_variable
   : 'SET' ID '=' stringLiteral ';'
   ;
stringLiteral: 
   Quote Quote? ( 
     (    set_variable
        | print_line
        | VARIABLE
        | ID
     )
     | STRING_CONSTANT  // redefine without the quotes
   )
   Quote
   ;

相关内容

  • 没有找到相关文章

最新更新