Changeset 33
- Timestamp:
- 16/09/04 15:07:05 (8 years ago)
- Location:
- trunk
- Files:
-
- 2 modified
-
airspeed.py (modified) (27 diffs)
-
airspeed_test.py (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/airspeed.py
r32 r33 30 30 if loader is None: loader = NullLoader() 31 31 self.ensure_compiled() 32 self.root_element.evaluate( namespace, fileobj, loader)32 self.root_element.evaluate(fileobj, namespace, loader) 33 33 34 34 … … 190 190 191 191 class Text(_Element): 192 PLAIN = re.compile(r'((?:[^\\\$#]|\\[\$#])+|\$[^!\{a-z0-9_]|\$$|\\ \\)(.*)$', re.S + re.I)192 PLAIN = re.compile(r'((?:[^\\\$#]|\\[\$#])+|\$[^!\{a-z0-9_]|\$$|\\.)(.*)$', re.S + re.I) 193 193 ESCAPED_CHAR = re.compile(r'\\([\\\$#])') 194 194 … … 199 199 self.text = self.ESCAPED_CHAR.sub(unescape, text) 200 200 201 def evaluate(self, namespace, stream, loader):201 def evaluate(self, stream, namespace, loader): 202 202 stream.write(self.text) 203 203 … … 210 210 self.value = int(self.value) 211 211 212 def calculate(self, namespace ):212 def calculate(self, namespace, loader): 213 213 return self.value 214 214 215 215 216 216 class StringLiteral(_Element): 217 STRING = re.compile(r '"((?:\\["nrbt\\\\]|[^"\n\r\\])+)"(.*)', re.S)218 ESCAPED_CHAR = re.compile(r '\\([nrbt"\\])')217 STRING = re.compile(r"'((?:\\['nrbt\\\\\\$]|[^'\n\r\\])+)'(.*)", re.S) 218 ESCAPED_CHAR = re.compile(r"\\([nrbt'\\])") 219 219 220 220 def parse(self): 221 221 value, = self.identity_match(self.STRING) 222 222 def unescape(match): 223 return {'n': '\n', 'r': '\r', 'b': '\b', 't': '\t', '"': '"', '\\': '\\', "'": "'"} [match.group(1)]223 return {'n': '\n', 'r': '\r', 'b': '\b', 't': '\t', '"': '"', '\\': '\\', "'": "'"}.get(match.group(1), '\\' + match.group(1)) 224 224 self.value = self.ESCAPED_CHAR.sub(unescape, value) 225 225 226 def calculate(self, namespace ):226 def calculate(self, namespace, loader): 227 227 return self.value 228 228 229 230 class SingleQuotedStringLiteral(StringLiteral): 231 STRING = re.compile(r"'((?:\\['nrbt\\\\]|[^'\n\r\\])+)'(.*)", re.S) 232 ESCAPED_CHAR = re.compile(r"\\([nrbt'\\])") 229 class InterpolatedStringLiteral(StringLiteral): 230 STRING = re.compile(r'"((?:\\["nrbt\\\\\\$]|[^"\n\r\\])+)"(.*)', re.S) 231 ESCAPED_CHAR = re.compile(r'\\([nrbt"\\])') 232 233 def parse(self): 234 StringLiteral.parse(self) 235 self.block = Block(self.value, 0) 236 237 def calculate(self, namespace, loader): 238 output = StringIO.StringIO() 239 self.block.evaluate(output, namespace, loader) 240 return output.getvalue() 233 241 234 242 … … 239 247 self.value1, self.value2 = map(int, self.identity_match(self.RANGE)) 240 248 241 def calculate(self, namespace ):249 def calculate(self, namespace, loader): 242 250 if self.value2 < self.value1: 243 251 return xrange(self.value1, self.value2 - 1, -1) … … 259 267 self.values.append(value) 260 268 261 def calculate(self, namespace ):269 def calculate(self, namespace, loader): 262 270 for value in self.values: 263 yield value.calculate(namespace )271 yield value.calculate(namespace, loader) 264 272 265 273 266 274 class _EmptyValues: 267 def calculate(self, namespace ):275 def calculate(self, namespace, loader): 268 276 return [] 269 277 … … 286 294 class Value(_Element): 287 295 def parse(self): 288 self.expression = self.next_element((SimpleReference, IntegerLiteral, StringLiteral, SingleQuotedStringLiteral, ArrayLiteral))289 290 def calculate(self, namespace ):291 return self.expression.calculate(namespace )296 self.expression = self.next_element((SimpleReference, IntegerLiteral, StringLiteral, InterpolatedStringLiteral, ArrayLiteral)) 297 298 def calculate(self, namespace, loader): 299 return self.expression.calculate(namespace, loader) 292 300 293 301 … … 301 309 except NoMatch: pass 302 310 303 def calculate(self, current_object, top_namespace):311 def calculate(self, current_object, loader, top_namespace): 304 312 look_in_dict = True 305 313 if not isinstance(current_object, LocalNamespace): … … 317 325 return None ## TODO: an explicit 'not found' exception? 318 326 if self.parameters is not None: 319 result = result(*self.parameters.calculate(top_namespace ))327 result = result(*self.parameters.calculate(top_namespace, loader)) 320 328 return result 321 329 … … 328 336 self.expression = self.next_element(VariableExpression) 329 337 330 def calculate(self, current_object, global_namespace):331 return self.expression.calculate(current_object, global_namespace)338 def calculate(self, current_object, loader, global_namespace): 339 return self.expression.calculate(current_object, loader, global_namespace) 332 340 333 341 … … 340 348 except NoMatch: pass 341 349 342 def calculate(self, namespace, global_namespace=None):350 def calculate(self, namespace, loader, global_namespace=None): 343 351 if global_namespace == None: 344 352 global_namespace = namespace 345 value = self.part.calculate(namespace, global_namespace)353 value = self.part.calculate(namespace, loader, global_namespace) 346 354 if self.subexpression: 347 value = self.subexpression.calculate(value, namespace)355 value = self.subexpression.calculate(value, loader, namespace) 348 356 return value 349 357 … … 361 369 self.require_match(self.END, ')') 362 370 363 def calculate(self, namespace ):364 return self.values.calculate(namespace )371 def calculate(self, namespace, loader): 372 return self.values.calculate(namespace, loader) 365 373 366 374 … … 374 382 if self.braces: self.require_match(self.CLOSING_BRACE, '}') 375 383 376 def evaluate(self, namespace, stream, loader):377 value = self.expression.calculate(namespace )384 def evaluate(self, stream, namespace, loader): 385 value = self.expression.calculate(namespace, loader) 378 386 if value is None: 379 387 if self.silent: value = '' … … 392 400 393 401 class Null: 394 def evaluate(self, namespace, stream, loader): pass402 def evaluate(self, stream, namespace, loader): pass 395 403 396 404 … … 428 436 self.require_match(self.END, ') or >') 429 437 430 def calculate(self, namespace ):438 def calculate(self, namespace, loader): 431 439 if self.binary_operator is None: 432 return self.value.calculate(namespace )433 value1, value2 = self.value.calculate(namespace ), self.value2.calculate(namespace)440 return self.value.calculate(namespace, loader) 441 value1, value2 = self.value.calculate(namespace, loader), self.value2.calculate(namespace, loader) 434 442 return self.binary_operator.apply_to(value1, value2) 435 443 … … 478 486 self.require_next_element(End, '#else, #elseif or #end') 479 487 480 def evaluate(self, namespace, stream, loader):481 if self.condition.calculate(namespace ):482 self.block.evaluate( namespace, stream, loader)488 def evaluate(self, stream, namespace, loader): 489 if self.condition.calculate(namespace, loader): 490 self.block.evaluate(stream, namespace, loader) 483 491 else: 484 492 for elseif in self.elseifs: 485 if elseif.calculate(namespace ):486 elseif.evaluate( namespace, stream, loader)493 if elseif.calculate(namespace, loader): 494 elseif.evaluate(stream, namespace, loader) 487 495 return 488 self.else_block.evaluate( namespace, stream, loader)496 self.else_block.evaluate(stream, namespace, loader) 489 497 490 498 … … 498 506 self.require_match(self.END, ')') 499 507 500 def calculate(self, namespace):501 namespace[self.var_name] = self.value.calculate(namespace )508 def evaluate(self, stream, namespace, loader): 509 namespace[self.var_name] = self.value.calculate(namespace, loader) 502 510 503 511 … … 524 532 self.require_next_element(End, 'block') 525 533 526 def evaluate(self, namespace, stream, loader):534 def evaluate(self, stream, namespace, loader): 527 535 macro_key = '#' + self.macro_name.lower() 528 536 if macro_key in namespace: … … 530 538 namespace[macro_key] = self 531 539 532 def execute_macro(self, namespace, stream, arg_value_elements, loader):540 def execute_macro(self, stream, namespace, arg_value_elements, loader): 533 541 if len(arg_value_elements) != len(self.arg_names): 534 542 raise Exception("expected %d arguments, got %d" % (len(self.arg_names), len(arg_value_elements))) 535 543 macro_namespace = LocalNamespace(namespace) 536 544 for arg_name, arg_value in zip(self.arg_names, arg_value_elements): 537 macro_namespace[arg_name] = arg_value.calculate(namespace )538 self.block.evaluate( macro_namespace, stream, loader)545 macro_namespace[arg_name] = arg_value.calculate(namespace, loader) 546 self.block.evaluate(stream, macro_namespace, loader) 539 547 540 548 … … 558 566 self.require_match(self.CLOSE_PAREN, 'argument value or )') 559 567 560 def evaluate(self, namespace, stream, loader):568 def evaluate(self, stream, namespace, loader): 561 569 try: macro = namespace['#' + self.macro_name] 562 570 except KeyError: raise Exception('no such macro: ' + self.macro_name) 563 macro.execute_macro( namespace, stream, self.args, loader)571 macro.execute_macro(stream, namespace, self.args, loader) 564 572 565 573 … … 572 580 self.identity_match(self.START) 573 581 self.require_match(self.OPEN_PAREN, '(') 574 self.name = self.require_next_element((StringLiteral, SingleQuotedStringLiteral, SimpleReference), 'template name')582 self.name = self.require_next_element((StringLiteral, InterpolatedStringLiteral, SimpleReference), 'template name') 575 583 self.require_match(self.CLOSE_PAREN, ')') 576 584 577 def evaluate(self, namespace, stream, loader):578 stream.write(loader.load_text(self.name.calculate(namespace )))585 def evaluate(self, stream, namespace, loader): 586 stream.write(loader.load_text(self.name.calculate(namespace, loader))) 579 587 580 588 … … 587 595 self.identity_match(self.START) 588 596 self.require_match(self.OPEN_PAREN, '(') 589 self.name = self.require_next_element((StringLiteral, SingleQuotedStringLiteral, SimpleReference), 'template name')597 self.name = self.require_next_element((StringLiteral, InterpolatedStringLiteral, SimpleReference), 'template name') 590 598 self.require_match(self.CLOSE_PAREN, ')') 591 599 592 def evaluate(self, namespace, stream, loader):593 template = loader.load_template(self.name.calculate(namespace ))600 def evaluate(self, stream, namespace, loader): 601 template = loader.load_template(self.name.calculate(namespace, loader)) 594 602 ## TODO: local namespace? 595 603 template.merge_to(namespace, stream, loader=loader) … … 603 611 self.assignment = self.require_next_element(Assignment, 'assignment') 604 612 605 def evaluate(self, namespace, stream, loader):606 self.assignment. calculate(namespace)613 def evaluate(self, stream, namespace, loader): 614 self.assignment.evaluate(stream, namespace, loader) 607 615 608 616 … … 625 633 self.require_next_element(End, '#end') 626 634 627 def evaluate(self, namespace, stream, loader):628 iterable = self.value.calculate(namespace )635 def evaluate(self, stream, namespace, loader): 636 iterable = self.value.calculate(namespace, loader) 629 637 counter = 1 630 638 try: … … 633 641 namespace['velocityCount'] = counter 634 642 namespace[self.loop_var_name] = item 635 self.block.evaluate( namespace, stream, loader)643 self.block.evaluate(stream, namespace, loader) 636 644 counter += 1 637 645 except TypeError: 638 print iterable639 646 raise 640 647 … … 646 653 raise self.syntax_error('block element') 647 654 648 def evaluate(self, namespace, stream, loader):655 def evaluate(self, stream, namespace, loader): 649 656 namespace = LocalNamespace(namespace) 650 self.block.evaluate( namespace, stream, loader)657 self.block.evaluate(stream, namespace, loader) 651 658 652 659 … … 658 665 except NoMatch: break 659 666 660 def evaluate(self, namespace, stream, loader):667 def evaluate(self, stream, namespace, loader): 661 668 for child in self.children: 662 child.evaluate( namespace, stream, loader)669 child.evaluate(stream, namespace, loader) -
trunk/airspeed_test.py
r32 r33 179 179 self.assertEquals("1296", template.merge(locals())) 180 180 181 def test_can_call_function_with_ one_parameter(self):181 def test_can_call_function_with_two_parameters(self): 182 182 def multiply(number1, number2): 183 183 return number1 * number2 … … 398 398 self.assertEquals(" $user.name ", template.merge({'user':MyObject()})) 399 399 400 def test_variables_expanded_in_double_quoted_strings(self): 401 template = airspeed.Template('#set($hello="hello, $name is my name")$hello') 402 self.assertEquals("hello, Steve is my name", template.merge({'name':'Steve'})) 403 404 def test_escaped_variable_references_not_expanded_in_double_quoted_strings(self): 405 template = airspeed.Template('#set($hello="hello, \\$name is my name")$hello') 406 self.assertEquals("hello, $name is my name", template.merge({'name':'Steve'})) 407 408 def test_macros_expanded_in_double_quoted_strings(self): 409 template = airspeed.Template('#macro(hi $person)$person says hello#end#set($hello="#hi($name)")$hello') 410 self.assertEquals("Steve says hello", template.merge({'name':'Steve'})) 411 400 412 # 401 413 # TODO: 402 414 # 415 # Report locations for template errors in strings 403 416 # Math expressions 404 417 # Gobbling up whitespace (tricky!) … … 406 419 # Bind #macro calls at compile time? 407 420 # #stop ? 408 # Interpolated strings -- what about \$ etc?409 # Directives inside string literals410 421 # map literals 411 422 # Sub-object assignment: #set( $customer.Behavior = $primate )
