簡體   English   中英

使用OCaml解析語法

[英]Parsing grammars using OCaml

我有一個任務是使用OCaml為(玩具)語法編寫(玩具)解析器,而不確定如何啟動(並繼續)此問題。

這是一個awk語法示例:

type ('nonterm, 'term) symbol = N of 'nonterm | T of 'term;;

type awksub_nonterminals = Expr | Term | Lvalue | Incrop | Binop | Num;;

let awksub_grammar =
  (Expr,
   function
     | Expr ->
         [[N Term; N Binop; N Expr];
          [N Term]]
     | Term ->
     [[N Num];
      [N Lvalue];
      [N Incrop; N Lvalue];
      [N Lvalue; N Incrop];
      [T"("; N Expr; T")"]]
     | Lvalue ->
     [[T"$"; N Expr]]
     | Incrop ->
     [[T"++"];
      [T"--"]]
     | Binop ->
     [[T"+"];
      [T"-"]]
     | Num ->
     [[T"0"]; [T"1"]; [T"2"]; [T"3"]; [T"4"];
      [T"5"]; [T"6"]; [T"7"]; [T"8"]; [T"9"]]);;

這里有一些要解析的片段:

let frag1 = ["4"; "+"; "3"];;
let frag2 = ["9"; "+"; "$"; "1"; "+"];;

我正在尋找的是一個規則列表,它是解析一個片段的結果,例如這個片段用於frag1 [“4”; “+”; “3”]:

 [(Expr, [N Term; N Binop; N Expr]);
  (Term, [N Num]);
  (Num, [T "3"]);
  (Binop, [T "+"]);
  (Expr, [N Term]);
  (Term, [N Num]);
  (Num, [T "4"])]

限制是不使用除List ...之外的任何OCaml庫:/

這是一個粗略的草圖 - 直接進入語法並按順序嘗試每個分支。 可能的優化:分支中單個非終端的尾遞歸。

exception Backtrack

let parse l =
  let rules = snd awksub_grammar in
  let rec descend gram l =
    let rec loop = function 
      | [] -> raise Backtrack
      | x::xs -> try attempt x l with Backtrack -> loop xs
    in
    loop (rules gram)
  and attempt branch (path,tokens) =
    match branch, tokens with
    | T x :: branch' , h::tokens' when h = x -> 
        attempt branch' ((T x :: path),tokens')
    | N n :: branch' , _ -> 
        let (path',tokens) = descend n ((N n :: path),tokens) in 
        attempt branch' (path', tokens)
    | [], _ -> path,tokens
    | _, _ -> raise Backtrack
  in
  let (path,tail) = descend (fst awksub_grammar) ([],l) in
  tail, List.rev path

好吧,首先想你應該做的就是寫一個詞法分析器。 這就是“原始”輸入的功能,如["3"; "-"; "("; "4"; "+"; "2"; ")"] ["3"; "-"; "("; "4"; "+"; "2"; ")"] ["3"; "-"; "("; "4"; "+"; "2"; ")"] ,並將其拆分為令牌列表(即終端符號的表示)。

您可以定義令牌

type token =
    | TokInt of int         (* an integer *)
    | TokBinOp of binop     (* a binary operator *)
    | TokOParen             (* an opening parenthesis *) 
    | TokCParen             (* a closing parenthesis *)     
and binop = Plus | Minus 

lexer函數的類型是string list -> token list和輸出

lexer ["3"; "-"; "("; "4"; "+"; "2"; ")"]

會是這樣的

[   TokInt 3; TokBinOp Minus; TokOParen; TokInt 4;
    TBinOp Plus; TokInt 2; TokCParen   ]

這將使編寫解析器的工作更容易,因為您不必擔心識別什么是整數,什么是運算符等。

這是第一個,不是太困難的步驟,因為令牌已經分開。 lexer所要做的就是識別它們。

完成后,您可以編寫一個更實際的詞法分析器,類型為string -> token list ,它接受實際的原始輸入,例如"3-(4+2)"並將其轉換為令牌列表。

我不確定您是否特別需要派生樹,或者這只是解析的第一步。 我假設后者。

您可以通過定義類型來定義生成的抽象語法樹的結構。 它可能是這樣的:

type expr =
    | Operation of term * binop * term
    | Term of term
and term =
    | Num of num
    | Lvalue of expr
    | Incrop of incrop * expression
and incrop = Incr | Decr
and binop = Plus | Minus
and num = int

然后我將實現一個遞歸下降解析器。 當然,如果你可以使用與預處理器camlp4of結合使用的streams那將會更好。

順便說一句,有大約OCaml的文檔中的算術表達式一個小例子在這里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM