python实现解释器_Python怎样使用解释器

将源码中的字符分割成标记符(token)

将标记符组织成一棵抽象语法树(AST)。抽象语法树就是中间表示。

评估这棵抽象语法树,并在最后打印这棵树的状态

将字符串分割成标记符的过程叫做「词法分析」,通过一个词法分析器完成。关键字是很短,易于理解的字符串,包含程序中最基本的部分,如数字、标识符、关键字和操作符。词法分析器会除去空格和注释,因为它们都会被解释器忽略。

resize,m_lfit,w_600,h_800,limit_1

将标记符组织成抽象语法树(AST)的过程称为「解析过程」。解析器将程序的结构提取成一张我们可以评估的表格。

resize,m_lfit,w_600,h_800,limit_1

实际执行这个解析过的抽象语法树的过程称为评估。这实际上是这个解析器中最简单的部分了。

本文会把重点放在词法分析器上。我们将编写一个通用的词汇库,然后用它来为IMP创建一个词法分析器。下一篇文章将会重点打造一个语法分析器和评估计算器。

词汇库

词法分析器的操作相当简单。它是基于正则表达式的,所以如果你不熟悉它们,你可能需要读一些资料。简单来说,正则表达式就是一种能描述其他字符串的特殊的格式化的字符串。你可以使用它们去匹配电话号码或是邮箱地址,或者是像我们遇到在这种情况,不同类型的标记符。

词法分析器的输入可能只是一个字符串。简单起见,我们将整个输入文件都读到内存中。输出是一个标记符列表。每个标记符包括一个值(它代表的字符串)和一个标记(表示它是一个什么类型的标记符)。语法分析器会使用这两个数据来决定如何构建一棵抽象语法树。

由于不论何种语言的词法分析器,其操作都大同小异,我们将创建一个通用的词法分析器,包括一个正则表达式列表和对应的标签(tag)。对每一个表达式,它都会检查是否和当前位置的输入文本匹配。如果匹配,匹配文本就会作为一个标记符被提取出来,并且被加上该正则表达式的标签。如果该正则表达式没有标签,那么这段文本将会被丢弃。这样免得我们被诸如注释和空格之类的垃圾字符干扰。如果没有匹配的正则表达式,程序就要报错并终止。这个过程会不断循环直到没有字符可匹配。

下面是一段来自词汇库的代码:

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

import sys

import re

def lex(characters, token_exprs):

pos = 0

tokens = []

while pos < len(characters):

match = None

for token_expr in token_exprs:

pattern, tag = token_expr

regex = re.compile(pattern)

match = regex.match(characters, pos)

if match:

text = match.group(0)

if tag:

token = (text, tag)

tokens.append(token)

break

if not match:

sys.stderr.write('Illegal character: %sn' % characters[pos])

sys.exit(1)

else:

pos = match.end(0)

return tokens

注意,我们遍历正则表达式的顺序很重要。lex会遍历所有的表达式,然后接受第一个匹配成功的表达式。这也就意味着,当使用词法分析器时,我们应当首先考虑最具体的表达式(像那些匹配算子(matching operator)和关键词),其次才是比较一般的表达式(像标识符和数字)。

词法分析器

给定上面的lex函数,为IMP定义一个词法分析器就非常简单了。首先我们要做的就是为标记符定义一系列的标签。IMP只需要三个标签。RESERVED表示一个保留字或操作符。INT表示一个文字整数。ID代表标识符。

Python

1

2

3

4

5

import lexer

RESERVED = 'RESERVED'

INT = 'INT'

ID = 'ID'

接下来定义词法分析器将会用到的标记符表达式。前两个表达式匹配空格和注释。它们没有标签,所以 lex 会丢弃它们匹配到的所有字符。

Python

1

2

3

token_exprs = [

(r'[ nt]+', None),

(r'#[^n]*', None),

然后,只剩下所有的操作符和保留字了。记住,每个正则表达式前面的“r”表示这个字符串是“raw”;Python不会处理任何转义字符。这使我们可以在字符串中包含进反斜线,正则表达式正是利用这一点来转义操作符比如“+”和“*”。

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

(r':=', RESERVED),

(r'(', RESERVED),

(r')', RESERVED),

(r';', RESERVED),

(r'+', RESERVED),

(r'-', RESERVED),

(r'*', RESERVED),

(r'/', RESERVED),

(r'<=', RESERVED),

(r'<', RESERVED),

(r'>=', RESERVED),

(r'>', RESERVED),

(r'=', RESERVED),

(r'!=', RESERVED),

(r'and', RESERVED),

(r'or', RESERVED),

(r'not', RESERVED),

(r'if', RESERVED),

(r'then', RESERVED),

(r'else', RESERVED),

(r'while', RESERVED),

(r'do', RESERVED),

(r'end', RESERVED),

最后,轮到整数和标识符的表达式。要注意的是,标识符的正则表达式会匹配上面的所有的保留字,所以它一定要留到最后。

Python

1

2

3

(r'[0-9]+', INT),

(r'[A-Za-z][A-Za-z0-9_]*', ID),

]

既然正则表达式已经定义好了,我们还需要创建一个实际的lexer函数。

Python

1

2

def imp_lex(characters):

return lexer.lex(characters, token_exprs)

如果你对这部分感兴趣,这里有一些驱动代码可以测试输出:

Python

1

2

3

4

5

6

7

8

9

10

11

import sys

from imp_lexer import *

if __name__ == '__main__':

filename = sys.argv[1]

file = open(filename)

characters = file.read()

file.close()

tokens = imp_lex(characters)

for token in tokens:

print token

继续……

在本系列的下一篇文章中,我会讨论解析器组合,然后描述如何使用他们从lexer中生成的标记符列表建立抽象语法树。

如果你对于实现IMP解释器很感兴趣,你可以从这里下载全部的源码。

在源码包含的示例文件中运行解释器:

Python

1

python imp.py hello.imp

运行单元测试:

Python

1

python test.py

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值