在C / C ++中实现解释器所需的参考

时间:2008-11-17 03:56:25

标签: c++ lisp interpreter

我发现自己依附于一个项目,将一个解释器整合到一个现有的应用程序中。要解释的语言是Lisp的衍生物,具有特定于应用程序的内置。个人'程序'将在应用程序中以批处理方式运行。

我很惊讶多年来我编写了几个编译器和几个数据语言翻译器/解析器,但我以前从未真正编写过翻译器。原型很远,用C ++实现为语法树walker。我可能会影响原型之外的架构,但不能影响实现语言(C ++)。所以,约束:

  • 实现将使用C ++
  • 解析可能会使用yacc / bison语法(现在是)
  • 来处理
  • 完整的VM / Interpreter生态系统(如NekoVM和LLVM)的建议可能对此项目不实用。自包含更好,即使这听起来像NIH。

我真正想要的是阅读有关实施口译员基础知识的材料。我做了一些浏览SO和另一个名为Lambda the Ultimate的网站,尽管它们更倾向于编程语言理论。

到目前为止,我收集了一些花絮:

  • Lisp in Small Pieces,来自Christian Queinnec。推荐它的人说它“从简单的解释器转到更高级的技术并完成呈现字节码和'Scheme to C'编译器。”

  • NekoVM。正如我上面提到的,我怀疑我们是否可以合并整个VM框架来支持这个项目。

  • Structure and Interpretation of Computer Programs。最初我建议这可能是矫枉过正,但是通过一个健康的块,我同意@JBF。信息量大,思维扩张。

  • 保罗·格雷厄姆的
  • On Lisp。我已经读过这篇文章,虽然它是对Lisp原理的一个信息性介绍,但还不足以快速开始构建一个解释器。

  • Parrot Implementation。这看起来很有趣。不确定它会为我提供基础知识。

  • Scheme from Scratch。 Peter Michaux正在攻击Scheme的各种实现,从用C编写的快速脏方案解释器(用作后续项目中的引导程序)到编译的Scheme代码。到目前为止非常有趣。

  • Language Implementation Patterns: Create Your Own Domain-Specific and General Programming Languages,在Books On Creating Interpreted Languages的评论主题中推荐。这本书包含两章专门介绍构建口译员的做法,所以我将它添加到我的阅读队列中。

  • (还有 Old ,即1979年):P。J. Brown的Writing Interactive Compilers and Interpreters。这已经绝版了,但是提供与基本解释器的实现相关的各种任务的概述很有趣。我已经看到了这个评价的混合评论,但因为它很便宜(我订购的价格大约是3.50美元)我会给它一个旋转。

那怎么样?是否有一本很好的书,用手拿新手,并展示了如何在C / C ++中为类似Lisp的语言构建一个解释器?您是否偏好语法树步行器或字节码解释器?

回答@JBF:

  • 当前的原型是一个解释器,它对我有意义,因为我们接受任意代码文件的路径并在我们的应用程序环境中执行它。内置函数用于影响我们的内存数据表示。

  • 它不应该非常慢。当前的树木行走者似乎可以接受。

  • Lisp上的语言基于 ,但不是Lisp,因此不需要符合标准。

  • 如上所述,我们不太可能允许添加完整的外部VM /解释器项目来解决此问题。

对于其他海报,我也会检查你的引文。谢谢,全部!

5 个答案:

答案 0 :(得分:12)

简短回答:

lisp解释器的基本阅读列表是SICP。如果你觉得你的书的第一部分过于资格而跳到第4章并开始解释,我根本不会称之为矫枉过正(虽然我觉得这会是一个损失,因为第1-3章确实很好!)

在小件中添加LISP(从现在起LISP),第1-3章。特别是第3章,如果你需要实现任何非平凡的控制形式。

请参阅JensAxelSøgaard关于最小自助托管计划的帖子:http://www.scheme.dk/blog/2006/12/self-evaluating-evaluator.html

答案略长:

如果不知道口译员的要求,很难提供建议。

  • 它真的需要成为一名翻译,还是你真的需要能够执行lisp代码?
  • 它需要快吗?
  • 是否需要符合标准?常见的嘴唇? R5RS? R6RS?您需要的任何SFRI?

如果您需要比简单的语法树助手更有趣,我强烈建议您嵌入快速方案子系统。想到了Gambit计划:http://dynamo.iro.umontreal.ca/~gambit/wiki/index.php/Main_Page

如果这不是SICP中的第5章和第5章 - LISP目标编译中的选项,以便更快地执行。

为了更快地解释,我将看一下最新的JavaScript解释器/编译器。似乎有很多想法进入快速JavaScript执行,你可以从中学习。 V8引用了两篇重要论文:http://code.google.com/apis/v8/design.html和squirrelfish引用了一对:http://webkit.org/blog/189/announcing-squirrelfish/

还有规范方案文件:http://library.readscheme.org/page1.html用于RABBIT编译器。

如果我进行一些过早的猜测,内存管理可能是一个难以破解的难题。 Nils M Holm出版了一本书“空间空间的方案9”http://www.t3x.org/s9fes/,其中包括一个简单的世界标记和扫描垃圾收集器。来源包括。

John Rose(较新的JVM成名)撰写了一篇关于将Scheme集成到C:http://library.readscheme.org/servlets/cite.ss?pattern=AcmDL-Ros-92的论文。

答案 1 :(得分:7)

是的SICP。

我已经好几次完成了这项任务,如果我是你,我就会这样做:

首先设计你的记忆模型。您需要某种GC系统。 WAAAAY首先比以后更容易做到这一点。

设计您的数据结构。在我的实现中,我有一个基本的缺点框,其中包含许多基本类型:atom,string,number,list,bool,primitive-function。

设计您的VM并确保保持API干净。我的最后一个实现是将它作为顶级API(原谅格式化 - SO是我的预览)

ConsBoxFactory &GetConsBoxFactory() { return mConsFactory; }
AtomFactory &GetAtomFactory() { return mAtomFactory; }
Environment &GetEnvironment() { return mEnvironment; }
t_ConsBox *Read(iostream &stm);
t_ConsBox *Eval(t_ConsBox *box);
void Print(basic_ostream<char> &stm, t_ConsBox *box);
void RunProgram(char *program);
void RunProgram(iostream &stm);

不需要RunProgram - 它是根据Read,Eval和Print实现的。 REPL是口译员的常见模式,尤其是LISP。

ConsBoxFactory可用于制作新的缺点框并对其进行操作。使用AtomFactory,以便等效的符号原子映射到一个对象。环境用于维护符号与缺陷框的绑定。

你的大多数工作都应该进入这三个步骤。然后你会发现你的客户端代码和支持代码看起来也非常像LISP:

t_ConsBox *ConsBoxFactory::Cadr(t_ConsBox *list)
{
    return Car(Cdr(list));
}

您可以在yacc / lex中编写解析器,但为什么要这么麻烦? Lisp是一个非常简单的语法和扫描/递归下降解析器对,它大约需要两个小时的工作。最糟糕的部分是编写谓词以识别标记(即IsString,IsNumber,IsQuotedExpr等),然后编写例程将标记转换为缺点框。

可以轻松地将胶水写入和输出C代码,并在出现问题时轻松调试问题。

答案 2 :(得分:5)

来自Samuel Kamin的书 Programming Languages,一种基于口译员的方法

The Kamin Interpreters,由Timothy Budd翻译成C ++。我不确定裸源代码有多么有用,因为它本书也适用,但它是一本很好的书,涵盖了用较低级语言实现Lisp的基础知识,包括垃圾收集等。(这不是本书的重点,一般是编程语言,但它已被涵盖。)

Lisp in Small Pieces 更深入,但这对你的情况既好又坏。有很多关于编译的材料,这些材料与你无关,它的简单解释器是在Scheme中,而不是C ++。

SICP肯定是好的。不是矫枉过正,但当然,编写口译员只是本书的一小部分。

JScheme的建议也很好(并且它包含了我的一些代码),但不会帮助你处理像GC这样的事情。

我可能会在稍后提出更多建议。

编辑:有些人说他们是从awklisp学到的。这无疑是一种奇怪的建议,但它非常小,可读,实际可用,与其他微小但可读的玩具Lisp不同,它实现了自己的垃圾收集器和数据表示,而不是依赖于底层的高级实现语言。提供他们。

答案 3 :(得分:3)

结帐JScheme from Peter Norvig。我发现这很难理解并移植到C ++。呃,不知道如何使用scheme作为脚本语言 - 教给jnrs很麻烦,感觉过时了(helloooo 1980's)。

答案 4 :(得分:2)

我想扩展我对Programming Languages: Application and Interpretation的建议。如果你想写一个翻译,那本书会带你到很短的路。如果您通过编写您阅读的代码并进行练习,您最终会得到一堆类似的解释器但不同(一个是渴望,另一个是懒惰,一个是动态的,另一个是打字,一个是动态范围,其他有词汇范围等)。