14

OCamlに関しては、私は完全な初心者です。私は最近(約2週間前に)その言語を使い始めたばかりですが、残念ながら、構成された言語の構文アナライザー(パーサー+レクサー、その機能は文を受け入れるかどうかのどちらかです)を作成する必要がありますMenhirを使用します。今、私はインターネット上でOCamlとMenhirに関するいくつかの資料を見つけました:

メンヒルマニュアル。

いくつかのフランス語大学のコースのこのウェブページ。

SourceforgeのTossのホームページにあるMenhirの短いチュートリアル。

derdonによるgithubのMenhirの例。

OCamlに関する本(ocamllex +ocamlyaccに関するいくつかのことを含む)

SooHyoungOhによるランダムなocamllexチュートリアル。

そして、Menhirのソースコードに付属している例。

(2つ以上のハイパーリンクを配置できないため、ここで言及しているWebサイトの一部に直接リンクすることはできません。申し訳ありません!)

ご覧のとおり、このプログラムの作成に役立つ資料を必死に探しています。残念ながら、私はまだ多くの概念を理解することができず、そのため、私は多くの、多くの困難を抱えています。

手始めに、プログラムを正しくコンパイルする方法がわかりません。私は次のコマンドを使用しています:

ocamlbuild -use-menhir -menhir "menhir --external-tokens Tokens" main.native

私のプログラムは4つの異なるファイルに分かれています:main.ml; lexer.mll; parser.mly; tokens.mly。main.mlは、引数として指定されたファイルシステム内のファイルから入力を取得する部分です。

let filename = Sys.argv.(1)

let () =
    let inBuffer = open_in filename in
    let lineBuffer = Lexing.from_channel inBuffer in
    try
        let acceptance = Parser.main Lexer.main lineBuffer in
        match acceptance with
            | true -> print_string "Accepted!\n"
            | false -> print_string "Not accepted!\n"
    with
        | Lexer.Error msg -> Printf.fprintf stderr "%s%!\n" msg
        | Parser.Error -> Printf.fprintf stderr "At offset %d: syntax error.\n%!" (Lexing.lexeme_start lineBuffer)

2番目のファイルはlexer.mllです。

{
  open Tokens
  exception Error of string
}

rule main = parse
  | [' ' '\t']+
      { main lexbuf }
  | ['0'-'9']+ as integer
      { INT (int_of_string integer) }
  | "True"
      { BOOL true }
  | "False"
      { BOOL false }
  | '+'
      { PLUS }
  | '-'
      { MINUS }
  | '*'
      { TIMES }
  | '/'
      { DIVIDE }
  | "def"
      { DEF }
  | "int"
      { INTTYPE }
  | ['A'-'Z' 'a'-'z' '_']['0'-'9' 'A'-'Z' 'a'-'z' '_']* as s
      { ID (s) }
  | '('
      { LPAREN }
  | ')'
      { RPAREN }
  | '>'
      { LARGER }
  | '<'
      { SMALLER }
  | ">="
      { EQLARGER }
  | "<="
      { EQSMALLER }
  | "="
      { EQUAL }
  | "!="
      { NOTEQUAL }
  | '~'
      { NOT }
  | "&&"
      { AND }
  | "||"
      { OR }
  | '('
      { LPAREN }
  | ')'
      { RPAREN }
  | "writeint"
      { WRITEINT }
  | '\n'
      { EOL }
  | eof
      { EOF }
  | _
      { raise (Error (Printf.sprintf "At offset %d: unexpected character.\n" (Lexing.lexeme_start lexbuf))) }

3番目のファイルはparser.mlyです。

%start <bool> main
%%

main:
| WRITEINT INT { true }

4つ目はtokens.mlyです

%token <string> ID
%token <int> INT
%token <bool> BOOL
%token EOF EOL DEF INTTYPE LPAREN RPAREN WRITEINT
%token PLUS MINUS TIMES DIVIDE
%token LARGER SMALLER EQLARGER EQSMALLER EQUAL NOTEQUAL
%token NOT AND OR

%left OR
%left AND
%nonassoc NOT
%nonassoc LARGER SMALLER EQLARGER EQSMALLER EQUAL NOTEQUAL
%left PLUS MINUS
%left TIMES DIVIDE
%nonassoc LPAREN
%nonassoc ATTRIB

%{
type token =
  | ID of (string)
  | INT
  | BOOL
  | DEF
  | INTTYPE
  | LPAREN
  | RPAREN
  | WRITEINT
  | PLUS
  | MINUS
  | TIMES
  | DIVIDE
  | LARGER
  | SMALLER
  | EQLARGER
  | EQSMALLER
  | EQUAL
  | NOTEQUAL
  | NOT
  | AND
  | OR
  | EOF
  | EOL
%}

%%

ここに未使用のシンボルがたくさんあることはわかっていますが、パーサーでそれらを使用するつもりです。ファイルにいくつ変更を加えても、コンパイラーは私の顔を爆破し続けます。私は考えられるすべてのことを試しましたが、何もうまくいかないようです。バインドされていないコンストラクターと未定義の開始シンボルの多数のエラーでocamlbuildを爆発させているのは何ですか?プログラムを正しくコンパイルするには、どのコマンドを使用する必要がありますか?メンヒルについて学ぶための意味のある資料はどこにありますか?

4

3 に答える 3

10

これを行う簡単な方法は、Parser/Tokens分離を削除することです。トーマスが指摘したように、宣言はメンヒルによってディレクティブtype token = ...から自動的に生成されるため、宣言の必要はありません。%token

したがって、次のように定義できますparser.mly

%start <bool> main

%token <string> ID
%token <int> INT
%token <bool> BOOL
%token EOF EOL DEF INTTYPE LPAREN RPAREN WRITEINT
%token PLUS MINUS TIMES DIVIDE
%token LARGER SMALLER EQLARGER EQSMALLER EQUAL NOTEQUAL
%token NOT AND OR

%left OR
%left AND
%nonassoc NOT
%nonassoc LARGER SMALLER EQLARGER EQSMALLER EQUAL NOTEQUAL
%left PLUS MINUS
%left TIMES DIVIDE
%nonassoc LPAREN
%nonassoc ATTRIB
%%

main:
| WRITEINT INT { true }

そしてとlexer.mllして:

{
  open Parser
  exception Error of string
}

[...] (* rest of the code not shown here *)

次に、を削除tokens.mlyし、でコンパイルします

ocamlbuild -use-menhir main.native

そしてそれはすべてうまくいきます。

于 2012-03-28T04:34:52.393 に答える
7

そのため、最初に、次のトークンを繰り返す必要はありませんtokens.mly

%token <string> ID
%token <int> INT
%token <bool> BOOL
%token EOF EOL DEF INTTYPE LPAREN RPAREN WRITEINT
%token PLUS MINUS TIMES DIVIDE
%token LARGER SMALLER EQLARGER EQSMALLER EQUAL NOTEQUAL
%token NOT AND OR

%left OR
%left AND
%nonassoc NOT
%nonassoc LARGER SMALLER EQLARGER EQSMALLER EQUAL NOTEQUAL
%left PLUS MINUS
%left TIMES DIVIDE
%nonassoc LPAREN
%nonassoc ATTRIB

%%

次に、渡す魔法のオプションがocamlbuildわからず、よくわかりませんが、私の理解では、すべてを1つのパーサーユニットmenhirに「パック」する必要があります。.mly

menhir tokens.mly parser.mly -base parser

次に、で出現するTokenバイトParserを置き換えるとlexer.mllocamlbuild -no-hygiene main.byte機能するはずです。ただし、それを行う賢い方法があるかもしれないことに注意してください。

于 2012-03-27T21:26:12.927 に答える
1

さらに、パーサーが現在のダイレクト以外のモジュールを必要とすることを除いて、同じ問題に遭遇しました。parser.{ml,mli} を 3 つの mly ファイルからビルドする必要があることを指定するために ocamlbuild を呼び出す方法がわかりませんでした。

  • モジュール .cmi を _build から現在のディレクトリにコピーします (menhir --infer を満たすため)
  • メンヒルを呼び出す
  • コピーされたモジュールを削除して、ocamlbuild を満たす
  • 次に、ocamlbuild を呼び出します

私はそれに満足していないので、より良い代替案に興味がありますが、本当にプロジェクトを最小限の労力で終わらせる必要がある場合は、それが道だと思います.

編集: 実際には、コンパイルされたモジュールをコピーして削除する必要はありません。2 番目のステップでオプションを menhir に渡すだけです: menhir --ocamlc "ocamlc -I \"../_build/modules/\"" --infer --基本パーサー

悲しいことに、これはまだ、パーサーの生成がモジュールの以前のコンパイルに基づいていることを意味するため、不要な (そして失敗した) 最初のコンパイルが予想されます。

于 2013-03-01T09:42:28.290 に答える