为什么我的ocaml模块未定义?

时间:2017-02-22 03:28:43

标签: ocaml

我有一个用ocamllex和menhir构建的词法分析器和解析器,当我在顶层使用它们时它们工作,但它们构成的模块仍未定义。

~: ocamlbuild -clean
~: ocamlbuild PhoebeParser.cma PhoebeLexer.cma 
ocamlopt.opt unix.cmxa -I /Users/Tim/.opam/system/lib/ocamlbuild /Users/Tim/.opam/system/lib/ocamlbuild/ocamlbuildlib.cmxa myocamlbuild.ml /Users/Tim/.opam/system/lib/ocamlbuild/ocamlbuild.cmx -o myocamlbuild
menhir --infer --raw-depend --ocamldep 'ocamldep.opt -modules' PhoebeParser.mly > PhoebeParser.mly.depends
ocamldep.opt -modules PhoebeAST.ml > PhoebeAST.ml.depends
ocamlc.opt -c -o PhoebeAST.cmo PhoebeAST.ml
menhir --ocamlc ocamlc.opt --infer PhoebeParser.mly
ocamldep.opt -modules PhoebeParser.mli > PhoebeParser.mli.depends
ocamlc.opt -c -o PhoebeParser.cmi PhoebeParser.mli
ocamldep.opt -modules PhoebeParser.ml > PhoebeParser.ml.depends
ocamlc.opt -c -o PhoebeParser.cmo PhoebeParser.ml
ocamlc.opt -a PhoebeAST.cmo PhoebeParser.cmo -o PhoebeParser.cma
ocamldep.opt -modules PhoebeLexer.mli > PhoebeLexer.mli.depends
ocamlc.opt -c -o PhoebeLexer.cmi PhoebeLexer.mli
ocamllex.opt -q PhoebeLexer.mll
ocamldep.opt -modules PhoebeLexer.ml > PhoebeLexer.ml.depends
ocamlc.opt -c -o PhoebeLexer.cmo PhoebeLexer.ml
ocamlc.opt -a PhoebeAST.cmo PhoebeParser.cmo PhoebeLexer.cmo -o PhoebeLexer.cma
~: cd _build/
~/_build: ocaml
        OCaml version 4.04.0

# PhoebeParser.phoebe_spec;;
Characters -1--1:
  PhoebeParser.phoebe_spec;;

Error: Reference to undefined global `PhoebeParser'
# PhoebeLexer.phoebe_lexer;;
Characters -1--1:
  PhoebeLexer.phoebe_lexer;;

Error: Reference to undefined global `PhoebeLexer'
# 

我做错了什么?

1 个答案:

答案 0 :(得分:2)

您的模块将在PhoebeLexer.cmaPhoebeParser.cma个文件中进行编译和存档。每个模块还有一个附带的.cmi文件,用于描述其界面。要将模块加载到顶级,您可以使用#load#load_rec directives#use指令没有用,因为它在源级别上运行(可以看作是复制粘贴的快捷方式)。

因此,在您的情况下,顶级交互应该如下所示(假设它是在_build文件夹中启动顶级):

# #load "PhoebeLexer.cma";;
# #load "PhoebeParser.cma";;
  

我可以#load的模块与我可以打开的模块之间的区别是什么?

我喜欢具体的问题!

当你打开一个模块M时,toplevel正在当前目录,安装OCaml的目录中以及显式添加的所有目录中搜索文件m.cmi#directory指令。 cmi文件包含机器可读的压缩模块接口(您可以将其视为已编译的模块接口)。此文件定义已加载模块的类型。您甚至无需加载模块即可访问模块类型。要获取模块的定义,您需要加载实现。它存储在cmo(已编译的模块对象文件)或cma(已编译的模块存档)中。 cma文件只是多个cmo的容器。 cmo文件包含实际代码,可以加载并与主程序链接(在这种情况下使用顶层程序)。

您可能已经注意到,接口和实现是完全不同的实体,可以独立加载。隐式查找界面,您不需要手动加载它,但有时您需要更改目录(通过在特定目录中加载顶层,或使用#cd指令),或者添加使用#directory指令的搜索路径的目录。应始终使用#load指令显式加载实现。

如果cmi可用,但未加载实现,则会出现Undefined value个错误。如果cmi不可用,那么尝试访问在缺少cmi文件的模块的接口中声明的值将会导致Unbound value错误(即使您自己加载了模块存档)。

总结:接口描述了可用的内容,实现定义了可用的位置。如果该值不在接口中,则它是未绑定的;如果值在接口中,但未找到定义,则它是未定义的。