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
#------------------------------------------------------------------------------------------------------------------
19
# temporary extended path (relative), ply is not installed
20
sys.path.insert(0,"../..")
22
from slcompile import *
23
#from astsimplify import *
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)
29
# set when any parse/syntax error occured
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)
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
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:
53
val1 = var_dict[p[1].name]
54
if isinstance(val1, vector_t): p1_isvec = True
55
if p3_isnum or p3_isvec:
58
val2 = var_dict[p[3].name]
59
if isinstance(val2, vector_t): p3_isvec = True
61
if opr == '.' or opr == '^':
62
if not (p1_isvec and p3_isvec):
63
raise Exception("Expected two vectors for dot or cross operator")
65
return astNumConst_t(eval(val1.asString() + ".dot(" + val2.asString() + ')'), lexer.lineno)
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)
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)
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):
92
if p3_isnum or p3_isvec:
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)
101
return astAssignExpr_t(p[1], asgn, astVecConst_t(eval(val1.asString() + oper + "vector_t(" + str(val2) + ")"), lexer.lineno), lexer.lineno)
104
return astAssignExpr_t(p[1], asgn, astNumConst_t(eval(str(val1) + oper + str(val2)), lexer.lineno), lexer.lineno)
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)
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"]:
121
numargs = len(pcargs)
124
if isinstance(arg1, astNumConst_t):
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":
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)
140
elif pcname == "mod":
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)
156
#------------------------------------------------------------
158
start = 'source_file'
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']
162
precedence = [('nonassoc', 'PSEUDO_LOW_PRECEDENCE') , ('nonassoc', 'IF', 'ELSE')]
164
# -------------- RULES ----------------
166
def p_source_file(p):
167
'''source_file : definitions'''
168
p[0] = astProgram_t(p[1], lexer.lineno)
170
def p_definitions(p):
172
| definitions shader_definition
173
| definitions function_definition'''
175
if isinstance(p[1], list):
179
p[0] = [p[2]] # p[1] always undefined at first, not a list yet
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)
186
# default parameter detail for shader parameters is 'uniform'
188
for f in p[4].formals:
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
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)
202
# default parameter detail for function parameters is 'varying'
204
for f in p[4].formals:
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
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
219
# default parameter detail for function parameters is 'varying'
221
for f in p[5].formals:
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
231
'''body : '{' statements '}' '''
232
p[0] = astStatements_t(p[2], lexer.lineno)
234
def p_shader_type(p):
235
'''shader_type : LIGHT
242
# sets flag of the parameters default expressions, and removes the params from the var_dict
243
def setParamFlag(formals, pname):
245
shader = (pname == "shparam")
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)
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):
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")
269
def p_opt_func_formals(p):
270
'''opt_func_formals :
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")
279
'''formals : formal_variable_definitions
280
| formals ';' formal_variable_definitions '''
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)
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
295
if p[2][1] == "string":
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
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)
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)
314
'''typespec : opt_detail type'''
315
p[0] = [p[1], p[2]] # !!!
317
def p_def_expressions(p):
318
'''def_expressions : def_expression
319
| def_expressions ',' def_expression'''
326
def p_def_expression(p):
327
'''def_expression : IDENTIFIER
328
| IDENTIFIER '=' expression
329
| IDENTIFIER '[' INTCONST ']'
330
| IDENTIFIER '[' INTCONST ']' '=' array_init '''
333
p[0] = astDefExpression_t(p[1], None, None, lexer.lineno)
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
340
p[0] = astDefExpression_t(p[1], p[3], None, lexer.lineno)
342
p[0] = astDefExpression_t(p[1], p[3], p[6], lexer.lineno)
344
def p_array_expr_list(p):
345
'''array_expr_list : expression
346
| array_expr_list ',' expression'''
348
if isinstance(p[1], astNumConst_t):
349
p[0] = [True, [p[1].val]]
351
p[0] = [False, [p[1]]]
353
if isinstance(p[3], astNumConst_t):
354
p[1][1].append(p[3].val)
361
'''array_init : '{' array_expr_list '}' '''
362
p[0] = array_t(p[2][0], p[2][1])
368
if len(p) > 1: p[0] = p[1]
370
def p_opt_outputspec(p):
373
if len(p) > 1: p[0] = p[1]
375
def p_opt_externspec(p):
378
if len(p) > 1: p[0] = p[1]
383
if len(p) > 1: p[0] = p[1]
398
| statements statement
399
| statements variable_definitions ';'
400
| statements lexical_function_definition'''
402
if isinstance(p[1], list):
406
p[0] = [p[2]] # p[1] always undefined at first, not a list yet
408
def p_statement_1(p):
409
'''statement : assignexpr ';' '''
412
def p_statement_2(p):
413
'''statement : procedurecall ';' '''
416
def p_statement_3(p):
417
'''statement : RETURN expression ';' '''
418
p[0] = astReturn_t(p[2], lexer.lineno)
420
def p_statement_4(p):
421
'''statement : BREAK opt_integer ';' '''
422
p[0] = astBreak_t(p[2], lexer.lineno)
424
def p_statement_5(p):
425
'''statement : CONTINUE opt_integer ';' '''
426
p[0] = astContinue_t(p[2], lexer.lineno)
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)
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)
436
def p_statement_8(p):
437
'''statement : WHILE '(' relation ')' statement'''
438
p[0] = astFor_t(None, p[3], None, p[5], lexer.lineno)
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)
444
def p_statement_10(p):
445
'''statement : SOLAR '(' ')' statement'''
446
p[0] = astSolar_t(None, None, p[4], lexer.lineno)
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)
452
def p_statement_12(p):
453
'''statement : ILLUMINATE '(' expression ')' statement'''
454
p[0] = astIlluminate_t(p[3], None, None, p[5], lexer.lineno)
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)
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)
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)
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)
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)
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)
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):
489
#-------------------------------------------------------------------------------------------------------------
490
# extra statement rules to simplify handling of a few procedures
492
def p_statement_19(p):
493
'''statement : ROTATE '(' expression ',' expression ',' expression ')' ';'
494
| ROTATE '(' expression ',' expression ',' expression ',' expression ')' ';' '''
496
p[0] = astRotate_t(p[3], p[5], p[7], None, lexer.lineno)
498
p[0] = astRotate_t(p[3], p[5], p[7], p[9], lexer.lineno)
500
def p_statement_20(p):
501
'''statement : FRESNEL '(' expression ',' expression ',' expression ',' expression ',' expression ')' ';'
502
| FRESNEL '(' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ')' ';' '''
504
p[0] = astFresnel_t(p[3], p[5], p[7], p[9], p[11], None, None, lexer.lineno)
506
p[0] = astFresnel_t(p[3], p[5], p[7], p[9], p[11], p[13], p[15], lexer.lineno)
508
# filterstep handled separately as well because of optional parameterlist
510
'''filterstep : FILTERSTEP '(' expression ',' expression opt_paramlist ')'
511
| FILTERSTEP '(' expression ',' expression ',' expression opt_paramlist ')' '''
513
p[0] = astFilterstep_t(p[3], p[5], None, p[6], lexer.lineno);
515
p[0] = astFilterstep_t(p[3], p[5], p[7], p[8], lexer.lineno)
517
def p_opt_paramlist(p):
520
| opt_paramlist param'''
530
'''param : ',' STRINGCONST ',' expression'''
533
#-------------------------------------------------------------------------------------------------------------
535
def p_opt_integer(p):
538
if len(p) > 1: p[0] = p[1]
541
'''primary : INTCONST'''
542
p[0] = astNumConst_t(p[1], lexer.lineno)
545
'''primary : FLOATCONST'''
546
p[0] = astNumConst_t(p[1], lexer.lineno)
554
'''primary : IDENTIFIER'''
556
p[0] = astNumConst_t(3.1415926535897932384626433832795029, lexer.lineno)
558
p[0] = astID_t(p[1], None, lexer.lineno)
561
'''primary : IDENTIFIER '[' expression ']' '''
562
p[0] = astID_t(p[1], p[3], lexer.lineno)
565
'''primary : STRINGCONST'''
566
p[0] = astStringConst_t(p[1], lexer.lineno)
569
'''primary : procedurecall'''
573
'''primary : '(' expression ')' '''
577
'''primary : '(' expression ',' expression ',' expression ')'
578
| '(' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ',' expression ')' '''
580
for i in range(2, len(p)-1, 2):
581
const &= isinstance(p[i], astNumConst_t)
584
for i in range(2, len(p)-1, 2):
587
p[0] = astVecConst_t(vector_t(*tp), lexer.lineno)
589
p[0] = astMtxConst_t(matrix_t(*tp), lexer.lineno)
592
for i in range(2, len(p)-1, 2):
594
p[0] = astTuple_t(t, lexer.lineno)
597
'''unary_expr : primary
599
| typecast unary_expr'''
603
if isinstance(p[2], astNumConst_t):
605
p[2].children = -p[2].children # completely unnecessary, just for tree printing
608
p[0] = astUnaryExpr_t(p[1], p[2], lexer.lineno)
610
p[0] = astUnaryExpr_t(p[1], p[2], lexer.lineno)
613
'''dot_expr : unary_expr
614
| dot_expr '.' unary_expr'''
618
#p[0] = astBinop_t(p[1], p[2], p[3], lexer.lineno)
621
def p_multiplicative_expr(p):
622
'''multiplicative_expr : dot_expr
623
| multiplicative_expr '*' dot_expr
624
| multiplicative_expr '/' dot_expr'''
628
#p[0] = astBinop_t(p[1], p[2], p[3], lexer.lineno)
632
'''cross_expr : multiplicative_expr
633
| cross_expr '^' multiplicative_expr'''
637
#p[0] = astBinop_t(p[1], p[2], p[3], lexer.lineno)
640
def p_additive_expr(p):
641
'''additive_expr : cross_expr
642
| additive_expr '+' cross_expr
643
| additive_expr '-' cross_expr'''
647
#p[0] = astBinop_t(p[1], p[2], p[3], lexer.lineno)
650
def p_conditional_expr(p):
651
'''conditional_expr : additive_expr
652
| relation '?' additive_expr ':' conditional_expr'''
656
p[0] = astIfElse_t(p[1], p[3], p[5], True, lexer.lineno)
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)
674
| IDENTIFIER '[' expression ']' '''
676
p[0] = astID_t(p[1], None, lexer.lineno)
678
p[0] = astID_t(p[1], p[3], lexer.lineno)
680
def p_assign_expr(p):
681
'''assign_expr : conditional_expr
686
'''expression : assign_expr'''
690
'''relation : relation2 OR relation'''
691
p[0] = astRelation_t(p[1], p[2], p[3], lexer.lineno)
694
'''relation : relation2'''
697
def p_relation2_1(p):
698
'''relation2 : relation3 AND relation2'''
699
p[0] = astRelation_t(p[1], p[2], p[3], lexer.lineno)
701
def p_relation2_2(p):
702
'''relation2 : relation3'''
705
def p_relation3_1(p):
706
'''relation3 : '!' relation3'''
707
p[0] = astRelation_t(None, p[1], p[2], lexer.lineno)
709
def p_relation3_2(p):
710
'''relation3 : relation4'''
713
def p_relation4_1(p):
714
'''relation4 : '(' relation ')' '''
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)
730
def p_procedurecall(p):
731
'''procedurecall : IDENTIFIER '(' opt_proc_arguments ')' '''
732
direval = evalMathProc(p[1], p[3])
736
p[0] = astProcedureCall_t(p[1], p[3], lexer.lineno)
741
| COLOR opt_spacetype
742
| POINT opt_spacetype
743
| VECTOR opt_spacetype
744
| NORMAL opt_spacetype
745
| MATRIX opt_spacetype'''
751
def p_opt_spacetype(p):
754
if len(p) > 1: p[0] = astStringConst_t(p[1], lexer.lineno)
756
def p_opt_proc_arguments(p):
757
'''opt_proc_arguments :
759
if len(p) > 1: p[0] = p[1]
761
def p_proc_arguments(p):
762
'''proc_arguments : expression
763
| proc_arguments ',' expression'''
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)
774
def p_texture_type(p):
775
'''texture_type : TEXTURE
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)
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)
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)
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)
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)
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)
805
#### Catastrophic error handler
809
print "SYNTAX ERROR AT LINE %d BEFORE TOKEN: '%s'" % (p.lineno, p.value)
813
print "SYNTAX ERROR AT EOF"
815
# -------------- RULES END ----------------
819
slparser = yacc.yacc(optimize=0, debug=1)
823
p = slparser.parse(data)
824
if slparser.error: return None
827
# returns tuple of the entire ast for PrettyPrinter
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))
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):
841
if isinstance(n2, astnode_t):
842
sl.append(makelist(n2))
849
if __name__ == '__main__':
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"
858
optimize, simplify, dopp = False, False, False
860
for arg in sys.argv[1:]:
868
else: # filename, the end
873
print "OPTIMIZE :", optimize
874
print "SIMPLIFY :", simplify
875
print "PRETTYPRINT :", dopp
876
print "filename : '%s'\n" % fname
879
tempname = fname + "_tmp"
881
os.system("cpp %s %s" % (fname, tempname))
883
print "[WARNING]: could not execute preprocessor, program might not compile!"
886
# read preprocessed file and compile
888
cppfile = open(tempname, 'r')
890
print "Could not open file"
894
data = cppfile.read()
899
if ast and not parse_error:
900
if dopp: # 'pretty print' the AST
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))
910
compile_t(optimize, simplify).visit(ast)
912
print "Error(s) occured"