python编程(ply库)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

    长久以来,我一直对编译器都比较感兴趣。网上,大家推荐的龙书、《自制编译器》等等虽然也看过,但是我觉得都没有办法满足自己编写工业软件的要求。后来有一次看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编程)