Commits

camlspotter  committed 2be0de6

example

  • Participants
  • Parent commits 2025213

Comments (0)

Files changed (1)

 * Quote が使いづらいと思ったら Camlp4Ast のコンストラクタを生書きすればよい。
 * Quote や Anti-quote の展開がわからなかったら例題を作って camlp4of で実際にどうなるか確かめる
 
-
 =================================
 文法拡張
 =================================
 
 とこき下ろしたが、実のところ Camlp4OCamlParser.ml は最も複雑な P4 文法拡張なので、ルールの削除や追加の例はこのファイルを眺めるのが最適である。読むなよ!眺めるだけだ!
 
+=========================
+例題
+=========================
+
+君は Scheme か LCF ML 基地外なので let rec という OCaml 構文を見ていつも引っかかりを覚えていた。何故だ、何故 let rec と中央に空白があるのだ。letrec でなければいけないはずだ... ついに君は CamlP4 拡張を書き始めた。
+
+やるべきこと
+* letrec という文法ルールを入れる! let rec のコピペでいいはずだ。
+* let rec という文法ルールは潰す! 
+
+ステップ1 ルールを探す
+-----------------------
+
+let rec に関する P4 のルールを探そう。
+Camlp4OCamlParser.ml には、見当たらない。
+では Camlp4OCamlRevisedParser.ml か?あった::
+
+    opt_rec:
+      [ [ "rec" -> <:rec_flag< rec >>
+        | `ANTIQUOT ("rec"|"anti" as n) s -> Ast.ReAnt (mk_anti n s)
+        | -> <:rec_flag<>>
+      ] ]
+    ;
+
+これはどうやら rec という文字列があれば <:rec_flag< rec >> を返し、それ以外は <:rec_flag<>> を返すようだ。え? ANTIQUOT? 知るか。残念だがここは変える所ではないらしい。opt_rec を使っているルールを探そう...今度は Camlp4OCamlParser.ml にあった。(Camlp4OCamlRevisedParser.ml にある opt_rec を使うルールは Camlp4OCamlParser.ml で DELETE_RULE により抹消されているので気にする必要はない)::
+
+    str_item:
+      [ "top"
+          [ "let"; r = opt_rec; bi = binding; "in"; x = expr ->
+              <:str_item< let $rec:r$ $bi$ in $x$ >>
+          | "let"; r = opt_rec; bi = binding ->
+              match bi with
+              [ <:binding< _ = $e$ >> -> <:str_item< $exp:e$ >>
+              | _ -> <:str_item< value $rec:r$ $bi$ >> ]
+          | ...
+
+    expr: LEVEL "top"
+      [ [ "let"; r = opt_rec; bi = binding; "in";
+          x = expr LEVEL ";" ->
+            <:expr< let $rec:r$ $bi$ in $x$ >>
+        | ...
+
+ステップ2 ルールを消す
+-----------------------
+
+では、まずこの汚らわしい let rec ルールを消そう。消すのには DELETE_RULE。Camlp4OCamlParser.ml の例を参考にして...::
+
+    DELETE_RULE Gram str_item: "let"; opt_rec; binding; "in"; expr END;
+    DELETE_RULE Gram str_item: "let"; opt_rec; binding END;
+    DELETE_RULE Gram expr: "let"; opt_rec; binding; "in"; expr END;
+
+ステップ3 [重要] テストテストテスト
+----------------------------------------
+と君は書いてみた。君は基地外ではあるが慎重でもあるので、この時点でテンプレートにこの三行を書き込みテストするのを忘れない::
+
+    open Camlp4
+    
+    module Id : Sig.Id = struct
+      let name = "pa_letrec"
+      let version = "1.0"
+    end
+    
+    module Make (Syntax : Sig.Camlp4Syntax) = struct
+      open Sig
+      include Syntax
+      open Ast
+    
+      DELETE_RULE Gram str_item: "let"; opt_rec; binding; "in"; expr END;
+      DELETE_RULE Gram str_item: "let"; opt_rec; binding END;
+      DELETE_RULE Gram expr: "let"; opt_rec; binding; "in"; expr END;
+
+    end
+    
+    let module M = Register.OCamlSyntaxExtension(Id)(Make) in ()
+
+コンパイルしてみる::
+
+    ocamlc -pp camlp4of -I `ocamlc -where`/camlp4 -c pa_letrec.ml
+
+では実際に使ってみよう::
+
+    $ ocaml
+            OCaml version 4.00.0+dev22_2012-07-20+camlp4-lexer-plug+annot+p4-expand-directory+typeloc    <--- まあ気にするな
+    
+    # #load "dynlink.cma";;
+    # #load "camlp4of.cma";;
+    	Camlp4 Parsing version 4.00.0+dev22_2012-07-20+camlp4-lexer-plug+annot+p4-expand-directory+typeloc   <--- まあ気にするな
+    
+    # #load "pa_letrec.cmo";;
+    Fatal error: exception Not_found
+
+あれ?あれれれ?ナンデ?P4ナンデ?
+
+まあ極まった私から言わせてもらうと、 DELETE_RULE する際にマッチするルールが無かったんだろう。よく見てみると最後のルール間違っている::
+
+    DELETE_RULE Gram expr: "let"; opt_rec; binding; "in"; expr LEVEL ";" END;
+
+と、 LEVEL についても書かないといけないのだ。うーん、トリッキー。これで実行すると::
+
+    # #load "pa_letrec.cmo";;
+    # let rec f x = f x;;
+    Characters 0-3:
+    let rec f x = f x;;
+    ^^^
+    Error: Parse error: "module" or "open" expected after "let" (in [str_item])
+
+こうなる。なんとエラーメッセージを見なさい。 let の後は module か open しか来ないと言っている。rec なんかは絶対来ないのだ!素晴らしい!(というか普通の let x = 1 とかも消してしまったのだが。)
+
+ステップ4 ルールを追加する
+----------------------------
+
+さて、仕事は半分終わった。残りは letrec だ。これは消したルールを基に作れる。まず拡張し終わって出来たパースルール群がどうなるか考えよう::
+
+    str_item:
+      [ "top"
+          [ "let"; bi = binding; "in"; x = expr ->
+              <:str_item< let $bi$ in $x$ >>
+          | "letrec"; bi = binding; "in"; x = expr ->
+              <:str_item< let rec $bi$ in $x$ >>
+          | "let"; bi = binding ->
+              match bi with
+              [ <:binding< _ = $e$ >> -> <:str_item< $exp:e$ >>
+              | _ -> <:str_item< let $bi$ >> ]
+          | "letrec"; bi = binding ->
+              match bi with
+              [ <:binding< _ = $e$ >> -> <:str_item< $exp:e$ >>
+              | _ -> <:str_item< let rec $bi$ >> ]
+          | ...
+
+    expr: LEVEL "top"
+      [ [ "let"; bi = binding; "in";
+          x = expr LEVEL ";" ->
+            <:expr< let $bi$ in $x$ >>
+        | "letrec"; bi = binding; "in";
+          x = expr LEVEL ";" ->
+            <:expr< let rec $bi$ in $x$ >>
+        ...
+
+こんな感じになるはずだ。元ソースでは Revised syntax だったがこちらは Original に書き換えてある。Quote の中身ではバニラOCaml を書かねばならないから let rec と書かざるを得ないが…ぐぐぅ。そこは革命のためだ我慢せよ。
+
+さてこれを拡張ルールとして書くには::
+
+* str_item と expr をいじることを GLOBAL で宣言する
+* let(非再帰)と と letrec のケースを追加する
+
+せねばならない。こう書く::
+
+    GLOBAL: str_item expr;
+
+    str_item: LEVEL "top"
+      [   [ "let"; bi = binding; "in"; x = expr ->
+              <:str_item< let $bi$ in $x$ >>
+          | "letrec"; bi = binding; "in"; x = expr ->
+              <:str_item< let rec $bi$ in $x$ >>
+          | "let"; bi = binding ->
+              match bi with
+              [ <:binding< _ = $e$ >> -> <:str_item< $exp:e$ >>
+              | _ -> <:str_item< let $bi$ >> ]
+          | "letrec"; bi = binding ->
+              match bi with
+              [ <:binding< _ = $e$ >> -> <:str_item< $exp:e$ >>
+              | _ -> <:str_item< let rec $bi$ >> ]
+       ];
+
+    expr: LEVEL "top"
+      [ [ "let"; bi = binding; "in";
+          x = expr LEVEL ";" ->
+            <:expr< let $bi$ in $x$ >>
+        | "letrec"; bi = binding; "in";
+          x = expr LEVEL ";" ->
+            <:expr< let rec $bi$ in $x$ >> ]
+      ];
+        
+おおっと、match の部分がまだ Revised だった::
+
+    str_item: LEVEL "top"
+      [   [ 
+          ...
+          | "let"; bi = binding ->
+              begin match bi with
+              | <:binding< _ = $e$ >> -> <:str_item< $exp:e$ >>
+              | _ -> <:str_item< let $bi$ >> 
+              end
+          | "letrec"; bi = binding ->
+              begin match bi with
+              | <:binding< _ = $e$ >> -> <:str_item< $exp:e$ >>
+              | _ -> <:str_item< let rec $bi$ >> 
+              end
+          ]
+       ];
+
+これで良いはずだ! begin match .. with .. end に注意だ! これが出来たら、 EXTEND Gram .. END の中に入れてコンパイルしよう::
+
+    open Camlp4
+    
+    module Id : Sig.Id = struct
+      let name = "pa_letrec"
+      let version = "1.0"
+    end
+    
+    module Make (Syntax : Sig.Camlp4Syntax) = struct
+      open Sig
+      include Syntax
+      open Ast
+    
+      DELETE_RULE Gram str_item: "let"; opt_rec; binding; "in"; expr END;
+      DELETE_RULE Gram str_item: "let"; opt_rec; binding END;
+      DELETE_RULE Gram expr: "let"; opt_rec; binding; "in"; expr END;
+
+      EXTEND Gram
+        str_item: LEVEL "top"
+          [   [ "let"; bi = binding; "in"; x = expr ->
+                  <:str_item< let $bi$ in $x$ >>
+              | "letrec"; bi = binding; "in"; x = expr ->
+                  <:str_item< let rec $bi$ in $x$ >>
+              | "let"; bi = binding ->
+                  begin match bi with
+                  | <:binding< _ = $e$ >> -> <:str_item< $exp:e$ >>
+                  | _ -> <:str_item< let $bi$ >> 
+                  end
+              | "letrec"; bi = binding ->
+                  begin match bi with
+                  | <:binding< _ = $e$ >> -> <:str_item< $exp:e$ >>
+                  | _ -> <:str_item< let rec $bi$ >> 
+                  end
+              ]
+           ];
+    
+        expr: LEVEL "top"
+          [ [ "let"; bi = binding; "in";
+              x = expr LEVEL ";" ->
+                <:expr< let $bi$ in $x$ >>
+            | "letrec"; bi = binding; "in";
+              x = expr LEVEL ";" ->
+                <:expr< let rec $bi$ in $x$ >> 
+            ]
+          ];
+      END
+    end
+    
+    let module M = Register.OCamlSyntaxExtension(Id)(Make) in ()
+
+コンパイルとテストはこうなる::
+
+    ocamlc -pp camlp4of -I `ocamlc -where`/camlp4 -c pa_letrec.ml
+    $ ocaml
+            OCaml version 4.00.0+dev22_2012-07-20+camlp4-lexer-plug+annot+p4-expand-directory+typeloc    <--- まあ気にするな
+    
+    # #load "dynlink.cma";;
+    # #load "camlp4of.cma";;
+    	Camlp4 Parsing version 4.00.0+dev22_2012-07-20+camlp4-lexer-plug+annot+p4-expand-directory+typeloc   <--- まあ気にするな
+    
+    # #load "pa_letrec.cmo";;
+    # let x = 1;;
+    val x : int = 1
+    # let rec f x = f x;;
+    Characters 0-3:
+      let rec f x = f x;;
+      ^^^
+    Error: Parse error: "module" or "open" or [binding] expected after "let" (in [str_item])
+    # letrec f x = f x;;
+    val f : 'a -> 'b = <fun>
+    # 
+
+あひゃひゃひゃひゃ!革命は成った!!
+
+演習問題
+-----------------------
+* [重要] 君も上の letrec を試して国際Scheme戦線に参加しなさい。LCF ML 懐古趣味でも可能
+