Mercurial > p > roundup > code
comparison roundup/cgi/TAL/TALInterpreter.py @ 1049:b9988e118055
moved
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Thu, 05 Sep 2002 00:37:09 +0000 |
| parents | |
| children | 309f125f86cc |
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 Interpreter for a pre-compiled TAL program. | |
| 16 """ | |
| 17 | |
| 18 import sys | |
| 19 import getopt | |
| 20 | |
| 21 from cgi import escape | |
| 22 from string import join, lower, rfind | |
| 23 try: | |
| 24 from strop import lower, rfind | |
| 25 except ImportError: | |
| 26 pass | |
| 27 | |
| 28 try: | |
| 29 from cStringIO import StringIO | |
| 30 except ImportError: | |
| 31 from StringIO import StringIO | |
| 32 | |
| 33 from TALDefs import quote, TAL_VERSION, TALError, METALError | |
| 34 from TALDefs import isCurrentVersion, getProgramVersion, getProgramMode | |
| 35 from TALGenerator import TALGenerator | |
| 36 | |
| 37 BOOLEAN_HTML_ATTRS = [ | |
| 38 # List of Boolean attributes in HTML that should be rendered in | |
| 39 # minimized form (e.g. <img ismap> rather than <img ismap="">) | |
| 40 # From http://www.w3.org/TR/xhtml1/#guidelines (C.10) | |
| 41 # XXX The problem with this is that this is not valid XML and | |
| 42 # can't be parsed back! | |
| 43 "compact", "nowrap", "ismap", "declare", "noshade", "checked", | |
| 44 "disabled", "readonly", "multiple", "selected", "noresize", | |
| 45 "defer" | |
| 46 ] | |
| 47 | |
| 48 EMPTY_HTML_TAGS = [ | |
| 49 # List of HTML tags with an empty content model; these are | |
| 50 # rendered in minimized form, e.g. <img />. | |
| 51 # From http://www.w3.org/TR/xhtml1/#dtds | |
| 52 "base", "meta", "link", "hr", "br", "param", "img", "area", | |
| 53 "input", "col", "basefont", "isindex", "frame", | |
| 54 ] | |
| 55 | |
| 56 class AltTALGenerator(TALGenerator): | |
| 57 | |
| 58 def __init__(self, repldict, expressionCompiler=None, xml=0): | |
| 59 self.repldict = repldict | |
| 60 self.enabled = 1 | |
| 61 TALGenerator.__init__(self, expressionCompiler, xml) | |
| 62 | |
| 63 def enable(self, enabled): | |
| 64 self.enabled = enabled | |
| 65 | |
| 66 def emit(self, *args): | |
| 67 if self.enabled: | |
| 68 apply(TALGenerator.emit, (self,) + args) | |
| 69 | |
| 70 def emitStartElement(self, name, attrlist, taldict, metaldict, | |
| 71 position=(None, None), isend=0): | |
| 72 metaldict = {} | |
| 73 taldict = {} | |
| 74 if self.enabled and self.repldict: | |
| 75 taldict["attributes"] = "x x" | |
| 76 TALGenerator.emitStartElement(self, name, attrlist, | |
| 77 taldict, metaldict, position, isend) | |
| 78 | |
| 79 def replaceAttrs(self, attrlist, repldict): | |
| 80 if self.enabled and self.repldict: | |
| 81 repldict = self.repldict | |
| 82 self.repldict = None | |
| 83 return TALGenerator.replaceAttrs(self, attrlist, repldict) | |
| 84 | |
| 85 | |
| 86 class TALInterpreter: | |
| 87 | |
| 88 def __init__(self, program, macros, engine, stream=None, | |
| 89 debug=0, wrap=60, metal=1, tal=1, showtal=-1, | |
| 90 strictinsert=1, stackLimit=100): | |
| 91 self.program = program | |
| 92 self.macros = macros | |
| 93 self.engine = engine | |
| 94 self.Default = engine.getDefault() | |
| 95 self.stream = stream or sys.stdout | |
| 96 self._stream_write = self.stream.write | |
| 97 self.debug = debug | |
| 98 self.wrap = wrap | |
| 99 self.metal = metal | |
| 100 self.tal = tal | |
| 101 if tal: | |
| 102 self.dispatch = self.bytecode_handlers_tal | |
| 103 else: | |
| 104 self.dispatch = self.bytecode_handlers | |
| 105 assert showtal in (-1, 0, 1) | |
| 106 if showtal == -1: | |
| 107 showtal = (not tal) | |
| 108 self.showtal = showtal | |
| 109 self.strictinsert = strictinsert | |
| 110 self.stackLimit = stackLimit | |
| 111 self.html = 0 | |
| 112 self.endsep = "/>" | |
| 113 self.endlen = len(self.endsep) | |
| 114 self.macroStack = [] | |
| 115 self.popMacro = self.macroStack.pop | |
| 116 self.position = None, None # (lineno, offset) | |
| 117 self.col = 0 | |
| 118 self.level = 0 | |
| 119 self.scopeLevel = 0 | |
| 120 self.sourceFile = None | |
| 121 | |
| 122 def saveState(self): | |
| 123 return (self.position, self.col, self.stream, | |
| 124 self.scopeLevel, self.level) | |
| 125 | |
| 126 def restoreState(self, state): | |
| 127 (self.position, self.col, self.stream, scopeLevel, level) = state | |
| 128 self._stream_write = self.stream.write | |
| 129 assert self.level == level | |
| 130 while self.scopeLevel > scopeLevel: | |
| 131 self.engine.endScope() | |
| 132 self.scopeLevel = self.scopeLevel - 1 | |
| 133 self.engine.setPosition(self.position) | |
| 134 | |
| 135 def restoreOutputState(self, state): | |
| 136 (dummy, self.col, self.stream, scopeLevel, level) = state | |
| 137 self._stream_write = self.stream.write | |
| 138 assert self.level == level | |
| 139 assert self.scopeLevel == scopeLevel | |
| 140 | |
| 141 def pushMacro(self, macroName, slots, entering=1): | |
| 142 if len(self.macroStack) >= self.stackLimit: | |
| 143 raise METALError("macro nesting limit (%d) exceeded " | |
| 144 "by %s" % (self.stackLimit, `macroName`)) | |
| 145 self.macroStack.append([macroName, slots, entering]) | |
| 146 | |
| 147 def macroContext(self, what): | |
| 148 macroStack = self.macroStack | |
| 149 i = len(macroStack) | |
| 150 while i > 0: | |
| 151 i = i-1 | |
| 152 if macroStack[i][0] == what: | |
| 153 return i | |
| 154 return -1 | |
| 155 | |
| 156 def __call__(self): | |
| 157 assert self.level == 0 | |
| 158 assert self.scopeLevel == 0 | |
| 159 self.interpret(self.program) | |
| 160 assert self.level == 0 | |
| 161 assert self.scopeLevel == 0 | |
| 162 if self.col > 0: | |
| 163 self._stream_write("\n") | |
| 164 self.col = 0 | |
| 165 | |
| 166 def stream_write(self, s, | |
| 167 len=len, rfind=rfind): | |
| 168 self._stream_write(s) | |
| 169 i = rfind(s, '\n') | |
| 170 if i < 0: | |
| 171 self.col = self.col + len(s) | |
| 172 else: | |
| 173 self.col = len(s) - (i + 1) | |
| 174 | |
| 175 bytecode_handlers = {} | |
| 176 | |
| 177 def interpret(self, program, None=None): | |
| 178 oldlevel = self.level | |
| 179 self.level = oldlevel + 1 | |
| 180 handlers = self.dispatch | |
| 181 try: | |
| 182 if self.debug: | |
| 183 for (opcode, args) in program: | |
| 184 s = "%sdo_%s%s\n" % (" "*self.level, opcode, | |
| 185 repr(args)) | |
| 186 if len(s) > 80: | |
| 187 s = s[:76] + "...\n" | |
| 188 sys.stderr.write(s) | |
| 189 handlers[opcode](self, args) | |
| 190 else: | |
| 191 for (opcode, args) in program: | |
| 192 handlers[opcode](self, args) | |
| 193 finally: | |
| 194 self.level = oldlevel | |
| 195 | |
| 196 def do_version(self, version): | |
| 197 assert version == TAL_VERSION | |
| 198 bytecode_handlers["version"] = do_version | |
| 199 | |
| 200 def do_mode(self, mode): | |
| 201 assert mode in ("html", "xml") | |
| 202 self.html = (mode == "html") | |
| 203 if self.html: | |
| 204 self.endsep = " />" | |
| 205 else: | |
| 206 self.endsep = "/>" | |
| 207 self.endlen = len(self.endsep) | |
| 208 bytecode_handlers["mode"] = do_mode | |
| 209 | |
| 210 def do_setSourceFile(self, source_file): | |
| 211 self.sourceFile = source_file | |
| 212 self.engine.setSourceFile(source_file) | |
| 213 bytecode_handlers["setSourceFile"] = do_setSourceFile | |
| 214 | |
| 215 def do_setPosition(self, position): | |
| 216 self.position = position | |
| 217 self.engine.setPosition(position) | |
| 218 bytecode_handlers["setPosition"] = do_setPosition | |
| 219 | |
| 220 def do_startEndTag(self, stuff): | |
| 221 self.do_startTag(stuff, self.endsep, self.endlen) | |
| 222 bytecode_handlers["startEndTag"] = do_startEndTag | |
| 223 | |
| 224 def do_startTag(self, (name, attrList), | |
| 225 end=">", endlen=1, _len=len): | |
| 226 # The bytecode generator does not cause calls to this method | |
| 227 # for start tags with no attributes; those are optimized down | |
| 228 # to rawtext events. Hence, there is no special "fast path" | |
| 229 # for that case. | |
| 230 _stream_write = self._stream_write | |
| 231 _stream_write("<" + name) | |
| 232 namelen = _len(name) | |
| 233 col = self.col + namelen + 1 | |
| 234 wrap = self.wrap | |
| 235 align = col + 1 | |
| 236 if align >= wrap/2: | |
| 237 align = 4 # Avoid a narrow column far to the right | |
| 238 attrAction = self.dispatch["<attrAction>"] | |
| 239 try: | |
| 240 for item in attrList: | |
| 241 if _len(item) == 2: | |
| 242 name, s = item | |
| 243 else: | |
| 244 ok, name, s = attrAction(self, item) | |
| 245 if not ok: | |
| 246 continue | |
| 247 slen = _len(s) | |
| 248 if (wrap and | |
| 249 col >= align and | |
| 250 col + 1 + slen > wrap): | |
| 251 _stream_write("\n" + " "*align) | |
| 252 col = align + slen | |
| 253 else: | |
| 254 s = " " + s | |
| 255 col = col + 1 + slen | |
| 256 _stream_write(s) | |
| 257 _stream_write(end) | |
| 258 col = col + endlen | |
| 259 finally: | |
| 260 self.col = col | |
| 261 bytecode_handlers["startTag"] = do_startTag | |
| 262 | |
| 263 def attrAction(self, item): | |
| 264 name, value, action = item[:3] | |
| 265 if action == 1 or (action > 1 and not self.showtal): | |
| 266 return 0, name, value | |
| 267 macs = self.macroStack | |
| 268 if action == 2 and self.metal and macs: | |
| 269 if len(macs) > 1 or not macs[-1][2]: | |
| 270 # Drop all METAL attributes at a use-depth above one. | |
| 271 return 0, name, value | |
| 272 # Clear 'entering' flag | |
| 273 macs[-1][2] = 0 | |
| 274 # Convert or drop depth-one METAL attributes. | |
| 275 i = rfind(name, ":") + 1 | |
| 276 prefix, suffix = name[:i], name[i:] | |
| 277 if suffix == "define-macro": | |
| 278 # Convert define-macro as we enter depth one. | |
| 279 name = prefix + "use-macro" | |
| 280 value = macs[-1][0] # Macro name | |
| 281 elif suffix == "define-slot": | |
| 282 name = prefix + "slot" | |
| 283 elif suffix == "fill-slot": | |
| 284 pass | |
| 285 else: | |
| 286 return 0, name, value | |
| 287 | |
| 288 if value is None: | |
| 289 value = name | |
| 290 else: | |
| 291 value = "%s=%s" % (name, quote(value)) | |
| 292 return 1, name, value | |
| 293 | |
| 294 def attrAction_tal(self, item): | |
| 295 name, value, action = item[:3] | |
| 296 if action > 1: | |
| 297 return self.attrAction(item) | |
| 298 ok = 1 | |
| 299 if self.html and lower(name) in BOOLEAN_HTML_ATTRS: | |
| 300 evalue = self.engine.evaluateBoolean(item[3]) | |
| 301 if evalue is self.Default: | |
| 302 if action == 1: # Cancelled insert | |
| 303 ok = 0 | |
| 304 elif evalue: | |
| 305 value = None | |
| 306 else: | |
| 307 ok = 0 | |
| 308 else: | |
| 309 evalue = self.engine.evaluateText(item[3]) | |
| 310 if evalue is self.Default: | |
| 311 if action == 1: # Cancelled insert | |
| 312 ok = 0 | |
| 313 else: | |
| 314 if evalue is None: | |
| 315 ok = 0 | |
| 316 value = evalue | |
| 317 if ok: | |
| 318 if value is None: | |
| 319 value = name | |
| 320 value = "%s=%s" % (name, quote(value)) | |
| 321 return ok, name, value | |
| 322 | |
| 323 bytecode_handlers["<attrAction>"] = attrAction | |
| 324 | |
| 325 def no_tag(self, start, program): | |
| 326 state = self.saveState() | |
| 327 self.stream = stream = StringIO() | |
| 328 self._stream_write = stream.write | |
| 329 self.interpret(start) | |
| 330 self.restoreOutputState(state) | |
| 331 self.interpret(program) | |
| 332 | |
| 333 def do_optTag(self, (name, cexpr, tag_ns, isend, start, program), | |
| 334 omit=0): | |
| 335 if tag_ns and not self.showtal: | |
| 336 return self.no_tag(start, program) | |
| 337 | |
| 338 self.interpret(start) | |
| 339 if not isend: | |
| 340 self.interpret(program) | |
| 341 s = '</%s>' % name | |
| 342 self._stream_write(s) | |
| 343 self.col = self.col + len(s) | |
| 344 | |
| 345 def do_optTag_tal(self, stuff): | |
| 346 cexpr = stuff[1] | |
| 347 if cexpr is not None and (cexpr == '' or | |
| 348 self.engine.evaluateBoolean(cexpr)): | |
| 349 self.no_tag(stuff[-2], stuff[-1]) | |
| 350 else: | |
| 351 self.do_optTag(stuff) | |
| 352 bytecode_handlers["optTag"] = do_optTag | |
| 353 | |
| 354 def dumpMacroStack(self, prefix, suffix, value): | |
| 355 sys.stderr.write("+---- %s%s = %s\n" % (prefix, suffix, value)) | |
| 356 for i in range(len(self.macroStack)): | |
| 357 what, macroName, slots = self.macroStack[i] | |
| 358 sys.stderr.write("| %2d. %-12s %-12s %s\n" % | |
| 359 (i, what, macroName, slots and slots.keys())) | |
| 360 sys.stderr.write("+--------------------------------------\n") | |
| 361 | |
| 362 def do_rawtextBeginScope(self, (s, col, position, closeprev, dict)): | |
| 363 self._stream_write(s) | |
| 364 self.col = col | |
| 365 self.do_setPosition(position) | |
| 366 if closeprev: | |
| 367 engine = self.engine | |
| 368 engine.endScope() | |
| 369 engine.beginScope() | |
| 370 else: | |
| 371 self.engine.beginScope() | |
| 372 self.scopeLevel = self.scopeLevel + 1 | |
| 373 | |
| 374 def do_rawtextBeginScope_tal(self, (s, col, position, closeprev, dict)): | |
| 375 self._stream_write(s) | |
| 376 self.col = col | |
| 377 self.do_setPosition(position) | |
| 378 engine = self.engine | |
| 379 if closeprev: | |
| 380 engine.endScope() | |
| 381 engine.beginScope() | |
| 382 else: | |
| 383 engine.beginScope() | |
| 384 self.scopeLevel = self.scopeLevel + 1 | |
| 385 engine.setLocal("attrs", dict) | |
| 386 bytecode_handlers["rawtextBeginScope"] = do_rawtextBeginScope | |
| 387 | |
| 388 def do_beginScope(self, dict): | |
| 389 self.engine.beginScope() | |
| 390 self.scopeLevel = self.scopeLevel + 1 | |
| 391 | |
| 392 def do_beginScope_tal(self, dict): | |
| 393 engine = self.engine | |
| 394 engine.beginScope() | |
| 395 engine.setLocal("attrs", dict) | |
| 396 self.scopeLevel = self.scopeLevel + 1 | |
| 397 bytecode_handlers["beginScope"] = do_beginScope | |
| 398 | |
| 399 def do_endScope(self, notused=None): | |
| 400 self.engine.endScope() | |
| 401 self.scopeLevel = self.scopeLevel - 1 | |
| 402 bytecode_handlers["endScope"] = do_endScope | |
| 403 | |
| 404 def do_setLocal(self, notused): | |
| 405 pass | |
| 406 | |
| 407 def do_setLocal_tal(self, (name, expr)): | |
| 408 self.engine.setLocal(name, self.engine.evaluateValue(expr)) | |
| 409 bytecode_handlers["setLocal"] = do_setLocal | |
| 410 | |
| 411 def do_setGlobal_tal(self, (name, expr)): | |
| 412 self.engine.setGlobal(name, self.engine.evaluateValue(expr)) | |
| 413 bytecode_handlers["setGlobal"] = do_setLocal | |
| 414 | |
| 415 def do_insertText(self, stuff): | |
| 416 self.interpret(stuff[1]) | |
| 417 | |
| 418 def do_insertText_tal(self, stuff): | |
| 419 text = self.engine.evaluateText(stuff[0]) | |
| 420 if text is None: | |
| 421 return | |
| 422 if text is self.Default: | |
| 423 self.interpret(stuff[1]) | |
| 424 return | |
| 425 s = escape(text) | |
| 426 self._stream_write(s) | |
| 427 i = rfind(s, '\n') | |
| 428 if i < 0: | |
| 429 self.col = self.col + len(s) | |
| 430 else: | |
| 431 self.col = len(s) - (i + 1) | |
| 432 bytecode_handlers["insertText"] = do_insertText | |
| 433 | |
| 434 def do_insertStructure(self, stuff): | |
| 435 self.interpret(stuff[2]) | |
| 436 | |
| 437 def do_insertStructure_tal(self, (expr, repldict, block)): | |
| 438 structure = self.engine.evaluateStructure(expr) | |
| 439 if structure is None: | |
| 440 return | |
| 441 if structure is self.Default: | |
| 442 self.interpret(block) | |
| 443 return | |
| 444 text = str(structure) | |
| 445 if not (repldict or self.strictinsert): | |
| 446 # Take a shortcut, no error checking | |
| 447 self.stream_write(text) | |
| 448 return | |
| 449 if self.html: | |
| 450 self.insertHTMLStructure(text, repldict) | |
| 451 else: | |
| 452 self.insertXMLStructure(text, repldict) | |
| 453 bytecode_handlers["insertStructure"] = do_insertStructure | |
| 454 | |
| 455 def insertHTMLStructure(self, text, repldict): | |
| 456 from HTMLTALParser import HTMLTALParser | |
| 457 gen = AltTALGenerator(repldict, self.engine, 0) | |
| 458 p = HTMLTALParser(gen) # Raises an exception if text is invalid | |
| 459 p.parseString(text) | |
| 460 program, macros = p.getCode() | |
| 461 self.interpret(program) | |
| 462 | |
| 463 def insertXMLStructure(self, text, repldict): | |
| 464 from TALParser import TALParser | |
| 465 gen = AltTALGenerator(repldict, self.engine, 0) | |
| 466 p = TALParser(gen) | |
| 467 gen.enable(0) | |
| 468 p.parseFragment('<!DOCTYPE foo PUBLIC "foo" "bar"><foo>') | |
| 469 gen.enable(1) | |
| 470 p.parseFragment(text) # Raises an exception if text is invalid | |
| 471 gen.enable(0) | |
| 472 p.parseFragment('</foo>', 1) | |
| 473 program, macros = gen.getCode() | |
| 474 self.interpret(program) | |
| 475 | |
| 476 def do_loop(self, (name, expr, block)): | |
| 477 self.interpret(block) | |
| 478 | |
| 479 def do_loop_tal(self, (name, expr, block)): | |
| 480 iterator = self.engine.setRepeat(name, expr) | |
| 481 while iterator.next(): | |
| 482 self.interpret(block) | |
| 483 bytecode_handlers["loop"] = do_loop | |
| 484 | |
| 485 def do_rawtextColumn(self, (s, col)): | |
| 486 self._stream_write(s) | |
| 487 self.col = col | |
| 488 bytecode_handlers["rawtextColumn"] = do_rawtextColumn | |
| 489 | |
| 490 def do_rawtextOffset(self, (s, offset)): | |
| 491 self._stream_write(s) | |
| 492 self.col = self.col + offset | |
| 493 bytecode_handlers["rawtextOffset"] = do_rawtextOffset | |
| 494 | |
| 495 def do_condition(self, (condition, block)): | |
| 496 if not self.tal or self.engine.evaluateBoolean(condition): | |
| 497 self.interpret(block) | |
| 498 bytecode_handlers["condition"] = do_condition | |
| 499 | |
| 500 def do_defineMacro(self, (macroName, macro)): | |
| 501 macs = self.macroStack | |
| 502 if len(macs) == 1: | |
| 503 entering = macs[-1][2] | |
| 504 if not entering: | |
| 505 macs.append(None) | |
| 506 self.interpret(macro) | |
| 507 macs.pop() | |
| 508 return | |
| 509 self.interpret(macro) | |
| 510 bytecode_handlers["defineMacro"] = do_defineMacro | |
| 511 | |
| 512 def do_useMacro(self, (macroName, macroExpr, compiledSlots, block)): | |
| 513 if not self.metal: | |
| 514 self.interpret(block) | |
| 515 return | |
| 516 macro = self.engine.evaluateMacro(macroExpr) | |
| 517 if macro is self.Default: | |
| 518 macro = block | |
| 519 else: | |
| 520 if not isCurrentVersion(macro): | |
| 521 raise METALError("macro %s has incompatible version %s" % | |
| 522 (`macroName`, `getProgramVersion(macro)`), | |
| 523 self.position) | |
| 524 mode = getProgramMode(macro) | |
| 525 if mode != (self.html and "html" or "xml"): | |
| 526 raise METALError("macro %s has incompatible mode %s" % | |
| 527 (`macroName`, `mode`), self.position) | |
| 528 self.pushMacro(macroName, compiledSlots) | |
| 529 saved_source = self.sourceFile | |
| 530 saved_position = self.position # Used by Boa Constructor | |
| 531 self.interpret(macro) | |
| 532 if self.sourceFile != saved_source: | |
| 533 self.engine.setSourceFile(saved_source) | |
| 534 self.sourceFile = saved_source | |
| 535 self.popMacro() | |
| 536 bytecode_handlers["useMacro"] = do_useMacro | |
| 537 | |
| 538 def do_fillSlot(self, (slotName, block)): | |
| 539 # This is only executed if the enclosing 'use-macro' evaluates | |
| 540 # to 'default'. | |
| 541 self.interpret(block) | |
| 542 bytecode_handlers["fillSlot"] = do_fillSlot | |
| 543 | |
| 544 def do_defineSlot(self, (slotName, block)): | |
| 545 if not self.metal: | |
| 546 self.interpret(block) | |
| 547 return | |
| 548 macs = self.macroStack | |
| 549 if macs and macs[-1] is not None: | |
| 550 saved_source = self.sourceFile | |
| 551 saved_position = self.position # Used by Boa Constructor | |
| 552 macroName, slots = self.popMacro()[:2] | |
| 553 slot = slots.get(slotName) | |
| 554 if slot is not None: | |
| 555 self.interpret(slot) | |
| 556 if self.sourceFile != saved_source: | |
| 557 self.engine.setSourceFile(saved_source) | |
| 558 self.sourceFile = saved_source | |
| 559 self.pushMacro(macroName, slots, entering=0) | |
| 560 return | |
| 561 self.pushMacro(macroName, slots) | |
| 562 if len(macs) == 1: | |
| 563 self.interpret(block) | |
| 564 return | |
| 565 self.interpret(block) | |
| 566 bytecode_handlers["defineSlot"] = do_defineSlot | |
| 567 | |
| 568 def do_onError(self, (block, handler)): | |
| 569 self.interpret(block) | |
| 570 | |
| 571 def do_onError_tal(self, (block, handler)): | |
| 572 state = self.saveState() | |
| 573 self.stream = stream = StringIO() | |
| 574 self._stream_write = stream.write | |
| 575 try: | |
| 576 self.interpret(block) | |
| 577 except: | |
| 578 exc = sys.exc_info()[1] | |
| 579 self.restoreState(state) | |
| 580 engine = self.engine | |
| 581 engine.beginScope() | |
| 582 error = engine.createErrorInfo(exc, self.position) | |
| 583 engine.setLocal('error', error) | |
| 584 try: | |
| 585 self.interpret(handler) | |
| 586 finally: | |
| 587 engine.endScope() | |
| 588 else: | |
| 589 self.restoreOutputState(state) | |
| 590 self.stream_write(stream.getvalue()) | |
| 591 bytecode_handlers["onError"] = do_onError | |
| 592 | |
| 593 bytecode_handlers_tal = bytecode_handlers.copy() | |
| 594 bytecode_handlers_tal["rawtextBeginScope"] = do_rawtextBeginScope_tal | |
| 595 bytecode_handlers_tal["beginScope"] = do_beginScope_tal | |
| 596 bytecode_handlers_tal["setLocal"] = do_setLocal_tal | |
| 597 bytecode_handlers_tal["setGlobal"] = do_setGlobal_tal | |
| 598 bytecode_handlers_tal["insertStructure"] = do_insertStructure_tal | |
| 599 bytecode_handlers_tal["insertText"] = do_insertText_tal | |
| 600 bytecode_handlers_tal["loop"] = do_loop_tal | |
| 601 bytecode_handlers_tal["onError"] = do_onError_tal | |
| 602 bytecode_handlers_tal["<attrAction>"] = attrAction_tal | |
| 603 bytecode_handlers_tal["optTag"] = do_optTag_tal | |
| 604 | |
| 605 | |
| 606 def test(): | |
| 607 from driver import FILE, parsefile | |
| 608 from DummyEngine import DummyEngine | |
| 609 try: | |
| 610 opts, args = getopt.getopt(sys.argv[1:], "") | |
| 611 except getopt.error, msg: | |
| 612 print msg | |
| 613 sys.exit(2) | |
| 614 if args: | |
| 615 file = args[0] | |
| 616 else: | |
| 617 file = FILE | |
| 618 doc = parsefile(file) | |
| 619 compiler = TALCompiler(doc) | |
| 620 program, macros = compiler() | |
| 621 engine = DummyEngine() | |
| 622 interpreter = TALInterpreter(program, macros, engine) | |
| 623 interpreter() | |
| 624 | |
| 625 if __name__ == "__main__": | |
| 626 test() |
