[Zope-Checkins] CVS: Zope3/lib/python/Zope/TAL - TALGenerator.py:1.58.6.1

Barry Warsaw barry@wooz.org
Tue, 25 Jun 2002 20:02:44 -0400


Update of /cvs-repository/Zope3/lib/python/Zope/TAL
In directory cvs.zope.org:/tmp/cvs-serv8442

Modified Files:
      Tag: tal-i18n-refactor-branch
	TALGenerator.py 
Log Message:
First pass at refactoring the i18n tal attribute handling.  Some stuff
works better, some stuff is broken (which is why this is on a
branch).  In particular i18n:name + tal:content now works, as does
i18n:name + implicit tal:replace, but i18n:name + (explicit)
tal:replace is broken.  Checkpointing so I can work on this at home.

Specific changes:

optimize(): Added a `noop' opcode which is used simply as a spacer
between endTag opcodes.  This is a signal to the optimizer that it
can't collect adjacent end tags because the i18n machinery needs to
see them as distinct.  optimize() gets rid of the noop spacers so the
TALInterpreter never sees them.

emitI18nVariable(): Reworked the logic.  The action is now explicitly
passed in (as one of 3 possible values).  Other changes include fixed
support for i18n:name + tal:content (by popping the program and using
that as the $name replacement), i18n:name + implicit tal:replace (by
popping the program and lopping off the first and last elements so the
start/end tags aren't included in the replacement), and (current
broken) support for i18n:name + explicit tal:replacement.

emitEndElement(): The default i18n:name action is I18N_REPLACE,
meaning if there are no tal: attributes in the same tag, we do a
replacement, i.e. get rid of the surrounding tag.

Also, cleanup the way end tag (not at the end of the program) handling
is done.  Instead of making and using a copy of the current program,
we simply insert a noop spacer so adjacent end tags won't run
together.

Rework the arguments to emitI18nVariable() to be cleaner (and
commented!).


=== Zope3/lib/python/Zope/TAL/TALGenerator.py 1.58 => 1.58.6.1 ===
 from TranslationContext import TranslationContext, DEFAULT_DOMAIN
 
+I18N_REPLACE = 1
+I18N_CONTENT = 2
+I18N_EXPRESSION = 3
+
 class TALGenerator:
 
     inMacroUse = 0
@@ -91,6 +95,12 @@
                 # instructions to be joined together.
                 output.append(self.optimizeArgsList(item))
                 continue
+            if opcode == 'noop':
+                # This is a spacer for end tags in the face of i18n:name
+                # attributes.  We can't let the optimizer collect immediately
+                # following end tags into the same rawtextOffset.
+                opcode = None
+                pass
             text = "".join(collect)
             if text:
                 i = text.rfind("\n")
@@ -299,7 +309,7 @@
             assert key == "structure"
             self.emit("insertStructure", cexpr, attrDict, program)
 
-    def emitI18nVariable(self, varname, arg):
+    def emitI18nVariable(self, varname, action, expression):
         # Used for i18n:name attributes.  arg is extra information describing
         # how the contents of the variable should get filled in, and it will
         # either be a 1-tuple or a 2-tuple.  If arg[0] is None, then the
@@ -313,15 +323,24 @@
         # "I live in <span i18n:name="country"
         #                  tal:replace="here/countryOfOrigin" />"
         key = cexpr = None
-        if arg[0] is not None:
-            key, expr = parseSubstitution(arg[0])
-            cexpr = self.compileExpression(expr)
-        else:
-            cexpr = self.optimize(arg[1][1:])
         program = self.popProgram()
+        if action == I18N_REPLACE:
+            # This is a tag with an i18n:name and a tal:replace (implicit or
+            # explicit).  Get rid of the first and last elements of the
+            # program, which are the start and end tag opcodes of the tag.
+            program = program[1:-1]
+        elif action == I18N_CONTENT:
+            # This is a tag with an i18n:name and a tal:content
+            # (explicit-only).  Keep the first and last elements of the
+            # program, so we keep the start and end tag output.
+            pass
+        else:
+            assert action == I18N_EXPRESSION
+            key, expr = parseSubstitution(expression)
+            cexpr = self.compileExpression(expr)
         # XXX Would key be anything but 'text' or None?
         assert key in ('text', None)
-        self.emit('i18nVariable', varname, cexpr, program)
+        self.emit('i18nVariable', varname, program, cexpr)
 
     def emitTranslation(self, msgid, i18ndata):
         program = self.popProgram()
@@ -685,19 +704,24 @@
             raise exc("%s attributes on <%s> require explicit </%s>" %
                       (what, name, name), position)
 
+        # If there's no tal:content or tal:replace in the tag with the
+        # i18n:name, tal:replace is the default.
+        i18nNameAction = I18N_REPLACE
         if content:
+            if varname:
+                i18nNameAction = I18N_CONTENT
             self.emitSubstitution(content, {})
         if msgid is not None:
             self.emitTranslation(msgid, i18ndata)
         if optTag:
             self.emitOptTag(name, optTag, isend)
         elif not isend:
-            # Before we emit the end tag, we need to see if the value of the
-            # current program is to be used as the value of the i18n:name
-            # interpolation variable.  If so, we need to make a copy of the
-            # program /without the end tag/ and squirrel it away for later.
-            if not replace and varname and varname[1] is None:
-                varname += (self.program[:],)
+            # If we're processing the end tag for a tag that contained
+            # i18n:name, we need to make sure that optimize() won't collect
+            # immediately following end tags into the same rawtextOffset, so
+            # put a spacer here that the optimizer will recognize.
+            if varname:
+                self.emit('noop')
             self.emitEndTag(name)
         # If i18n:name appeared in the same tag as tal:replace then we're
         # going to do the substitution a little bit differently.  The results
@@ -705,7 +729,16 @@
         if replace:
             self.emitSubstitution(replace, repldict)
         elif varname:
-            self.emitI18nVariable(varname[0], varname[1:])
+            if varname[1] is not None:
+                i18nNameAction = I18N_EXPRESSION
+            # o varname[0] is the variable name
+            # o i18nNameAction is either
+            #   - I18N_REPLACE for implicit tal:replace
+            #   - I18N_CONTENT for tal:content
+            #   - I18N_EXPRESSION for explicit tal:replace
+            # o varname[1] will be None for the first two actions and the
+            #   replacement tal expression for the third action.
+            self.emitI18nVariable(varname[0], i18nNameAction, varname[1])
         if repeat:
             self.emitRepeat(repeat)
         if condition: