Commit 2d93fe3f authored by Guido van Rossum's avatar Guido van Rossum

Add macro expansion. No slot expansion yet.

parent e957faf1
...@@ -90,8 +90,10 @@ import string ...@@ -90,8 +90,10 @@ import string
import re import re
from xml.dom import Node from xml.dom import Node
from CopyingDOMVisitor import CopyingDOMVisitor from CopyingDOMVisitor import CopyingDOMVisitor
from DOMVisitor import DOMVisitor
ZOPE_TAL_NS = "http://xml.zope.org/namespaces/tal" ZOPE_TAL_NS = "http://xml.zope.org/namespaces/tal"
ZOPE_METAL_NS = "http://xml.zope.org/namespaces/metal"
NAME_RE = "[a-zA-Z_][a-zA-Z0-9_]*" NAME_RE = "[a-zA-Z_][a-zA-Z0-9_]*"
...@@ -107,7 +109,9 @@ class TALVisitor(CopyingDOMVisitor): ...@@ -107,7 +109,9 @@ class TALVisitor(CopyingDOMVisitor):
def __init__(self, document, documentFactory, engine): def __init__(self, document, documentFactory, engine):
CopyingDOMVisitor.__init__(self, document, documentFactory) CopyingDOMVisitor.__init__(self, document, documentFactory)
self.document = document
self.engine = engine self.engine = engine
self.currentMacro = None
def visitElement(self, node): def visitElement(self, node):
self.engine.beginScope() self.engine.beginScope()
...@@ -127,6 +131,7 @@ class TALVisitor(CopyingDOMVisitor): ...@@ -127,6 +131,7 @@ class TALVisitor(CopyingDOMVisitor):
modOps = [] modOps = []
attrOps = [] attrOps = []
omit = 0 omit = 0
macroNode = 0
for attr in attrs.values(): for attr in attrs.values():
if attr.namespaceURI == ZOPE_TAL_NS: if attr.namespaceURI == ZOPE_TAL_NS:
name = attr.localName name = attr.localName
...@@ -144,10 +149,27 @@ class TALVisitor(CopyingDOMVisitor): ...@@ -144,10 +149,27 @@ class TALVisitor(CopyingDOMVisitor):
print "Unrecognized ZOPE/TAL attribute:", print "Unrecognized ZOPE/TAL attribute:",
print "%s=%s" % (attr.nodeName, `attr.nodeValue`) print "%s=%s" % (attr.nodeName, `attr.nodeValue`)
# This doesn't stop us from doing the rest! # This doesn't stop us from doing the rest!
elif attr.namespaceURI == ZOPE_METAL_NS:
name = attr.localName
if name == "define-macro":
pass
elif name == "define-slot":
pass
elif name == "use-macro":
macroNode = self.findMacro(attr.nodeValue)
elif name == "use-slot":
pass
else:
print "Unrecognized ZOPE/METAL attribute:",
print "%s=%s" % (attr.nodeName, `attr.nodeValue`)
# If any of these assertions fail, the DOM is broken # If any of these assertions fail, the DOM is broken
assert len(setOps) <= 1 assert len(setOps) <= 1
assert len(ifOps) <= 1 assert len(ifOps) <= 1
assert len(attrOps) <= 1 assert len(attrOps) <= 1
# Macro expansion overrides anything else:
if macroNode:
self.expandMacro(macroNode)
return 1
# Execute TAL attributes in proper order: # Execute TAL attributes in proper order:
# 0. omit, 1. define, 2. condition, 3. insert/replace, 4. attributes # 0. omit, 1. define, 2. condition, 3. insert/replace, 4. attributes
if omit: if omit:
...@@ -173,15 +195,38 @@ class TALVisitor(CopyingDOMVisitor): ...@@ -173,15 +195,38 @@ class TALVisitor(CopyingDOMVisitor):
return self.doModify( return self.doModify(
node, attr.localName, attr.nodeValue, attrDict) node, attr.localName, attr.nodeValue, attrDict)
if attrDict: if attrDict:
##saveNode = self.curNode
self.copyElement(node) self.copyElement(node)
self.copyAttributes(node, attrDict) self.copyAttributes(node, attrDict)
self.visitAllChildren(node) self.visitAllChildren(node)
self.endVisitElement(node) self.endVisitElement(node)
##self.curNode = saveNode
return 1 return 1
return 0 return 0
def findMacro(self, macroName):
# XXX This is not written for speed :-)
doc, localName = self.engine.findMacroDocument(macroName)
if not doc:
doc = self.document
macroDict = MacroIndexer(doc)()
if macroDict.has_key(localName):
return macroDict[localName]
else:
print "No macro found:", macroName
return None
def expandMacro(self, node):
assert node.nodeType == Node.ELEMENT_NODE
saveCurrentMacro = self.currentMacro
self.currentMacro = node
self.engine.beginScope()
if not self.checkTAL(node):
self.copyElement(node)
self.copyAttributes(node, {})
self.visitAllChildren(node)
self.endVisitElement(node)
self.engine.endScope()
self.currentMacro = saveCurrentMacro
def doDefine(self, node, arg): def doDefine(self, node, arg):
for part in self.splitParts(arg): for part in self.splitParts(arg):
m = re.match( m = re.match(
...@@ -260,7 +305,7 @@ class TALVisitor(CopyingDOMVisitor): ...@@ -260,7 +305,7 @@ class TALVisitor(CopyingDOMVisitor):
attrDone = inserting or not attrDict attrDone = inserting or not attrDict
for newChild in data: for newChild in data:
self.curNode.appendChild(newChild) self.curNode.appendChild(newChild)
if not attrDone and newChild.nodeType == ELEMENT_NODE: if not attrDone and newChild.nodeType == Node.ELEMENT_NODE:
self.changeAttributes(newChild, attrDict) self.changeAttributes(newChild, attrDict)
attrDone = 1 attrDone = 1
if not attrDone: if not attrDone:
...@@ -281,6 +326,11 @@ class TALVisitor(CopyingDOMVisitor): ...@@ -281,6 +326,11 @@ class TALVisitor(CopyingDOMVisitor):
if attrValue is None: if attrValue is None:
continue continue
if namespaceURI: if namespaceURI:
# When expanding a macro, change its define-macro to use-macro
if (self.currentMacro and
namespaceURI == ZOPE_METAL_NS and
attr.localName == "define-macro"):
attrName = attr.prefix + ":use-macro"
self.curNode.setAttributeNS(namespaceURI, attrName, attrValue) self.curNode.setAttributeNS(namespaceURI, attrName, attrValue)
else: else:
self.curNode.setAttribute(attrName, attrValue) self.curNode.setAttribute(attrName, attrValue)
...@@ -309,3 +359,29 @@ class TALVisitor(CopyingDOMVisitor): ...@@ -309,3 +359,29 @@ class TALVisitor(CopyingDOMVisitor):
break break
parts[i-1:i+2] = ["%s;%s" % (parts[i-1], parts[i+1])] parts[i-1:i+2] = ["%s;%s" % (parts[i-1], parts[i+1])]
return parts return parts
class MacroIndexer(DOMVisitor):
"""
Helper class to create an index of all macros in a DOM tree.
"""
def __call__(self):
self.macroIndex = {}
DOMVisitor.__call__(self)
return self.macroIndex
def visitElement(self, node):
macroName = node.getAttributeNS(ZOPE_METAL_NS, "define-macro")
if macroName:
self.defineMacro(macroName, node)
# No need to call visitAllAttributes() or endVisitElement()
self.visitAllChildren(node)
def defineMacro(self, macroName, node):
if self.macroIndex.has_key(macroName):
print ("Duplicate macro definition: %s in <%s>" %
(macroName, node.nodeName))
else:
self.macroIndex[macroName] = node
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment