[ZPT] CVS: Packages/TAL - TALGenerator.py:1.44 TALInterpreter.py:1.51

fred@digicool.com fred@digicool.com
Sat, 9 Jun 2001 01:25:22 -0400 (EDT)


Update of /cvs-repository/Packages/TAL
In directory korak.digicool.com:/tmp/cvs-serv21730

Modified Files:
	TALGenerator.py TALInterpreter.py 
Log Message:
More micro-optimizations.

A (minor) change to the bytecode format: for "extended" attributes
(handled by TALInterpreter.attrAction()), the instruction contains the
integer previously retrieved from the actionIndex table; the bytecode
generator now takes care of the lookup.  This allows attrAction() to
no longer need to lookup the table that it used to lookup the
integers.

Many handlers are now separated into TAL and non-TAL versions -- the
TALInterpreter constructor determines which dispatch table to use when
it initializes self.tal (which is not changed later).  This allows
each of the handlers to do only what is needed without having to
decide based on the self.tal attribute.

TALInterpreter.do_startTag() no longer contains a special case for an
empty attribute list; that case is already optimized away by the
bytecode generator.  What remains would actually work in that case,
but it simply doesn't occur.  This removes one test of the attrList
parameter.



--- Updated File TALGenerator.py in package Packages/TAL --
--- TALGenerator.py	2001/06/08 22:59:33	1.43
+++ TALGenerator.py	2001/06/09 05:25:21	1.44
@@ -157,6 +157,8 @@
             collect = []
         return output
 
+    actionIndex = {"replace":0, "insert":1, "metal":2, "tal":3, "xmlns":4,
+                   0: 0, 1: 1, 2: 2, 3: 3, 4: 4}
     def optimizeStartTag(self, collect, name, attrlist, end):
         if not attrlist:
             collect.append("<%s%s" % (name, end))
@@ -167,6 +169,9 @@
             item = attrlist[i]
             if len(item) > 2:
                 opt = 0
+                name, value, action = item[:3]
+                action = self.actionIndex[action]
+                attrlist[i] = (name, value, action) + item[3:]
             else:
                 if item[1] is None:
                     s = item[0]

--- Updated File TALInterpreter.py in package Packages/TAL --
--- TALInterpreter.py	2001/06/08 22:59:33	1.50
+++ TALInterpreter.py	2001/06/09 05:25:21	1.51
@@ -170,6 +170,10 @@
         self.wrap = wrap
         self.metal = metal
         self.tal = tal
+        if tal:
+            self.dispatch = self.bytecode_handlers_tal
+        else:
+            self.dispatch = self.bytecode_handlers
         assert showtal in (-1, 0, 1)
         if showtal == -1:
             showtal = (not tal)
@@ -178,7 +182,9 @@
         self.stackLimit = stackLimit
         self.html = 0
         self.endsep = "/>"
+        self.endlen = len(self.endsep)
         self.macroStack = []
+        self.popMacro = self.macroStack.pop
         self.position = None, None  # (lineno, offset)
         self.col = 0
         self.level = 0
@@ -229,7 +235,8 @@
             self._stream_write("\n")
             self.col = 0
 
-    def stream_write(self, s):
+    def stream_write(self, s,
+                     len=len, rfind=rfind):
         self._stream_write(s)
         i = rfind(s, '\n')
         if i < 0:
@@ -241,7 +248,7 @@
 
     def interpret(self, program):
         self.level = self.level + 1
-        handlers = self.bytecode_handlers
+        handlers = self.dispatch
         _apply = apply
         _tuple = tuple
         tup = (self,)
@@ -272,29 +279,29 @@
             self.endsep = " />"
         else:
             self.endsep = "/>"
+        self.endlen = len(self.endsep)
     bytecode_handlers["mode"] = do_mode
 
     def do_setPosition(self, position):
         self.position = position
     bytecode_handlers["setPosition"] = do_setPosition
 
-    def do_startEndTag(self, (name, attrList)):
-        self.do_startTag((name, attrList), self.endsep)
+    def do_startEndTag(self, stuff):
+        self.do_startTag(stuff, self.endsep, self.endlen)
     bytecode_handlers["startEndTag"] = do_startEndTag
 
-    def do_startTag(self, (name, attrList), end=">"):
-        if not attrList:
-            s = "<%s%s" % (name, end)
-            self._stream_write(s)
-            self.col = self.col + len(s)
-            return
-        _len = len
-        _quote = quote
+    def do_startTag(self, (name, attrList),
+                    end=">", endlen=1, _len=len, _quote=quote):
+        # The bytecode generator does not cause calls to this method
+        # for start tags with no attributes; those are optimized down
+        # to rawtext events.  Hence, there is no special "fast path"
+        # for that case.
         _stream_write = self._stream_write
         _stream_write("<" + name)
-        col = self.col + _len(name) + 1
+        namelen = _len(name)
+        col = self.col + namelen + 1
         wrap = self.wrap
-        align = col + 1 + _len(name)
+        align = col + 1 + namelen
         if align >= wrap/2:
             align = 4  # Avoid a narrow column far to the right
         try:
@@ -309,25 +316,24 @@
                         s = name
                     else:
                         s = "%s=%s" % (name, _quote(value))
+                slen = _len(s)
                 if (wrap and
                     col >= align and
-                    col + 1 + _len(s) > wrap):
+                    col + 1 + slen > wrap):
                     _stream_write("\n" + " "*align)
-                    col = align + _len(s)
+                    col = align + slen
                 else:
                     s = " " + s
-                    col = col + 1 + _len(s)
+                    col = col + 1 + slen
                 _stream_write(s)
             _stream_write(end)
-            col = col + _len(end)
+            col = col + endlen
         finally:
             self.col = col
     bytecode_handlers["startTag"] = do_startTag
 
-    actionIndex = {"replace":0, "insert":1, "metal":2, "tal":3, "xmlns":4}
     def attrAction(self, item):
-        name, value = item[:2]
-        action = self.actionIndex[item[2]]
+        name, value, action = item[:3]
         if not self.showtal and action > 1:
             return 0, name, value
         ok = 1
@@ -349,8 +355,7 @@
                 else:
                     if evalue is None:
                         ok = 0
-                    else:
-                        value = evalue
+                    value = evalue
         elif action == 2 and self.metal:
             i = rfind(name, ":") + 1
             prefix, suffix = name[:i], name[i:]
@@ -387,36 +392,38 @@
         sys.stderr.write("+--------------------------------------\n")
 
     def do_beginScope(self, dict):
-        if self.tal:
-            engine = self.engine
-            engine.beginScope()
-            engine.setLocal("attrs", dict)
-        else:
-            self.engine.beginScope()
+        self.engine.beginScope()
         self.scopeLevel = self.scopeLevel + 1
+
+    def do_beginScope_tal(self, dict):
+        engine = self.engine
+        engine.beginScope()
+        engine.setLocal("attrs", dict)
+        self.scopeLevel = self.scopeLevel + 1
     bytecode_handlers["beginScope"] = do_beginScope
 
     def do_endScope(self, notused=None):
         self.engine.endScope()
         self.scopeLevel = self.scopeLevel - 1
     bytecode_handlers["endScope"] = do_endScope
+
+    def do_setLocal(self, junk):
+        pass
 
-    def do_setLocal(self, (name, expr)):
-        if self.tal:
-            value = self.engine.evaluateValue(expr)
-            self.engine.setLocal(name, value)
+    def do_setLocal_tal(self, (name, expr)):
+        value = self.engine.evaluateValue(expr)
+        self.engine.setLocal(name, value)
     bytecode_handlers["setLocal"] = do_setLocal
 
-    def do_setGlobal(self, (name, expr)):
-        if self.tal:
-            value = self.engine.evaluateValue(expr)
-            self.engine.setGlobal(name, value)
-    bytecode_handlers["setGlobal"] = do_setGlobal
+    def do_setGlobal_tal(self, (name, expr)):
+        value = self.engine.evaluateValue(expr)
+        self.engine.setGlobal(name, value)
+    bytecode_handlers["setGlobal"] = do_setLocal
 
     def do_insertText(self, (expr, block)):
-        if not self.tal:
-            self.interpret(block)
-            return
+        self.interpret(block)
+
+    def do_insertText_tal(self, (expr, block)):
         text = self.engine.evaluateText(expr)
         if text is None:
             return
@@ -469,9 +476,9 @@
         self.interpret(program)
 
     def do_loop(self, (name, expr, block)):
-        if not self.tal:
-            self.interpret(block)
-            return
+        self.interpret(block)
+
+    def do_loop_tal(self, (name, expr, block)):
         iterator = self.engine.setRepeat(name, expr)
         while iterator.next():
             self.interpret(block)
@@ -548,9 +555,9 @@
     bytecode_handlers["defineSlot"] = do_defineSlot
 
     def do_onError(self, (block, handler)):
-        if not self.tal:
-            self.interpret(block)
-            return
+        self.interpret(block)
+
+    def do_onError_tal(self, (block, handler)):
         state = self.saveState()
         self.stream = stream = StringIO()
         self._stream_write = stream.write
@@ -568,6 +575,14 @@
             self.restoreOutputState(state)
             self.stream_write(stream.getvalue())
     bytecode_handlers["onError"] = do_onError
+
+    bytecode_handlers_tal = bytecode_handlers.copy()
+    bytecode_handlers_tal["beginScope"] = do_beginScope_tal
+    bytecode_handlers_tal["setLocal"] = do_setLocal_tal
+    bytecode_handlers_tal["setGlobal"] = do_setGlobal_tal
+    bytecode_handlers_tal["insertText"] = do_insertText_tal
+    bytecode_handlers_tal["loop"] = do_loop_tal
+    bytecode_handlers_tal["onError"] = do_onError_tal
 
 
 def test():