字句解析の定義の記述法は以下の通りです。
{ header } let ident = regexp ... rule entrypoint [arg_1... arg_n] = parse regexp { action } | ... | regexp { action } and entrypoint [arg_1... arg_n] = parse ... and ... { trailer }
コメントは Caml と同じように (* と *) で区切られます。 parseキーワードはshortestキーワードで置き換えられますが、その意味は以下で解説します。
ヘッダ(header) と trailer セクションには中カッコ({ ... })でくくって任意の Caml コードを入れることが出来ます。これらはどちらも省略可能です。存在すれば、header は出力ファイルの先頭に、trailer は出力ファイルの終端に置かれます。大抵、header セクションには action で必要な openディレクティブを置きます。actions 内で使う補助関数を定義することもあります。
header と entry points のあいだで、頻繁に発生する正規表現に名前を与えることが出来ます。 let ident = regexp のように書きます。今後、正規表現を書くところに regexp の代わりに識別子 ident を使うことが出来ます。
エントリーポイントの名前は Caml の値の名前 (小文字で始まる) として有効な識別子である必要があります。同様に、引数arg1 ... argn
もCamlとして有効な識別子でなければなりません。
それぞれのエントリーポイントは n
+ 1 個の引数を受け問り、最後に追加される暗黙の引数の型はLexing.lexbufです。
文字を Lexing.lexbuf から読み、rule にある正規表現にマッチさせます。その正規表現に対応する action が評価され、関数の結果として返されます。
先頭文字列にマッチする正規表現が複数ある場合は、"最大マッチ" する正規表現が適用され、先頭文字列を長くして最後までマッチする正規表現を選択します。それでもふるいきれないときは、rule のなかで先に出現したものが優先されます。
しかし、parseキーワードの代わりにshortestキーワードが用いられている場合は、"最小マッチ"が適用されます:つまり最短の先頭文字列が選択されます。それでもふるいきれないときは、ruleの中で先に出現したものが優先されます。 この機能は通常の字句解析で用いられることを意図しておらず、ocamllexを単なるテキスト処理ツールとして用いることを想定しています。
正規表現は lex と同じですが、より Caml らしい文法です。
文字定数。Objective Caml の文字定数と同じ文法です。その文字とマッチします。
(アンダースコア) どんな文字とでもマッチします。
入力終端にマッチします。
警告: システムによっては対話式入力において、end-of-file のあとにさらに文字列が続く場合がありますが、ocamllex では eof の後に何かが続く正規表現を正しく扱うことは出来ません。
文字列定数。Objective Caml の文字列定数と同じ文法です。文字列にマッチします。
指定された文字集合に属する 1 文字とマッチします。有効な文字集合は、文字定数 1 つの ' c ' 、範囲 ' c1 ' - ' c2 ' (c1 と c2 自身を含むその間の文字すべて) 、または 2 つ以上の文字集合を結合したもののどれかです。
指定された文字集合に属さない 1 文字とマッチします。
(文字集合の差). regexp1 と regexp2 は [ ... ] (もしくは _ 以外の文字定数)で定義される文字集合でなければなりません。2つの指定された集合の差とマッチします。
(反復) regexp とマッチする文字列が 0 個以上連なった文字列にマッチします。
(厳しい反復) regexp とマッチする文字列が 1 個以上連なった文字列にマッチします。
(オプション) 空文字列か、regexp とマッチする文字列にマッチします。
(結合) regexp1 にマッチする文字列のあとに regexp2 にマッチする文字列を結合した文字列にマッチします。
regexp と同じです。
演算子の優先順位は、*
と +
が最優先、次に ?
、次に結合、最後に |
になります。
action は Caml の任意な式です。これらの式は lexbuf 識別子に現在の字句解析バッファが割り当てられた環境で評価されます。lexbuf の主な利用法を、Lexing 標準ライブラリモジュールの字句解析バッファ処理関数と関連付けながら以下に示します。
マッチした文字列を返します。
マッチした文字列の n 番目の文字を返します。最初の文字は n = 0 になります。
マッチした文字列の先頭の文字が入力文字列全体において何番目の文字であるかを返します。入力文字列の先頭は 0 です。
マッチした文字列の末尾の文字が入力文字列全体において何番目の文字であるかを返します。入力文字列の先頭は 0 です。
(entrypoint には、同じ字句解析器の定義内にある別のエントリーポイント名が入ります) 字句解析器の指定されたエントリーポイントを再帰的に呼びます。入れ子のコメントなどの字句解析に便利です。
'as'コンストラクタは多くの正規表現パッケージが提供している"グループ"に似ています。これらの変数の型は、string、char、string、option、char optionのいずれかです。
まず線形なパターン、つまりすべての束縛変数がお互いに異なる場合について考えてみましょう。''regexp as ident''において、identの型は通常string(もしくはstring option)になります。例外はregexpが文字列定数、アンダースコア、長さ1の文字列定数、文字集合、これらの選択の場合です。その場合、identの型はchar(もしくはchar option)になります。option型は、ルールのマッチングが必ず成功するとは限らない場合です。 例えば、( regexp as ident) ?やregexp_1 | ( regexp_2 as ident )のような場合です。
変数束縛が非線形の場合について考えてみましょう。 変数が一度以上束縛される場合、前述のルールは以下のように拡張さえれます。
前述したルールで、全ての変数出現が文字に束縛されている場合、変数は文字変数になります。
いずれかの式がこの変数を束縛しない場合は、変数はoptionになります。
例えば、'' ('a' as x) | ( 'a' (_ as x) )' ''において、変数''x''はchar型になります。一方、'' ("ab" as x) | ( 'a' (_ as x) ? )''において変数''x''はstring option型になります。
多くの場合、連続したマッチがユニークな束縛を生まないことがあります。 例えば、''aba''を'' (('a'|"ab") as x) (("ba"|'a') as y)''はxを"ab"にyを"a"に束縛するか、xを"a"にyを"ba"に束縛するかのいずれかになります。 camllexはこのような曖昧な正規表現の場合、ありうる束縛のうち、いずれかを選びます。ただし、選択の方法は規定されていません。