2017-02-22 62 views
2

我有一個用ocamllex和menhir構建的詞法分析器和解析器,當我在頂層使用它們時它們工作,但它們構成的模塊仍未定義。爲什麼我的ocaml模塊未定義?

~: 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' 
# 

我在做什麼錯?

回答

2

你的模塊編譯和PhoebeLexer.cmaPhoebeParser.cma文件存檔。每個模塊還有一個附帶的.cmi文件,它描述了它的接口。要將模塊加載到頂層,可以使用#load#load_recdirectives#use指令沒有用,因爲它在源級別上運行(可以將其視爲複製粘貼的快捷方式)。

所以,你的情況頂級的互動應該是這樣的(假設開始在_build文件夾中的頂級):

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

什麼是模塊,我可以的區別#load和我可以打開的模塊?

我想具體的問題!

當你打開一個模塊M,頂層正在尋找當前目錄中的文件m.cmi,在安裝OCaml的目錄,並與#directory指令明確添加所有目錄。 cmi文件包含機器可讀的濃縮模塊接口(您可以將其視爲編譯的模塊接口)。這個文件定義了一個加載模塊的類型。你甚至可以訪問模塊的類型而無需加載模塊。要獲取模塊的定義,您需要加載實現。它存儲在cmo(編譯模塊對象文件)或cma(編譯模塊存檔)中。 A cma文件只是多個cmo的容器。一個cmo文件包含實際的代碼,可以加載並鏈接到主程序(在這種情況下使用頂層程序)。

你可能注意到了,一個接口和一個實現是完全不同的實體,可以獨立加載。該接口隱式查找,您不需要手動加載它,但有時您需要更改目錄(通過加載特定目錄中的頂級目錄,或使用#cd指令),或者通過將目錄添加到搜索路徑與#directory指令。應該始終使用#load指令加載實現。

如果CMI是可用的,但沒有加載一個實現,那麼你會得到一些Undefined value錯誤。如果cmi不可用,那麼嘗試訪問在缺少cmi文件的模塊的界面中聲明的值將會導致Unbound value錯誤(即使您已加載模塊歸檔本身)。

總結:接口描述什麼是可用的,實現定義它是可用的。如果該值不在界面中,則它是未綁定的;如果該值在接口中,但沒有找到定義,那麼它是未定義的。

+0

沒有在你的回答的到底是什麼意思'#load「PhoebeLexer.cma」 ;;'(和'當然Parser')? – Virgile

+0

該死的,它發生)))謝謝,@ Virgile! – ivg

+0

只要我在做頂級互動,#load就可以。但我仍然不明白爲什麼PhoebeParser和PhoebeLexer是未定義的全局變量。我可以加載的模塊和我可以打開的模塊之間有什麼區別? –