如何编写解析器(递归下降?)在C#?现在,我只需要一个简单的解析器来解析算术表达式(并读取变量?)。尽管稍后我打算编写一个xml和html解析器(用于学习目的)。我之所以这样做,是因为解析器在很多方面都很有用:网页开发、编程语言解释器、内部工具、游戏引擎、地图和图块编辑器等。那么,编写解析器的基本理论是什么?我如何在C#中实现解析器?C#是解析器的合适语言吗(我曾经用C++编写了一个简单的算术解析器,它非常高效。JIT编译会证明同样好吗?)。任何有用的资源和文章。最重要的是,代码示例(或代码示例的链接)。
注意:出于好奇,回答这个问题的人有没有用C#实现过解析器?
发布于 2011-09-11 17:42:23
我用C#实现了几个解析器--手写的和工具生成的。
Let's Build a Compiler是一个很好的语法分析入门教程,它演示了如何构建递归下降解析器;对于任何有能力的开发人员来说,这些概念都可以很容易地从他的语言(我认为是Pascal)转换成C#。这将教你递归下降解析器是如何工作的,但是手工编写一个完整的编程语言解析器是完全不切实际的。
如果您决心编写一个classical recursive descent parser (TinyPG、Coco/R、Irony),那么您应该研究一些工具来为您生成代码。请记住,现在还有其他方法可以编写解析器,这些方法通常性能更好,定义也更简单(例如TDOP parsing或Monadic Parsing)。
关于C#是否能胜任这项任务-- C#有一些最好的文本库。今天很多解析器(在其他语言中)都有大量的代码来处理Unicode等等。我不会对JITted代码做太多的评论,因为它可能会变得很有宗教色彩--不过你应该没问题。IronJS是CLR上解析器/运行时的一个很好的例子(尽管它是用F#编写的),它的性能与谷歌V8相差无几。
旁注:与语言解析器相比,标记解析器是完全不同的东西-在大多数情况下,它们都是手工编写的-并且在扫描器/解析器级别非常简单;它们通常不是递归下降的-特别是在XML的情况下,最好不要编写递归下降解析器(为了避免堆栈溢出,因为“平面”解析器可以在SAX/push模式下使用)。
发布于 2012-11-05 20:34:27
Sprache是一个用.NET编写解析器的强大而轻量级的框架,还有一个Sprache NuGet package。为了让您对该框架有一个概念,这里是一个可以将简单的算术表达式解析为.NET表达式树的samples。我想说这真是太神奇了。
using System;
using System.Linq.Expressions;
using Sprache;
namespace LinqyCalculator
{
static class ExpressionParser
{
public static Expression<Func<decimal>> ParseExpression(string text)
{
return Lambda.Parse(text);
}
static Parser<ExpressionType> Operator(string op, ExpressionType opType)
{
return Parse.String(op).Token().Return(opType);
}
static readonly Parser<ExpressionType> Add = Operator("+", ExpressionType.AddChecked);
static readonly Parser<ExpressionType> Subtract = Operator("-", ExpressionType.SubtractChecked);
static readonly Parser<ExpressionType> Multiply = Operator("*", ExpressionType.MultiplyChecked);
static readonly Parser<ExpressionType> Divide = Operator("/", ExpressionType.Divide);
static readonly Parser<Expression> Constant =
(from d in Parse.Decimal.Token()
select (Expression)Expression.Constant(decimal.Parse(d))).Named("number");
static readonly Parser<Expression> Factor =
((from lparen in Parse.Char('(')
from expr in Parse.Ref(() => Expr)
from rparen in Parse.Char(')')
select expr).Named("expression")
.XOr(Constant)).Token();
static readonly Parser<Expression> Term = Parse.ChainOperator(Multiply.Or(Divide), Factor, Expression.MakeBinary);
static readonly Parser<Expression> Expr = Parse.ChainOperator(Add.Or(Subtract), Term, Expression.MakeBinary);
static readonly Parser<Expression<Func<decimal>>> Lambda =
Expr.End().Select(body => Expression.Lambda<Func<decimal>>(body));
}
}
发布于 2011-09-12 02:55:58
C#几乎是一种不错的函数式语言,所以在其中实现类似Parsec的东西并不是什么大不了的事情。下面是如何做到这一点的一个例子:http://jparsec.codehaus.org/NParsec+Tutorial
也可以用非常类似的方式实现一个基于组合子的Packrat,但这一次将全局解析状态保留在某个地方,而不是执行纯函数操作。在我的(非常基本的和特别的)实现中,它相当快,但当然,像this这样的代码生成器必须执行得更好。
https://stackoverflow.com/questions/7377344
复制相似问题