たとえば、 "1+2*3" といった文字列が与えられたとき、 "+" と "*" の優先度に従って、"1+(2*3)" という形で読みこみ、 それを、この実験で作成する処理系の内部データ構造に変換する。
BNF での構文の定義
int_lit ::= 0 | 1 | 2 | 3 | ... (integer literal; 自然数の定数) exp ::= int_lit | Plus(exp,exp) | Times(exp,exp) (expression; 式)
OCaml のデータ型の定義
type exp = IntLit of int (* 整数リテラル *) | Plus of exp * exp (* 足し算 *) | Times of exp * exp (* かけ算 *)type というキーワードを使って、新しいデータ型 exp を定義した。 このデータ型は、BNF とは多少違ってはいるが (int_lit が IntLit になるなど)、 本質的に BNF定義された exp と同等のものを表していることが理解できよう。 たとえば、BNF では Plus(0,1) という式は、exp型のデータとしては、 Plus(IntLit(0),IntLit(1))と表現される。 exp型の定義における exp * exp というのは、「exp型とexp型の直積型」とい う意味であり、Plusの引数が exp型のデータ2つであることを意味している。
なお、exp, int という「型」の名前が小文字で始まっていて、 IntLit, Plus, Times という「データを構成するもの(構成子)」の名前が大文字で始まっているのは、 OCaml言語での規約による。 (大文字で始まる単語と、小文字で始まる単語は使える場所が明確に異なる。 それを間違えると、OCamlでは、意味を理解しにくいエラーが出ることが多い。)
テスト・データ:
let e0 = IntLit(0) ;; let e1 = IntLit(1) ;; let e2 = IntLit(2) ;; let e3 = Minus(e2,e1) ;; let e4 = Div(e3,e1) ;; let e5 = Div(e4,Minus(e4,e4)) ;; let e6 = Div(e4,Plus(e2,e1)) ;;
テスト・データ:
is_literal e0 --> true is_literal e1 --> true is_literal e3 --> false is_literal e4 --> false is_literal e5 --> false
type exp = | IntLit of int (* リテラル *) | Plus of exp * exp (* 足し算 *) | Times of exp * exp (* かけ算 *)この方が、文字は1文字多いが、一様な定義となって見易いので、 これ以降、こちらの書き方を採用する。
構文解析アルゴリズムは、古くからよく研究されており、 C言語に対しては、言語の文法規則等をBNF風の書式で記述するだけで、 その文法に特化した構文解析器を自動生成してくれる yacc/lex という夢のようなツールがある。 (OCaml では、ocamlyacc と ocamllex という名称である。) これらを使うと、 我々は、構文解析器をどう効率よく書くか、といったことで悩まなくて済む。 ただし、これらを最初から使うと、 内部データ構造のことがよくわからなくなってしまうので、 本実験では以下のプランで学習する。
亀山 幸義