c# – 用手编写解析器的最好方法是什么?

我们使用ANTLR创建一个类似SQL的语法的解析器,而在大多数情况下,结果令人满意,我们需要修复一些边缘案例;因为我们自己没有写解析器,所以我们并没有真正理解它,以便能够做出明智的改变。

所以,我们想编写自己的解析器。手工编写解析器的最好方法是什么?我们应该使用什么样的解析器 – 推荐递归下降;是对的吗?我们将在C#中编写它,所以用这种语言编写解析器的任何教程都将被感激地收到。

更新:我也对涉及F#的答案感兴趣 – 我一直在寻找一个在项目中使用它的原因。

最佳答案
我强烈推荐F#语言作为您在.NET平台上解析的首选语言。它是ML系列语言的根源,意味着它极大地支持面向语言的编程。

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

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

在第二行,作为示例,stmt和ch是解析器,sepBy1是一个单一解析器组合器,它使用两个简单的解析器并返回组合解析器。在这种情况下,sepBy1 p sep返回一个解析器,它解析由sep分隔的一个或多个出现的p。因此,您可以看到一个强大的解析器可以从简单的解析器中组合起来。 F#对覆盖操作符的支持也允许简洁的中缀符号,例如排序组合器和选择组合器可以被指定为>>。和< |。 最好的运气, 丹尼

转载注明原文:c# – 用手编写解析器的最好方法是什么? - 代码日志