Advertisement
Guest User

c to cython translator - auto OO

a guest
Jan 13th, 2013
111
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 20.07 KB | None | 0 0
  1. #------------------------------------------------------------------------------
  2. # pycparser: cython_generator.py
  3. #
  4. # Cython code generator from pycparser AST nodes.
  5. #
  6. # Copyright (C) 2008-2012, Eli Bendersky and Brett Hartshorn
  7. # License: BSD
  8. #------------------------------------------------------------------------------
  9. from . import c_ast
  10.  
  11. CLASSIFY = True
  12. MARKER = '#######$$$$$'
  13.  
  14. class CythonGenerator(object):
  15.     """ Uses the same visitor pattern as c_ast.NodeVisitor, but modified to
  16.        return a value from each visit method, using string accumulation in
  17.        generic_visit.
  18.    """
  19.     def __init__(self):
  20.         self.output = ''
  21.        
  22.         # Statements start with indentation of self.indent_level spaces, using
  23.         # the _make_indent method
  24.         #
  25.         self.indent_level = 0
  26.         self.classes = {}  # dict of class (typedef) names of generated classes : []
  27.  
  28.     def visit_FileAST(self, n):
  29.         s = ''
  30.         for ext in n.ext:
  31.             if isinstance(ext, c_ast.FuncDef):
  32.                 s += self.visit(ext)
  33.             else:
  34.                 s += self.visit(ext) + ';\n'
  35.  
  36.         for class_name in self.classes:
  37.             funcs = self.classes[ class_name ]
  38.             a = []
  39.             for func in funcs:
  40.                 for i,line in enumerate(func.splitlines()):
  41.                     a.append( '  '+line )
  42.             s = s.replace(MARKER+class_name, '\n'.join(a))
  43.  
  44.         result = []  ## clean up
  45.         for line in s.splitlines():
  46.             if line.endswith(';'): line = line[:-1]
  47.             if line.strip(): result.append( line )
  48.  
  49.         return '\n'.join( result )
  50.  
  51.    
  52.     def _make_indent(self):
  53.         return ' ' * self.indent_level
  54.    
  55.     def visit(self, node, cdef=True):
  56.         method = 'visit_' + node.__class__.__name__
  57.         print(method)
  58.         if method in 'visit_Decl visit_ParamList'.split():
  59.             return getattr(self, method, self.generic_visit)(node, cdef=cdef)
  60.         else:
  61.             return getattr(self, method, self.generic_visit)(node)
  62.    
  63.     def generic_visit(self, node):
  64.         #~ print('generic:', type(node))
  65.         if node is None:
  66.             return ''
  67.         else:
  68.             return ''.join(self.visit(c) for c in node.children())
  69.    
  70.     def visit_Constant(self, n):
  71.         return n.value
  72.        
  73.     def visit_ID(self, n):
  74.         return n.name
  75.  
  76.     def visit_ArrayRef(self, n):
  77.         arrref = self._parenthesize_unless_simple(n.name)
  78.         return arrref + '[' + self.visit(n.subscript) + ']'
  79.  
  80.     def visit_StructRef(self, n):
  81.         sref = self._parenthesize_unless_simple(n.name)
  82.         if CLASSIFY: n.type = '.'  # TODO check if struct is in self.classes or 'self'
  83.         return sref + n.type + self.visit(n.field)
  84.  
  85.     def visit_FuncCall(self, n):
  86.         fref = self._parenthesize_unless_simple(n.name)
  87.         if fref == 'printf': fref = 'print'
  88.         return fref + '(' + self.visit(n.args) + ')'
  89.  
  90.     def visit_UnaryOp(self, n):
  91.         operand = self._parenthesize_unless_simple(n.expr)
  92.         if n.op == 'p++':
  93.             return '%s += 1' % operand
  94.         elif n.op == 'p--':
  95.             return '%s -= 1' % operand
  96.         elif n.op == 'sizeof':
  97.             # Always parenthesize the argument of sizeof since it can be
  98.             # a name.
  99.             return 'sizeof(%s)' % self.visit(n.expr)
  100.         else:
  101.             return '%s%s' % (n.op, operand)
  102.  
  103.     def visit_BinaryOp(self, n):
  104.         lval_str = self._parenthesize_if(n.left,
  105.                             lambda d: not self._is_simple_node(d))
  106.         rval_str = self._parenthesize_if(n.right,
  107.                             lambda d: not self._is_simple_node(d))
  108.         return '%s %s %s' % (lval_str, n.op, rval_str)
  109.  
  110.     def visit_Assignment(self, n):
  111.         rval_str = self._parenthesize_if(
  112.                             n.rvalue,
  113.                             lambda n: isinstance(n, c_ast.Assignment))
  114.         return '%s %s %s' % (self.visit(n.lvalue), n.op, rval_str)
  115.  
  116.     def visit_IdentifierType(self, n):
  117.         return ' '.join(n.names)
  118.  
  119.     def visit_Decl(self, n, no_type=False, cdef=True):
  120.         # no_type is used when a Decl is part of a DeclList, where the type is
  121.         # explicitly only for the first delaration in a list.
  122.         #
  123.         s = n.name if no_type else self._generate_decl(n)
  124.         print('visit_Decl', s)
  125.         n.show()
  126.         print( n.type, type(n.type) )
  127.  
  128.         if not isinstance(n.type, (c_ast.Enum, c_ast.Struct)):
  129.             s = 'cdef ' + s
  130.  
  131.         if n.bitsize: s += ' : ' + self.visit(n.bitsize)
  132.         if n.init:
  133.             if isinstance(n.init, c_ast.InitList):
  134.                 s += ' = {' + self.visit(n.init) + '}'
  135.             elif isinstance(n.init, c_ast.ExprList):
  136.                 s += ' = (' + self.visit(n.init) + ')'
  137.             else:
  138.                 s += ' = ' + self.visit(n.init)
  139.         return s
  140.  
  141.     def visit_DeclList(self, n):
  142.         s = self.visit(n.decls[0])
  143.         if len(n.decls) > 1:
  144.             s += ', ' + ', '.join(self.visit_Decl(decl, no_type=True)
  145.                                     for decl in n.decls[1:])
  146.         return s
  147.  
  148.     def visit_Typedef(self, n):
  149.         a = self._generate_type(n.type)
  150.         a = '\n'.join( a.splitlines()[:-1] ) # remove name at end.
  151.         a = a.replace(';', '')
  152.  
  153.         if n.storage:
  154.             s = ' '.join(n.storage) + ' '
  155.             s = s.replace('typedef ', 'ctypedef ')
  156.             simple = True
  157.             sname = None
  158.             tname = None
  159.             classify = False
  160.             if isinstance(n.type, c_ast.TypeDecl):
  161.                 if isinstance(n.type.type, c_ast.Struct):
  162.                     sname = n.type.type.name
  163.                     tname = n.type.declname
  164.                     if CLASSIFY:
  165.                         classify = True
  166.  
  167.                     if not n.type.type.name:
  168.                         simple = True
  169.                     elif n.type.declname == n.type.type.name:
  170.                         simple = True
  171.                     elif n.type.declname != n.type.type.name:
  172.                         simple = False
  173.  
  174.             if classify:
  175.                 return self.struct_to_class(a, sname, tname)
  176.  
  177.             elif simple:
  178.                 if a.startswith('cdef '):
  179.                     a = a[5:]
  180.                 return s + a
  181.             else:
  182.                 return a + '\nctypedef %s %s\n'%(n.type.type.name, n.type.declname)
  183.         else:
  184.             return a
  185.  
  186.     def struct_to_class(self, txt, struct_name, type_name):
  187.         head = txt.splitlines()[0]
  188.         body = txt.splitlines()[1:]
  189.         head = head.replace(' struct ', ' class ')
  190.         if not struct_name:
  191.             head = head.replace(':', '%s:'%type_name)
  192.         elif type_name != struct_name:
  193.             head = head.replace('%s:'%struct_name, '%s:'%type_name)
  194.  
  195.         lines = [ head ]
  196.         args = ['self'] # no need to type self
  197.         init = []
  198.         for line in body:
  199.             a = line.strip()
  200.             lines.append( '  cdef public %s'%a)
  201.             args.append( a )
  202.             n = a.split()[-1]
  203.             init.append( '    self.%s = %s'%(n,n) )
  204.         ## __init__ requires "def" not "cdef"
  205.         lines.append( '  def __init__(%s):'%(','.join(args)) )
  206.         lines.extend( init )
  207.  
  208.         lines.append(MARKER+type_name)
  209.         assert type_name not in self.classes
  210.         self.classes[ type_name ] = []  # list of methods
  211.  
  212.         return '\n'.join( lines )
  213.  
  214.     def visit_Cast(self, n):
  215.         s = '(' + self._generate_type(n.to_type) + ')'
  216.         return s + ' ' + self._parenthesize_unless_simple(n.expr)
  217.  
  218.     def visit_ExprList(self, n):
  219.         visited_subexprs = []
  220.         for expr in n.exprs:
  221.             if isinstance(expr, c_ast.ExprList):
  222.                 visited_subexprs.append('{' + self.visit(expr) + '}')
  223.             else:
  224.                 visited_subexprs.append(self.visit(expr))
  225.         return ', '.join(visited_subexprs)
  226.  
  227.     def visit_InitList(self, n):
  228.         visited_subexprs = []
  229.         for expr in n.exprs:
  230.             if isinstance(expr, c_ast.InitList):
  231.                 visited_subexprs.append('(' + self.visit(expr) + ')')
  232.             else:
  233.                 visited_subexprs.append(self.visit(expr))
  234.         return ', '.join(visited_subexprs)
  235.  
  236.  
  237.     def visit_Enum(self, n):
  238.         s = '# enum'
  239.         if n.name: s += ' ' + n.name
  240.         items = []
  241.         if n.values:
  242.             for i, enumerator in enumerate(n.values.enumerators):
  243.                 if enumerator.value:
  244.                     items.append( 'cdef int '+enumerator.name + ' = ' + self.visit(enumerator.value) )
  245.                 else:
  246.                     items.append( 'cdef int '+enumerator.name + ' = %s'%i)
  247.         return s + '\n' + '\n'.join( items )
  248.  
  249.  
  250.     def visit_FuncDef(self, n):
  251.         print('visit funcdef', n.decl)
  252.         classify = False
  253.         funcdecl = n.decl.type
  254.         if CLASSIFY and funcdecl.args:
  255.             arg = funcdecl.args.params[0]
  256.             if isinstance(arg, c_ast.Decl) and isinstance(arg.type, c_ast.PtrDecl):
  257.                 ptr = arg.type
  258.                 if isinstance( ptr.type, c_ast.TypeDecl) and isinstance(ptr.type.type, c_ast.IdentifierType):
  259.                     name = ptr.type.type.names[0]
  260.                     if name in self.classes:
  261.                         self.function_to_method( n, name, arg.name )
  262.                         classify = (name, arg.name)
  263.  
  264.  
  265.         decl = self.visit(n.decl) + ':'
  266.         decl = 'cpdef ' + decl.replace('cdef ', '')  ## to keep things simple just remove "cdef " from parameters
  267.         self.indent_level = 0
  268.         body = self.visit(n.body)
  269.         if n.param_decls:
  270.             knrdecls = ';\n'.join(self.visit(p) for p in n.param_decls)
  271.             result = decl + '\n' + knrdecls + ';\n' + body + '\n'
  272.         else:
  273.             result = decl + '\n' + body + '\n'
  274.  
  275.         if classify:
  276.             cname, vname = classify
  277.             result = result.replace('*'+vname, '')
  278.             self.classes[ cname ].append( result )
  279.             return ''
  280.         else:
  281.             return result
  282.  
  283.     def function_to_method(self, n, class_name, var_name):
  284.         print( 'n', n, class_name, var_name)
  285.         for a in n.children():
  286.             tag, child = a
  287.             print('child', child)
  288.             if isinstance(child, c_ast.IdentifierType):
  289.                 if len(child.names)==1 and child.names[0] == class_name:
  290.                     child.names[0] = 'self'
  291.             elif isinstance(child, c_ast.ID) and child.name == var_name:
  292.                 child.name = 'self'
  293.             else:
  294.                 self.function_to_method( child, class_name, var_name )
  295.  
  296.  
  297.     def visit_Compound(self, n):
  298.         #s = self._make_indent() + '{\n'
  299.         s = self._make_indent() + '\n'
  300.         self.indent_level += 2
  301.         if n.block_items:
  302.             s += ''.join(self._generate_stmt(stmt) for stmt in n.block_items)
  303.         self.indent_level -= 2
  304.         #s += self._make_indent() + '}\n'
  305.         s += self._make_indent() + '\n'
  306.         return s
  307.  
  308.     def visit_EmptyStatement(self, n):
  309.         return ';'
  310.  
  311.     def visit_ParamList(self, n, cdef=False):
  312.         return ', '.join(self.visit(param, cdef=cdef) for param in n.params)
  313.  
  314.     def visit_Return(self, n):
  315.         s = 'return'
  316.         if n.expr: s += ' ' + self.visit(n.expr)
  317.         return s + ';'
  318.  
  319.     def visit_Break(self, n):
  320.         return 'break;'
  321.  
  322.     def visit_Continue(self, n):
  323.         return 'continue;'
  324.  
  325.     def visit_TernaryOp(self, n):
  326.         s = self.visit(n.cond) + ' ? '
  327.         s += self.visit(n.iftrue) + ' : '
  328.         s += self.visit(n.iffalse)
  329.         return s
  330.  
  331.     def visit_If(self, n):
  332.         s = 'if ('
  333.         if n.cond: s += self.visit(n.cond)
  334.         s += '):\n'
  335.         s += self._generate_stmt(n.iftrue, add_indent=True)
  336.         if n.iffalse:
  337.             s += self._make_indent() + 'else:\n'
  338.             s += self._generate_stmt(n.iffalse, add_indent=True)
  339.         return s
  340.  
  341.  
  342.     def is_simple_for_loop(self, n):
  343.         if isinstance(n.init, c_ast.Assignment) and n.init.op=='=' and isinstance(n.init.lvalue, c_ast.ID):
  344.             if isinstance( n.init.rvalue, c_ast.Constant) and isinstance( n.cond, c_ast.BinaryOp):
  345.                 if isinstance( n.next, c_ast.UnaryOp ) and n.next.op == 'p++':
  346.                     return True
  347.         return False
  348.  
  349.     def visit_For(self, n):
  350.         if self.is_simple_for_loop( n ):
  351.             var = n.init.lvalue.name
  352.             start = n.init.rvalue.value
  353.             s = 'for %s from %s <= %s'%(var,start,var)
  354.             s += ' %s %s:\n'%(n.cond.op, n.cond.right.name)
  355.         else:  ## not cython yet TODO
  356.             s = 'for ('
  357.             if n.init: s += self.visit(n.init)
  358.             s += ';'
  359.             if n.cond: s += ' ' + self.visit(n.cond)
  360.             s += ';'
  361.             if n.next: s += ' ' + self.visit(n.next)
  362.             s += '):\n'
  363.         s += self._generate_stmt(n.stmt, add_indent=True)
  364.         return s
  365.  
  366.     def visit_While(self, n):
  367.         s = 'while ('
  368.         if n.cond: s += self.visit(n.cond)
  369.         s += '):\n'
  370.         s += self._generate_stmt(n.stmt, add_indent=True)
  371.         return s
  372.  
  373.     def visit_DoWhile(self, n):
  374.         s = 'do\n'
  375.         s += self._generate_stmt(n.stmt, add_indent=True)
  376.         s += self._make_indent() + 'while ('
  377.         if n.cond: s += self.visit(n.cond)
  378.         s += ');'
  379.         return s
  380.  
  381.     def visit_Switch(self, n):
  382.         s = 'switch (' + self.visit(n.cond) + ')\n'
  383.         s += self._generate_stmt(n.stmt, add_indent=True)
  384.         return s
  385.  
  386.     def visit_Case(self, n):
  387.         s = 'case ' + self.visit(n.expr) + ':\n'
  388.         for stmt in n.stmts:
  389.             s += self._generate_stmt(stmt, add_indent=True)
  390.         return s
  391.  
  392.     def visit_Default(self, n):
  393.         s = 'default:\n'
  394.         for stmt in n.stmts:
  395.             s += self._generate_stmt(stmt, add_indent=True)
  396.         return s
  397.  
  398.     def visit_Label(self, n):
  399.         return n.name + ':\n' + self._generate_stmt(n.stmt)
  400.  
  401.     def visit_Goto(self, n):
  402.         return 'goto ' + n.name + ';'
  403.  
  404.     def visit_EllipsisParam(self, n):
  405.         return '...'
  406.  
  407.     def visit_Struct(self, n):
  408.         return self._generate_struct_union(n, 'struct')
  409.  
  410.     def visit_Typename(self, n):
  411.         return self._generate_type(n.type)
  412.        
  413.     def visit_Union(self, n):
  414.         return self._generate_struct_union(n, 'union')
  415.  
  416.     def visit_NamedInitializer(self, n):
  417.         s = ''
  418.         for name in n.name:
  419.             if isinstance(name, c_ast.ID):
  420.                 s += '.' + name.name
  421.             elif isinstance(name, c_ast.Constant):
  422.                 s += '[' + name.value + ']'
  423.         s += ' = ' + self.visit(n.expr)
  424.         return s
  425.  
  426.     def _generate_struct_union(self, n, name):
  427.         """ Generates code for structs and unions. name should be either
  428.            'struct' or union.
  429.        """
  430.         a = 'cdef ' + name + ' ' + (n.name or '') + ':'
  431.         if n.decls:
  432.             s = '\n'
  433.             s += self._make_indent()
  434.             self.indent_level += 2
  435.             for decl in n.decls:
  436.                 s += self._generate_stmt(decl)
  437.             self.indent_level -= 2
  438.             s += self._make_indent()
  439.         return a + s.replace('cdef ', '')
  440.  
  441.     def _generate_struct_union_as_class(self, n, name):
  442.         """ Generates code for structs and unions. name should be either
  443.            'struct' or union.
  444.        """
  445.         assert n.name ## TODO unnamed structs
  446.         s = 'cdef class %s:'%n.name
  447.         if n.decls:
  448.             s += '\n'
  449.             s += self._make_indent()
  450.             self.indent_level += 2
  451.             for decl in n.decls:
  452.                 s += self._generate_stmt(decl)
  453.             self.indent_level -= 2
  454.             s += self._make_indent()
  455.  
  456.         return s
  457.  
  458.  
  459.     def _generate_stmt(self, n, add_indent=False):
  460.         """ Generation from a statement node. This method exists as a wrapper
  461.            for individual visit_* methods to handle different treatment of
  462.            some statements in this context.
  463.        """
  464.         typ = type(n)
  465.         if add_indent: self.indent_level += 2
  466.         indent = self._make_indent()
  467.         if add_indent: self.indent_level -= 2
  468.        
  469.         if typ in (
  470.                 c_ast.Decl, c_ast.Assignment, c_ast.Cast, c_ast.UnaryOp,
  471.                 c_ast.BinaryOp, c_ast.TernaryOp, c_ast.FuncCall, c_ast.ArrayRef,
  472.                 c_ast.StructRef, c_ast.Constant, c_ast.ID, c_ast.Typedef):
  473.             # These can also appear in an expression context so no semicolon
  474.             # is added to them automatically
  475.             #
  476.             return indent + self.visit(n) + ';\n'
  477.         elif typ in (c_ast.Compound,):
  478.             # No extra indentation required before the opening brace of a
  479.             # compound - because it consists of multiple lines it has to
  480.             # compute its own indentation.
  481.             #
  482.             return self.visit(n)
  483.         else:
  484.             return indent + self.visit(n) + '\n'
  485.  
  486.     def _generate_decl(self, n):
  487.         """ Generation from a Decl node.
  488.        """
  489.         s = ''
  490.         if n.funcspec: s = ' '.join(n.funcspec) + ' '
  491.         if n.storage: s += ' '.join(n.storage) + ' '
  492.         s += self._generate_type(n.type)
  493.         return s
  494.    
  495.     def _generate_type(self, n, modifiers=[]):
  496.         """ Recursive generation from a type node. n is the type node.
  497.            modifiers collects the PtrDecl, ArrayDecl and FuncDecl modifiers
  498.            encountered on the way down to a TypeDecl, to allow proper
  499.            generation from it.
  500.        """
  501.         typ = type(n)
  502.         #~ print(n, modifiers)
  503.        
  504.         if typ == c_ast.TypeDecl:
  505.             s = ''
  506.             if n.quals: s += ' '.join(n.quals) + ' '
  507.             s += self.visit(n.type)
  508.            
  509.             nstr = n.declname if n.declname else ''
  510.             # Resolve modifiers.
  511.             # Wrap in parens to distinguish pointer to array and pointer to
  512.             # function syntax.
  513.             #
  514.             for i, modifier in enumerate(modifiers):
  515.                 if isinstance(modifier, c_ast.ArrayDecl):
  516.                     if (i != 0 and isinstance(modifiers[i - 1], c_ast.PtrDecl)):
  517.                         nstr = '(' + nstr + ')'
  518.                     nstr += '[' + self.visit(modifier.dim) + ']'
  519.                 elif isinstance(modifier, c_ast.FuncDecl):
  520.                     if (i != 0 and isinstance(modifiers[i - 1], c_ast.PtrDecl)):
  521.                         nstr = '(' + nstr + ')'
  522.                     nstr += '(' + self.visit(modifier.args) + ')'
  523.                 elif isinstance(modifier, c_ast.PtrDecl):
  524.                     if modifier.quals:
  525.                         nstr = '* %s %s' % (' '.join(modifier.quals), nstr)
  526.                     else:
  527.                         nstr = '*' + nstr
  528.             if nstr: s += ' ' + nstr
  529.             return s
  530.         elif typ == c_ast.Decl:
  531.             return self._generate_decl(n.type)
  532.         elif typ == c_ast.Typename:
  533.             return self._generate_type(n.type)
  534.         elif typ == c_ast.IdentifierType:
  535.             return ' '.join(n.names) + ' '
  536.         elif typ in (c_ast.ArrayDecl, c_ast.PtrDecl, c_ast.FuncDecl):
  537.             return self._generate_type(n.type, modifiers + [n])
  538.         else:
  539.             return self.visit(n)
  540.  
  541.     def _parenthesize_if(self, n, condition):
  542.         """ Visits 'n' and returns its string representation, parenthesized
  543.            if the condition function applied to the node returns True.
  544.        """
  545.         s = self.visit(n)
  546.         if condition(n):
  547.             return '(' + s + ')'
  548.         else:
  549.             return s
  550.  
  551.     def _parenthesize_unless_simple(self, n):
  552.         """ Common use case for _parenthesize_if
  553.        """
  554.         return self._parenthesize_if(n, lambda d: not self._is_simple_node(d))
  555.  
  556.     def _is_simple_node(self, n):
  557.         """ Returns True for nodes that are "simple" - i.e. nodes that always
  558.            have higher precedence than operators.
  559.        """
  560.         return isinstance(n,(   c_ast.Constant, c_ast.ID, c_ast.ArrayRef,
  561.                                 c_ast.StructRef, c_ast.FuncCall))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement