0

我完全没有想法。我今天把每一分钟的空闲时间都花在这上面,但我完全没有想法。

这是我的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表达式的a+b位置然后停止。没有错误信息,没有别的。它只是停止,好像整个文本都被完全解析而没有满足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 }
4

1 回答 1

4

第一点:我有点讨厌你没有提供可编译的源代码。我不得不重新发明 AST 类型、%token声明等来测试你的代码。

问题是两者之间微妙的相互作用

| eof { raise End_of_file }

词法规则和你的语法。

Enf_of_file如果您的语法永远不会自然地遇到文件末尾,那么在词法分析器中提高EOF 是一个好主意。例如,自然\n终止或;;终止的语法将在此时停止解析,并且永远不会到达 EOF 标记。

但你的语法不是其中之一。当解析器到达 时DEF proto expr .,它会偶然地询问下一个标记,看看它是否不是,并且OP,因此它调用词法分析器,它找到EOF,然后进行打击。

这是我的修复:

在 lex.mll 中:

    | eof { EOF }

在 parse.mly 中:%token EOF

%start stmt_eof
%type <Types.stmt> stmt_eof

[...]

stmt_eof: stmt EOF { $1 }

最后,您应该认真考虑将Menhir作为 ocamlyacc 的替代品。它所做的一切ocamlyacc,只有更好,更清晰的语法文件(例如,你不必foo_list每次都重新发明非终结符),更好的错误消息,调试功能......

于 2011-05-07T05:24:17.897 回答