2017-02-22 4 views
2

Ich habe einen Lexer und Parser, den ich mit ocamllex und menhir gebaut habe, und sie funktionieren, wenn ich sie auf höchster Ebene verwende, aber die Module, die sie bilden, sind immer noch undefiniert.Warum ist mein Ocaml-Modul undefiniert?

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

Was mache ich falsch?

Antwort

2

Ihre Module werden in PhoebeLexer.cma und PhoebeParser.cma Dateien kompiliert und archiviert. Jedes Modul hat auch eine begleitende Datei .cmi, die seine Schnittstelle beschreibt. Um ein Modul auf die oberste Ebene zu laden, können Sie #load oder #load_recdirectives verwenden. Die #use Direktive ist nicht nützlich, da sie auf einer Quellenebene arbeitet (sie kann als eine Verknüpfung zum Kopieren und Einfügen angesehen werden).

in Ihrem Fall also die Top-Level-Interaktion sollte wie folgt aussehen (unter der Annahme, dass das Top-Level im _build Ordnern beginnen):

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

Was ist der Unterschied zwischen einem Modul kann ich #load und ein Modul, das ich öffnen kann?

Ich mag konkrete Fragen!

Wenn Sie ein Modul M wird die Toplevel für eine Datei m.cmi im aktuellen Verzeichnis, in dem Verzeichnis suchen zu öffnen, wo OCaml installiert ist, und in allen Verzeichnissen explizit mit der #directory Richtlinie hinzugefügt. Die cmi Datei enthält eine maschinenlesbare kondensierte Modulschnittstelle (Sie können sich diese als eine kompilierte Modulschnittstelle vorstellen). Diese Datei definiert einen Typ eines geladenen Moduls. Und Sie können auf Typen eines Moduls zugreifen, ohne das Modul zu laden. Um die Definitionen eines Moduls zu erhalten, müssen Sie die Implementierung laden. Diese wird entweder in cmo (kompilierte Modulobjektdatei) oder cma (kompiliertes Modularchiv) gespeichert. Eine cma Datei ist nur ein Container für mehrere cmo. Eine cmo Datei enthält tatsächlichen Code, der geladen und mit dem Hauptprogramm (in diesem Fall mit dem Toplevel-Programm) verknüpft werden kann.

Wie Sie vielleicht bemerkt haben, sind eine Schnittstelle und eine Implementierung völlig unterschiedliche Entitäten, die unabhängig voneinander geladen werden können. Die Schnittstelle wird implizit gesucht, Sie müssen sie nicht manuell laden, aber manchmal müssen Sie entweder ein Verzeichnis ändern (indem Sie die oberste Ebene in ein bestimmtes Verzeichnis laden oder indem Sie die Direktive #cd verwenden) oder indem Sie ein Verzeichnis hinzufügen Suchpfade mit der #directory Direktive. Implementierungen sollten immer explizit mit der Direktive geladen werden.

Wenn cmi verfügbar ist, aber eine Implementierung nicht geladen ist, wird ein Fehler von angezeigt. Wenn cmi nicht verfügbar ist, wird beim Versuch, auf den Wert zuzugreifen, der in der Schnittstelle eines Moduls mit fehlender cmi-Datei deklariert wurde, ein Unbound value Fehler ausgegeben (selbst wenn Sie das Modularchiv selbst geladen haben).

Zusammengefasst: Schnittstelle beschreibt, was verfügbar ist, Implementierung definiert, wo es verfügbar ist. Wenn der Wert nicht in der Schnittstelle ist, ist er nicht gebunden. Wenn der Wert in der Schnittstelle ist, aber die Definition nicht gefunden wird, ist sie nicht definiert.

+0

meinst du nicht '#load" PhoebeLexer.cma ";;' (und 'Parser' natürlich) am Ende deiner Antwort? – Virgile

+0

Verdammt, es passiert))) Danke, @Virgile! – ivg

+0

Solange ich die Interaktion auf höchster Ebene mache, wird #load ausreichen.Aber ich verstehe immer noch nicht, warum PhoebeParser und PhoebeLexer nicht definiert sind. Was ist der Unterschied zwischen einem Modul, das ich laden kann und einem Modul, das ich öffnen kann? –