【问题标题】:Writing a C-Interpreter for interactive C code为交互式 C 代码编写 C 解释器
【发布时间】:2013-08-04 22:10:49
【问题描述】:

我想练习C,所以我决定本着python解释器的精神写一个C解释器。我有一些 C 知识,但我一直是一个边做边学的程序员。

到目前为止,我所拥有的非常简单。只需解析用户的输入,一次一行,并区分以下声明:

int x = 10;
char c = 'a';

我创建了一个结构,该结构表示变量的类型、名称和 ivalue(用于 int 值)和 cvalue(用于 char 值)。还有很多事情要做,但一步一步。

我也可以解析函数调用,例如:

printf("value of x = %d\n, x);

我提取函数的名称,并将 args 存储在 char** args 中。

这听起来很傻,但我想避免为每个标准 c 库函数编写映射器,以便执行对 printf、strstr 或 strcpy 之类的调用。反正有没有这种方法动态调用标准c函数?

另外,非常欢迎对这个东西的设计提出建议。

【问题讨论】:

  • 不使用ctypes?
  • @IgnacioVazquez-Abrams 我正在用 C 而不是 python 编写解释器。
  • Quib​​ble:int x = 10;char c = 'a'; 是带有初始化的声明,而不是赋值语句。如果你想实现 C,你需要了解它的语法。
  • @KeithThompson 只是写错了。我代码中的函数实际上叫做 parse_declaration
  • 如果这个程序是用 C 编写的,并且解释了 C,那么它不应该被 Python 标记。

标签: c interpreter


【解决方案1】:

你不能(就像我想的那样)写一个 C 解释器。
我猜你会遇到编写 C 解释器非常困难的事情。 您可能必须编写一个编译器。
当然,您可以即时“剖析”该语言,并在您进行时解析代码。
真正的问题(如我所见)是处理外部引用。

在 Python 中,您使用 import 关键字处理外部引用。
如您所知,某些库可能有冲突的方法(例如 lxmllibxml2)。
此冲突已通过 importing 正确的库得到解决。
您当然可以考虑一些有效“链接”或导入所有需要的外部引用的机制。
这可能会有某些非常具体的假设。
这样一来,当你遇到#include <stdlib.h>你其实是import吧。
就此而言,导入它可能意味着在 windows 下使用 LoadLibrary()LoadLibraryEx 之类的东西加载 stdlib 的 dll。
加载所有遇到的#include's 后,如果没有找到引用的定义,那么您可能会遍历本地目录以获取其他 C 文件,直到遇到寻求的引用,此时我不知道应该怎么做。
那是关于链接问题(老实说,如果没有适当的编译,我真的看不出你将如何克服)。

另一部分实际上也非常难。您需要编写一个 Lexer。
那就是解析所有这些 C 代码行的小恶魔。
我假设您已经摆弄过 Scheme/List 解释器的编写,甚至可能是一些更复杂的解析器。
谨防! C 不是 Scheme!
它是一种高度复杂的解析语言。它的描述文件跨越数百页。
编写 C 词法分析器不是编写解释器的练习。
C 有一些令人讨厌的与上下文相关的解析——这基本上意味着它不是 CFL(上下文无关语言)——这意味着你不能编写一个好的有限自动机来解析它。

我将以an example 结尾,取自Eli Bendersky 的精彩博客。

typedef int AA;
void foo()
{
    AA aa;       /* OK - define variable aa of type AA */
    float AA;    /* OK - define variable AA of type float */
}

这只是说明上下文相关的语法有多么棘手。

【讨论】:

  • 感谢您的解释。我开始使用tcc 并读取一些输入行,然后在主函数中编译它们并调用它。但是,我认为会有更好的方法。有没有一种方法可以一次编译一行,这样它的执行就会成功,同时能够解决依赖关系,即如果 int x 在三或四行之前定义,那么下一行是 int z = x + 10。
  • 你在说什么,通常意味着解释而不是编译(我认为)。似乎通过中间字节码并试图解释它是合乎逻辑的事情。
  • 错了。编写解释器是完全可能的(尽管编译器和解释器之间的区别非常模糊)。看看这个,例如:code.google.com/p/c-semantics
  • 我没有说你不能写解释器,我只是说这是一项最艰巨的任务——同时指出了一些明显的困难。
  • @Trevor,只有一种方法可以解析你的句子“你不能写解释器......你必须写一个编译器”。请更正。
【解决方案2】:

除了解析 C 语法的麻烦之外,它实际上非常适合解释,因为它被设计为一次性处理,以解决 1970 年代设备的内存限制。

事实上,Herbert Schildt 的C: The Complete Reference, Fourth Edition 的第 6 部分(第 713-787 页)致力于通过引导您编写这样的解释器,将本书其余部分的所有内容联系在一起。

我没查过书里的版本有没有修改,但是他的“小C”是在Dr. Dobb's Journal, August, 1989第一次呈现给全世界的。

另外,this question 有一堆答案列出了现有的 C 解释器。

至于动态调用 C 函数,在 POSIX 平台上比在 Windows 上更容易,因为您可能需要 .lib 文件来解析 .dlls 中的符号,而 .so 文件没有等价物。

对于.so 文件,您只需dlopen 库路径并使用dlsym 按名称检索函数指针。

通过运行man dlopen,可以在 dlopen 的 Linux 手册页中找到这样做的示例。

【讨论】:

    猜你喜欢
    • 2015-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多