具有条件.NET的语言分析器

时间:2011-07-05 07:21:00

标签: .net parsing

我正在寻找一种可以解决如下内容的语言解析器:

(x=7 OR y=1) AND (x>0)

我正在考虑使用ANTLR Parser Generator。在更高级的.NET框架(.NET 3.5,.NET 4.0)中是否有更简单的语言分析器?

4 个答案:

答案 0 :(得分:4)

如果问题只是涉及monadic和binary运算符,括号,变量值和常量操作数的表达式,则可以编写一个递归下降解析器/求值器,它将同时解析和计算表达式。无需建造树木或IL或......

[如果你需要编写更复杂的语法,如语句和方法,你需要一个更复杂的解析器,然后解析器生成器得到回报]

对于一个表达式,您可以直接从BNF为表达式编写递归下降解析器。确保你 第一个左因子是每个规则的共性,例如,不是

 SUM = TERM '+' TERM ;
 SUM = TERM '-' TERM ;

 SUM = TERM ( '+' TERM |  '-' TERM ) ;

对于每个左侧非终结符,创建一个(可能是递归的)子例程,将索引放入要解析的字符串中,返回表达式的值(假设为float),或者抛出(语法)错误。对于每个右侧令牌,您编写测试代码;如果令牌不存在,它将控制传递给替代,或者如果没有其他替代方法则抛出语法错误。如果令牌存在,则评估令牌:如果终值(例如,数字或可变量)获得该值并推进输入;如果是非终结符,则调用它以查看是否存在该语法;如果一个操作员,只是忽略它但推进输入。如果您的测试与右侧的所有元素匹配,请计算表达式结果并将其返回。

所以对于语法:

EXP = SUM ;
SUM = TERM ( '+' TERM |  '-' TERM ) ;
TERM = PRIMARY ( '*' PRIMARY | '/' PRIMARY ) ;
PRIMARY = '-' PRIMARY | '(' EXP ')' | NUMBER | VARIABLE ;

给定包含表达式的字符INPUT缓冲区, 和全局变量I是INPUT的索引,代码大致是:

float EXP()
{  return SUM();
}

float SUM()
{  float t=TERM();
   if MATCH("+") return t+TERM(); 
   if MATCH("-") return t-TERM();
   throw SYNTAXERROR;
}

float TERM()
{  float t= PRIMARY();
   if MATCH("*") return t*PRIMARY();
   if MATCH("/") return t/PRIMARY();
   throw SYNTAXERROR;
}

float PRIMARY()
{  float t;
   if MATCH("-") return -PRIMARY();
   if MATCH("(")
       { t=EXP();
         if MATCH(")") return t;
         else throw SYNTAXERROR
       }
   try t=NUMBER();
   catch SYNTAXERROR
      return VARIABLE();
   endtry
}

float NUMBER()  // simple float input conversion
{ float t=0;
  fractiondigits=0;
  exponent=0;
  switch INPUT(I)
  {  case "0".."9":
     { t=t*10+INPUT(I)-"0"; I++;
       while ISDIGIT(INPUT(I))
          { t=t*10+INPUT(I)-"0"; I++ }
       if MATCH(".")
          goto collect_fraction;
       else goto collect_exponent
      }
     case ".": goto collect_fraction;
     default: throw SYNTAXERROR
  }
  collect_fraction:
     while ISDIGIT(INPUT(I))
       { t=t*10+INPUT(I)-"0"; I++; fraction_digits++; }
  collect_exponent:
     if MATCH("E")
       { sign=false;
         if MATCH("-") sign=true;
         if !ISDIGIT(INPUT(I)) throw SYNTAXERROR;
         while ISDIGIT(INPUT(I))
           { exponent=exponent*10+INPUT(I)-"0"; I++; }
         if sign=true then exponenent=-exponent;
       }
       return t*10^(exponent-fractiondigits);
}

float VARIABLE() // handles single letter variable names.
{  if ISLETTER(INPUT(I))
   { I++;
     return VARIABLEVALUE[INPUT(I)-"A"]
   }
   else throw SYNTAXERROR
}

 boolean MATCH(c: char)
 {  if INPUT(I)==c
       {  I++;
          return true;
       }
    else return false

您显然希望为要评估的表达式编写语法。但假设您只添加关系和AND,OR和NOT运算符,遵循此样式,您需要大约30分钟来编写整个代码。

这不考虑输入表达式如何被收集到INPUT缓冲区中,也没有解决变量如何得到值的问题;我假设变量名称到值的映射已经提前以某种方式神奇地填充。如果你想允许简单的赋值和表达式,只需通过添加一个允许赋值的规则来扩展BNF,例如,

EXP = VARIABLE ':=' EXP ;

处理分配需要一些技巧:当您匹配各个部分并发现VARIABLE时,您需要一种方法来捕获变量名称(修改VARAIBLE以记住全局变量名称),以及已识别分配规则的语法,将变量名称的映射更新为已收集的值。

浮点输入代码是一个hack,并且可以产生稍微不正确的输入值(但它很容易编码:)如果你想要更精确的浮点输入转换,你应该简单地收集构成浮点数常量的字符,并且然后将它们交给库字符串到浮点转换例程。

答案 1 :(得分:0)

如果您只需要像示例中那样的简单表达式,则可以使用NCalc。快速和容易使用。

答案 2 :(得分:0)

您可以在示例中查看Irony和示例表达式评估程序。

here's a good article开始(如果你知道定义语法的基础知识,你会很快准备好):

答案 3 :(得分:-1)

对于给出的简单表达式,我会写一个递归下降解析器。首先写出你的BNF,然后编写代码。您可以在评估方面采用以下三种方法之一:

  1. 发表词汇树
  2. Emit IL
  3. 滚动您自己的字节码VM
  4. 我选择(1),因为这是最简单的方法。