~ubuntu-branches/ubuntu/intrepid/blender/intrepid-updates

« back to all changes in this revision

Viewing changes to extern/qdune/slcompiler/qdslc.py

  • Committer: Bazaar Package Importer
  • Author(s): Cyril Brulebois
  • Date: 2008-08-08 02:45:40 UTC
  • mfrom: (12.1.14 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080808024540-kkjp7ekfivzhuw3l
Tags: 2.46+dfsg-4
* Fix python syntax warning in import_dxf.py, which led to nasty output
  in installation/upgrade logs during byte-compilation, using a patch
  provided by the script author (Closes: #492280):
   - debian/patches/45_fix_python_syntax_warning

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
 
 
3
#------------------------------------------------------------------------------------------------------------------
 
4
# This is the (prototype) QuietDune shader compiler for the Renderman Shader Language.
 
5
# This is a very recent work in progress, although not unusable, results will probably vary quite a bit...
 
6
# Disclaimer again: I'm not a 'real' programmer, and I have never done things like this before,
 
7
# there probably are some rather basic errors, wrong assumptions and general stupidities here as always.
 
8
# Initially it was based on the basic RSL yacc grammar by Andrew Bromage,
 
9
# available here: http://andrew.bromage.org/renderman.html,
 
10
# which I converted to python with 'yply' of (and for) the python 'ply' parser.
 
11
# This was then further extended and modified.
 
12
# Special Thanks go to Alejandro Conty Estevez for patiently explaining some of the basics of parsing,
 
13
# as well as kindly creating some example code for me to parse/compile simple math expressions,
 
14
# on which pretty much all of this code is based on (especially the use of the visitor pattern) :)
 
15
# TODO add correct syntax error handling
 
16
#------------------------------------------------------------------------------------------------------------------
 
17
 
 
18
import sys
 
19
# temporary extended path (relative), ply is not installed
 
20
sys.path.insert(0,"../..")
 
21
 
 
22
from slcompile import *
 
23
#from astsimplify import *
 
24
 
 
25
# dictionary that is used to keep track of any declared variables with constant values for direct evaluation, see evalBinop() below
 
26
# when exiting braced statements and after any function it is reset to simplify scope handling
 
27
# (TODO add scope info as well, now some constant statements will still be executed in code)
 
28
var_dict = {}
 
29
# set when any parse/syntax error occured
 
30
parse_error = False
 
31
 
 
32
 
 
33
# NOTE: while the idea was nice and sofar it doesn't seem to have caused any problems for all currently tested shaders,
 
34
#       the evalsomething() funcs below can't possibly work properly in a first pass,
 
35
#       not when variables are involved at least, so that case is disabled for now, pure number constants are ok.
 
36
#       (eg. "i=0; while (i<10) { i += 1; }" would be 'optimized' to "i=0;  while (i<10) { i=1; }" <- infinite loop)
 
37
 
 
38
# for the binop expressions, can do 'constant folding/propagation' when both operands are
 
39
# either constant numbers or variables with constant values,
 
40
# in which case the result node will be another constant number node
 
41
def evalBinop(p):
 
42
        p1_isnum = isinstance(p[1], astNumConst_t)
 
43
        p3_isnum = isinstance(p[3], astNumConst_t)
 
44
        p1_isvec = isinstance(p[1], astVecConst_t)
 
45
        p3_isvec = isinstance(p[3], astVecConst_t)
 
46
        p1_iscvar = (isinstance(p[1], astID_t) and var_dict.has_key(p[1].name))
 
47
        p3_iscvar = (isinstance(p[3], astID_t) and var_dict.has_key(p[3].name))
 
48
        #if (p1_isnum or p1_isvec or p1_iscvar) and (p3_isnum or p3_isvec or p3_iscvar):
 
49
        if (p1_isnum or p1_isvec) and (p3_isnum or p3_isvec):
 
50
                if p1_isnum or p1_isvec:
 
51
                        val1 = p[1].val
 
52
                else:
 
53
                        val1 = var_dict[p[1].name]
 
54
                        if isinstance(val1, vector_t): p1_isvec = True
 
55
                if p3_isnum or p3_isvec:
 
56
                        val2 = p[3].val
 
57
                else:
 
58
                        val2 = var_dict[p[3].name]
 
59
                        if isinstance(val2, vector_t): p3_isvec = True
 
60
                opr = p[2]
 
61
                if opr == '.' or opr == '^':
 
62
                        if not (p1_isvec and p3_isvec):
 
63
                                raise Exception("Expected two vectors for dot or cross operator")
 
64
                        if opr == '.':
 
65
                                return astNumConst_t(eval(val1.asString() + ".dot(" + val2.asString() + ')'), lexer.lineno)
 
66
                        else:
 
67
                                return astVecConst_t(eval(val1.asString() + ".cross(" + val2.asString() + ')'), lexer.lineno)
 
68
                if p1_isnum and p3_isvec:
 
69
                        return astVecConst_t(eval("vector_t(" + str(val1) + ")" + opr + val2.asString()), lexer.lineno)
 
70
                elif p1_isvec and p3_isnum:
 
71
                        return astVecConst_t(eval(val1.asString() + opr + "vector_t(" + str(val2) + ")"), lexer.lineno)
 
72
                elif p1_isvec and p3_isvec:
 
73
                        return astVecConst_t(eval(val1.asString() + opr + val2.asString()), lexer.lineno)
 
74
                else:   # both num.
 
75
                        return astNumConst_t(eval(str(val1) + opr + str(val2)), lexer.lineno)
 
76
        else:   # cannot evaluate directly
 
77
                return astBinop_t(p[1], p[2], p[3], lexer.lineno)
 
78
 
 
79
# and the same for the assignment arith.expressions for the case that rhs is a constant (num. or var.)
 
80
# eg. "a *= b" becomes "a = result", where result is the result of "a *= b"
 
81
def evalAssignExpr(p):
 
82
        p3_isnum = isinstance(p[3], astNumConst_t)
 
83
        p3_isvec = isinstance(p[3], astVecConst_t)
 
84
        p1_iscvar = (isinstance(p[1], astID_t) and var_dict.has_key(p[1].name))
 
85
        p3_iscvar = (isinstance(p[3], astID_t) and var_dict.has_key(p[3].name))
 
86
        if False: #p1_iscvar and (p3_isnum or p3_isvec or p3_iscvar):
 
87
                val1 = var_dict[p[1].name]
 
88
                if isinstance(val1, vector_t):
 
89
                        p1_isvec = True
 
90
                else:
 
91
                        p1_isvec = False
 
92
                if p3_isnum or p3_isvec:
 
93
                        val2 = p[3].val
 
94
                else:
 
95
                        val2 = var_dict[p[3].name]
 
96
                        if isinstance(val2, vector_t): p3_isvec = True
 
97
                asgn, oper = '=', p[2][0]
 
98
                if p1_isvec and p3_isvec:
 
99
                        return astAssignExpr_t(p[1], asgn, astVecConst_t(eval(val1.asString() + oper + val2.asString()), lexer.lineno), lexer.lineno)
 
100
                elif p1_isvec:
 
101
                        return astAssignExpr_t(p[1], asgn, astVecConst_t(eval(val1.asString() + oper + "vector_t(" + str(val2) + ")"), lexer.lineno), lexer.lineno)
 
102
                else:   # both num.
 
103
                        try:
 
104
                                return astAssignExpr_t(p[1], asgn, astNumConst_t(eval(str(val1) + oper + str(val2)), lexer.lineno), lexer.lineno)
 
105
                        except:
 
106
                                # some weird error?
 
107
                                print var_dict
 
108
                                print val1, oper, val2
 
109
                                print p[1], p[2], p[3]
 
110
                                raise Exception("ERROR AT Line " + str(lexer.lineno))
 
111
        else:   # cannot evaluate directly
 
112
                # in this case lhs if id must be removed from var_dict if in var_dict since it is no longer a constant
 
113
                if p1_iscvar: var_dict.pop(p[1].name)
 
114
                return astAssignExpr_t(p[1], p[2], p[3], lexer.lineno)
 
115
 
 
116
# Similarly, math funcs with constant number (or vars with const.num) arguments can also be evaluated directly.
 
117
# With 3 exceptions (abs, mod & atan), all possible funcs have a direct python math module equivalent
 
118
def evalMathProc(pcname, pcargs):
 
119
        if pcname in ["sin", "cos", "tan", "asin", "acos", "atan", "exp", "sqrt", "log", "pow", "ceil", "floor", "radians", "degrees", "mod", "abs"]:
 
120
                import math
 
121
                numargs = len(pcargs)
 
122
                if numargs == 1:
 
123
                        arg1 = pcargs[0]
 
124
                        if isinstance(arg1, astNumConst_t):
 
125
                                if pcname == "abs":
 
126
                                        func = "math.fabs"
 
127
                                else:
 
128
                                        func = "math." + pcname
 
129
                                return astNumConst_t(eval(func + '(' + str(arg1.val) + ')'), lexer.lineno)
 
130
#                       elif isinstance(arg1, astID_t) and var_dict.has_key(arg1.name):
 
131
#                               if pcname == "abs":
 
132
#                                       func = "math.fabs"
 
133
#                               else:
 
134
#                                       func = "math." + pcname
 
135
#                               return astNumConst_t(eval(func + '(' + str(var_dict[arg1.name]) + ')'), lexer.lineno)
 
136
                elif numargs == 2:      # two arg funcs (pow, log, atan & mod)
 
137
                        arg1, arg2 = pcargs
 
138
                        if pcname == "atan":
 
139
                                func = "math.atan2"
 
140
                        elif pcname == "mod":
 
141
                                func = "math.fmod"
 
142
                        else:
 
143
                                func = "math." + pcname
 
144
                        # handling constant variables is more complicated here since the two args can be both constant vars, or one can be var and other num and vice versa...
 
145
                        if isinstance(arg2, astNumConst_t):
 
146
                                if isinstance(arg1, astNumConst_t):
 
147
                                        return astNumConst_t(eval(func + '(' + str(arg1.val) + ',' + str(arg2.val) + ')'), lexer.lineno)
 
148
#                               elif isinstance(arg1, astID_t) and var_dict.has_key(arg1.name):
 
149
#                                       return astNumConst_t(eval(func + '(' + str(var_dict[arg1.name]) + ',' + str(arg2.val) + ')'), lexer.lineno)
 
150
#                       elif isinstance(arg2, astID_t) and var_dict.has_key(arg2.name):
 
151
#                               if isinstance(arg1, astNumConst_t):
 
152
#                                       return astNumConst_t(eval(func + '(' + str(arg1.val) + ',' + str(var_dict[arg2.name]) + ')'), lexer.lineno)
 
153
#                               elif isinstance(arg1, astID_t) and var_dict.has_key(arg1.name):
 
154
#                                       return astNumConst_t(eval(func + '(' + str(var_dict[arg1.name]) + ',' + str(var_dict[arg2.name]) + ')'), lexer.lineno)
 
155
 
 
156
#------------------------------------------------------------
 
157
 
 
158
start = 'source_file'
 
159
 
 
160
tokens =  ['PSEUDO_LOW_PRECEDENCE', 'IDENTIFIER', 'STRINGCONST', 'FLOATCONST', 'INTCONST', 'LIGHT', 'SURFACE', 'VOLUME', 'DISPLACEMENT', 'IMAGER', 'VARYING', 'UNIFORM', 'OUTPUT', 'EXTERN', 'VOID', 'FLOAT', 'STRING', 'POINT', 'COLOR', 'VECTOR', 'NORMAL', 'MATRIX', 'IF', 'ELSE', 'BREAK', 'CONTINUE', 'RETURN', 'WHILE', 'FOR', 'SOLAR', 'ILLUMINATE', 'ILLUMINANCE', 'TEXTURE', 'ENVIRONMENT', 'SHADOW', 'PLUSEQ', 'MINUSEQ', 'MULTEQ', 'DIVEQ', 'EQ', 'NEQ', 'LEQ', 'GEQ', 'AND', 'OR', 'FILTERSTEP', 'ROTATE', 'FRESNEL']
 
161
 
 
162
precedence =  [('nonassoc', 'PSEUDO_LOW_PRECEDENCE') , ('nonassoc', 'IF', 'ELSE')]
 
163
 
 
164
# -------------- RULES ----------------
 
165
 
 
166
def p_source_file(p):
 
167
        '''source_file : definitions'''
 
168
        p[0] = astProgram_t(p[1], lexer.lineno)
 
169
 
 
170
def p_definitions(p):
 
171
        '''definitions :
 
172
                       | definitions shader_definition
 
173
                       | definitions function_definition'''
 
174
        if len(p) > 1:
 
175
                if isinstance(p[1], list):
 
176
                        p[1].append(p[2])
 
177
                        p[0] = p[1]
 
178
                else:
 
179
                        p[0] = [p[2]]   # p[1] always undefined at first, not a list yet
 
180
 
 
181
def p_shader_definition(p):
 
182
        '''shader_definition : shader_type IDENTIFIER '(' opt_formals ')' body'''
 
183
        p[0] = astShaderDef_t(p[1], p[2], p[4], p[6], lexer.lineno)
 
184
        # reset the var_dict
 
185
        var_dict.clear()
 
186
        # default parameter detail for shader parameters is 'uniform'
 
187
        if p[4]:
 
188
                for f in p[4].formals:
 
189
                        tps = f.typespec
 
190
                        if tps[0] == None:
 
191
                                tps[0] = "uniform"
 
192
                        elif tps[0] == "varying" and tps[1] == "string":
 
193
                                # strings are always uniform, so warn and modify if 'varying' specified
 
194
                                print "WARNING, at line %d: string detail type must always be uniform" % f.lineno
 
195
                                tps[0] = "uniform"
 
196
 
 
197
def p_function_definition(p):
 
198
        '''function_definition : opt_type IDENTIFIER '(' opt_func_formals ')' body'''
 
199
        p[0] = astFunctionDef_t(p[1], p[2], p[4], p[6], False, lexer.lineno)
 
200
        # reset the var_dict
 
201
        var_dict.clear()
 
202
        # default parameter detail for function parameters is 'varying'
 
203
        if p[4]:
 
204
                for f in p[4].formals:
 
205
                        tps = f.typespec
 
206
                        if tps[0] == None:
 
207
                                tps[0] = "varying"
 
208
                        elif tps[0] == "varying" and tps[1] == "string":
 
209
                                # strings are always uniform, so warn and modify if 'varying' specified
 
210
                                print "WARNING, at line %d: string detail type must always be uniform" % f.lineno
 
211
                                tps[0] = "uniform"
 
212
 
 
213
# opt_externspec & typespec only seem to be here to solve shift/reduce conflicts, otherwise it should be exactly the same as function_definition above
 
214
def p_lexical_function_definition(p):
 
215
        '''lexical_function_definition : opt_externspec typespec IDENTIFIER '(' opt_func_formals ')' body'''
 
216
        p[0] = astFunctionDef_t(p[2], p[3], p[5], p[7], True, lexer.lineno)
 
217
        # constant variables assigned to while in the function are not valid and will cause errors when assigning after it, so clear the var_dict
 
218
        var_dict.clear()
 
219
        # default parameter detail for function parameters is 'varying'
 
220
        if p[5]:
 
221
                for f in p[5].formals:
 
222
                        tps = f.typespec
 
223
                        if tps[0] == None:
 
224
                                tps[0] = "varying"
 
225
                        elif tps[0] == "varying" and tps[1] == "string":
 
226
                                # strings are always uniform, so warn and modify if 'varying' specified
 
227
                                print "WARNING, at line %d: string detail type must always be uniform" % f.lineno
 
228
                                tps[0] = "uniform"
 
229
 
 
230
def p_body(p):
 
231
        '''body : '{' statements '}' '''
 
232
        p[0] = astStatements_t(p[2], lexer.lineno)
 
233
 
 
234
def p_shader_type(p):
 
235
        '''shader_type : LIGHT
 
236
                       | SURFACE
 
237
                       | VOLUME
 
238
                       | DISPLACEMENT
 
239
                       | IMAGER'''
 
240
        p[0] = p[1]
 
241
 
 
242
# sets flag of the parameters default expressions, and removes the params from the var_dict
 
243
def setParamFlag(formals, pname):
 
244
        global var_dict
 
245
        shader = (pname == "shparam")
 
246
        varid = {}
 
247
        for param in formals:
 
248
                for dexpr in param.exprs:
 
249
                        if shader:      # shader parameters *must* have a default value assigned to them
 
250
                                if dexpr.def_init == None:
 
251
                                        raise Exception("SYNTAX ERROR, at line %d: shader parameter '%s' has no default value" % (lexer.lineno, dexpr.id))
 
252
                        else:   # function parameters however *cannot*
 
253
                                if dexpr.def_init != None:
 
254
                                        raise Exception("SYNTAX ERROR, at line %d: function parameter '%s', cannot assign default value" % (lexer.lineno, dexpr.id))
 
255
                        exec("dexpr.%s = True" % pname)
 
256
                        if shader and var_dict.has_key(dexpr.id): var_dict.pop(dexpr.id)
 
257
 
 
258
# original opt_formals split into p_opt_formals() for shader parameters (to allow direct eval of constant values), and p_opt_func_formals() for function parameters
 
259
def p_opt_formals(p):
 
260
        '''opt_formals :
 
261
                       | formals
 
262
                       | formals ';' '''
 
263
        if len(p) > 1:
 
264
                p[0] = astFormals_t(p[1], lexer.lineno)
 
265
                # Shader parameters now known, set the 'shparam' flag in the def_expression list of each.
 
266
                # Also remove any parameters from var_dict, since parameters can only be evaluated directly when they still have default values
 
267
                setParamFlag(p[1], "shparam")
 
268
 
 
269
def p_opt_func_formals(p):
 
270
        '''opt_func_formals :
 
271
                            | formals
 
272
                            | formals ';' '''
 
273
        if len(p) > 1:
 
274
                p[0] = astFormals_t(p[1], lexer.lineno)
 
275
                # Function parameters now known, set the 'funcparam' flag in the def_expression list of each.
 
276
                setParamFlag(p[1], "funcparam")
 
277
 
 
278
def p_formals(p):
 
279
        '''formals : formal_variable_definitions
 
280
                   | formals ';' formal_variable_definitions '''
 
281
        if len(p) == 2:
 
282
                p[0] = [p[1]]
 
283
        else:
 
284
                p[1].append(p[3])
 
285
                p[0] = p[1]
 
286
 
 
287
def p_formal_variable_definitions(p):
 
288
        '''formal_variable_definitions : opt_outputspec typespec def_expressions'''
 
289
        p[0] = astParameter_t(p[1], p[2], p[3], lexer.lineno)
 
290
 
 
291
def p_variable_definitions(p):
 
292
        '''variable_definitions : opt_externspec typespec def_expressions'''
 
293
        # default variable detail if not specified is 'varying', UNLESS it's a string
 
294
        if p[2][0] == None:
 
295
                if p[2][1] == "string":
 
296
                        p[2][0] = "uniform"
 
297
                else:
 
298
                        p[2][0] = "varying"
 
299
        elif p[2][0] == "varying" and p[2][1] == "string":
 
300
                # strings are always uniform, so warn and modify if 'varying' specified
 
301
                print "[WARNING] line %d: string detail type must always be uniform" % lexer.lineno
 
302
                p[2][0] = "uniform"
 
303
        # remove extern declared variables inside functions from the vardict,
 
304
        # if the variable is going to be modified, it's no longer constant, so cannot be used for direct eval.
 
305
        # (It's possible that the var is actually only read, not modified, but that can't be determined at this stage.
 
306
        #  Well, it might be possible, but better would be a separate 'simplify' stage after parse)
 
307
        if p[1]:
 
308
                for dexpr in p[3]:
 
309
                        if var_dict.has_key(dexpr.id):
 
310
                                var_dict.pop(dexpr.id)
 
311
        p[0] = astVariable_t(p[1], p[2], p[3], lexer.lineno)
 
312
 
 
313
def p_typespec(p):
 
314
        '''typespec : opt_detail type'''
 
315
        p[0] = [p[1], p[2]]     # !!!
 
316
 
 
317
def p_def_expressions(p):
 
318
        '''def_expressions : def_expression
 
319
                           | def_expressions ',' def_expression'''
 
320
        if len(p) == 2:
 
321
                p[0] = [p[1]]
 
322
        else:
 
323
                p[1].append(p[3])
 
324
                p[0] = p[1]
 
325
 
 
326
def p_def_expression(p):
 
327
        '''def_expression : IDENTIFIER
 
328
                          | IDENTIFIER '=' expression
 
329
                          | IDENTIFIER '[' INTCONST ']'
 
330
                          | IDENTIFIER '[' INTCONST ']' '=' array_init '''
 
331
        plen = len(p)
 
332
        if plen == 2:
 
333
                p[0] = astDefExpression_t(p[1], None, None, lexer.lineno)
 
334
        elif p[2] == '=':
 
335
                p[0] = astDefExpression_t(p[1], None, p[3], lexer.lineno)
 
336
                # keep track of any variable with a constant value, needed for possible direct evaluation
 
337
                if isinstance(p[3], astNumConst_t) or isinstance(p[3], astVecConst_t):
 
338
                        var_dict[p[1]] = p[3].val
 
339
        elif plen == 5:
 
340
                p[0] = astDefExpression_t(p[1], p[3], None, lexer.lineno)
 
341
        else:
 
342
                p[0] = astDefExpression_t(p[1], p[3], p[6], lexer.lineno)
 
343
 
 
344
def p_array_expr_list(p):
 
345
        '''array_expr_list : expression
 
346
                            | array_expr_list ',' expression'''
 
347
        if len(p) == 2:
 
348
                if isinstance(p[1], astNumConst_t):
 
349
                        p[0] = [True, [p[1].val]]
 
350
                else:
 
351
                        p[0] = [False, [p[1]]]
 
352
        else:
 
353
                if isinstance(p[3], astNumConst_t):
 
354
                        p[1][1].append(p[3].val)
 
355
                else:
 
356
                        p[1][0] = False
 
357
                        p[1][1].append(p[3])
 
358
                p[0] = p[1]
 
359
 
 
360
def p_array_init(p):
 
361
        '''array_init : '{' array_expr_list '}' '''
 
362
        p[0] = array_t(p[2][0], p[2][1])
 
363
 
 
364
def p_opt_detail(p):
 
365
        '''opt_detail :
 
366
                      | VARYING
 
367
                      | UNIFORM'''
 
368
        if len(p) > 1: p[0] = p[1]
 
369
 
 
370
def p_opt_outputspec(p):
 
371
        '''opt_outputspec :
 
372
                          | OUTPUT'''
 
373
        if len(p) > 1: p[0] = p[1]
 
374
 
 
375
def p_opt_externspec(p):
 
376
        '''opt_externspec :
 
377
                          | EXTERN'''
 
378
        if len(p) > 1: p[0] = p[1]
 
379
 
 
380
def p_opt_type(p):
 
381
        '''opt_type :
 
382
                    | type'''
 
383
        if len(p) > 1: p[0] = p[1]
 
384
 
 
385
def p_type(p):
 
386
        '''type : FLOAT
 
387
                | STRING
 
388
                | COLOR
 
389
                | POINT
 
390
                | VECTOR
 
391
                | NORMAL
 
392
                | MATRIX
 
393
                | VOID'''
 
394
        p[0] = p[1]
 
395
 
 
396
def p_statements(p):
 
397
        '''statements :
 
398
                      | statements statement
 
399
                      | statements variable_definitions ';'
 
400
                      | statements lexical_function_definition'''
 
401
        if len(p) > 1:
 
402
                if isinstance(p[1], list):
 
403
                        p[1].append(p[2])
 
404
                        p[0] = p[1]
 
405
                else:
 
406
                        p[0] = [p[2]]   # p[1] always undefined at first, not a list yet
 
407
 
 
408
def p_statement_1(p):
 
409
        '''statement : assignexpr ';' '''
 
410
        p[0] = p[1]
 
411
 
 
412
def p_statement_2(p):
 
413
        '''statement : procedurecall ';' '''
 
414
        p[0] = p[1]
 
415
 
 
416
def p_statement_3(p):
 
417
        '''statement : RETURN expression ';' '''
 
418
        p[0] = astReturn_t(p[2], lexer.lineno)
 
419
 
 
420
def p_statement_4(p):
 
421
        '''statement : BREAK opt_integer ';' '''
 
422
        p[0] = astBreak_t(p[2], lexer.lineno)
 
423
 
 
424
def p_statement_5(p):
 
425
        '''statement : CONTINUE opt_integer ';' '''
 
426
        p[0] = astContinue_t(p[2], lexer.lineno)
 
427
 
 
428
def p_statement_6(p):
 
429
        '''statement : IF '(' relation ')' statement %prec PSEUDO_LOW_PRECEDENCE'''
 
430
        p[0] = astIfElse_t(p[3], p[5], None, False, lexer.lineno)
 
431
 
 
432
def p_statement_7(p):
 
433
        '''statement : IF '(' relation  ')' statement ELSE statement'''
 
434
        p[0] = astIfElse_t(p[3], p[5], p[7], False, lexer.lineno)
 
435
 
 
436
def p_statement_8(p):
 
437
        '''statement : WHILE '(' relation ')' statement'''
 
438
        p[0] = astFor_t(None, p[3], None, p[5], lexer.lineno)
 
439
 
 
440
def p_statement_9(p):
 
441
        '''statement : FOR '(' expression ';' relation ';' expression ')' statement'''
 
442
        p[0] = astFor_t(p[3], p[5], p[7], p[9], lexer.lineno)
 
443
 
 
444
def p_statement_10(p):
 
445
        '''statement : SOLAR '(' ')' statement'''
 
446
        p[0] = astSolar_t(None, None, p[4], lexer.lineno)
 
447
 
 
448
def p_statement_11(p):
 
449
        '''statement : SOLAR '(' expression ',' expression ')' statement'''
 
450
        p[0] = astSolar_t(p[3], p[5], p[7], lexer.lineno)
 
451
 
 
452
def p_statement_12(p):
 
453
        '''statement : ILLUMINATE '(' expression ')' statement'''
 
454
        p[0] = astIlluminate_t(p[3], None, None, p[5], lexer.lineno)
 
455
 
 
456
def p_statement_13(p):
 
457
        '''statement : ILLUMINATE '(' expression ',' expression ',' expression ')' statement'''
 
458
        p[0] = astIlluminate_t(p[3], p[5], p[7], p[9], lexer.lineno)
 
459
 
 
460
def p_statement_14(p):
 
461
        '''statement : ILLUMINANCE '(' expression ')' statement'''
 
462
        p[0] = astIlluminance_t(None, p[3], None, None, p[5], lexer.lineno)
 
463
 
 
464
def p_statement_15(p):
 
465
        '''statement : ILLUMINANCE '(' expression ',' expression ',' expression ')' statement'''
 
466
        p[0] = astIlluminance_t(None, p[3], p[5], p[7], p[9], lexer.lineno)
 
467
 
 
468
def p_statement_16(p):
 
469
        '''statement : ILLUMINANCE '(' STRINGCONST ',' expression ')' statement'''
 
470
        p[0] = astIlluminance_t(p[3], p[5], None, None, p[7], lexer.lineno)
 
471
 
 
472
def p_statement_17(p):
 
473
        '''statement : ILLUMINANCE '(' STRINGCONST ',' expression ',' expression ',' expression ')' statement'''
 
474
        p[0] = astIlluminance_t(p[3], p[5], p[7], p[9], p[11], lexer.lineno)
 
475
 
 
476
def p_statement_18(p):
 
477
        '''statement : '{' statements '}' opt_semicol '''
 
478
        # only need node to indicate that scope changes at this point
 
479
        p[0] = astNewScope_t(astStatements_t(p[2], lexer.lineno), lexer.lineno)
 
480
        # reset var_dict
 
481
        var_dict.clear()
 
482
 
 
483
# doesn't do anything, only needed when macros are used with braced statement that have semicolon at end of line
 
484
def p_opt_semicol(p):
 
485
        '''opt_semicol :
 
486
                       | ';' '''
 
487
        pass
 
488
 
 
489
#-------------------------------------------------------------------------------------------------------------
 
490
# extra statement rules to simplify handling of a few procedures
 
491
 
 
492
def p_statement_19(p):
 
493
        '''statement : ROTATE '(' expression ',' expression ',' expression ')' ';'
 
494
                     | ROTATE '(' expression ',' expression ',' expression ',' expression ')' ';' '''
 
495
        if len(p) == 9:
 
496
                p[0] = astRotate_t(p[3], p[5], p[7], None, lexer.lineno)
 
497
        else:
 
498
                p[0] = astRotate_t(p[3], p[5], p[7], p[9], lexer.lineno)
 
499
 
 
500
def p_statement_20(p):
 
501
        '''statement : FRESNEL '(' expression ',' expression ',' expression ',' expression ',' expression ')' ';'
 
502
                     | FRESNEL '(' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ')' ';' '''
 
503
        if len(p) == 14:
 
504
                p[0] = astFresnel_t(p[3], p[5], p[7], p[9], p[11], None, None, lexer.lineno)
 
505
        else:
 
506
                p[0] = astFresnel_t(p[3], p[5], p[7], p[9], p[11], p[13], p[15], lexer.lineno)
 
507
 
 
508
# filterstep handled separately as well because of optional parameterlist
 
509
def p_filterstep(p):
 
510
        '''filterstep : FILTERSTEP '(' expression ',' expression opt_paramlist ')'
 
511
                      | FILTERSTEP '(' expression ',' expression ',' expression opt_paramlist ')' '''
 
512
        if len(p) == 8:
 
513
                p[0] = astFilterstep_t(p[3], p[5], None, p[6], lexer.lineno);
 
514
        else:
 
515
                p[0] = astFilterstep_t(p[3], p[5], p[7], p[8], lexer.lineno)
 
516
 
 
517
def p_opt_paramlist(p):
 
518
        '''opt_paramlist :
 
519
                         | param
 
520
                         | opt_paramlist param'''
 
521
        lp = len(p)
 
522
        if lp > 1:
 
523
                if lp == 2:
 
524
                        p[0] = [p[1]]
 
525
                else:
 
526
                        p[1].append(p[2])
 
527
                        p[0] = p[1]
 
528
 
 
529
def p_param(p):
 
530
        '''param : ',' STRINGCONST ',' expression'''
 
531
        p[0] = [p[2], p[4]]
 
532
 
 
533
#-------------------------------------------------------------------------------------------------------------
 
534
 
 
535
def p_opt_integer(p):
 
536
        '''opt_integer :
 
537
                       | INTCONST'''
 
538
        if len(p) > 1: p[0] = p[1]
 
539
 
 
540
def p_primary_1(p):
 
541
        '''primary : INTCONST'''
 
542
        p[0] = astNumConst_t(p[1], lexer.lineno)
 
543
 
 
544
def p_primary_2(p):
 
545
        '''primary : FLOATCONST'''
 
546
        p[0] = astNumConst_t(p[1], lexer.lineno)
 
547
 
 
548
def p_primary_3(p):
 
549
        '''primary : texture
 
550
                   | filterstep '''
 
551
        p[0] = p[1]
 
552
 
 
553
def p_primary_4(p):
 
554
        '''primary : IDENTIFIER'''
 
555
        if p[1] == "PI":
 
556
                p[0] = astNumConst_t(3.1415926535897932384626433832795029, lexer.lineno)
 
557
        else:
 
558
                p[0] = astID_t(p[1], None, lexer.lineno)
 
559
 
 
560
def p_primary_5(p):
 
561
        '''primary : IDENTIFIER '[' expression ']' '''
 
562
        p[0] = astID_t(p[1], p[3], lexer.lineno)
 
563
 
 
564
def p_primary_6(p):
 
565
        '''primary : STRINGCONST'''
 
566
        p[0] = astStringConst_t(p[1], lexer.lineno)
 
567
 
 
568
def p_primary_7(p):
 
569
        '''primary : procedurecall'''
 
570
        p[0] = p[1]
 
571
 
 
572
def p_primary_8(p):
 
573
        '''primary : '(' expression ')' '''
 
574
        p[0] = p[2]
 
575
 
 
576
def p_primary_9(p):
 
577
        '''primary : '(' expression ',' expression ',' expression ')'
 
578
             | '(' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ')' '''
 
579
        const = True
 
580
        for i in range(2, len(p)-1, 2):
 
581
                const &= isinstance(p[i], astNumConst_t)
 
582
        if const:
 
583
                tp = []
 
584
                for i in range(2, len(p)-1, 2):
 
585
                        tp.append(p[i].val)
 
586
                if len(tp) == 3:
 
587
                        p[0] = astVecConst_t(vector_t(*tp), lexer.lineno)
 
588
                else:
 
589
                        p[0] = astMtxConst_t(matrix_t(*tp), lexer.lineno)
 
590
                return
 
591
        t = []
 
592
        for i in range(2, len(p)-1, 2):
 
593
                t.append(p[i])
 
594
        p[0] = astTuple_t(t, lexer.lineno)
 
595
 
 
596
def p_unary_expr(p):
 
597
        '''unary_expr : primary
 
598
                      | '-' unary_expr
 
599
                      | typecast unary_expr'''
 
600
        if len(p) == 2:
 
601
                p[0] = p[1]
 
602
        elif p[1] == '-':
 
603
                if isinstance(p[2], astNumConst_t):
 
604
                        p[2].val = -p[2].val
 
605
                        p[2].children = -p[2].children  # completely unnecessary, just for tree printing
 
606
                        p[0] = p[2]
 
607
                else:
 
608
                        p[0] = astUnaryExpr_t(p[1], p[2], lexer.lineno)
 
609
        else:
 
610
                p[0] = astUnaryExpr_t(p[1], p[2], lexer.lineno)
 
611
 
 
612
def p_dot_expr(p):
 
613
        '''dot_expr : unary_expr
 
614
                    | dot_expr '.' unary_expr'''
 
615
        if len(p) == 2:
 
616
                p[0] = p[1]
 
617
        else:
 
618
                #p[0] = astBinop_t(p[1], p[2], p[3], lexer.lineno)
 
619
                p[0] = evalBinop(p)
 
620
 
 
621
def p_multiplicative_expr(p):
 
622
        '''multiplicative_expr : dot_expr
 
623
                               | multiplicative_expr '*' dot_expr
 
624
                               | multiplicative_expr '/' dot_expr'''
 
625
        if len(p) == 2:
 
626
                p[0] = p[1]
 
627
        else:
 
628
                #p[0] = astBinop_t(p[1], p[2], p[3], lexer.lineno)
 
629
                p[0] = evalBinop(p)
 
630
 
 
631
def p_cross_expr(p):
 
632
        '''cross_expr : multiplicative_expr
 
633
                      | cross_expr '^' multiplicative_expr'''
 
634
        if len(p) == 2:
 
635
                p[0] = p[1]
 
636
        else:
 
637
                #p[0] = astBinop_t(p[1], p[2], p[3], lexer.lineno)
 
638
                p[0] = evalBinop(p)
 
639
 
 
640
def p_additive_expr(p):
 
641
        '''additive_expr : cross_expr
 
642
                         | additive_expr '+' cross_expr
 
643
                         | additive_expr '-' cross_expr'''
 
644
        if len(p) == 2:
 
645
                p[0] = p[1]
 
646
        else:
 
647
                #p[0] = astBinop_t(p[1], p[2], p[3], lexer.lineno)
 
648
                p[0] = evalBinop(p)
 
649
 
 
650
def p_conditional_expr(p):
 
651
        '''conditional_expr : additive_expr
 
652
                            | relation '?' additive_expr ':' conditional_expr'''
 
653
        if len(p) == 2:
 
654
                p[0] = p[1]
 
655
        else:
 
656
                p[0] = astIfElse_t(p[1], p[3], p[5], True, lexer.lineno)
 
657
 
 
658
def p_assignexpr(p):
 
659
        '''assignexpr : lhs asgnop assign_expr'''
 
660
        # skip direct evaluation here, see comments in evalAssignExpr() above
 
661
        p[0] = evalAssignExpr(p)
 
662
        #p[0] = astAssignExpr_t(p[1], p[2], p[3], lexer.lineno)
 
663
 
 
664
def p_asgnop(p):
 
665
        '''asgnop : '='
 
666
                  | PLUSEQ
 
667
                  | MINUSEQ
 
668
                  | MULTEQ
 
669
                  | DIVEQ'''
 
670
        p[0] = p[1]
 
671
 
 
672
def p_lhs(p):
 
673
        '''lhs : IDENTIFIER
 
674
               | IDENTIFIER '[' expression ']' '''
 
675
        if len(p) == 2:
 
676
                p[0] = astID_t(p[1], None, lexer.lineno)
 
677
        else:
 
678
                p[0] = astID_t(p[1], p[3], lexer.lineno)
 
679
 
 
680
def p_assign_expr(p):
 
681
        '''assign_expr : conditional_expr
 
682
                       | assignexpr'''
 
683
        p[0] = p[1]
 
684
 
 
685
def p_expression(p):
 
686
        '''expression : assign_expr'''
 
687
        p[0] = p[1]
 
688
 
 
689
def p_relation_1(p):
 
690
        '''relation : relation2 OR relation'''
 
691
        p[0] = astRelation_t(p[1], p[2], p[3], lexer.lineno)
 
692
 
 
693
def p_relation_2(p):
 
694
        '''relation : relation2'''
 
695
        p[0] = p[1]
 
696
 
 
697
def p_relation2_1(p):
 
698
        '''relation2 : relation3 AND relation2'''
 
699
        p[0] = astRelation_t(p[1], p[2], p[3], lexer.lineno)
 
700
 
 
701
def p_relation2_2(p):
 
702
        '''relation2 : relation3'''
 
703
        p[0] = p[1]
 
704
 
 
705
def p_relation3_1(p):
 
706
        '''relation3 : '!' relation3'''
 
707
        p[0] = astRelation_t(None, p[1], p[2], lexer.lineno)
 
708
 
 
709
def p_relation3_2(p):
 
710
        '''relation3 : relation4'''
 
711
        p[0] = p[1]
 
712
 
 
713
def p_relation4_1(p):
 
714
        '''relation4 : '(' relation ')' '''
 
715
        p[0] = p[2]
 
716
 
 
717
def p_relation4_2(p):
 
718
        '''relation4 : additive_expr relop additive_expr'''
 
719
        p[0] = astRelation_t(p[1], p[2], p[3], lexer.lineno)
 
720
 
 
721
def p_relop(p):
 
722
        '''relop : '>'
 
723
                 | GEQ
 
724
                 | '<'
 
725
                 | LEQ
 
726
                 | EQ
 
727
                 | NEQ'''
 
728
        p[0] = p[1]
 
729
 
 
730
def p_procedurecall(p):
 
731
        '''procedurecall : IDENTIFIER '(' opt_proc_arguments ')' '''
 
732
        direval = evalMathProc(p[1], p[3])
 
733
        if direval:
 
734
                p[0] = direval
 
735
        else:
 
736
                p[0] = astProcedureCall_t(p[1], p[3], lexer.lineno)
 
737
 
 
738
def p_typecast(p):
 
739
        '''typecast : FLOAT
 
740
                    | STRING
 
741
                    | COLOR opt_spacetype
 
742
                    | POINT opt_spacetype
 
743
                    | VECTOR opt_spacetype
 
744
                    | NORMAL opt_spacetype
 
745
                    | MATRIX opt_spacetype'''
 
746
        if len(p) == 2:
 
747
                p[0] = [p[1], None]
 
748
        else:
 
749
                p[0] = [p[1], p[2]]
 
750
 
 
751
def p_opt_spacetype(p):
 
752
        '''opt_spacetype :
 
753
                         | STRINGCONST'''
 
754
        if len(p) > 1: p[0] = astStringConst_t(p[1], lexer.lineno)
 
755
 
 
756
def p_opt_proc_arguments(p):
 
757
        '''opt_proc_arguments :
 
758
                              | proc_arguments'''
 
759
        if len(p) > 1: p[0] = p[1]
 
760
 
 
761
def p_proc_arguments(p):
 
762
        '''proc_arguments : expression
 
763
                          | proc_arguments ',' expression'''
 
764
        if len(p) == 2:
 
765
                p[0] = [p[1]]
 
766
        else:
 
767
                p[1].append(p[3])
 
768
                p[0] = p[1]
 
769
 
 
770
def p_texture(p):
 
771
        '''texture : texture_type '(' texture_filename_with_opt_channel opt_texture_arguments ')' '''
 
772
        p[0] = astTexture_t(p[1], p[3], p[4], lexer.lineno)
 
773
 
 
774
def p_texture_type(p):
 
775
        '''texture_type : TEXTURE
 
776
                        | ENVIRONMENT
 
777
                        | SHADOW'''
 
778
        p[0] = p[1]
 
779
 
 
780
def p_texture_filename_with_opt_channel_1(p):
 
781
        '''texture_filename_with_opt_channel : IDENTIFIER '[' expression ']' '[' INTCONST ']' '''
 
782
        p[0] = astTexFileChan_t(p[1], p[3], p[6], lexer.lineno)
 
783
 
 
784
def p_texture_filename_with_opt_channel_2(p):
 
785
        '''texture_filename_with_opt_channel : STRINGCONST '[' expression ']' '''
 
786
        p[0] = astTexFileChan_t(p[1], p[3], None, lexer.lineno)
 
787
 
 
788
def p_texture_filename_with_opt_channel_3(p):
 
789
        '''texture_filename_with_opt_channel : IDENTIFIER '[' expression ']' '''
 
790
        p[0] = astTexFileChan_t(p[1], p[3], None, lexer.lineno)
 
791
 
 
792
def p_texture_filename_with_opt_channel_4(p):
 
793
        '''texture_filename_with_opt_channel : IDENTIFIER'''
 
794
        p[0] = astTexFileChan_t(p[1], None, None, lexer.lineno)
 
795
 
 
796
def p_texture_filename_with_opt_channel_5(p):
 
797
        '''texture_filename_with_opt_channel : STRINGCONST'''
 
798
        p[0] = astTexFileChan_t(p[1], None, None, lexer.lineno)
 
799
 
 
800
def p_opt_texture_arguments(p):
 
801
        '''opt_texture_arguments :
 
802
                                 | ',' proc_arguments'''
 
803
        if len(p) > 1: p[0] = astTexArgs_t(p[2], lexer.lineno)
 
804
 
 
805
#### Catastrophic error handler
 
806
def p_error(p):
 
807
        global parse_error
 
808
        if p:
 
809
                print "SYNTAX ERROR AT LINE %d BEFORE TOKEN: '%s'" % (p.lineno, p.value)
 
810
                parse_error = True
 
811
                yacc.errok()
 
812
        else:
 
813
                print "SYNTAX ERROR AT EOF"
 
814
 
 
815
# -------------- RULES END ----------------
 
816
 
 
817
from ply import *
 
818
from qdlex import *
 
819
slparser = yacc.yacc(optimize=0, debug=1)
 
820
 
 
821
def slparse(data):
 
822
        slparser.error = 0
 
823
        p = slparser.parse(data)
 
824
        if slparser.error: return None
 
825
        return p
 
826
 
 
827
# returns tuple of the entire ast for PrettyPrinter
 
828
def makelist(ast):
 
829
        sl = [type(ast).__name__]
 
830
        if not isinstance(ast.children, list):  # if not list, node is leaf
 
831
                if isinstance(ast.children, astnode_t):
 
832
                        sl.append(makelist(ast.children))
 
833
                else:
 
834
                        sl.append(ast.children)
 
835
        elif len(ast.children):
 
836
                for n in ast.children:
 
837
                        if isinstance(n, astnode_t):
 
838
                                sl.append(makelist(n))
 
839
                        elif isinstance(n, list):
 
840
                                for n2 in n:
 
841
                                        if isinstance(n2, astnode_t):
 
842
                                                sl.append(makelist(n2))
 
843
                                        else:
 
844
                                                sl.append(n2)
 
845
                        else:
 
846
                                sl.append(n)
 
847
        return tuple(sl)
 
848
 
 
849
if __name__ == '__main__':
 
850
        argc = len(sys.argv)
 
851
        if argc == 1:
 
852
                print "Usage: %s [options] inputfile" % sys.argv[0]
 
853
                print "possible options:"
 
854
                print "-O optimize code (currently braindead optimization, don't expect miracles ;)"
 
855
                print "-S simplify register names"
 
856
                print "-P print original source and 'pretty print' the resulting AST"
 
857
        else:
 
858
                optimize, simplify, dopp = False, False, False
 
859
                if argc > 2:
 
860
                        for arg in sys.argv[1:]:
 
861
                                if arg[0] == '-':
 
862
                                        if arg[1] == 'O':
 
863
                                                optimize = True
 
864
                                        elif arg[1] == 'S':
 
865
                                                simplify = True
 
866
                                        elif arg[1] == 'P':
 
867
                                                dopp = True
 
868
                                else:   # filename, the end
 
869
                                        fname = arg
 
870
                                        break
 
871
                else:
 
872
                        fname = sys.argv[1]
 
873
                print "OPTIMIZE    :", optimize
 
874
                print "SIMPLIFY    :", simplify
 
875
                print "PRETTYPRINT :", dopp
 
876
                print "filename    : '%s'\n" % fname
 
877
                # try cpp pass
 
878
                import os
 
879
                tempname = fname + "_tmp"
 
880
                try:
 
881
                        os.system("cpp %s %s" % (fname, tempname))
 
882
                except:
 
883
                        print "[WARNING]: could not execute preprocessor, program might not compile!"
 
884
                else:
 
885
                        fname = tempname
 
886
                # read preprocessed file and compile
 
887
                try:
 
888
                        cppfile = open(tempname, 'r')
 
889
                except IOError:
 
890
                        print "Could not open file"
 
891
                except:
 
892
                        raise Exception()
 
893
                else:
 
894
                        data = cppfile.read()
 
895
                        cppfile.close()
 
896
                        if dopp:
 
897
                                print data, '\n'
 
898
                        ast = slparse(data)
 
899
                        if ast and not parse_error:
 
900
                                if dopp:        # 'pretty print' the AST
 
901
                                        print
 
902
                                        import pprint
 
903
                                        pp = pprint.PrettyPrinter()
 
904
                                        pp.pprint(makelist(ast))
 
905
                                #print "\nsimplify\n"
 
906
                                #astsimplify_t().visit(ast)
 
907
                                #if dopp:       # 'pretty print' the AST
 
908
                                #       pp.pprint(makelist(ast))
 
909
                                print "\nbuild\n"
 
910
                                compile_t(optimize, simplify).visit(ast)
 
911
                        else:
 
912
                                print "Error(s) occured"
 
913
                        print "\nDone"
 
914
                        os.remove(tempname)