程序员达达

Python

pycparser源码阅读笔记(3) – grammer

translation_unit : external_declaration | translation_unit external_declaration   external_declaration : function_definition | declaration | pp_directive | SEMI   pp_directive : PPHASH   function_definition : declarator declaration_list_opt compound_statement | declaration_specifiers declarator declaration_list_opt compound_statement   statement : labeled_statement | expression_statement | compound_statement | selection_statement | iteration_statement | jump_statement   decl_body : declaration_specifiers init_declarator_list_opt   declaration : decl_body SEMI…

pycparser源码阅读笔记(2) – c_lexer.py

lexer

里面整个就定义了一个名为Clexer的类。调用类成员函数input,就能设置好输入的string stream。而每次调用token则可以得到不同的token。 def __init__(self, error_func, on_lbrace_func, on_rbrace_func, type_lookup_func): """ Create a new Lexer. error_func: An error function. Will be called with an error message, line and column as arguments, in case of an error during lexing.   on_lbrace_func, on_rbrace_func: Called when an LBRACE or RBRACE is encountered (likely to push/pop type_lookup_func’s scope)   type_lookup_func: A…

pycparser源码阅读笔记(1) – example code

一共有6个py文件,教你如何使用pycparser。 cdecl.py 这个例子展示的是如果将变量的定义转化为人类可理解的语言。这个如果用来教大一新生C语言,估计效果不错。哈哈 c-to-c.py 使用方法为 python c-to-c.py filename 其中有两个函数“translate_to_c”和“_zz_test_translate”。其中后者是拿来测试用的,真正调用的是前者。比较两个函数,可以发现,生成ast有两种方法。 1. 如果输入是文件的话,可以使用: ast = parse_file(filename, use_cpp=True) 2. 如果输入是raw string的话,可以使用(假定src存放的是raw string): parser = c_parser.CParser() ast = parser.parse(src) 要遍历ast然后生成c代码的话,只需要在拿到ast的基础上先构造一个c_generator,然后调用改类的visit成员函数即可。估计CGenerator的visit函数是通过遍历了ast生成c代码。 generator = c_generator.CGenerator() print(generator.visit(ast)) explore_ast.py 这个文件里面提供了大量的注释。这些注释对于理解pycparser的工作原理以及进行二次开发都是很有帮助的。里面讲到,ast的顶层节点是FileAST,其children是“external declarations”,存放于ext[]的list中。总之如果想要研究ast的结构,那么就要好好读这个文件。 func_calls.py 该程序展示将某函数的调用全部显示出来。里面定义了一个名为FuncCallVisitor的类(继承于NodeVisitor)。其中visit_FuncCall被重载为: def visit_FuncCall(self, node): if node.name.name == self.funcname: print(’%s called at %s’ % ( self.funcname, node.name.coord)) 这样在调用visit来遍历ast的时候,会输出相应的信息。在不输入任何参数的情况下,使用hash.c作为c的原文件,而所跟踪的函数为malloc func_defs.py 该程序的作用就是将源文件中定义函数的名称以及行号全部显示出来。里面定义了一个作用于FuncDef的visitor。 using_cpp_libc.py 这个文件还是比较有研究价值的。pycparser不支持include语句,所以如果源代码里面有的话,最简单的办法就是调用cpp做preprocessing。但是pycparser在构建ast的时候,还是需要通过include一些假的头文件,来保证生成的ast不包含真正头文件里面的定义的。这个例子也看到,parse_file这个函数可以接收三个参数,分别指定1.是否使用cpp;2.cpp的名字;3.cpp所在路径。…

pycparser源码阅读笔记 (0)

C-programming-language

认真阅读完pycparser在GitHub的README,总结为如下: 1. 纯Python实现,非常容易上手以及改写,熟悉Lex和Yacc的人也很容易理解。 2。常用的用途有: – C code obfuscator (就是在C code里面加一堆乱七八糟的东西,让人看不懂。。) – Front-end for various specialized C compilers – Static code checker – Automatic unit-test discovery (用parser来辅助单元测试?果然compiler的技术用处很多啊) – Adding specialized extensions to the C language (基本上就是在C的基础上添加一些关键字) 3. 不支未经preprocess过的C代码,也就是说有#include和#define的都不行,需要用cpp预处理一遍。那肯定有人吐操说,尼马有几个C源文件不包含头文件的引用啊,比如说#include 这种。作者已经早有准备,通过用fake的include来取代头文件。也就是说碰到C标准library,parsing的时候不管,因为反正最后生成AST的时候,也不检查类型的semantics,只要保证用到的变量或者函数之前被定义过就好。 4. 如果你要改动pycparser,需要关注的是“pycparser/c_parser.py”和“pycparser/_c_ast.cfg”。前者里面大段的注释保证你能看懂parser究竟干了啥。后者是AST的节点结构定义。后者其实是利用脚本“_ast_gen.py”,参照配置文件“_c_ast.cfg”自动生成的

Lex/Yacc的Python实现

lex & yacc

最近在做一些compiler方面的工作,需要对源代码进行一些处理。以前上compiler课程的时候,用的是flex和bison作为C/C++下的前端工具。最近打算试试Python。找来找去,找到一个类似的模块“PLY”,是Python下面的对应实现,可以用来实现词法分析/语法分析。 PLY: http://www.dabeaz.com/ply/ 利用该模块,有人实现了一个简单的C的parser: pycparser: https://github.com/eliben/pycparser (GitHub上有两个,别点错了) 之后打算分析一下pycparser的源代码,好好学习一下怎么用这个模块。