Commit cc83ce7e authored by Guido van Rossum's avatar Guido van Rossum

Refactored and optimized; this now uses TALGenerator as a base class.

Most expression compilation has moved to TALGenerator (exceptions for
now: attribute replacements and macro name).
parent 4d6ab18b
...@@ -87,74 +87,26 @@ Compile a DOM tree for efficient METAL and TAL expansion. ...@@ -87,74 +87,26 @@ Compile a DOM tree for efficient METAL and TAL expansion.
""" """
import string import string
import re
from xml.dom import Node from xml.dom import Node
from DOMVisitor import DOMVisitor from DOMVisitor import DOMVisitor
from TALGenerator import TALGenerator
from TALDefs import *
from TALDefs import ZOPE_TAL_NS, ZOPE_METAL_NS, NAME_RE, XMLNS_NS class METALCompiler(DOMVisitor, TALGenerator):
from TALDefs import macroIndexer, slotIndexer
from TALDefs import splitParts, parseAttributeReplacements
from TALDefs import parseSubstitution
class TALError(Exception):
pass
class METALError(TALError):
pass
KNOWN_METAL_ATTRIBUTES = [
"define-macro",
"use-macro",
"define-slot",
"fill-slot",
]
KNOWN_TAL_ATTRIBUTES = [
"define",
"condition",
"insert",
"replace",
"repeat",
"attributes",
]
class DummyCompiler:
def compile(self, expr):
return expr
class METALCompiler(DOMVisitor):
def __init__(self, document, expressionCompiler=None): def __init__(self, document, expressionCompiler=None):
self.document = document self.document = document
DOMVisitor.__init__(self, document) DOMVisitor.__init__(self, document)
if not expressionCompiler: TALGenerator.__init__(self, expressionCompiler)
expressionCompiler = DummyCompiler()
self.expressionCompiler = expressionCompiler
def __call__(self): def __call__(self):
self.macros = {}
self.program = []
self.stack = []
self.namespaceDict = {} self.namespaceDict = {}
self.namespaceStack = [self.namespaceDict] self.namespaceStack = [self.namespaceDict]
DOMVisitor.__call__(self) DOMVisitor.__call__(self)
assert not self.stack assert not self.stack
return self.program, self.macros return self.program, self.macros
def compileExpression(self, expr):
return self.expressionCompiler.compile(expr)
def pushProgram(self):
self.stack.append(self.program)
self.program = []
def popProgram(self):
program = self.program
self.program = self.stack.pop()
return program
def pushNS(self): def pushNS(self):
self.namespaceStack.append(self.namespaceDict) self.namespaceStack.append(self.namespaceDict)
...@@ -171,9 +123,11 @@ class METALCompiler(DOMVisitor): ...@@ -171,9 +123,11 @@ class METALCompiler(DOMVisitor):
return 0 return 0
def getFullAttrList(self, node): def getFullAttrList(self, node):
attr_nodes = node.attributes.values() # Cache this!
self.checkSpuriousAttributes(attr_nodes)
list = [] list = []
# First, call newNS() for explicit xmlns and xmlns:prefix attributes # First, call newNS() for explicit xmlns and xmlns:prefix attributes
for attr in node.attributes.values(): for attr in attr_nodes:
if attr.namespaceURI == XMLNS_NS: if attr.namespaceURI == XMLNS_NS:
# This is a namespace declaration # This is a namespace declaration
# XXX Should be able to use prefix and localName, but can't # XXX Should be able to use prefix and localName, but can't
...@@ -192,7 +146,7 @@ class METALCompiler(DOMVisitor): ...@@ -192,7 +146,7 @@ class METALCompiler(DOMVisitor):
else: else:
list.append(("xmlns", node.namespaceURI)) list.append(("xmlns", node.namespaceURI))
# Add namespace declarations for each attribute, if needed # Add namespace declarations for each attribute, if needed
for attr in node.attributes.values(): for attr in attr_nodes:
if attr.namespaceURI: if attr.namespaceURI:
if attr.namespaceURI == XMLNS_NS: if attr.namespaceURI == XMLNS_NS:
continue continue
...@@ -203,12 +157,9 @@ class METALCompiler(DOMVisitor): ...@@ -203,12 +157,9 @@ class METALCompiler(DOMVisitor):
else: else:
list.append(("xmlns", node.namespaceURI)) list.append(("xmlns", node.namespaceURI))
# Add the node's attributes # Add the node's attributes
list.extend(self.getAttributeList(node)) list.extend(self.getAttributeList(node, attr_nodes))
return list return list
def emit(self, *instruction):
self.program.append(instruction)
def emitStartTag(self, node): def emitStartTag(self, node):
self.emit("startTag", node.nodeName, self.getFullAttrList(node)) self.emit("startTag", node.nodeName, self.getFullAttrList(node))
...@@ -223,16 +174,15 @@ class METALCompiler(DOMVisitor): ...@@ -223,16 +174,15 @@ class METALCompiler(DOMVisitor):
if not node.hasAttributes(): if not node.hasAttributes():
self.emitElement(node) self.emitElement(node)
else: else:
self.checkSpuriousAttributes(node)
self.expandElement(node) self.expandElement(node)
self.popNS() self.popNS()
def checkSpuriousAttributes(self, node, def checkSpuriousAttributes(self, attr_nodes,
ns=ZOPE_METAL_NS, ns=ZOPE_METAL_NS,
known=KNOWN_METAL_ATTRIBUTES, known=KNOWN_METAL_ATTRIBUTES,
error=METALError, error=METALError,
what="METAL"): what="METAL"):
for attr in node.attributes.values(): for attr in attr_nodes:
if attr.namespaceURI == ns: if attr.namespaceURI == ns:
if attr.localName not in known: if attr.localName not in known:
raise error( raise error(
...@@ -295,16 +245,16 @@ class METALCompiler(DOMVisitor): ...@@ -295,16 +245,16 @@ class METALCompiler(DOMVisitor):
self.emitEndTag(node) self.emitEndTag(node)
def visitText(self, node): def visitText(self, node):
self.emit("text", node.nodeValue) self.emitText(node.nodeValue)
def visitComment(self, node): def visitComment(self, node):
self.emit("comment", node.nodeValue) self.emit("comment", node.nodeValue)
def getAttributeList(self, node): def getAttributeList(self, node, attr_nodes):
if not node.hasAttributes(): if not attr_nodes:
return [] return []
attrList = [] attrList = []
for attrNode in node.attributes.values(): for attrNode in attr_nodes:
item = attrNode.nodeName, attrNode.nodeValue item = attrNode.nodeName, attrNode.nodeValue
if (attrNode.namespaceURI == ZOPE_METAL_NS and if (attrNode.namespaceURI == ZOPE_METAL_NS and
attrNode.localName == "define-macro"): attrNode.localName == "define-macro"):
...@@ -315,8 +265,8 @@ class METALCompiler(DOMVisitor): ...@@ -315,8 +265,8 @@ class METALCompiler(DOMVisitor):
class TALCompiler(METALCompiler): class TALCompiler(METALCompiler):
# Extending METAL method to add attribute replacements # Extending METAL method to add attribute replacements
def getAttributeList(self, node): def getAttributeList(self, node, attr_nodes):
attrList = METALCompiler.getAttributeList(self, node) attrList = METALCompiler.getAttributeList(self, node, attr_nodes)
attrDict = self.getAttributeReplacements(node) attrDict = self.getAttributeReplacements(node)
if not attrDict: if not attrDict:
return attrList return attrList
...@@ -325,7 +275,7 @@ class TALCompiler(METALCompiler): ...@@ -325,7 +275,7 @@ class TALCompiler(METALCompiler):
key, value = item[:2] key, value = item[:2]
if attrDict.has_key(key): if attrDict.has_key(key):
item = (key, value, "replace", attrDict[key]) item = (key, value, "replace", attrDict[key])
del attrDict[key] del attrDict[key] # XXX Why?
list.append(item) list.append(item)
return list return list
...@@ -343,32 +293,18 @@ class TALCompiler(METALCompiler): ...@@ -343,32 +293,18 @@ class TALCompiler(METALCompiler):
self.conditionalElement(node) self.conditionalElement(node)
# Extending METAL method to check for TAL statements # Extending METAL method to check for TAL statements
def checkSpuriousAttributes(self, node): def checkSpuriousAttributes(self, attr_nodes):
METALCompiler.checkSpuriousAttributes(self, node) METALCompiler.checkSpuriousAttributes(self, attr_nodes)
METALCompiler.checkSpuriousAttributes( METALCompiler.checkSpuriousAttributes(
self, node, ZOPE_TAL_NS, KNOWN_TAL_ATTRIBUTES, TALError, "TAL") self, attr_nodes,
ZOPE_TAL_NS, KNOWN_TAL_ATTRIBUTES, TALError, "TAL")
def emitDefines(self, defines):
for part in splitParts(defines):
m = re.match(
r"\s*(?:(global|local)\s+)?(%s)\s+(.*)" % NAME_RE, part)
if not m:
raise TALError("invalid z:define syntax: " + `part`)
scope, name, expr = m.group(1, 2, 3)
scope = scope or "local"
cexpr = self.compileExpression(expr)
if scope == "local":
self.emit("setLocal", name, cexpr)
else:
self.emit("setGlobal", name, cexpr)
def conditionalElement(self, node): def conditionalElement(self, node):
condition = node.getAttributeNS(ZOPE_TAL_NS, "condition") condition = node.getAttributeNS(ZOPE_TAL_NS, "condition")
if condition: if condition:
self.pushProgram() self.pushProgram()
self.modifyingElement(node) self.modifyingElement(node)
block = self.popProgram() self.emitCondition(condition)
self.emit("condition", condition, block)
else: else:
self.modifyingElement(node) self.modifyingElement(node)
...@@ -397,48 +333,24 @@ class TALCompiler(METALCompiler): ...@@ -397,48 +333,24 @@ class TALCompiler(METALCompiler):
self.emitElement(node) self.emitElement(node)
def doInsert(self, node, arg): def doInsert(self, node, arg):
key, expr = parseSubstitution(arg)
if not key:
return 0
self.emitStartTag(node) self.emitStartTag(node)
self.pushProgram() self.pushProgram()
self.visitAllChildren(node) self.visitAllChildren(node)
block = self.popProgram() self.emitSubstitution(arg)
self.doSubstitution(key, expr, {}, block)
self.emitEndTag(node) self.emitEndTag(node)
return 1 return 1
def doReplace(self, node, arg): def doReplace(self, node, arg):
key, expr = parseSubstitution(arg)
if not key:
return 0
attrDict = self.getAttributeReplacements(node) attrDict = self.getAttributeReplacements(node)
self.pushProgram() self.pushProgram()
self.emitElement(node) self.emitElement(node)
block = self.popProgram() self.emitSubstitution(arg, attrDict)
self.doSubstitution(key, expr, attrDict, block)
return 1 return 1
def doSubstitution(self, key, expr, attrDict, block):
cexpr = self.compileExpression(expr)
if key == "text":
if attrDict:
print "Warning: z:attributes unused for text replacement"
self.emit("insertText", cexpr, block)
else:
assert key == "structure"
self.emit("insertStructure", cexpr, attrDict, block)
def doRepeat(self, node, arg): def doRepeat(self, node, arg):
m = re.match("\s*(%s)\s+(.*)" % NAME_RE, arg)
if not m:
raise TALError("invalid z:repeat syntax: " + `arg`)
name, expr = m.group(1, 2)
cexpr = self.compileExpression(expr)
self.pushProgram() self.pushProgram()
self.emitElement(node) self.emitElement(node)
block = self.popProgram() self.emitRepeat(arg)
self.emit("loop", name, cexpr, block)
return 1 return 1
def getAttributeReplacements(self, node): def getAttributeReplacements(self, 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