当前位置:  开发笔记 > 编程语言 > 正文

手动编写解析器的最佳方法是什么?

如何解决《手动编写解析器的最佳方法是什么?》经验,为你挑选了5个好方法。

我们使用ANTLR为类似SQL的语法创建解析器,虽然在大多数情况下结果令人满意,但我们需要修复一些边缘情况.因为我们自己没有编写解析器,所以我们并不能很好地理解它,以便能够做出明智的改变.

所以,我们想编写自己的解析器.手动编写解析器的最佳方法是什么?我们应该使用什么样的解析器 - 建议使用递归下降; 是对的吗?我们将用C#编写它,所以任何用该语言编写解析器的教程都会感激不尽.

更新:我也对涉及F#的答案感兴趣 - 我一直在寻找在项目中使用它的理由.



1> Daniel Asher..:

我强烈建议使用F#语言作为在.NET平台上解析的首选语言.ML系列语言的根源意味着它对面向语言的编程提供了出色的支持.

歧视的联合和模式匹配允许您的AST非常简洁和强大的规范.高阶函数允许定义解析操作及其组成.对monadic类型的一流支持允许隐式处理状态管理,大大简化了解析器的组成.强大的类型推断极大地帮助了这些(复杂)类型的定义.所有这些都可以通过交互方式指定和执行,从而快速进行原型设计.

Stephan Tolksdorf通过他的解析器组合库FParsec将其付诸实践

从他的例子我们看到AST的指定自然:

type expr =
    | Val of string
    | Int of int
    | Float of float
    | Decr of expr

type stmt =
    | Assign of string * expr
    | While of expr * stmt
    | Seq of stmt list
    | IfThen of expr * stmt
    | IfThenElse of expr * stmt * stmt
    | Print of expr

type prog = Prog of stmt list

解析器的实现(部分省略)同样简洁:

let stmt, stmtRef = createParserForwardedToRef()

let stmtList = sepBy1 stmt (ch ';')

let assign =
    pipe2 id (str ":=" >>. expr) (fun id e -> Assign(id, e))

let print = str "print" >>. expr |>> Print

let pwhile =
    pipe2 (str "while" >>. expr) (str "do" >>. stmt) (fun e s -> While(e, s))

let seq =
    str "begin" >>. stmtList .>> str "end" |>> Seq

let ifthen =
    pipe3 (str "if" >>. expr) (str "then" >>. stmt) (opt (str "else" >>. stmt))
          (fun e s1 optS2 ->
               match optS2 with
               | None    -> IfThen(e, s1)
               | Some s2 -> IfThenElse(e, s1, s2))

do stmtRef:= choice [ifthen; pwhile; seq; print; assign]


let prog =
    ws >>. stmtList .>> eof |>> Prog

在第二行,作为一个例子,stmtch是解析器,sepBy1是一个monadic解析器组合器,它接受两个简单的解析器并返回一个组合解析器.在这种情况下,sepBy1 p sep返回一个解析器,该解析器解析一个或多个出现的p分隔符sep.因此,您可以看到可以从简单的解析器中快速组合强大的解析器.F#对重写的运算符的支持也允许简洁的中缀表示法,例如序列组合器和选择组合子可以指定为>>.<|>.

祝你好运,

丹尼



2> Anton Gogole..:

可以由理智的人类手写的唯一一种解析器是递归下降.也就是说,手写自下而上的解析器仍然是可能的,但这是非常不可取的.

如果您正在使用RD解析器,则必须验证SQL语法不是左递归的(并在必要时消除递归),然后基本上为每个语法规则编写一个函数.有待进一步参考,请参阅此



3> Spence..:

递归下降会给你最简单的方法,但我不得不同意mouviciel那种flex和bison,绝对值得学习.当你发现你的语法有错误时,在flex/bison中修复语言的定义将比重写你的递归下降代码容易得多.

仅供参考,C#解析器是递归下降的,它往往非常健壮.


一个更容易的地方是夸张.递归下降解析器中的方法是语法(一旦识别出来就很简单)......

4> Mike Dunlave..:

将我的声音添加到合唱中,支持递归下降(LL1).它们简单,快速,而且IMO,完全不难维护.

但是,请仔细查看您的语言以确保它是LL1.如果你有像C这样的语法,比如((((type))foo)[])你可能需要先删除多层括号,然后才能发现你是在查看类型,变量还是表达式, LL1将非常困难,自下而上获胜.


正确的做法是将语言更改为LL1

5> Joe Soul-bri..:

递归下降解析器确实是最好的,也许是唯一的,可以手动构建的解析器.您仍然需要了解正式的,无上下文的语言,并将您的语言置于正常形式.我个人建议您删除左递归并将您的语言放在Greibach Normal Form中.当你这样做时,解析器只是写自己.

例如,这个生产:

A => aC 
A => bD
A => eF

变成简单的东西:

int A() {
   chr = read();
   switch char
     case 'a': C();
     case 'b': D();
     case 'e': F();
     default: throw_syntax_error('A', chr);
}

而且这里没有更难的案例(更难的是确保你的语法形式正确,但这可以让你控制你所提到的).

Anton's Link也很出色.

推荐阅读
女女的家_747
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有