[ZPT] CVS: Packages/TAL - TALInterpreter.py:1.41

guido@digicool.com guido@digicool.com
Wed, 16 May 2001 17:20:12 -0400 (EDT)


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

Modified Files:
	TALInterpreter.py 
Log Message:
Implemented more sensible behavior of define-slot and fill-slot:

- Define-slot can now be nested inside fill-slot, so that a macro can
  call another macro and use one of its slots to fill that other
  macro's slots.  (This is essential for sensible semantics of nested
  macros.)

- Inside a use-macro, if the macro defines a slot that's not filled,
  the define-slot turns into fill-slot.  (This is just a convenience
  feature.)

To implement this, the current macro call and its slots are turned
into a stack (they already had stack behavior but the stack was
inaccessible) and added a variable indicating the macro currently
being defined (if any).

In the process, refactored startTagCommon a bit, turning the decision
procedure for argument replacement into a separate method
(attrAction()).

Added a safeguard to the attribute-wrapping feature: if the alignment
column is too far to the right (half of the wrap column), change
alignment to 4 spaces.  This solves the problem reported by Gregor
Hoffleit with attribute wrapping in a long line containing several
elements.



--- Updated File TALInterpreter.py in package Packages/TAL --
--- TALInterpreter.py	2001/05/15 16:09:02	1.40
+++ TALInterpreter.py	2001/05/16 21:20:11	1.41
@@ -116,7 +116,7 @@
     # rendered in minimized form, e.g. <img />.
     # From http://www.w3.org/TR/xhtml1/#dtds
     "base", "meta", "link", "hr", "br", "param", "img", "area",
-    "input", "col", "basefont", "isindex", "frame", 
+    "input", "col", "basefont", "isindex", "frame",
 ]
 
 class AltTALGenerator(TALGenerator):
@@ -170,8 +170,8 @@
         self.strictinsert = strictinsert
         self.html = 0
         self.endsep = "/>"
-        self.slots = {}
-        self.currentMacro = None
+        self.macroStack = []
+        self.definingMacro = None
         self.position = None, None  # (lineno, offset)
         self.col = 0
         self.level = 0
@@ -246,47 +246,21 @@
     def do_startTag(self, name, attrList):
         self.startTagCommon(name, attrList, ">")
 
-    actionIndex = ["replace", "insert", "metal", "tal", "xmlns"].index
     def startTagCommon(self, name, attrList, end):
         if not attrList:
             self.stream_write("<%s%s" % (name, end))
             return
         self.stream_write("<" + name)
         align = self.col+1
+        if align >= self.wrap/2:
+            align = 4 # Avoid a narrow column far to the right
         for item in attrList:
-            name, value = item[:2]
-            if len(item) > 2:
-                try:
-                    action = self.actionIndex(item[2])
-                except ValueError:
-                    raise TALError, ('Error in TAL program', self.position)
-                if not self.showtal and action > 1:
+            if len(item) == 2:
+                name, value = item[:2]
+            else:
+                ok, name, value = self.attrAction(item)
+                if not ok:
                     continue
-                if action <= 1 and self.tal:
-                    if self.html and string.lower(name) in BOOLEAN_HTML_ATTRS:
-                        evalue = self.engine.evaluateBoolean(item[3])
-                        if evalue is self.Default:
-                            if action == 1: # Cancelled insert
-                                continue
-                        elif not evalue:
-                            continue
-                        else:
-                            value = None
-                    else:
-                        evalue = self.engine.evaluateText(item[3])
-                        if evalue is self.Default:
-                            if action == 1: # Cancelled insert
-                                continue
-                        else:
-                            value = evalue
-                            if value is None:
-                                continue
-                elif (action == 2 and self.currentMacro and
-                      name[-13:] == ":define-macro" and self.metal):
-                    name = name[:-13] + ":use-macro"
-                    value = self.currentMacro
-                elif action == 1:
-                    continue # Unexecuted insert
             if value is None:
                 s = name
             else:
@@ -299,6 +273,63 @@
                 self.stream_write(" " + s)
         self.stream_write(end)
 
+    actionIndex = {"replace":0, "insert":1, "metal":2, "tal":3, "xmlns":4}
+    def attrAction(self, item):
+        name, value = item[:2]
+        try:
+            action = self.actionIndex[item[2]]
+        except KeyError:
+            raise TALError, ('Error in TAL program', self.position)
+        if not self.showtal and action > 1:
+            return 0, name, value
+        ok = 1
+        if action <= 1 and self.tal:
+            if self.html and string.lower(name) in BOOLEAN_HTML_ATTRS:
+                evalue = self.engine.evaluateBoolean(item[3])
+                if evalue is self.Default:
+                    if action == 1: # Cancelled insert
+                        ok = 0
+                elif not evalue:
+                    ok = 0
+                else:
+                    value = None
+            else:
+                evalue = self.engine.evaluateText(item[3])
+                if evalue is self.Default:
+                    if action == 1: # Cancelled insert
+                        ok = 0
+                else:
+                    value = evalue
+                    if value is None:
+                        ok = 0
+        elif action == 2 and self.macroStack:
+            i = string.rfind(name, ":") + 1
+            prefix, suffix = name[:i], name[i:]
+            if suffix == "define-macro":
+                if len(self.macroStack) == 1:
+                    macroName, slots = self.macroStack[-1]
+                    name = prefix + "use-macro"
+                    value = macroName
+                else:
+                    ok = 0
+            if suffix == "fill-slot":
+                macroName, slots = self.macroStack[0]
+                if not slots.has_key(value):
+                    ok = 0
+            if suffix == "define-slot" and not self.definingMacro:
+                name = prefix + "fill-slot"
+        elif action == 1: # Unexecuted insert
+            ok = 0
+        return ok, name, value
+
+##    def dumpMacroStack(self, prefix, suffix, value):
+##        sys.stderr.write("+-- %s%s = %s\n" % (prefix, suffix, value))
+##        for i in range(len(self.macroStack)):
+##            macroName, slots = self.macroStack[i]
+##            sys.stderr.write("|   %3d. %-20s %s\n" %
+##                             (i, macroName, slots.keys()))
+##        sys.stderr.write("+--------------------------------------\n")
+
     def do_endTag(self, name):
         self.stream_write("</%s>" % name)
 
@@ -392,7 +423,10 @@
             self.interpret(block)
 
     def do_defineMacro(self, macroName, macro):
+        save = self.definingMacro
+        self.definingMacro = macroName
         self.interpret(macro)
+        self.definingMacro = save
 
     def do_useMacro(self, macroName, macroExpr, compiledSlots, block):
         if not self.metal:
@@ -410,19 +444,19 @@
         if mode != (self.html and "html" or "xml"):
             raise METALError("macro %s has incompatible mode %s" %
                              (`macroName`, `mode`), self.position)
-        save = self.slots, self.currentMacro
-        self.slots = compiledSlots
-        self.currentMacro = macroName
+        self.macroStack.append((macroName, compiledSlots))
         self.interpret(macro)
-        self.slots, self.currentMacro = save
+        self.macroStack.pop()
 
     def do_fillSlot(self, slotName, block):
         self.interpret(block)
 
     def do_defineSlot(self, slotName, block):
-        compiledSlot = self.metal and self.slots.get(slotName)
-        if compiledSlot:
-            self.interpret(compiledSlot)
+        slot = None
+        for macroName, slots in self.macroStack:
+            slot = slots.get(slotName) or slot
+        if slot:
+            self.interpret(slot)
         else:
             self.interpret(block)