前两个周末写了《手写PHP转Python编译器》的词法,语法分析部分,上个周末卡文了。

访问器部分写了两次都不满意,没办法,只好停下来,参考一下Python的实现。我实现的部分正好和Python是一个思路,就是生成CST(Concrete syntax tree)之后,再生成AST。由于我想创(tou)新(lan),所以未没有详细实现AST,而想绕过AST去生成代码。这下有点欲速不达了。

先看看Python执行代码的过程:

1.     Tokenizer进行词法分析,把源程序分解为Token

2.     Parser根据Token创建CST

3.     将CST转换为AST

4.     将AST编译为字节码

5.     执行字节码

现在我们要实现第3步。参考一下Python源码:

/* Transform the CST rooted at node * to the appropriate AST*/

mod_ty
PyAST_FromNode(const node *n, PyCompilerFlags *flags, const char *filename, PyArena *arena)

这是将CST转换为AST的入口,Node就是CST的节点。而下一步处理的函数为ast_for_stmt。

在ast_for_stmt里面,根据语法定义表,由各子函数生成AST的节点。例如函数调用:ast_for_call最后生成了这样的结构

Call(name_expr, NULL, NULL, NULL, NULL, LINENO(n),
n->n_col_offset, c->c_arena);

而这个C语言的函数与Python的AST库接口一模一样。

我们来看一个AST库的例子

import ast

expr = """
def hello(name='world'):
print("hello " + name)
hello()
"""
expr_ast = ast.parse(expr) print(ast.dump(expr_ast)) exec compile(expr_ast, '<string>', 'exec')

运行后的结果:

Module(
body=[
FunctionDef(
name='hello',
args=arguments(args=[Name(id='name', ctx=Param())], vararg=None,
kwarg=None,
defaults=[Str(s='world')]),
body=[
Print(dest=None, values=[BinOp(left=Str(s='hello '), op=Add(), right=Name(id='name', ctx=Load()))], nl=True)], decorator_list=[]),
Expr(value=Call(func=Name(id='hello', ctx=Load()), args=[], keywords=[], starargs=None, kwargs=None))])
hello world

注意结果中的Call(func=Name(id='hello', ctx=Load()), args=[], keywords=[], starargs=None, kwargs=None)

为了更好的说明问题,我们来为AST增加一条函数调用

import ast

expr = """
def hello(name='world'):
print("hello " + name)
hello()
"""
expr_ast = ast.parse(expr) n = ast.Name('hello', ast.Load(), lineno=5, col_offset=0)
p = ast.Str('windfic', lineno=5, col_offset=0)
c = ast.Call(n, [p], [], None, None, lineno=5, col_offset=0)
e = ast.Expr(c, lineno=5, col_offset=0)
expr_ast.body.append(e) exec compile(expr_ast, '<string>', 'exec')

运行后,结果为

hello world
hello windfic

就这样,我们已经可以直接生成一个AST结构,并且运行了。

以前,我一直是想先由Php生成AST,再生成Python代码。但是突然发现,我们不必生成代码了,可以直接生成兼容Python的AST结构,并且可以在Python中直接运行。

而Python的语法定义也不算很大,在Python文档中已经完整列出了

 module Python version "$Revision$"
{
mod = Module(stmt* body)
| Interactive(stmt* body)
| Expression(expr body) -- not really an actual node but useful in Jython's typesystem.
| Suite(stmt* body) stmt = FunctionDef(identifier name, arguments args,
stmt* body, expr* decorator_list)
| ClassDef(identifier name, expr* bases, stmt* body, expr* decorator_list)
| Return(expr? value) | Delete(expr* targets)
| Assign(expr* targets, expr value)
| AugAssign(expr target, operator op, expr value) -- not sure if bool is allowed, can always use int
| Print(expr? dest, expr* values, bool nl) -- use 'orelse' because else is a keyword in target languages
| For(expr target, expr iter, stmt* body, stmt* orelse)
| While(expr test, stmt* body, stmt* orelse)
| If(expr test, stmt* body, stmt* orelse)
| With(expr context_expr, expr? optional_vars, stmt* body) ...

想要完整实现Php的CST转Python的AST还需要一些时间,先来一个例子吧demo.php

<?php

function hello($name='world'){
echo 'hello ' . $name;
} hello();
if __name__ == '__main__':
CompilerPhp().compile_file('src/demo.php').execute()

运行上面的例子,结果与Python版本的例子完全一样。

试着用Unparse生成代码,也一切正常,唯一要说的,就是编码问题,不能使用Unicode。

以上

源码:converterV0.5.zip

这个项目到现在差不多有三个星期的狂热编码,做到现在也已经完全和最初的设想是两码事了。最核心及最难的部分,都已经完成。是时候休整一段时间了。最后再啰嗦几句。

很多人可能还不了解这个项目的价值所在,或者说我想输出的价值在哪里。这个项目实际上是一种廉价编译器的试验品。廉价编译器,意味着我们可以很轻易地扩充支持的语言,比如所有的主流语言。这个不难。做到了这一步,我们可以想像一下,这个工具的价值在哪里。至少有三种后续的可能:

1、还是Transpiler,就是这个项目。

2、源码阅读器。

3、源码(片段)管理、搜索、自动生成工具。

转换器5:参考Python源码,实现Php代码转Ast并直接运行的更多相关文章

  1. python源码书籍

    <Python源码剖析>一书现在很难买到,目前大部分都是电子书. 为了更好地利用Python语言,无论是使用Python语言本身,还是将Python与C/C++交互使用,深刻理解Pytho ...

  2. Python 源码学习之内存管理 -- (转)

    Python 的内存管理架构(Objects/obmalloc.c): _____ ______ ______ ________ [ int ] [ dict ] [ list ] ... [ str ...

  3. 分位数回归及其Python源码

    分位数回归及其Python源码 天朗气清,惠风和畅.赋闲在家,正宜读书.前人文章,不得其解.代码开源,无人注释.你们不来,我行我上.废话少说,直入主题.o( ̄︶ ̄)o 我们要探测自变量 与因变量 的关 ...

  4. 读python源码--对象模型

    学python的人都知道,python中一切皆是对象,如class生成的对象是对象,class本身也是对象,int是对象,str是对象,dict是对象....所以,我很好奇,python是怎样实现这些 ...

  5. VS2013编译python源码

    系统:win10 手头有个python模块,是用C写的,想编译安装就需要让python调用C编译器.直接编译发现使用的是vc9编译,不支持C99标准(两个槽点:为啥VS2008都还不支持C99?手头这 ...

  6. 三种排序算法python源码——冒泡排序、插入排序、选择排序

    最近在学习python,用python实现几个简单的排序算法,一方面巩固一下数据结构的知识,另一方面加深一下python的简单语法. 冒泡排序算法的思路是对任意两个相邻的数据进行比较,每次将最小和最大 ...

  7. 《python源码剖析》笔记一——python编译

    1.python的架构: 2.python源码的组织结构: 3.windows环境下编译python:

  8. 类似py2exe软件真的能保护python源码吗

    类似py2exe软件真的能保护python源码吗 背景 最近写了个工具用于对项目中C/C++文件的字符串常量进行自动化加密处理,用python写的,工具效果不错,所以打算在公司内部推广.为了防止代码泄 ...

  9. list源码4(参考STL源码--侯捷):transfer、splice、merge、reverse、sort

    list源码1(参考STL源码--侯捷):list节点.迭代器.数据结构 list源码2(参考STL源码--侯捷):constructor.push_back.insert list源码3(参考STL ...

随机推荐

  1. canvas粒子时钟

    前面的话 本文将使用canvas实现粒子时钟效果 效果展示 点阵数字 digit.js是一个三维数组,包含的是0到9以及冒号(digit[10])的二维点阵.每个数字的点阵表示是7*10大小的二维数组 ...

  2. C# 6 与 .NET Core 1.0 高级编程 - 37 章 ADO.NET

    译文,个人原创,转载请注明出处,有不对的地方欢迎指出与交流. 英文原文:Professional C# 6 and .NET Core 1.0 - 37 ADO.NET --------------- ...

  3. asp.net权限认证:摘要认证(digest authentication)

    asp.net权限认证系列 asp.net权限认证:Forms认证 asp.net权限认证:HTTP基本认证(http basic) asp.net权限认证:Windows认证 asp.net权限认证 ...

  4. 图片上传裁剪zyupload

    图片上传控件用的是zyupload控件,使用过程中遇到了一些问题,特别记录下来 上图是目前的使用效果,这个控件我是用js代码动态添加出来的 HTML代码: <div class="wi ...

  5. php抽奖概率算法(刮刮卡,大转盘)

    两种方法:①概率随着抽的奖项的变少而时刻变化 经典的概率算法函数:如下 <?php /*  * 经典的概率算法,  * $proArr是一个预先设置的数组,  * 假设数组为:array(20, ...

  6. Spring应用上下文中Bean的生命周期

    Bean装载到Spring应用上下文的生命周期,如图: Bean在Spring容器中从创建到销毁经历了若干个阶段,每一阶段都可以对Spring如何管理Bean进行个性化定制,以下我们通过代码去验证生命 ...

  7. getchar()不停止原因

    应该是你的输入流中还有残留的字符,getchar()()会接受那个字符.你可以在调用getchar()()之前用fflush(stdin)刷新一下输入缓冲区. 上面一段里面,应该有读入语句吧,没读干净 ...

  8. BZOJ 2456: mode(乱搞)

    挺神奇的一道题,被1M内存坑了好久= =,这道题得记录当前众数以及众数与其他数的差,如果现在读入的这个数与众数相等,就加1,否则减一,如果差为0就替代掉他,可以证明如果众数存在的话这样一定能找出来 C ...

  9. c++中,int *a=new int(120)语句的意思

    int *a=new int(120); 申请一个整型变量空间,赋初值为120,并定义一个整型指针a指向该地址空间 int *a=new int[120]; 申请120个整型变量空间,没有赋初值,并定 ...

  10. ldd获得可执行程序的所有库并输出到指定目录

    #!/bin/bash ########################################################################## #ldd可以查看程序的库依 ...