我认为 ocamlyacc 中没有选项可以自动执行您想要的操作,因此让我尝试在下面提供一个完整的描述,说明可以采取哪些措施来处理语法错误并获得更多有用的消息。也许这不是你问的。
错误实际上必须在词法错误和解析错误中分开,这取决于错误发生在解析过程的哪个阶段。
- 在
mll
文件Failure
中,如果出现意外模式,将引发异常
- 在
mly
文件中,这是一个Parsing.Parse_error
将生成的异常
所以你有几个解决方案:
- 让词法分析器和解析器代码引发它们的异常,并在调用它们的代码中捕获它们
- 在其中任何一个中实施具体的错误案例
- 词法分析器的全部规则(或一些更具体的模式,如有必要)
- 使用
error
解析器规则中的特殊终端来捕获特定位置的错误
在任何情况下,您都必须创建函数来获取有关源中错误位置的信息。
Lexing
并且Parsing
两者都使用在location
中定义的记录,Lexing
其中包含以下字段:
- pos_fname : 当前处理的文件名
- pos_lnum : 文件中的行号
- pos_bol :从文件开始到行首的字符号
- pos_cnum : 当前位置的字符号
词法分析器使用的lexbuf
变量有两个类似的值来跟踪当前被词法分析的标记(并lexeme_start_p
让您访问这些数据)。解析器有四个来跟踪即将被合成的当前符号(或非终结符),以及当前规则项,可以使用函数(and ,以及and )检索。lexeme_curr_p
Lexing
Parsing
rhs_start_pos
rhs_end_pos
symbol_start_pos
symbol_end_pos
这里有一些函数可以生成更详细的异常:
exception LexErr of string
exception ParseErr of string
let error msg start finish =
Printf.sprintf "(line %d: char %d..%d): %s" start.pos_lnum
(start.pos_cnum -start.pos_bol) (finish.pos_cnum - finish.pos_bol) msg
let lex_error lexbuf =
raise ( LexErr (error (lexeme lexbuf) (lexeme_start_p lexbuf) (lexeme_end_p lexbuf)))
let parse_error msg nterm =
raise ( ParseErr (error msg (rhs_start_p nterm) (rhs_end_p nterm)))
和一些基本用例:
解析器:%token ERR
/* ... */
wsorword:
WS { $1 }
| WORD { $1 }
| error { parse_error "wsorword" 1; ERR "" } /* a token needed for typecheck */
;
词法分析器:
rule lexer = parse
(* ... *)
(* catch all pattern *)
| _ { lex_error lexbuf }
剩下要做的就是修改您的顶级函数以捕获异常并处理它们。
最后,出于调试目的,有一个set_trace
功能可以Parsing
启用解析引擎使用的状态机的显示消息:它跟踪自动机的所有内部状态变化。