在C#中实现自顶向下解析器

时间:2011-11-11 17:02:08

标签: c# artificial-intelligence nlp

我是学生,我想在我使用c#语言开发的语言翻译项目中实现自上而下的解析器。例如,如果我需要为句子“我的名字是Husni而我是学生”构建一个解析器树,我怎么能用C#语言来做。

4 个答案:

答案 0 :(得分:3)

我强烈推荐这本书:

Basics of Compiler Design

您可以免费下载PDF。它涵盖了全面解析(自上而下和自下而上),而不会对您的背景做太多假设。

非常好读。

至于如何在C#中做到这一点?就像使用C#语法一样,你可以用任何其他语言完成它。学习理论,代码自然而然。

答案 1 :(得分:0)

在本书之后,您还可以找到有趣的阅读编译器生成器ANTLR,它可以帮助您编写编译器(也在C#中)并在视觉上浏览AST。

答案 2 :(得分:0)

您安装Antlr:http://www.antlr.org/

一个很棒的编译器构造工具,可以从正式的语法规范中生成自上而下的递归下降解析器。

你得到了Terrance Parr的书籍副本:

另一个选择是Irony.Net:http://irony.codeplex.com/

反讽是一个用于在.NET平台上实现语言的开发工具包。与大多数现有的yacc / lex风格解决方案不同,Irony不会使用以专门的元语言编写的语法规范生成任何扫描程序或解析器代码。在Irony中,目标语言语法使用运算符重载直接在c#中编码,以表达语法结构。 Irony的扫描程序和解析器模块使用编码为c#类的语法来控制解析过程。

以下是用于解析算术表达式的Irony表达式语法示例:

using System;
using System.Collections.Generic;
using System.Text;
using Irony.Parsing;
using Irony.Ast;

namespace Irony.Samples
{
  // This grammar describes programs that consist of simple expressions and assignments
  // for ex:
  // x = 3
  // y = -x + 5
  //  the result of calculation is the result of last expression or assignment.
  //  Irony's default  runtime provides expression evaluation. 
  //  supports inc/dec operators (++,--), both prefix and postfix,
  //  and combined assignment operators like +=, -=, etc.

  [Language("ExpressionEvaluator", "1.0", "Multi-line expression evaluator")]
  public class ExpressionEvaluatorGrammar : Irony.Parsing.Grammar
  {

    public ExpressionEvaluatorGrammar()
    {

      // 1. Terminals
      var number = new NumberLiteral("number");

      //Let's allow big integers (with unlimited number of digits):
      number.DefaultIntTypes = new TypeCode[] { TypeCode.Int32, TypeCode.Int64, NumberLiteral.TypeCodeBigInt };
      var identifier         = new IdentifierTerminal("identifier");
      var comment            = new CommentTerminal("comment", "#", "\n", "\r"); 

      //comment must to be added to NonGrammarTerminals list; it is not used directly in grammar rules,
      // so we add it to this list to let Scanner know that it is also a valid terminal. 
      base.NonGrammarTerminals.Add(comment);

      // 2. Non-terminals
      var Expr           = new NonTerminal("Expr");
      var Term           = new NonTerminal("Term");
      var BinExpr        = new NonTerminal("BinExpr", typeof(BinExprNode));
      var ParExpr        = new NonTerminal("ParExpr");
      var UnExpr         = new NonTerminal("UnExpr", typeof(UnExprNode));
      var UnOp           = new NonTerminal("UnOp");
      var BinOp          = new NonTerminal("BinOp", "operator");
      var PostFixExpr    = new NonTerminal("PostFixExpr", typeof(UnExprNode));
      var PostFixOp      = new NonTerminal("PostFixOp");
      var AssignmentStmt = new NonTerminal("AssignmentStmt", typeof(AssigmentNode));
      var AssignmentOp   = new NonTerminal("AssignmentOp", "assignment operator");
      var Statement      = new NonTerminal("Statement");
      var ProgramLine    = new NonTerminal("ProgramLine");
      var Program        = new NonTerminal("Program", typeof(StatementListNode));

      // 3. BNF rules
      Expr.Rule           = Term | UnExpr | BinExpr | PostFixExpr;
      Term.Rule           = number | ParExpr | identifier;
      ParExpr.Rule        = "(" + Expr + ")";
      UnExpr.Rule         = UnOp + Term;
      UnOp.Rule           = ToTerm("+") | "-" | "++" | "--";
      BinExpr.Rule        = Expr + BinOp + Expr;
      BinOp.Rule          = ToTerm("+") | "-" | "*" | "/" | "**";
      PostFixExpr.Rule    = Term + PostFixOp;
      PostFixOp.Rule      = ToTerm("++") | "--";
      AssignmentStmt.Rule = identifier + AssignmentOp + Expr;
      AssignmentOp.Rule   = ToTerm("=") | "+=" | "-=" | "*=" | "/=";
      Statement.Rule      = AssignmentStmt | Expr | Empty;
      ProgramLine.Rule    = Statement + NewLine;
      Program.Rule        = MakeStarRule(Program, ProgramLine);
      this.Root           = Program;       // Set grammar root

      // 4. Operators precedence
      RegisterOperators(1, "+", "-");
      RegisterOperators(2, "*", "/");
      RegisterOperators(3, Associativity.Right, "**");

      // 5. Punctuation and transient terms
      RegisterPunctuation("(", ")");
      RegisterBracePair("(", ")"); 
      MarkTransient(Term, Expr, Statement, BinOp, UnOp, PostFixOp, AssignmentOp, ProgramLine, ParExpr);

      //automatically add NewLine before EOF so that our BNF rules work correctly when there's no final line break in source
      this.LanguageFlags = LanguageFlags.CreateAst | LanguageFlags.NewLineBeforeEOF | LanguageFlags.CanRunSample; 

    }

  }

}//namespace

第三种选择是使用类似NParsec的东西,Haskell的Parsec的C#端口(monadic解析器组合器 - 在C#中,基本上使用Linq编写解析器):http://www.haskell.org/haskellwiki/Parsec#Parsec_clones_in_other_languages,或其他类似的库,如Rx Parser :http://rxx.codeplex.com/wikipage?title=Parsers

有关monadic解析器组合的更多信息:

答案 3 :(得分:0)

您正在尝试解析一种含糊不清的自然语言。这意味着您的解析器将允许句子的多个解析树。这就是为什么我不认为像ANTLR这样的常规语言设计工具会有所帮助。

我正在使用PEP自上而下的动态CFG解析器。它是用Java编写的。将它移植到c#比从头开始编写新文件要容易。