Changeset 3

Show
Ignore:
Timestamp:
11/08/04 19:00:22 (4 years ago)
Author:
steve
Message:

foreach over plain content

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/airspeed.py

    r2 r3  
    33import re 
    44 
    5 class SyntaxError(Exception): pass 
     5class TemplateSyntaxError(Exception): pass 
    66 
    77class Tokeniser: 
    8     PLAIN, IF, PLACEHOLDER, END = range(4
     8    PLAIN, IF, PLACEHOLDER, FOREACH, END = range(5
    99 
    1010    UP_TO_NEXT_TEMPLATE_BIT = re.compile('^(.*?)((?:#|\$).*)', re.MULTILINE + re.DOTALL) 
    1111    REST = '(.*)$' 
    12     NAME_OR_CALL = '[a-z0-9_]+(?:\(\))?' 
     12    NAME = '[a-z0-9_]+' 
     13    NAME_OR_CALL = NAME + '(?:\(\))?' 
    1314    EXPRESSION = '(' + NAME_OR_CALL + '(?:\.' + NAME_OR_CALL + ')*)' 
    1415    PLACEHOLDER_PATTERN = re.compile('^\$(!?)({?)' + EXPRESSION + '(}?)' + REST, re.IGNORECASE + re.DOTALL + re.MULTILINE) 
    1516    BEGIN_IF_PATTERN = re.compile('^#if[ \t]*\([ \t]*\$' + EXPRESSION + '[ \t]*\)' + REST, re.IGNORECASE + re.DOTALL + re.MULTILINE) 
     17    BEGIN_FOREACH_PATTERN = re.compile('^#foreach[ \t]*\([ \t]*\$(' + NAME + ')[ \t]+in[ \t]+\$' + EXPRESSION + '[ \t]*\)' + REST, re.IGNORECASE + re.DOTALL + re.MULTILINE) 
    1618    END_PATTERN = re.compile('^#end' + REST, re.IGNORECASE + re.DOTALL + re.MULTILINE) 
    1719 
     
    3436                yield self.IF, expression 
    3537                continue 
     38            m = self.BEGIN_FOREACH_PATTERN.match(interesting) 
     39            if m: 
     40                iter_var, expression, text = m.groups() 
     41                yield self.FOREACH, (expression, iter_var) 
     42                continue 
    3643            m = self.END_PATTERN.match(interesting) 
    3744            if m: 
     
    3946                (text,) = m.groups() 
    4047                continue 
    41             raise SyntaxError("invalid token: %s" % text[:40]) 
     48            raise TemplateSyntaxError("invalid token: %s" % text[:40]) 
    4249 
    4350    def get_placeholder(self, match): 
    4451        silent, open_brace, var_name, close_brace, rest = match.groups() 
    4552        if open_brace and not close_brace: 
    46             raise SyntaxError("unmatched braces") 
     53            raise TemplateSyntaxError("unmatched braces") 
    4754        if close_brace and not open_brace: 
    4855            rest = close_brace + rest 
     
    6168        output = [] 
    6269        filter_output_at_nesting_level = [False] 
    63         tokens = Tokeniser().tokenise(str(content)) 
    64         for token_type, token_value in tokens: 
     70        tokens = list(Tokeniser().tokenise(str(content))) 
     71        tokens.reverse() 
     72        while tokens: 
     73            token_type, token_value = tokens.pop() 
    6574            filter_at_this_level = filter_output_at_nesting_level[-1] 
    6675            if token_type == Tokeniser.PLAIN: 
     
    8291                filter_output_at_nesting_level.append(filter_my_content) 
    8392                continue 
     93            if token_type == Tokeniser.FOREACH: 
     94                expression, iter_var = token_value 
     95                filter_output_at_nesting_level.append(filter_at_this_level) 
     96                token_type, token_value = tokens.pop() 
     97                values = self.find(expression) 
     98                if token_type == Tokeniser.PLAIN: 
     99                    for item in values: 
     100                        if not filter_at_this_level: output.append(token_value) 
     101                continue 
    84102            if token_type == Tokeniser.END: 
    85103                if len(filter_output_at_nesting_level) == 1: 
    86                     raise SyntaxError("#end without beginning of block") 
     104                    raise TemplateSyntaxError("#end without beginning of block") 
    87105                del filter_output_at_nesting_level[-1] 
    88106                continue 
    89             raise SyntaxError("invalid token: %s" % text[:40]) 
     107            raise TemplateSyntaxError("invalid token: %s" % text[:40]) 
    90108 
    91109        if len(filter_output_at_nesting_level) > 1: 
    92             raise SyntaxError("Unclosed block") 
     110            raise TemplateSyntaxError("Unclosed block") 
    93111        return ''.join(output) 
    94112 
  • trunk/airspeed_test.py

    r2 r3  
    4444        template = airspeed.Template("Hello ${name.") 
    4545        parser["name"] = "World" 
    46         self.assertRaises(airspeed.SyntaxError, parser.merge, template) 
     46        self.assertRaises(airspeed.TemplateSyntaxError, parser.merge, template) 
    4747 
    4848    def test_unmatched_trailing_brace_preserved(self): 
     
    110110        self.assertEquals("hello Steve.\nHappy Birthday\n.\nOff out later?", parser.merge(template)) 
    111111 
     112    def test_foreach_with_plain_content_loops_correctly(self): 
     113        parser = airspeed.Parser() 
     114        template = airspeed.Template("#foreach ($name in $names)Hello you. #end") 
     115        parser["names"] = ["Chris", "Steve"] 
     116        self.assertEquals("Hello you. Hello you. ", parser.merge(template)) 
    112117 
    113118if __name__ == '__main__':