天天看點

轉換器3:手寫PHP轉Python編譯器,詞法部分

上周寫了《ThinkPhp模闆轉Flask、Django模闆》

一時技癢,自然而然地想搞個大家夥,把整個PHP程式轉成Python。不比模闆,可以用正則比對偷懶,這次非寫一個Php編譯器不可。

上網搜了一下,發現大部分Python to xxx的transpile都是直接基于AST,省略了最重要的Tokenizer,Parser。直接寫個Visitor了事。要不然就是基于Antlr之類的生成器,搞一大堆代碼,看得令人心煩。

既然大家都不想做這個苦力,我就來試試,手工寫一個Php編譯器。分Tokenizer,Parser,Visitor三個部分來實作。

翻出《龍書》《虎書》做參考,仔細學了一回PHP,不學不知道,原來PHP有那麼多特性,做個編譯器真心累人。

詞法部分很簡單,就是一個自動機。設計了一個結構存放自動機,然後簡單粗暴地在自動機上程式設計,也顧不上什麼性能了,就是個一錘子買賣。

寫得還算快,調試不是很順,不過我是不會說的,哈

自動機不複雜,發上來大家看看,敬請指正。

self.statemachine = {
            'current': {
                'state': 'default', 'content': '', 'line': 0},
            'default': [
                {'name': 'open', 'next': 'php', 'extra': 0, 'start': 0, 'end': 0, 'cache': '',
                 'token': r'<\?'},
                {'name': 'open', 'next': 'php', 'extra': 0, 'start': 0, 'end': 0, 'cache': '',
                 'token': r'<\?php'}],
            'php': [
                {'name': 'close', 'next': 'default', 'extra': 0,
                 'token': r'\?>', 'start': 0, 'end': 0, 'cache': ''},
                {'name': 'lnum', 'next': '', 'extra': 0, 'start': 0, 'end': 0, 'cache': '',
                 'token': r'[0-9]+'},
                {'name': 'dnum', 'next': '', 'extra': 0, 'start': 0, 'end': 0, 'cache': '',
                 'token': r'([0-9]*\.[0-9]+)|([0-9]+\.[0-9]*)'},
                {'name': 'exponent', 'next': '', 'extra': 0, 'start': 0, 'end': 0, 'cache': '',
                 'token': r'(([0-9]+|([0-9]*\.[0-9]+)|([0-9]+\.[0-9]*))[eE][+-]?[0-9]+)'},
                {'name': 'hnum', 'next': '', 'extra': 0, 'start': 0, 'end': 0, 'cache': '',
                 'token': r'0x[0-9a-fA-F]+'},
                {'name': 'bnum', 'next': '', 'extra': 0, 'start': 0, 'end': 0, 'cache': '',
                 'token': r'0b[01]+'},
                {'name': 'label', 'next': '', 'extra': 0, 'start': 0, 'end': 0, 'cache': '',
                 'token': r'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'},
                {'name': 'comment', 'next': 'commentline', 'extra': 1,
                 'token': r'//', 'start': 0, 'end': 0, 'cache': ''},
                {'name': 'comment', 'next': 'commentline', 'extra': 1,
                 'token': r'#', 'start': 0, 'end': 0, 'cache': ''},
                {'name': 'comment', 'next': 'comment', 'extra': 1,
                 'token': r'/\*', 'start': 0, 'end': 0, 'cache': ''},
                {'name': 'string', 'next': 'string1', 'extra': 1,
                 'token': r'\'', 'start': 0, 'end': 0, 'cache': ''},
                {'name': 'string', 'next': 'string2', 'extra': 1,
                 'token': r'"', 'start': 0, 'end': 0, 'cache': ''},
                {'name': 'symbol', 'next': '', 'extra': 0, 'start': 0, 'end': 0, 'cache': '',
                 'token': r'[\\\{\};:,\.\[\]\(\)\|\^&\+-/\*=%!~$<>\?@]'}],
            'string1': [
                {'name': 'string', 'next': 'php', 'extra': 0,
                 'token': r'\'', 'start': 0, 'end': 0, 'cache': ''},
                {'name': 'string', 'next': 'escape1', 'extra': 1,
                 'token': r'\\', 'start': 0, 'end': 0, 'cache': ''},
                {'name': 'string', 'next': '', 'extra': 1,
                 'token': r'', 'start': 0, 'end': 0, 'cache': ''}],
            'escape1': [
                {'name': 'string', 'next': 'string1', 'extra': 1,
                 'token': r'.', 'start': 0, 'end': 0, 'cache': ''}],
            'string2': [
                {'name': 'string', 'next': 'php', 'extra': 0,
                 'token': r'\'', 'start': 0, 'end': 0, 'cache': ''},
                {'name': 'string', 'next': 'escape2', 'extra': 1,
                 'token': r'\\', 'start': 0, 'end': 0, 'cache': ''},
                {'name': 'string', 'next': '', 'extra': 1,
                 'token': r'', 'start': 0, 'end': 0, 'cache': ''}],
            'escape2': [
                {'name': 'string', 'next': 'string2', 'extra': 1,
                 'token': r'.', 'start': 0, 'end': 0, 'cache': ''}],
            'commentline': [
                {'name': 'comment', 'next': 'php', 'extra': 0,
                 'token': r'(\r|\n|\r\n)', 'start': 0, 'end': 0, 'cache': ''},
                {'name': 'comment', 'next': 'php', 'extra': 0,
                 'token': r'', 'start': 0, 'end': 0, 'cache': ''}],
            'comment': [
                {'name': 'comment', 'next': 'php', 'extra': 0,
                 'token': r'\*/', 'start': 0, 'end': 0, 'cache': ''},
                {'name': 'comment', 'next': '', 'extra': 1,
                 'token': r'', 'start': 0, 'end': 0, 'cache': ''}]}      

源碼:converterV0.3.zip

<未完待續>