Skip to content

Commit

Permalink
updated parser, renderers, and generator. The extern declaration gene…
Browse files Browse the repository at this point in the history
…rator is probably at 90% complete with minor bug fixing and #include->cimport left to do.
  • Loading branch information
sccolbert committed Apr 16, 2011
1 parent 18bba4c commit 3f92634
Show file tree
Hide file tree
Showing 5 changed files with 364 additions and 314 deletions.
99 changes: 45 additions & 54 deletions cwrap/codegen.py
@@ -1,61 +1,52 @@
from collections import defaultdict
from cStringIO import StringIO
import os
import re
import subprocess
import tempfile

import pycparser

import cy_ast
import translate
import parser
import renderers


class CodeGenerator(object):

def __init__(self, config):
self.config = config
self._translator = translate.ASTTranslator()
self._extern_renderer = renderers.ExternRenderer()

def _preprocess(self, header):
cmds = ['gcc']
for inc_dir in self.config.include_dirs:
cmds.append('-I' + inc_dir)
cmds.append('-E')
cmds.append(header.path)
p = subprocess.Popen(cmds, stdout=subprocess.PIPE)
c_code, _ = p.communicate()

# we need to remove any gcc __attribute__ declarations
# from the code as this will cause PyCParser to fail.
c_code = re.sub('__attribute__\(\(.*?\)\)', '', c_code)

return c_code

def _parse(self, c_code):
parser = pycparser.CParser()
ast = parser.parse(c_code)
return ast

def _translate(self, c_ast):
module_ast = self._translator.translate(c_ast)
return module_ast

def _render_extern(self, module_ast, header):
return self._extern_renderer.render(module_ast, header)

def generate(self):
save_dir = self.config.save_dir
for header in self.config.headers:
c_code = self._preprocess(header)
c_ast = self._parse(c_code)
module_ast = self._translate(c_ast)

extern_code = self._render_extern(module_ast, header)
extern_name = '_' + header.mod_name + '.pxd'
extern_save_pth = os.path.join(save_dir, extern_name)

with open(extern_save_pth, 'w') as f:
f.write(extern_code)
def _parse(header, include_dirs):
""" Parse the given header file into and ast. The include
dirs are passed along to gccxml.
"""
# A temporary file to store the xml generated by gccxml
xml_file = tempfile.NamedTemporaryFile(suffix='.xml', delete=False)
xml_file.close()

# buildup the gccxml command
cmds = ['gccxml']
for inc_dir in include_dirs:
cmds.append('-I' + inc_dir)
cmds.append(header.path)
cmds.append('-fxml=%s' % xml_file.name)

# we pipe stdout so the preprocessing doesn't dump to the
# shell. We really don't care about it.
p = subprocess.Popen(cmds, stdout=subprocess.PIPE)
cpp, _ = p.communicate()

# Parse the xml into the ast then delete the temp file
ast = parser.parse(xml_file.name)
os.remove(xml_file.name)

return ast


def _render_extern(ast, header, config):
renderer = renderers.ExternRenderer()
return renderer.render(ast, header.path, config)


def generate(config):
save_dir = config.save_dir
include_dirs = config.include_dirs
for header in config.headers:
items = _parse(header, include_dirs)
extern_code = _render_extern(items, header, config)
extern_path = os.path.join(save_dir, header.pxd + '.pxd')
with open(extern_path, 'wb') as f:
f.write(extern_code)


30 changes: 24 additions & 6 deletions cwrap/config.py
Expand Up @@ -3,14 +3,21 @@

class Header(object):

def __init__(self, path, mod_name=None):
self.path = path
self.header_name = os.path.split(path)[-1]
def __init__(self, path, pxd=None, pyx=None):
self.path = os.path.abspath(path)
self.header_name = os.path.split(self.path)[-1]

mod_base = self.header_name.rstrip('.h')

if pxd is None:
self.pxd = '_' + mod_base
else:
self.pxd = extern

if mod_name is None:
self.mod_name = self.header_name.rstrip('.h')
if pyx is None:
self.pyx = mod_base
else:
self.mod_name = mod_name
self.pyx = pyx


class Config(object):
Expand All @@ -19,5 +26,16 @@ def __init__(self, include_dirs=None, save_dir=None, headers=None):
self.include_dirs = include_dirs or []
self.save_dir = save_dir or os.getcwd()
self.headers = headers or []

self._header_map = {}
for header in self.headers:
self._header_map[header.path] = header

def header(self, header_path):
return self._header_map[header_path]

def pxd_name(self, header_path):
return self._header_map[header_path].pxd

def pyx_name(self, header_path):
return self._header_map[header_path].pyx

0 comments on commit 3f92634

Please sign in to comment.