From c34c27cc0db483fc49dd464bd2a80186d3145384 Mon Sep 17 00:00:00 2001
From: Guido van Rossum <guido@python.org>
Date: Fri, 9 Feb 2001 17:15:08 +0000
Subject: [PATCH] Add an optional expressionCompiler argument to the
 [ME]TALCompiler class constructor, so a caller can pass in an expression
 compiler. Every expression occurring in the syntax is compiled first (even
 the macro name in use-macro; but not slot names, nor the macro name in
 define-macro).  If the expressionCompiler argument is left unspecified, a
 default compiler is chosen whose compiled output is identical to its input.

---
 lib/python/TAL/TALCompiler.py | 47 ++++++++++++++++++++++++-----------
 1 file changed, 32 insertions(+), 15 deletions(-)

diff --git a/lib/python/TAL/TALCompiler.py b/lib/python/TAL/TALCompiler.py
index 7df42d23a..376ff6491 100644
--- a/lib/python/TAL/TALCompiler.py
+++ b/lib/python/TAL/TALCompiler.py
@@ -119,10 +119,18 @@ KNOWN_TAL_ATTRIBUTES = [
     "attributes",
     ]
 
+class DummyCompiler:
+
+    def compile(self, expr):
+        return expr
+
 class METALCompiler(DOMVisitor):
 
-    def __init__(self, document):
+    def __init__(self, document, expressionCompiler=None):
         DOMVisitor.__init__(self, document)
+        if not expressionCompiler:
+            expressionCompiler = DummyCompiler()
+        self.expressionCompiler = expressionCompiler
 
     def __call__(self):
         self.macros = {}
@@ -134,6 +142,9 @@ class METALCompiler(DOMVisitor):
         assert not self.stack
         return self.program, self.macros
 
+    def compileExpression(self, expr):
+        return self.expressionCompiler.compile(expr)
+
     def pushProgram(self):
         self.stack.append(self.program)
         self.program = []
@@ -234,7 +245,8 @@ class METALCompiler(DOMVisitor):
                     self.pushProgram()
                     self.visitElement(slotNode)
                     compiledSlots[slotName] = self.popProgram()
-            self.emit("useMacro", macroName, compiledSlots)
+            cexpr = self.compileExpression(macroName)
+            self.emit("useMacro", cexpr, compiledSlots)
             return
         macroName = node.getAttributeNS(ZOPE_METAL_NS, "define-macro")
         if macroName:
@@ -297,7 +309,7 @@ class TALCompiler(METALCompiler):
     # Extending METAL method to add attribute replacements
     def getAttributeList(self, node):
         attrList = METALCompiler.getAttributeList(self, node)
-        attrDict = getAttributeReplacements(node)
+        attrDict = self.getAttributeReplacements(node)
         if not attrDict:
             return attrList
         list = []
@@ -337,10 +349,11 @@ class TALCompiler(METALCompiler):
             else:
                 scope, name, expr = m.group(1, 2, 3)
                 scope = scope or "local"
+                cexpr = self.compileExpression(expr)
                 if scope == "local":
-                    self.emit("setLocal", name, expr)
+                    self.emit("setLocal", name, cexpr)
                 else:
-                    self.emit("setGlobal", name, expr)
+                    self.emit("setGlobal", name, cexpr)
 
     def conditionalElement(self, node):
         condition = node.getAttributeNS(ZOPE_TAL_NS, "condition")
@@ -387,18 +400,19 @@ class TALCompiler(METALCompiler):
         key, expr = parseSubstitution(arg)
         if not key:
             return 0
-        attrDict = getAttributeReplacements(node)
+        attrDict = self.getAttributeReplacements(node)
         self.doSubstitution(key, expr, attrDict)
         return 1
 
     def doSubstitution(self, key, expr, attrDict):
+        cexpr = self.compileExpression(expr)
         if key == "text":
             if attrDict:
                 print "Warning: z:attributes unused for text replacement"
-            self.emit("insertText", expr)
+            self.emit("insertText", cexpr)
         else:
             assert key == "structure"
-            self.emit("insertStructure", expr, attrDict)
+            self.emit("insertStructure", cexpr, attrDict)
 
     def doRepeat(self, node, arg):
         m = re.match("\s*(%s)\s+(.*)" % NAME_RE, arg)
@@ -406,18 +420,21 @@ class TALCompiler(METALCompiler):
             print "Bad syntax in z:repeat:", `arg`
             return 0
         name, expr = m.group(1, 2)
+        cexpr = self.compileExpression(expr)
         self.pushProgram()
         self.emitElement(node)
         block = self.popProgram()
-        self.emit("loop", name, expr, block)
+        self.emit("loop", name, cexpr, block)
         return 1
 
-def getAttributeReplacements(node):
-    attributes = node.getAttributeNS(ZOPE_TAL_NS, "attributes")
-    if not attributes:
-        return {}
-    else:
-        return parseAttributeReplacements(attributes)
+    def getAttributeReplacements(self, node):
+        attrDict = {}
+        value = node.getAttributeNS(ZOPE_TAL_NS, "attributes")
+        if value:
+            rawDict = parseAttributeReplacements(value)
+            for key, expr in rawDict.items():
+                attrDict[key] = self.compileExpression(expr)
+        return attrDict
 
 def test():
     from driver import FILE, parsefile
-- 
2.30.9