An OCaml program for checking if OCaml code compiles
我想编写一个具有 compile 函数的 OCaml 模块,该函数将接受一个包含 OCaml 程序的字符串并将编译结果输出为正确或错误,在错误情况下,将输出有关行和字符的信息的第一个错误。我不关心错误是什么(至少现在是这样)。
一个基本的模块类型是:
1 2 3 4 5
| module type Compile =
sig
type result = Correct | Error of (int*int)
val compile : string -> result
end |
至少有两种完全不同的实现方式:
快速破解方式——将程序中的字符串写入文件,使用Unix进程在命令行调用ocamlc并解析stderr;
正确的方法——使用 OCaml 工具来分析代码。
关于 (1),我无法获取 stderr 的内容。我开始使用
Unix.open_process_in,它将标准输出重定向到我可以读取的 in_channel。
由于 ocamlc 输出的编译错误转到标准错误(而不是标准输出),我尝试使用
1
| let ic = Unix.open_process_in "ocamlc test.ml 2>&1" |
以便 stderr 的内容在命令行被重定向到 stdout。这样,我希望,ic
将包含编译错误。不幸的是,情况并非如此,ic 仅包含 End_of_file.
然后我转到 Unix.create_process 命令,因为它允许我为 out、in、err 选择新通道。但是我在选择这些频道时遇到了麻烦。输出通道应该是什么,以便我可以在我的程序中读取它?
关于(2),您认为(合理)简单的方法是什么?
非常感谢您的帮助!
- 关于(2):OCaml 不是最简单的语言。您必须构建一个完整的编译器前端。即使是那些不必问这个问题的人也可能需要几周的时间才能获得一个大部分工作的版本。如果我假设您对编译器构造(甚至解析,就此而言)几乎没有经验的假设是正确的......
-
但是,@delnan OCaml 确实为您提供了 Camlp4 中编译器的所有部分。
-
@delnan 我已经使用 OCaml 的 lex 和 yacc 对应物编写了解析器。但我并没有考虑从头开始为 OCaml 编写编译器。这将是一场噩梦般的工作。此外,OCaml 已经提供了解析和编译 OCaml 代码的工具。我在(2)中的问题是如何最好地使用这些工具来达到这个目的。
关于 2),OCaml 安装通常有一个 compiler-lib 目录,其中包含许多组成编译器的模块,包括解析器。这没有记录,但可用于进行编译和类型检查。对于解析,还有 Camlp4,它可以用来做除了预处理之外的事情(我想你可以将 Camlp4 加载到你的程序中并将它用作库,可能在对其进行一些修补之后),但它只包含语法而不是类型检查器。
-
请注意,这将比使用 toplevellib 更不便携。 OCaml 的默认安装过程不安装任何 compiler-lib 目录。
-
我有Camlp5,但我想这应该是相同的,不是吗?无论如何,我真的需要整个编译器。我需要确保我正在检查的任何内容实际上都可以在 ocamlc 下编译,而不仅仅是解析。感谢指向编译器目录的指针,这很有帮助。不过,我那里只有 .cm* 和 .o 文件,所以我看不到编译器模块的内容。我找到了 Daniel Bunzli 建议的 Toploop.execute_phrase 函数,所以我现在试一试。谢谢!
-
我想我会试试camlp5。 Toploop.exectute_phrase 是无证的,结果不是很容易弄清楚。我写入文件、编译和读取 stderr 的技巧工作正常,但对于我需要的密集使用来说太慢了。因此,我将使用camlp5 作为过滤器:只有那些在camlp5 中解析的才会通过ocamlc 进行测试。
关于 1) 我认为你做错了什么。在我的机器上:
1 2 3 4 5 6 7 8 9 10 11
| > ocaml unix .cma
Objective Caml version 3 .12 .0
# let ic = Unix.open_process_in "ocamlc test.ml 2>&1";;
val ic : in_channel =
# input_line ic ;;
- : string ="File "test .ml ", line 1, characters 0-1:"
# input_line ic ;;
- : string ="Error: I/O error: test.ml: No such file or directory"
# input_line ic ;;
Exception: End_of_file . |
For Unix.create_process 一种方法是将 Unix.stdin 传递给 new_stdout 以便您可以通过程序的标准输入读取进程的输出(假设您不想使用其他程序的标准输入)。
但我更简单的方法是使用 Unix.open_process_full.
关于 2) 你可以尝试使用 toplevellib.cma (但是请注意,这是完全没有文档且不受支持的)。查看发行版中的 toplevel/toploop.mli,尤其是 Toploop.execute_phrase.
- 有趣...我刚刚回到代码。 Unix.open_process_in 位工作得很好。问题出在其他地方,对于处理标准输出行的递归函数的基本情况是一个非常愚蠢的错误。很抱歉浪费您的时间。尽管如此,正是我看到它为你工作的事实引发了我对代码不同部分的关注。关于(2)的建议很宝贵。我会尝试让 Toploop.execute_phrase 工作。非常感谢!
-
是的,Unix.open_process_full 似乎更合适。我应该看到的;我查看了 Unix 模块文档的那部分,但我错过了。通过使用 _full 版本,我可以避免 2>&1 有点老套的东西。谢谢!