python 写一个scheme 解释器 (二)——简单求值器内核

 

这一篇开始正式完成求值器,首先本着一个基本原则:

先将整个流程实现,才逐步细化每个过程,最终扩充比较难的特性。

 

一、 词法分析

def tokenAnalysis(strings):
    return strings.replace(‘(‘,‘ ( ‘).replace(‘)‘,‘ ) ‘).split()

    将字符串流按照空白字符分割成一个个子串,split 和 replace 函数的更多用法可以查阅python手册。

示例:

(cons  1  2)

拆分结果:’(’ ‘cons’ ‘1’ ‘2’  ‘)’

注意,replace用于在(和其它字符之间添加空白字符,这样才可以正确的将括号和cons 分割开来,因为scheme语法中(和其它字符是可以连着写的

二、 语法分析

语法的分析原则见上一篇博文,简单来说:就是将每一个子表达式做成一个list,直至没有更小的表达式为止,这个过程显然是递归的,我们处理的时候也采用递归的方式来做。

def parse(tokens):
    first_token=tokens.pop(0)
    if first_token==‘(‘:
        expr=[]
        while tokens[0] != ‘)‘:
            expr.append(parse(tokens))
            tokens.pop(0)
            return expr
    elif first_token==‘)‘:
        raise SyntaxError("there need left bracket!")
    else :
        return parse_constant_value(first_token)

def parse_constant_value(token):
    try:
        return int(token)
    except ValueError:
        try:
            return float(token)
        except ValueError:
            return Symbol(token)

1. 首先我们取出tokens中的第一个token,它要么是可直接求值的类型,要么是‘(’ ;

2. 如果它是可求值的类型,对应最后一个else:语句,转到parse_constant_value 处处理,处理的时候采用异常抛出的方式,分别对应int float  str类型;

3. 如果是左括号开始的语句,则不断读取tokens,直至末元素为‘)’,注意给exor中添加新token的时候采用递归的方式加入,这样可以保证任何程度的表达式嵌套都可以正确的处理

 

三 . eval 内核

def eval(stat, env=global_env):
    while (True):

        if isinstance(stat, Symbol):
            return eval_var(stat, env)

        elif not isinstance(stat, List):
            return eval_constant(stat)

        elif is_arit_expr(stat):
            return eval_arit_expr(stat, env)

stat为statement的所写,env 是求值环境,和用racket来实现的时候如出一辙。

注意一点:这里eval里面有一条while(True): ,这其实是为了尾递归优化而做的改变,这里可以不考虑这一句,因为每个子项都会直接返回。

env的定义:

class Env(dict):
    def find(self, var):
        return self if (var in self) else self.outer.find(var)

    def __init__(self, parms=(), args=(), outer=None):
        self.update(zip(parms, args))
        self.outer = outer

很简单的一个类,有2个函数可用,一个是初始化构建新的环境所用的__init__,另一个 是在环境中由里到外递归查找var对应的value

也就是说,我们构建env的时候是将新的var-value键对加入新的Env()对象,并且这个对象的outer是旧的环境。这也满足我们对于求值环境的接口约定,而我们知道,只要满足这样的接口约定的都是合法的

 

Symbol = str

List =list

上面是两个简单类型的定义,左边是scheme中的类型,右边是对应的python类型。

eval_var eval_constant 都十分容易,这里看看简易求值器的计算器部分:

operator = [‘+‘, ‘-‘, ‘*‘, ‘/‘, ‘>‘, ‘<‘, ‘=‘, ‘>=‘, ‘<=‘]

def is_arit_expr(stat):    if stat[0] in operator:        return True    else:        return False
 
def eval_arit_expr(stat, env):
    (op, first, second) = stat
    first = eval(first, env)
    second = eval(second, env)
    if op == ‘+‘:
        return first + second
    elif op == ‘-‘:
        return first - second
    elif op == ‘*‘:
        return first * second
    elif op == ‘/‘:
        if second == 0:
            print("Wrong , the divide number cannot be zero!")
            return None
        else:
            return first / second
    elif op == ‘>‘:
        return first > second
    elif op == ‘<‘:
        return first < second
    elif op == ‘=‘:
        return first == second
    elif op == ‘<=‘:
        return first <= second
    elif op == ‘>=‘:
        return first >= second

非常简单,就不再赘述了。

以上是变量、常量、数学运算的部分。

        elif is_begin_with(stat, ‘quote‘):
            return eval_quote(stat)

        elif is_begin_with(stat, ‘if‘):
            (_, test, if_part, else_part) = stat
            stat = (if_part if eval(test, env) else else_part)
        elif is_begin_with(stat, ‘define‘):
            return eval_define(stat, env)

以上的quote 、 if、 define 也很容易添加

注意这里的if其实是优化尾递归之后的版本,我们没有直接return ,而是将待求值的表达式赋值为if中满足条件该执行的表达式。

 

四、交互环境和输出:

def REPL(program=";;;GD-interpreter >"):
    while True:
        result = eval(parse(tokenAnalysis(input(program))))
        my_print(result)

def my_print(value):
    if value is True:
        print("#t")
    elif value is False:
        print("#f")
    elif value is None:
        pass
    else:
        print (value)

运行的时候只要REPL()就可以了。my_print主要是为了让输出更加符合scheme的习惯,没有这个函数也无所谓。

 

最关键的lambda 函数和过程调用的支持和函数调用尾递归优化见下一篇。

时间: 2024-10-05 14:43:47

python 写一个scheme 解释器 (二)——简单求值器内核的相关文章

python 写一个scheme解释器(一)

解释器的本质 ? 我们换一种语言来写解释器的时候,其实本质和scheme写scheme是一样的,即将输入的一串字符串作为源程序执行而语法和语义均由自己预先设计好并严格执行. ? 这里我们采用python 来实现我们的第二版的scheme解释器,首先python支持的列表推导式.lambda.模式匹配等语法糖十分适合去编写解释器,另一方面,python内置数据结构齐全,使用简单. 当然我们采用C++ 也是完全可行的,但由于C++对字符串的处理功能比较弱,并且不同类型数据的转换都需要手工去完成,实现

用Python写一个ftp下载脚本

用Python写一个ftp下载脚本 ----基于Red Hat Enterprise Linux Server release 6.4 (Santiago):python 2.6.6 Ps:少侠我接触Python半个月以来接到的第一个需求,虽然如此简单的一个脚本,少侠我磕磕绊绊却用了将近一天半的时间才写出来,但还是很开心,毕竟也粗来了,废话不多说,切入正题.因为一开始没有用过ftplib模块,所以各种谷歌度娘一堆资料杂乱不堪,话不清,理不乱的,本文实现的功能简单,下面介绍一下,以免误导读者. 需

python写一个脚本解析文件

Python写一个脚本解析文件 ----基于Red Hat Enterprise Linux Server release 6.4 (Santiago):python 2.6.6 需求: 1.去掉空行 2.去掉空行后输出到一个新文件 附加需求(排版):1.'-'缩进n个字符 '-'缩进2n个字符 以此类推 2.'-'开头的所有句子输出在一行 '-'开头的句子输出在一行 以此类推 --------------------------------------------分隔线------------

python写一个通讯录V2.0

python写一个通讯录step by step V2.0 引用知识 list + dict用于临时存储用户数据信息 cPickle用于格式化文件存取 依旧使用file来进行文件的存储 解决问题 1.操刀开始去做 原始代码 实现功能(可做模板) 1.判断输入内容是否在给出的menu目录内,在的话,返回对应结果,不在就报错 2.调用os模块的exit功能 3.字典配合循环加上函数实现switch的功能 #!/usr/bin/env python #coding:utf8 #Author:zhuim

python写一个通讯录

闲着没事,用python写一个模拟通讯录,要求要实现常用的通讯录的功能,基本流程如下 ? 接下来就按照这个流程实现各个模块的功能 1. 定义一个类,并初始化 1 import json 2 import time 3 4 5 class Address(object): 6 def __init__(self): 7 with open("通讯录.txt", 'r', encoding='utf-8') as f: 8 self.data = json.loads(f.read())

老男孩教育每日一题-2017-04-17:使用Shell或Python写一个脚本,CPU使用率超过80%或硬盘超过85%邮件报警

老男孩教育每日一题-2017-04-17: 使用Shell或Python写一个脚本,CPU使用率超过80%或硬盘超过85%邮件报警. 今天是老男孩教育每日一题陪伴大家的第29天.

[py]python写一个通讯录step by step V3.0

python写一个通讯录step by step V3.0 参考: http://blog.51cto.com/lovelace/1631831 更新功能: 数据库进行数据存入和读取操作 字典配合函数调用实现switch功能 其他:函数.字典.模块调用 注意问题: 1.更优美的格式化输出 2.把日期换算成年龄 3.更新操作做的更优雅 准备工作 db准备 - 创建数据库 mysql> create database txl charset utf8; Query OK, 1 row affecte

使用python写一个监控mysql的脚本,在zabbix web上自定义模板

##先使用MySQLdb的接口关联数据库. [[email protected] python]# cat check_Mysql_custom.py #!/usr/local/bin/python '''author = chenmingle''' '''Description:get mysql status''' import os import sys try:     import MySQLdb as mysql except Exception, e:     print e   

十行代码--用python写一个USB病毒 (知乎 DeepWeaver)

昨天在上厕所的时候突发奇想,当你把usb插进去的时候,能不能自动执行usb上的程序.查了一下,发现只有windows上可以,具体的大家也可以搜索(搜索关键词usb autorun)到.但是,如果我想,比如,当一个usb插入时,在后台自动把usb里的重要文件神不知鬼不觉地拷贝到本地或者上传到某个服务器,就需要特殊的软件辅助. 于是我心想,能不能用python写一个程序,让它在后台运行.每当有u盘插入的时候,就自动拷贝其中重要文件. 如何判断U盘的插入与否? 首先我们打开电脑终端,进入/Volume