【python】詞法語法解析模塊ply


官方手冊:http://www.dabeaz.com/ply/ply.html

以下例子都來自官方手冊:

 

以四則運算為例: x = 3 + 42 * (s - t)

詞法分析:

需要將其分解為:

'x','=', '3', '+', '42', '*', '(', 's', '-', 't', ')'

並且給每個部分起一個名字,標識這是什么東西。這些標識會用在后面的語法分析中。

('ID','x'), ('EQUALS','='), ('NUMBER','3'), 
('PLUS','+'), ('NUMBER','42), ('TIMES','*'),
('LPAREN','('), ('ID','s'), ('MINUS','-'),
('ID','t'), ('RPAREN',')'

例子:

# ------------------------------------------------------------
# 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()

注意:

里面名字的命名格式是固定的,ID的名稱必須叫tokens,每個ID具體的內容必須用t_ID來指定。

單個字符可以直接定義變量,復雜成分要用函數形式表示,並且一定要有一個該成分的正則表達式字符串

比如上面例子中,t_NUMBER函數中有一個r'\d+',這在一般的python程序中看起來沒有意義,但是在ply中則是必須的!它指定了模塊如何划分NUMBER。

更多注意事項參考官方手冊。

具體使用:

# Test it out
data = '''
3 + 4 * 10
  + -20 *2
'''

# Give the lexer some input
lexer.input(data)

# Tokenize
while True:
    tok = lexer.token()
    if not tok: 
        break      # No more input
    print(tok)

結果:

$ python example.py
LexToken(NUMBER,3,2,1)
LexToken(PLUS,'+',2,3)
LexToken(NUMBER,4,2,5)
LexToken(TIMES,'*',2,7)
LexToken(NUMBER,10,2,10)
LexToken(PLUS,'+',3,14)
LexToken(MINUS,'-',3,16)
LexToken(NUMBER,20,3,18)
LexToken(TIMES,'*',3,20)
LexToken(NUMBER,2,3,21)

 

 語法分析:

一個四則運算的語法結構是下面這個樣子:

expression : expression + term
           | expression - term
           | term

term       : term * factor
           | term / factor
           | factor

factor     : NUMBER
           | ( expression )

 

 

用ply實現四則運算語法分析:

# Yacc example

import ply.yacc as yacc

# Get the token map from the lexer.  This is required.
from calclex import tokens

def p_expression_plus(p):
    'expression : expression PLUS term'
    p[0] = p[1] + p[3]

def p_expression_minus(p):
    'expression : expression MINUS term'
    p[0] = p[1] - p[3]

def p_expression_term(p):
    'expression : term'
    p[0] = p[1]

def p_term_times(p):
    'term : term TIMES factor'
    p[0] = p[1] * p[3]

def p_term_div(p):
    'term : term DIVIDE factor'
    p[0] = p[1] / p[3]

def p_term_factor(p):
    'term : factor'
    p[0] = p[1]

def p_factor_num(p):
    'factor : NUMBER'
    p[0] = p[1]

def p_factor_expr(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
   result = parser.parse(s)
   print(result)

與詞法分析一樣,語法分析的命名方式也是固定的 p_成分名_動作。

函數一開始必須是一個聲明字符串,格式是 “成分名 :成分名 成分名 ...”  其中成分名可以是詞法分析中的ID,比如上面的PLUS, NUMBER等等。冒號右邊的是這個函數結果的成分名。即,語法分析通過組合各個ID得到結構化的結果。

成分相同的結構可以合並,如同時定義加法和減法

def p_expression(p):
    '''expression : expression PLUS term
                  | expression MINUS term'''
    if p[2] == '+':
        p[0] = p[1] + p[3]
    elif p[2] == '-':
        p[0] = p[1] - p[3]

可以看到字符串的內容有變化,多余一種組合的用 | 換行分隔。注意這個字符串的格式是固定的,不過PLUS和MINUS的順序沒有影響,哪個在上面都可以。

 

更多細節,參見手冊。

 

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM