【问题标题】:An OCaml program for checking if OCaml code compiles用于检查 OCaml 代码是否编译的 OCaml 程序
【发布时间】:2011-06-30 12:37:27
【问题描述】:

我想写一个 OCaml 模块,它有一个 compile 函数,它将接受一个包含 OCaml 程序的字符串,并将编译结果输出为正确或错误,在错误的情况下,关于行和字符的信息的第一个错误。我不关心错误是什么(至少现在是这样)。

一个基本的模块类型是:

module type Compile =
    sig
        type result = Correct | Error of (int*int)
        val compile : string -> result
    end

至少有两种完全不同的实现方式:

  1. 快速破解方法 -- 将程序字符串写入文件,使用Unix进程在命令行调用ocamlc并解析stderr;
  2. 正确方法 -- 使用 OCaml 工具分析代码。

关于 (1),我无法获取 stderr 的内容。我开始使用 Unix.open_process_in,它将 stdout 重定向到我可以读取的 in_channel。 由于ocamlc 输出的编译错误转到stderr(而不是stdout),我尝试使用

let ic = Unix.open_process_in "ocamlc test.ml 2>&1" 

以便 stderr 的内容在命令行被重定向到 stdout。这样,我希望,ic 将包含编译错误。不幸的是,情况并非如此,ic 只包含End_of_file

然后我转到Unix.create_process 命令,因为它允许我为输出、输入、错误选择新通道。但是我在选择这些频道时遇到了麻烦。输出通道应该是什么,以便我可以在我的程序中读取它?

关于 (2),您认为(合理)简单的方法是什么?

非常感谢您的帮助!

【问题讨论】:

  • 关于 (2):OCaml 不是最简单的语言。您必须构建一个完整的编译器前端。即使是那些不必问这个问题的人也可能需要几周的时间才能获得一个主要工作的版本。如果我假设你对编译器构造(甚至解析,就此而言)没有什么经验是正确的......
  • @delnan OCaml 确实为您提供了 Camlp4 中编译器的所有部分。
  • @delnan 我已经使用 OCaml 的 lex 和 yacc 对应物编写了解析器。但我并没有考虑从头开始为 OCaml 编写编译器。这将是一场噩梦般的工作。此外,OCaml 已经提供了解析和编译 OCaml 代码的工具。我在 (2) 中的问题是如何最好地使用这些工具来实现此目的。

标签: ocaml dynamic-compilation


【解决方案1】:

关于 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 进行测试。
【解决方案2】:

关于 1) 我认为你做错了什么。在我的机器上:

> ocaml unix.cma
        Objective Caml version 3.12.0

# let ic = Unix.open_process_in "ocamlc test.ml 2>&1";;
val ic : in_channel = <abstr>
# 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.

对于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&gt;&amp;1 有点hacky 的东西。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多