Forth/FORTH 中的解析,或“编译器怎么了”
要了解 FORTH 语言,我们可以从检查当您向其输入命令行时 FORTH 会做什么开始。以下是“解释”(“立即执行”)或“编译”(“稍后执行”)循环的正式定义。虽然可能很难看清森林中的树木,但让我们简要描述整个过程,然后重新回顾并更详细地解释其意义。
FORTH 通过使其非常统一简化了人机指令的过程。要向 FORTH 系统发出指令,需要键入一系列词语到计算机中。然后,FORTH 从第一个到最后一个“读取”这些词语。为了判断一个词语在哪里结束,另一个词语从哪里开始,使用了一个简单的规则:词语之间用一个或多个 ASCII 空格字符 (代码 32) 分隔。每个词语都按键入的顺序进行处理 (或从源文件中读取)。
当每个传入的词语从输入流中分离出来时,FORTH 将确定它在“字典”中是否已经存在对它的定义。FORTH 指的是其符号表,它被安排为“字典”结构的一部分。在每个字典条目的名称字段中搜索与词语名称的匹配项 (从最近定义的词语开始,到字典中最旧的词语结束)。如果在字典中找不到该词语,FORTH 将考虑输入词语是否可以被解释为当前基数中的数字。如果不是,则该输入对 FORTH 来说不可理解,它将拒绝该输入,并显示一条消息,表明字典中找不到这样的词语。
如果找到了定义或输入是可识别的数字,FORTH 将考虑是否应该立即执行此命令或稍后执行。如果答案是“立即执行”(“解释”),它将执行与其字典条目关联的词语的定义,或将该数字压入 FORTH 的中央数据交换机,即堆栈。如果答案是“稍后执行”(“编译”),这通常意味着我们正在将一个新的词语添加到字典中,使其成为最近定义的词语。
因此,我们将对输入词语或数字的引用作为正在定义的词语的定义中的下一个条目。它将在稍后按顺序执行,但仅在有人请求执行我们正在定义的新词语时才会执行。
然后,我们重复上述过程来解析输入流中的下一个词语。这将持续进行,直到没有更多输入。
上述过程有哪些含义?它与其他方法相比如何?(注意:在以下部分中,术语“词语”和“子例程”可以互换使用,以强调其平行性。)
- 可访问性
字典中的任何词语都可以从命令行调用。因此,字典中的每个词语及其包含的应用程序都可以立即单独测试。只需从命令行调用有问题的词语即可。找到一个错误?与传统的工具链不同,您不需要重新编译应用程序来包含面向调试的自定义调用以查看内部子例程。只需从命令行调用失败的词语。失败?查看它的定义。按顺序键入这些词语到命令行。这些词语中的任何一个失败了吗?重复。可以在不需要采取特殊措施使内部子例程可调用的情况下隔离问题。
- 词汇表。
但这并不意味着每个内部词语都需要一直对客户可用,包括那些可能例如删除其文件的危险词语。词语组可以组织在词汇表列表中,并且特定的词汇表列表可以提供给特定的命令行。
- 自然地阻止循环定义和思考:词语必须按合理的自然顺序添加到字典中。在构建词语的定义时,如果调用了字典中尚未存在的词语,则会收到“字典中不存在词语”的错误。因此,必须组织一系列词语,从仅依赖自身的基石词语开始,然后是依赖它们的词语层,然后是依赖它们的词语。这有助于以逻辑的方式构建字典。
- 简化和合并工具链的编译和加载部分
传统的工具链具有不同的编译和加载阶段。编译器将目标代码模块放置到库中,这些库稍后以静态或动态方式加载和链接。只有这样,才会生成缺少子例程的报告。FORTH 简化并合并了这些步骤。这就像每个库模块都被加载并被赋予内存中的固定地址。一旦它的地址确定,调用它的模块就可以被加载 (字典中的后续词语)。然后,这些调用词语可以直接指向它们想要调用的词语的入口点的内存地址,与传统链接器的外部引用过程相比。
- 源代码和目标代码之间的区别消失了。
事实证明,这是其他计算机语言不必要的负担,毕竟可能不是一个有用的概念。在这门语言中,我们看到了定义一个新词语的小开销 (存储它的名称,将其附加到字典的其余部分,以及其他包装开销)。将词语的“源代码”定义编译成“目标代码”定义,惊人的是,仅仅是将源文本中找到的每个词语的名称从 ASCII 转换为其入口点的相应内存地址。这个过程很容易反转。编译结果是删除源代码中的人类可读注释 (机器在目标代码中不需要它们,它无法读取它们),并将每个调用的词语的人类可读名称转换为硬件使用的相应数值机器地址。作为充满错误案例、与大脑作斗争的巨大字节的神秘“编译器”不再存在。只是一些定义明确的查找。所有其他编译学术到底有什么用呢?
- 在这个系统中,**编译**意味着将 (一个新词语) 添加到字典的顶部。为此,每个被调用的词语名称都会从源文本中找到的 ASCII 名称转换为其入口点的相应内存地址。这个数字将添加到词语的“目标代码”定义的末尾。