2017-02-22 28 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.cma и PhoebeParser.cma. Каждый модуль также содержит файл .cmi, описывающий его интерфейс. Чтобы загрузить модуль на верхний уровень, вы можете использовать #load или #load_recdirectives. Директива #use не полезна, так как она работает на уровне источника (это можно увидеть как ярлык для копирования).

Таким образом, в вашем случае взаимодействие верхнего уровня должно выглядеть следующим образом (при условии, что начинает верхний уровень в папке _build):

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

В чем разница между модулем смогу #load и модуль, который я могу открыть?

Мне нравятся конкретные вопросы!

Когда вы открываете модуль M, верхний уровень ищет файл m.cmi в текущем каталоге, в каталоге, где установлено OCaml, и во всех каталогах, добавленных в явном виде с #directory директивой. Файл cmi содержит машиночитаемый интерфейс с конденсированным модулем (вы можете рассматривать его как скомпилированный интерфейс модуля). Этот файл определяет тип загруженного модуля. И вы можете получить доступ к типам модулей, даже не загружая модуль. Чтобы получить определения модуля, вам нужно загрузить реализацию. Это сохраняется либо в cmo (скомпилированный объектный файл модуля), либо cma (архив скомпилированного модуля). Файл 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 являются неопределенными глобальными. В чем разница между модулем, который я могу загрузить, и модулем, который я могу открыть? –