Menhir配置多解析器时跨文件引用%public规则报错问题
错误原因
你的配置存在以下几个问题,直接导致构建失败:
- 低级笔误:
test_parser.mly里规则绑定的列表变量是el,语义动作里错写成e,触发未定义变量报错。 - dune配置逻辑错误:
merge_into参数的作用是把多个mly文件合并为单个解析器模块,不会生成独立的Parser模块,和你要两个独立解析器的需求完全相悖;同时你新增的menhir stanza和原有stanza都包含parser模块,导致构建规则冲突。 - 规则引用逻辑错误:Menhir的
%public修饰符只是允许规则被其他文件导入,不会自动跨文件生效,你没有在test_parser.mly里显式引入parser.mly的内容,自然找不到expr规则。 - 声明缺失:规则里用了
COMMAtoken,但你既没有在任何mly文件里定义这个token,也没在lexer里加逗号对应的分词规则。 - 架构隐患:你当前只有一个lexer,如果强行拆成两个独立mly,两个mly生成的token类型是互相独立的,会出现lexer返回的token和解析器期望类型不匹配的错误,额外增加很多兼容成本。
修复方案
你当前场景是同一套词法规则下两个解析入口,优先用单mly多入口的实现方式,这是Menhir官方推荐的多入口写法,零额外兼容成本:
- 直接修改
parser.mly,补充COMMAtoken定义,新增test入口,修正变量名:
%token <int> INT %token PLUS %token EOL %token COMMA %left PLUS %start <int> main %start <int> test %% main: | e = expr EOL { e } test: | el = separated_nonempty_list(COMMA, expr) EOL { List.fold_left (+) 0 el } %public expr: | i = INT { i } | e1 = expr PLUS e2 = expr { e1 + e2 }
- 修改
lexer.mll,在分词规则里补充逗号的匹配逻辑:
| ',' { COMMA }
- 还原
bin/dune配置,删掉之前错误添加的test_parser相关menhir配置和merge_into参数,最终核心配置如下:
(ocamllex (modules lexer)) (menhir (modules parser) (flags --table) ) (executable (name main) (public_name program) (libraries menhirLib) ) (rule (targets parser_messages.ml) (deps parser.messages parser.mly) (action (with-stdout-to %{targets} (run menhir --compile-errors %{deps}))))
- 重新生成错误信息文件后构建即可:
menhir --list-errors bin/parser.mly > bin/parser.messages dune build
构建完成后直接调用Parser.main和Parser.test两个独立入口即可,所有规则自动复用,没有类型冲突。
如果你确实有强需求必须拆成两个独立mly文件(比如两个解析器用完全独立的词法规则、需要独立发布),需要抽离公共token和规则到单独的共享mly文件,再让两个解析器mly通过%include引入共享内容,同时注意要么给每个解析器配独立lexer,要么手动统一两个解析器的token类型,避免类型不匹配问题,这种方式配置成本高,非必要不选。
内容的提问来源于stack exchange,提问作者Lhooq
相关产品推荐
相关产品推荐

