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

标签 c# .net parsing f#

<分区>

我们已经使用 ANTLR 为类似 SQL 的语法创建了一个解析器,虽然在大多数情况下结果令人满意,但我们需要修复一些边缘情况;由于我们没有自己编写解析器,因此我们对它的理解还不够深入,无法做出明智的更改。

所以,我们想编写自己的解析器。手动编写解析器的最佳方法是什么?我们应该使用什么样的解析器——推荐使用递归下降;那正确吗?我们将使用 C# 编写它,因此我们将不胜感激地收到任何使用该语言编写解析器的教程。

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

最佳答案

我强烈推荐 F# 语言作为在 .NET 平台上进行解析的首选语言。它起源于 ML 语言家族,这意味着它对面向语言的编程提供了出色的支持。

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

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是一个单子(monad)解析器组合器,它接受两个简单的解析器并返回一个组合解析器。在这种情况下 sepBy1 p sep返回一个解析器,该解析器解析一次或多次出现的 psep分隔.因此,您可以看到从简单的解析器中组合出强大的解析器的速度有多快。 F# 对重写运算符的支持还允许使用简洁的中缀表示法,例如排序组合器和选择组合器可以指定为 >>.<|> .

祝你好运

丹尼

关于c# - 手动编写解析器的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/531707/

相关文章:

c# - 使用本地数据库为 winForms 创建安装程序

java - Android 解析 xml 文件时出错

c# - LINQ:根据列值选择重复行

c# - 将参数传递给 Postman 中的 asmx 网络服务

c# - 函数数组与开关的性能

c# - 在 WPF (MVVM) 中使模型静态化是一种好习惯吗?

c# - 如何为嵌套集合编写 FluentAssertion,与顺序无关?

c# - 单击 gridview 列标题整个 gridview 试图覆盖窗口

c - 解析 int 变量

javascript - 如何从 JSON 对象中的键中获取值?