原文:Let’s Build A Simple Interpreter. Part 1.

文章目录

  • 【编译原理】让我们来构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 1.)(python/c/c++版)(part 1)
    • 将输入字符串分解为token标记的过程称为词法分析(lexical analysis),词法分析器( lexical analyzer或 lexer )也叫扫描器(scanner )或标记器(tokenizer)。
    • 用c/c++实现(版本一:原版)
    • 版本二(版本一的升级版,[自认为]解决了版本一的一些混乱逻辑)

【编译原理】让我们来构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 1.)(python/c/c++版)(part 1)

pascal代码,我们要为这种代码做一个解释器

program factorial;function factorial(n: integer): longint;
beginif n = 0 thenfactorial := 1elsefactorial := n * factorial(n - 1);
end;varn: integer;beginfor n := 0 to 16 dowriteln(n, '! = ', factorial(n));
end.

首先我们先来实现pascal编译器的一个小功能,两个数的加法,我们准备用python实现,但是如果你想用其他语言实现也是可行的,这是它的代码:

# -*- coding: utf-8 -*-
"""
@File    : 1.py
@Time    : 2021/5/19 14:44
@Author  : Dontla
@Email   : sxana@qq.com
@Software: PyCharm
"""
# Token types
#
# EOF (end-of-file) token is used to indicate that
# there is no more input left for lexical analysis
INTEGER, PLUS, EOF = 'INTEGER', 'PLUS', 'EOF'class Token(object):def __init__(self, type_, value):# token type: INTEGER, PLUS, or EOFself.type = type_# token value: 0, 1, 2. 3, 4, 5, 6, 7, 8, 9, '+', or Noneself.value = valuedef __str__(self):"""String representation of the class instance.Examples:Token(INTEGER, 3)Token(PLUS '+')"""return 'Token({type}, {value})'.format(type=self.type,value=repr(self.value))def __repr__(self):return self.__str__()class Interpreter(object):def __init__(self, text):# 客户端字符串输入, 比如 "3+5"self.text = text# self.pos is an index into self.textself.pos = 0# current token instance(当前标记实例)self.current_token = Nonedef error(self):raise Exception('Error parsing input')  # 语法分析输入出错def get_next_token(self):"""Lexical analyzer (also known as scanner or tokenizer)This method is responsible for breaking a sentenceapart into tokens. One token at a time.词法分析器(也称为扫描器scanner或标记器tokenizer)这个方法负责将一个句子分解成标记tokens。一次一个标记"""text = self.text# is self.pos index past the end of the self.text ?# if so, then return EOF token because there is no more# input left to convert into tokens# self.pos索引是否超过self.text的结尾?# 如果是,则返回EOF标记,因为没有更多的标记# 向左输入以转换为标记if self.pos > len(text) - 1:return Token(EOF, None)# get a character at the position self.pos and decide# what token to create based on the single character# 在self.pos位置获取一个字符,并根据单个字符决定要创建的标记current_char = text[self.pos]# if the character is a digit then convert it to# integer, create an INTEGER token, increment self.pos# index to point to the next character after the digit,# and return the INTEGER token# 如果字符是数字,则将其转换为整型,创建整型标记,增加self.pos索引以指向数字后面的下一个字符,然后返回整型标记if current_char.isdigit():  # isdigit()函数,全是数字返回True,否则返回Falsetoken = Token(INTEGER, int(current_char))  # 创建一个tokenself.pos += 1return tokenif current_char == '+':token = Token(PLUS, current_char)self.pos += 1return tokenself.error()def eat(self, token_type):# compare the current token type with the passed token# type and if they match then "eat" the current token# and assign the next token to the self.current_token,# otherwise raise an exception.if self.current_token.type == token_type:self.current_token = self.get_next_token()else:self.error()def expr(self):"""expr -> INTEGER PLUS INTEGER"""# set current token to the first token taken from the inputself.current_token = self.get_next_token()# we expect the current token to be a single-digit integerleft = self.current_tokenself.eat(INTEGER)# we expect the current token to be a '+' tokenop = self.current_tokenself.eat(PLUS)# we expect the current token to be a single-digit integerright = self.current_tokenself.eat(INTEGER)# after the above call the self.current_token is set to# EOF token# at this point INTEGER PLUS INTEGER sequence of tokens# has been successfully found and the method can just# return the result of adding two integers, thus# effectively interpreting client inputresult = left.value + right.valuereturn resultdef main():while True:try:# 要在Python3下运行,请将“raw_input”调用替换为“input”# text = raw_input('calc> ')text = input('calc> ')  # 获取键盘输入,参数为提示信息except EOFError:  # 不知是什么异常breakif not text:continueinterpreter = Interpreter(text)result = interpreter.expr()print(result)if __name__ == '__main__':main()

运行结果:

D:\python_virtualenv\my_flask\Scripts\python.exe C:/Users/Administrator/Desktop/新建文件夹/1.py
calc> 1+2
3
calc>

将输入字符串分解为token标记的过程称为词法分析(lexical analysis),词法分析器( lexical analyzer或 lexer )也叫扫描器(scanner )或标记器(tokenizer)。

>>> from calc1 import Interpreter
>>>
>>> interpreter = Interpreter('3+5')
>>> interpreter.get_next_token()
Token(INTEGER, 3)
>>>
>>> interpreter.get_next_token()
Token(PLUS, '+')
>>>
>>> interpreter.get_next_token()
Token(INTEGER, 5)
>>>
>>> interpreter.get_next_token()
Token(EOF, None)
>>>

让我们回顾一下您的解释器如何评估算术表达式:

  • 解释器接受一个输入字符串,比如说“3+5”
  • 解释器调用expr方法在词法分析器get_next_token返回的标记流中查找结构。它试图找到的结构是INTEGER PLUS INTEGER的形式。在确认结构后,它通过添加两个INTEGER标记的值来解释输入,因为此时解释器很清楚它需要做的是添加两个整数 3 和 5。

检查理解:
什么是解释器interpreter?
什么是编译器compiler?
解释器和编译器有什么区别?
什么是标记token?
将输入分解为标记的过程的名称是什么?(词法分析)
进行词法分析lexical analysis的解释器的部分是什么?
解释器或编译器的那部分的其他常见名称是什么?

用c/c++实现(版本一:原版)

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>struct Interpreter
{char* text;int pos;struct Token (*get_next_token)(struct Interpreter*);
};struct Token
{int type;char value;
};struct Token get_next_token(struct Interpreter* pipt) {if (pipt->pos > (strlen(pipt->text)-1)) {struct Token token = {3, '\0'};//3表示EOF,2表示+,1表示数字return token;}char current_char = pipt->text[pipt->pos];if (current_char>='0' && current_char<='9') {struct Token token = {1, current_char};pipt->pos++;return token;}if (current_char == '+') {struct Token token = { 2, current_char };pipt->pos++;return token;}printf("输入非法!\n");exit(-1);//如果都不是以上的字符,则报错并退出程序
}char eat(struct Token* pcurrent_token, struct Interpreter* pipt, int type) {char former_token_value = pcurrent_token->value;if (pcurrent_token->type == type) {*pcurrent_token = pipt->get_next_token(pipt);}else {printf("输入非法!\n");exit(-1);}return former_token_value;
}int expr(char* text) {struct Interpreter ipt = {text, 0, get_next_token};struct Token current_token = ipt.get_next_token(&ipt);char temp;temp = eat(&current_token, &ipt, 1);//断言第一个字符是数字int left = temp - '0';eat(&current_token, &ipt, 2);//断言第三个字符是加号temp = eat(&current_token, &ipt, 1);//断言第三个字符是数字int right = temp - '0';int result = left + right;return result;}int main() {char text[10];while (1){printf("请输入算式:\n");scanf_s("%s", text, sizeof(text));int result = expr(text);printf("= %d\n\n", result);}return 0;
}

运行结果:

请输入算式:
2+8
= 10请输入算式:
1+5
= 6请输入算式:
3+4
= 7请输入算式:

版本二(版本一的升级版,[自认为]解决了版本一的一些混乱逻辑)

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>#define is_digital 0
#define is_plus 1
#define is_minus 2
#define is_EOF 3struct Token
{int type;char value;
};struct Interpreter
{char* text;int pos;struct Token current_token;//struct Token(*get_next_token)(struct Interpreter*);
};void error() {printf("输入非法!\n");exit(-1);
}void get_next_token(struct Interpreter* pipt) {if (pipt->pos > (strlen(pipt->text) - 1)) {pipt->current_token = { is_EOF, '\0' };return;//struct Token token = { is_EOF, '\0' };//3表示EOF,2表示+,1表示数字//pipt->current_token = token;}char current_char = pipt->text[pipt->pos];if (current_char >= '0' && current_char <= '9') {//struct Token token = { is_digital, current_char };pipt->current_token = { is_digital, current_char };pipt->pos++;return;//pipt->current_token = token;}if (current_char == '+') {//struct Token token = { is_plus , current_char };pipt->current_token = { is_plus, current_char };pipt->pos++;return;//pipt->current_token = token;}if (current_char == '-') {//struct Token token = { is_minus , current_char };pipt->current_token = { is_minus, current_char };pipt->pos++;return;//pipt->current_token = token;}error();//如果都不是以上的字符,则报错并退出程序
}char eat(struct Interpreter* pipt, int type) {get_next_token(pipt);char token_value = pipt->current_token.value;if (pipt->current_token.type == type) {return token_value;}else {error();}
}int expr(char* text) {struct Interpreter ipt = { text, 0 };//get_next_token(&ipt);char temp;temp = eat( &ipt, is_digital);//断言第一个字符是数字int left = temp - '0';eat( &ipt, is_plus);//断言第三个字符是加号temp = eat( &ipt, is_digital);//断言第三个字符是数字int right = temp - '0';int result = left + right;return result;}int main() {char text[10];while (1){printf("请输入算式:\n");scanf_s("%s", text, sizeof(text));int result = expr(text);printf("= %d\n\n", result);}return 0;
}

运行结果:

请输入算式:
3+6
= 9请输入算式:
8+3
= 11请输入算式:

【编译原理】让我们来构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 1.)(python/c/c++版)(笔记)相关推荐

  1. 【编译原理】构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 9.)(笔记)语法分析(未完,先搁置了!)

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 9.) 文章目录 spi.py spi_lexer 我记得当我在大学(很久以前) ...

  2. 【编译原理】构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 8.)(笔记)一元运算符正负(+,-)

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 8.) 文章目录 C语言代码(作者没提供完整的python代码,关键的改动提供了 ...

  3. 【编译原理】构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 7.)(笔记)解释器 interpreter 解析器 parser 抽象语法树AST

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 7.) 文章目录 python代码 插--后序遍历 C语言代码(有错误) C语言 ...

  4. 【编译原理】让我们来构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 6.)(python/c/c++版)(笔记)

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 6.) 文章目录 python代码 C语言代码 总结 今天是这一天:) &quo ...

  5. 【编译原理】让我们来构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 5.)(python/c/c++版)(笔记)Lexer词法分析程序

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 5.) 文章目录 python代码 C语言代码 总结 你如何处理像理解如何创建解 ...

  6. 【编译原理】让我们来构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 4.)(python/c/c++版)(笔记)

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 4.) 文章目录 python代码 C语言代码 总结 在上一篇文章中,您学习了如 ...

  7. 【编译原理】让我们来构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 3.)(python/c/c++版)(笔记)

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 3.) 文章目录 python代码calc3.py C语言代码(calc3.cp ...

  8. 【编译原理】让我们来构建一个简单的解释器(Let’s Build A Simple Interpreter. Part 2.)(python/c/c++版)(笔记)

    [编译原理]让我们来构建一个简单的解释器(Let's Build A Simple Interpreter. Part 2.) 文章目录 python代码 c代码 总结 让我们再次深入研究解释器和编译 ...

  9. 学了编译原理能否用 Java 写一个编译器或解释器?

    16 个回答 默认排序​ RednaxelaFX JavaScript.编译原理.编程 等 7 个话题的优秀回答者 282 人赞同了该回答 能.我一开始学编译原理的时候就是用Java写了好多小编译器和 ...

最新文章

  1. log4j.logger java_log4j的多logger记录日志的简明使用
  2. java一个点向着另一个点移动_java – 在线性路径中从一个点移动一个对象
  3. Linux C 实现文件传输
  4. 北京关于领取2021年上半年合格证书的通知
  5. C++多态的基本语法与原理剖析
  6. 小程序点击调转带参数_带你走遍苏大的每个角落,校园导览小程序上线!
  7. python随机数生成的方法_python生成随机数的方法
  8. java类加载全过程
  9. RHEL5 安装VMware tools
  10. 数据结构与算法分析:第1、2章:引论和算法分析
  11. 电子发票对报销类saas的影响
  12. 数据加密 ---- SHA 加密
  13. Pyecharts--第一个培训数据dashboard(不太完美)
  14. 用canvas画转动的阴阳鱼
  15. python中步长什么意思_python步长什么意思【Python教程】,Python,step,步长
  16. 服务器被攻击后处理办法
  17. 超级眼局域网监控软件 员工禁止软件 可以控制时间段
  18. 安卓逆向学习——多开原理和实验
  19. vue的v-model双向数据绑定原理
  20. python HTTPConnectionPool(host:XX)Max retries exceeded with url

热门文章

  1. nagios新添加服务有时显示,有时不显示问题解决
  2. ASP.NET的五大数据控件分析
  3. java 枚举高级应用_【后端】java基础(5.6)java高级基础之枚举
  4. 【转载】扫盲概念RPA
  5. SAP支持服务的QA
  6. 一个关于VOFM的文章
  7. sap 预制凭证与暂存凭证的区别
  8. AgilePoint商业流程管理平台
  9. 如何接受上级指令_向上级领导汇报工作是经常的,想得赏识,了解如何接受上级的命令...
  10. swift python_单从语法角度讲,Swift 完爆 Java Python 和 C#吗?