我完全没有想法。我今天每一分钟都花在这上面,但我完全没有想法。
这是我的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
的{{1}}然后停止。没有错误消息,没有别的。它就像完全解析整个文本框而不符合a+b
中的任何规则一样停止。
没有移位/减少错误或类似错误。 AST类型也不是问题。我不知道了,也许别人可以提供帮助。当然这是明显的,但我看不到它。
编辑:受欢迎需求的Lexer:
stmt
答案 0 :(得分:4)
第一点:我因为没有提供可编译的源代码而讨厌你。我不得不重新发明AST类型,%token
声明等来测试你的代码。
问题是
之间的微妙相互作用| eof { raise End_of_file }
lexing规则和你的语法。
如果您的语法从不自然地遇到文件的末尾,那么在词法分析器中提高Enf_of_file
EOF是个好主意。例如,自然\n
- 被终止或;;
终止的语法将在此时停止解析,并且永远不会到达EOF令牌。
但你的语法不是其中之一。当解析器到达DEF proto expr .
时,它会要求下一个令牌,以查看它是否是偶然的OP
,因此它会调用找到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
非终结符号),更好的错误信息,调试功能......