转换器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': ''}]}
<未完待续>
转换器3:手写PHP转Python编译器,词法部分的更多相关文章
- 转换器4:手写PHP转Python编译器,语法解析部分
写完词法部分,又有很多杂事,周末终于有空来实现伟大的语法解析部分了. 撸完代码之后发现,程序太短了,不算上状态机,才186行(含注释),关键代码不到100行.运行调试过后,发现还行.居然可以解析One ...
- 为sproto手写了一个python parser
这是sproto系列文章的第三篇,可以参考前面的<为sproto添加python绑定>.<为python-sproto添加map支持>. sproto是云风设计的序列化协议,用 ...
- 手写 Vue2 系列 之 编译器
前言 接下来就要正式进入手写 Vue2 系列了.这里不会从零开始,会基于 lyn-vue 直接进行升级,所以如果你没有阅读过 手写 Vue 系列 之 Vue1.x,请先从这篇文章开始,按照顺序进行学习 ...
- KNN手写实践:Python基于数据集整体计算以及排序
1. 距离计算,不要通过遍历每个样本来计算和指定样本距离,而是通过对于指定样本进行广播(复制)成为一个shape和全局一致后,再进行整体计算,这里的广播 / 复制采用的是tile函数来实现的: 2. ...
- Python学习宝典,Python400集让你成为从零基础到手写神经网络的Python大神
当您学完Python,你学到了什么? 开发网站! 或者, 基础语法要点.函数.面向对象编程.调试.IO编程.进程与线程.正则表达式... 当你学完Python,你可以干什么? 当程序员! 或者, 手写 ...
- TensorFlow下利用MNIST训练模型识别手写数字
本文将参考TensorFlow中文社区官方文档使用mnist数据集训练一个多层卷积神经网络(LeNet5网络),并利用所训练的模型识别自己手写数字. 训练MNIST数据集,并保存训练模型 # Pyth ...
- 手写 Vue2 系列 之 初始渲染
前言 上一篇文章 手写 Vue2 系列 之 编译器 中完成了从模版字符串到 render 函数的工作.当我们得到 render 函数之后,接下来就该进入到真正的挂载阶段了: 挂载 -> 实例化渲 ...
- 使用神经网络来识别手写数字【译】(三)- 用Python代码实现
实现我们分类数字的网络 好,让我们使用随机梯度下降和 MNIST训练数据来写一个程序来学习怎样识别手写数字. 我们用Python (2.7) 来实现.只有 74 行代码!我们需要的第一个东西是 MNI ...
- 手写数字识别 ----在已经训练好的数据上根据28*28的图片获取识别概率(基于Tensorflow,Python)
通过: 手写数字识别 ----卷积神经网络模型官方案例详解(基于Tensorflow,Python) 手写数字识别 ----Softmax回归模型官方案例详解(基于Tensorflow,Pytho ...
随机推荐
- js模块化开发——require.js的用法
这个系列的第一部分和第二部分,介绍了Javascript模块原型和理论概念,今天介绍如何将它们用于实战. 我采用的是一个非常流行的库require.js. 一.为什么要用require.js? 最早的 ...
- Java学习之旅基础知识篇:数据类型及流程控制
经过开篇对Java运行机制及相关环境搭建,本篇主要讨论Java程序开发的基础知识点,我简单的梳理一下.在讲解数据类型之前,我顺便提及一下Java注释:单行注释.多行注释以及文档注释,这里重点强调文档注 ...
- spring mvc redirect设置FlashAttribute
在Controller中设置: @RequestMapping("/redir") public String redir(Model model, RedirectAttribu ...
- java算法 蓝桥杯 格子位置
问题描述 输入三个自然数N,i,j (1<=i<=N,1<=j<=N),输出在一个N*N格的棋盘中,与格子(i,j)同行.同列.同一对角线的所有格子的位置. 输入格式 输入共三 ...
- iOS 图片压缩方法
iOS 图片压缩方法 两种图片压缩方法 两种压缩图片的方法:压缩图片质量(Quality),压缩图片尺寸(Size). 压缩图片质量 NSData *data = UIImageJPEGReprese ...
- Neutron Router 工作原理 - 每天5分钟玩转 OpenStack(142)
上一节我们创建了 router 连通了 vlan100 和 vlan101, 今天分析router是如何工作的.首先查看控制节点的网络结构发生了什么变化: br-int 上多了两个 port: 1. ...
- 模式字符串匹配问题(KMP算法)
这两天又看了一遍<算法导论>上面的字符串匹配那一节,下面是实现的几个程序,可能有错误,仅供参考和交流. 关于详细的讲解,网上有很多,大多数算法及数据结构书中都应该有涉及,由于时间限制,在这 ...
- JAVA版A星算法实现
import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.u ...
- 【java设计模式】之 建造者(Builder)模式
我们还是举上一节的例子:生产汽车.上一节我们通过模板方法模式控制汽车跑起来的动作,那么需求是无止境的,现在如果老板又增加了额外的需求:汽车启动.停止.鸣笛引擎声都由客户自己控制,他想要什么顺序就什么顺 ...
- 复制vmware虚拟机后,eth0无法显示问题
1. rm /etc/udev/rules.d/70-persistent-net.rules 2. 删除 /etc/sysconfig/network-scripts/ifcfg-eth0下的UUI ...