自己动手写个简单的解释器(二)

我只是个项目的搬运工,来自国外一个非常有趣的博客

英文原博: Ruslan Spivak
中文翻译可参考:博乐在线

之前第一版只是实现了非常简单的加减法,原博最终完成版就是带括号的加减乘除。

其实实现这个计算器,说难也不难说简单也不简单,最重要的是理解词法分析器和解析器,从而简单地了解解释器的处理过程。在这一版的完善过程中,作者同时讲解了文法语法图的概念,其实这俩东西很强大,在处理逻辑的过程中可以很简单地将文法对应到代码,当项目越复杂,文法越显作用。


"""
1. 任意数量加减乘除
2. 可以有括号
3. 去除运算符与数字间空格

"""

# token类型:integer, '+', '-', '*', '/', '(', ')', end-of-file
INTEGER, PLUS, MINUS, MUL, DIV, LPAREN, RPAREN, EOF = 'INTEGER', 'PLUS', 'MINUS', 'MUL', 'DIV', '(', ')', 'EOF'


class Token(object):
    """记号类"""
    def __init__(self, type, value):
        self.type = type
        self.value = value

    def __str__(self):
        return 'Token({type}, {value})'.format(
            type = self.type,
            value = self.value
        )

    def __repr__(self):
        return self.__str__()


class Lexer(object):
    """词法分析器"""
    def __init__(self, text):
        self.text = text  # 输入的内容
        self.pos = 0  # 当前内容的索引
        self.current_char = self.text[self.pos]  # 当前字符

    def error(self):
        raise Exception('Invalid character')

    def advance(self):
        """后移pos设置current_char"""
        self.pos += 1
        if self.pos > len(self.text)-1:
            self.current_char = None
        else:
            self.current_char = self.text[self.pos]

    def skip_whitespace(self):
        """跳过空格"""
        while self.current_char and self.current_char.isspace():
            self.advance()

    def integer(self):
        """返回多位数字"""
        result = ''
        while self.current_char and self.current_char.isdigit():
            result += self.current_char
            self.advance()
        return int(result)

    def get_next_token(self):
        """获取当前记号"""
        while self.current_char:

            if self.current_char.isspace():
                self.skip_whitespace()
                continue

            if self.current_char.isdigit():
                return Token(INTEGER, self.integer())

            if self.current_char == '+':
                self.advance()
                return Token(PLUS, '+')

            if self.current_char == '-':
                self.advance()
                return Token(MINUS, '-')

            if self.current_char == '*':
                self.advance()
                return Token(MUL, '*')

            if self.current_char == '/':
                self.advance()
                return Token(DIV, '/')

            if self.current_char == '(':
                self.advance()
                return Token(LPAREN, '(')

            if self.current_char == ')':
                self.advance()
                return Token(RPAREN, ')')

            self.error()

        return Token(EOF, None)


class Interpreter(object):
    """解释器类"""
    def __init__(self, lexer):
        self.lexer = lexer
        self.current_token = self.lexer.get_next_token()  # 当前记号

    def error(self):
        # 解析错误时抛出异常
        raise Exception('Invalid syntax')

    def eat(self, token_type):
        """消除与 token_type 类型符合的当前记号,后面的记号前移"""
        if self.current_token.type == token_type:
            self.current_token = self.lexer.get_next_token()
        else:
            self.error()

    def factor(self):
        """
        返回数值并设置当前token
        factor: INTEGER | LPAREN expr RPAREN
        """
        token  = self.current_token
        if token.type == INTEGER:
            self.eat(INTEGER)
            return token.value
        elif token.type == LPAREN:
            self.eat(LPAREN)
            result = self.expr()
            self.eat(RPAREN)
            return result

    def term(self):
        """
        返回优先级高的运算结果
        term: factor((MUL|DIV)factor)*
        """
        result = self.factor()
        while self.current_token.type in (MUL, DIV):
            token = self.current_token
            if token.type == MUL:
                self.eat(MUL)
                result *= self.factor()
            elif token.type == DIV:
                self.eat(DIV)
                result /= self.factor()
        return result


    def expr(self):
        """
        文法:

        expr: term((PLUS|MINUS)term)*
        term: factor((MUL|DIV)factor)*
        factor: INTEGER | LPAREN expr RPAREN
        """
        result = self.term()
        while self.current_token.type in (PLUS, MINUS):
            token = self.current_token
            if token.type == PLUS:
                self.eat(PLUS)
                result += self.term()
            elif token.type == MINUS:
                self.eat(MINUS)
                result -= self.term()
        return result
        

def main():
    while True:
        try:
            text = input('calc>')
        except EOFError:
            break
        if not text:
            continue
        lexer = Lexer(text)
        interpreter = Interpreter(lexer)
        result = interpreter.expr()
        print(result)
            

if __name__ == '__main__':
    main()

另外,这天真冷~

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 176,328评论 25 709
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 14,688评论 4 61
  • 12、师傅的闺女养了两只狗,一只博美,一只吉娃娃,两只都特别好玩儿,毛绒绒又精致小巧,毛发梳得一丝不苟,走起路来同...
    王家人宁阅读 1,274评论 0 0
  • 简友者,至纯简单之友也。 直接明了,简之是为德。 无间隙,无猜疑,无繁杂,无牵扯,无挂碍。见堵则疏,见喜则欢,见怒...
    一元真人阅读 2,578评论 10 13

友情链接更多精彩内容