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

发布于 2024-08-17 06:58:09 字数 1767 浏览 6 评论 0原文

我仍在我的追求一种非常简单的语言,但我现在知道不存在这种语言。所以我自己用 ANTLR3 写了一个。

我在这个答案中找到了一个非常好的例子:

Exp.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;}
    ;

Java 代码:

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

并得到 13 作为结果。

现在,我的用例唯一缺少的是一种将简单双变量注入其中的方法,以便我可以通过提供 {"A": 12.0, "B":14.0} 作为输入映射来评估以下内容:

(A+B)/2

任何想法?

I'm still on my quest for a really simple language and I know now that there are none. So I'm writing one myself using ANTLR3.

I found a really great example in this answer:

Exp.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;}
    ;

Java Code:

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

Using this ANTLR grammer I can evaluate expressions like

(12+14)/2

and get 13 as a result.

Now the only thing missing for my use-case is a way to inject simple double variables into this, so that I can evaluate the following by supplying {"A": 12.0, "B":14.0} as the input map:

(A+B)/2

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

眼泪都笑了 2024-08-24 06:58:09

您可以创建一个 Map在你的解析器中添加一个 Identifier 并在你的语法中引入一个 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

祝你好运!

You could create a Map<String, Double> memory in your parser and introduce a Identifier in your grammar:

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

Then your atomExp parser rule would look like this:

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

Here's a small (complete) demo:

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;}
    ;

And now theres no need to instantiate the parser/lexer yourself, you can simply do:

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));
    }
}

which will produce:

6.283185307179586

Good luck!

緦唸λ蓇 2024-08-24 06:58:09

啊,花时间来实现这个,所以不妨发布它,即使我被击败了:)

在下面的语法中,我已经实现了您想要做的变量分配的格式。

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*;

Bah, took the time to implement this so might as well post it, even though I was beat to the punch :)

In the grammar below I have implemented the format of variable assigning you were looking to do.

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*;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文