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

« back to all changes in this revision

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

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#-------------------------------------------------------------------------
 
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