parsing - 如何从头开始编写递归下降解析器?

标签 parsing f#

作为纯粹的学术练习,我正在从头开始编写递归下降解析器——不使用 ANTLR 或 lex/yacc。

我正在编写一个简单的函数,它将数学表达式转换为它们等效的 AST。我有以下几点:

// grammar
type expr =
    | Lit of float
    | Add of expr * expr
    | Mul of expr * expr
    | Div of expr * expr
    | Sub of expr * expr

// tokens
type tokens =
    | Num of float
    | LParen | RParen
    | XPlus | XStar | XMinus | XSlash

let tokenize (input : string) =
    Regex.Matches(input.Replace(" ", ""), "\d+|[+/*\-()]")
    |> Seq.cast<Match>
    |> Seq.map (fun x -> x.Value)
    |> Seq.map (function
        | "+" -> XPlus
        | "-" -> XMinus
        | "/" -> XSlash
        | "*" -> XStar
        | "(" -> LParen
        | ")" -> RParen
        | num -> Num(float num))
    |> Seq.to_list

所以,tokenize "10 * (4 + 5) - 1"返回以下 token 流:
[Num 10.0; XStar; LParen; Num 4.0; XPlus; Num 5.0; RParen; XMinus; Num 1.0]

此时,我想根据运算符优先级将 token 流映射到其 AST:
Sub(
    Mul(
        Lit 10.0
        ,Add(Lit 4.0, Lit 5.0)
       )
    ,Lit 1.0
   )

但是,我正在绘制一个空白。我从来没有从头开始编写解析器,我什至不知道原则上如何开始。

如何将 token 流转换为其代表的 AST?

最佳答案

你了解语言语法吗?

假设是,你有一个带有规则的语法

...
addTerm := mulTerm addOp addTerm
         | mulTerm

addOp   := XPlus | XMinus

mulTerm := litOrParen mulOp mulTerm
         | litOrParen
...

最终变成代码(在浏览器中编写代码,从未编译)
let rec AddTerm() =
    let mulTerm = MulTerm() // will parse next mul term (error if fails to parse)
    match TryAddOp with  // peek ahead in token stream to try parse
    | None -> mulTerm    // next token was not prefix for addOp rule, stop here
    | Some(ao) ->        // did parse an addOp
         let rhsMulTerm = MulTerm()
         match ao with
         | XPlus -> Add(mulTerm, rhsMulTerm)
         | XMinus -> Sub(mulTerm, rhsMulTerm)
and TryAddOp() =
    let next = tokens.Peek() 
    match next with
    | XPlus | XMinus ->
        tokens.ConsumeNext()
        Some(next)
    | _ -> None
...

希望你看到基本的想法。这假设了一个全局可变 token 流,它允许“查看下一个 token ”和“使用下一个 token ”。

关于parsing - 如何从头开始编写递归下降解析器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1639888/

相关文章:

C# 读取和写入特定的 CSS 类

python - Pandas:将 CSV 中的 x 轴日期映射到 y 轴

python - 用大括号解析文件

.net - F# 绑定(bind)重定向不适用于 F# 4.3.0-4.3.1

iis - 使用 HttpPlatformHandler 托管在 IIS 上的 Suave 应用程序关闭连接

java - 返回字符串数组代替字符串

c - 尝试解析文档并存储节点信息,不知道为什么会出现段错误

f# - 使用ParserResult

c# - 解构 C# 元组

F# 将函数应用于列表树的每个节点中的值