提交 ab24cc3b 编写于 作者: W wizardforcel

3.6.3.

上级 e1001187
......@@ -835,3 +835,69 @@ empty
![](img/sier.png)
## 3.6.3 结构
这一章描述了 Logo 解释器的普遍结构。虽然这一章是独立的,它也确实引用了配套项目。完成这个项目会从零制造出这一章描述的解释器的有效实现。
Logo 的解释器可以共享计算器解释器的相同结构。解析器产生表达式数据结构,可由求值器来解释。求值函数检查表达式的形式,并且对于调用表达式,它在一些参数上调用函数来应用某个过程。但是,还是是一些结构上的不同以适应 Logo 的特殊语法。
**行。**Logo 解析器并不读取一行代码,而是读取可能按序包含多个表达式的整行代码。它不返回表达式树,而是返回 Logo 句子。
解析器实际上只做微小的语法分析。特别是,解析工作并不会将调用表达式的运算符和操作数子表达式区分为树的不同枝干。反之,调用表达式的组件顺序排列,嵌套调用表达式表示为摊平的记号序列。最终,解析工作并不判断基本表达式,例如数值的类型,因为 Logo 没有丰富的类型系统。反之,每个元素都是单词或句子。
```py
>>> parse_line('print sum 10 difference 7 3')
['print', 'sum', '10', 'difference', '7', '3']
```
解析器做了很魏霞的分析,因为 Logo 的动态特性需要求值器解析嵌套表达式的结构。
解析器并不会弄清句子的嵌套结构,句子中的句子表示为 Python 的嵌套列表。
```py
>>> parse_line('print sentence "this [is a [deep] list]')
['print', 'sentence', '"this', ['is', 'a', ['deep'], 'list']]
```
`parse_line`的完整实现在配套项目的`logo_parser.py`中。
**求值。**Logo 一次求值一行。求值器的一个框架实现定义在配套项目的`logo.py`中。有`parse_line`反馈的句子传给了`eval_line`函数,它求出行中的每个表达式。`eval_line`函数重复调用`logo_eval`,它求出行中的第一个完整表达式,直到这一行全部求值完毕,之后返回最后一个值。`logo_eval`函数求出单一的表达式。
![](img/logo_eval.png)
`logo_eval`函数求出不同形式的表达式,我们已经在上一节中介绍过它们了:基本、变量、定义、引用和调用表达式。Logo 中多元素表达式的形式可以由检查第一个元素来判断。表达式的每个形式都有自己的求值规则。
1. 基本表达式(可以解释为数值、`True`或`False`的单词)求值为自身。
2. 变量在环境中查找。环境会在下一节中详细讨论。
3. 定义处理为特殊情况。用户定义过程也在下一节中详细讨论、
4. 引用表达式求值为引用的文本,它是个去掉前导引号的字符串。句子(表示为 Python 列表)也看做引用,它们求值为自身。
5. 调用表达式在当前环境中查找运算符名称,并且调用绑定到该名称的过程。
下面是`logo_apply`的简单实现。我们去掉了一些错误检查,以专注于我们的讨论。配套项目中有更加健壮的实现。
```py
>>> def logo_eval(line, env):
"""Evaluate the first expression in a line."""
token = line.pop()
if isprimitive(token):
return token
elif isvariable(token):
return env.lookup_variable(variable_name(token))
elif isdefinition(token):
return eval_definition(line, env)
elif isquoted(token):
return text_of_quotation(token)
else:
procedure = env.procedures.get(token, None)
return apply_procedure(procedure, line, env)
```
上面的最后情况调用了第二个过程,它由函数`apply_procedure`来表达。为了调用由运算符记号命名的过程,这个运算符会在当前环境中查找。在上面的定义中,`env`是`Environment `类的实例,会在下一节中描述。`env.procedures`属性是个储存运算符名称和过程之间映射的字典。在 Logo 中,环境拥有单词的这种映射,并且没有局部定义的过程。而且,Logo 为过程名称和变量名称维护分离的映射,叫做分离的命名空间。但是,以这种方式复用名称并不推荐。
**过程调用。**过程调用以调用`apply_procedure`函数开始,它被传入由`logo_apply`查找到的函数,并带有代码的当前行和当前环境。Logo 中过程调用的过程比计算器中的`calc_apply`更加通用。特别是,`apply_procedure`必须检查打算调用的过程,以便在求解`n`个运算符表达式之前,判断它的参数数量`n`。这里我们会看到,为什么 Logo 解析器不能仅仅由语法分析构建表达式树,因为树的结构由过程决定。
`apply_procedure`函数调用`collect_args `函数,它必须重复调用`logo_eval`来求解行中的下`n`个表达式。之后,计算完过程的参数之后,`apply_procedure`调用了`logo_apply`,实际上这个函数在参数上调用过程。下面的调用图示展示了这个过程。
![](img/logo_apply.png)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册