【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163】
长久以来,我一直对编译器都比较感兴趣。网上,大家推荐的龙书、《自制编译器》等等虽然也看过,但是我觉得都没有办法满足自己编写工业软件的要求。后来有一次看lua,发现lua早期也是使用lex&yacc完成字符解析的,一下子找到了方法。最近因为使用python的关系,想看看python有没有类似的库,结果就找到了ply库。
1、安装ply库
建议大家到官网下载,直接利用源代码安装
sudo python setup.py install
偷懒一点的朋友,也可以直接用apt安装,
sudo apt-get install python-ply
2、ply资料
关于ply的资料,最权威的还是官方文档。
3、词法分析和语法分析
和lex、yacc工具不同,ply本身的规则文件也是编写成python形式的。而lex和yacc都是将规则文件转换成c文件,然后一起编译。
4、示例代码
这份示例代码也是根据官网的代码做了一点修改。其中calclex.py完成词法分析,calc.py完成语法分析。
calclex.py文件如下,
##!/bin/python
# ------------------------------------------------------------
# calclex.py
#
# tokenizer for a simple expression evaluator for
# numbers and +,-,*,/
# ------------------------------------------------------------
import ply.lex as lex
# List of token names. This is always required
tokens = (
'NUMBER',
'PLUS',
'MINUS',
'TIMES',
'DIVIDE',
'LPAREN',
'RPAREN',
)
# Regular expression rules for simple tokens
t_PLUS = r'\+'
t_MINUS = r'-'
t_TIMES = r'\*'
t_DIVIDE = r'/'
t_LPAREN = r'\('
t_RPAREN = r'\)'
# A regular expression rule with some action code
def t_NUMBER(t):
r'\d+'
t.value = int(t.value)
return t
# Define a rule so we can track line numbers
def t_newline(t):
r'\n+'
t.lexer.lineno += len(t.value)
# A string containing ignored characters (spaces and tabs)
t_ignore = ' \t'
# Error handling rule
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
# Build the lexer
lexer = lex.lex()
calc.py文件如下,
# Yacc example
import ply.yacc as yacc
# Get the token map from the lexer. This is required.
from calclex import tokens
def p_add(p):
'expression : expression PLUS term'
p[0] = p[1] + p[3]
def p_sub(p):
'expression : expression MINUS term'
p[0] = p[1] - p[3]
def p_term(p):
'expression : term'
p[0] = p[1]
def p_term_factor(p):
'term : term TIMES factor'
p[0] = p[1] * p[3]
def p_term_divide(p):
'term : term DIVIDE factor'
p[0] = p[1] / p[3]
def p_factor(p):
'term : factor'
p[0] = p[1]
def p_factor_num(p):
'factor : NUMBER'
p[0] = p[1]
def p_fact_param(p):
'factor : LPAREN expression RPAREN'
p[0] = p[2]
# Error rule for syntax errors
def p_error(p):
print("Syntax error in input!")
# Build the parser
parser = yacc.yacc()
while True:
try:
s = raw_input('calc > ')
except EOFError:
break
if not s: continue
if s == 'q' or s =='Q': break
result = parser.parse(s)
print(result)
5、添加Makefile
因为编译的时候会生成很多中间文件,所以建议大家可以编一个Makefile,完成自动化清理和执行的工作。
.PHONY: all clean
all: run
run:process clean
process:
python calc.py
clean:
@rm -rf parser.out *.pyc parsetab.py
6、后续
关于lex&yacc其实有两种用法,一种是生成ast,也就是抽象语法树,这样可以进一步进行语义分析、生成中间语言、优化、生成汇编代码等等。另外一种就是直接生成执行语言,这也是大多数脚本语言的做法,比如lua。脚本语言特别适合无编译运行,此外它的动态加载、垃圾回收、和c自由交互等系列功能也很实用。希望大家可以借ply这个工具好好了解一下。
更多推荐
python编程(ply库)
发布评论