2009-10-28 71 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 

然後您可以通過另一個遞歸下降到樹計算表達式的程序運行完整的樹,直到您有答案。如果您的解析器不理解樹,則會出現語法錯誤。希望有所幫助。