2012-03-27 99 views
11

對於OCaml,我是一個完整的新手。我最近纔開始使用該語言(大約2周前),但不幸的是,我的任務是編寫語法分析器(解析器+詞法分析器,其功能是接受或不是句子)使用Menhir。現在,我在網上找到了關於OCaml和Menhir的一些資料:OCaml + Menhir編譯/寫入

The Menhir手冊。

This webpage for some French University course.

上折騰的主頁在SourceForge短巨石教程。

由derdon在github上的Menhir示例。

A book on OCaml (with a few things about ocamllex+ocamlyacc

隨機ocamllex教程由SooHyoung哦。

以及Menhir源代碼附帶的例子。

(我不能把兩個以上的超鏈接,所以我不能直接將您鏈接到一些我提到這裏的網站。對不起!)

所以,你可以看到,我我一直在拼命尋找越來越多的材料來幫助我製作這個節目。不幸的是,我仍然無法理解許多概念,因此我遇到了許多困難。

對於初學者,我不知道如何正確編譯我的程序。我一直在使用以下命令:

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

我的程序分爲四個不同的文件: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) 

第二個文件是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))) } 

第三個文件是parser.mly。

%start <bool> main 
%% 

main: 
| WRITEINT INT { true } 

第四個是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在大量的未綁定構造函數和未定義的開始符號的錯誤中爆炸?我應該使用什麼命令來正確編譯程序?我在哪裏可以找到有意義的材料來了解Menhir?

回答

8

更簡單的方法是去除Parser/Tokens間隔。正如托馬斯指出的,沒有必要聲明type token = ...,因爲它是由%token指令的menhir自動生成的。

所以,你可以定義爲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 

編譯,這一切運作良好。

+0

事實上,只有一個'mly'就簡單多了。我沒有在我的答案中提出這個解決方案,因爲我認爲@Lopson想要使用menhir的「單獨編譯解析單元」功能。 – Thomas 2012-03-28 08:00:12

+0

感謝所有的幫助,各位,你不知道你的帖子對我來說有多寶貴!最後,事情開始變得有意義。 – 2012-03-28 11:19:19

7

因此,首先,你不需要repet的令牌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,我不知道menhir非常好,但是,在我的理解,你需要「打包」所有的.mly到一個分析單元:

menhir tokens.mly parser.mly -base parser 

然後,如果你在lexer.mll取代Token任何發生BYT Parserocamlbuild -no-hygiene main.byte應該可以工作。但請注意,可能有一個聰明的方法來做到這一點。

1

我遇到了同樣的問題,除了解析器需要當前直接之外的模塊。我無法弄清楚如何調用ocamlbuild指定解析器{毫升,美林}必須從3個MLY文件生成,所以我只是做了一個生成文件:

  • 複製模塊從_build .cmi到當前目錄(滿足巨石--infer)
  • 調用巨石
  • 刪除複製的模塊,以滿足ocamlbuild
  • 然後調用ocamlbuild

我不滿意,所以我很感興趣在任何更好的ALT但是如果你真的必須以最小的努力完成你的項目,我想這就是要走的路線

編輯: 實際上,沒有必要複製和刪除編譯模塊,只需將選項傳遞給menhir第二步: menhir --ocamlc「ocamlc -I \」../_ build/modules/\「」--infer --base parser

不幸的是,這仍然意味着解析器的生成將與以前的編譯模塊,因此預期不必要的(並且失敗的)第一編譯。