~ubuntu-branches/ubuntu/maverick/blender/maverick

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Khashayar Naderehvandi, Khashayar Naderehvandi, Alessio Treglia
  • Date: 2009-01-22 16:53:59 UTC
  • mfrom: (14.1.1 experimental)
  • Revision ID: james.westby@ubuntu.com-20090122165359-v0996tn7fbit64ni
Tags: 2.48a+dfsg-1ubuntu1
[ Khashayar Naderehvandi ]
* Merge from debian experimental (LP: #320045), Ubuntu remaining changes:
  - Add patch correcting header file locations.
  - Add libvorbis-dev and libgsm1-dev to Build-Depends.
  - Use avcodec_decode_audio2() in source/blender/src/hddaudio.c

[ Alessio Treglia ]
* Add missing previous changelog entries.

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)