Mercurial > p > roundup > code
comparison roundup/cgi/TAL/TALGenerator.py @ 1049:b9988e118055
moved
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Thu, 05 Sep 2002 00:37:09 +0000 |
| parents | |
| children | 8dd4f736370b |
comparison
equal
deleted
inserted
replaced
| 1048:1250251f2793 | 1049:b9988e118055 |
|---|---|
| 1 ############################################################################## | |
| 2 # | |
| 3 # Copyright (c) 2001, 2002 Zope Corporation and Contributors. | |
| 4 # All Rights Reserved. | |
| 5 # | |
| 6 # This software is subject to the provisions of the Zope Public License, | |
| 7 # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. | |
| 8 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED | |
| 9 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
| 10 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS | |
| 11 # FOR A PARTICULAR PURPOSE | |
| 12 # | |
| 13 ############################################################################## | |
| 14 """ | |
| 15 Code generator for TALInterpreter intermediate code. | |
| 16 """ | |
| 17 | |
| 18 import string | |
| 19 import re | |
| 20 import cgi | |
| 21 | |
| 22 from TALDefs import * | |
| 23 | |
| 24 class TALGenerator: | |
| 25 | |
| 26 inMacroUse = 0 | |
| 27 inMacroDef = 0 | |
| 28 source_file = None | |
| 29 | |
| 30 def __init__(self, expressionCompiler=None, xml=1, source_file=None): | |
| 31 if not expressionCompiler: | |
| 32 from DummyEngine import DummyEngine | |
| 33 expressionCompiler = DummyEngine() | |
| 34 self.expressionCompiler = expressionCompiler | |
| 35 self.CompilerError = expressionCompiler.getCompilerError() | |
| 36 self.program = [] | |
| 37 self.stack = [] | |
| 38 self.todoStack = [] | |
| 39 self.macros = {} | |
| 40 self.slots = {} | |
| 41 self.slotStack = [] | |
| 42 self.xml = xml | |
| 43 self.emit("version", TAL_VERSION) | |
| 44 self.emit("mode", xml and "xml" or "html") | |
| 45 if source_file is not None: | |
| 46 self.source_file = source_file | |
| 47 self.emit("setSourceFile", source_file) | |
| 48 | |
| 49 def getCode(self): | |
| 50 assert not self.stack | |
| 51 assert not self.todoStack | |
| 52 return self.optimize(self.program), self.macros | |
| 53 | |
| 54 def optimize(self, program): | |
| 55 output = [] | |
| 56 collect = [] | |
| 57 rawseen = cursor = 0 | |
| 58 if self.xml: | |
| 59 endsep = "/>" | |
| 60 else: | |
| 61 endsep = " />" | |
| 62 for cursor in xrange(len(program)+1): | |
| 63 try: | |
| 64 item = program[cursor] | |
| 65 except IndexError: | |
| 66 item = (None, None) | |
| 67 opcode = item[0] | |
| 68 if opcode == "rawtext": | |
| 69 collect.append(item[1]) | |
| 70 continue | |
| 71 if opcode == "endTag": | |
| 72 collect.append("</%s>" % item[1]) | |
| 73 continue | |
| 74 if opcode == "startTag": | |
| 75 if self.optimizeStartTag(collect, item[1], item[2], ">"): | |
| 76 continue | |
| 77 if opcode == "startEndTag": | |
| 78 if self.optimizeStartTag(collect, item[1], item[2], endsep): | |
| 79 continue | |
| 80 if opcode in ("beginScope", "endScope"): | |
| 81 # Push *Scope instructions in front of any text instructions; | |
| 82 # this allows text instructions separated only by *Scope | |
| 83 # instructions to be joined together. | |
| 84 output.append(self.optimizeArgsList(item)) | |
| 85 continue | |
| 86 text = string.join(collect, "") | |
| 87 if text: | |
| 88 i = string.rfind(text, "\n") | |
| 89 if i >= 0: | |
| 90 i = len(text) - (i + 1) | |
| 91 output.append(("rawtextColumn", (text, i))) | |
| 92 else: | |
| 93 output.append(("rawtextOffset", (text, len(text)))) | |
| 94 if opcode != None: | |
| 95 output.append(self.optimizeArgsList(item)) | |
| 96 rawseen = cursor+1 | |
| 97 collect = [] | |
| 98 return self.optimizeCommonTriple(output) | |
| 99 | |
| 100 def optimizeArgsList(self, item): | |
| 101 if len(item) == 2: | |
| 102 return item | |
| 103 else: | |
| 104 return item[0], tuple(item[1:]) | |
| 105 | |
| 106 actionIndex = {"replace":0, "insert":1, "metal":2, "tal":3, "xmlns":4, | |
| 107 0: 0, 1: 1, 2: 2, 3: 3, 4: 4} | |
| 108 def optimizeStartTag(self, collect, name, attrlist, end): | |
| 109 if not attrlist: | |
| 110 collect.append("<%s%s" % (name, end)) | |
| 111 return 1 | |
| 112 opt = 1 | |
| 113 new = ["<" + name] | |
| 114 for i in range(len(attrlist)): | |
| 115 item = attrlist[i] | |
| 116 if len(item) > 2: | |
| 117 opt = 0 | |
| 118 name, value, action = item[:3] | |
| 119 action = self.actionIndex[action] | |
| 120 attrlist[i] = (name, value, action) + item[3:] | |
| 121 else: | |
| 122 if item[1] is None: | |
| 123 s = item[0] | |
| 124 else: | |
| 125 s = "%s=%s" % (item[0], quote(item[1])) | |
| 126 attrlist[i] = item[0], s | |
| 127 if item[1] is None: | |
| 128 new.append(" " + item[0]) | |
| 129 else: | |
| 130 new.append(" %s=%s" % (item[0], quote(item[1]))) | |
| 131 if opt: | |
| 132 new.append(end) | |
| 133 collect.extend(new) | |
| 134 return opt | |
| 135 | |
| 136 def optimizeCommonTriple(self, program): | |
| 137 if len(program) < 3: | |
| 138 return program | |
| 139 output = program[:2] | |
| 140 prev2, prev1 = output | |
| 141 for item in program[2:]: | |
| 142 if ( item[0] == "beginScope" | |
| 143 and prev1[0] == "setPosition" | |
| 144 and prev2[0] == "rawtextColumn"): | |
| 145 position = output.pop()[1] | |
| 146 text, column = output.pop()[1] | |
| 147 prev1 = None, None | |
| 148 closeprev = 0 | |
| 149 if output and output[-1][0] == "endScope": | |
| 150 closeprev = 1 | |
| 151 output.pop() | |
| 152 item = ("rawtextBeginScope", | |
| 153 (text, column, position, closeprev, item[1])) | |
| 154 output.append(item) | |
| 155 prev2 = prev1 | |
| 156 prev1 = item | |
| 157 return output | |
| 158 | |
| 159 def todoPush(self, todo): | |
| 160 self.todoStack.append(todo) | |
| 161 | |
| 162 def todoPop(self): | |
| 163 return self.todoStack.pop() | |
| 164 | |
| 165 def compileExpression(self, expr): | |
| 166 try: | |
| 167 return self.expressionCompiler.compile(expr) | |
| 168 except self.CompilerError, err: | |
| 169 raise TALError('%s in expression %s' % (err.args[0], `expr`), | |
| 170 self.position) | |
| 171 | |
| 172 def pushProgram(self): | |
| 173 self.stack.append(self.program) | |
| 174 self.program = [] | |
| 175 | |
| 176 def popProgram(self): | |
| 177 program = self.program | |
| 178 self.program = self.stack.pop() | |
| 179 return self.optimize(program) | |
| 180 | |
| 181 def pushSlots(self): | |
| 182 self.slotStack.append(self.slots) | |
| 183 self.slots = {} | |
| 184 | |
| 185 def popSlots(self): | |
| 186 slots = self.slots | |
| 187 self.slots = self.slotStack.pop() | |
| 188 return slots | |
| 189 | |
| 190 def emit(self, *instruction): | |
| 191 self.program.append(instruction) | |
| 192 | |
| 193 def emitStartTag(self, name, attrlist, isend=0): | |
| 194 if isend: | |
| 195 opcode = "startEndTag" | |
| 196 else: | |
| 197 opcode = "startTag" | |
| 198 self.emit(opcode, name, attrlist) | |
| 199 | |
| 200 def emitEndTag(self, name): | |
| 201 if self.xml and self.program and self.program[-1][0] == "startTag": | |
| 202 # Minimize empty element | |
| 203 self.program[-1] = ("startEndTag",) + self.program[-1][1:] | |
| 204 else: | |
| 205 self.emit("endTag", name) | |
| 206 | |
| 207 def emitOptTag(self, name, optTag, isend): | |
| 208 program = self.popProgram() #block | |
| 209 start = self.popProgram() #start tag | |
| 210 if (isend or not program) and self.xml: | |
| 211 # Minimize empty element | |
| 212 start[-1] = ("startEndTag",) + start[-1][1:] | |
| 213 isend = 1 | |
| 214 cexpr = optTag[0] | |
| 215 if cexpr: | |
| 216 cexpr = self.compileExpression(optTag[0]) | |
| 217 self.emit("optTag", name, cexpr, optTag[1], isend, start, program) | |
| 218 | |
| 219 def emitRawText(self, text): | |
| 220 self.emit("rawtext", text) | |
| 221 | |
| 222 def emitText(self, text): | |
| 223 self.emitRawText(cgi.escape(text)) | |
| 224 | |
| 225 def emitDefines(self, defines): | |
| 226 for part in splitParts(defines): | |
| 227 m = re.match( | |
| 228 r"(?s)\s*(?:(global|local)\s+)?(%s)\s+(.*)\Z" % NAME_RE, part) | |
| 229 if not m: | |
| 230 raise TALError("invalid define syntax: " + `part`, | |
| 231 self.position) | |
| 232 scope, name, expr = m.group(1, 2, 3) | |
| 233 scope = scope or "local" | |
| 234 cexpr = self.compileExpression(expr) | |
| 235 if scope == "local": | |
| 236 self.emit("setLocal", name, cexpr) | |
| 237 else: | |
| 238 self.emit("setGlobal", name, cexpr) | |
| 239 | |
| 240 def emitOnError(self, name, onError): | |
| 241 block = self.popProgram() | |
| 242 key, expr = parseSubstitution(onError) | |
| 243 cexpr = self.compileExpression(expr) | |
| 244 if key == "text": | |
| 245 self.emit("insertText", cexpr, []) | |
| 246 else: | |
| 247 assert key == "structure" | |
| 248 self.emit("insertStructure", cexpr, {}, []) | |
| 249 self.emitEndTag(name) | |
| 250 handler = self.popProgram() | |
| 251 self.emit("onError", block, handler) | |
| 252 | |
| 253 def emitCondition(self, expr): | |
| 254 cexpr = self.compileExpression(expr) | |
| 255 program = self.popProgram() | |
| 256 self.emit("condition", cexpr, program) | |
| 257 | |
| 258 def emitRepeat(self, arg): | |
| 259 m = re.match("(?s)\s*(%s)\s+(.*)\Z" % NAME_RE, arg) | |
| 260 if not m: | |
| 261 raise TALError("invalid repeat syntax: " + `arg`, | |
| 262 self.position) | |
| 263 name, expr = m.group(1, 2) | |
| 264 cexpr = self.compileExpression(expr) | |
| 265 program = self.popProgram() | |
| 266 self.emit("loop", name, cexpr, program) | |
| 267 | |
| 268 def emitSubstitution(self, arg, attrDict={}): | |
| 269 key, expr = parseSubstitution(arg) | |
| 270 cexpr = self.compileExpression(expr) | |
| 271 program = self.popProgram() | |
| 272 if key == "text": | |
| 273 self.emit("insertText", cexpr, program) | |
| 274 else: | |
| 275 assert key == "structure" | |
| 276 self.emit("insertStructure", cexpr, attrDict, program) | |
| 277 | |
| 278 def emitDefineMacro(self, macroName): | |
| 279 program = self.popProgram() | |
| 280 macroName = string.strip(macroName) | |
| 281 if self.macros.has_key(macroName): | |
| 282 raise METALError("duplicate macro definition: %s" % `macroName`, | |
| 283 self.position) | |
| 284 if not re.match('%s$' % NAME_RE, macroName): | |
| 285 raise METALError("invalid macro name: %s" % `macroName`, | |
| 286 self.position) | |
| 287 self.macros[macroName] = program | |
| 288 self.inMacroDef = self.inMacroDef - 1 | |
| 289 self.emit("defineMacro", macroName, program) | |
| 290 | |
| 291 def emitUseMacro(self, expr): | |
| 292 cexpr = self.compileExpression(expr) | |
| 293 program = self.popProgram() | |
| 294 self.inMacroUse = 0 | |
| 295 self.emit("useMacro", expr, cexpr, self.popSlots(), program) | |
| 296 | |
| 297 def emitDefineSlot(self, slotName): | |
| 298 program = self.popProgram() | |
| 299 slotName = string.strip(slotName) | |
| 300 if not re.match('%s$' % NAME_RE, slotName): | |
| 301 raise METALError("invalid slot name: %s" % `slotName`, | |
| 302 self.position) | |
| 303 self.emit("defineSlot", slotName, program) | |
| 304 | |
| 305 def emitFillSlot(self, slotName): | |
| 306 program = self.popProgram() | |
| 307 slotName = string.strip(slotName) | |
| 308 if self.slots.has_key(slotName): | |
| 309 raise METALError("duplicate fill-slot name: %s" % `slotName`, | |
| 310 self.position) | |
| 311 if not re.match('%s$' % NAME_RE, slotName): | |
| 312 raise METALError("invalid slot name: %s" % `slotName`, | |
| 313 self.position) | |
| 314 self.slots[slotName] = program | |
| 315 self.inMacroUse = 1 | |
| 316 self.emit("fillSlot", slotName, program) | |
| 317 | |
| 318 def unEmitWhitespace(self): | |
| 319 collect = [] | |
| 320 i = len(self.program) - 1 | |
| 321 while i >= 0: | |
| 322 item = self.program[i] | |
| 323 if item[0] != "rawtext": | |
| 324 break | |
| 325 text = item[1] | |
| 326 if not re.match(r"\A\s*\Z", text): | |
| 327 break | |
| 328 collect.append(text) | |
| 329 i = i-1 | |
| 330 del self.program[i+1:] | |
| 331 if i >= 0 and self.program[i][0] == "rawtext": | |
| 332 text = self.program[i][1] | |
| 333 m = re.search(r"\s+\Z", text) | |
| 334 if m: | |
| 335 self.program[i] = ("rawtext", text[:m.start()]) | |
| 336 collect.append(m.group()) | |
| 337 collect.reverse() | |
| 338 return string.join(collect, "") | |
| 339 | |
| 340 def unEmitNewlineWhitespace(self): | |
| 341 collect = [] | |
| 342 i = len(self.program) | |
| 343 while i > 0: | |
| 344 i = i-1 | |
| 345 item = self.program[i] | |
| 346 if item[0] != "rawtext": | |
| 347 break | |
| 348 text = item[1] | |
| 349 if re.match(r"\A[ \t]*\Z", text): | |
| 350 collect.append(text) | |
| 351 continue | |
| 352 m = re.match(r"(?s)^(.*)(\n[ \t]*)\Z", text) | |
| 353 if not m: | |
| 354 break | |
| 355 text, rest = m.group(1, 2) | |
| 356 collect.reverse() | |
| 357 rest = rest + string.join(collect, "") | |
| 358 del self.program[i:] | |
| 359 if text: | |
| 360 self.emit("rawtext", text) | |
| 361 return rest | |
| 362 return None | |
| 363 | |
| 364 def replaceAttrs(self, attrlist, repldict): | |
| 365 if not repldict: | |
| 366 return attrlist | |
| 367 newlist = [] | |
| 368 for item in attrlist: | |
| 369 key = item[0] | |
| 370 if repldict.has_key(key): | |
| 371 item = item[:2] + ("replace", repldict[key]) | |
| 372 del repldict[key] | |
| 373 newlist.append(item) | |
| 374 for key, value in repldict.items(): # Add dynamic-only attributes | |
| 375 item = (key, None, "insert", value) | |
| 376 newlist.append(item) | |
| 377 return newlist | |
| 378 | |
| 379 def emitStartElement(self, name, attrlist, taldict, metaldict, | |
| 380 position=(None, None), isend=0): | |
| 381 if not taldict and not metaldict: | |
| 382 # Handle the simple, common case | |
| 383 self.emitStartTag(name, attrlist, isend) | |
| 384 self.todoPush({}) | |
| 385 if isend: | |
| 386 self.emitEndElement(name, isend) | |
| 387 return | |
| 388 | |
| 389 self.position = position | |
| 390 for key, value in taldict.items(): | |
| 391 if key not in KNOWN_TAL_ATTRIBUTES: | |
| 392 raise TALError("bad TAL attribute: " + `key`, position) | |
| 393 if not (value or key == 'omit-tag'): | |
| 394 raise TALError("missing value for TAL attribute: " + | |
| 395 `key`, position) | |
| 396 for key, value in metaldict.items(): | |
| 397 if key not in KNOWN_METAL_ATTRIBUTES: | |
| 398 raise METALError("bad METAL attribute: " + `key`, | |
| 399 position) | |
| 400 if not value: | |
| 401 raise TALError("missing value for METAL attribute: " + | |
| 402 `key`, position) | |
| 403 todo = {} | |
| 404 defineMacro = metaldict.get("define-macro") | |
| 405 useMacro = metaldict.get("use-macro") | |
| 406 defineSlot = metaldict.get("define-slot") | |
| 407 fillSlot = metaldict.get("fill-slot") | |
| 408 define = taldict.get("define") | |
| 409 condition = taldict.get("condition") | |
| 410 repeat = taldict.get("repeat") | |
| 411 content = taldict.get("content") | |
| 412 replace = taldict.get("replace") | |
| 413 attrsubst = taldict.get("attributes") | |
| 414 onError = taldict.get("on-error") | |
| 415 omitTag = taldict.get("omit-tag") | |
| 416 TALtag = taldict.get("tal tag") | |
| 417 if len(metaldict) > 1 and (defineMacro or useMacro): | |
| 418 raise METALError("define-macro and use-macro cannot be used " | |
| 419 "together or with define-slot or fill-slot", | |
| 420 position) | |
| 421 if content and replace: | |
| 422 raise TALError("content and replace are mutually exclusive", | |
| 423 position) | |
| 424 | |
| 425 repeatWhitespace = None | |
| 426 if repeat: | |
| 427 # Hack to include preceding whitespace in the loop program | |
| 428 repeatWhitespace = self.unEmitNewlineWhitespace() | |
| 429 if position != (None, None): | |
| 430 # XXX at some point we should insist on a non-trivial position | |
| 431 self.emit("setPosition", position) | |
| 432 if self.inMacroUse: | |
| 433 if fillSlot: | |
| 434 self.pushProgram() | |
| 435 if self.source_file is not None: | |
| 436 self.emit("setSourceFile", self.source_file) | |
| 437 todo["fillSlot"] = fillSlot | |
| 438 self.inMacroUse = 0 | |
| 439 else: | |
| 440 if fillSlot: | |
| 441 raise METALError, ("fill-slot must be within a use-macro", | |
| 442 position) | |
| 443 if not self.inMacroUse: | |
| 444 if defineMacro: | |
| 445 self.pushProgram() | |
| 446 self.emit("version", TAL_VERSION) | |
| 447 self.emit("mode", self.xml and "xml" or "html") | |
| 448 if self.source_file is not None: | |
| 449 self.emit("setSourceFile", self.source_file) | |
| 450 todo["defineMacro"] = defineMacro | |
| 451 self.inMacroDef = self.inMacroDef + 1 | |
| 452 if useMacro: | |
| 453 self.pushSlots() | |
| 454 self.pushProgram() | |
| 455 todo["useMacro"] = useMacro | |
| 456 self.inMacroUse = 1 | |
| 457 if defineSlot: | |
| 458 if not self.inMacroDef: | |
| 459 raise METALError, ( | |
| 460 "define-slot must be within a define-macro", | |
| 461 position) | |
| 462 self.pushProgram() | |
| 463 todo["defineSlot"] = defineSlot | |
| 464 | |
| 465 if taldict: | |
| 466 dict = {} | |
| 467 for item in attrlist: | |
| 468 key, value = item[:2] | |
| 469 dict[key] = value | |
| 470 self.emit("beginScope", dict) | |
| 471 todo["scope"] = 1 | |
| 472 if onError: | |
| 473 self.pushProgram() # handler | |
| 474 self.emitStartTag(name, list(attrlist)) # Must copy attrlist! | |
| 475 self.pushProgram() # block | |
| 476 todo["onError"] = onError | |
| 477 if define: | |
| 478 self.emitDefines(define) | |
| 479 todo["define"] = define | |
| 480 if condition: | |
| 481 self.pushProgram() | |
| 482 todo["condition"] = condition | |
| 483 if repeat: | |
| 484 todo["repeat"] = repeat | |
| 485 self.pushProgram() | |
| 486 if repeatWhitespace: | |
| 487 self.emitText(repeatWhitespace) | |
| 488 if content: | |
| 489 todo["content"] = content | |
| 490 if replace: | |
| 491 todo["replace"] = replace | |
| 492 self.pushProgram() | |
| 493 optTag = omitTag is not None or TALtag | |
| 494 if optTag: | |
| 495 todo["optional tag"] = omitTag, TALtag | |
| 496 self.pushProgram() | |
| 497 if attrsubst: | |
| 498 repldict = parseAttributeReplacements(attrsubst) | |
| 499 for key, value in repldict.items(): | |
| 500 repldict[key] = self.compileExpression(value) | |
| 501 else: | |
| 502 repldict = {} | |
| 503 if replace: | |
| 504 todo["repldict"] = repldict | |
| 505 repldict = {} | |
| 506 self.emitStartTag(name, self.replaceAttrs(attrlist, repldict), isend) | |
| 507 if optTag: | |
| 508 self.pushProgram() | |
| 509 if content: | |
| 510 self.pushProgram() | |
| 511 if todo and position != (None, None): | |
| 512 todo["position"] = position | |
| 513 self.todoPush(todo) | |
| 514 if isend: | |
| 515 self.emitEndElement(name, isend) | |
| 516 | |
| 517 def emitEndElement(self, name, isend=0, implied=0): | |
| 518 todo = self.todoPop() | |
| 519 if not todo: | |
| 520 # Shortcut | |
| 521 if not isend: | |
| 522 self.emitEndTag(name) | |
| 523 return | |
| 524 | |
| 525 self.position = position = todo.get("position", (None, None)) | |
| 526 defineMacro = todo.get("defineMacro") | |
| 527 useMacro = todo.get("useMacro") | |
| 528 defineSlot = todo.get("defineSlot") | |
| 529 fillSlot = todo.get("fillSlot") | |
| 530 repeat = todo.get("repeat") | |
| 531 content = todo.get("content") | |
| 532 replace = todo.get("replace") | |
| 533 condition = todo.get("condition") | |
| 534 onError = todo.get("onError") | |
| 535 define = todo.get("define") | |
| 536 repldict = todo.get("repldict", {}) | |
| 537 scope = todo.get("scope") | |
| 538 optTag = todo.get("optional tag") | |
| 539 | |
| 540 if implied > 0: | |
| 541 if defineMacro or useMacro or defineSlot or fillSlot: | |
| 542 exc = METALError | |
| 543 what = "METAL" | |
| 544 else: | |
| 545 exc = TALError | |
| 546 what = "TAL" | |
| 547 raise exc("%s attributes on <%s> require explicit </%s>" % | |
| 548 (what, name, name), position) | |
| 549 | |
| 550 if content: | |
| 551 self.emitSubstitution(content, {}) | |
| 552 if optTag: | |
| 553 self.emitOptTag(name, optTag, isend) | |
| 554 elif not isend: | |
| 555 self.emitEndTag(name) | |
| 556 if replace: | |
| 557 self.emitSubstitution(replace, repldict) | |
| 558 if repeat: | |
| 559 self.emitRepeat(repeat) | |
| 560 if condition: | |
| 561 self.emitCondition(condition) | |
| 562 if onError: | |
| 563 self.emitOnError(name, onError) | |
| 564 if scope: | |
| 565 self.emit("endScope") | |
| 566 if defineSlot: | |
| 567 self.emitDefineSlot(defineSlot) | |
| 568 if fillSlot: | |
| 569 self.emitFillSlot(fillSlot) | |
| 570 if useMacro: | |
| 571 self.emitUseMacro(useMacro) | |
| 572 if defineMacro: | |
| 573 self.emitDefineMacro(defineMacro) | |
| 574 | |
| 575 def test(): | |
| 576 t = TALGenerator() | |
| 577 t.pushProgram() | |
| 578 t.emit("bar") | |
| 579 p = t.popProgram() | |
| 580 t.emit("foo", p) | |
| 581 | |
| 582 if __name__ == "__main__": | |
| 583 test() |
