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

« back to all changes in this revision

Viewing changes to extern/qdune/slcompiler/slcompile.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
 
#-------------------------------------------------------------------------
2
 
# The actual shader compiler code
3
 
#
4
 
# Current code is too complex, should be split into
5
 
# separate 'simplify' and 'optimize' procedures.
6
 
# Current error checking can be misleading if the code uses include files
7
 
# or macros as far as the line number is concerned, since it actually
8
 
# relates to the preprocessed sourcecode file.
9
 
# Must be adapted to make use of the preprocessor #line declarations, TODO
10
 
# Of course if there are no include files, then it usually is ok.
11
 
# There is lots of ugly patch work and special case handling, as usual...
12
 
# Especially the handling of user-defined functions is a real mess.
13
 
# It should all be redone anyway, this is just a prototype after all, TODO
14
 
# (or not, all this is probably completely useless to blender...
15
 
#  at least, in its current form anyway)
16
 
#-------------------------------------------------------------------------
17
 
 
18
 
#-------------------------------------
19
 
# The following AST nodes are defined:
20
 
#-------------------------------------
21
 
#  1: astProgram_t(definitions)
22
 
#  3: astFormals_t(formals)
23
 
#  4: astShaderDef_t(shadertype, id, formals, body)
24
 
#  5: astFunctionDef_t(returntype, id, formals, body, lexfunc)
25
 
#  6: astNewScope_t(stmts)
26
 
#  7: astNumConst_t(val)
27
 
#  8: astStringConst_t(val)
28
 
#  9: astVecConst_t(val)
29
 
# 10: astMtxConst_t(val)
30
 
# 11: astIfElse_t(rel, if, else, iscond)
31
 
# 12: astExprList_t(expr_list, expr)
32
 
# 13: astDefExpression_t(id, array_length, def_init)
33
 
# 15: astParameter_t(output, typespec, exprs)
34
 
# 16: astVariable_t(extern, typespec, exprs)
35
 
# 17: astTuple_t(values)
36
 
# 18: astStatements_t(statements)
37
 
# 19: astReturn_t(returnval)
38
 
# 20: astBreak_t(level)
39
 
# 21: astContinue_t(level)
40
 
# 22: astFor_t(init_expr, rel, inc_expr, stmt)
41
 
# 23: astSolar_t(axis, angle, stmt)
42
 
# 24: astIlluminate_t(pos, axis, angle, stmt)
43
 
# 25: astIlluminance_t(cat, pos, axis, angle, stmt)
44
 
# 26: astID_t(name, array_idx)
45
 
# 27: astUnaryExpr_t(typecast, expr)
46
 
# 28: astBinop_t(expr1, op, expr2)
47
 
# 29: astAssignExpr_t(lhs, asgnop, assign_expr)
48
 
# 30: astRelation_t(expr1, relop, expr2)
49
 
# 31: astProcedureCall_t(name, args)
50
 
# 33: astTexture_t(tex_type, texfilechan, tex_args)
51
 
# 34: astTexFileChan_t(id, expr, array_idx)
52
 
# 35: astTexArgs_t(args)
53
 
# 36: astFilterstep_t(edge, s1, s2, paramlist)
54
 
# 37: astRotate_t(arg1, arg2, arg3)
55
 
# 38: astFresnel_t(I, N, eta, Kr, Kt, R, T)
56
 
#----------------------------------------------
57
 
# NOTE: All the above nodes also have an extra
58
 
# 'lineno' argument which holds the current
59
 
# line number in the source code.
60
 
#----------------------------------------------
61
 
 
62
 
 
63
 
# base node
64
 
class astnode_t(object):
65
 
        def visit(self, visitor):
66
 
                raise Exception('visit() not defined in ' + type(self).__name__)
67
 
 
68
 
# NOTE: 'children' is solely used to pretty print the AST
69
 
# (not strictly necessary even for that purpose, since could just use vars() or node.__dict__ directly,
70
 
#  but since it returns a dictionary, results are unordered)
71
 
# 'lineno' is the line number corresponding to the code line in the sourcecode, used for error reports.
72
 
class astProgram_t(astnode_t):
73
 
        def __init__(self, definitions, lineno):
74
 
                self.definitions = definitions
75
 
                self.lineno = lineno
76
 
                self.children = definitions
77
 
        def visit(self, visitor):
78
 
                visitor.visitProgram(self)
79
 
 
80
 
class astFormals_t(astnode_t):
81
 
        def __init__(self, formals, lineno):
82
 
                self.formals = formals
83
 
                self.lineno = lineno
84
 
                self.children = [formals]
85
 
        def visit(self, visitor):
86
 
                visitor.visitFormals(self)
87
 
 
88
 
class astShaderDef_t(astnode_t):
89
 
        def __init__(self, shadertype, _id, formals, body, lineno):
90
 
                self.shadertype = shadertype
91
 
                self.id = _id
92
 
                self.formals = formals
93
 
                self.body = body
94
 
                self.lineno = lineno
95
 
                self.children = [shadertype, _id, formals, body]
96
 
        def visit(self, visitor):
97
 
                visitor.visitShaderDef(self)
98
 
 
99
 
class astFunctionDef_t(astnode_t):
100
 
        def __init__(self, returntype, _id, formals, body, lexfunc, lineno):
101
 
                self.returntype = returntype
102
 
                self.id = _id
103
 
                self.formals = formals
104
 
                self.body = body
105
 
                self.lexfunc = lexfunc
106
 
                self.lineno = lineno
107
 
                self.children = [returntype, _id, formals, body, lexfunc]
108
 
        def visit(self, visitor):
109
 
                visitor.visitFunctionDef(self)
110
 
 
111
 
# only needed to keep track of scope changes
112
 
class astNewScope_t(astnode_t):
113
 
        def __init__(self, statements, lineno):
114
 
                self.statements = statements
115
 
                self.lineno = lineno
116
 
                self.children = statements
117
 
        def visit(self, visitor):
118
 
                visitor.visitNewScope(self)
119
 
 
120
 
# used for both floats and integers, val arg must be cast to the correct type
121
 
class astNumConst_t(astnode_t):
122
 
        def __init__(self, val, lineno):
123
 
                self.val = val
124
 
                self.lineno = lineno
125
 
                self.children = val
126
 
        def visit(self, visitor):
127
 
                visitor.visitNumConst(self)
128
 
 
129
 
class astStringConst_t(astnode_t):
130
 
        def __init__(self, val, lineno):
131
 
                self.val = val
132
 
                self.lineno = lineno
133
 
                self.children = val
134
 
        def visit(self, visitor):
135
 
                visitor.visitStringConst(self)
136
 
 
137
 
class astVecConst_t(astnode_t):
138
 
        def __init__(self, val, lineno):
139
 
                self.val = val
140
 
                self.lineno = lineno
141
 
                self.children = val
142
 
        def visit(self, visitor):
143
 
                visitor.visitVecConst(self)
144
 
 
145
 
class astMtxConst_t(astnode_t):
146
 
        def __init__(self, val, lineno):
147
 
                self.val = val
148
 
                self.lineno = lineno
149
 
                self.children = val
150
 
        def visit(self, visitor):
151
 
                visitor.visitMtxConst(self)
152
 
 
153
 
# 'iscond' designates the conditional operator
154
 
class astIfElse_t(astnode_t):
155
 
        def __init__(self, rel, _if, _else, iscond, lineno):
156
 
                self.rel = rel
157
 
                self._if = _if
158
 
                self._else = _else
159
 
                self.iscond = iscond
160
 
                self.lineno = lineno
161
 
                self.children = [rel, _if, _else, iscond]
162
 
        def visit(self, visitor):
163
 
                visitor.visitIfElse(self)
164
 
 
165
 
class astExprList_t(astnode_t):
166
 
        def __init__(self, expr_list, expr, lineno):
167
 
                self.expr_list = expr_list
168
 
                self.expr = expr
169
 
                self.lineno = lineno
170
 
                self.children = [expr_list, expr]
171
 
        def visit(self, visitor):
172
 
                visitor.visitExprList(self)
173
 
 
174
 
class astDefExpression_t(astnode_t):
175
 
        def __init__(self, _id, array_length, def_init, lineno):
176
 
                self.id = _id
177
 
                if array_length == None:
178
 
                        self.array_length = 0
179
 
                else:
180
 
                        self.array_length = array_length
181
 
                self.def_init = def_init
182
 
                # flag that indicates that this default expression is either part of a shader or function parameter declaration
183
 
                self.shparam = False
184
 
                self.lineno = lineno
185
 
                self.children = [_id, array_length, def_init]
186
 
        def visit(self, visitor):
187
 
                visitor.visitDefExpression(self)
188
 
 
189
 
class astDefExprList_t(astnode_t):
190
 
        def __init__(self, def_exprlist, lineno):
191
 
                self.def_exprlist = def_exprlist
192
 
                self.lineno = lineno
193
 
                self.children = [def_exprlist]
194
 
        def visit(self, visitor):
195
 
                visitor.visitDefExprList(self)
196
 
 
197
 
class astParameter_t(astnode_t):
198
 
        def __init__(self, output, typespec, exprs, lineno):
199
 
                self.output = output
200
 
                self.typespec = typespec
201
 
                self.exprs = exprs
202
 
                self.lineno = lineno
203
 
                self.children = [output, typespec, exprs]
204
 
        def visit(self, visitor):
205
 
                visitor.visitParameter(self)
206
 
 
207
 
class astVariable_t(astnode_t):
208
 
        def __init__(self, extern, typespec, expr, lineno):
209
 
                self.extern = extern
210
 
                self.typespec = typespec
211
 
                self.expr = expr
212
 
                self.lineno = lineno
213
 
                self.children = [extern, typespec, expr]
214
 
        def visit(self, visitor):
215
 
                visitor.visitVariable(self)
216
 
 
217
 
class astTuple_t(astnode_t):
218
 
        def __init__(self, values, lineno):
219
 
                self.values = values
220
 
                self.lineno = lineno
221
 
                self.children = values  # already list
222
 
        def visit(self, visitor):
223
 
                visitor.visitTuple(self)
224
 
 
225
 
class astStatements_t(astnode_t):
226
 
        def __init__(self, statements, lineno):
227
 
                self.statements = statements
228
 
                self.lineno = lineno
229
 
                self.children = [statements]
230
 
        def visit(self, visitor):
231
 
                visitor.visitStatements(self)
232
 
 
233
 
class astReturn_t(astnode_t):
234
 
        def __init__(self, returnval, lineno):
235
 
                self.returnval = returnval
236
 
                self.lineno = lineno
237
 
                self.children = returnval
238
 
        def visit(self, visitor):
239
 
                visitor.visitReturn(self)
240
 
 
241
 
class astBreak_t(astnode_t):
242
 
        def __init__(self, level, lineno):
243
 
                self.level = level
244
 
                self.lineno = lineno
245
 
                self.children = level
246
 
        def visit(self, visitor):
247
 
                visitor.visitBreak(self)
248
 
 
249
 
class astContinue_t(astnode_t):
250
 
        def __init__(self, level, lineno):
251
 
                self.level = level
252
 
                self.lineno = lineno
253
 
                self.children = level
254
 
        def visit(self, visitor):
255
 
                visitor.visitContinue(self)
256
 
 
257
 
class astFor_t(astnode_t):
258
 
        def __init__(self, init_expr, rel, inc_expr, stmt, lineno):
259
 
                self.init_expr = init_expr
260
 
                self.rel = rel
261
 
                self.inc_expr = inc_expr
262
 
                self.stmt = stmt
263
 
                self.lineno = lineno
264
 
                self.children = [init_expr, rel, inc_expr, stmt]
265
 
        def visit(self, visitor):
266
 
                visitor.visitFor(self)
267
 
 
268
 
class astSolar_t(astnode_t):
269
 
        def __init__(self, axis, angle, stmt, lineno):
270
 
                self.axis = axis
271
 
                self.angle = angle
272
 
                self.stmt = stmt
273
 
                self.lineno = lineno
274
 
                self.children = [axis, angle, stmt]
275
 
        def visit(self, visitor):
276
 
                visitor.visitSolar(self)
277
 
 
278
 
class astIlluminate_t(astnode_t):
279
 
        def __init__(self, pos, axis, angle, stmt, lineno):
280
 
                self.pos = pos
281
 
                self.axis = axis
282
 
                self.angle = angle
283
 
                self.stmt = stmt
284
 
                self.lineno = lineno
285
 
                self.children = [pos, axis, angle, stmt]
286
 
        def visit(self, visitor):
287
 
                visitor.visitIlluminate(self)
288
 
 
289
 
class astIlluminance_t(astnode_t):
290
 
        def __init__(self, cat, pos, axis, angle, stmt, lineno):
291
 
                self.cat = cat
292
 
                self.pos = pos
293
 
                self.axis = axis
294
 
                self.angle = angle
295
 
                self.stmt = stmt
296
 
                self.lineno = lineno
297
 
                self.children = [cat, pos, axis, angle, stmt]
298
 
        def visit(self, visitor):
299
 
                visitor.visitIlluminance(self)
300
 
 
301
 
class astID_t(astnode_t):
302
 
        def __init__(self, name, array_idx, lineno):
303
 
                self.name = name
304
 
                self.array_idx = array_idx
305
 
                self.lineno = lineno
306
 
                self.children = [name, array_idx]
307
 
        def visit(self, visitor):
308
 
                visitor.visitID(self)
309
 
 
310
 
class astUnaryExpr_t(astnode_t):
311
 
        def __init__(self, typecast, expr, lineno):
312
 
                self.typecast = typecast
313
 
                self.expr = expr
314
 
                self.lineno = lineno
315
 
                self.children = [typecast, expr]
316
 
        def visit(self, visitor):
317
 
                visitor.visitUnaryExpr(self)
318
 
 
319
 
class astBinop_t(astnode_t):
320
 
        def __init__(self, expr1, op, expr2, lineno):
321
 
                self.expr1 = expr1
322
 
                self.op = op
323
 
                self.expr2 = expr2
324
 
                self.lineno = lineno
325
 
                self.children = [expr1, op, expr2]
326
 
        def visit(self, visitor):
327
 
                visitor.visitBinop(self)
328
 
 
329
 
class astAssignExpr_t(astnode_t):
330
 
        def __init__(self, lhs, asgnop, assign_expr, lineno):
331
 
                self.lhs = lhs
332
 
                self.asgnop = asgnop
333
 
                self.assign_expr = assign_expr
334
 
                self.lineno = lineno
335
 
                self.children = [lhs, asgnop, assign_expr]
336
 
        def visit(self, visitor):
337
 
                visitor.visitAssignExpr(self)
338
 
 
339
 
class astRelation_t(astnode_t):
340
 
        def __init__(self, expr1, relop, expr2, lineno):
341
 
                self.expr1 = expr1
342
 
                self.relop = relop
343
 
                self.expr2 = expr2
344
 
                self.lineno = lineno
345
 
                self.children = [expr1, relop, expr2]
346
 
        def visit(self, visitor):
347
 
                visitor.visitRelation(self)
348
 
 
349
 
class astProcedureCall_t(astnode_t):
350
 
        def __init__(self, name, args, lineno):
351
 
                self.name = name
352
 
                self.args = args
353
 
                self.lineno = lineno
354
 
                self.children = [name, args]
355
 
        def visit(self, visitor):
356
 
                visitor.visitProcedureCall(self)
357
 
 
358
 
class astTexture_t(astnode_t):
359
 
        def __init__(self, tex_type, texfilechan, tex_args, lineno):
360
 
                self.tex_type = tex_type
361
 
                self.texfilechan = texfilechan
362
 
                self.tex_args = tex_args
363
 
                self.lineno = lineno
364
 
                self.children = [tex_type, texfilechan, tex_args]
365
 
        def visit(self, visitor):
366
 
                visitor.visitTexture(self)
367
 
 
368
 
class astTexFileChan_t(astnode_t):
369
 
        def __init__(self, _id, expr, array_idx, lineno):
370
 
                self.id = _id
371
 
                self.expr = expr
372
 
                self.array_idx = array_idx
373
 
                self.lineno = lineno
374
 
                self.children = [_id, expr, array_idx]
375
 
        def visit(self, visitor):
376
 
                visitor.visitTexFileChan(self)
377
 
 
378
 
class astTexArgs_t(astnode_t):
379
 
        def __init__(self, args, lineno):
380
 
                self.args = args
381
 
                self.lineno = lineno
382
 
                self.children = args
383
 
        def visit(self, visitor):
384
 
                visitor.visitTexArgs(self)
385
 
 
386
 
class astFilterstep_t(astnode_t):
387
 
        def __init__(self, edge, s1, s2, paramlist, lineno):
388
 
                self.edge = edge
389
 
                self.s1 = s1
390
 
                self.s2 = s2
391
 
                self.paramlist = paramlist
392
 
                self.lineno = lineno
393
 
                self.children = [edge, s1, s2, paramlist]
394
 
        def visit(self, visitor):
395
 
                visitor.visitFilterstep(self)
396
 
 
397
 
class astRotate_t(astnode_t):
398
 
        def __init__(self, arg1, arg2, arg3, lineno):
399
 
                self.arg1 = arg1
400
 
                self.arg2 = arg2
401
 
                self.arg3 = arg3
402
 
                self.arg4 = arg4
403
 
                self.lineno = lineno
404
 
                self.children = [arg1, arg2, arg3, arg4]
405
 
        def visit(self, visitor):
406
 
                visitor.visitRotate(self)
407
 
 
408
 
class astFresnel_t(astnode_t):
409
 
        def __init__(self, I, N, eta, Kr, Kt, R, T, lineno):
410
 
                self.I = I
411
 
                self.N = N
412
 
                self.eta = eta
413
 
                self.Kr = Kr
414
 
                self.Kt = Kt
415
 
                self.R = R
416
 
                self.T = T
417
 
                self.lineno = lineno
418
 
                self.children = [I, N, eta, Kr, Kt, R, T]
419
 
        def visit(self, visitor):
420
 
                visitor.visitFresnel(self)
421
 
 
422
 
#------------------------------------------------------------
423
 
 
424
 
class visitor_t(object):
425
 
        def visitProgram(self, node): raise Exception('visitProgram not defined in ' + type(self).__name__)
426
 
        def visitFormals(self, node): raise Exception('visitFormals not defined in ' + type(self).__name__)
427
 
        def visitShaderDef(self, node): raise Exception('visitShaderDef not defined in ' + type(self).__name__)
428
 
        def visitFunctionDef(self, node): raise Exception('visitFunctionDef not defined in ' + type(self).__name__)
429
 
        def visitNewScope(self, node): raise Exception('visitNewScope not defined in ' + type(self).__name__)
430
 
        def visitNumConst(self, node): raise Exception('visitNumConst not defined in ' + type(self).__name__)
431
 
        def visitStringConst(self, node): raise Exception('visitStringConst not defined in ' + type(self).__name__)
432
 
        def visitVecConst(self, node): raise Exception('visitVecConst not defined in ' + type(self).__name__)
433
 
        def visitMtxConst(self, node): raise Exception('visitMtxConst not defined in ' + type(self).__name__)
434
 
        def visitIfElse(self, node): raise Exception('visitIfElse not defined in ' + type(self).__name__)
435
 
        def visitExprList(self, node): raise Exception('visitExprList not defined in ' + type(self).__name__)
436
 
        def visitDefExpression(self, node): raise Exception('visitDefExpression not defined in ' + type(self).__name__)
437
 
        def visitDefExprList(self, node): raise Exception('visitDefExprList not defined in ' + type(self).__name__)
438
 
        def visitParameter(self, node): raise Exception('visitParameter not defined in ' + type(self).__name__)
439
 
        def visitVariable(self, node): raise Exception('visitVariable not defined in ' + type(self).__name__)
440
 
        def visitTuple(self, node): raise Exception('visitTuple not defined in ' + type(self).__name__)
441
 
        def visitStatements(self, node): raise Exception('visitStatements not defined in ' + type(self).__name__)
442
 
        def visitReturn(self, node): raise Exception('visitReturn not defined in ' + type(self).__name__)
443
 
        def visitBreak(self, node): raise Exception('visitBreak not defined in ' + type(self).__name__)
444
 
        def visitContinue(self, node): raise Exception('visitContinue not defined in ' + type(self).__name__)
445
 
        def visitFor(self, node): raise Exception('visitFor not defined in ' + type(self).__name__)
446
 
        def visitSolar(self, node): raise Exception('visitSolar not defined in ' + type(self).__name__)
447
 
        def visitIlluminate(self, node): raise Exception('visitIlluminate not defined in ' + type(self).__name__)
448
 
        def visitIlluminance(self, node): raise Exception('visitIlluminance not defined in ' + type(self).__name__)
449
 
        def visitID(self, node): raise Exception('visitID not defined in ' + type(self).__name__)
450
 
        def visitUnaryExpr(self, node): raise Exception('visitUnaryExpr not defined in ' + type(self).__name__)
451
 
        def visitBinop(self, node): raise Exception('visitBinop not defined in ' + type(self).__name__)
452
 
        def visitAssignExpr(self, node): raise Exception('visitAssignExpr not defined in ' + type(self).__name__)
453
 
        def visitRelation(self, node): raise Exception('visitRelation not defined in ' + type(self).__name__)
454
 
        def visitProcedureCall(self, node): raise Exception('visitProcedureCall not defined in ' + type(self).__name__)
455
 
        def visitTexture(self, node): raise Exception('visitTexture not defined in ' + type(self).__name__)
456
 
        def visitTexFileChan(self, node): raise Exception('visitTexFileChan not defined in ' + type(self).__name__)
457
 
        def visitTexArgs(self, node): raise Exception('visitTexArgs not defined in ' + type(self).__name__)
458
 
        def visitFilterstep(self, node): raise Exception('visitFilterstep not defined in ' + type(self).__name__)
459
 
        def visitRotate(self, node): raise Exception('visitRotate not defined in ' + type(self).__name__)
460
 
        def visitFresnel(self, node): raise Exception('visitFresnel not defined in ' + type(self).__name__)
461
 
 
462
 
#------------------------------------------------------------
463
 
 
464
 
# built-in functions
465
 
import slprocedures
466
 
 
467
 
#------------------------------------------------------------
468
 
 
469
 
class vector_t(object):
470
 
        def __init__(self, *args):
471
 
                L = len(args)
472
 
                if L == 0:      # defaults
473
 
                        self.v = (0.0, 0.0, 0.0)
474
 
                elif L == 1:    # single float, assign to all
475
 
                        self.v = (args[0], args[0], args[0])
476
 
                elif L == 3:
477
 
                        self.v = args
478
 
                else:
479
 
                        raise Exception("vector_t: Expected 3 values got %d -> %s" % (L, args))
480
 
                self.detail = None
481
 
                self.idx = 0
482
 
        def __iter__(self): return self
483
 
        def next(self):
484
 
                if self.idx == 3:
485
 
                        self.idx = 0
486
 
                        raise StopIteration
487
 
                self.idx += 1
488
 
                return self.v[self.idx - 1]
489
 
        # next math ops for direct evaluation where possible while building ast
490
 
        def __add__(self, o):
491
 
                return vector_t(self.v[0] + o.v[0], self.v[1] + o.v[1], self.v[2] + o.v[2])
492
 
        def __sub__(self, o):
493
 
                return vector_t(self.v[0] - o.v[0], self.v[1] - o.v[1], self.v[2] - o.v[2])
494
 
        def __mul__(self, o):
495
 
                if isinstance(o, float) or isinstance(o, int):
496
 
                        return vector_t(self.v[0] * o, self.v[1] * o, self.v[2] * o)
497
 
                else:
498
 
                        return vector_t(self.v[0] * o.v[0], self.v[1] * o.v[1], self.v[2] * o.v[2])
499
 
        def __div__(self, o):
500
 
                if isinstance(o, float) or isinstance(o, int):
501
 
                        return vector_t(self.v[0] / o, self.v[1] / o, self.v[2] / o)
502
 
                else:
503
 
                        return vector_t(self.v[0] / o.v[0], self.v[1] / o.v[1], self.v[2] / o.v[2])
504
 
        def __eq__(self, o):
505
 
                if self.v[0] == o.v[0] and self.v[1] == o.v[1] and self.v[2] == o.v[2]: return True
506
 
                return False
507
 
        def __ne__(self, o):
508
 
                if self.v[0] == o.v[0] or self.v[1] == o.v[1] or self.v[2] == o.v[2]: return False
509
 
                return True
510
 
        def dot(self, o):
511
 
                return self.v[0]*o.v[0] + self.v[1]*o.v[1] + self.v[2]*o.v[2]
512
 
        def cross(self, o):
513
 
                return vector_t(self.v[1]*o.v[2] - self.v[2]*o.v[1], self.v[2]*o.v[0] - self.v[0]*o.v[2], self.v[0]*o.v[1] - self.v[1]*o.v[0])
514
 
        # the following three funcs might seem odd, but asString() is used for direct evaluation using eval()
515
 
        # __str__() is used to print the values in a format needed for the compiler
516
 
        # and __repr__() is just useful as general object info, like when used with print
517
 
        def asString(self):
518
 
                return "vector_t(%g, %g, %g)" % (self.v[0], self.v[1], self.v[2])
519
 
        def __str__(self):
520
 
                return "%s %s %s" % self.v
521
 
        def __repr__(self):
522
 
                return "<vector_t %g %g %g>" % self.v
523
 
 
524
 
class matrix_t(object):
525
 
        def __init__(self, *args):
526
 
                L = len(args)
527
 
                if L == 0:      # defaults
528
 
                        self.m = (0.0, 0.0, 0.0, 0.0,  0.0, 0.0, 0.0, 0.0,  0.0, 0.0, 0.0, 0.0,  0.0, 0.0, 0.0, 0.0)
529
 
                elif L == 1:    # single float, assign to mtx.diagonal
530
 
                        self.m = (args[0], 0.0, 0.0, 0.0,  0.0, args[0], 0.0, 0.0,  0.0, 0.0, args[0], 0.0,  0.0, 0.0, 0.0, args[0])
531
 
                elif L == 16:
532
 
                        self.m = args
533
 
                else:
534
 
                        raise Exception("matrix_t: Expected 16 values got %d" % L)
535
 
                self.detail = None
536
 
                self.idx = 0
537
 
        def __iter__(self): return self
538
 
        def next(self):
539
 
                if self.idx == 16:
540
 
                        self.idx = 0
541
 
                        raise StopIteration
542
 
                self.idx += 1
543
 
                return self.m[self.idx - 1]
544
 
        def __str__(self):
545
 
                return "%s %s %s %s  %s %s %s %s  %s %s %s %s  %s %s %s %s" % self.m
546
 
        def __repr__(self):
547
 
                return "<matrix_t %g %g %g %g  %g %g %g %g  %g %g %g %g  %g %g %g %g>" % self.m
548
 
 
549
 
# function parameter flags used in the constant_t, register_t, variable_t class below
550
 
FF_OUTPUT = 1   # 'output' declared variable, so may be modified by function
551
 
FF_FPARAM = 2   # variable is a function parameter
552
 
 
553
 
# register class
554
 
# type is one char of 'fbmvs' for float, bool, matrix, vector and string respectively (vector is also used for color/point & normal)
555
 
# detail is either 'u' or 'v', for uniform  or varying
556
 
# number is the register number used as its id
557
 
class register_t(object):
558
 
        def __init__(self, _type, detail, number):
559
 
                self.type = _type
560
 
                self.detail = detail
561
 
                self.number = number
562
 
                # function flags, not set directly here in ctor
563
 
                self.flags = 0  # NOTE: mutable, do not use for comparisons or hashing!
564
 
        def __eq__(self, other):
565
 
                if type(self) != type(other): return False
566
 
                if other.type == self.type and other.detail == self.detail and other.number == self.number: return True
567
 
                return False
568
 
        def __ne__(self, other):
569
 
                if type(self) != type(other): return True
570
 
                if other.type != self.type and other.detail != self.detail and other.number != self.number: return True
571
 
                return False
572
 
        def  __hash__(self):
573
 
                return hash(self.type + self.detail) ^ self.number
574
 
        # String representation for registers and constants are somewhat complicated, both can be created and 'deleted' at any time.
575
 
        # This means that a simple '$number' representation will not look right, multiple registers/constants with the same name might be the result,
576
 
        # (They are still unique since its hash value is based on all its members, only the names will look the same)
577
 
        # Therefore here the name is created using all the important members (besides aforementioned reason, it also makes it more readable for 'visual debugging' purposes ;),
578
 
        # optionally after the code has been built all can be converted to the simpler name if desired ('simplify_regnames' option).
579
 
        def __str__(self):
580
 
                return "$r%s%d" % (self.type + self.detail, self.number)
581
 
        def __repr__(self):
582
 
                if self.flags:
583
 
                        return "<register_t FUNC $r%s%d>" % (self.type + self.detail, self.number)
584
 
                return "<register_t $r%s%d>" % (self.type + self.detail, self.number)
585
 
 
586
 
# constant value register class
587
 
# type is one char of 'fbmvs' for float, bool, matrix, vector and string respectively (vector is also used for color/point & normal)
588
 
# number is the register number used as its id
589
 
# val is its assigned value
590
 
# refc is its refrerence count (numbers can be used more than once in the program, so it can not be removed until the ref.count has reached 0)
591
 
# constants are always uniform, and also have global scope
592
 
class constant_t(object):
593
 
        def __init__(self, _type, number, val):
594
 
                self.type = _type
595
 
                self.number = number
596
 
                self.val = val
597
 
                self.refc = 1   # NOTE: mutable, do not use for comparisons or hashing!
598
 
                self.flags = 0  # NOTE: mutable, do not use for comparisons or hashing!
599
 
        def __eq__(self, other):
600
 
                if type(self) != type(other): return False
601
 
                if other.type == self.type and other.number == self.number and other.val == self.val: return True
602
 
                return False
603
 
        def __ne__(self, other):
604
 
                if type(self) != type(other): return True
605
 
                if other.type != self.type and other.number != self.number and other.val != self.val: return True
606
 
                return False
607
 
        def  __hash__(self):
608
 
                return hash(self.type) ^ self.number ^ hash(self.val)
609
 
        def __str__(self):
610
 
                return "$c%s%d" % (self.type, self.number)
611
 
        def __repr__(self):
612
 
                return "<constant_t $c%s%d>" % (self.type, self.number)
613
 
 
614
 
# variable class
615
 
# type is one char of 'fmvcpns' for float, matrix, vector, color, point, normal, and string respectively (bool not used here, temp or const register only)
616
 
# detail is either 'u' or 'v', for uniform  or varying
617
 
# name is the name of the variable
618
 
# arlen is an optional array length
619
 
# scope is an integer indicating the scope level of the variable
620
 
# flags is an integer bitmask, see above FF_ list
621
 
class variable_t(object):
622
 
        def __init__(self, _type, detail, name, arlen, scope):
623
 
                self.type = _type
624
 
                self.detail = detail
625
 
                self.name = name
626
 
                self.arlen = arlen
627
 
                self.scope = scope
628
 
                self.flags = 0  # NOTE: mutable, do not use for comparisons or hashing!
629
 
        def __eq__(self, other):
630
 
                if type(self) != type(other): return False
631
 
                if other.type == self.type and other.detail == self.detail and other.name == self.name and other.arlen == self.arlen and other.scope == self.scope: return True
632
 
                return False
633
 
        def __ne__(self, other):
634
 
                if type(self) != type(other): return True
635
 
                if other.type != self.type and other.detail != self.detail and other.name != self.name and other.arlen != self.arlen and other.scope != self.scope: return True
636
 
                return False
637
 
        def __hash__(self):
638
 
                return hash(self.name + self.type + self.detail) ^ self.arlen ^ hash(self.scope)
639
 
        def __str__(self):
640
 
                #if self.scope: return "%s_%d" % (self.name, self.scope)
641
 
                return self.name
642
 
        def __repr__(self):
643
 
                ar = ""
644
 
                if self.arlen: ar = '[' + str(self.arlen) + ']'
645
 
                st = ""
646
 
                if self.flags: st = "FUNC"
647
 
                if self.scope: return "<variable_t %s '%s_%d%s'>" % (st, self.name, self.scope, ar)
648
 
                return "<variable_t %s '%s'>" % (st, self.name + ar)
649
 
 
650
 
# array class
651
 
class array_t(object):
652
 
        def __init__(self, isconst, values):
653
 
                self.isconst = isconst
654
 
                self.values = values
655
 
                self.idx = 0
656
 
        def __iter__(self): return self
657
 
        def next(self):
658
 
                if self.idx == len(self.values):
659
 
                        self.idx = 0
660
 
                        raise StopIteration
661
 
                self.idx += 1
662
 
                return self.values[self.idx - 1]
663
 
        def __repr__(self):
664
 
                return "<array_t %s>" % self.values
665
 
 
666
 
class compile_t(visitor_t):
667
 
        # used for conversion cpn types to v
668
 
        cpn_set = "cpn"
669
 
        # dictionary of all possible typecasts.
670
 
        # this is more tolerant than most, since basically the typecast is based on the type size,
671
 
        # not its actual type, so a color can be cast to a normal for instance.
672
 
        typecast_dict = {'f': "fcpvnm", 's': 's', 'c': "cpvn", 'p': "cpvn", 'v': "cpvn", 'n': "cpvn", 'm': 'm'}
673
 
        # probably more correct version of the above, though only difference is that a color cannot be cast to anything but itself, but not currently used
674
 
        #typecast_dict = {'f': "fcpvnm", 's': 's', 'c': 'c', 'p': "pvn", 'v': "pvn", 'n': "pvn", 'm': 'm'}
675
 
        # dictionary to expand type character to full type name
676
 
        fulltype = {'f': "float", 'b': "bool", 's': "string", 'c': "color", 'p': "point", 'v': "vector", 'n': "normal", 'm': "matrix", '*': "void"}
677
 
        # same for detail type
678
 
        fulldetail = {'u': "uniform", 'v': "varying"}
679
 
        # relation operator
680
 
        relop_dict = {'<': "lt", "<=": "le", "==" : "eq", "!=": "ne", ">=": "ge", '>': "gt"}
681
 
        # 'not' of relation operator, needed for relation expression of for(){} loops
682
 
        notrelop_dict = {'<': "ge", "<=": "gt", "==" : "ne", "!=": "eq", ">=": "lt", '>': "le"}
683
 
        # as above, but using the actual operator name as key, only used in visitFor()
684
 
        w_notrelop_dict = {"lt": "ge", "le": "gt", "eq" : "ne", "ne": "eq", "ge": "lt", "gt": "le"}
685
 
 
686
 
        def __init__(self, optimize = True, simplify_regnames = True):
687
 
                # the value of all variable dicts is a string designating the type and detail,
688
 
                # it consists of the type character (one of 'cpvnfsm') followed by the detail type, 'v' or 'u' for either 'varying' or 'uniform'
689
 
 
690
 
                # set of all registers used (sets require python version >= 2.4, though could just as well use dictionary with any arbitrary value)
691
 
                self.tempregs = set()
692
 
                # dictionary to keep track of register usage count, if 0, can be removed, used for optimization code
693
 
                self.reguse = {}
694
 
                # set of all variables defined in the shader
695
 
                self.variables = set()
696
 
                # this dict is used to make sure variable names are unique, since it is possible that variables have same names but different types/detail/scope
697
 
                self.allnames = {}
698
 
                # the set of global variables used by the shader
699
 
                self.globals_used = set()
700
 
                # list of lists of variable dictionaries ordered according to scope, global first, top is current local scope
701
 
                self.scopes = []
702
 
                # dict. of constants, one where value is the key, and for the other the key is its associated register
703
 
                self.const_byval = {}
704
 
                self.const_byreg = {}
705
 
                # dictionary of all parameters, value is a list containing the type and detail string and its default value if it has one
706
 
                self.params = {}
707
 
                # initialization code
708
 
                self.initcode = []
709
 
                # main code
710
 
                self.code = []
711
 
                # dictionary of user-defined functions
712
 
                self.userfuncs = {}
713
 
                # temporary, a list, only available while processing function parameters list to keep the parameters in order
714
 
                self.funcparams = None
715
 
                # dictionary of variables declared 'extern', only used when processing functions
716
 
                self.vars_extern ={}
717
 
                # flags that are set when inside a userdefined function body and handling 'extern' var.declarations
718
 
                self.infuncbody = False
719
 
                self.islexfunc = False
720
 
                self.externvar = False
721
 
                # the intermediate stack, from which values are popped for a register based representation
722
 
                # NOTE: only call newreg() *after* first popping all args of the stack (in reverse order!)
723
 
                self.stack = []
724
 
                # registers are unlimited and denoted by '$number', so to keep track of all currently 'allocated' registers, only need a simple counter
725
 
                self.curreg = 0
726
 
                # since constants are handled separately, they need their own counter
727
 
                self.const_curreg = 0
728
 
                # label counter
729
 
                self.curlabel = 0
730
 
                # current type and detail of a default declaration as a two-char string
731
 
                self.typedet = None
732
 
                # current scope level
733
 
                self.curscope = 0
734
 
                # flag that is set when currently evaluating the lhs of an assignment (needed because of array element assignment, lhs/rhs are handled differently)
735
 
                self.asgn_lhs = False
736
 
                # flag when set indicates rhs of assigment is also an assignment (eg. 'a = b = c = d = 1.23;')
737
 
                self.chain_asgn = False
738
 
                # the name of the shader which is also used as the name of the output file + ".sqd"
739
 
                self.shader_name = None
740
 
                # stack that keeps track of start & end labels in for/while loops, needed for 'continue' & 'break' statements
741
 
                self.looplabels = []
742
 
                # If 'optimize' is true, try to optimize the code a bit
743
 
                self.optimize = optimize
744
 
                # 'simplify_regnames' when true will simplify the register names to a simple '$number' representation, see register_t and constant_t classes for more info
745
 
                self.simplify_regnames = simplify_regnames
746
 
 
747
 
        # return new register name of type t and detail d, where t is 'f', 's', 'v' or 'm' (c, p, or n are converted to vector), and d is either 'u' or 'v'
748
 
        def newreg(self, t, d):
749
 
                if t in self.cpn_set: t = 'v'
750
 
                self.curreg += 1
751
 
                reg = register_t(t, d, self.curreg)
752
 
                # update or add register usage count
753
 
                if reg in self.tempregs:
754
 
                        self.reguse[reg] += 1
755
 
                else:
756
 
                        self.reguse[reg] = 0
757
 
                self.tempregs.add(reg)
758
 
                return reg
759
 
 
760
 
        # 'delete' register
761
 
        def delreg(self):
762
 
                self.curreg -= 1
763
 
                assert self.curreg >= 0 # if not positive, there must be some error somewhere
764
 
 
765
 
        # return a new constant register of type t and value v
766
 
        def newconst(self, t, v):
767
 
                if self.const_byval.has_key(v): # already defined, inc ref.count
768
 
                        creg = self.const_byval[v]
769
 
                        creg.refc += 1
770
 
                else:   # new constant
771
 
                        self.const_curreg += 1
772
 
                        creg = constant_t(t, self.const_curreg, v)
773
 
                        self.const_byval[v] = creg
774
 
                        self.const_byreg[creg] = v
775
 
                return creg
776
 
 
777
 
        # 'delete' a constant register, returns its value
778
 
        def delconst(self, creg):
779
 
                val = self.const_byreg[creg]
780
 
                creg.refc -= 1
781
 
                if creg.refc == 0:
782
 
                        self.const_byreg.pop(creg)
783
 
                        self.const_byval.pop(val)
784
 
                        self.const_curreg -= 1
785
 
                return val
786
 
 
787
 
        # returns type and detail as two char pair of given variable
788
 
        # converts 'cpn' to 'v' if 'convert' true
789
 
        def typeof(self, var, convert = True):
790
 
                if isinstance(var, str) and var[0] == '"':
791
 
                        return 'su'
792
 
                elif self.params.has_key(var):
793
 
                        tp = self.params[var][0].lower()
794
 
                elif var in self.tempregs:
795
 
                        tp = var.type + var.detail
796
 
                elif self.const_byreg.has_key(var):
797
 
                        tp = var.type  + 'u'
798
 
                elif isinstance(var, register_t):
799
 
                        tp = var.type + var.detail
800
 
                elif isinstance(var, constant_t):
801
 
                        tp = var.type + 'u'
802
 
                elif isinstance(var, variable_t):
803
 
                        tp = var.type + var.detail
804
 
                else:
805
 
                        # search all variable dictionaries starting at the current scope
806
 
                        # first match is returned
807
 
                        tp = None
808
 
                        for scopelist in reversed(self.scopes):
809
 
                                for vardict in reversed(scopelist):
810
 
                                        if vardict.has_key(var):
811
 
                                                tv = vardict[var]
812
 
                                                tp = tv.type + tv.detail
813
 
                                                break
814
 
                        if tp == None:
815
 
                                raise Exception("typeof(): Cannot determine type of: '%s' " % var)
816
 
                # convert cpn to v
817
 
                if convert:
818
 
                        if tp[0] in self.cpn_set: tp = 'v' + tp[1]
819
 
                return tp
820
 
 
821
 
        # push new item on stack
822
 
        def push(self, item):
823
 
                self.stack.append(item)
824
 
 
825
 
        # pop and return item from stack
826
 
        def pop(self):
827
 
                if len(self.stack) == 0:
828
 
                        raise Exception("Empty stack!")
829
 
                item = self.stack.pop()
830
 
                # if popping a register (not constant), 'delete' it, UNLESS it is currently a function parameter (must remain on stack until function exit)
831
 
                if isinstance(item, register_t) and ((item.flags & FF_FPARAM) == 0):
832
 
                        self.delreg()
833
 
                return item
834
 
 
835
 
        # returns top item on stack without popping it
836
 
        def top(self):
837
 
                return self.stack[-1]
838
 
 
839
 
        def visitProgram(self, node):
840
 
                for d in node.definitions:
841
 
                        d.visit(self)
842
 
 
843
 
        def visitFormals(self, node):
844
 
                if node.formals:
845
 
                        for f in node.formals:
846
 
                                f.visit(self)
847
 
 
848
 
        def visitShaderDef(self, node):
849
 
                self.shader_name = node.id      # used for output file
850
 
                self.code.append(node.shadertype + " " + node.id)
851
 
 
852
 
                # now that the shader type is known, add the dictionary of predefined variables that are accessible to the shader,
853
 
                # (note that for instance in the case of a surface shader, and it redefines the output variables Ci and or Oi in the main scope, the shader will be pretty much useless...)
854
 
                if node.shadertype == "surface":
855
 
                        predefs = {"Cs": "cv", "Os": "cv", "dPdu": "vv", "dPdv": "vv", "P": "pv", "N": "nv", "Ng": "nv",
856
 
                                   "u": "fv", "v": "fv", "du": "fv", "dv": "fv", "s": "fv", "t": "fv", "L": "vv",
857
 
                                   "Cl": "cv", "Ol": "cv", "E": "pu", "I": "vv",
858
 
                                   "ncomps": "fu", "time": "fu", "dtime": "fu", "dPdtime": "fv",
859
 
                                   "Ci": "cv", "Oi": "cv"}
860
 
                elif node.shadertype == "light":
861
 
                        predefs = {"dPdu": "vv", "dPdv": "vv", "P": "pv", "N": "nv", "Ng": "nv",
862
 
                                   "u": "fv", "v": "fv", "du": "fv", "dv": "fv", "s": "fv", "t": "fv", "L": "vv",
863
 
                                   "Ps": "pv", "Cl": "cv", "Ol": "cv", "E": "pu",
864
 
                                   "ncomps": "fu", "time": "fu", "dtime": "fu"}
865
 
                elif node.shadertype == "displacement":
866
 
                        predefs = {"dPdu": "vv", "dPdv": "vv", "P": "pv", "N": "nv", "Ng": "nv",
867
 
                                   "u": "fv", "v": "fv", "du": "fv", "dv": "fv", "s": "fv", "t": "fv",
868
 
                                   "E": "pu", "I": "vv",
869
 
                                   "ncomps": "fu", "time": "fu", "dtime": "fu",
870
 
                                   "Ci": "cv", "Oi": "cv"}
871
 
                elif node.shadertype == "volume":
872
 
                        predefs = {"Cs": "cv", "Os": "cv", "dPdu": "vv", "dPdv": "vv", "P": "pv", "N": "nv", "Ng": "nv",
873
 
                                   "u": "fv", "v": "fv", "du": "fv", "dv": "fv", "s": "fv", "t": "fv", "L": "vv",
874
 
                                   "Ps": "pv", "Cl": "cv", "Ol": "cv", "E": "pu", "I": "vv",
875
 
                                   "ncomps": "fu", "time": "fu", "dtime": "fu", "dPdtime": "fv",
876
 
                                   "alpha": "fu", "Ci": "cv", "Oi": "cv"}
877
 
                elif node.shadertype == "imager":
878
 
                        predefs = {"P": "pv", "ncomps": "fu", "time": "fu", "dtime": "fu",
879
 
                                   "alpha": "fu", "Ci": "cv", "Oi": "cv"}
880
 
                else:   # never happens
881
 
                        raise Exception("Line %d: Unknown shader type?!? -> '%s'" % node.lineno, node.shadertype)
882
 
                d = {}
883
 
                for name in predefs:
884
 
                        td = predefs[name]
885
 
                        newvar = variable_t(td[0], td[1], name, 0, None)        # note: scope set to None, indicates global var.
886
 
                        d[name] = newvar
887
 
                        # add names to variable name list as well to make sure no duplicates are created
888
 
                        self.allnames[name] = 0 # dict is empty yet, so this is the first entry
889
 
                self.scopes.append([d])
890
 
 
891
 
                if node.formals: node.formals.visit(self)
892
 
                # add the main code start label if initialization code was created,
893
 
                # always 1 (or should be anyway, no jumps in initcode possible as far as I can see..)
894
 
                if len(self.initcode):
895
 
                        self.curlabel += 1
896
 
                        self.code.append(["@%d" % self.curlabel, None])
897
 
                if node.body:
898
 
                        self.curscope = 0       # main scope
899
 
                        self.scopes.append([{}])
900
 
                        node.body.visit(self)
901
 
                        self.scopes.pop()
902
 
 
903
 
        def visitFunctionDef(self, node):
904
 
                # Store the node into a table, later when the function is called it will be expanded inline.
905
 
                # 'argtypes' holds the function parameter types and detail,
906
 
                # except the first, which is only the returntype char, detail is not known in advance.
907
 
                # Return type 'v' is reserved for vector, '*' is for 'void' (also used to indicate 'variadic' when used in func.args).
908
 
                # Uppercase chars mean the variable is an 'output' declared var, so may be modified by the function.
909
 
                if node.returntype == "void":
910
 
                        argtypes = ['*']
911
 
                else:
912
 
                        if node.lexfunc:
913
 
                                argtypes = [node.returntype[1][0]]
914
 
                        else:
915
 
                                argtypes = [node.returntype[0]]
916
 
                        if argtypes[0] in self.cpn_set: argtypes[0] = 'v'
917
 
                varnames = []   # variable names used in the function, only used to map to arguments
918
 
                if node.formals:
919
 
                        # declare list to temporarily store the function parameters in order
920
 
                        self.funcparams = []
921
 
                        fdict = {}
922
 
                        self.scopes.append([fdict])
923
 
                        node.formals.visit(self)
924
 
                        for vname in self.funcparams:
925
 
                                varnames.append(vname)
926
 
                                var = fdict[vname]
927
 
                                # if variable is 'output', flag argument typechar for handleUserFunc() below by making it uppercase
928
 
                                if var.flags & FF_OUTPUT:
929
 
                                        td = self.typeof(var)
930
 
                                        argtypes.append(td[0].upper() + td[1])
931
 
                                else:
932
 
                                        argtypes.append(self.typeof(var))
933
 
                        # remove the dictionary again
934
 
                        self.scopes.pop()
935
 
                        # temporary var.list can also be removed
936
 
                        self.funcparams = None
937
 
                self.userfuncs[node.id] = [argtypes, varnames, node]
938
 
 
939
 
        # used in visitProcedureCall() for userdefined functions
940
 
        def handleUserFunc(self, funcdef):
941
 
                argtypes, varnames, node = funcdef
942
 
                if node.body:
943
 
                        # new temporary scope, no reference to any previous scope, except for 'extern' declared vars
944
 
                        funcparams = {}
945
 
                        for i in range(len(varnames)):
946
 
                                vname = varnames[i]
947
 
                                exp_tp = argtypes[i + 1]
948
 
                                var = self.stack[-(i+1)]        # no pop yet, since arguments are used directly, done after all func.params processed, see below
949
 
                                var_tp = self.typeof(var)
950
 
                                output = False
951
 
                                # type of variable must be exected type
952
 
                                if exp_tp[0].isupper():
953
 
                                        output = True
954
 
                                        exp_tp = exp_tp[0].lower() + exp_tp[1]
955
 
                                # types must be same
956
 
                                if var_tp[0] != exp_tp[0]:
957
 
                                        raise Exception("Line %d: function '%s' argument '%s' type '%s' not same as function parameter '%s' of type '%s'" % (node.lineno, node.id, repr(var), var_tp[0], vname, exp_tp[0]))
958
 
                                # if parameter detail is uniform, then argument must also be uniform, anything else ok
959
 
                                if exp_tp[1] == 'u' and var_tp[1] != 'u':
960
 
                                        raise Exception("Line %d: function '%s' argument '%s' is varying but function parameter '%s' expects uniform" % (node.lineno, node.id, repr(var), vname))
961
 
                                newvar = var
962
 
                                if not isinstance(var, str):
963
 
                                        newvar.flags |= FF_FPARAM
964
 
                                        if output:
965
 
                                                assert isinstance(var, constant_t) == False     # constants can never be output
966
 
                                                newvar.flags |= FF_OUTPUT
967
 
                                funcparams[vname] = newvar
968
 
                        # all params done, now can pop values
969
 
                        for fp in funcparams.values():
970
 
                                self.pop()
971
 
                        # prepare for the function body
972
 
                        self.curscope += 1
973
 
                        pifb = self.infuncbody
974
 
                        self.infuncbody = True
975
 
                        pilf = self.islexfunc
976
 
                        self.islexfunc = node.lexfunc
977
 
                        if node.lexfunc:
978
 
                                self.scopes[-1].append(funcparams)      # add to parent scope
979
 
                        else:
980
 
                                self.scopes.append([funcparams])        # new scope
981
 
                        last_externs = self.vars_extern.copy()
982
 
                        node.body.visit(self)
983
 
                        # reset everything
984
 
                        self.vars_extern = last_externs
985
 
                        for fp in funcparams.values():
986
 
                                if not isinstance(fp, str):
987
 
                                        if fp.flags & FF_FPARAM: fp.flags = 0
988
 
                        self.islexfunc = pilf
989
 
                        self.infuncbody = pifb
990
 
                        if node.lexfunc:
991
 
                                self.scopes[-1].pop()
992
 
                        else:
993
 
                                self.scopes.pop()
994
 
                        self.curscope -= 1
995
 
 
996
 
        def visitNewScope(self, node):
997
 
                self.curscope += 1
998
 
                self.scopes[-1].append({})
999
 
                node.statements.visit(self)
1000
 
                # delete all variables now out of scope
1001
 
                self.scopes[-1].pop()
1002
 
                self.curscope -= 1
1003
 
 
1004
 
        def visitNumConst(self, node):
1005
 
                self.push(self.newconst('f', node.val))
1006
 
 
1007
 
        def visitVecConst(self, node):
1008
 
                self.push(self.newconst('v', node.val))
1009
 
 
1010
 
        def visitMtxConst(self, node):
1011
 
                self.push(self.newconst('m', node.val))
1012
 
 
1013
 
        def visitStringConst(self, node):
1014
 
                self.push(self.newconst('s', node.val))
1015
 
 
1016
 
        # only used in visitIfElse_t() below, tries to find out type & detail from name string
1017
 
        def typefromstring(self, s):
1018
 
                if s[0] == '$':
1019
 
                        if s[1] == 'c':
1020
 
                                return s[2] + 'u'
1021
 
                        return s[2] + s[3]
1022
 
                else:
1023
 
                        try:
1024
 
                                td = self.typeof(s)
1025
 
                        except:
1026
 
                                # try variable list, have to do linear search, not indexed by name
1027
 
                                for v in self.variables:
1028
 
                                        if str(v) == s: return v.type + v.detail
1029
 
                                # nothing...
1030
 
                                raise Exception("Cannot determine type from string '%s'" % s)
1031
 
                        else:
1032
 
                                return td
1033
 
 
1034
 
        def visitIfElse(self, node):
1035
 
                nrel = node.rel
1036
 
                nrel.visit(self)
1037
 
                if node.iscond: # conditional operator, has both if/else
1038
 
                        node._if.visit(self)
1039
 
                        node._else.visit(self)
1040
 
                        op3, op2, op1 = self.pop(), self.pop(), self.pop()
1041
 
                        op1t, op2t, op3t = self.typeof(op1), self.typeof(op2), self.typeof(op3)
1042
 
                        # op2/op3 must be same type/detail
1043
 
                        if op2t == op3t:
1044
 
                                lhs = self.newreg(op2t[0], op1t[1])     # note: lhs must be detail of boolean here, it indicates detail of the actual statement lhs
1045
 
                                self.code.append(["cmov%s" % op2t[0], "%s %s %s %s" % (lhs, op1, op2, op3)])
1046
 
                                self.push(lhs)
1047
 
                        else:   # not same type/detail, have to do full if/else
1048
 
                                lhs = self.newreg(op2t[0], op2t[1])
1049
 
                                self.code.append(["cond_push", "%s" % op1])
1050
 
                                self.code.append(["mov%s" % (op2t[0] + op2t[0]), "%s %s" % (lhs, op2)])
1051
 
                                self.code.append(["cond_else", None])
1052
 
                                self.code.append(["mov%s" % (op2t[0] + op2t[0]), "%s %s" % (lhs, op3)])
1053
 
                                self.code.append(["cond_pop", None])
1054
 
                                self.push(lhs)
1055
 
                        return
1056
 
                # simple optimization, if relation expression operands are both uniform, can replace it by single conditional jump
1057
 
                # (This might in fact not be an optimization at all, performance may actually be worse, have to test, not sure)
1058
 
                # update: ok, tested, it is definitely faster ('uberlight' shader, about 4 sec. less)
1059
 
                if self.optimize and nrel.relop != "&&" and nrel.relop != "||" and nrel.relop != '!':   # not logic stmts
1060
 
                        last_codeline = self.code[-1]
1061
 
                        last_oprs = last_codeline[1].split()
1062
 
                        # in this case, have to determine type/detail from actual name string if register name ('$' is first char)
1063
 
                        lop1t, lop2t = self.typefromstring(last_oprs[1]), self.typefromstring(last_oprs[2])
1064
 
                        if lop1t[1] == 'u' and lop2t[1] == 'u':
1065
 
                                self.curlabel += 2
1066
 
                                lab1, lab2 = self.curlabel-1, self.curlabel
1067
 
                                # want 'not' of relation here
1068
 
                                opc = self.notrelop_dict[nrel.relop]
1069
 
                                last_codeline[0], last_codeline[1] = "j%s" % (opc + lop1t[0] + lop2t[0]), "%s %s @%d" % (last_oprs[1], last_oprs[2], lab1)
1070
 
                                # the lhs of the old statement can now be deleted if not still in use
1071
 
                                last_lhs = self.pop()
1072
 
                                if self.reguse[last_lhs] == 0:
1073
 
                                        self.tempregs.remove(last_lhs)
1074
 
                                node._if.visit(self)
1075
 
                                if node._else: self.code.append(["jmp", "@%d" % lab2])
1076
 
                                self.code.append(["@%d" % lab1, None])
1077
 
                                if node._else:
1078
 
                                        node._else.visit(self)
1079
 
                                        self.code.append(["@%d" % lab2, None])
1080
 
                                return
1081
 
                # 'complex' or relation expression using varyings
1082
 
                lhs = self.pop()
1083
 
                if self.typeof(lhs)[1] == 'u':
1084
 
                        # uniform relation, can do a conditional jump
1085
 
                        self.curlabel += 2
1086
 
                        lab1, lab2 = self.curlabel-1, self.curlabel
1087
 
                        self.code.append(["cjmpnot", "%s @%d" % (lhs, lab1)])
1088
 
                        node._if.visit(self)
1089
 
                        if node._else: self.code.append(["jmp", "@%d" % lab2])
1090
 
                        self.code.append(["@%d" % lab1, None])
1091
 
                        if node._else:
1092
 
                                node._else.visit(self)
1093
 
                                self.code.append(["@%d" % lab2, None])
1094
 
                        return
1095
 
                # varying relation
1096
 
                self.code.append(["cond_push", "%s" % lhs])
1097
 
                node._if.visit(self)
1098
 
                if node._else:
1099
 
                        self.code.append(["cond_else", None])
1100
 
                        node._else.visit(self)
1101
 
                self.code.append(["cond_pop", None])
1102
 
 
1103
 
        def visitExprList(self, node):
1104
 
                if node.expr_list: node.expr_list.visit(self)
1105
 
                node.expr.visit(self)
1106
 
 
1107
 
        # add a new variable to set
1108
 
        # there can be vars with the same name, but have different type,
1109
 
        # so this function is used to make sure names are unique
1110
 
        def addNewVariable(self, var):
1111
 
                name = str(var)
1112
 
                if name in self.allnames:
1113
 
                        self.allnames[name] += 1
1114
 
                        var.name += "_%d" % self.allnames[name]
1115
 
                else:
1116
 
                        self.allnames[name] = 0
1117
 
                self.variables.add(var)
1118
 
 
1119
 
        # separate code to handle array default expressions
1120
 
        def handleDefArray(self, node):
1121
 
                arr = node.def_init
1122
 
                if arr.isconst:
1123
 
                        if node.shparam:
1124
 
                                # check redefinition
1125
 
                                if self.params.has_key(node.id):
1126
 
                                        raise Exception("Line %d: Parameter '%s' already defined" % (node.lineno, node.id))
1127
 
                                self.params[node.id] = [self.typedet, arr.values]
1128
 
                        else:
1129
 
                                tp = self.typedet
1130
 
                                newvar = variable_t(tp[0], tp[1], node.id, node.array_length, self.curscope)
1131
 
                                if self.funcparams == None: self.addNewVariable(newvar)
1132
 
                                for i in range(node.array_length):
1133
 
                                        self.code.append(["movta%s" % (tp[0] + tp[0]), "%s %s %s" % (node.id, self.newconst('f', i), self.newconst(tp[0], arr.values[i]))])
1134
 
                else:
1135
 
                        # array not completely constant, initialization code needed
1136
 
                        lastcodelen = len(self.code)    # used to copy newly added code to initcode segment if required
1137
 
                        if node.shparam:
1138
 
                                tp = self.typedet
1139
 
                                if tp[0] == 'f':
1140
 
                                        defvals = [0.0]*node.array_length
1141
 
                                elif tp[0] == 'v':
1142
 
                                        defvals = [vector_t() for i in range(node.array_length)]
1143
 
                                        for v in defvals: v.detail = tp[1]
1144
 
                                elif tp[0] == 'm':
1145
 
                                        defvals = [matrix_t() for i in range(node.array_length)]
1146
 
                                        for m in defvals: m.detail = tp[1]
1147
 
                                elif tp[0] == 's':
1148
 
                                        defvals = [""]*node.array_length
1149
 
                                else:   # never happens
1150
 
                                        raise Exception("Line %d: DefExpression() array, unexpected type!?! -> %s" % (node.lineno, tp))
1151
 
                                for i in range(node.array_length):
1152
 
                                        if isinstance(arr.values[i], astnode_t):
1153
 
                                                arr.values[i].visit(self)
1154
 
                                                self.code.append(["movta%s" % (tp[0] + tp[0]), "%s %s %s" % (node.id, self.newconst('f', i), self.pop())])
1155
 
                                        else:
1156
 
                                                defvals[i] = arr.values[i]
1157
 
                                # check redefinition
1158
 
                                if self.params.has_key(node.id):
1159
 
                                        raise Exception("Line %d: Parameter '%s' already defined" % (node.lineno, node.id))
1160
 
                                self.params[node.id] = [tp, defvals]
1161
 
                                # move newly added codelines to initcode
1162
 
                                lc = len(self.code) - lastcodelen
1163
 
                                if lc != 0:
1164
 
                                        for i in range(lc):
1165
 
                                                self.initcode.append(self.code[i + lastcodelen])
1166
 
                                        for i in range(lc):
1167
 
                                                self.code.pop()
1168
 
                        else:
1169
 
                                tp = self.typedet
1170
 
                                newvar = variable_t(tp[0], tp[1], node.id, node.array_length, self.curscope)
1171
 
                                if self.funcparams == None: self.addNewVariable(newvar)
1172
 
                                for i in range(node.array_length):
1173
 
                                        if isinstance(arr.values[i], astnode_t):
1174
 
                                                arr.values[i].visit(self)
1175
 
                                                val = self.pop()
1176
 
                                                self.code.append(["movta%s" % (tp[0] + tp[0]), "%s %s %s" % (node.id, self.newconst('f', i), val)])
1177
 
                                        else:
1178
 
                                                self.code.append(["movta%s" % (tp[0] + tp[0]), "%s %s %s" % (node.id, self.newconst('f', i), self.newconst(tp[0], arr.values[i]))])
1179
 
 
1180
 
                if not node.shparam:
1181
 
                        # check possible redefinition
1182
 
                        curscope = self.scopes[-1][-1]
1183
 
                        if node.id in curscope:
1184
 
                                raise Exception("Line %d: Variable '%s' already defined" % (node.lineno, node.id))
1185
 
                        # if 'extern' declared variable, add to externlist
1186
 
                        if self.externvar:
1187
 
                                self.vars_extern[node.id] = self.typedet
1188
 
                        else:
1189
 
                                # add new variable if not processing function parameters
1190
 
                                newvar = variable_t(self.typedet[0], self.typedet[1], node.id, node.array_length, self.curscope)
1191
 
                                if self.funcparams == None: self.addNewVariable(newvar)
1192
 
                                # also add to current scope
1193
 
                                if not curscope.has_key(node.id):
1194
 
                                        curscope[node.id] = newvar
1195
 
                                        # add to ordered function parameter list if processing function parameters
1196
 
                                        if self.funcparams != None:
1197
 
                                                self.funcparams.append(node.id)
1198
 
 
1199
 
 
1200
 
        def visitDefExpression(self, node):
1201
 
                lastcodelen = len(self.code)    # used to copy newly added code to initcode segment if required
1202
 
                if node.def_init:
1203
 
                        if isinstance(node.def_init, array_t):
1204
 
                                self.handleDefArray(node)
1205
 
                                return
1206
 
                        node.def_init.visit(self)
1207
 
                need_initcode = False
1208
 
                isconstparam = False
1209
 
                if node.shparam:
1210
 
                        val = self.top()
1211
 
                        if isinstance(val, constant_t):
1212
 
                                # constant register, no initcode
1213
 
                                need_initcode = False
1214
 
                                isconstparam = True
1215
 
                        elif isinstance(val, register_t):
1216
 
                                need_initcode = True
1217
 
                        elif val in self.params:
1218
 
                                # parameter ID, replace by its value
1219
 
                                val = self.params[val][1]
1220
 
                                need_initcode = False
1221
 
                                isconstparam = True
1222
 
                        else:   # should never happen
1223
 
                                raise Exception("Line " + str(node.lineno) + ": Don't know what to do with this -> " + repr(val) + " (pytype: %s)" % type(val))
1224
 
                        if need_initcode:
1225
 
                                # since it is initialized later, use a default value for the declaration
1226
 
                                tp = self.typeof(val)
1227
 
                                # check implicit typecast
1228
 
                                if self.typedet[0] not in self.typecast_dict[tp[0]]:
1229
 
                                        raise Exception("Line %d: parameter '%s', Illegal implicit typecast of %s to %s" % (node.lineno, node.id, self.fulltype[tp[0]], self.fulltype[self.typedet[0]]))
1230
 
                                if tp[0] == 'f':
1231
 
                                        defval = 0.0
1232
 
                                elif tp[0] == 'v':
1233
 
                                        defval = vector_t()
1234
 
                                        defval.detail = tp[1]
1235
 
                                elif tp[0] == 'm':
1236
 
                                        defval = matrix_t()
1237
 
                                        defval.detail = tp[1]
1238
 
                                elif tp[0] == 's':
1239
 
                                        defval = ""
1240
 
                                else:   # never happens
1241
 
                                        raise Exception("Line " + str(node.lineno) + ": DefExpression() Unexpected type!?! -> " + tp)
1242
 
                                # check redefinition
1243
 
                                if self.params.has_key(node.id):
1244
 
                                        raise Exception("Line %d: Parameter '%s' already defined" % (node.lineno, node.id))
1245
 
                                self.params[node.id] = [self.typedet, defval]
1246
 
                                # move newly added codelines to initcode
1247
 
                                lc = len(self.code) - lastcodelen
1248
 
                                if lc == 0: # nothing was added, so no initcode
1249
 
                                        need_initcode = False
1250
 
                                else:
1251
 
                                        for i in range(lc):
1252
 
                                                self.initcode.append(self.code[i + lastcodelen])
1253
 
                                        for i in range(lc):
1254
 
                                                self.code.pop()
1255
 
                        else:   # no initcode
1256
 
                                # if val is a constant register, replace with its actual value,
1257
 
                                # and since this is a parameter, remove the constant value from the dictionary (unless use count != 0),
1258
 
                                # and delete the value just pushed onto the stack as well
1259
 
                                if isconstparam:
1260
 
                                        tp = self.typeof(val)[0]
1261
 
                                        # check implicit typecast
1262
 
                                        if self.typedet[0] not in self.typecast_dict[tp[0]]:
1263
 
                                                raise Exception("Line %d: parameter '%s', Illegal implicit typecast of %s to %s" % (node.lineno, node.id, self.fulltype[tp[0]], self.fulltype[self.typedet[0]]))
1264
 
                                        reg = self.pop()
1265
 
                                        val = self.delconst(reg)
1266
 
                                        # a parameter declaration can also require an implicit typecast, eg. color c = 1
1267
 
                                        # if so, expand to full v/c/p/n triple
1268
 
                                        if (self.typedet[0] == 'v' or (self.typedet[0] in self.cpn_set)) and tp == 'f':
1269
 
                                                val = "%g %g %g" % (val, val, val)
1270
 
                                # check redefinition
1271
 
                                if self.params.has_key(node.id):
1272
 
                                        raise Exception("Line %d: Parameter '%s' already defined" % (node.lineno, node.id))
1273
 
                                self.params[node.id] = [self.typedet, val]
1274
 
                                # if this is a parameter with a constant default value, there is nothing to do after this, so can return now
1275
 
                                if isconstparam: return
1276
 
 
1277
 
                # 'temp' type, no default value, is assigned in code
1278
 
                newvar = None
1279
 
                if not node.shparam:
1280
 
                        curscope = self.scopes[-1][-1]
1281
 
                        # check possible redefinition
1282
 
                        if node.id in curscope:
1283
 
                                raise Exception("Line %d: Variable '%s' already defined" % (node.lineno, node.id))
1284
 
                        # if 'extern' declared variable, add to externlist
1285
 
                        if self.externvar:
1286
 
                                self.vars_extern[node.id] = self.typedet
1287
 
                        else:
1288
 
                                # add new variable set if not processing function parameters
1289
 
                                newvar = variable_t(self.typedet[0], self.typedet[1], node.id, node.array_length, self.curscope)
1290
 
                                if self.funcparams == None: self.addNewVariable(newvar)
1291
 
                                # also add to current scope
1292
 
                                if not curscope.has_key(node.id):
1293
 
                                        curscope[node.id] = newvar
1294
 
                                        # add to ordered function parameter list if processing function parameters
1295
 
                                        if self.funcparams != None:
1296
 
                                                self.funcparams.append(node.id)
1297
 
                # optional default assignment code
1298
 
                if need_initcode:
1299
 
                        curcode = self.initcode
1300
 
                else:
1301
 
                        curcode = self.code
1302
 
                if node.def_init:
1303
 
                        op2 = self.pop()
1304
 
                        # type here is the last defined type
1305
 
                        op1t, op2t = self.typedet, self.typeof(op2)
1306
 
                        # convert op1t to v if cpn
1307
 
                        if op1t[0] in self.cpn_set: op1t = 'v' + op1t[1]
1308
 
                        # simple optimization, if previous line of code is 'opcode op2 ...' and op2 is a register,
1309
 
                        # and this line will be 'mov op1 op2' then concatenate the two, modifying previous codeline to 'opcode op1 ...'
1310
 
                        # the two operands of mov must be same type, but can be different detail, but in case of mixed detail, lhs must be varying
1311
 
                        if self.optimize and isinstance(op2, register_t) and curcode[-1][1]:
1312
 
                                last_codeline = curcode[-1][1]
1313
 
                                last_ops = last_codeline.split()
1314
 
                                op2str = str(op2)
1315
 
                                if last_ops[0] == op2str and op1t[0] == op2t[0] and not (op1t[1] == 'u' and op2t[1] == 'v'):
1316
 
                                        if not node.shparam:    # use name of newly created variable
1317
 
                                                curcode[-1][1] = str(newvar) + last_codeline[len(op2str):]
1318
 
                                        else:
1319
 
                                                curcode[-1][1] = node.id + last_codeline[len(op2str):]
1320
 
                                        # if op2 is only the lhs of the previous line, remove op from tempregs, result register no longer used
1321
 
                                        op2_count = 0
1322
 
                                        for opr in last_ops:
1323
 
                                                if opr == op2str: op2_count += 1
1324
 
                                        if op2_count == 1:
1325
 
                                                print "OPTIM 1 rm", op2, "USE", self.reguse[op2]
1326
 
                                                if self.reguse[op2] == 0:
1327
 
                                                        self.tempregs.remove(op2)
1328
 
                                        return
1329
 
                        # the actual default initialization, if this is not a parameter OR op2 is a register or constant add a mov instruction
1330
 
                        if (not node.shparam) or isinstance(op2, register_t) or isinstance(op2, constant_t):
1331
 
                                # check implicit typecast
1332
 
                                if op1t[0] not in self.typecast_dict[op2t[0]]:
1333
 
                                        raise Exception("Line %d: variable '%s', Illegal implicit typecast of %s to %s" % (node.lineno, node.id, self.fulltype[op2t[0]], self.fulltype[op1t[0]]))
1334
 
                                # check detail, lhs must be varying if rhs is varying
1335
 
                                if op2t[1] == 'v' and op1t[1] != 'v':
1336
 
                                        raise Exception("Line %d: variable '%s', cannot assign varying to uniform" % (node.lineno, node.id))
1337
 
                                typest = op1t[0] + op2t[0]
1338
 
                                if newvar:
1339
 
                                        curcode.append(["mov%s" % typest, "%s %s" % (newvar, op2)])
1340
 
                                else:
1341
 
                                        curcode.append(["mov%s" % typest, "%s %s" % (node.id, op2)])
1342
 
 
1343
 
        def visitDefExprList(self, node):
1344
 
                for e in node.expr_list:
1345
 
                        e.visit(self)
1346
 
 
1347
 
        def visitParameter(self, node):
1348
 
                # set type and detail for any possible default expressions, just use first char of each
1349
 
                ptd = self.typedet
1350
 
                if node.typespec[1] == "void":  # exception is void since 'v' already reserved for vector, use '*' instead in that case
1351
 
                        self.typedet = '*' + node.typespec[0][0]
1352
 
                else:
1353
 
                        self.typedet = node.typespec[1][0] + node.typespec[0][0]
1354
 
                for e in node.exprs:
1355
 
                        e.visit(self)
1356
 
                # set the 'output' flag if needed
1357
 
                if node.output:
1358
 
                        csd = self.scopes[-1][-1]
1359
 
                        for dexprs in node.exprs:
1360
 
                                if csd.has_key(dexprs.id):
1361
 
                                        csd[dexprs.id].flags |= FF_OUTPUT
1362
 
                                elif self.params.has_key(dexprs.id):
1363
 
                                        # shader parameter, make typechar uppercase to flag as output
1364
 
                                        ptdval = self.params[dexprs.id]
1365
 
                                        self.params[dexprs.id] = [ptdval[0][0].upper() + ptdval[0][1], ptdval[1]]
1366
 
                # reset type & detail, now unknown until eg. assignment statements etc.
1367
 
                self.typedet = ptd
1368
 
 
1369
 
        def visitVariable(self, node):
1370
 
                # set type and detail for any possible default expressions
1371
 
                ptd = self.typedet
1372
 
                if node.typespec[1] == "void":  # exception is void since 'v' already reserved for vector, use '*' instead in that case
1373
 
                        self.typedet = '*' + node.typespec[0][0]
1374
 
                else:
1375
 
                        self.typedet = node.typespec[1][0] + node.typespec[0][0]
1376
 
                # set extern flag for the duration of the function parameter declarations if required
1377
 
                if self.infuncbody and node.extern:
1378
 
                        pev = self.externvar
1379
 
                        self.externvar = True
1380
 
                for e in node.expr:
1381
 
                        e.visit(self)
1382
 
                if self.externvar: self.externvar = pev
1383
 
                # reset type & detail, now unknown until eg. assignment statements etc.
1384
 
                self.typedet = ptd
1385
 
 
1386
 
        def visitTuple(self, node):
1387
 
                # note: must visit all values first, then pop
1388
 
                lhs_det = 'u'   # lhs detail is varying if any value of tuple is varying
1389
 
                for tpval in node.values:
1390
 
                        tpval.visit(self)
1391
 
                        if self.typeof(self.top())[1] == 'v': lhs_det = 'v'
1392
 
                tup = []
1393
 
                for tpval in node.values:
1394
 
                        tup.append(self.pop())
1395
 
                tup.reverse()
1396
 
                ltp = len(node.values)
1397
 
                if ltp == 3:
1398
 
                        v = vector_t(*tup)
1399
 
                        v.detail = lhs_det
1400
 
                        # code to 'cast' actual vector to register
1401
 
                        rv = self.newreg('v', v.detail)
1402
 
                        self.code.append(["movvf3", "%s %s" % (rv, v)])
1403
 
                        self.push(rv)
1404
 
                elif ltp == 16:
1405
 
                        m = matrix_t(*tup)
1406
 
                        m.detail = lhs_det
1407
 
                        # cast to matrix register var
1408
 
                        rm = self.newreg('m', m.detail)
1409
 
                        self.code.append(["movmf16", "%s %s" % (rm, m)])
1410
 
                        self.push(rm)
1411
 
                else:   # never happens
1412
 
                        raise Exception("Line %d: Expected tuple length of 3 or 16, got %d" % (node.lineno, ltp))
1413
 
 
1414
 
        def visitStatements(self, node):
1415
 
                if node.statements:
1416
 
                        for s in node.statements:
1417
 
                                s.visit(self)
1418
 
 
1419
 
        def visitReturn(self, node):
1420
 
                if node.returnval:
1421
 
                        node.returnval.visit(self)
1422
 
                        op1 = self.pop()
1423
 
                        op1t = self.typeof(op1)
1424
 
                        lhs = self.newreg(op1t[0], op1t[1])
1425
 
                        if lhs != op1:  # don't add code if move-to-self
1426
 
                                # don't have to check detail here, since both have same type/detail
1427
 
                                self.code.append(["mov%s" % (op1t[0] + op1t[0]), "%s %s" % (lhs, op1)])
1428
 
                        self.push(lhs)
1429
 
 
1430
 
        def visitBreak(self, node):
1431
 
                # must do this differently, maybe add separate VM instruction for this, disabled for now
1432
 
                raise Exception("Line %d: 'break' statement not implemented yet" % node.lineno)
1433
 
                # jump to end of loop
1434
 
                numlab = len(self.looplabels)
1435
 
                if numlab != 0:
1436
 
                        self.code.append(["cond_reset", None])
1437
 
                        if node.level:
1438
 
                                if node.level > numlab:
1439
 
                                        raise Exception("Line %d: illegal level in 'break' statement" % node.lineno)
1440
 
                                self.code.append(["jmp", "@%d" % (self.looplabels[-node.level][1])])
1441
 
                        else:
1442
 
                                self.code.append(["jmp", "@%d" % (self.looplabels[-1][1])])
1443
 
 
1444
 
        def visitContinue(self, node):
1445
 
                # must do this differently, maybe add separate VM instruction for this, disabled for now
1446
 
                raise Exception("Line %d: 'continue' statement not implemented yet" % node.lineno)
1447
 
                # jump to start of loop
1448
 
                numlab = len(self.looplabels)
1449
 
                if numlab != 0:
1450
 
                        self.code.append(["cond_reset", None])
1451
 
                        if node.level:
1452
 
                                if node.level > numlab:
1453
 
                                        raise Exception("Line %d: illegal level in 'continue' statement" % node.lineno)
1454
 
                                self.code.append(["jmp", "@%d" % (self.looplabels[-node.level][0])])
1455
 
                        else:
1456
 
                                self.code.append(["jmp", "@%d" % (self.looplabels[-1][0])])
1457
 
 
1458
 
        def visitFor(self, node):
1459
 
                if node.init_expr:      # while stmt has no init.expr
1460
 
                        node.init_expr.visit(self)
1461
 
                self.curlabel += 2
1462
 
                lab1, lab2 = self.curlabel-1, self.curlabel
1463
 
                self.looplabels.append([lab1, lab2])
1464
 
                self.code.append(["@%d" % lab1, None])
1465
 
                nrel = node.rel
1466
 
                vary = False
1467
 
                nrel.visit(self)
1468
 
                op1 = self.pop()
1469
 
                op1t = self.typeof(op1)
1470
 
                # this doesn't always work for some reason, not entirely sure why yet (shadowedclouds.sl infinite loop, maybe uni/vary cond. mixup?), so disabled for now...
1471
 
                """
1472
 
                # as in visitIfElse(), simplify code to a compare and jump if simple relation expression with uniform operands
1473
 
                if self.optimize and op1t[1] == 'u' \
1474
 
                        and not isinstance(nrel.expr1, astRelation_t) and not isinstance(nrel.expr2, astRelation_t) \
1475
 
                        and nrel.relop != "&&" and nrel.relop != "||" and nrel.relop != '!':    # not logic stmts
1476
 
                        last_codeline = self.code[-1]
1477
 
                        ops = last_codeline[1].split()
1478
 
                        last_codeline[0] = "j" + self.w_notrelop_dict[last_codeline[0][2:4]] + last_codeline[0][4:]
1479
 
                        last_codeline[1] = "%s %s @%d" % (ops[1], ops[2], lab2)
1480
 
                        # can remove lhs if not in use (not indexed by name, so have to do a search here)
1481
 
                        last_lhs = None
1482
 
                        for reg in self.reguse.keys():
1483
 
                                if str(reg) == ops[0]:
1484
 
                                        last_lhs = reg
1485
 
                                        break
1486
 
                        if last_lhs and self.reguse[last_lhs] == 0:
1487
 
                                print "OPTIM 6 rm", last_lhs, "USE", self.reguse[last_lhs]
1488
 
                                self.tempregs.remove(last_lhs)
1489
 
                else:   # varying, or not possible to optimize
1490
 
                """
1491
 
                self.code.append(["cjmpnot", "%s @%d" % (op1, lab2)])
1492
 
                if op1t[1] == 'v':
1493
 
                        self.code.append(["cond_push", str(op1)])
1494
 
                        vary = True
1495
 
                node.stmt.visit(self)
1496
 
                if vary: self.code.append(["cond_pop", None])
1497
 
                if node.inc_expr:       # while stmt has no incr.expr
1498
 
                        node.inc_expr.visit(self)
1499
 
                self.code.append(["jmp", "@%d" % lab1])
1500
 
                self.code.append(["@%d" % lab2, None])
1501
 
                self.looplabels.pop()
1502
 
 
1503
 
        def visitSolar(self, node):
1504
 
                if node.axis:
1505
 
                        node.axis.visit(self)
1506
 
                        node.angle.visit(self)
1507
 
                        op2, op1 = self.pop(), self.pop()
1508
 
                        self.code.append(["solar2", "%s %s" % (op1, op2)])
1509
 
                else:
1510
 
                        self.code.append(["solar1", None])
1511
 
                node.stmt.visit(self)
1512
 
                self.code.append(["end_solar", None])
1513
 
 
1514
 
        def visitIlluminate(self, node):
1515
 
                if node.pos: node.pos.visit(self)
1516
 
                if node.axis: node.axis.visit(self)
1517
 
                if node.angle: node.angle.visit(self)
1518
 
                if node.pos and ((node.axis == None) and (node.angle == None)):
1519
 
                        self.code.append(["illuminate1", "%s" % self.pop()])
1520
 
                else:
1521
 
                        op3, op2, op1 = self.pop(), self.pop(), self.pop()
1522
 
                        self.code.append(["illuminate2", "%s %s %s" % (op1, op2, op3)])
1523
 
                if node.stmt: node.stmt.visit(self)
1524
 
                self.code.append(["end_illuminate", None])
1525
 
 
1526
 
        def visitIlluminance(self, node):
1527
 
                if node.cat:    # category, not used yet
1528
 
                        node.cat.visit(self)
1529
 
                        category = self.pop()
1530
 
                node.pos.visit(self)
1531
 
                if node.axis: node.axis.visit(self)
1532
 
                if node.angle: node.angle.visit(self)
1533
 
                self.curlabel += 2
1534
 
                lab1, lab2 = self.curlabel-1, self.curlabel
1535
 
                self.code.append(["@%d" % lab1, None])
1536
 
                if node.pos and ((node.axis == None) and (node.angle == None)):
1537
 
                        self.code.append(["illuminance1", "%s @%d" % (self.pop(), lab2)])
1538
 
                else:
1539
 
                        op3, op2, op1 = self.pop(), self.pop(), self.pop()
1540
 
                        self.code.append(["illuminance2", "%s %s %s @%d" % (op1, op2, op3, lab2)])
1541
 
                node.stmt.visit(self)
1542
 
                self.code.append(["jmp", "@%d" % lab1])
1543
 
                self.code.append(["@%d" % lab2, None])
1544
 
 
1545
 
        def getID(self, node):
1546
 
                # if currently inside a function, only search local scope, unless there are any 'extern' declared variables
1547
 
                if self.infuncbody:
1548
 
                        for vardict in reversed(self.scopes[-1]):       # last first
1549
 
                                if vardict.has_key(node.name):
1550
 
                                        return vardict[node.name]
1551
 
                        # not found, search for possibly extern declared vars (global scope)
1552
 
                        if self.vars_extern and self.vars_extern.has_key(node.name):
1553
 
                                # found, but declared type must be same as that of the global variable
1554
 
                                dtp, etp = self.vars_extern[node.name], self.typeof(node.name, False)
1555
 
                                if dtp != etp:
1556
 
                                        raise Exception("Line %d: 'extern' declared variable '%s' is wrong type, declared as '%s', expected '%s'" % (node.lineno, node.name, self.fulltype[dtp[0]], self.fulltype[etp[0]]))
1557
 
                                if self.islexfunc:
1558
 
                                        # function declared inside shader or other function, can also access 'parent' scope
1559
 
                                        scl = len(self.scopes[-1])
1560
 
                                        for vardict in reversed(self.scopes[-1]):
1561
 
                                                scl -= 1
1562
 
                                                if vardict.has_key(node.name):
1563
 
                                                        var = vardict[node.name]
1564
 
                                                        # if the variable was found in the very first scope dictionary, then it was a global predefined shader variable
1565
 
                                                        if scl == 0: self.globals_used.add(node.name)
1566
 
                                                        return var
1567
 
                                        # not found, try parameters
1568
 
                                        if self.params.has_key(node.name):
1569
 
                                                return node.name
1570
 
                                        # nothing found
1571
 
                                        raise Exception("Line %d: Unknown 'extern' declared variable '%s'" % (node.lineno, node.name))
1572
 
                                else:
1573
 
                                        # function outside main scope, only search global
1574
 
                                        if self.scopes[0][0].has_key(node.name):
1575
 
                                                # add to global list
1576
 
                                                self.globals_used.add(node.name)
1577
 
                                                return self.scopes[0][0][node.name]
1578
 
                                        # not found
1579
 
                                        raise Exception("Line %d: Unknown 'extern' declared variable '%s'" % (node.lineno, node.name))
1580
 
                        # nothing found
1581
 
                        raise Exception("Line %d: Unknown function variable '%s' (forgot 'extern' declaration?)" % (node.lineno, node.name))
1582
 
                # Not a function
1583
 
                # search the parameter list first, since these are stored separately
1584
 
                # if found, push its name, not the value
1585
 
                if node.name in self.params: return node.name
1586
 
                # search the scopelist from current to older ones for the identifier
1587
 
                scl = len(self.scopes)
1588
 
                for scopelist in reversed(self.scopes):
1589
 
                        scl -= 1
1590
 
                        for vardict in reversed(scopelist):
1591
 
                                if vardict.has_key(node.name):
1592
 
                                        var = vardict[node.name]
1593
 
                                        # if the variable was found in the very first scope dictionary, then it was a global predefined shader variable
1594
 
                                        if scl == 0: self.globals_used.add(node.name)
1595
 
                                        return var
1596
 
                # not found, try parameter name match
1597
 
                if self.params.has_key(node.name):
1598
 
                        return node.name
1599
 
                # unknown, error
1600
 
                raise Exception("Line %d: Unknown identifier '%s'" % (node.lineno, node.name))
1601
 
 
1602
 
        def visitID(self, node):
1603
 
                # value bound to id
1604
 
                self.push(self.getID(node))
1605
 
                # if array index specified, add a move-from-array instruction, unless ID is on the left hand side (move-to-array handled in visitAssignExpr())
1606
 
                if node.array_idx and not self.asgn_lhs:
1607
 
                        op1 = self.pop()
1608
 
                        node.array_idx.visit(self)
1609
 
                        op2 = self.pop()        # array index
1610
 
                        op1t, op2t = self.typeof(op1), self.typeof(op2)
1611
 
                        lhs = self.newreg(op2t[0], op2t[1])
1612
 
                        self.code.append(["movfa%s" % (op2t[0] + op2t[0]), "%s %s %s" % (lhs, op1, op2)])
1613
 
                        self.push(lhs)
1614
 
 
1615
 
        def visitUnaryExpr(self, node):
1616
 
                if node.typecast:
1617
 
                        if node.typecast == '-':        # not typecast, but a negation
1618
 
                                node.expr.visit(self)
1619
 
                                op1 = self.pop()
1620
 
                                op1t = self.typeof(op1)
1621
 
                                lhs = self.newreg(op1t[0], op1t[1])
1622
 
                                self.code.append(["neg%s" % (op1t[0]*2), "%s %s" % (lhs, op1)])
1623
 
                                self.push(lhs)
1624
 
                                return
1625
 
                        else:   # actual typecast
1626
 
                                vartype, spacetype = node.typecast[0], node.typecast[1]
1627
 
                                newtype = vartype[0]
1628
 
                                if newtype in self.cpn_set: newtype = 'v'
1629
 
                                # temporarily reset the current type
1630
 
                                ptd = self.typedet
1631
 
                                if ptd: self.typedet = newtype + self.typedet[1]
1632
 
                                node.expr.visit(self)
1633
 
                                self.typedet = ptd
1634
 
                                # nothing to do further if no transform required and expression result is already correct type
1635
 
                                if (spacetype == None) and (newtype == self.typeof(self.top())[0]): return      # NOTE: do not pop! value must remain on stack in this case
1636
 
                                op2 = self.pop()
1637
 
                                op2t = self.typeof(op2)
1638
 
                                # check explicit cast validity
1639
 
                                if vartype[0] not in self.typecast_dict[op2t[0]]:
1640
 
                                        raise Exception("Line %d: Illegal typecast of %s to %s" % (node.lineno, self.fulltype[op2t[0]], vartype))
1641
 
                                # add code for implicit type cast if needed (eg. point p = 1)
1642
 
                                cast = None
1643
 
                                if op2t[0] == 'f' and (newtype == 'v' or newtype == 'm'):
1644
 
                                        lhs = self.newreg(newtype, op2t[1])
1645
 
                                        # don't have to check detail here, since both have same detail
1646
 
                                        self.code.append(["mov%s" % (newtype + 'f'), "%s %s" % (lhs, op2)])
1647
 
                                        op2 = lhs
1648
 
                                        op2t = self.typeof(op2)
1649
 
                                        cast = True
1650
 
                                if spacetype:   # transform code needed
1651
 
                                        spacetype.visit(self)
1652
 
                                        lhs = self.newreg(vartype[0], op2t[1])
1653
 
                                        self.code.append(["%stocurr" % vartype[0], "%s %s %s" % (lhs, self.pop(), op2)])
1654
 
                                        self.push(lhs)
1655
 
                                else:   # only implicit type case or nothing to be done at all, push last expression result (note: have to 're-alloc' to keep regcount in sync!)
1656
 
                                        if cast:
1657
 
                                                self.push(op2)
1658
 
                                        else:
1659
 
                                                self.push(self.newreg(op2t[0], op2t[1]))
1660
 
 
1661
 
        def visitBinop(self, node):
1662
 
                node.expr1.visit(self)
1663
 
                node.expr2.visit(self)
1664
 
                # lhs type depends on operand types
1665
 
                op2, op1 = self.stack[-1], self.stack[-2]       # cannot pop yet because of possible typecast, must be done after new register request !
1666
 
                op1t, op2t = self.typeof(op1), self.typeof(op2)
1667
 
                # for the 'fv' or 'vf' cases need extra code to make float a vector
1668
 
                if op1t[0] == 'f' and op2t[0] == 'v':
1669
 
                        if node.op == '*':
1670
 
                                op2, op1 = self.pop(), self.pop()
1671
 
                                # multiply, swap operands if type order 'fv', want 'vf'
1672
 
                                op1t, op2t = op2t, op1t
1673
 
                                op1, op2 = op2, op1
1674
 
                        else:   # for all others need extra mov to make float a vector
1675
 
                                lhs = self.newreg('v', op1t[1])
1676
 
                                op2, op1 = self.pop(), self.pop()
1677
 
                                # no detail check needed, both same
1678
 
                                self.code.append(["movvf", "%s %s" % (lhs, op1)])
1679
 
                                op1 = lhs
1680
 
                                op1t = 'v' + op1t[1]
1681
 
                elif op1t[0] == 'v' and op2t[0] == 'f' and node.op != '*' and node.op != '/':   # need extra mov to make float a vector (unless mul/div)
1682
 
                        lhs = self.newreg('v',  op2t[1])
1683
 
                        op2, op1 = self.pop(), self.pop()
1684
 
                        # no detail check needed, both same
1685
 
                        self.code.append(["movvf", "%s %s" % (lhs, op2)])
1686
 
                        op2 = lhs
1687
 
                        op2t = 'v' + op2t[1]
1688
 
                else:
1689
 
                        # nothing, just pop
1690
 
                        op2, op1 = self.pop(), self.pop()
1691
 
                # check implicit typecast
1692
 
                rop1t, rop2t = self.typeof(op1, False), self.typeof(op2, False) # 'real' types
1693
 
                if rop1t[0] not in self.typecast_dict[rop2t[0]]:
1694
 
                        raise Exception("Line %d: Illegal implicit typecast of %s to %s" % (node.lineno, self.fulltype[rop2t[0]], self.fulltype[rop1t[0]]))
1695
 
                if node.op == '*':
1696
 
                        opc = "mul"
1697
 
                elif node.op == '/':
1698
 
                        opc = "div"
1699
 
                elif node.op == '+':
1700
 
                        opc = "add"
1701
 
                elif node.op == '-':
1702
 
                        opc = "sub"
1703
 
                elif node.op == '^':
1704
 
                        opc = "vcross"
1705
 
                elif node.op == '.':
1706
 
                        opc = "vdot"
1707
 
                else:   # never happens
1708
 
                        raise Exception("Line %d: Unknown binary operator '%s'?" % (node.lineno, node.op))
1709
 
                # lhs type, use type of first operand (should always be ok, I think...)
1710
 
                if node.op == '.':      # vdot returns float
1711
 
                        lhs_type = 'f' + op1t[1]
1712
 
                else:
1713
 
                        if op1t[1] == 'v' and op2t[1] == 'v':   # both varying, lhs varying
1714
 
                                lhs_type = op1t
1715
 
                        elif op1t[1] == 'v' or op2t[1] == 'v':  # either varying, lhs varying
1716
 
                                lhs_type = op1t[0] + 'v'
1717
 
                        else:   # both uniform
1718
 
                                lhs_type = op1t[0] + 'u'
1719
 
                lhs = self.newreg(lhs_type[0], lhs_type[1])
1720
 
                # last three char part of instruction which consists of the argument types, but only if not vdot or vcross, which don't have any
1721
 
                if node.op != '.' and node.op != '^':
1722
 
                        opc += lhs_type[0] + op1t[0] + op2t[0]
1723
 
                # simple optimization, if last codeline was a multiply of the form 'mul x x y' and this line will be '(add|sub) z z x'
1724
 
                # then concatenate the two, modifying previous to 'm(add|sub) z x y' form
1725
 
                last_codeline = self.code[-1]
1726
 
                optim = False
1727
 
                if self.optimize and (node.op == '+' or node.op == '-') and last_codeline[0][:3] == "mul":
1728
 
                        last_ops = last_codeline[1].split()
1729
 
                        op2str = str(op2)
1730
 
                        if last_ops[0] == last_ops[1] and last_ops[0] == op2str and lhs == op1:
1731
 
                                # if op2 is only the lhs of the previous line, remove op from tempregs, result register no longer used
1732
 
                                op2_count = 0
1733
 
                                for opr in last_ops:
1734
 
                                        if opr == op2str: op2_count += 1
1735
 
                                if op2_count == 1:
1736
 
                                        print "OPTIM 2 rm", op2, "USE", self.reguse[op2]
1737
 
                                        if self.reguse[op2] == 0:
1738
 
                                                self.tempregs.remove(op2)
1739
 
                                # modify codeline
1740
 
                                if node.op == '+':
1741
 
                                        last_codeline[0] = "madd" + last_codeline[0][3:]        # same type extension as previous code
1742
 
                                else:
1743
 
                                        last_codeline[0] = "msub" + last_codeline[0][3:]        # same type extension as previous code
1744
 
                                last_codeline[1] = str(lhs) + last_codeline[1][len(op2str):]
1745
 
                                optim = True
1746
 
                if not optim:
1747
 
                        self.code.append(["%s" % opc, "%s %s %s" % (lhs, op1, op2)])
1748
 
                self.push(lhs)
1749
 
 
1750
 
        def visitAssignExpr(self, node):
1751
 
                self.asgn_lhs = True    # needs to be set here to only add a move-from-array instuction when appearing on rhs
1752
 
                node.lhs.visit(self)
1753
 
                self.asgn_lhs = False   # and reset again
1754
 
                op1 = self.pop()
1755
 
                op1t = self.typeof(op1)
1756
 
                # set current type and detail to type of lhs
1757
 
                ptd = self.typedet
1758
 
                self.typedet = op1t
1759
 
                # set flag if rh.expr is assignment as well
1760
 
                pca = self.chain_asgn
1761
 
                if isinstance(node.assign_expr, astAssignExpr_t):
1762
 
                        self.chain_asgn = True
1763
 
                node.assign_expr.visit(self)
1764
 
                # reset to previous
1765
 
                self.chain_asgn = pca
1766
 
                # reset type & detail
1767
 
                self.typedet = ptd
1768
 
                op2 = self.pop()
1769
 
                op2t = self.typeof(op2)
1770
 
                # for the 'fv' or 'vf' cases need extra code to make float a vector
1771
 
                if op1t[0] == 'f' and op2t[0] == 'v':
1772
 
                        if node.asgnop == "*=":
1773
 
                                # multiply, swap operands if type order 'fv', want 'vf'
1774
 
                                op1t, op2t = op2t, op1t
1775
 
                                op1, op2 = op2, op1
1776
 
                        else:   # for all others need extra mov to make float a vector
1777
 
                                lhs = self.newreg('v', op1t[1])
1778
 
                                # no detail check needed, both same
1779
 
                                self.code.append(["movvf", "%s %s" % (lhs, op1)])
1780
 
                                op1 = lhs
1781
 
                                op1t = 'v' + op1t[1]
1782
 
                elif op1t[0] == 'v' and op2t[0] == 'f' and node.asgnop != '*' and node.asgnop != '/':   # need extra mov to make float a vector (unless mul/div)
1783
 
                        lhs = self.newreg('v',  op2t[1])
1784
 
                        # no detail check needed, both same
1785
 
                        self.code.append(["movvf", "%s %s" % (lhs, op2)])
1786
 
                        op2 = lhs
1787
 
                        op2t = 'v' + op2t[1]
1788
 
                # check implicit typecast
1789
 
                rop1t, rop2t = self.typeof(op1, False), self.typeof(op2, False) # 'real' types
1790
 
                if rop1t[0] not in self.typecast_dict[rop2t[0]]:
1791
 
                        raise Exception("Line %d: Illegal implicit typecast of %s to %s" % (node.lineno, self.fulltype[rop2t[0]], self.fulltype[rop1t[0]]))
1792
 
                # insctruction lhs type depends on operand types
1793
 
                # use type of first operand (should always be ok, I think...)
1794
 
                if op1t[1] == 'v' and op2t[1] == 'v':   # both varying, lhs varying
1795
 
                        lhs_type = op1t
1796
 
                elif op1t[1] == 'v' or op2t[1] == 'v':  # either varying, lhs varying
1797
 
                        lhs_type = op1t[0] + 'v'
1798
 
                else:   # both uniform
1799
 
                        lhs_type = op1t[0] + 'u'
1800
 
                # last two (mov) or three char part of instruction which consists of the argument types
1801
 
                if node.asgnop == '=':
1802
 
                        # check for invalid assigment to function parameters that are not declared 'output'
1803
 
                        if op1 in self.params:
1804
 
                                # shader parameter can be used for output too, typechar is then uppercase
1805
 
                                if not self.params[op1][0][0].upper():
1806
 
                                        raise Exception("Line %d: Cannot assign to shader parameter '%s', not declared 'output'" % (node.lineno, op1))
1807
 
                        elif (op1.flags & FF_FPARAM) and ((op1.flags & FF_OUTPUT) == 0):
1808
 
                                raise Exception("Line %d: Cannot assign to function parameter '%s', not declared 'output'" % (node.lineno, op1.name))
1809
 
                        # simple optimization, if previous line of code is 'opcode op2 ...'
1810
 
                        # and this line will be 'mov op1 op2' (but not move-to-array!) then concatenate the two, modifying previous codeline to 'opcode op1 ...'
1811
 
                        # the two operands of mov must be same type, but can be different detail, but in case of mixed detail, lhs must be varying
1812
 
                        # exception here is if the previous codeline was a madd/msub instruction (eg. x += y*z, cannot replace x)
1813
 
                        last_codeline = self.code[-1]
1814
 
                        op2str = str(op2)
1815
 
                        L = len(op2str)
1816
 
                        if self.optimize and not (last_codeline[0][:4] == "madd" or last_codeline[0][:4] == "msub") and not node.lhs.array_idx \
1817
 
                                               and isinstance(op2, register_t) and op1t[0] == op2t[0] and not (op1t[1] == 'u' and op2t[1] == 'v') \
1818
 
                                               and last_codeline[1] and last_codeline[1][:L] == op2str:
1819
 
                                # if op2 is only the lhs of the previous line, remove op from tempregs, result register no longer used
1820
 
                                op2_count = 0
1821
 
                                for opr in last_codeline[1].split():
1822
 
                                        if opr == op2str: op2_count += 1
1823
 
                                if op2_count == 1:
1824
 
                                        print "OPTIM 3 rm", op2, "USE", self.reguse[op2]
1825
 
                                        if self.reguse[op2] == 0:
1826
 
                                                self.tempregs.remove(op2)
1827
 
                                self.code[-1][1] = str(op1) + last_codeline[1][L:]
1828
 
                                # if chained assignment, push lhs on stack
1829
 
                                if self.chain_asgn: self.push(op1)
1830
 
                                return
1831
 
                        else:
1832
 
                                # check detail, lhs must be varying if rhs is varying
1833
 
                                if op2t[1] == 'v' and op1t[1] != 'v':
1834
 
                                        raise Exception("Line %d: cannot assign varying to uniform" % node.lineno)
1835
 
                                typest = op1t[0] + op2t[0]
1836
 
                                if node.lhs.array_idx:  # assign to array element
1837
 
                                        node.lhs.array_idx.visit(self)  # !!!
1838
 
                                        self.code.append(["movta%s" % typest, "%s %s %s" % (op1, self.pop(), op2)])
1839
 
                                else:
1840
 
                                        self.code.append(["mov%s" % typest, "%s %s" % (op1, op2)])
1841
 
                                        # if chained assignment, push lhs on stack
1842
 
                                        if self.chain_asgn: self.push(op1)
1843
 
                elif node.asgnop == "+=" or node.asgnop == "-=":
1844
 
                        # simple optimization, if last codeline was a multiply of the form 'mul r y z' and this line will be '(add|sub) x x r', 'r' being a register,
1845
 
                        # then concatenate the two, modifying previous to 'm(add|sub) x y z' form. 'r' can then also be removed, unless r was not only lhs of the prev.line
1846
 
                        last_codeline = self.code[-1]
1847
 
                        optim = False
1848
 
                        if self.optimize and last_codeline[0][:3] == "mul" and isinstance(op2, register_t):
1849
 
                                last_ops = last_codeline[1].split()
1850
 
                                op2str = str(op2)
1851
 
                                if last_ops[0] == op2str:
1852
 
                                        # if op2 is only the lhs of the previous line, remove op from tempregs, result register no longer used
1853
 
                                        op2_count = 0
1854
 
                                        for opr in last_ops:
1855
 
                                                if opr == op2str: op2_count += 1
1856
 
                                        if op2_count == 1:
1857
 
                                                print "OPTIM 4 rm", op2, "USE", self.reguse[op2]
1858
 
                                                if self.reguse[op2] == 0:
1859
 
                                                        self.tempregs.remove(op2)
1860
 
                                        # modify codeline
1861
 
                                        if node.asgnop == "+=":
1862
 
                                                last_codeline[0] = "madd" + last_codeline[0][3:]        # same type extension as previous code
1863
 
                                        else:
1864
 
                                                last_codeline[0] = "msub" + last_codeline[0][3:]        # same type extension as previous code
1865
 
                                        last_codeline[1] = str(op1) + last_codeline[1][len(op2str):]
1866
 
                                        optim = True
1867
 
                        if not optim:
1868
 
                                typest = lhs_type[0] + op1t[0] + op2t[0]
1869
 
                                if node.asgnop == "+=":
1870
 
                                        self.code.append(["add%s" % typest, "%s %s %s" % (op1, op1, op2)])
1871
 
                                elif node.asgnop == '-=':
1872
 
                                        self.code.append(["sub%s" % typest, "%s %s %s" % (op1, op1, op2)])
1873
 
                else:
1874
 
                        typest = lhs_type[0] + op1t[0] + op2t[0]
1875
 
                        if node.asgnop == "*=":
1876
 
                                self.code.append(["mul%s" % typest, "%s %s %s" % (op1, op1, op2)])
1877
 
                        elif node.asgnop == '/=':
1878
 
                                self.code.append(["div%s" % typest, "%s %s %s" % (op1, op1, op2)])
1879
 
                        else:   # never happens
1880
 
                                raise Exception("Line %d: Unknown assignment operator '%s'?!?" % (node.lineno, node.asgnop))
1881
 
                # NOTE: Not entirely sure about this, but the result here might have to be pushed again on to the stack,
1882
 
                # the grammar suggests that an expression of the form: a = (b += c) would also be valid,
1883
 
                # currently this will not compile, TODO (Pixie does produce code, but result is incorrect, not sure about 3dl)
1884
 
 
1885
 
        def visitRelation(self, node):
1886
 
                if node.expr1: node.expr1.visit(self)
1887
 
                node.expr2.visit(self)
1888
 
                opc = None
1889
 
                if node.relop == '!':
1890
 
                        op1 = self.pop()
1891
 
                        lhs = self.newreg('b', self.typeof(op1)[1])
1892
 
                        self.code.append(["not", "%s %s" % (lhs, op1)])
1893
 
                        self.push(lhs)
1894
 
                        return
1895
 
                elif node.relop == "&&":
1896
 
                        opc = "and"
1897
 
                elif node.relop == "||":
1898
 
                        opc = "or"
1899
 
                else:
1900
 
                        try:
1901
 
                                opc = self.relop_dict[node.relop]
1902
 
                        except: # never happens
1903
 
                                raise Exception("Line %d: Unknown relop '%s'?!?" % (node.lineno, node.relop))
1904
 
                op2, op1 = self.pop(), self.pop()
1905
 
                op1t, op2t = self.typeof(op1), self.typeof(op2)
1906
 
                if op1t[1] == 'v' and op2t[1] == 'v':   # both varying, lhs varying
1907
 
                        lhs_type = 'bv'
1908
 
                elif op1t[1] == 'v' or op2t[1] == 'v':  # either varying, lhs varying
1909
 
                        lhs_type = 'bv'
1910
 
                else:   # both uniform
1911
 
                        lhs_type = 'bu'
1912
 
                if opc == "and" or opc == "or":
1913
 
                        # simple optimization, if previous codeline was of the form 'ifxxxx b2 ...', and this line will be '(and|or) b1 b1 b2'
1914
 
                        # AND b1 b2 have the same detail, then concatenate, modifying previous line to "andxxxx b1 ...'
1915
 
                        last_codeline = self.code[-1]
1916
 
                        if self.optimize and last_codeline[0][:2] == 'if':
1917
 
                                last_ops = last_codeline[1].split()
1918
 
                                if (last_ops[0] == str(op2)) and (op1t[1] == op2t[1]):
1919
 
                                        last_codeline[0] = opc + last_codeline[0][2:]
1920
 
                                        last_codeline[1] = "%s %s %s" % (op1, last_ops[1], last_ops[2])
1921
 
                                        # NOTE even though op1 is the 'result', cannot just directly push op1 here,
1922
 
                                        # it is necesary to 're-alloc' it anyway, since otherwise reg.count will go out of sync
1923
 
                                        self.push(self.newreg(lhs_type[0], lhs_type[1]))
1924
 
                                        # op2 can now be removed from tempregs
1925
 
                                        print "OPTIM 5 rm", op2, "USE", self.reguse[op2]
1926
 
                                        if self.reguse[op2] == 0:
1927
 
                                                self.tempregs.remove(op2)
1928
 
                                        return
1929
 
                        lhs = self.newreg(lhs_type[0], lhs_type[1])
1930
 
                        self.code.append([opc, "%s %s %s" % (lhs, op1, op2)])
1931
 
                        self.push(lhs)
1932
 
                else:
1933
 
                        lhs = self.newreg('b', op1t[1])
1934
 
                        self.code.append(["if%s" % (opc + op1t[0] + op2t[0]), "%s %s %s" % (lhs, op1, op2)])
1935
 
                        self.push(lhs)
1936
 
 
1937
 
        # used in visitProcedureCall() below
1938
 
        def lookup_slproc(self, slpnames, argtypes, convert=False):
1939
 
                t = None
1940
 
                for p in slpnames:
1941
 
                        pargs = p[1]
1942
 
                        if convert:
1943
 
                                # convert any cpn to v
1944
 
                                ca = ""
1945
 
                                for s in pargs:
1946
 
                                        if s in self.cpn_set:
1947
 
                                                ca += 'v'
1948
 
                                        else:
1949
 
                                                ca += s
1950
 
                                pargs = ca
1951
 
                        # if variadic, only need to match args up to that point
1952
 
                        variadic = pargs[1:].rfind('*') # '*' after first char -> 'variadic' function
1953
 
                        if variadic != -1:
1954
 
                                if pargs[:variadic] == argtypes[:variadic]:
1955
 
                                        t = p
1956
 
                                        break
1957
 
                        elif pargs == argtypes: # otherwise try exact match
1958
 
                                t = p
1959
 
                                break
1960
 
                return t
1961
 
 
1962
 
        def visitProcedureCall(self, node):
1963
 
                sts = len(self.stack)
1964
 
                if node.args:
1965
 
                        for a in reversed(node.args):   # note: must be reverse iteration here
1966
 
                                a.visit(self)
1967
 
                argtypes, vargs = "", ""
1968
 
                # get types of all variables pushed on stack
1969
 
                lhs_det = 'u' # determine lhs detail, only uniform if all operands uniform
1970
 
                for i in range(len(self.stack) - sts):
1971
 
                        t = self.typeof(self.stack[sts + i], False)
1972
 
                        if t[1] == 'v': lhs_det = 'v'
1973
 
                        argtypes = t[0] + argtypes
1974
 
                        vargs = self.typeof(self.stack[sts + i])[0] + vargs
1975
 
                cast = False
1976
 
                if slprocedures.slprocs.has_key(node.name):
1977
 
                        slp = slprocedures.slprocs[node.name]
1978
 
                elif slprocedures.slcastprocs.has_key(node.name):       # cast function
1979
 
                        slp = slprocedures.slcastprocs[node.name]
1980
 
                        cast = True
1981
 
                else:   # not a built-in function, maybe user-defined?
1982
 
                        try:
1983
 
                                slp = self.userfuncs[node.name]
1984
 
                        except:
1985
 
                                raise Exception("Line " + str(node.lineno) + ": Unknown procedure name -> " + node.name)
1986
 
                        else:
1987
 
                                # userdefined function found, expand inline
1988
 
                                self.handleUserFunc(slp)
1989
 
                                return
1990
 
                if len(slp) == 0:
1991
 
                        # dummy function, just pop args, skip functioncall for now, which of course might break things from here...
1992
 
                        for i in range(len(self.stack) - sts):
1993
 
                                self.pop()
1994
 
                        return
1995
 
                # return type, if cast function, use declared type instead (unless void func.)
1996
 
                rtp = slp[0]
1997
 
                tfunc = (rtp == 'T')    # 'template' function
1998
 
                if cast and rtp != '*': rtp = self.typedet[0]
1999
 
                # if it has multiple instruction options, pick the one that matches the argument types
2000
 
                slp = slp[1:]
2001
 
                if len(slp) == 1:       # nothing to choose, only one option
2002
 
                        slproc = slp[0]
2003
 
                else:
2004
 
                        slproc = self.lookup_slproc(slp, argtypes)
2005
 
                        if slproc == None:
2006
 
                                # no match found, use the argument string that has all 'cpn' converted to 'v' and try again
2007
 
                                argtypes = vargs
2008
 
                                slproc = self.lookup_slproc(slp, vargs, True)
2009
 
                                # if still nothing, give up
2010
 
                                if slproc == None:
2011
 
                                        raise Exception("Line %d: Cannot find a match for procedure '%s' and required argument types '%s' (conv.'%s')" % (node.lineno, node.name, argtypes, vargs))
2012
 
                # operands
2013
 
                oprs = ""
2014
 
                numoprs = len(self.stack) - sts
2015
 
                trtp = None     # for 'template' functions, use the first template argument type as the return type
2016
 
                if numoprs:
2017
 
                        for i in range(numoprs):
2018
 
                                if i < len(slproc[1]):
2019
 
                                        if slproc[1][i] == 'T' and trtp == None: trtp = self.typeof(self.top())
2020
 
                                oprs += " " + str(self.pop())
2021
 
                else:
2022
 
                        # no operands, set lhs detail to last defined (if set)
2023
 
                        if self.typedet:
2024
 
                                lhs_det = self.typedet[1]
2025
 
                        else:
2026
 
                                lhs_det = 'v'
2027
 
                # if function is a 'template' function, use the first template argument type as the return type
2028
 
                if tfunc:
2029
 
                        # if function has no arguments, use the declared type instead... (this means that not all code will compile properly unless adding casts)
2030
 
                        if trtp:
2031
 
                                rtp = trtp[0]
2032
 
                        else:
2033
 
                                rtp = self.typedet[0]
2034
 
                # if cast or template function, add return type char to instruction name
2035
 
                iname = slproc[0]
2036
 
                if (cast or tfunc) and rtp != '*':
2037
 
                        if rtp in self.cpn_set:
2038
 
                                iname += 'v'
2039
 
                        else:
2040
 
                                iname += rtp
2041
 
                if rtp == '*':  # void func, no return value
2042
 
                        self.code.append([iname, oprs[1:]])
2043
 
                else:
2044
 
                        reg = self.newreg(rtp, lhs_det)
2045
 
                        self.code.append([iname, str(reg) + oprs])
2046
 
                        self.push(reg)
2047
 
 
2048
 
        # doesn't always work, texture statement not implemented completely yet in VM
2049
 
        def visitTexture(self, node):
2050
 
                # [tex_type, texfilechan, tex_args]
2051
 
                opc = node.tex_type
2052
 
                numopr = len(self.stack)
2053
 
                if node.texfilechan: node.texfilechan.visit(self)
2054
 
                if node.tex_args: node.tex_args.visit(self)
2055
 
                numopr = len(self.stack) - numopr
2056
 
                opt_oprs = ""
2057
 
                if numopr:
2058
 
                        for i in range(numopr):
2059
 
                                opt_oprs += str(self.pop())
2060
 
                                if i != (numopr - 1): opt_oprs += " "
2061
 
                if node.texfilechan:
2062
 
                        lhs = self.newreg(self.typedet[0], self.typedet[1])
2063
 
                        ntfc = node.texfilechan
2064
 
                        if ntfc.expr and ntfc.array_idx:
2065
 
                                opc += "chana"
2066
 
                                if opc[:7] == "texture": opc += self.typedet[0]
2067
 
                                self.code.append([opc, "%s %s %s %d %s" % (lhs, ntfc.id, self.pop(), ntfc.array_idx, opt_oprs)])
2068
 
                        elif ntfc.expr:
2069
 
                                opc += "chan"
2070
 
                                if opc[:7] == "texture": opc += self.typedet[0]
2071
 
                                self.code.append([opc, "%s %s %s %s" % (lhs, ntfc.id, self.pop(), opt_oprs)])
2072
 
                        else:
2073
 
                                if opc == "texture": opc += self.typedet[0]
2074
 
                                self.code.append([opc, "%s %s %s" % (lhs, ntfc.id, opt_oprs)])
2075
 
                        self.push(lhs)
2076
 
 
2077
 
        def visitTexFileChan(self, node):
2078
 
                # [id, expr, array_idx]
2079
 
                if node.expr: node.expr.visit(self)
2080
 
                #if node.array_idx: node.array_idx.visit(self)
2081
 
 
2082
 
        def visitTexArgs(self, node):
2083
 
                if node.args:
2084
 
                        for a in reversed(node.args):   # must do reverse iteration because of how list is built
2085
 
                                a.visit(self)
2086
 
 
2087
 
        def visitFilterstep(self, node):
2088
 
                # edge, s1, s2, paramlist
2089
 
                node.edge.visit(self)
2090
 
                node.s1.visit(self)
2091
 
                if node.s2: node.s2.visit(self)
2092
 
                if node.paramlist: node.visit.paramlist(self)
2093
 
                if node.s2 and node.paramlist:
2094
 
                        raise Exception("Filterstep does not handle paramer lists yet")
2095
 
                elif node.s2:
2096
 
                        op3, op2, op1 = self.pop(), self.pop(), self.pop()
2097
 
                        lhs = self.newreg('f', self.typeof(op1)[1])
2098
 
                        self.code.append(["filterstep2", "%s %s %s %s" % (lhs, op1, op2, op3)])
2099
 
                        self.push(lhs)
2100
 
                else:
2101
 
                        op2, op1 = self.pop(), self.pop()
2102
 
                        lhs = self.newreg('f', self.typeof(op1)[1])
2103
 
                        self.code.append(["filterstep1", "%s %s %s" % (lhs, op1, op2)])
2104
 
                        self.push(lhs)
2105
 
 
2106
 
        # TODO
2107
 
        def visitRotate(self, node):
2108
 
                raise Exception("rotate() function not implemented yet")
2109
 
 
2110
 
        def visitFresnel(self, node):
2111
 
                node.I.visit(self)
2112
 
                node.N.visit(self)
2113
 
                node.eta.visit(self)
2114
 
                node.Kr.visit(self)
2115
 
                node.Kt.visit(self)
2116
 
                op5, op4, op3, op2, op1 = self.pop(), self.pop(), self.pop(), self.pop(), self.pop()
2117
 
                # NOTE: all outputs on lhs
2118
 
                if node.R:      # implies node.T as well
2119
 
                        assert node.T != None
2120
 
                        node.R.visit(self)
2121
 
                        node.T.visit(self)
2122
 
                        op7, op6 = self.pop(), self.pop()
2123
 
                        self.code.append(["fresnel2", "%s %s %s %s %s %s %s" % (op4, op5, op6, op7, op1, op2, op3)])
2124
 
                else:
2125
 
                        self.code.append(["fresnel1", "%s %s %s %s %s" % (op4, op5, op1, op2, op3)])
2126
 
 
2127
 
        # main, starts ast traversal and builds code
2128
 
        def visit(self, node):
2129
 
                # build the code
2130
 
                node.visit(self)
2131
 
                # simple optimization, any "cond_pop" instructions at end of code can be removed,
2132
 
                # since by definition, no code can follow it, it's kind of pointless to execute it anyway...
2133
 
                if self.optimize:
2134
 
                        while self.code[-1][0] == "cond_pop":
2135
 
                                self.code.pop()
2136
 
                # check that the stack is now empty, if it ain't, there has to be some serious error somewhere...
2137
 
                if len(self.stack) != 0:
2138
 
                        raise Exception("Stack is not empty! -> %s" % self.stack)
2139
 
                # print out code
2140
 
                assert self.shader_name != None
2141
 
                fp = open(self.shader_name + ".sqd", 'w')
2142
 
                fp.write("# QDSLC version 0.0.1-alpha\n")       # which it probably will always remain...
2143
 
                fp.write("%s\n" % self.code[0])
2144
 
                # parameters
2145
 
                for name, val in self.params.iteritems():
2146
 
                        fp.write("param %s %s " % (self.fulldetail[val[0][1]], self.fulltype[val[0][0].lower()]))
2147
 
                        if isinstance(val[1], list):    # array
2148
 
                                st = ""
2149
 
                                for v in val[1]:
2150
 
                                        st += str(v) + " "
2151
 
                                fp.write("%s[%d] %s\n" % (name, len(val[1]), st[:-1]))
2152
 
                        else:
2153
 
                                fp.write("%s %s\n" % (name, str(val[1])))
2154
 
                # variables
2155
 
                for v in self.variables:
2156
 
                        if v.arlen:
2157
 
                                fp.write("temp %s %s %s[%d]\n" % (self.fulldetail[v.detail], self.fulltype[v.type], str(v), v.arlen))
2158
 
                        else:
2159
 
                                fp.write("temp %s %s %s\n" % (self.fulldetail[v.detail], self.fulltype[v.type], str(v)))
2160
 
                # temporaries
2161
 
                # if 'simplify_regnames' set, first change all register names to a simpler representation, '$number'
2162
 
                if self.simplify_regnames:
2163
 
                        sreg = {}
2164
 
                        numreg = 1
2165
 
                        for r in self.tempregs:
2166
 
                                sreg[str(r)] = "$%d" % numreg
2167
 
                                numreg += 1
2168
 
                        for r in self.tempregs:
2169
 
                                fp.write("temp %s %s %s\n" % (self.fulldetail[r.detail], self.fulltype[r.type], sreg[str(r)]))
2170
 
                else:
2171
 
                        for r in self.tempregs:
2172
 
                                fp.write("temp %s %s %s\n" % (self.fulldetail[r.detail], self.fulltype[r.type], str(r)))
2173
 
                # constants
2174
 
                # also simplify the register names if needed
2175
 
                if self.simplify_regnames:
2176
 
                        for c in self.const_byval:
2177
 
                                td = self.const_byval[c]
2178
 
                                sreg[str(td)] = "$%d" % numreg
2179
 
                                numreg += 1
2180
 
                        for c in self.const_byval:
2181
 
                                td = self.const_byval[c]
2182
 
                                fp.write("const %s %s %s\n" % (self.fulltype[td.type], sreg[str(td)], str(c)))
2183
 
                else:
2184
 
                        for c in self.const_byval:
2185
 
                                td = self.const_byval[c]
2186
 
                                fp.write("const %s %s %s\n" % (self.fulltype[td.type], str(td), str(c)))
2187
 
                # globals
2188
 
                if len(self.globals_used):
2189
 
                        globstr = "global"
2190
 
                        for v in self.globals_used:
2191
 
                                globstr += " " + v
2192
 
                        fp.write("%s\n" % globstr)
2193
 
                # code
2194
 
                if len(self.initcode):
2195
 
                        fp.write("codesegment @1\n")    # should always be 1...
2196
 
                else:
2197
 
                        fp.write("codesegment\n")
2198
 
                spacest = " "*16
2199
 
                # initcode segment
2200
 
                if len(self.initcode):
2201
 
                        if self.simplify_regnames:
2202
 
                                for cl in self.initcode:
2203
 
                                        if cl[1]:
2204
 
                                                fp.write("\t%s%s" % (cl[0], spacest[:16-len(cl[0])]))
2205
 
                                                for op in cl[1].split():
2206
 
                                                        if op[0] == '$':
2207
 
                                                                fp.write(" %s" % sreg[op])
2208
 
                                                        else:
2209
 
                                                                fp.write(" %s" % op)
2210
 
                                                fp.write('\n')
2211
 
                                        else:
2212
 
                                                fp.write("\t%s\n" % cl[0])
2213
 
                        else:
2214
 
                                for cl in self.initcode:
2215
 
                                        if cl[1]:
2216
 
                                                fp.write("\t%s%s%s\n" % (cl[0], spacest[:16-len(cl[0])], cl[1]))
2217
 
                                        else:
2218
 
                                                fp.write("\t%s\n" % cl[0])
2219
 
                # main code
2220
 
                if self.simplify_regnames:
2221
 
                        for cl in self.code[1:]:
2222
 
                                if cl[1]:
2223
 
                                        fp.write("\t%s%s" % (cl[0], spacest[:16-len(cl[0])]))
2224
 
                                        for op in cl[1].split():
2225
 
                                                if op[0] == '$':
2226
 
                                                        fp.write(" %s" % sreg[op])
2227
 
                                                else:
2228
 
                                                        fp.write(" %s" % op)
2229
 
                                        fp.write('\n')
2230
 
                                else:
2231
 
                                        if cl[0][0] == '@':     # label, no tab
2232
 
                                                fp.write("%s\n" % cl[0])
2233
 
                                        else:
2234
 
                                                fp.write("\t%s\n" % cl[0])
2235
 
                else:
2236
 
                        for cl in self.code[1:]:
2237
 
                                if cl[1]:
2238
 
                                        fp.write("\t%s%s%s\n" % (cl[0], spacest[:16-len(cl[0])], cl[1]))
2239
 
                                else:
2240
 
                                        if cl[0][0] == '@':     # label, no tab
2241
 
                                                fp.write("%s\n" % cl[0])
2242
 
                                        else:
2243
 
                                                fp.write("\t%s\n" % cl[0])
2244
 
                fp.write("\treturn\n")
2245
 
                fp.close()
2246