2011-05-06 59 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