source: sasmodels/sasmodels/py2c.py @ 4c72117

Last change on this file since 4c72117 was 4c72117, checked in by Omer Eisenberg <omereis@…>, 6 years ago

Comparison running: fixed bug in 'visit_Return', deleted commented statements

  • Property mode set to 100644
File size: 37.6 KB
Line 
1
2"""
3    codegen
4    ~~~~~~~
5
6    Extension to ast that allow ast -> python code generation.
7
8    :copyright: Copyright 2008 by Armin Ronacher.
9    :license: BSD.
10"""
11"""
12    Variables definition in C
13    -------------------------
14    Defining variables within the Translate function is a bit of a guess work,
15    using following rules.
16    *   By default, a variable is a 'double'.
17    *   Variable in a for loop is an int.
18    *   Variable that is references with brackets is an array of doubles. The
19        variable within the brackets is integer. For example, in the
20        reference 'var1[var2]', var1 is a double array, and var2 is an integer.
21    *   Assignment to an argument makes that argument an array, and the index in
22        that assignment is 0.
23        For example, the following python code
24            def func(arg1, arg2):
25                arg2 = 17.
26        is translated to the following C code
27            double func(double arg1)
28            {
29                arg2[0] = 17.0;
30            }
31        For example, the following python code is translated to the following C code
32            def func(arg1, arg2):          double func(double arg1) {
33                arg2 = 17.                      arg2[0] = 17.0;
34                                            }
35    *   All functions are defined as double, even if there is no return statement.
36
37
38Update Notes
39============
4011/22 14:15, O.E.   Each 'visit_*' method is to build a C statement string. It
41                    shold insert 4 blanks per indentation level.
42                    The 'body' method will combine all the strings, by adding
43                    the 'current_statement' to the c_proc string list
44   11/2017, OE: variables, argument definition implemented.
45   Note: An argument is considered an array if it is the target of an
46        assignment. In that case it is translated to <var>[0]
4711/27/2017, OE: 'pow' basicly working
48  /12/2017, OE: Multiple assignment: a1,a2,...,an=b1,b2,...bn implemented
49  /12/2017, OE: Power function, including special cases of
50                square(x)(pow(x,2)) and cube(x)(pow(x,3)), implemented in
51                translate_power, called from visit_BinOp
5212/07/2017, OE: Translation of integer division, '\\' in python, implemented
53                in translate_integer_divide, called from visit_BinOp
5412/07/2017, OE: C variable definition handled in 'define_C_Vars'
55              : Python integer division, '//', translated to C in
56                'translate_integer_divide'
5712/15/2017, OE: Precedence maintained by writing opening and closing
58                parenthesesm '(',')', in procedure 'visit_BinOp'.
5912/18/2017, OE: Added call to 'add_current_line()' at the beginning
60                of visit_Return
61
62"""
63import ast
64import sys
65from ast import NodeVisitor
66
67BINOP_SYMBOLS = {}
68BINOP_SYMBOLS[ast.Add] = '+'
69BINOP_SYMBOLS[ast.Sub] = '-'
70BINOP_SYMBOLS[ast.Mult] = '*'
71BINOP_SYMBOLS[ast.Div] = '/'
72BINOP_SYMBOLS[ast.Mod] = '%'
73BINOP_SYMBOLS[ast.Pow] = '**'
74BINOP_SYMBOLS[ast.LShift] = '<<'
75BINOP_SYMBOLS[ast.RShift] = '>>'
76BINOP_SYMBOLS[ast.BitOr] = '|'
77BINOP_SYMBOLS[ast.BitXor] = '^'
78BINOP_SYMBOLS[ast.BitAnd] = '&'
79BINOP_SYMBOLS[ast.FloorDiv] = '//'
80
81BOOLOP_SYMBOLS = {}
82BOOLOP_SYMBOLS[ast.And] = '&&'
83BOOLOP_SYMBOLS[ast.Or]  = '||'
84
85CMPOP_SYMBOLS = {}
86CMPOP_SYMBOLS[ast.Eq]    = '=='
87CMPOP_SYMBOLS[ast.NotEq] = '!='
88CMPOP_SYMBOLS[ast.Lt] = '<'
89CMPOP_SYMBOLS[ast.LtE] = '<='
90CMPOP_SYMBOLS[ast.Gt] = '>'
91CMPOP_SYMBOLS[ast.GtE] = '>='
92CMPOP_SYMBOLS[ast.Is] = 'is'
93CMPOP_SYMBOLS[ast.IsNot] = 'is not'
94CMPOP_SYMBOLS[ast.In] = 'in'
95CMPOP_SYMBOLS[ast.NotIn] = 'not in'
96
97UNARYOP_SYMBOLS = {}
98UNARYOP_SYMBOLS[ast.Invert] = '~'
99UNARYOP_SYMBOLS[ast.Not] = 'not'
100UNARYOP_SYMBOLS[ast.UAdd] = '+'
101UNARYOP_SYMBOLS[ast.USub] = '-'
102
103
104def to_source(node, func_name, constants=None):
105    """This function can convert a node tree back into python sourcecode.
106    This is useful for debugging purposes, especially if you're dealing with
107    custom asts not generated by python itself.
108
109    It could be that the sourcecode is evaluable when the AST itself is not
110    compilable / evaluable.  The reason for this is that the AST contains some
111    more data than regular sourcecode does, which is dropped during
112    conversion.
113
114    Each level of indentation is replaced with `indent_with`.  Per default this
115    parameter is equal to four spaces as suggested by PEP 8, but it might be
116    adjusted to match the application's styleguide.
117
118    If `add_line_information` is set to `True` comments for the line numbers
119    of the nodes are added to the output.  This can be used to spot wrong line
120    number information of statement nodes.
121    """
122    generator = SourceGenerator(' ' * 4, False, constants)
123    generator.visit(node)
124
125    return ''.join(generator.c_proc)
126
127def isevaluable(s):
128    try:
129        eval(s)
130        return True
131    except:
132        return False
133
134class SourceGenerator(NodeVisitor):
135    """This visitor is able to transform a well formed syntax tree into python
136    sourcecode.  For more details have a look at the docstring of the
137    `node_to_source` function.
138    """
139
140    def __init__(self, indent_with, add_line_information=False, constants=None):
141        self.result = []
142        self.indent_with = indent_with
143        self.add_line_information = add_line_information
144        self.indentation = 0
145        self.new_lines = 0
146        self.c_proc = []
147# for C
148        self.signature_line = 0
149        self.arguments = []
150        self.name = ""
151        self.warnings = []
152        self.Statements = []
153        self.current_statement = ""
154        self.strMethodSignature = ""
155        self.C_Vars = []
156        self.C_IntVars = []
157        self.MathIncludeed = False
158        self.C_Pointers = []
159        self.C_DclPointers = []
160        self.C_Functions = []
161        self.C_Vectors = []
162        self.C_Constants = constants
163        self.SubRef = False
164        self.InSubscript = False
165        self.Tuples = []
166        self.required_functions = []
167        self.is_sequence = False
168        self.visited_args = False
169
170    def write_python(self, x):
171        if self.new_lines:
172            if self.result:
173                self.result.append('\n' * self.new_lines)
174            self.result.append(self.indent_with * self.indentation)
175            self.new_lines = 0
176        self.result.append(x)
177
178    def write_c(self, x):
179        self.current_statement += x
180
181    def add_c_line(self, x):
182        string = ''
183        for i in range(self.indentation):
184            string += ("    ")
185        string += str(x)
186        self.c_proc.append(str(string + "\n"))
187        x = ''
188
189    def add_current_line(self):
190        if(len(self.current_statement) > 0):
191            self.add_c_line(self.current_statement)
192            self.current_statement = ''
193
194    def AddUniqueVar(self, new_var):
195        if((new_var not in self.C_Vars)):
196            self.C_Vars.append(str(new_var))
197
198    def WriteSincos(self, node):
199        angle = str(node.args[0].id)
200        self.write_c(node.args[1].id + " = sin(" + angle + ");")
201        self.add_current_line()
202        self.write_c(node.args[2].id + " = cos(" + angle + ");")
203        self.add_current_line()
204        for arg in node.args:
205            self.AddUniqueVar(arg.id)
206
207    def newline(self, node=None, extra=0):
208        self.new_lines = max(self.new_lines, 1 + extra)
209        if node is not None and self.add_line_information:
210            self.write_c('# line: %s' % node.lineno)
211            self.new_lines = 1
212        if(len(self.current_statement)):
213            self.Statements.append(self.current_statement)
214            self.current_statement = ''
215
216    def body(self, statements):
217        if(len(self.current_statement)):
218            self.add_current_line()
219        self.new_line = True
220        self.indentation += 1
221        for stmt in statements:
222            target_name = ''
223            if(hasattr(stmt, 'targets')):
224                if(hasattr(stmt.targets[0], 'id')):
225                    target_name = stmt.targets[0].id # target name needed for debug only
226            self.visit(stmt)
227        self.add_current_line() # just for breaking point. to be deleted.
228        self.indentation -= 1
229
230    def body_or_else(self, node):
231        self.body(node.body)
232        if node.orelse:
233            self.newline()
234            self.write_c('else:')
235            self.body(node.orelse)
236
237    def signature(self, node):
238        want_comma = []
239        def write_comma():
240            if want_comma:
241                self.write_c(', ')
242            else:
243                want_comma.append(True)
244# for C
245        for arg in node.args:
246            self.arguments.append(arg.arg)
247
248        padding = [None] *(len(node.args) - len(node.defaults))
249        for arg, default in zip(node.args, padding + node.defaults):
250            if default is not None:
251                self.warnings.append("Default Parameter unknown to C")
252                w_str = "Default Parameters are unknown to C: '" + arg.arg + \
253                        " = " + str(default.n) + "'"
254                self.warnings.append(w_str)
255
256    def decorators(self, node):
257        for decorator in node.decorator_list:
258            self.newline(decorator)
259            self.write_python('@')
260            self.visit(decorator)
261
262    # Statements
263
264    def visit_Assert(self, node):
265        self.newline(node)
266        self.write_c('assert ')
267        self.visit(node.test)
268        if node.msg is not None:
269            self.write_python(', ')
270            self.visit(node.msg)
271
272    def define_C_Vars(self, target):
273        if(hasattr(target, 'id')):
274# a variable is considered an array if it apears in the agrument list
275# and being assigned to. For example, the variable p in the following
276# sniplet is a pointer, while q is not
277# def somefunc(p, q):
278#  p = q + 1
279#  return
280#
281            if(target.id not in self.C_Vars):
282                if(target.id in self.arguments):
283                    idx = self.arguments.index(target.id)
284                    new_target = self.arguments[idx] + "[0]"
285                    if(new_target not in self.C_Pointers):
286                        target.id = new_target
287                        self.C_Pointers.append(self.arguments[idx])
288                else:
289                    self.C_Vars.append(target.id)
290
291    def add_semi_colon(self):
292        semi_pos = self.current_statement.find(';')
293        if(semi_pos > 0.0):
294            self.current_statement = self.current_statement.replace(';','')
295        self.write_c(';')
296
297    def visit_Assign(self, node):
298        self.add_current_line()
299        for idx, target in enumerate(node.targets): # multi assign, as in 'a = b = c = 7'
300            if idx:
301                self.write_c(' = ')
302            self.define_C_Vars(target)
303            self.visit(target)
304        if(len(self.Tuples) > 0):
305            tplTargets = list(self.Tuples)
306            self.Tuples.clear()
307        self.write_c(' = ')
308        self.is_sequence = False
309        self.visited_args = False
310        self.visit(node.value)
311        self.add_semi_colon()
312        self.add_current_line()
313        for n, item in enumerate(self.Tuples):
314            self.visit(tplTargets[n])
315            self.write_c(' = ')
316            self.visit(item)
317            self.add_semi_colon()
318            self.add_current_line()
319        if((self.is_sequence) and (not self.visited_args)):
320            for target in node.targets:
321                if(hasattr(target, 'id')):
322                    if((target.id in self.C_Vars) and(target.id not in self.C_DclPointers)):
323                        if(target.id not in self.C_DclPointers):
324                            self.C_DclPointers.append(target.id)
325                            if(target.id in self.C_Vars):
326                                self.C_Vars.remove(target.id)
327        self.current_statement = ''
328
329    def visit_AugAssign(self, node):
330        if(node.target.id not in self.C_Vars):
331            if(node.target.id not in self.arguments):
332                self.C_Vars.append(node.target.id)
333        self.visit(node.target)
334        self.write_c(' ' + BINOP_SYMBOLS[type(node.op)] + '= ')
335        self.visit(node.value)
336        self.add_semi_colon()
337        self.add_current_line()
338
339    def visit_ImportFrom(self, node):
340        self.newline(node)
341        self.write_python('from %s%s import ' %('.' * node.level, node.module))
342        for idx, item in enumerate(node.names):
343            if idx:
344                self.write_python(', ')
345            self.write_python(item)
346
347    def visit_Import(self, node):
348        self.newline(node)
349        for item in node.names:
350            self.write_python('import ')
351            self.visit(item)
352
353    def visit_Expr(self, node):
354        self.newline(node)
355        self.generic_visit(node)
356
357    def listToDeclare(self, Vars):
358        s = ''
359        if(len(Vars) > 0):
360            s = ",".join(Vars)
361        return(s)
362
363    def write_C_Pointers(self, start_var):
364        if(len(self.C_DclPointers) > 0):
365            vars = ""
366            for c_ptr in self.C_DclPointers:
367                if(len(vars) > 0):
368                    vars += ", "
369                if(c_ptr not in self.arguments):
370                    vars += "*" + c_ptr
371                if(c_ptr in self.C_Vars):
372                    if(c_ptr in self.C_Vars):
373                        self.C_Vars.remove(c_ptr)
374            if(len(vars) > 0):
375                c_dcl = "    double " + vars + ";"
376                self.c_proc.insert(start_var, c_dcl + "\n")
377                start_var += 1
378        return start_var
379
380    def insert_C_Vars(self, start_var):
381        fLine = False
382        start_var = self.write_C_Pointers(start_var)
383        if(len(self.C_IntVars) > 0):
384            for var in self.C_IntVars:
385                if(var in self.C_Vars):
386                    self.C_Vars.remove(var)
387            s = self.listToDeclare(self.C_IntVars)
388            self.c_proc.insert(start_var, "    int " + s + ";\n")
389            fLine = True
390            start_var += 1
391
392        if(len(self.C_Vars) > 0):
393            s = self.listToDeclare(self.C_Vars)
394            self.c_proc.insert(start_var, "    double " + s + ";\n")
395            fLine = True
396            start_var += 1
397        if(len(self.C_Vectors) > 0):
398            s = self.listToDeclare(self.C_Vectors)
399            for n in range(len(self.C_Vectors)):
400                name = "vec" + str(n+1)
401                c_dcl = "    double " + name + "[] = {" + self.C_Vectors[n] + "};"
402                self.c_proc.insert(start_var, c_dcl + "\n")
403                start_var += 1
404        self.C_Vars.clear()
405        self.C_IntVars.clear()
406        self.C_Vectors.clear()
407        self.C_Pointers.clear()
408        self.C_DclPointers
409        if(fLine == True):
410            self.c_proc.insert(start_var, "\n")
411        return
412        s = ''
413        for n in range(len(self.C_Vars)):
414            s += str(self.C_Vars[n])
415            if n < len(self.C_Vars) - 1:
416                s += ", "
417        if(len(s) > 0):
418            self.c_proc.insert(start_var, "    double " + s + ";\n")
419            self.c_proc.insert(start_var + 1, "\n")
420
421    def writeInclude(self):
422        if(self.MathIncludeed == False):
423            self.add_c_line("#include <math.h>\n")
424            self.add_c_line("static double pi = 3.14159265359;\n")
425            self.MathIncludeed = True
426
427    def ListToString(self, strings):
428        s = ''
429        for n in range(len(strings)):
430            s += strings[n]
431            if(n < (len(strings) - 1)):
432                s += ", "
433        return(s)
434
435    def getMethodSignature(self):
436        args_str = ''
437        for n in range(len(self.arguments)):
438            args_str += "double " + self.arguments[n]
439            if(n < (len(self.arguments) - 1)):
440                args_str += ", "
441        return(args_str)
442
443    def InsertSignature(self):
444        args_str = ''
445        for n in range(len(self.arguments)):
446            args_str += "double " + self.arguments[n]
447            if(self.arguments[n] in self.C_Pointers):
448                args_str += "[]"
449            if(n < (len(self.arguments) - 1)):
450                args_str += ", "
451        self.strMethodSignature = 'double ' + self.name + '(' + args_str + ")"
452        if(self.signature_line >= 0):
453            self.c_proc.insert(self.signature_line, self.strMethodSignature)
454
455    def visit_FunctionDef(self, node):
456        self.newline(extra=1)
457        self.decorators(node)
458        self.newline(node)
459        self.arguments = []
460        self.name = node.name
461        print("Parsing '" + self.name + "'")
462        args_str = ""
463
464        self.visit(node.args)
465        self.getMethodSignature()
466        self.signature_line = len(self.c_proc)
467        self.add_c_line("\n{")
468        start_vars = len(self.c_proc) + 1
469        self.body(node.body)
470        self.add_c_line("}\n")
471        self.InsertSignature()
472        self.insert_C_Vars(start_vars)
473        self.C_Pointers = []
474
475    def visit_ClassDef(self, node):
476        have_args = []
477        def paren_or_comma():
478            if have_args:
479                self.write_python(', ')
480            else:
481                have_args.append(True)
482                self.write_python('(')
483
484        self.newline(extra=2)
485        self.decorators(node)
486        self.newline(node)
487        self.write_python('class %s' % node.name)
488        for base in node.bases:
489            paren_or_comma()
490            self.visit(base)
491        # XXX: the if here is used to keep this module compatible
492        #      with python 2.6.
493        if hasattr(node, 'keywords'):
494            for keyword in node.keywords:
495                paren_or_comma()
496                self.write_python(keyword.arg + '=')
497                self.visit(keyword.value)
498            if node.starargs is not None:
499                paren_or_comma()
500                self.write_python('*')
501                self.visit(node.starargs)
502            if node.kwargs is not None:
503                paren_or_comma()
504                self.write_python('**')
505                self.visit(node.kwargs)
506        self.write_python(have_args and '):' or ':')
507        self.body(node.body)
508
509    def visit_If(self, node):
510        self.write_c('if ')
511        self.visit(node.test)
512        self.write_c(' {')
513        self.body(node.body)
514        self.add_c_line('}')
515        while True:
516            else_ = node.orelse
517            if len(else_) == 0:
518                break
519#            elif hasattr(else_, 'orelse'):
520            elif len(else_) == 1 and isinstance(else_[0], ast.If):
521                node = else_[0]
522#                self.newline()
523                self.write_c('else if ')
524                self.visit(node.test)
525                self.write_c(' {')
526                self.body(node.body)
527                self.add_current_line()
528                self.add_c_line('}')
529#                break
530            else:
531                self.newline()
532                self.write_c('else {')
533                self.body(node.body)
534                self.add_c_line('}')
535                break
536
537    def getNodeLineNo(self, node):
538        line_number = -1
539        if(hasattr(node,'value')):
540            line_number = node.value.lineno
541        elif hasattr(node,'iter'):
542            if hasattr(node.iter,'lineno'):
543                line_number = node.iter.lineno
544        return(line_number)
545
546    def GetNodeAsString(self, node):
547        res = ''
548        if(hasattr(node, 'n')):
549            res = str(node.n)
550        elif(hasattr(node, 'id')):
551            res = node.id
552        return(res)
553
554    def GetForRange(self, node):
555        stop = ""
556        start = '0'
557        step = '1'
558        for_args = []
559        temp_statement = self.current_statement
560        self.current_statement = ''
561        for arg in node.iter.args:
562            self.visit(arg)
563            for_args.append(self.current_statement)
564            self.current_statement = ''
565        self.current_statement = temp_statement
566        if(len(for_args) == 1):
567            stop = for_args[0]
568        elif(len(for_args) == 2):
569            start = for_args[0]
570            stop = for_args[1]
571        elif(len(for_args) == 3):
572            start = for_args[0]
573            stop = for_args[1]
574            start = for_args[2]
575        else:
576            raise("Ilegal for loop parameters")
577        return(start, stop, step)
578
579    def visit_For(self, node):
580# node: for iterator is stored in node.target.
581# Iterator name is in node.target.id.
582        self.add_current_line()
583        fForDone = False
584        self.current_statement = ''
585        if(hasattr(node.iter, 'func')):
586            if(hasattr(node.iter.func, 'id')):
587                if(node.iter.func.id == 'range'):
588                    self.visit(node.target)
589                    iterator = self.current_statement
590                    self.current_statement = ''
591                    if(iterator not in self.C_IntVars):
592                        self.C_IntVars.append(iterator)
593                    start, stop, step = self.GetForRange(node)
594                    self.write_c("for(" + iterator + "=" + str(start) + \
595                                  " ; " + iterator + " < " + str(stop) + \
596                                  " ; " + iterator + " += " + str(step) + ") {")
597                    self.body_or_else(node)
598                    self.write_c("}")
599                    fForDone = True
600        if(fForDone == False):
601            line_number = self.getNodeLineNo(node)
602            self.current_statement = ''
603            self.write_c('for ')
604            self.visit(node.target)
605            self.write_c(' in ')
606            self.visit(node.iter)
607            self.write_c(':')
608            errStr = "Conversion Error in function " + self.name + ", Line #" + str(line_number)
609            errStr += "\nPython for expression not supported: '" + self.current_statement + "'"
610            raise Exception(errStr)
611
612    def visit_While(self, node):
613        self.newline(node)
614        self.write_c('while ')
615        self.visit(node.test)
616        self.write_c(':')
617        self.body_or_else(node)
618
619    def visit_With(self, node):
620        self.newline(node)
621        self.write_python('with ')
622        self.visit(node.context_expr)
623        if node.optional_vars is not None:
624            self.write_python(' as ')
625            self.visit(node.optional_vars)
626        self.write_python(':')
627        self.body(node.body)
628
629    def visit_Pass(self, node):
630        self.newline(node)
631        self.write_python('pass')
632
633    def visit_Print(self, node):
634# XXX: python 2.6 only
635        self.newline(node)
636        self.write_c('print ')
637        want_comma = False
638        if node.dest is not None:
639            self.write_c(' >> ')
640            self.visit(node.dest)
641            want_comma = True
642        for value in node.values:
643            if want_comma:
644                self.write_c(', ')
645            self.visit(value)
646            want_comma = True
647        if not node.nl:
648            self.write_c(',')
649
650    def visit_Delete(self, node):
651        self.newline(node)
652        self.write_python('del ')
653        for idx, target in enumerate(node):
654            if idx:
655                self.write_python(', ')
656            self.visit(target)
657
658    def visit_TryExcept(self, node):
659        self.newline(node)
660        self.write_python('try:')
661        self.body(node.body)
662        for handler in node.handlers:
663            self.visit(handler)
664
665    def visit_TryFinally(self, node):
666        self.newline(node)
667        self.write_python('try:')
668        self.body(node.body)
669        self.newline(node)
670        self.write_python('finally:')
671        self.body(node.finalbody)
672
673    def visit_Global(self, node):
674        self.newline(node)
675        self.write_python('global ' + ', '.join(node.names))
676
677    def visit_Nonlocal(self, node):
678        self.newline(node)
679        self.write_python('nonlocal ' + ', '.join(node.names))
680
681    def visit_Return(self, node):
682        self.add_current_line()
683        if node.value is None:
684            self.write_c('return')
685        else:
686            self.write_c('return(')
687            self.visit(node.value)
688        self.write_c(')')
689        self.add_semi_colon()
690        self.add_c_line(self.current_statement)
691        self.current_statement = ''
692
693    def visit_Break(self, node):
694        self.newline(node)
695        self.write_c('break')
696
697    def visit_Continue(self, node):
698        self.newline(node)
699        self.write_c('continue')
700
701    def visit_Raise(self, node):
702        # XXX: Python 2.6 / 3.0 compatibility
703        self.newline(node)
704        self.write_python('raise')
705        if hasattr(node, 'exc') and node.exc is not None:
706            self.write_python(' ')
707            self.visit(node.exc)
708            if node.cause is not None:
709                self.write_python(' from ')
710                self.visit(node.cause)
711        elif hasattr(node, 'type') and node.type is not None:
712            self.visit(node.type)
713            if node.inst is not None:
714                self.write_python(', ')
715                self.visit(node.inst)
716            if node.tback is not None:
717                self.write_python(', ')
718                self.visit(node.tback)
719
720    # Expressions
721
722    def visit_Attribute(self, node):
723        errStr = "Conversion Error in function " + self.name + ", Line #" + str(node.value.lineno)
724        errStr += "\nPython expression not supported: '" + node.value.id + "." + node.attr + "'"
725        raise Exception(errStr)
726        self.visit(node.value)
727        self.write_python('.' + node.attr)
728
729    def visit_Call(self, node):
730        want_comma = []
731        def write_comma():
732            if want_comma:
733                self.write_c(', ')
734            else:
735                want_comma.append(True)
736        if(hasattr(node.func, 'id')):
737            if(node.func.id not in self.C_Functions):
738                self.C_Functions.append(node.func.id)
739            if(node.func.id == 'abs'):
740                self.write_c("fabs ")
741            elif(node.func.id == 'int'):
742                self.write_c('(int) ')
743            elif(node.func.id == "SINCOS"):
744                self.WriteSincos(node)
745                return
746            else:
747                self.visit(node.func)
748        else:
749            self.visit(node.func)
750#self.C_Functions
751        self.write_c('(')
752        for arg in node.args:
753            write_comma()
754            self.visited_args = True
755            self.visit(arg)
756        for keyword in node.keywords:
757            write_comma()
758            self.write_c(keyword.arg + '=')
759            self.visit(keyword.value)
760        if hasattr(node, 'starargs'):
761            if node.starargs is not None:
762                write_comma()
763                self.write_c('*')
764                self.visit(node.starargs)
765        if hasattr(node, 'kwargs'):
766            if node.kwargs is not None:
767                write_comma()
768                self.write_c('**')
769                self.visit(node.kwargs)
770        self.write_c(');')
771
772    def visit_Name(self, node):
773        self.write_c(node.id)
774        if((node.id in self.C_Pointers) and(not self.SubRef)):
775            self.write_c("[0]")
776        name = ""
777        sub = node.id.find("[")
778        if(sub > 0):
779            name = node.id[0:sub].strip()
780        else:
781            name = node.id
782#       add variable to C_Vars if it ins't there yet, not an argument and not a number
783        if ((name not in self.C_Functions) and (name not in self.C_Vars) and \
784            (name not in self.C_IntVars) and (name not in self.arguments) and \
785            (name not in self.C_Constants) and (name.isnumeric() == False)):
786            if(self.InSubscript):
787                self.C_IntVars.append(node.id)
788            else:
789                self.C_Vars.append(node.id)
790
791    def visit_Str(self, node):
792        self.write_c(repr(node.s))
793
794    def visit_Bytes(self, node):
795        self.write_c(repr(node.s))
796
797    def visit_Num(self, node):
798        self.write_c(repr(node.n))
799
800    def visit_Tuple(self, node):
801        for idx, item in enumerate(node.elts):
802            if idx:
803                self.Tuples.append(item)
804            else:
805                self.visit(item)
806
807    def sequence_visit(left, right):
808        def visit(self, node):
809            self.is_sequence = True
810            s = ""
811            for idx, item in enumerate(node.elts):
812                if((idx > 0) and(len(s) > 0)):
813                    s += ', '
814                if(hasattr(item, 'id')):
815                    s += item.id
816                elif(hasattr(item, 'n')):
817                    s += str(item.n)
818            if(len(s) > 0):
819                self.C_Vectors.append(s)
820                vec_name = "vec"  + str(len(self.C_Vectors))
821                self.write_c(vec_name)
822                vec_name += "#"
823        return visit
824
825    visit_List = sequence_visit('[', ']')
826    visit_Set = sequence_visit('{', '}')
827    del sequence_visit
828
829    def visit_Dict(self, node):
830        self.write_python('{')
831        for idx, (key, value) in enumerate(zip(node.keys, node.values)):
832            if idx:
833                self.write_python(', ')
834            self.visit(key)
835            self.write_python(': ')
836            self.visit(value)
837        self.write_python('}')
838
839    def get_special_power(self, string):
840        function_name = ''
841        is_negative_exp = False
842        if(isevaluable(str(self.current_statement))):
843            exponent = eval(string)
844            is_negative_exp = exponent < 0
845            abs_exponent = abs(exponent)
846            if(abs_exponent == 2):
847                function_name = "square"
848            elif(abs_exponent == 3):
849                function_name = "cube"
850            elif(abs_exponent == 0.5):
851                function_name = "sqrt"
852            elif(abs_exponent == 1.0/3.0):
853                function_name = "cbrt"
854        if(function_name == ''):
855            function_name = "pow"
856        return function_name, is_negative_exp
857
858    def translate_power(self, node):
859# get exponent by visiting the right hand argument.
860        function_name = "pow"
861        temp_statement = self.current_statement
862# 'visit' functions write the results to the 'current_statement' class memnber
863# Here, a temporary variable, 'temp_statement', is used, that enables the
864# use of the 'visit' function
865        self.current_statement = ''
866        self.visit(node.right)
867        exponent = self.current_statement.replace(' ', '')
868        function_name, is_negative_exp = self.get_special_power(self.current_statement)
869        self.current_statement = temp_statement
870        if(is_negative_exp):
871            self.write_c("1.0 /(")
872        self.write_c(function_name + "(")
873        self.visit(node.left)
874        if(function_name == "pow"):
875            self.write_c(", ")
876            self.visit(node.right)
877        self.write_c(")")
878        if(is_negative_exp):
879            self.write_c(")")
880        self.write_c(" ")
881
882    def translate_integer_divide(self, node):
883        self.write_c("(int)(")
884        self.visit(node.left)
885        self.write_c(") /(int)(")
886        self.visit(node.right)
887        self.write_c(")")
888
889    def visit_BinOp(self, node):
890        self.write_c("(")
891        if('%s' % BINOP_SYMBOLS[type(node.op)] == BINOP_SYMBOLS[ast.Pow]):
892            self.translate_power(node)
893        elif('%s' % BINOP_SYMBOLS[type(node.op)] == BINOP_SYMBOLS[ast.FloorDiv]):
894            self.translate_integer_divide(node)
895        else:
896            self.visit(node.left)
897            self.write_c(' %s ' % BINOP_SYMBOLS[type(node.op)])
898            self.visit(node.right)
899        self.write_c(")")
900
901#       for C
902    def visit_BoolOp(self, node):
903        self.write_c('(')
904        for idx, value in enumerate(node.values):
905            if idx:
906                self.write_c(' %s ' % BOOLOP_SYMBOLS[type(node.op)])
907            self.visit(value)
908        self.write_c(')')
909
910    def visit_Compare(self, node):
911        self.write_c('(')
912        self.visit(node.left)
913        for op, right in zip(node.ops, node.comparators):
914            self.write_c(' %s ' % CMPOP_SYMBOLS[type(op)])
915            self.visit(right)
916        self.write_c(')')
917
918    def visit_UnaryOp(self, node):
919        self.write_c('(')
920        op = UNARYOP_SYMBOLS[type(node.op)]
921        self.write_c(op)
922        if op == 'not':
923            self.write_c(' ')
924        self.visit(node.operand)
925        self.write_c(')')
926
927    def visit_Subscript(self, node):
928        if (node.value.id not in self.C_Constants):
929            if(node.value.id not in self.C_Pointers):
930                self.C_Pointers.append(node.value.id)
931        self.SubRef = True
932        self.visit(node.value)
933        self.SubRef = False
934        self.write_c('[')
935        self.InSubscript = True
936        self.visit(node.slice)
937        self.InSubscript = False
938        self.write_c(']')
939
940    def visit_Slice(self, node):
941        if node.lower is not None:
942            self.visit(node.lower)
943        self.write_python(':')
944        if node.upper is not None:
945            self.visit(node.upper)
946        if node.step is not None:
947            self.write_python(':')
948            if not(isinstance(node.step, Name) and node.step.id == 'None'):
949                self.visit(node.step)
950
951    def visit_ExtSlice(self, node):
952        for idx, item in node.dims:
953            if idx:
954                self.write_python(', ')
955            self.visit(item)
956
957    def visit_Yield(self, node):
958        self.write_python('yield ')
959        self.visit(node.value)
960
961    def visit_Lambda(self, node):
962        self.write_python('lambda ')
963        self.visit(node.args)
964        self.write_python(': ')
965        self.visit(node.body)
966
967    def visit_Ellipsis(self, node):
968        self.write_python('Ellipsis')
969
970    def generator_visit(left, right):
971        def visit(self, node):
972            self.write_python(left)
973            self.write_c(left)
974            self.visit(node.elt)
975            for comprehension in node.generators:
976                self.visit(comprehension)
977            self.write_c(right)
978#            self.write_python(right)
979        return visit
980
981    visit_ListComp = generator_visit('[', ']')
982    visit_GeneratorExp = generator_visit('(', ')')
983    visit_SetComp = generator_visit('{', '}')
984    del generator_visit
985
986    def visit_DictComp(self, node):
987        self.write_python('{')
988        self.visit(node.key)
989        self.write_python(': ')
990        self.visit(node.value)
991        for comprehension in node.generators:
992            self.visit(comprehension)
993        self.write_python('}')
994
995    def visit_IfExp(self, node):
996        self.visit(node.body)
997        self.write_c(' if ')
998        self.visit(node.test)
999        self.write_c(' else ')
1000        self.visit(node.orelse)
1001
1002    def visit_Starred(self, node):
1003        self.write_c('*')
1004        self.visit(node.value)
1005
1006    def visit_Repr(self, node):
1007        # XXX: python 2.6 only
1008        self.write_c('`')
1009        self.visit(node.value)
1010        self.write_python('`')
1011
1012    # Helper Nodes
1013
1014    def visit_alias(self, node):
1015        self.write_python(node.name)
1016        if node.asname is not None:
1017            self.write_python(' as ' + node.asname)
1018
1019    def visit_comprehension(self, node):
1020        self.write_c(' for ')
1021        self.visit(node.target)
1022        self.write_C(' in ')
1023#        self.write_python(' in ')
1024        self.visit(node.iter)
1025        if node.ifs:
1026            for if_ in node.ifs:
1027                self.write_python(' if ')
1028                self.visit(if_)
1029
1030    def visit_arguments(self, node):
1031        self.signature(node)
1032
1033def print_function(f=None):
1034    """
1035    Print out the code for the function
1036    """
1037    # Include some comments to see if they get printed
1038    import ast
1039    import inspect
1040    if f is not None:
1041        tree = ast.parse(inspect.getsource(f))
1042        tree_source = to_source(tree)
1043        print(tree_source)
1044
1045def add_constants (sniplets, c_constants):
1046    sniplets.append("#include <math.h>")
1047    sniplets.append("")
1048    vars = c_constants.keys()
1049    for c_var in vars:
1050        c_values = c_constants[c_var]
1051        declare_values = str(c_values)
1052        str_dcl = "double " + c_var
1053        if (hasattr(c_values,'__len__')):
1054            str_dcl += "[]"
1055            len_prev = len(declare_values)
1056            len_after = len_prev - 1
1057            declare_values = declare_values.replace ('[','')
1058            declare_values = declare_values.replace (']','').strip()
1059            while (len_after < len_prev):
1060                len_prev = len_after
1061                declare_values = declare_values.replace ('  ',' ')
1062                len_after = len(declare_values)
1063            declare_values = "{" + declare_values.replace (' ',',') + "}"
1064        str_dcl += " = " + declare_values + ";"
1065        sniplets.append (str_dcl)
1066        sniplets.append("")
1067
1068def translate(functions, constants=0):
1069    sniplets = []
1070    add_constants (sniplets, constants)
1071    for source,fname,line_no in functions:
1072        line_directive = '#line %d "%s"' %(line_no,fname)
1073        line_directive = line_directive.replace('\\','\\\\')
1074#        sniplets.append(line_directive)
1075        tree = ast.parse(source)
1076        sniplet = to_source(tree, functions, constants) # in the future add filename, offset, constants
1077        sniplets.append(sniplet)
1078    c_code = "\n".join(sniplets)
1079    f_out = open ("xlate.c", "w+")
1080    f_out.write (c_code)
1081    f_out.close()
1082    return("\n".join(sniplets))
1083
1084def get_file_names():
1085    fname_in = ""
1086    fname_out = ""
1087    if(len(sys.argv) > 1):
1088        fname_in = sys.argv[1]
1089        fname_base = os.path.splitext(fname_in)
1090        if(len(sys.argv) == 2):
1091            fname_out = str(fname_base[0]) + '.c'
1092        else:
1093            fname_out = sys.argv[2]
1094        if(len(fname_in) > 0):
1095            python_file = open(sys.argv[1], "r")
1096            if(len(fname_out) > 0):
1097                file_out = open(fname_out, "w+")
1098    return len(sys.argv), fname_in, fname_out
1099
1100if __name__ == "__main__":
1101    import os
1102    print("Parsing...using Python" + sys.version)
1103    try:
1104        fname_in = ""
1105        fname_out = ""
1106        if(len(sys.argv) == 1):
1107            print("Usage:\npython parse01.py <infile> [<outfile>](if omitted, output file is '<infile>.c'")
1108        else:
1109            fname_in = sys.argv[1]
1110            fname_base = os.path.splitext(fname_in)
1111            if(len(sys.argv) == 2):
1112                fname_out = str(fname_base[0]) + '.c'
1113            else:
1114                fname_out = sys.argv[2]
1115            if(len(fname_in) > 0):
1116                python_file = open(sys.argv[1], "r")
1117                if(len(fname_out) > 0):
1118                    file_out = open(fname_out, "w+")
1119                functions = ["MultAsgn", "Iq41", "Iq2"]
1120                tpls = [functions, fname_in, 0]
1121                c_txt = translate(tpls)
1122                file_out.write(c_txt)
1123                file_out.close()
1124    except Exception as excp:
1125        print("Error:\n" + str(excp.args))
1126    print("...Done")
Note: See TracBrowser for help on using the repository browser.