2009-10-28 63 views
12

作为一个纯粹的学术练习,我从头开始编写递归下降解析器 - 不使用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"返回以下标记流:

[Num 10.0; XStar; LParen; Num 4.0; XPlus; Num 5.0; RParen; XMinus; Num 1.0] 

在这一点上,我想标流其AST相对于运算符优先级映射:

Sub(
    Mul(
     Lit 10.0 
     ,Add(Lit 4.0, Lit 5.0) 
     ) 
    ,Lit 1.0 
    ) 

但是,我画了一个空白。我从来没有从头开始编写解析器,甚至在原则上也不知道如何开始。

如何将标记流转换为其代表AST?

+4

真是巧合!我只是在F#中创建项目来编写解析器!最终参考=龙书。 – 2009-10-28 20:25:53

+0

请参阅http://stackoverflow.com/a/2336769/120163 – 2015-11-30 14:44:35

回答

8

你知道语言语法吗?

假设是,你必须沿着这最终变成这样的代码(在浏览器中编写代码,从来没有编译)

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 
... 

希望你看到的线条

... 
addTerm := mulTerm addOp addTerm 
     | mulTerm 

addOp := XPlus | XMinus 

mulTerm := litOrParen mulOp mulTerm 
     | litOrParen 
... 

与规则语法基本的想法。这假设一个全局可变的标记流,允许'偷看下一个标记'和'消耗下一个标记'。

+2

+1。还要确保语法不是*左递归。 – 2009-10-28 20:51:44

0

如果我从大专班还记得当时的想法是建立表达式树,如:

<program> --> <expression> <op> <expression> | <expression> 
<expression> --> (<expression>) | <constant> 
<op> --> * | - | + |/
<constant> --> <constant><constant> | [0-9] 

那么一旦你有建设你的树完全所以你喜欢的东西:

  exp 
     exp op  exp 
    5  +  and so on 

然后您可以通过另一个递归下降到树计算表达式的程序运行完整的树,直到您有答案。如果您的解析器不理解树,则会出现语法错误。希望有所帮助。