Ocaml 解析器在解析过程中停止

Ocaml 解析器在解析过程中停止,ocaml,ocamlyacc,Ocaml,Ocamlyacc,我完全没有主意了。我把这一天的每一分钟都花在这上面,但我完全没有主意了 这是我的Ocamlyacc语法: input: /* empty */ { } | input stmt { } stmt: extern { print_endline "Got an extern import" } | func { print_endline "Got function definition" } | call { print_endline "Got funct

我完全没有主意了。我把这一天的每一分钟都花在这上面,但我完全没有主意了

这是我的
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+b
a
,然后停止。没有错误信息,没有其他信息。它只是停止,就好像整个文本已被完全解析,而不符合
stmt
中的任何规则一样

没有换档/减速错误或类似错误。AST类型也不是问题所在。我再也不知道了,也许其他人可以帮忙。当然这是显而易见的,但我看不到

编辑:按流行需求分类的Lexer:

{
    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 }

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

问题是两个国家之间微妙的相互作用

| eof { raise End_of_file }
词法规则和你的语法

在lexer中的EOF上提升
Enf_文件
,如果您的语法从未自然遇到文件的结尾,那么这是一个好主意。例如,自然以
\n
-结尾或
;结尾的语法-terminated将在此时停止解析,并且永远不会访问EOF令牌

但你的语法不是其中之一。当解析器到达
DEF proto expr.
时,它会请求下一个令牌,以查看它是否是(偶然的)和
OP
,因此它调用lexer,该lexer会查找
EOF

以下是我的解决方案:

在lex.mll中:

    | eof { EOF }
在parse.mly中: %令牌EOF

%start stmt_eof
%type <Types.stmt> stmt_eof

[...]

stmt_eof: stmt EOF { $1 }
%start stmt\u eof
%类型stmt_eof
[...]
stmt_eof:stmt eof{$1}

最后,你应该认真考虑作为OcAMLACC的替代品。它做了ocamlyacc所做的一切,只是更好,有了更清晰的语法文件(例如,你不必每次都重新创建
foo\u列表
nonterminal)、更好的错误消息、调试功能……

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

问题是两个国家之间微妙的相互作用

| eof { raise End_of_file }
词法规则和你的语法

在lexer中的EOF上提升
Enf_文件
,如果您的语法从未自然遇到文件的结尾,那么这是一个好主意。例如,自然以
\n
-结尾或
;结尾的语法-terminated将在此时停止解析,并且永远不会访问EOF令牌

但你的语法不是其中之一。当解析器到达
DEF proto expr.
时,它会请求下一个令牌,以查看它是否是(偶然的)和
OP
,因此它调用lexer,该lexer会查找
EOF

以下是我的解决方案:

在lex.mll中:

    | eof { EOF }
在parse.mly中: %令牌EOF

%start stmt_eof
%type <Types.stmt> stmt_eof

[...]

stmt_eof: stmt EOF { $1 }
%start stmt\u eof
%类型stmt_eof
[...]
stmt_eof:stmt eof{$1}

最后,你应该认真考虑作为OcAMLACC的替代品。它做的一切都是
ocamlyacc
做的,只是更好,语法文件更清晰(例如,你不必每次都重新创建
foo\u列表
nonterminal),错误消息更好,调试功能更好……

看起来不错。肯定没什么明显的。莱克斯?看起来不错。肯定没什么明显的。谢谢,我切换到了
Menhir
并替换了
eof
规则。另外,尽管你讨厌我,还是谢谢你帮助我。@Scán:请注意,在
stmt
之后添加另一个要求
eof
rules通常是一个好主意:它确保语法只接受对输入进行整体解析的输入。如果你不这样做,并且你的语法中有一些错误,它可能会很高兴地返回它可以解析的最长前缀,而不是提醒你这个问题。好的,谢谢你的提示。现在我唯一的问题是让
ocamlbuild
找到
Llvm
模块。谢谢,我切换到
Menhir
并替换了
eof
规则。另外,尽管你讨厌我,还是谢谢你帮助我。@Scán:请注意,在
stmt
之后添加另一个要求
eof
rules通常是一个好主意:它确保语法只接受对输入进行整体解析的输入。如果你不这样做,并且你的语法中有一些错误,它可能会很高兴地返回它可以解析的最长前缀,而不是提醒你这个问题。好的,谢谢你的提示。现在我唯一的问题是让
ocamlbuild
找到
Llvm
模块。