每種標記用一個正則表達式規則來表示,每個規則需要以”t_“開頭聲明,表示該聲明是對標記的規則定義。對于簡單的標記,可以定義成這樣(在Python中使用raw string能比較方便的書寫正則表達式):
~~~
t_PLUS = r'\+'
~~~
這里,緊跟在t_后面的單詞,必須跟標記列表中的某個標記名稱對應。如果需要執行動作的話,規則可以寫成一個方法。例如,下面的規則匹配數字字串,并且將匹配的字符串轉化成Python的整型:
~~~
def t_NUMBER(t):
r'\d+'
t.value = int(t.value)
return t
~~~
如果使用方法的話,正則表達式寫成方法的文檔字符串。方法總是需要接受一個LexToken實例的參數,該實例有一個t.type的屬性(字符串表示)來表示標記的類型名稱,t.value是標記值(匹配的實際的字符串),t.lineno表示當前在源輸入串中的作業行,t.lexpos表示標記相對于輸入串起始位置的偏移。默認情況下,t.type是以t_開頭的變量或方法的后面部分。方法可以在方法體里面修改這些屬性。但是,如果這樣做,應該返回結果token,否則,標記將被丟棄。
在lex內部,lex.py用`re`模塊處理模式匹配,在構造最終的完整的正則式的時候,用戶提供的規則按照下面的順序加入:
1. 所有由方法定義的標記規則,按照他們的出現順序依次加入
2. 由字符串變量定義的標記規則按照其正則式長度倒序后,依次加入(長的先入)
3. 順序的約定對于精確匹配是必要的。比如,如果你想區分‘=’和‘==’,你需要確保‘==’優先檢查。如果用字符串來定義這樣的表達式的話,通過將較長的正則式先加入,可以幫助解決這個問題。用方法定義標記,可以顯示地控制哪個規則優先檢查。
為了處理保留字,你應該寫一個單一的規則來匹配這些標識,并在方法里面作特殊的查詢:
~~~
reserved = {
'if' : 'IF',
'then' : 'THEN',
'else' : 'ELSE',
'while' : 'WHILE',
...
}
tokens = ['LPAREN','RPAREN',...,'ID'] + list(reserved.values())
def t_ID(t):
r'[a-zA-Z_][a-zA-Z_0-9]*'
t.type = reserved.get(t.value,'ID') # Check for reserved words
return t
~~~
這樣做可以大大減少正則式的個數,并稍稍加快處理速度。注意:你應該避免為保留字編寫單獨的規則,例如,如果你像下面這樣寫:
~~~
t_FOR = r'for'
t_PRINT = r'print'
~~~
但是,這些規則照樣也能夠匹配以這些字符開頭的單詞,比如’forget’或者’printed’,這通常不是你想要的。
- 0 一些翻譯約定
- 1 前言和預備
- 2 介紹
- 3 PLY概要
- 4 Lex
- 4.1 Lex的例子
- 4.2 標記列表
- 4.3 標記的規則
- 4.4 標記的值
- 4.5 丟棄標記
- 4.6 行號和位置信息
- 4.7 忽略字符
- 4.8 字面字符
- 4.9 錯誤處理
- 4.10 構建和使用lexer
- 4.11 @TOKEN裝飾器
- 4.12 優化模式
- 4.13 調試
- 4.14 其他方式定義詞法規則
- 4.15 額外狀態維護
- 4.16 Lexer克隆
- 4.17 Lexer的內部狀態
- 4.18 基于條件的掃描和啟動條件
- 4.19 其他問題
- 5 語法分析基礎
- 6 Yacc
- 6.1 一個例子
- 6.2 將語法規則合并
- 6.3 字面字符
- 6.4 空產生式
- 6.5 改變起始符號
- 6.6 處理二義文法
- 6.7 parser.out調試文件
- 6.8 處理語法錯誤
- 6.9 行號和位置的跟蹤
- 6.10 構造抽象語法樹
- 6.11 嵌入式動作
- 6.12 Yacc的其他
- 7 多個語法和詞法分析器
- 8 使用Python的優化模式
- 9 高級調試
- 9.1 調試lex()和yacc()命令
- 9.2 運行時調試
- 10 如何繼續