[Checkins] SVN: z3c.pt/trunk/z3c/pt/ Added preliminary support for specifying an expression translation class; in this changeset, an implementation for a Python-expressions is given.

Malthe Borch mborch at gmail.com
Sun Mar 16 13:20:12 EDT 2008


Log message for revision 84711:
  Added preliminary support for specifying an expression translation class; in this changeset, an implementation for a Python-expressions is given.

Changed:
  U   z3c.pt/trunk/z3c/pt/README.txt
  U   z3c.pt/trunk/z3c/pt/clauses.py
  A   z3c.pt/trunk/z3c/pt/configure.zcml
  A   z3c.pt/trunk/z3c/pt/expressions.py
  A   z3c.pt/trunk/z3c/pt/generation.py
  U   z3c.pt/trunk/z3c/pt/i18n.txt
  A   z3c.pt/trunk/z3c/pt/interfaces.py
  D   z3c.pt/trunk/z3c/pt/io.py
  D   z3c.pt/trunk/z3c/pt/parsing.py
  U   z3c.pt/trunk/z3c/pt/template.py
  A   z3c.pt/trunk/z3c/pt/testing.py
  U   z3c.pt/trunk/z3c/pt/tests/test_doctests.py
  U   z3c.pt/trunk/z3c/pt/translation.py
  U   z3c.pt/trunk/z3c/pt/translation.txt

-=-
Modified: z3c.pt/trunk/z3c/pt/README.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/README.txt	2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/README.txt	2008-03-16 17:20:11 UTC (rev 84711)
@@ -28,6 +28,13 @@
 ``ViewTextTemplate``, ``ViewTextTemplateFile``
        See above.
 
+Setup
+-----
+
+  >>> from zope.configuration.xmlconfig import XMLConfig
+  >>> import z3c.pt
+  >>> XMLConfig('configure.zcml', z3c.pt)()
+       
 Page template classes
 ---------------------
 

Modified: z3c.pt/trunk/z3c/pt/clauses.py
===================================================================
--- z3c.pt/trunk/z3c/pt/clauses.py	2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/clauses.py	2008-03-16 17:20:11 UTC (rev 84711)
@@ -3,8 +3,8 @@
 
 class Assign(object):
     """
-      >>> from z3c.pt.io import CodeIO; stream = CodeIO()
-      >>> from z3c.pt.parsing import value
+      >>> from z3c.pt.generation import CodeIO; stream = CodeIO()
+      >>> from z3c.pt.testing import value
 
     Simple assignment:
 
@@ -61,8 +61,8 @@
         
 class Define(object):
     """
-      >>> from z3c.pt.io import CodeIO; stream = CodeIO()
-      >>> from z3c.pt.parsing import value
+      >>> from z3c.pt.generation import CodeIO; stream = CodeIO()
+      >>> from z3c.pt.testing import value
       
     Variable scope:
 
@@ -208,8 +208,8 @@
 
 class Condition(object):
     """
-      >>> from z3c.pt.io import CodeIO
-      >>> from z3c.pt.parsing import value
+      >>> from z3c.pt.generation import CodeIO
+      >>> from z3c.pt.testing import value
       
     Unlimited scope:
     
@@ -325,8 +325,8 @@
     
 class Tag(object):
     """
-      >>> from z3c.pt.io import CodeIO
-      >>> from z3c.pt.parsing import value
+      >>> from z3c.pt.generation import CodeIO
+      >>> from z3c.pt.testing import value
       >>> from StringIO import StringIO
       >>> from cgi import escape as _escape
       
@@ -424,8 +424,8 @@
 
 class Repeat(object):
     """
-      >>> from z3c.pt.io import CodeIO; stream = CodeIO()
-      >>> from z3c.pt.parsing import value
+      >>> from z3c.pt.generation import CodeIO; stream = CodeIO()
+      >>> from z3c.pt.testing import value
 
     We need to set up the repeat object.
 
@@ -482,8 +482,8 @@
 
 class Write(object):
     """
-      >>> from z3c.pt.io import CodeIO; stream = CodeIO()
-      >>> from z3c.pt.parsing import value
+      >>> from z3c.pt.generation import CodeIO; stream = CodeIO()
+      >>> from z3c.pt.testing import value
       >>> from StringIO import StringIO
 
       >>> _out = StringIO()
@@ -539,8 +539,8 @@
 
 class Out(object):
     """
-      >>> from z3c.pt.io import CodeIO; stream = CodeIO()
-      >>> from z3c.pt.parsing import value
+      >>> from z3c.pt.generation import CodeIO; stream = CodeIO()
+      >>> from z3c.pt.testing import value
       >>> from StringIO import StringIO
       >>> _out = StringIO()
       

Added: z3c.pt/trunk/z3c/pt/configure.zcml
===================================================================
--- z3c.pt/trunk/z3c/pt/configure.zcml	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/configure.zcml	2008-03-16 17:20:11 UTC (rev 84711)
@@ -0,0 +1,10 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+  <include package="zope.component" file="meta.zcml" />
+  
+  <utility
+     name="python"
+     factory=".expressions.PythonTranslation" />
+  
+</configure>
+ 

Added: z3c.pt/trunk/z3c/pt/expressions.py
===================================================================
--- z3c.pt/trunk/z3c/pt/expressions.py	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/expressions.py	2008-03-16 17:20:11 UTC (rev 84711)
@@ -0,0 +1,276 @@
+import zope.interface
+import parser
+
+from interfaces import IExpressionTranslation
+
+class ExpressionTranslation(object):
+    zope.interface.implements(IExpressionTranslation)
+
+    def name(self, string):
+        return string
+    
+    def value(self, string):
+        return NotImplementedError("Must be implemented by subclass.")
+
+    def search(self, string):
+        """
+        We need to implement a ``value``-method. Let's define that an
+        expression is valid if it contains an odd number of
+        characters.
+        
+          >>> class MockExpressionTranslation(ExpressionTranslation):
+          ...     def value(self, string):
+          ...         if len(string) % 2 == 0: raise SyntaxError()
+
+          >>> search = MockExpressionTranslation().search
+          >>> search("a")
+          'a'
+          >>> search("ab")
+          'a'
+          >>> search("abc")
+          'abc'
+          
+        """
+
+        left = 0
+        right = left + 1
+
+        current = None
+
+        while right <= len(string):
+            expression = string[left:right]
+
+            try:
+                e = self.value(expression)
+                current = expression
+            except SyntaxError, e:
+                if right == len(string):
+                    if current is not None:
+                        return current
+
+                    raise e
+
+            right += 1
+
+        return current
+
+    def variable(self, string):
+        """
+          >>> variable = ExpressionTranslation().variable
+
+        Single variable:
+
+          >>> variable("variable")
+          ('variable',)
+
+        Multiple variables:
+
+          >>> variable("variable1, variable2")
+          ('variable1', 'variable2')
+        """
+
+        variables = []
+        for var in string.split(', '):
+            var = var.strip()
+
+            if var in ('repeat',):
+                raise ValueError, "Invalid variable name '%s' (reserved)." % variable
+
+            if var.startswith('_') and not var.startswith('_tmp'):
+                raise ValueError(
+                    "Invalid variable name '%s' (starts with an underscore)." % var)
+
+            variables.append(var)
+
+        return tuple(variables)
+
+    def mapping(self, string):
+        """
+          >>> mapping = ExpressionTranslation().mapping
+          
+          >>> mapping("abc def")
+          [('abc', 'def')]
+
+          >>> mapping("abc")
+          [('abc', None)]
+
+          >>> mapping("abc; def ghi")
+          [('abc', None), ('def', 'ghi')]
+
+        """
+
+        defs = string.split(';')
+        mappings = []
+        for d in defs:
+            d = d.strip()
+            while '  ' in d:
+                d = d.replace('  ', ' ')
+
+            parts = d.split(' ')
+            if len(parts) == 1:
+                mappings.append((d, None))
+            elif len(parts) == 2:
+                mappings.append((parts[0], parts[1]))
+            else:
+                raise ValueError, "Invalid mapping (%s)." % string
+
+        return mappings
+
+    def definitions(self, string):
+        """
+        We need to subclass the parser and implement a simple
+        ``value``-method.
+
+          >>> class MockExpressionTranslation(ExpressionTranslation):
+          ...     def value(self, string):
+          ...         return (string.strip(),)
+
+          >>> definitions = MockExpressionTranslation().definitions
+
+        Single define:
+
+          >>> definitions("variable expression")
+          ((['variable'], ('expression',)),)
+
+        Multiple defines:
+
+          >>> definitions("variable1 expression1; variable2 expression2")
+          ((['variable1'], ('expression1',)), (['variable2'], ('expression2',)))
+          
+        Tuple define:
+
+          >>> definitions("(variable1, variable2) (expression1, expression2)")
+          ((['variable1', 'variable2'], ('(expression1, expression2)',)),)
+
+        A define clause that ends in a semicolon:
+
+          >>> definitions("variable expression;")
+          ((['variable'], ('expression',)),)
+
+        A define clause with a trivial expression (we do allow this):
+
+          >>> definitions("variable")
+          ((['variable'], None),)
+
+        A proper define clause following one with a trivial expression:
+
+          >>> definitions("variable1 expression; variable2")
+          ((['variable1'], ('expression',)), (['variable2'], None))
+
+        """
+
+        string = string.replace('\n', '').strip()
+
+        defines = []
+
+        i = 0
+        while i < len(string):
+            while string[i] == ' ':
+                i += 1
+
+            # get variable definition
+            if string[i] == '(':
+                j = string.find(')', i+1)
+                if j == -1:
+                    raise ValueError, "Invalid variable tuple definition (%s)." % string
+                var = self.variable(string[i+1:j])
+                j += 1
+            else:
+                j = string.find(' ', i + 1)
+                if j == -1:
+                    var = self.variable(string[i:])
+                    j = len(string)
+                else:
+                    var = self.variable(string[i:j])
+
+            # get expression
+            i = j
+            while j < len(string):
+                j = string.find(';', j+1)
+                if j == -1:
+                    j = len(string)
+
+                try:
+                    expr = self.value(string[i:j])
+                except SyntaxError, e:
+                    if j < len(string):
+                        continue
+                    raise e
+                break
+            else:
+                expr = None
+
+            defines.append((list(var), expr))
+
+            i = j + 1
+
+        return tuple(defines)
+
+    def definition(self, string):
+        defs = self.definitions(string)
+        if len(defs) != 1:
+            raise ValueError, "Multiple definitions not allowed."
+
+        return defs[0]
+
+class PythonTranslation(ExpressionTranslation):
+    def value(self, string):
+        """
+        Specification:
+
+        value :: = python_expression [ |* value ]
+        python_expresion ::= a python expression string
+
+        *) Using | as logical or is not supported.
+
+            >>> value = PythonTranslation().value
+            
+            >>> value("4 + 5")
+            ['4 + 5']
+
+        Complex expressions:
+
+            >>> value("a.non_defined_method() | 1")
+            ['a.non_defined_method() ', '1']
+
+        Expression with non-semantic horizontal bar.
+
+            >>> value("'|'")
+            ["'|'"]
+
+        Expression with non-semantic horizontal bar and semantic bar.
+
+            >>> value("a.non_defined_method() | '|'")
+            ['a.non_defined_method() ', "'|'"]
+
+        """
+
+        string = string.replace('\n', '').strip()
+
+        if not string:
+            return []
+
+        expressions = []
+
+        i = j = 0
+        while i < len(string):
+            j = string.find('|', j + 1)
+            if j == -1:
+                j = len(string)
+
+            expr = string[i:j].lstrip()
+
+            try:
+                # we use the ``parser`` module to determine if
+                # an expression is a valid python expression
+                parser.expr(expr.encode('utf-8'))
+            except SyntaxError, e:
+                if j < len(string):
+                    continue
+
+                raise e
+
+            expressions.append(expr)
+            i = j + 1
+
+        return expressions

Copied: z3c.pt/trunk/z3c/pt/generation.py (from rev 84456, z3c.pt/trunk/z3c/pt/io.py)
===================================================================
--- z3c.pt/trunk/z3c/pt/generation.py	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/generation.py	2008-03-16 17:20:11 UTC (rev 84711)
@@ -0,0 +1,96 @@
+from StringIO import StringIO
+
+wrapper = """\
+def render(%starget_language=None):
+\tglobal utils
+
+\t_out = utils.initialize_stream()
+    
+\t(_attributes, repeat) = utils.initialize_tal()
+\t(_domain, _translate) = utils.initialize_i18n()
+\t(_escape, _marker) = utils.initialize_helpers()
+
+\t_target_language = target_language
+%s
+\treturn _out.getvalue().decode('utf-8')
+"""
+
+class CodeIO(StringIO):
+    """
+    A high-level I/O class to write Python code to a stream.
+    Indentation is managed using ``indent`` and ``outdent``.
+
+    Also:
+    
+    * Convenience methods for keeping track of temporary
+    variables (see ``save``, ``restore`` and ``getvariable``).
+
+    * Methods to process clauses (see ``begin`` and ``end``).
+    
+    """
+
+    t_prefix = '_tmp'
+    v_prefix = '_var'
+
+    def __init__(self, indentation=0, indentation_string="\t"):
+        StringIO.__init__(self)
+        self.indentation = indentation
+        self.indentation_string = indentation_string
+        self.queue = u''
+        self.scope = [set()]
+        
+        self._variables = {}
+        self.t_counter = 0
+
+    def save(self):
+        self.t_counter += 1
+        return "%s%d" % (self.t_prefix, self.t_counter)
+        
+    def restore(self):
+        var = "%s%d" % (self.t_prefix, self.t_counter)
+        self.t_counter -= 1
+        return var
+        
+    def indent(self, amount=1):
+        if amount > 0:
+            self.cook()
+            self.indentation += amount
+
+    def outdent(self, amount=1):
+        if amount > 0:
+            self.cook()
+            self.indentation -= amount
+
+    def out(self, string):
+        self.queue += string
+
+    def cook(self):
+        if self.queue:
+            queue = self.queue
+            self.queue = ''
+            self.write("_out.write('%s')" %
+                       queue.replace('\n', '\\n').replace("'", "\\'"))
+        
+    def write(self, string):
+        self.cook()
+        StringIO.write(
+            self, self.indentation_string * self.indentation + string + '\n')
+
+    def getvalue(self):
+        self.cook()
+        return StringIO.getvalue(self)
+
+    def begin(self, clauses):
+        if isinstance(clauses, (list, tuple)):
+            for clause in clauses:
+                self.begin(clause)
+        else:
+            clauses.begin(self)
+            
+    def end(self, clauses):
+        if isinstance(clauses, (list, tuple)):
+            for clause in reversed(clauses):
+                self.end(clause)
+        else:
+            clauses.end(self)
+        

Modified: z3c.pt/trunk/z3c/pt/i18n.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/i18n.txt	2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/i18n.txt	2008-03-16 17:20:11 UTC (rev 84711)
@@ -31,6 +31,12 @@
 Translation of tag contents
 ---------------------------
 
+We'll first register the Python expression translator.
+
+  >>> from z3c.pt.expressions import PythonTranslation
+  >>> from zope.component import provideUtility
+  >>> provideUtility(PythonTranslation(), name="python")
+
 First, a simple example:
 
   >>> body = """\

Added: z3c.pt/trunk/z3c/pt/interfaces.py
===================================================================
--- z3c.pt/trunk/z3c/pt/interfaces.py	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/interfaces.py	2008-03-16 17:20:11 UTC (rev 84711)
@@ -0,0 +1,40 @@
+from zope import interface
+
+class IExpressionTranslation(interface.Interface):
+    """This interface defines an expression translation utility."""
+    
+    def name(string):
+        """Should interpret ``string`` as a name. This method will
+        usually be the identity function."""
+    
+    def value(string):
+        """Translate ``string`` to a Python value-expression."""
+        
+    def search(string):
+        """Extracts the longest valid expression from the beginning of
+        the provided string."""
+
+    def variable(string):
+        """A variable definition.
+        
+        Specification:
+        
+           variables :: = variable_name [, variables]
+
+        """
+        
+    def mapping(string):
+        """A mapping definition."""
+
+    def definition(string):
+        """A definition."""
+
+    def definitions(string):
+        """Multiple definitions.
+        
+        Specification:
+
+           argument ::= define_var [';' define_var]
+           define_var ::= Name python_expression
+           
+        """

Deleted: z3c.pt/trunk/z3c/pt/io.py
===================================================================
--- z3c.pt/trunk/z3c/pt/io.py	2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/io.py	2008-03-16 17:20:11 UTC (rev 84711)
@@ -1,81 +0,0 @@
-from StringIO import StringIO
-
-class CodeIO(StringIO):
-    """
-    A high-level I/O class to write Python code to a stream.
-    Indentation is managed using ``indent`` and ``outdent``.
-
-    Also:
-    
-    * Convenience methods for keeping track of temporary
-    variables (see ``save``, ``restore`` and ``getvariable``).
-
-    * Methods to process clauses (see ``begin`` and ``end``).
-    
-    """
-
-    t_prefix = '_tmp'
-    v_prefix = '_var'
-    
-    def __init__(self, indentation=0, indentation_string="\t"):
-        StringIO.__init__(self)
-        self.indentation = indentation
-        self.indentation_string = indentation_string
-        self.queue = u''
-        self.scope = [set()]
-        
-        self._variables = {}
-        self.t_counter = 0
-
-    def save(self):
-        self.t_counter += 1
-        return "%s%d" % (self.t_prefix, self.t_counter)
-        
-    def restore(self):
-        var = "%s%d" % (self.t_prefix, self.t_counter)
-        self.t_counter -= 1
-        return var
-        
-    def indent(self, amount=1):
-        if amount > 0:
-            self.cook()
-            self.indentation += amount
-
-    def outdent(self, amount=1):
-        if amount > 0:
-            self.cook()
-            self.indentation -= amount
-
-    def out(self, string):
-        self.queue += string
-
-    def cook(self):
-        if self.queue:
-            queue = self.queue
-            self.queue = ''
-            self.write("_out.write('%s')" %
-                       queue.replace('\n', '\\n').replace("'", "\\'"))
-        
-    def write(self, string):
-        self.cook()
-        StringIO.write(
-            self, self.indentation_string * self.indentation + string + '\n')
-
-    def getvalue(self):
-        self.cook()
-        return StringIO.getvalue(self)
-
-    def begin(self, clauses):
-        if isinstance(clauses, (list, tuple)):
-            for clause in clauses:
-                self.begin(clause)
-        else:
-            clauses.begin(self)
-            
-    def end(self, clauses):
-        if isinstance(clauses, (list, tuple)):
-            for clause in reversed(clauses):
-                self.end(clause)
-        else:
-            clauses.end(self)
-        

Deleted: z3c.pt/trunk/z3c/pt/parsing.py
===================================================================
--- z3c.pt/trunk/z3c/pt/parsing.py	2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/parsing.py	2008-03-16 17:20:11 UTC (rev 84711)
@@ -1,263 +0,0 @@
-import parser
-
-def name(string):
-    return string
-
-def search(string):
-    """
-    Extracts the longest valid Python-expression from the beginning of
-    the provided string..
-    
-      >>> search("'Hello World'")
-      "'Hello World'"
-      
-      >>> search("'Hello World'}")
-      "'Hello World'"
-
-      >>> search("'Hello World' + '")
-      "'Hello World' "
-
-    """
-    
-    left = 0
-    right = left + 1
-
-    current = None
-                    
-    while right <= len(string):
-        expression = string[left:right]
-        
-        try:
-            e = value(expression)
-            current = expression
-        except SyntaxError, e:
-            if right == len(string):
-                if current is not None:
-                    return current
-            
-                raise e
-                    
-        right += 1
-
-    return current
-    
-def value(string):
-    """
-    Specification:
-
-    value :: = python_expression [ |* value ]
-    python_expresion ::= a python expression string
-
-    *) Using | as logical or is not supported.
-
-      >>> value("4 + 5")
-      ['4 + 5']
-
-    Complex expressions:
-
-      >>> value("a.non_defined_method() | 1")
-      ['a.non_defined_method() ', '1']
-
-    Expression with non-semantic horizontal bar.
-
-      >>> value("'|'")
-      ["'|'"]
-
-    Expression with non-semantic horizontal bar and semantic bar.
-
-      >>> value("a.non_defined_method() | '|'")
-      ['a.non_defined_method() ', "'|'"]
-
-    """
-
-    string = string.replace('\n', '').strip()
-
-    if not string:
-        return []
-        
-    expressions = []
-
-    i = j = 0
-    while i < len(string):
-        j = string.find('|', j + 1)
-        if j == -1:
-            j = len(string)
-
-        expr = string[i:j].lstrip()
-
-        try:
-            # we use the ``parser`` module to determine if
-            # an expression is a valid python expression
-            parser.expr(expr.encode('utf-8'))
-        except SyntaxError, e:
-            if j < len(string):
-                continue
-
-            raise e
-            
-        expressions.append(expr)
-        i = j + 1
-
-    return expressions
-
-def variable(string):
-    """
-    Specification:
-    
-    variables :: = variable_name [, variables]
-
-    Single variable:
-
-      >>> variable("variable")
-      ('variable',)
-
-    Multiple variables:
-
-      >>> variable("variable1, variable2")
-      ('variable1', 'variable2')
-      
-    """
-
-    variables = []
-    for var in string.split(', '):
-        var = var.strip()
-
-        if var in ('repeat',):
-            raise ValueError, "Invalid variable name '%s' (reserved)." % variable
-
-        if var.startswith('_') and not var.startswith('_tmp'):
-            raise ValueError(
-                "Invalid variable name '%s' (starts with an underscore)." % var)
-        
-        variables.append(var)
-
-    return tuple(variables)
-
-def mapping(string):
-    """
-
-      >>> mapping("abc def")
-      [('abc', 'def')]
-
-      >>> mapping("abc")
-      [('abc', None)]
-
-      >>> mapping("abc; def ghi")
-      [('abc', None), ('def', 'ghi')]
-
-    """
-
-    defs = string.split(';')
-    mappings = []
-    for d in defs:
-        d = d.strip()
-        while '  ' in d:
-            d = d.replace('  ', ' ')
-
-        parts = d.split(' ')
-        if len(parts) == 1:
-            mappings.append((d, None))
-        elif len(parts) == 2:
-            mappings.append((parts[0], parts[1]))
-        else:
-            raise ValueError, "Invalid mapping (%s)." % string
-
-    return mappings
-    
-def definitions(string):
-    """
-    Specification:
-
-    argument ::= define_var [';' define_var]
-    define_var ::= Name python_expression
-
-    Single define:
-
-      >>> definitions("variable expression")
-      [(['variable'], ['expression'])]
-    
-    Multiple defines:
-
-      >>> definitions("variable1 expression1; variable2 expression2")
-      [(['variable1'], ['expression1']), (['variable2'], ['expression2'])]
-
-    Tuple define:
-
-      >>> definitions("(variable1, variable2) (expression1, expression2)")
-      [(['variable1', 'variable2'], ['(expression1, expression2)'])]
-
-    Use of unescaped semicolon in an expression:
-
-      >>> definitions("variable ';'")
-      [(['variable'], ["';'"])]
-    
-    A define clause that ends in a semicolon:
-
-      >>> definitions("variable expression;")
-      [(['variable'], ['expression'])]
-
-    A define clause with a trivial expression (we do allow this):
-    
-      >>> definitions("variable")
-      [(['variable'], None)]
-
-    A proper define clause following one with a trivial expression:
-
-      >>> definitions("variable1 expression; variable2")
-      [(['variable1'], ['expression']), (['variable2'], None)]
-      
-    """
-
-    string = string.replace('\n', '').strip()
-
-    defines = []
-
-    i = 0
-    while i < len(string):
-        while string[i] == ' ':
-            i += 1
-
-        # get variable definition
-        if string[i] == '(':
-            j = string.find(')', i+1)
-            if j == -1:
-                raise ValueError, "Invalid variable tuple definition (%s)." % string
-            var = variable(string[i+1:j])
-            j += 1
-        else:
-            j = string.find(' ', i + 1)
-            if j == -1:
-                var = variable(string[i:])
-                j = len(string)
-            else:
-                var = variable(string[i:j])
-
-        # get expression
-        i = j
-        while j < len(string):
-            j = string.find(';', j+1)
-            if j == -1:
-                j = len(string)
-
-            try:
-                expr = value(string[i:j])
-            except SyntaxError, e:
-                if j < len(string):
-                    continue
-                raise e
-            break
-        else:
-            expr = None
-
-        defines.append((list(var), expr))
-
-        i = j + 1
-
-    return defines
-
-def definition(string):
-    defs = definitions(string)
-    if len(defs) != 1:
-        raise ValueError, "Multiple definitions not allowed."
-
-    return defs[0]

Modified: z3c.pt/trunk/z3c/pt/template.py
===================================================================
--- z3c.pt/trunk/z3c/pt/template.py	2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/template.py	2008-03-16 17:20:11 UTC (rev 84711)
@@ -4,7 +4,8 @@
 
 class BaseTemplate(object):
     registry = {}
-
+    default_expression = 'python'
+    
     def __init__(self, body):
         self.body = body
         self.signature = hash(body)        
@@ -14,7 +15,8 @@
         return NotImplementedError("Must be implemented by subclass.")
 
     def cook(self, params):
-        source, _globals = self.translate(self.body, params)
+        source, _globals = self.translate(
+            self.body, params=params, default_expression=self.default_expression)
         suite = codegen.Suite(source)
 
         self.source = source

Added: z3c.pt/trunk/z3c/pt/testing.py
===================================================================
--- z3c.pt/trunk/z3c/pt/testing.py	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/testing.py	2008-03-16 17:20:11 UTC (rev 84711)
@@ -0,0 +1,5 @@
+import expressions
+
+def value(string):
+    translator = expressions.PythonTranslation()
+    return translator.value(string)

Modified: z3c.pt/trunk/z3c/pt/tests/test_doctests.py
===================================================================
--- z3c.pt/trunk/z3c/pt/tests/test_doctests.py	2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/tests/test_doctests.py	2008-03-16 17:20:11 UTC (rev 84711)
@@ -1,15 +1,14 @@
 import zope.testing
 import unittest
 
-OPTIONFLAGS = (#zope.testing.doctest.REPORT_ONLY_FIRST_FAILURE |
-               zope.testing.doctest.ELLIPSIS |
+OPTIONFLAGS = (zope.testing.doctest.ELLIPSIS |
                zope.testing.doctest.NORMALIZE_WHITESPACE)
 
 import zope.component.testing
 
 def test_suite():
     filesuites = ['README.txt', 'BENCHMARKS.txt', 'translation.txt', 'i18n.txt', 'codegen.txt']
-    testsuites = ['z3c.pt.translation', 'z3c.pt.parsing', 'z3c.pt.clauses']
+    testsuites = ['z3c.pt.translation', 'z3c.pt.clauses', 'z3c.pt.expressions']
 
     return unittest.TestSuite(
         [zope.testing.doctest.DocTestSuite(doctest,

Modified: z3c.pt/trunk/z3c/pt/translation.py
===================================================================
--- z3c.pt/trunk/z3c/pt/translation.py	2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/translation.py	2008-03-16 17:20:11 UTC (rev 84711)
@@ -1,3 +1,5 @@
+from zope import component
+
 from StringIO import StringIO
 import lxml.etree
 import re
@@ -2,38 +4,22 @@
 
-import io
+import generation
 import utils
-import parsing
+import expressions
 import clauses
+import interfaces
 
-# set up namespace
-lookup = lxml.etree.ElementNamespaceClassLookup()
-parser = lxml.etree.XMLParser()
-parser.setElementClassLookup(lookup)
-
-xhtml = lxml.etree.Namespace('http://www.w3.org/1999/xhtml')
-tal = lxml.etree.Namespace('http://xml.zope.org/namespaces/tal')
-
-wrapper = """\
-def render(%starget_language=None):
-\tglobal utils
-
-\t_out = utils.initialize_stream()
-    
-\t(_attributes, repeat) = utils.initialize_tal()
-\t(_domain, _translate) = utils.initialize_i18n()
-\t(_escape, _marker) = utils.initialize_helpers()
-
-\t_target_language = target_language
-%s
-\treturn _out.getvalue().decode('utf-8')
-"""
 interpolation_regex = re.compile(r'([^\\]\$|^\$){(?P<expression>.*)}')
 
-def attribute(ns, factory, default=None):
+def attribute(ns, factory=None, default=None):
     def get(self):
         value = self.attrib.get(ns)
         if value is not None:
-            return factory(value)
+            if factory is None:
+                return value
+
+            f = factory(self._translator())
+            return f(value)
         elif default is not None:
             return default
+        
     def set(self, value):
@@ -43,13 +29,13 @@
 
     return property(get, set)
 
-def interpolate(string):
+def interpolate(string, translator):
     m = interpolation_regex.search(string)
     if m is None:
         return None
 
     left = m.start()
-    exp = parsing.search(string[left+3:])
+    exp = translator.search(string[left+3:])
     right = left+4+len(exp)
 
     m = interpolation_regex.search(string[:right])
@@ -65,10 +51,10 @@
 class Element(lxml.etree.ElementBase):
     def begin(self, stream):
         stream.scope.append(set())
-        stream.begin(self.clauses())
+        stream.begin(self._clauses())
         
     def end(self, stream):
-        stream.end(self.clauses())
+        stream.end(self._clauses())
         stream.scope.pop()
 
     def body(self, stream):
@@ -103,9 +89,10 @@
 
     def interpolate(self, stream):
         # interpolate text
+        translator = self._translator()
         if self.text is not None:
             while self.text:
-                m = interpolate(self.text)
+                m = interpolate(self.text, translator)
                 if m is None:
                     break
 
@@ -119,7 +106,7 @@
         # interpolate tail
         if self.tail is not None:
             while self.tail:
-                m = interpolate(self.tail)
+                m = interpolate(self.tail, translator)
                 if m is None:
                     break
 
@@ -143,7 +130,7 @@
 
             while value:
                 string = value[i:]
-                m = interpolate(string)
+                m = interpolate(string, translator)
                 if m is None:
                     break
 
@@ -157,7 +144,7 @@
                 format += '%s%s'
                 exp = m.group('expression')
 
-                if len(parsing.value(exp)) == 1:
+                if len(translator.value(exp)) == 1:
                     terms.extend(("'%s'" % text.replace("'", "\\'"), exp))
                 else:
                     var = stream.save()
@@ -188,7 +175,7 @@
                 else:
                     self.attrib[define] = '%s %s' % (name, expression)
                 
-    def clauses(self):
+    def _clauses(self):
         _ = []
 
         # i18n domain
@@ -272,7 +259,7 @@
                     
                     subclauses = []
                     subclauses.append(clauses.Define('_out', ['utils.initialize_stream()']))
-                    subclauses.append(clauses.Group(element.clauses()))
+                    subclauses.append(clauses.Group(element._clauses()))
                     subclauses.append(clauses.Assign(['_out.getvalue()'],
                                                      "%s['%s']" % (mapping, name)))
 
@@ -380,36 +367,48 @@
 
         return attributes
 
+    def _translator(self):
+        while self.default_expression is None:
+            self = self.getparent()
+            if self is None:
+                raise ValueError("Default expression not set.")
+            
+        return component.getUtility(
+            interfaces.IExpressionTranslation, name=self.default_expression)
+
     define = attribute(
-        "{http://xml.zope.org/namespaces/tal}define", parsing.definitions)
+        "{http://xml.zope.org/namespaces/tal}define", lambda p: p.definitions)
     condition = attribute(
-        "{http://xml.zope.org/namespaces/tal}condition", parsing.value)
+        "{http://xml.zope.org/namespaces/tal}condition", lambda p: p.value)
     repeat = attribute(
-        "{http://xml.zope.org/namespaces/tal}repeat", parsing.definition)
+        "{http://xml.zope.org/namespaces/tal}repeat", lambda p: p.definition)
     attributes = attribute(
-        "{http://xml.zope.org/namespaces/tal}attributes", parsing.definitions)
+        "{http://xml.zope.org/namespaces/tal}attributes", lambda p: p.definitions)
     content = attribute(
-        "{http://xml.zope.org/namespaces/tal}content", parsing.value)
+        "{http://xml.zope.org/namespaces/tal}content", lambda p: p.value)
     replace = attribute(
-        "{http://xml.zope.org/namespaces/tal}replace", parsing.value)
+        "{http://xml.zope.org/namespaces/tal}replace", lambda p: p.value)
     omit = attribute(
-        "{http://xml.zope.org/namespaces/tal}omit-tag", parsing.value)
+        "{http://xml.zope.org/namespaces/tal}omit-tag", lambda p: p.value)
     i18n_translate = attribute(
-        "{http://xml.zope.org/namespaces/i18n}translate", parsing.name)
+        "{http://xml.zope.org/namespaces/i18n}translate")
     i18n_attributes = attribute(
-        "{http://xml.zope.org/namespaces/i18n}attributes", parsing.mapping)
+        "{http://xml.zope.org/namespaces/i18n}attributes", lambda p: p.mapping)
     i18n_domain = attribute(
-        "{http://xml.zope.org/namespaces/i18n}domain", parsing.name)
+        "{http://xml.zope.org/namespaces/i18n}domain")
     i18n_name = attribute(
-        "{http://xml.zope.org/namespaces/i18n}name", parsing.name)
-
+        "{http://xml.zope.org/namespaces/i18n}name")
+    default_expression = attribute(
+        "{http://xml.zope.org/namespaces/tal}default-expression")
+    
 class TALElement(Element):
-    define = attribute("define", parsing.definitions)
-    replace = attribute("replace", parsing.value)
-    repeat = attribute("repeat", parsing.definition)
-    attributes = attribute("attributes", parsing.value)
-    content = attribute("content", parsing.value)
-    omit = attribute("omit-tag", parsing.value, u"")
+    define = attribute("define", lambda p: p.definitions)
+    replace = attribute("replace", lambda p: p.value)
+    repeat = attribute("repeat", lambda p: p.definition)
+    attributes = attribute("attributes", lambda p: p.value)
+    content = attribute("content", lambda p: p.value)
+    omit = attribute("omit-tag", lambda p: p.value, u"")
+    default_expression = attribute("default-expression", lambda p: p.name)
     
     def _static_attributes(self):
         attributes = {}
@@ -428,38 +427,50 @@
 
         return attributes
 
-xhtml[None] = Element
-tal[None] = TALElement
+# set up namespaces for XML parsing
+lookup = lxml.etree.ElementNamespaceClassLookup()
+parser = lxml.etree.XMLParser()
+parser.setElementClassLookup(lookup)
 
-def translate_xml(body, params=[]):
+lxml.etree.Namespace('http://www.w3.org/1999/xhtml')[None] = Element
+lxml.etree.Namespace('http://xml.zope.org/namespaces/tal')[None] = TALElement
+
+def translate_xml(body, *args, **kwargs):
     tree = lxml.etree.parse(StringIO(body), parser)
     root = tree.getroot()
-    return translate_etree(root, params=params)
+    return translate_etree(root, *args, **kwargs)
 
-def translate_etree(root, params=[]):
+def translate_etree(root, params=[], default_expression='python'):
     if None not in root.nsmap:
         raise ValueError, "Must set default namespace."
-        
-    stream = io.CodeIO(indentation=1, indentation_string="\t")
 
+    # set up code generation stream
+    stream = generation.CodeIO(indentation=1, indentation_string="\t")
     stream.scope.append(set(params + ['_out']))
 
+    # set default expression name
+    key = '{http://xml.zope.org/namespaces/tal}default-expression'
+    if key not in root.attrib:
+        root.attrib[key] = default_expression
+
+    # visit root
     root.interpolate(stream)
     root.visit(stream)
 
-    code = stream.getvalue()
+    # prepare template arguments
     args = ', '.join(params)
     if args: args += ', '
-    
-    return wrapper % (args, code), {'utils': utils}
 
-def translate_text(body, params=[]):
+    code = stream.getvalue()
+    return generation.wrapper % (args, code), {'utils': utils}
+
+def translate_text(body, *args, **kwargs):
     xml = parser.makeelement(
         '{http://www.w3.org/1999/xhtml}text',
         nsmap={None: 'http://www.w3.org/1999/xhtml'})
     xml.text = body
     xml.attrib['{http://xml.zope.org/namespaces/tal}omit-tag'] = ''
-    return translate_etree(xml, params=params)    
+    return translate_etree(xml, *args, **kwargs)
     
 def _translate(expressions, mapping=None, default=None):
     return [("_translate(%s, domain=_domain, mapping=%s, " + \

Modified: z3c.pt/trunk/z3c/pt/translation.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/translation.txt	2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/translation.txt	2008-03-16 17:20:11 UTC (rev 84711)
@@ -11,15 +11,21 @@
   ...    exec source in _globals, _locals
   ...    return _locals['render'](**kwargs)
 
+We'll first register the Python expression translator.
+
+  >>> from z3c.pt.expressions import PythonTranslation
+  >>> from zope.component import provideUtility
+  >>> provideUtility(PythonTranslation(), name="python")
+
 TAL templates
 -------------
 
-  >>> from z3c.pt.translation import translate_xml
-
 For the functional testing of TAL templates we provide a complex
 template that covers most cases. It's been developed alongside
 development and is not meant to be easy on the eye.
-  
+
+  >>> from z3c.pt.translation import translate_xml
+
   >>> body = """\
   ... <div xmlns="http://www.w3.org/1999/xhtml"
   ...      xmlns:tal="http://xml.zope.org/namespaces/tal">



More information about the Checkins mailing list