代码之家  ›  专栏  ›  技术社区  ›  arturh

扩展简单ANTLR语法以支持输入变量

  •  12
  • arturh  · 技术社区  · 16 年前

    我还在继续 my quest for a really simple language 我现在知道没有了。所以我自己用ANTLR3写了一个。

    this answer :

    示例g:

    grammar Exp;
    
    eval returns [double value]
        :    exp=additionExp {$value = $exp.value;}
        ;
    
    additionExp returns [double value]
        :    m1=multiplyExp       {$value =  $m1.value;} 
             ( '+' m2=multiplyExp {$value += $m2.value;} 
             | '-' m2=multiplyExp {$value -= $m2.value;}
             )* 
        ;
    
    multiplyExp returns [double value]
        :    a1=atomExp       {$value =  $a1.value;}
             ( '*' a2=atomExp {$value *= $a2.value;} 
             | '/' a2=atomExp {$value /= $a2.value;}
             )* 
        ;
    
    atomExp returns [double value]
        :    n=Number                {$value = Double.parseDouble($n.text);}
        |    '(' exp=additionExp ')' {$value = $exp.value;}
        ;
    
    Number
        :    ('0'..'9')+ ('.' ('0'..'9')+)?
        ;
    
    WS  
        :   (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;}
        ;
    

    public Double evaluate(String string, Map<String, Double> input) throws RecognitionException {
        ANTLRStringStream in = new ANTLRStringStream(string);
        ExpLexer lexer = new ExpLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        return new ExpParser(tokens).eval();
    }
    

    使用这个ANTLR语法,我可以计算如下表达式

    (12+14)/2
    

    (A+B)/2
    

    有什么想法吗?

    2 回复  |  直到 7 年前
        1
  •  21
  •   Bart Kiers    16 年前

    你可以创建一个 Map<String, Double> memory Identifier 在你的语法中:

    Identifier
      :  ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*
      ;
    

    atomExp 解析器规则如下所示:

    atomExp returns [double value]
        :    n=Number                {$value = Double.parseDouble($n.text);}
        |    i=Identifier            {$value = memory.get($i.text);} // <- added!
        |    '(' exp=additionExp ')' {$value = $exp.value;}
        ;
    

    下面是一个小的(完整的)演示:

    grammar Exp;
    
    @parser::members {
    
      private java.util.HashMap<String, Double> memory = new java.util.HashMap<String, Double>();
    
      public static Double eval(String expression) throws Exception {
        return eval(expression, new java.util.HashMap<String, Double>()); 
      }
    
      public static Double eval(String expression, java.util.Map<String, Double> vars) throws Exception {
        ANTLRStringStream in = new ANTLRStringStream(expression);
        ExpLexer lexer = new ExpLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        ExpParser parser = new ExpParser(tokens);
        parser.memory.putAll(vars);
        return parser.parse(); 
      }
    }
    
    parse returns [double value]
        :    exp=additionExp {$value = $exp.value;}
        ;
    
    additionExp returns [double value]
        :    m1=multiplyExp      {$value =  $m1.value;} 
            ( '+' m2=multiplyExp {$value += $m2.value;} 
            | '-' m2=multiplyExp {$value -= $m2.value;}
            )*  
        ;
    
    multiplyExp returns [double value]
        :   a1=atomExp       {$value =  $a1.value;}
            ( '*' a2=atomExp {$value *= $a2.value;} 
            | '/' a2=atomExp {$value /= $a2.value;}
            )*  
        ;
    
    atomExp returns [double value]
        :    n=Number                {$value = Double.parseDouble($n.text);}
        |    i=Identifier            {$value = memory.get($i.text);}
        |    '(' exp=additionExp ')' {$value = $exp.value;}
        ;
    
    Identifier
        :    ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*
        ;
    
    Number
        :    ('0'..'9')+ ('.' ('0'..'9')+)?
        ;
    
    WS  
        :   (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;}
        ;
    

    import org.antlr.runtime.*;
    import java.util.*;
    
    public class ANTLRDemo {
        public static void main(String[] args) throws Exception {
            Map<String, Double> vars = new HashMap<String, Double>();
            vars.put("two", 2.0);
            vars.put("pi", Math.PI);
            System.out.println(ExpParser.eval("two * pi", vars));
        }
    }
    

    这将产生:

    6.283185307179586
    

    祝你好运

        2
  •  8
  •   Darien Ford    16 年前

    在下面的语法中,我实现了您希望执行的变量赋值格式。

    grammar Exp;
    
    
    
    eval returns [double value]
    scope
    {
        java.util.Hashtable varMap; 
    }
    @init
    {
        $eval::varMap = new java.util.Hashtable();
    }
    :    exp=additionExp {$value = $exp.value;}
        | varList
    ;
    
    additionExp returns [double value]
        :    m1=multiplyExp       {$value =  $m1.value;} 
             ( '+' m2=multiplyExp {$value += $m2.value;} 
             | '-' m2=multiplyExp {$value -= $m2.value;}
             )* 
        ;
    
    multiplyExp returns [double value]
        :    a1=atomExp       {$value =  $a1.value;}
             ( '*' a2=atomExp {$value *= $a2.value;} 
             | '/' a2=atomExp {$value /= $a2.value;}
             )* 
        ;
    
    atomExp returns [double value]
        :    n=Number                {$value = Double.parseDouble($n.text);}
        |    v=ID            {$value = $eval::varMap.get($v);}
        |    '(' exp=additionExp ')' {$value = $exp.value;}
        ;
    
    varList
        :   
        OPEN_BRACE assignVar (COMMA assignVar)+ CLOSE_BRACE
        ;
    
    assignVar
        :QUOTE var=ID n=Number QUOTE COLON { $eval::varMap.put($var, $n); }
        ;
    
    
    Number
        :    ('0'..'9')+ ('.' ('0'..'9')+)?
        ;
    
    WS  
        :   (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;}
        ;
    
    
    
    fragment LETTER: LOWER | UPPER;
    fragment LOWER: 'a'..'z';
    fragment UPPER: 'A'..'Z';
    
    OPEN_BRACE
        :   '{'
        ;
    
    CLOSE_BRACE
        :   '}'
        ;
    
    COLON   : ';';
    COMMA   :   ',';
    
    QUOTE   :   '"';
    
    ID
    : LETTER*;