Changeset 30

Show
Ignore:
Timestamp:
23/08/04 15:51:52 (4 years ago)
Author:
steve
Message:

array literals

Files:

Legend:

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

    r29 r30  
    4646        if len(got) > 40: 
    4747            got = got[:36] + ' ...' 
    48         Exception.__init__(self, "line %d, column %d: expected %s, got: %s ..." % (self.line, self.column, expected, got)) 
     48        Exception.__init__(self, "line %d, column %d: expected %s in %s, got: %s ..." % (self.line, self.column, expected, self.element_name(), got)) 
    4949 
    5050    def get_position_strings(self): 
     
    5757        caret_pos = self.column 
    5858        return [error_line, ' ' * (caret_pos - 1) + '^'] 
     59 
     60    def element_name(self): 
     61        return re.sub('([A-Z])', lambda m: ' ' + m.group(1).lower(), self.element.__class__.__name__).strip() 
    5962 
    6063 
     
    230233 
    231234 
    232 class RangeLiteral(_Element): 
    233     RANGE = re.compile(r'\[[ \t]*(\-?\d+)[ \t]*\.\.[ \t]*(\-?\d+)[ \t]*\](.*)$') 
     235class Range(_Element): 
     236    RANGE = re.compile(r'(\-?\d+)[ \t]*\.\.[ \t]*(\-?\d+)(.*)$') 
    234237 
    235238    def parse(self): 
     
    242245 
    243246 
     247class ValueList(_Element): 
     248    COMMA = re.compile(r'\s*,\s*(.*)$', re.S) 
     249 
     250    def parse(self): 
     251        self.values = [] 
     252        try: value = self.next_element(Value) 
     253        except NoMatch: 
     254            pass 
     255        else: 
     256            self.values.append(value) 
     257            while self.optional_match(self.COMMA): 
     258                value = self.require_next_element(Value, 'value') 
     259                self.values.append(value) 
     260 
     261    def calculate(self, namespace): 
     262        for value in self.values: 
     263            yield value.calculate(namespace) 
     264 
     265 
     266class _EmptyValues: 
     267    def calculate(self, namespace): 
     268        return [] 
     269 
     270 
     271class ArrayLiteral(_Element): 
     272    START = re.compile(r'\[[ \t]*(.*)$') 
     273    END = re.compile(r'[ \t]*\](.*)$') 
     274    values = _EmptyValues() 
     275 
     276    def parse(self): 
     277        self.identity_match(self.START) 
     278        try: 
     279            self.values = self.next_element((Range, ValueList)) 
     280        except NoMatch: 
     281            pass 
     282        self.require_match(self.END, ']') 
     283        self.calculate = self.values.calculate 
     284 
     285 
    244286class Value(_Element): 
    245287    def parse(self): 
    246         self.expression = self.next_element((SimpleReference, IntegerLiteral, StringLiteral, SingleQuotedStringLiteral, RangeLiteral)) 
     288        self.expression = self.next_element((SimpleReference, IntegerLiteral, StringLiteral, SingleQuotedStringLiteral, ArrayLiteral)) 
    247289 
    248290    def calculate(self, namespace): 
     
    273315            return None ## TODO: an explicit 'not found' exception? 
    274316        if self.parameters is not None: 
    275             values = [value.calculate(top_namespace) for value in self.parameters.values] 
    276             result = result(*values) 
     317            result = result(*self.parameters.calculate(top_namespace)) 
    277318        return result 
    278319 
     
    308349class ParameterList(_Element): 
    309350    START = re.compile(r'\(\s*(.*)$', re.S) 
     351    COMMA = re.compile(r'\s*,\s*(.*)$', re.S) 
    310352    END = re.compile(r'\s*\)(.*)$', re.S) 
    311     COMMA = re.compile(r'\s*,\s*(.*)$', re.S) 
    312  
    313     def parse(self): 
    314         self.values = [] 
    315         self.identity_match(self.START) 
    316         try: value = self.next_element(Value) 
    317         except NoMatch: 
    318             pass 
    319         else: 
    320             self.values.append(value) 
    321             while self.optional_match(self.COMMA): 
    322                 value = self.require_next_element(Value, 'value') 
    323                 self.values.append(value) 
     353    values = _EmptyValues() 
     354 
     355    def parse(self): 
     356        self.identity_match(self.START) 
     357        try: self.values = self.next_element(ValueList) 
     358        except NoMatch: pass 
    324359        self.require_match(self.END, ')') 
     360 
     361    def calculate(self, namespace): 
     362        return self.values.calculate(namespace) 
    325363 
    326364 
  • trunk/airspeed_test.py

    r29 r30  
    377377        self.assertEquals('2,1,0,-1,-2,', template.merge({})) 
    378378 
    379     def test_local_namespace_methods_not_available(self): 
     379    def test_local_namespace_methods_are_not_available_in_context(self): 
    380380        template = airspeed.Template('#macro(tryme)$values#end#tryme()') 
    381381        self.assertEquals('$values', template.merge({})) 
     382 
     383    def test_array_literal(self): 
     384        template = airspeed.Template('#set($values = ["Hello ", $person, ", your lucky number is ", 7])#foreach($value in $values)$value#end') 
     385        self.assertEquals('Hello Chris, your lucky number is 7', template.merge({'person': 'Chris'})) 
     386 
     387    def test_nested_array_literals(self): 
     388        template = airspeed.Template('#set($values = [["Hello ", "Steve"], ["Hello", " Chris"]])#foreach($pair in $values)#foreach($word in $pair)$word#end. #end') 
     389        self.assertEquals('Hello Steve. Hello Chris. ', template.merge({})) 
    382390 
    383391# 
     
    386394#  Math expressions 
    387395#  Gobbling up whitespace (tricky!) 
    388 #  range literals 
    389396#  list literals 
    390397#  Bind #macro calls at compile time? 
    391398#  #stop ? 
    392 #  Interpolated strings 
     399#  Interpolated strings -- what about \$ etc? 
    393400#  Directives inside string literals 
    394401#  map literals