2011-05-06 75 views
0

我完全沒有想法。我今天花了每一分鐘的時間,但我完全沒有想法。解析器停止mid-parse

這是我Ocamlyacc語法:

input: /* empty */ { } 
    | input stmt { } 

stmt: 
    extern { print_endline "Got an extern import" } 
    | func { print_endline "Got function definition" } 
    | call { print_endline "Got function call" } 

extern: 
    EXTERN proto { Extern $2 } 

func: 
    DEF proto expr { Function ($2, $3) } 

proto: 
    IDENTIFIER LPAREN id_list RPAREN { print_endline "Got prototype definition"; Prototype ($1, $3) } 

id_list: 
    /* empty */ { [] } 
    | IDENTIFIER { [$1] } 
    | id_list COMMA IDENTIFIER { $3 :: $1 } 

expr_list: 
    /* empty */ { [] } 
    | expr { [$1] } 
    | expr_list COMMA expr { $3 :: $1 } 

expr: 
    call { $1 } 
    | expr OP expr { Binary ($2, $1, $3) } 
    | IDENTIFIER { Variable $1 } 
    | NUMBER { Number $1 } 
    | LPAREN expr RPAREN { $2 } 

call: 
    IDENTIFIER LPAREN expr_list RPAREN { Call ($1, $3) } 

當我開始分析def foo(a,b) a+b應該告訴我它有一個功能和原型聲明,根據調試消息。但是,相反,我只收到解析proto規則的消息。

進一步的調試消息顯示解析器會盡可能地與表達式a+ba然後停止。沒有錯誤信息,沒有別的。它只是停止,如果整個文本帽子完全解析,而不符合stmt中的任何規則。

沒有移位/減少錯誤或相似。 AST類型也不是問題。我不知道任何更多,也許別人可以幫助。當然,這是顯而易見的,但我看不到它。

編輯:詞法大衆的需求:

{ 
    open Parser 
} 

rule token = parse 
    | [' ' '\t' '\n'] { token lexbuf } 
    | "def" { DEF } 
    | "extern" { EXTERN } 
    | "if" { IF } 
    | "then" { THEN } 
    | "else" { ELSE } 
    | ['+' '-' '*' '/'] as c { OP c } 
    | ['A'-'Z' 'a'-'z'] ['A'-'Z' 'a'-'z' '0'-'9' '_']* as id { IDENTIFIER id } 
    | ['0'-'9']*'.'['0'-'9']+ as num { NUMBER (float_of_string num) } 
    | '(' { LPAREN } 
    | ')' { RPAREN } 
    | ',' { COMMA } 
    | '#' { comment lexbuf } 
    | _ { raise Parsing.Parse_error } 
    | eof { raise End_of_file } 
and comment = parse 
    | '\n' { token lexbuf } 
    | _ { comment lexbuf } 
+0

看起來不錯。絕對沒有明顯的。 lexxer? – nlucaroni 2011-05-06 21:32:51

回答

4

第一點:我恨你有點不給予編譯源代碼。我不得不重新創建AST類型,%token聲明等來測試您的代碼。

的問題是

| eof { raise End_of_file } 

詞法規則,你的語法之間的微妙的相互作用。

在詞法分析器中提高EOF上的Enf_of_file是一個好主意,如果您的語法永遠不會自然地遇到文件的末尾。例如,語法爲\n-termination或;;-terminmin的語法將在此時停止解析,並且永遠不會到達EOF標記。

但是你的語法不是其中之一。當解析器到達DEF proto expr .時,它會詢問下一個標記,看看它是否是偶然的,因此它會調用詞法分析器,它會發現EOF,併發出聲音。

這裏是我的解決辦法:

在lex.mll:

| eof { EOF } 

在parse.mly: %令牌EOF

%start stmt_eof 
%type <Types.stmt> stmt_eof 

[...] 

stmt_eof: stmt EOF { $1 } 

最後,你應該認真考慮Menhir作爲替換爲ocamlyacc。它做的一切ocamlyacc,只有更好,更清晰的語法文件(例如,你不必重新發明foo_list nonterminal每次),更好的錯誤信息,調試功能...

+0

謝謝,我切換到'Menhir'並替換了'eof'規則。另外,謝謝你幫助我,儘管你恨我。 – Lanbo 2011-05-07 08:39:24

+0

@Scán:請注意,在'stmt'之後添加不同的'stmt_eof'規則通常是一個好主意:它確保語法只接受解析輸入,如果它可以解析它*整個*。如果你不這樣做,並在你的語法中有一些錯誤,它可能會愉快地返回它可以解析的最長的前綴,而不是提醒你這個問題。 – gasche 2011-05-07 10:29:05

+0

好的謝謝你的提示。現在我唯一的問題是讓'ocamlbuild'找到'Llvm'模塊。 – Lanbo 2011-05-07 13:13:19