5 Debian OCaml 発する言語インタプリタ


PIC
____________________________________________________________________

5.1 OCaml はどんな言語

的型言語というのが認識です言語らしい能がされますがそれスタイル利用 れます

ここではこのみすすめるにあた必要そうな OCaml 能について単にします

5.1.1

以下をすすめます

ocaml-interp /usr/bin/ocaml プログラムです

# プロンプトですわりを認識してもらうために;;しますなので # プロンプトろから;;までがです- :からまる です

# 1;;
- : int = 1
# "abc";;
- : string = "abc"
# (1, "abc");;
- : int * string = (1, "abc")
# (1, ("abc", 2), 3);;
- : int * (string * int) * int = (1, ("abc", 2), 3)
# [ "a"; "b"; "c"];;
- : string list = ["a"; "b"; "c"]
# "a" :: "b" :: "c" :: [];;
- : string list = ["a"; "b"; "c"]

表すすると名前しています1int"abc"stringとなています し た の で 評 価 な わ れ そ の と し て の で す

(1, ("abc", 2), 3)のようにコンマでくくタプルです表現できます名前 *連ねます任意にできる能です

[ ]リスト表現することができますまた::lisp でいうところの cons ですリスト同じである必要 があります

5.1.2 レコバリアント

表現するにあらかじめ必要であるようなです

# type vec_2d = { x : int; y : int };;
type vec_2d = { x : int; y : int; }
# { x = 1; y = 2 };;
- : vec_2d = {x = 1; y = 2}
# type int_or_string_or_none = I of int | S of string | N;;
type int_or_string_or_none = I of int | S of string | N
# I 3;;
- : int_or_string_or_none = I 3
# S "hello";;
- : int_or_string_or_none = S "hello"
# N;;
- : int_or_string_or_none = N

レコですC 造体のようなものでルド名とルドしますvec_2dとい レコして{ x = 1; y = 2 }という評価させました

バリアントあるいはばれているですint_or_string_or_noneというしてい そのI というタグいた int S というタグいた string N というタグのどれかということですこのタグ のことをコンストラクタびます

バリアント的な能でこの能で単に造を表現できます

# type str_tree = Leaf of string | Tree of (str_tree * str_tree);;
type str_tree = Leaf of string | Tree of (str_tree * str_tree)
# Leaf "abc";;
- : str_tree = Leaf "abc"
# Tree (Leaf "a", Tree (Leaf "b", Leaf "c"));;
- : str_tree = Tree (Leaf "a", Tree (Leaf "b", Leaf "c"))
5.1.3

表現するです

# (fun x -> x + 1);;
- : int -> int = <fun>
# (fun x -> x);;
- : ’a -> ’a = <fun>

(fun x -> x + 1)ですint -> intされていますがこれは int けとint という 味です+int int なのでとしてxintfun x -> x + 1int -> intというように われています

(fun x -> x)です’a -> ’aされていますがこれはかあるけと同じ という味ですこの’a -> ’a任意して適用能なのでてのについてこの されている (int -> intstring -> string... etc) のと同様がありますこのような多相 ま す

5.1.4 束縛

let使束縛します以下vにたいして整数fして束縛しています

# let v = 123;;
val v : int = 123
# v;;
- : int = 123
# let f x y = (x + y) * (x - y);;
val f : int -> int -> int = <fun>
# f 5 3;;
- : int = 16
5.1.5 パタ

OCaml 造を分解しつつ束縛することができますではタプル分解して束縛てい ます

# let (x, y) = (2, 3 + 4);;
val x : int = 2
val y : int = 7
# let (a, (b, c)) = (1, (2, 3));;
val a : int = 1
val b : int = 2
val c : int = 3

match ... with使すると造分解みつつ分岐することができます

lenリストかどうかを判定しながらしています束縛使するときにはletではな let rec使います

what程定したバリアント int_or_string_or_noneによ文字えてい ます

get_intint_or_string_or_noneint のときだけ Some int そうでないときは None します’a option みのある有るかあるいは無いかを表現できるバリアントです

# let rec len ls = match ls with [] -> 0 | x :: rest -> 1 + len rest;;
val len : ’a list -> int = <fun>
# len [1; 2; 3];;
- : int = 3
# let what v = match v with I _ -> "int" | S _ -> "string" | N -> "none";;
val what : int_or_string_or_none -> string = <fun>
# what (S "abc");;
- : string = "string"
# what N;;
- : string = "none"
# let get_int v = match v with I i -> Some i | _ -> None ;;
val get_int : int_or_string_or_none -> int option = <fun>
# get_int N;;
- : int option = None
# get_int (I 10);;
- : int option = Some 10
5.1.6 モジ

プログラムがおおきくなてくると名前になてきますOCaml には module 能が あり名前分けることができますあるコンパイル単位xyz.ml というイル名だその Xyz という module されますモジにたとえば abc という名前があ さ れ て い れ ば 他 の コ ン パ イ ル 単 位 イ ル か ら も Xyz.abc という名前アクセスすることができ ます

(* xyz.ml *)
let abc = "abc"
...


(*  他のイル *)
... Xyz.abc ...

module にさらに module することもできますxyz.ml module SubXyz Xyz.SubXyz という 名前module です

みのモジ使module することもできますみの module である List 使Xyz.L しています

(* xyz.ml *)
module SubXyz =
  struct
  ...
  end

module L = List

5.2 言語インタプリタ単なりかた

言語とはでしうかここではとしてあつかえる言語ということにしてそのような言語インタプリタ をします

5.2.1 ナイインタプリタ

let  

えば以下のようなプログラムえます

;; scheme function
(define (f)
  ;; env A
  (let ((x 1) (y 2) (z 3))
    ;; env B
    (let ((y 4))
      ;; env C
      (let ((z 5))
;; env D
(+ x y z)))))
(* OCaml function *)
let f () =
  (* env A *)
  let (x, y, z) = (1, 2, 3) in
    (* env B *)
  let y = 4 in
    (* env C *)
  let z = 5 in
    (* env D *)
    x + y + z

のためのブル(environment) ぶことにしてような造になているとえてみます からなわれるとするとそれぞれのコメントしているとえてよいは ずです

PIC

しにおける  

さらに以下のようなプログラムえます

(define (f x y)
  ;; env f
  (* x y 2))

;;env X

(let ((x 1) (y 2))
  (define (g)
    ;; env g
    (f 3 4))
  (g))

;;; may be another call of (f x y)
let rec f x y =
  (* env f *)
  x * y * 2

(* env X *)

let (x, y) = (1, 2)
let rec g () =
  (* env g *)
  f 3 4

let v = g ()

(* may be another call of f x y *)

するとそれぞれのコメントのようになているはずです

PIC

ここでX 同じなので有させることにするとのような造をえることができますここでする必要 あるのはf f されるなるということですf であるX ですがf f されるX 長する必要があります

PIC

クロ  

以下のようなプログラムえます

(define h
  (let ((x 1) (y 2))
    ;; env c
    (let ((f (lambda (z)
               ;; env l
       (+ x y z))))
      f)))

;; env X

(h 3)
;;; may be another call of (h x)
let rec h =
  let (x, y) = (1, 2) in
  (* env c *)
  let f z =
    (* env l *)
    x + y + z
  in f

(* env X *)

let v = h 3
(* may be another call of h x *)

h 束縛されていますしかも h x, y 束縛している let 側にあるにもかかわらずしたには x, y 評価します

この let のような能をレキシカルスコまたこのようなレキシカルクロ (lexical closure) あるいは 単にクロ (closure) びます

でのえてみるとのような造になているはずです

PIC

h 束縛されている closure はあきらかにc ている必要がありますなぜならにはc 生成しなければならないからですしたがclosure 以下のような造になていると えることができますenv closure args 仮引リストそして body です

PIC

評価必要評価しながらlet しのには長するると的簡単に ンタプリタすることができますまたする造をえれば closure することもでき ます

: http://www.sato.kuis.kyoto-u.ac.jp/~igarashi/class/isle4-05w/text/eopl003.html

5.3 OCaml での lexing parsing - ocamllex ocamlyacc

5.3.1 ocamllex

ocamllex OCaml 属している数生成(lexer generator) OCaml からせる lexer 生成してくれ ます以下のように C 言語でもおなじみの lex, flex たような使になています


{
  (* header *)
  (* Lexing 部分したいOCaml  *)
}

(*  文字パタ *)

(*  (lexing)  *)

{
  (* trailer *)
  (*  部分生成されたするOCaml  *)
}
5.3.2 ocamlyacc

同様ocamlyacc OCaml 属している文解数生成(parser generator) OCaml から parser 生成してくれますこちらもやはり以下のように yacc, bison たような使になてい ます


%{
  (* header *)
  (*  文木生成処したいOCaml  *)
%}
  /* declarations */
  /*  端記文木root  */
%%
  /* rules */
  /*  文脈自由文法と文木生成処 */
%%
  (* trailer *)
  (*  生成された parser するOCaml  *)
5.3.3 ocamllex ocamlyacc 使

ocamllex ocamlyacc 併用するにはocamlyacc 生成させた parser 端記lexer からさせるよう にするのがもとも単純使です

以下S parser 生成させるですsParser.mly ocamlyacc すると sParser.ml 生成されsLexer.mll ocamllex すると sLexer.ml 生成されます

sParser.mly ごとに端記%token %start %type 文木root とそのしますroot 名前文解 名前になります

yacc 同じ文脈自由文法をしていきますexpr 文字であるかあるいはexpr また expr べたものをたものというになています


%{
  (* sParser.mly *)

  module C = SCons

%}

/* File sparser.mly */
%token LPAREN RPAREN DOT_SYMBOL EOL BOOL_TRUE BOOL_FALSE
%token <SCons.s_int> INT
%token <float> FLOAT
%token <string> SYMBOL
%token <string> STRING
%start expr
%type <SCons.s_expr> expr
%%

expr_list:
  { C.Null }
| expr DOT_SYMBOL expr { C.Cons($1, $3) }
| expr expr_list { C.Cons($1, $2) }

expr:
| BOOL_TRUE               { C.Bool(true) }
| BOOL_FALSE              { C.Bool(false) }
| INT                     { C.Int($1) }
| FLOAT                   { C.Float($1) }
| SYMBOL                  { C.Symbol($1) }
| STRING                  { C.String($1) }
| LPAREN expr_list RPAREN { $2 }

sLexer.mll 文字パタでは表現文字集しの表現利用してlet 名前けていきま 文字でくくる表現同様ですした文字パタをさらに文字パタ利用することが できます

部 分 で は token 文字パタtoken 生成lex していきますrule 文字lexer 名になりますなのでここではその名前token です

(Lexing) におけるイルlexbuf lex_start_p (token) lex_curr_p token 了のされていますocamllex デフルト動作では pos_cnum ルド されるのみなのでイル頭からのバイトしかわかりません認識タブによるカラ なうにはlex_start_p token をもとに lex_curr_p してやる必要があります *9

{
  (* sLexer.mll*)

  module LX = Lexing
  module P = SParser
  ... (*   *)
  let fix_position lexbuf =
    let newline pos = {
      pos with
LX.pos_lnum = pos.LX.pos_lnum + 1;
LX.pos_cnum = pos.LX.pos_cnum + 1;
LX.pos_bol = pos.LX.pos_cnum + 1;
    } in

    let tab pos = {
      pos with
LX.pos_cnum = pos.LX.pos_cnum + 8 - (pos.LX.pos_cnum - pos.LX.pos_bol) mod 8
    } in

    let other pos = {
      pos with
LX.pos_cnum = pos.LX.pos_cnum + 1
    } in

    let rec fix_pos_rec pos str =
      let len = (String.length str) in
match (if len > 0 then (Some (str.[0]), String.sub str 1 (len - 1))
       else (None, "")) with
    (None, _) -> pos
  | (Some ’\n’, rest) -> fix_pos_rec (newline pos) rest
  | (Some ’\t’, rest) -> fix_pos_rec (tab pos) rest
  | (Some _, rest) -> fix_pos_rec (other pos) rest
    in
    let _ = lexbuf.LX.lex_curr_p <- fix_pos_rec (LX.lexeme_start_p lexbuf) (LX.lexeme lexbuf) in
      ()
}

/*  文字パタ */
let str_esc = ’\\’
let double_quote = ’"’
let str_escaped_char = str_esc _
let str_char = [^ ’\\’ ’"’]
let str = double_quote (str_char | str_escaped_char)* double_quote

let left_paren = ’(’
let right_paren = ’)’
let space = [’ ’ ’\t’ ’\n’ ’\r’]+
let dot_symbol = ’.’
let bool_true  = ’#’ ’t’
let bool_false = ’#’ ’f’

let int = ’-’? [’0’ - ’9’]+
let float = ’-’? [’0’ - ’9’]+ ’.’ [’0’ - ’9’]* | ’-’? [’0’ - ’9’]* ’.’ [’0’ - ’9’]+
let symbol = [^ ’"’ ’(’ ’)’ ’ ’ ’\t’ ’\n’ ’\r’]+

/* lexing  */
rule token = parse
  | left_paren      { P.LPAREN }
  | right_paren     { P.RPAREN }
  | space      { fix_position lexbuf; token lexbuf }
  | dot_symbol      { P.DOT_SYMBOL }
  | bool_true       { P.BOOL_TRUE }
  | bool_false      { P.BOOL_FALSE }
  | int      { expr_integer (LX.lexeme lexbuf) }
  | float    { P.FLOAT(Pervasives.float_of_string(LX.lexeme lexbuf)) }
  | symbol   { P.SYMBOL(LX.lexeme lexbuf) }
  | str      { fix_position lexbuf; P.STRING(expr_string(LX.lexeme lexbuf)) }
  | eof      { raise Eof }

5.4 Haskell Lexing

Haskell にはブロ了の token りの token 略することができる layout rule という能がありま そのため略された token lexing てやる必要があります

まず同様lexing token 生成その token 則にtoken うというように2 ないます

5.4.1 layout なしの Lexing

lexer0.mll header 部分  

まずは.mll header 部分です

から layout rule において必要となるカラムえあげるためになう(fix_position) していますまたHaskell 文字および文字リテラルリテラル複雑なので lexer() しつつ文字表現する(decode_char, decode_string) してい ます

fix_position  

{
  (* lexer0.mll header 部分 *)
  module LX = Lexing
  module P = Parser
  ... (*   *)
  let fix_position lexbuf =
    let newline pos =
      { pos with
          LX.pos_lnum = pos.LX.pos_lnum + 1;
          LX.pos_cnum = pos.LX.pos_cnum + 1;
          LX.pos_bol = pos.LX.pos_cnum + 1;
      } in

    let tab pos =
      { pos with
          LX.pos_cnum = pos.LX.pos_cnum + 8 - (pos.LX.pos_cnum - pos.LX.pos_bol) mod 8
      } in

    let other pos =
      { pos with
          LX.pos_cnum = pos.LX.pos_cnum + 1
      } in

    let rec fix_pos_rec pos str =
      let len = (String.length str) in
        match (if len > 0 then (Some (str.[0]), String.sub str 1 (len - 1))
               else (None, "")) with
            (None, _) -> pos
          | (Some ’\n’, rest) -> fix_pos_rec (newline pos) rest
          | (Some ’\t’, rest) -> fix_pos_rec (tab pos) rest
          | (Some _, rest) -> fix_pos_rec (other pos) rest
    in
    let _ = lexbuf.LX.lex_curr_p <- fix_pos_rec (LX.lexeme_start_p lexbuf) (LX.lexeme lexbuf) in
      ()
  ... (*   *)

decode_char, decode_string 文字および文字デコ  

  ... (*   *)
  let decode_cexpr cexpr =
    let fchar = String.get cexpr 0 in
    let escexp = String.sub cexpr 1 ((String.length cexpr) - 1) in
    let fmatch exp str = Str.string_match (Str.regexp exp) str 0 in
      if fchar = ’\\’ then
        match escexp with
            "NUL"   -> Some ’\x00’
          | "SOH" | "^A"   -> Some ’\x01’
          | "STX" | "^B"   -> Some ’\x02’
        ... (*   *)
          | "RS"  | "^^"   -> Some ’\x1e’
          | "US"  | "^_"   -> Some ’\x1f’
          | "SP"           -> Some ’ ’

          | "\\"           -> Some ’\\’
          | "\""           -> Some ’"’
          | "’"            -> Some ’\’’

          | "DEL"          -> Some ’\x7f’

          | _ when fmatch "^[0-9]+$" escexp
              -> Some (Char.chr (int_of_string escexp))
          | _ when fmatch "^[xX][0-9a-zA-Z]+$" escexp
              -> Some (Char.chr (int_of_string ("0" ^ escexp)))
          | _ when fmatch "^[oO][0-7]+$" escexp
              -> Some (Char.chr (int_of_string ("0" ^ escexp)))

          | _ -> None

      else Some fchar

  let decode_char lexbuf =
    let cstr = LX.lexeme lexbuf in
    let len = String.length cstr in
      match decode_cexpr (String.sub cstr 1 (len - 2)) with
          Some c -> c
        | None   -> failwith (F.sprintf "Unkown char expression %s" cstr)

  let decode_string lexbuf =
    let sexpr = LX.lexeme lexbuf in
    let len = String.length sexpr in
    let strlbuf = Lexing.from_string (String.sub sexpr 1 (len - 2)) in
    let rec decode result =
      match HsStr.char strlbuf with
          HsStr.Eos -> result
        | HsStr.Char cstr ->
            if cstr = "\\&" then decode (result ^ "&")
            else decode (result ^
                           match (decode_cexpr cstr) with
                               None -> failwith (F.sprintf "Unkown char expression ’%s’ in literal string" cstr)
                             | Some c -> (String.make 1 c))
        | HsStr.Gap g -> decode result
    in decode ""
}

lexer0.mll 文字パタ部分  

文字パタです

はち多いですがしいところはありませんリテラル文字ですがリテラル文字部分文字 なく表現できていますしかしリテラル表現される文字するのが複雑なのでおよび のような必要になります

/* lexer0.mll  文字パタ部分 */
let special = [’(’ ’)’ ’,’ ’;’ ’[’ ’]’ ’‘’ ’{’ ’}’]

let space = ’ ’
let newline = ("\r\n"|[’\n’ ’\r’])
let tab = ’\t’

let dashes = ’-’ ’-’ ’-’*

let ascSmall = [’a’-’z’]
let small = ascSmall | ’_’
let ascLarge = [’A’-’Z’]
let large = ascLarge

let plus = ’+’
let minus = ’-’
let exclamation = ’!’
let ascSymbol_nbs = [ ’!’ ’#’ ’$’ ’%’ ’&’ ’*’ ’+’ ’.’ ’/’ ’<’ ’=’ ’>’ ’?’ ’@’ ’^’ ’|’ ’-’ ’~’ ]
let ascSymbol = ascSymbol_nbs | ’\\’
let symbol = ascSymbol

let ascDigit = [’0’-’9’]
let digit = ascDigit

let octit = [’0’-’7’]
let hexit = ascDigit | [’a’-’z’ ’A’-’Z’]

let decimal = (digit)+
let octal = (octit)+
let hexadecimal = (hexit)+

let exponent = [’e’ ’E’] [’+’ ’-’]? decimal
let float = decimal ’.’ decimal exponent? | decimal exponent

let graphic = small | large | symbol | digit | special | [’:’ ’"’ ’\’’]
let any = graphic | space | tab

let comment = dashes ((space | tab | small | large | symbol | digit | special | [’:’ ’"’ ’\’’]) (any)*)? newline

let whitechar = newline | space | tab
let whitestuff = whitechar | comment
let whitespace = (whitestuff)+

(*
let lwhitechar = space | tab
let lwhitestuff = lwhitechar | comment
let lwhitespace = (lwhitestuff)+
*)

let char_gr = small | large | ascSymbol_nbs | digit | special | [’:’ ’"’]
let str_gr  = small | large | ascSymbol_nbs | digit | special | [’:’ ’\’’]

let charesc = [’a’ ’b’ ’f’ ’n’ ’r’ ’t’ ’v’ ’\\’ ’"’ ’\’’]
let str_charesc = charesc | ’&’
let cntrl = ascLarge | [’@’ ’[’ ’\\’ ’]’ ’^’ ’_’]
let gap = ’\\’ (whitechar)+ ’\\’
(* let gap = ’\\’ (lwhitechar | newline)+ ’\\’ *)

let ascii = (’^’ cntrl) | "NUL" | "SOH" | "STX" | "ETX" | "EOT" | "ENQ" | "ACK"
  | "BEL" | "BS" | "HT" | "LF" | "VT" | "FF" | "CR" | "SO" | "SI" | "DLE"
  | "DC1" | "DC2" | "DC3" | "DC4" | "NAK" | "SYN" | "ETB" | "CAN"
  | "EM" | "SUB" | "ESC" | "FS" | "GS" | "RS" | "US" | "SP" | "DEL"

let escape = ’\\’ ( charesc | ascii | decimal | ’o’ octal | ’x’ hexadecimal )
let str_escape = ’\\’ ( str_charesc | ascii | decimal | ’o’ octal | ’x’ hexadecimal )

let char = ’\’’ (char_gr | space | escape) ’\’’
let string = ’"’ (str_gr | space | str_escape | gap)* ’"’

let varid = small (small | large | digit | ’\’’)*
let conid = large (small | large | digit | ’\’’)*

let varsym = symbol (symbol | ’:’)*
let consym = ’:’ (symbol | ’:’)*

let modid = conid

lexer0.mll 部分  

最後です

スペタブなどをんでいる whitespace string のところで fix_position んでしています また char string リテラルから文字文字するために decode_char, decode_string してい ます

(* lexer0.mll  部分 *)
rule token = parse
  | ’(’  { P.SP_LEFT_PAREN(loc lexbuf) }
  | ’)’  { P.SP_RIGHT_PAREN(loc lexbuf) }
  | ’,’  { P.SP_COMMA(loc lexbuf) }
  | ’;’  { P.SP_SEMI(loc lexbuf) }
  | ’[’  { P.SP_LEFT_BRACKET(loc lexbuf) }
  | ’]’  { P.SP_RIGHT_BRACKET(loc lexbuf) }
  | ’‘’  { P.SP_B_QUOTE(loc lexbuf) }
  | ’{’  { P.SP_LEFT_BRACE(loc lexbuf) }
  | ’}’  { P.SP_RIGHT_BRACE(loc lexbuf) }
      (** special tokens *)

  | "case"     { P.K_CASE(loc lexbuf) }
  | "class"    { P.K_CLASS(loc lexbuf) }
  | "data"     { P.K_DATA(loc lexbuf) }
  | "default"  { P.K_DEFAULT(loc lexbuf) }
  | "deriving" { P.K_DERIVING(loc lexbuf) }
  | "do"       { P.K_DO(loc lexbuf) }
  | "else"     { P.K_ELSE(loc lexbuf) }
  | "if"       { P.K_IF(loc lexbuf) }
  | "import"   { P.K_IMPORT(loc lexbuf) }
  | "in"       { P.K_IN(loc lexbuf) }
  | "infix"    { P.K_INFIX(loc lexbuf) }
  | "infixl"   { P.K_INFIXL(loc lexbuf) }
  | "infixr"   { P.K_INFIXR(loc lexbuf) }
  | "instance" { P.K_INSTANCE(loc lexbuf) }
  | "let"      { P.K_LET(loc lexbuf) }
  | "module"   { P.K_MODULE(loc lexbuf) }
  | "newtype"  { P.K_NEWTYPE(loc lexbuf) }
  | "of"       { P.K_OF(loc lexbuf) }
  | "then"     { P.K_THEN(loc lexbuf) }
  | "type"     { P.K_TYPE(loc lexbuf) }
  | "where"    { P.K_WHERE(loc lexbuf) }
  | "_"        { P.K_WILDCARD(loc lexbuf) }
      (** reservedid *)

  | ".."       { P.KS_DOTDOT(loc lexbuf) }
  | ":"        { P.KS_COLON(loc lexbuf) }
  | "::"       { P.KS_2_COLON(loc lexbuf) }
  | "="        { P.KS_EQ(loc lexbuf) }
  | "\\"       { P.KS_B_SLASH(loc lexbuf) }
  | "|"        { P.KS_BAR(loc lexbuf) }
  | "<-"       { P.KS_L_ARROW(loc lexbuf) }
  | "->"       { P.KS_R_ARROW(loc lexbuf) }
  | "@"        { P.KS_AT(loc lexbuf) }
  | "~"        { P.KS_TILDE(loc lexbuf) }
  | "=>"       { P.KS_R_W_ARROW(loc lexbuf) }
      (** reservedop *)

  | "as"              { P.K_AS(loc lexbuf) }  (** maybe varid *)
  | "qualified"       { P.K_QUALIFIED(loc lexbuf) }  (** maybe varid *)
  | "hiding"          { P.K_HIDING(loc lexbuf) }  (** maybe varid *)
  | varid      { P.T_VARID(LX.lexeme lexbuf, loc lexbuf) }
  | conid      { P.T_CONID(LX.lexeme lexbuf, loc lexbuf) }
      (** identifiers or may be qualified ones *)

  | whitespace  { fix_position lexbuf; P.WS_WHITE(loc lexbuf) }  (** comment begining with dashes is not varsym *)
      (** white spaces *)

  | plus       { P.KS_PLUS(loc lexbuf) }  (** maybe varsym *)
  | minus      { P.KS_MINUS(loc lexbuf) } (** maybe varsym *)
  | exclamation  { P.KS_EXCLAM(loc lexbuf) } (** maybe varsym *)
  | varsym     { P.T_VARSYM(LX.lexeme lexbuf, loc lexbuf) }
  | consym     { P.T_CONSYM(LX.lexeme lexbuf, loc lexbuf) }
      (** symbols or may be qualified ones *)

  | modid ’.’ varid   { P.T_MOD_VARID(decode_with_mod lexbuf, loc lexbuf) }
  | modid ’.’ conid   { P.T_MOD_CONID(decode_with_mod lexbuf, loc lexbuf) }
  | modid ’.’ varsym  { P.T_MOD_VARSYM(decode_with_mod lexbuf, loc lexbuf) }
  | modid ’.’ consym  { P.T_MOD_CONSYM(decode_with_mod lexbuf, loc lexbuf) }
      (** qualified xx *)

  | char      { P.L_CHAR(decode_char lexbuf, loc lexbuf) }
  | string    { fix_position lexbuf; P.L_STRING(decode_string lexbuf, loc lexbuf) }

  | decimal | (’0’ [’o’ ’O’] octal) | (’0’ [’x’ ’X’] hexadecimal)
        { P.L_INTEGER(Int64.of_string(LX.lexeme lexbuf), loc lexbuf) }

  | float      { P.L_FLOAT(float_of_string(LX.lexeme lexbuf), loc lexbuf) }

  | eof        { P.EOF(loc lexbuf) }
  ... /*  以下 */

hsStr.mll  

文字lexer です

ここでの token 文字リテラル1 文字表現あるいは(gap) ですHaskell では 1 つの文字リテラル中断 してコメントしたすることができますこのコメント部分gap です

ここでされた char 利用して decode_string 文字していくようになています

{
  (* hsStr.mll *)
  module LX = Lexing

  type ct =
      Char of string
    | Gap of string
    | Eos
}

let special = [’(’ ’)’ ’,’ ’;’ ’[’ ’]’ ’‘’ ’{’ ’}’]

let space = ’ ’
let newline = ("\r\n"|[’\n’ ’\r’])
let tab = ’\t’

let ascSmall = [’a’-’z’]
let small = ascSmall
let ascLarge = [’A’-’Z’]
let large = ascLarge

let ascSymbol_nbs = [ ’!’ ’#’ ’$’ ’%’ ’&’ ’*’ ’+’ ’.’ ’/’ ’<’ ’=’ ’>’ ’?’ ’@’ ’^’ ’|’ ’-’ ’~’ ]

let ascDigit = [’0’-’9’]
let digit = ascDigit

let octit = [’0’-’7’]
let hexit = ascDigit | [’a’-’z’ ’A’-’Z’]

let decimal = (digit)+
let octal = (octit)+
let hexadecimal = (hexit)+

let lwhitechar = space | tab

let str_gr  = small | large | ascSymbol_nbs | digit | special | [’:’ ’\’’]

let charesc = [’a’ ’b’ ’f’ ’n’ ’r’ ’t’ ’v’ ’\\’ ’"’ ’\’’]
let str_charesc = charesc | ’&’
let cntrl = ascLarge | [’@’ ’[’ ’\\’ ’]’ ’^’ ’_’]
let gap = ’\\’ (lwhitechar | newline)+ ’\\’

let ascii = (’^’ cntrl) | "NUL" | "SOH" | "STX" | "ETX" | "EOT" | "ENQ" | "ACK"
  | "BEL" | "BS" | "HT" | "LF" | "VT" | "FF" | "CR" | "SO" | "SI" | "DLE"
  | "DC1" | "DC2" | "DC3" | "DC4" | "NAK" | "SYN" | "ETB" | "CAN"
  | "EM" | "SUB" | "ESC" | "FS" | "GS" | "RS" | "US" | "SP" | "DEL"

let str_escape = ’\\’ ( str_charesc | ascii | decimal | ’o’ octal | ’x’ hexadecimal )

rule char = parse
  | str_gr | space | str_escape  { Char(LX.lexeme lexbuf) }
  | gap                          { Gap(LX.lexeme lexbuf) }
  | eof                          { Eos }

: http://www.sampou.org/haskell/report-revised-j/lexemes.html

5.4.2 Haskell layout rule

このめにもいたように layout rule token にさらに token てやるです まずは認してみまし以下Haskell 98 Language Report *10 してみます

................................................................................................... ここ から.........................................................................................................

レイアウトこのではレイアウトいているプログラムどのようにしてブレセミコロ するかをすることによするこのL をとるL への

インデント文字カラムであるひとつのインデントとはにある インデント表すこのカラムするために以下のようなをもつント する

レイアウトルにあわせるためにスプログラムUnicode 文字ASCII 文字同じであると しかしながらとのけるためプログラマレイアウト味が文字存するようなプログ ラムかないようにすべきである

適用  

L tokens [ ] tokens レイアウトしないをもたらすここでtokens モジおよび ようにカラム表示子したであるL 以下のとおりここでは :ストリとして使 [ ]ストリである

L (<n>:ts) (m:ms)   = ; : (L ts (m:ms))  if m = n
                    = } : (L (<n>:ts) ms) if n < m
L (<n>:ts) ms       = L ts ms
L ({n}:ts) (m:ms)   = { : (L ts (n:m:ms)) if n > m   (Note 1)
L ({n}:ts) []       = { : (L ts [n]) if n > 0        (Note 1)
L ({n}:ts) ms       = { : } : (L (<n>:ts) ms)        (Note 2)
L (}:ts) (0:ms)     = } : (L ts ms)                  (Note 3)
L (}:ts) ms         = parse-error                    (Note 3)
L ({:ts) ms         = { : (L ts (0:ms))              (Note 4)
L (t:ts) (m:ms)     = } : (L (t:ts) ms) if m /= 0 and parse-error(t)  (Note 5)
L (t:ts) ms         = t : (L ts ms)
L [] []             = []
L [] (m:ms)         = } : L [] ms if m /=0           (Note 6)

............................................................................................ここまで ........................................................................................................

だいぶ長くなてしまたので Note 略です

わかりにくいですがここでも的には 2 になています

まずtoken 適用します以下のような則だとえるとわかりやすいかもしれま せん

つぎにであるL 適用します

L はもとの token インデントレベルスタにとりもとの token token したものをです やはり以下のような則だとえるとわかりやすいかもしれません

: http://www.sampou.org/haskell/report-revised-j/syntax-iso.html#layout

OCaml での  

parse error にならないブロ続」 はかなりがやかいでした利用した ocamlyacc yacc bison 同じように LALR(1) parser generator となており的には token 途中までんだとその token およびtoken によ文解動作していますここでてきたような parse error になるまで ブロじるかわからないようなとはあまりくありませんbacktrack なえるような parser ならこのよ うなしてもより単にできるとえられますtoken token ごとに parse error フラグしてや りなおしをなうことでしました行効率はよくありませんがになるほど token 多くなら なければそうです的には Packrat parsing のような方法parser 直してみたいところ です

OCaml layout rule したのようなじですtoken へのせて いますP.BLK_OPEN {n}P.BLK_LEVEL < n > にあたるものですもとのたよう表現できてい るのがわかるでしうか

let all_token_rev_list lexbuf =
  let unget_s = S.create () in
  let get_token () = L0.token lexbuf in
  let blk_level_pair tk =
    let loc = L0.get_location tk in (loc.T.start_p.T.col + 1, loc) in
  let eof_token_p = (function P.EOF(_) -> true | _ -> false) in

  let rec scan_start () =
    match get_token () with
        (P.SP_LEFT_BRACE _ | P.K_MODULE _) as start -> start
      | P.WS_WHITE _ -> scan_start ()
      | other ->
          let _ = S.push other unget_s in
            P.BLK_OPEN (blk_level_pair other)
  in

  let scan_next prev =
    let rec scan_next_rec () =
      let cur =
        if (S.is_empty unget_s) then (get_token ())
        else (S.pop unget_s) in

        match (prev, cur) with
            (_, (P.EOF(_) as eoft)) -> eoft
          | (_, P.WS_WHITE(_)) -> (scan_next_rec ())
          | ((P.K_LET(_) | P.K_WHERE(_) | P.K_DO(_) | P.K_OF(_)), (P.SP_LEFT_BRACE(_) as lbr)) -> lbr
          | ((P.K_LET(_) | P.K_WHERE(_) | P.K_DO(_) | P.K_OF(_)), tk) ->
              let (_, (level, loc)) = (S.push tk unget_s, blk_level_pair tk) in
                P.BLK_OPEN((if (eof_token_p tk) then 0 else level), loc)
          | (_, tk) ->
              let (_, loc) as p = blk_level_pair tk in
                if (loc.T.start_p.T.line
                    - (L0.get_location prev).T.end_p.T.line) > 0 then
                  let _ = S.push tk unget_s in P.BLK_LEVEL p
                else tk
    in (scan_next_rec ())
  in
    (LST.fold_left
       (fun r a -> ((a, new_err_flag ()) :: r))
       []
       (LST.create_stream (scan_start ()) scan_next eof_token_p))
let rec layout istream levels =
  let push_new_token tok lform =
    LST.Cons ((tok, new_err_flag ()), lform)
  in

  let (tok, err) =
    match LST.peek istream with
        None -> raise Parsing.Parse_error
      | Some x -> x
  in
    match (tok, levels) with
        ((P.BLK_LEVEL (n, loc)), (m :: mstl as ms)) when m = n ->
          let addtk = P.SP_SEMI(loc) in
            push_new_token addtk (lazy (layout (LST.tl istream) ms))
      | ((P.BLK_LEVEL (n, loc)), m :: ms) when n < m  ->
          push_new_token (P.SP_RIGHT_BRACE(loc)) (lazy (layout istream ms))
      | ((P.BLK_LEVEL (n, _)), ms)                         -> layout (LST.tl istream) ms
      | ((P.BLK_OPEN (n, loc)), (m :: ms as levels)) when n > m  ->
          push_new_token (P.SP_LEFT_BRACE(loc)) (lazy (layout (LST.tl istream) (n :: levels))) (* Note 1 *)
      | ((P.BLK_OPEN (n, loc)), []) when n > 0             ->
          push_new_token (P.SP_LEFT_BRACE(loc)) (lazy (layout (LST.tl istream) [n])) (* Note 1 *)
      | ((P.BLK_OPEN (n, loc)), ms)                        ->
          push_new_token
            (P.SP_LEFT_BRACE(loc))
            (lazy (push_new_token
                     (P.SP_RIGHT_BRACE(loc))
                     (lazy (layout (push_new_token
                                      (P.BLK_LEVEL(n, loc))
                                      (lazy (LST.tl istream))) ms)))) (* Note 2 *)
      | ((P.SP_RIGHT_BRACE _ as rbr), 0 :: ms)        ->
          LST.Cons ((rbr, err), lazy (layout (LST.tl istream) ms)) (* Note 3 *)
      | ((P.SP_RIGHT_BRACE _), ms)                   -> raise Parsing.Parse_error (* Note 3 *)
      | ((P.SP_LEFT_BRACE _ as lbr), ms)             ->
          LST.Cons ((lbr, err), lazy (layout (LST.tl istream) (0 :: ms))) (* Note 4 *)

      | ((P.EOF loc as eoft), [])                    -> LST.Cons ((eoft, err), lazy (LST.Nil))
      | ((P.EOF loc), m :: ms) when m <> 0           ->
          push_new_token (P.SP_RIGHT_BRACE(loc)) (lazy (layout istream ms)) (* Note 6 *)

      | (t, (m :: mstl)) when m <> 0 && (!err)       ->
          err := false;
          push_new_token (P.SP_RIGHT_BRACE(L0.get_location t)) (lazy (layout istream mstl))
          (* parse-error(t) Note 5 case *)
      | (t, ((m :: mstl) as ms))                   ->
          LST.Cons ((t, err),
                   lazy (layout (LST.tl istream) ms))
      | (t, ms)                                    ->
          LST.Cons ((t, err),
                   lazy (layout (LST.tl istream) ms))

5.5 Haskell Parsing

文脈自由文法のせてしまうと長すぎてなのでここでは単項expression 部分および パタていくことにします

5.5.1 Haskell Expression

単項expression  

単項表現 aexp としてここでされているのはコンストラクタリテラルでくくられた expression プルリストリスト包表でくくられた left secionでくくられた right secion (section 算式でどち らか足しているもの)レコ生成レココピしてです単項表現 となります

/* parser.mly  単項 expression */
/*
 aexp    ->      qvar    (variable)
        |       gcon    (general constructor)
        |       literal
        |       ( exp )         (parenthesized expression)
        |       ( exp1 , ... , expk )   (tuple, k>=2)
        |       [ exp1 , ... , expk ]   (list, k>=1)
        |       [ exp1 [, exp2] .. [exp3] ]     (arithmetic sequence)
        |       [ exp | qual1 , ... , qualn ]   (list comprehension, n>=1)
        |       ( expi+1 qop(a,i) )     (left section)
        |       ( lexpi qop(l,i) )      (left section)
        |       ( qop(a,i)<-> expi+1 )  (right section)
        |       ( qop(r,i)<-> rexpi )   (right section)
        |       qcon { fbind1 , ... , fbindn }  (labeled construction, n>=0)
        |       aexp<qcon> { fbind1 , ... , fbindn }    (labeled update, n >= 1)
*/

aexp:
  qvar  { E.VarE $1 }   /*(variable)*/
| gcon  { E.ConsE $1 }  /*(general constructor)*/
| literal  { E.LiteralE $1 }
| SP_LEFT_PAREN exp SP_RIGHT_PAREN  { E.ParenE $2 }     /*(parenthesized expression)*/
| SP_LEFT_PAREN exp SP_COMMA exp_list SP_RIGHT_PAREN  { E.TupleE ($2 :: $4) }   /*(tuple, k>=2)*/
| SP_LEFT_BRACKET exp_list SP_RIGHT_BRACKET  { E.ListE ($2) }   /*(list, k>=1)*/
| SP_LEFT_BRACKET exp KS_DOTDOT SP_RIGHT_BRACKET  { E.ASeqE($2, None, None) }   /*(arithmetic sequence)*/
| SP_LEFT_BRACKET exp SP_COMMA exp KS_DOTDOT SP_RIGHT_BRACKET  { E.ASeqE($2, Some $4, None) }   /*(arithmetic sequence)*/
| SP_LEFT_BRACKET exp KS_DOTDOT exp SP_RIGHT_BRACKET  { E.ASeqE($2, None, Some $4) }    /*(arithmetic sequence)*/
| SP_LEFT_BRACKET exp SP_COMMA exp KS_DOTDOT exp SP_RIGHT_BRACKET  { E.ASeqE($2, Some $4, Some $6) }    /*(arithmetic sequence)*/
| SP_LEFT_BRACKET exp KS_BAR qual_list SP_RIGHT_BRACKET  { E.LCompE ($2, $4) }  /*(list comprehension, n>=1)*/

| SP_LEFT_PAREN op2_left_section SP_RIGHT_PAREN  { E.MayLeftSecE ($2) }         /*(left section)*/
| SP_LEFT_PAREN op2_right_section SP_RIGHT_PAREN  { E.MayRightSecE ($2) }       /*(right section)*/

| qcon SP_LEFT_BRACE fbind_list SP_RIGHT_BRACE  { E.LabelConsE ($1, OH.of_list $3) }    /*(labeled construction, n>=1)*/
| qcon SP_LEFT_BRACE SP_RIGHT_BRACE  { E.LabelConsE ($1, OH.create 0) }         /*(labeled construction, n=0)*/
| aexp SP_LEFT_BRACE fbind_list SP_RIGHT_BRACE  { E.LabelUpdE ($1, OH.of_list $3) }     /*(labeled update, n >= 1)*/
;

exp_list:
  exp SP_COMMA exp_list  { $1 :: $3 }
| exp  { [$1] }
;

qual_list:
  qual SP_COMMA qual_list  { $1 :: $3 }
| qual  { [$1] }
;

fbind_list:
  fbind SP_COMMA fbind_list  { $1 :: $3 }
| fbind  { [$1] }
;

qual:
  pat KS_L_ARROW exp  { LC.Gen($1, $3) }        /*(generator)*/
| K_LET decl_list  { LC.Let $2 }        /*(local declaration)*/
| exp  { LC.Guard $1 }  /*(guard)*/
;
5.5.2 算子優先

expression およびパタ文解  

Haskell 算子には level 0 から level 9 までの優先(おおきい) がありその優先( なし) 算子しているスコすることができますしかも算子として *11 ことも できなしで複雑能ですパタについてもにもなているコンストラクタ算子とし 使算式パタしていけますもちろん算子として使コンストラクタにも優先 則があり方法同様となています

こ こ で と な る の は 文 解 優 先 し て い な い と い う こ と で す こ こ で は expressionパタ 算子交互連なているリストとして文解ています

/* parser.mly   expression */
... /*   */
/* expression */
exp:
  exp0  { E.Top ($1, None) }
| exp0 KS_2_COLON context KS_R_W_ARROW typ  { E.Top ($1, Some ($5, Some $3)) }  /*(expression type signature)*/
| exp0 KS_2_COLON typ  { E.Top ($1, Some ($3, None)) }  /*(expression type signature)*/

/*
lexp6:
  - exp7
;
*/

/*
expi    ->      expi+1 [qop(n,i) expi+1]
        |       lexpi
        |       rexpi
lexpi   ->      (lexpi | expi+1) qop(l,i) expi+1
rexpi   ->      expi+1 qop(r,i) (rexpi | expi+1)
*/

/*
exp0:   ->      [-] exp10 {qop [-] exp10}
*/

exp0:
  op2_expn_list  { E.Exp0 $1 }

op2_expn_list:
  ks_minus exp10 op2_right_section  { E.ExpF (E.Minus $2, $3) }
| exp10 op2_right_section  { E.ExpF ($1, $2) }
| ks_minus exp10  { E.ExpF (E.Minus $2, E.Op2End) }
| exp10  { E.ExpF ($1, E.Op2End) }

op2_right_section:
  qop op2_expn_list { E.Op2F ($1, $2) }

op2_left_section:
  ks_minus exp10 qop op2_left_section  { E.ExpF (E.Minus $2, E.Op2F ($3, $4)) }
| exp10 qop op2_left_section  { E.ExpF ($1, E.Op2F($2, $3)) }
| ks_minus exp10 qop  { E.ExpF (E.Minus $2, E.Op2F ($3, E.Op2NoArg)) }
| exp10 qop  { E.ExpF ($1, E.Op2F ($2, E.Op2NoArg)) }

exp10:
  KS_B_SLASH apat_list KS_R_ARROW exp  { E.LambdaE ($2, $4) }   /*(lambda abstraction, n>=1)*/
| K_LET decl_list K_IN exp  { E.LetE ($2, $4) }         /*(let expression)*/
| K_IF exp K_THEN exp K_ELSE exp  { E.IfE ($2, $4, $6) }        /*(conditional)*/
| K_CASE exp K_OF SP_LEFT_BRACE alt_list SP_RIGHT_BRACE  { E.CaseE ($2, $5) }   /*(case expression)*/
| K_DO SP_LEFT_BRACE stmt_list_exp SP_RIGHT_BRACE  { E.DoE $3 }         /*(do expression)*/
| fexp  { E.FexpE $1 }

/*
 fexp    ->      [fexp] aexp     (function application)
*/

fexp:
  aexp_list  { E.make_fexp $1 }
;

aexp_list:
  aexp aexp_list  { fun fexp -> $2 (E.FappE (fexp, $1)) }
| aexp  { fun fexp -> E.FappE (fexp, $1) }
;
/* fexp -- FfunE (fexp) */
/* fexp ae1 -- FappE (FfunE (fexp), ae1) */
/* fexp ae1 ae2 -- FappE (FappE (FfunE (fexp), ae1), ae2) */
/* parser.mly  パタおよび  パタ */
.../*   */
pat:
  var ks_plus integer   /*(successor pattern)*/
      { match $3 with (S.Int (i), loc) -> P.PlusP($1, i, loc) | _ -> failwith "plus integer pattern syntax error." }
| pat0  { $1 }

/*
pati     ->      pati+1 [qconop(n,i) pati+1]
        |       lpati
        |       rpati
*/

/*
lpati   ->      (lpati | pati+1) qconop(l,i) pati+1
*/

/*
lpat6:
  ks_minus integer      (negative literal)
      { match $2 with
          (S.Int (v), l) -> S.P.MIntP (v, l)
         | _ -> failwith "negative integer literal pattern syntax error." }
| ks_minus float        (negative literal)
      { match $2 with
          (S.Float (v), l) -> S.P.MFloatP (v, l)
        | _ -> failwith "negative integer literal pattern syntax error." }
;
*/

/*
rpati   ->      pati+1 qconop(r,i) (rpati | pati+1)
*/

pat0:
  op2_patn_list  { P.Pat0 $1 }

op2_patn_list:
  ks_minus integer op2_patn_right
    { let p = match $2 with
        (S.Int (x), loc) -> P.MIntP (x, loc)
      | _ -> failwith "negative integer literal pattern syntax error."
      in P.PatF (p, $3)
    }
| ks_minus float op2_patn_right
    { let p = match $2 with
        (S.Float (x), loc) -> P.MFloatP (x, loc)
      | _ -> failwith "negative integer literal pattern syntax error."
      in P.PatF (p, $3)
    }
| pat10 op2_patn_right  { P.PatF ($1, $2) }

op2_patn_right:
  qconop op2_patn_list  { P.Op2F ($1, $2) }
|   { P.Op2End }

pat10:
  apat  { $1 }
| gcon apat_list        /*(arity gcon = k, k>=1)*/
      { P.ConP($1, $2) }

apat_list:
  apat apat_list { $1::$2 }
| apat           { [$1] }

apat:
  var
      { P.VarP $1 }
| var KS_AT apat        /*(as pattern)*/
      { P.AsP($1, $3) }
| gcon  /*(arity gcon = 0)*/
      { P.ConP($1, []) }
| qcon SP_LEFT_BRACE fpat_list SP_RIGHT_BRACE   /*(labeled pattern, k>=0)*/ /* may be error pattern */
      { P.LabelP($1, $3) }
| literal
      { P.LiteralP($1) }
| K_WILDCARD    /*(wildcard)*/
      { P.WCardP }
| SP_LEFT_PAREN pat SP_RIGHT_PAREN      /*(parenthesized pattern)*/
      { $2 }
| SP_LEFT_PAREN tuple_pat SP_RIGHT_PAREN        /*(tuple pattern, k>=2)*/
      { P.TupleP $2 }
| SP_LEFT_BRACKET list_pat SP_RIGHT_BRACKET     /*(list pattern, k>=1)*/
      { P.ListP $2 }
| KS_TILDE apat         /*(irrefutable pattern)*/
      { P.Irref $2 }

算子優先  

文解了した算子優先ないます文木的に辿り算式交互リス 造にえます

的には算式側の 2 つの単項表現expaa, expbb および 2 つの算子opaa, opbb についてすると 算式りのいずれかのになているはずです

< >
/
opaa
/ \
expaa
expbb
< >
/ /
opaa
opbb
/ /
expaa
expbb

このをもとに算子優先則を慮しつつしのすると以下のようになり ます

  type ’exp op2list_opf =
      Op2F of (ID.idwl * ’exp op2list_expf)
    | Op2End
  and ’exp op2list_expf =
      ExpF of (’exp * ’exp op2list_opf)
(*     | UniOpF of (ID.idwl * ’exp * ’exp op2list_opf) *)
    | Op2NoArg
  ... (*   *)
  let rec explist2term func list =
    let exp10_fun = SYA.maptree_exp10 func in

    let rec fold_leafs list =
      let scanned_op2exp op expAA expBB =
        E.VarOp2E (op,
                   exp10_fun expAA,
                   exp10_fun expBB) in
        match list with
          | E.ExpF (exp, E.Op2End) -> (* list *)
              E.uni_exp (exp10_fun exp)
          | E.ExpF (expAA, E.Op2F (op_aa,
                                   (E.ExpF (expBB, E.Op2End)))) ->
              E.uni_exp (scanned_op2exp op_aa expAA expBB)
          | E.ExpF (expAA, E.Op2F ((op_aa, _) as op_aa_wl,
                                   ((E.ExpF (expBB, E.Op2F ((op_bb, _) as op_bb_wl, rest))) as cdr))) ->
              begin
                let (aa_fixity, _) = eval_op2_fixity modbuf op_aa in
                let (bb_fixity, _) = eval_op2_fixity modbuf op_bb in
                  (* F.printf "(%s, %d) vs (%s, %d)\n" (ID.name_str op_aa) (snd aa_fixity) (ID.name_str op_bb) (snd bb_fixity); *)
                  match (aa_fixity, bb_fixity) with
                    | ((_, aa_i), (_, bb_i)) when aa_i > bb_i ->
                        fold_leafs (E.expf_cons (scanned_op2exp op_aa_wl expAA expBB) op_bb_wl rest)
                    | ((SYN.InfixLeft, aa_i), (SYN.InfixLeft, bb_i)) when aa_i = bb_i ->
                        fold_leafs (E.expf_cons (scanned_op2exp op_aa_wl expAA expBB) op_bb_wl rest)
                    | ((_, aa_i), (_, bb_i)) when aa_i < bb_i ->
                        E.expf_cons expAA op_aa_wl (fold_leafs cdr)
                    | ((SYN.InfixRight, aa_i), (SYN.InfixRight, bb_i)) when aa_i = bb_i ->
                        E.expf_cons expAA op_aa_wl (fold_leafs cdr)
                    | _ ->
                        failwith (F.sprintf "Syntax error for operator priority. left fixity %s, right fixity %s"
                                    (SYN.fixity_str aa_fixity)
                                    (SYN.fixity_str bb_fixity))
              end
          | _ -> failwith "Arity 2 operator expression syntax error."
    in
      match fold_leafs list with
        | E.ExpF (exp, E.Op2End) -> exp
        | E.ExpF (exp, E.Op2F (_, E.Op2NoArg)) -> failwith "explist2term: section not implemented."
        | folded -> explist2term func folded
  type ’pat op2list_opf =
      Op2F of (ID.idwl * ’pat op2list_patf)
    | Op2End
  and ’pat op2list_patf =
      PatF of (’pat * ’pat op2list_opf)
    | Op2NoArg
  ... (*   *)
  let rec patlist2term min_i func list =
    let pat_fun = SYA.maptree_pat func in

    let rec fold_leafs list =
      let scanned_op2pat op patAA patBB =
        P.ConOp2P (op,
                   pat_fun patAA,
                   pat_fun patBB) in

        match list with
          | P.PatF (pat, P.Op2End) ->
              P.uni_pat (pat_fun pat)
          | P.PatF (patAA, P.Op2F (op_aa_wl, (P.PatF (patBB, P.Op2End)))) ->
              P.uni_pat (scanned_op2pat op_aa_wl patAA patBB)
          | P.PatF (patAA, P.Op2F ((op_aa, _) as op_aa_wl, ((P.PatF (patBB, P.Op2F ((op_bb, _) as op_bb_wl, rest))) as cdr))) ->
              begin
                let (aa_fixity, _) = eval_op2_fixity modbuf op_aa in
                let (bb_fixity, _) = eval_op2_fixity modbuf op_bb in
                  match (aa_fixity, bb_fixity) with
                      ((_, aa_i), _) when aa_i < min_i ->
                        failwith (F.sprintf "Pat%d cannot involve fixity %s operator." min_i (SYN.fixity_str aa_fixity))
                    | (_, (_, bb_i)) when bb_i < min_i ->
                        failwith (F.sprintf "Pat%d cannot involve fixity %s operator." min_i (SYN.fixity_str bb_fixity))
                    | ((_, aa_i), (_, bb_i)) when aa_i > bb_i ->
                        fold_leafs (P.patf_cons (scanned_op2pat op_aa_wl patAA patBB) op_bb_wl rest)
                    | ((SYN.InfixLeft, aa_i), (SYN.InfixLeft, bb_i)) when aa_i = bb_i ->
                        fold_leafs (P.patf_cons (scanned_op2pat op_aa_wl patAA patBB) op_bb_wl rest)
                    | ((_, aa_i), (_, bb_i)) when aa_i < bb_i ->
                        P.patf_cons patAA op_aa_wl (fold_leafs cdr)
                    | ((SYN.InfixRight, aa_i), (SYN.InfixRight, bb_i)) when aa_i = bb_i ->
                        P.patf_cons patAA op_aa_wl (fold_leafs cdr)
                    | _ ->
                        failwith (F.sprintf "Syntax error for operation priority. left fixity %s, right fixity %s"
                                    (SYN.fixity_str aa_fixity)
                                    (SYN.fixity_str bb_fixity))
              end
          | _ -> failwith "Arity 2 operator pattern syntax error."
    in
      match fold_leafs list with
        | P.PatF (pat, P.Op2End) -> pat
        | P.PatF (pat, P.Op2F (_, P.Op2NoArg)) -> failwith "patlist2term: section not implemented."
        | folded -> patlist2term min_i func folded

5.6 Haskell 評価

5.2.1でもべたしてゆく方法Haskell 評価するために以下(env_t)単純 closure(lambda_t)のための closure(closure_t) しました

での closure 仮引リストbody expressionおよび3 つをているでしたこち らで同じたす lambda_t ではHaskell 仮引パタ(pattern match) なう ので仮引リストわりに pattern リストているのとHaskell 束縛およびパタ による束縛における where するためのするのルドやしてい ます

またHaskell pattern match によ複数expression 分ける能を単純closure える のはなため複数closure つことのできるclosure_t 導入しました

type lambda_t = {
  arg_pat_list : P.pat list;
  body : E.t;
  lambda_env : env_t;
  apply_where : (env_t -> env_t);
}

and closure_t =
  | SPat of (lambda_t)
  | MPat of (lambda_t list)
  | Prim of (thunk_t list -> value_t)

and value_t =
  | Bottom
  | IO
  | Literal of SYN.literal
  | Cons of (ID.id * (thunk_t list))
  | LabelCons of (ID.id * (ID.id, thunk_t) OH.t )
  | Tuple of (thunk_t list)
  | List of (thunk_t list)
  | Closure of (closure_t * int * E.aexp list)

and thunk_t = unit -> value_t

and pre_value_t =
    Thunk of (unit -> value_t)
  | Thawed of value_t

and scope_t = (S.t, thunk_t) H.t

(*  あるスコでの *)
and env_t = {
  symtabs : (scope_t) list;
  top_scope : scope_t;
}
5.6.1 評価

Haskell 評価略はデフルト評価 (lazy evaluation) ですのようなプログラムしてg (f 1 2) 2評価 ることをえてみます

f x y = x + y
g x y = x * y

単にするために+, *プリミテであるということにするとまずg評価すると(f 1 2) * 2となります ぎに*はそれ評価しても味がないプリミテなので f 評価され(1 + 2) * 2となり以下3 * 2, 6とな 了します

他の多数プログラミング言語eager evaluation しているものが多くその評価された あとに評価されますきくだしてみるとg (f 1 2) 2-> g (1 + 2) 2-> g 3 2-> g 3 2-> 3 * 2 -> 6のようなじになるはずです

評価  

lazy evaluation するには評価するときに最後まで評価するのではなくなうような closure 生成することですることができますこのさな closure をここでは thunk んでいますenv_t ているthunk_t にしているのはそのためです

make_thunk thunk ごとに評価(Thunk) または評価(Thawed) する pre_value_t 造を ていますthunk めてしたときに評価なわれてされthunk しでは単に Thawed するるようになります

and thunk_t = unit -> value_t

and pre_value_t =
    Thunk of (unit -> value_t)
  | Thawed of value_t

and scope_t = (S.t, thunk_t) H.t

(*  あるスコでの *)
and env_t = {
  symtabs : (scope_t) list;
  top_scope : scope_t;
}
...(*   *)
let thunk_value thunk =
  match thunk with
      Thunk (f) -> f ()
    | Thawed (v) -> v

let expand_thunk thunk_ref =
  match !thunk_ref with
      Thunk (_)  ->
        let v = thunk_value (!thunk_ref) in
        let _ = thunk_ref := Thawed v in
          v
    | Thawed (v) -> v

let make_thawed value =
  (fun () -> value)

let make_thunk eval_fun env evalee =
  let delay_fun = fun () -> (eval_fun env evalee) in
  let thunk_ref = ref (Thunk delay_fun) in
    fun () -> expand_thunk thunk_ref
5.6.2 パタ

Haskell パタ(pattern match) lazy evaluation わさるようにして動作します評価必要部分 しか pattern match する expression 評価しないように動きます

プログラムえます

main = let { (p, (q, r)) = (print 1, (print 2, print 3)) } in
       q

このプログラムすると2のみがされます(p, (q, r))(print 1, (print 2, print 3))pattern match なわれますが必要になるqのみが最後まで評価されます

パタ  

pattern match pattern 造分解ないその造分解した thunk から thunk への分解 いきます端にpattern があればthunk みますpattern match をたとえば case 認識する ためにしています

たとえばタプルまずタプルするはずの thunk 評価タプル分解しますタプルのそれ ぞれのはやはりまた thunk になているのでタプルpattern のそれぞれのpattern match なう ようにしますそれぞれのpattern match すればタプルpattern match です

(* Lazy pattern match against thunk *)
and bind_pat_with_thunk pat =
  let sub_patterns_match env pat_list thunk_list =
    L.fold_left2
      (fun (matchp_sum, tlist_sum) pat thunk ->
         let (matchp, tlist) = bind_pat_with_thunk pat env thunk in
           (matchp_sum & matchp, L.append tlist_sum tlist))
      (true, [])
      pat_list
      thunk_list
  in
    match pat with
... (*   *)
      | P.VarP (id, _) ->
          (fun env thunk ->
             let _ = bind_thunk_to_env env id thunk in (true, [thunk]))

      | P.AsP ((id, _), pat) ->
          (fun env thunk ->
             let (_, (matchp, tlist)) = (bind_thunk_to_env env id thunk,
                                         bind_pat_with_thunk pat env thunk)
             in (matchp, thunk :: tlist))
... (*   *)
      | P.WCardP ->
          (fun _ thunk -> (true, [thunk]))

      | P.TupleP pat_list ->
          (fun env thunk ->
             let value = thunk () in
               match value with
                   Tuple (args) when (L.length args) = (L.length pat_list)
                     -> sub_patterns_match env pat_list args
                 | _ -> (false, [thunk]))

      | P.ListP pat_list ->
          (fun env thunk ->
             let value = thunk () in
               match value with
                   List (args) when (L.length args) = (L.length pat_list)
                     -> sub_patterns_match env pat_list args
                 | _ -> (false, [thunk]))
... (*   *)

5.7 まとめと今後

分量になてしまいましたがそれでもかなり端折言語プログラミングHaskell ライクインタプリタ するにあたしたトピしてみました

今後としては未実部分していくこととocamlyacc による parsing Packrat parsing えること でより沿文解エレガントなえるようにすることです

61 エリア Debian 2010 2
____________________________________________________________________________________________