| #!/usr/bin/python | 
 | # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file | 
 | # for details. All rights reserved. Use of this source code is governed by a | 
 | # BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | import logging.config | 
 | import pprint | 
 | import re | 
 | import sys | 
 | import unittest | 
 | from pegparser import * | 
 |  | 
 |  | 
 | class PegParserTestCase(unittest.TestCase): | 
 |  | 
 |   def _run_test(self, grammar, text, expected, | 
 |           strings_are_tokens=False, whitespace_rule=None): | 
 |     """Utility for running a parser test and comparing results. | 
 |  | 
 |     Program exits (sys.exit) if expected does not match actual. | 
 |  | 
 |     Args: | 
 |       grammar -- the root rule to be used by the parser. | 
 |       text -- the text to parse. | 
 |       expected -- the expected abstract syntax tree. None means | 
 |         failure is expected. | 
 |       strings_are_tokens -- whether strings are treated as tokens. | 
 |       whitespace_rule -- the rule used for matching whitespace. | 
 |         Default is None, which means that no whitespace is tolerated. | 
 |     """ | 
 |     parser = PegParser(grammar, whitespace_rule, | 
 |                strings_are_tokens=strings_are_tokens) | 
 |     actual = None | 
 |     error = None | 
 |     try: | 
 |       actual = parser.parse(text) | 
 |     except SyntaxError, e: | 
 |       error = e | 
 |       pass | 
 |  | 
 |     if actual != expected: | 
 |       msg = ''' | 
 | CONTENT: | 
 | %s | 
 | EXPECTED: | 
 | %s | 
 | ACTUAL: | 
 | %s | 
 | ERROR: %s''' % (text, pprint.pformat(expected), pprint.pformat(actual), error) | 
 |       self.fail(msg) | 
 |  | 
 |   def test_sequence(self): | 
 |     sequence = SEQUENCE('A', 'BB', 'C') | 
 |     self._run_test(grammar=sequence, text='ABBC', expected=['A', 'BB', 'C']) | 
 |     self._run_test(grammar=sequence, text='BBAC', expected=None) | 
 |     # Syntax Sugar | 
 |     sequence = ['A', 'BB', 'C'] | 
 |     self._run_test(grammar=sequence, text='ABBC', expected=['A', 'BB', 'C']) | 
 |     self._run_test(grammar=sequence, text='BBAC', expected=None) | 
 |  | 
 |   def test_regex(self): | 
 |     regex = re.compile(r'[A-Za-z]*') | 
 |     self._run_test(grammar=regex, text='AaBb', expected='AaBb') | 
 |     self._run_test(grammar=regex, text='0AaBb', expected=None) | 
 |     self._run_test(grammar=regex, text='Aa0Bb', expected=None) | 
 |  | 
 |   def test_function(self): | 
 |     def Func(): | 
 |       return 'ABC' | 
 |     self._run_test(grammar=Func, text='ABC', expected=('Func', 'ABC')) | 
 |     self._run_test(grammar=Func, text='XYZ', expected=None) | 
 |  | 
 |   def test_function_label(self): | 
 |     def func(): | 
 |       return 'ABC' | 
 |  | 
 |     def _func(): | 
 |       return 'ABC' | 
 |  | 
 |     self._run_test(grammar=func, text='ABC', expected=('func', 'ABC')) | 
 |     self._run_test(grammar=_func, text='ABC', expected='ABC') | 
 |  | 
 |   def test_label(self): | 
 |     sequence = [TOKEN('def'), LABEL('funcName', re.compile(r'[a-z0-9]*')), | 
 |           TOKEN('():')] | 
 |     self._run_test(grammar=sequence, text='def f1():', | 
 |       whitespace_rule=' ', expected=[('funcName', 'f1')]) | 
 |     self._run_test(grammar=sequence, text='def f2():', | 
 |       whitespace_rule=' ', expected=[('funcName', 'f2')]) | 
 |  | 
 |   def test_or(self): | 
 |     grammer = OR('A', 'B') | 
 |     self._run_test(grammar=grammer, text='A', expected='A') | 
 |     self._run_test(grammar=grammer, text='B', expected='B') | 
 |     self._run_test(grammar=grammer, text='C', expected=None) | 
 |  | 
 |   def test_maybe(self): | 
 |     seq = ['A', MAYBE('B'), 'C'] | 
 |     self._run_test(grammar=seq, text='ABC', expected=['A', 'B', 'C']) | 
 |     self._run_test(grammar=seq, text='ADC', expected=None) | 
 |     self._run_test(grammar=seq, text='AC', expected=['A', 'C']) | 
 |     self._run_test(grammar=seq, text='AB', expected=None) | 
 |  | 
 |   def test_many(self): | 
 |     seq = ['A', MANY('B'), 'C'] | 
 |     self._run_test(grammar=seq, text='ABC', expected=['A', 'B', 'C']) | 
 |     self._run_test(grammar=seq, text='ABBBBC', | 
 |       expected=['A', 'B', 'B', 'B', 'B', 'C']) | 
 |     self._run_test(grammar=seq, text='AC', expected=None) | 
 |  | 
 |   def test_many_with_separator(self): | 
 |     letter = OR('A', 'B', 'C') | 
 |  | 
 |     def _gram(): | 
 |       return [letter, MAYBE([TOKEN(','), _gram])] | 
 |  | 
 |     self._run_test(grammar=_gram, text='A,B,C,B', | 
 |       expected=['A', 'B', 'C', 'B']) | 
 |     self._run_test(grammar=_gram, text='A B C', expected=None) | 
 |     shortergrammar = MANY(letter, TOKEN(',')) | 
 |     self._run_test(grammar=shortergrammar, text='A,B,C,B', | 
 |       expected=['A', 'B', 'C', 'B']) | 
 |     self._run_test(grammar=shortergrammar, text='A B C', expected=None) | 
 |  | 
 |   def test_raise(self): | 
 |     self._run_test(grammar=['A', 'B'], text='AB', | 
 |       expected=['A', 'B']) | 
 |     try: | 
 |       self._run_test(grammar=['A', 'B', RAISE('test')], text='AB', | 
 |         expected=None) | 
 |       print 'Expected RuntimeError' | 
 |       sys.exit(-1) | 
 |     except RuntimeError, e: | 
 |       return | 
 |  | 
 |   def test_whitespace(self): | 
 |     gram = MANY('A') | 
 |     self._run_test(grammar=gram, text='A A  A', expected=None) | 
 |     self._run_test(grammar=gram, whitespace_rule=' ', text='A A  A', | 
 |       expected=['A', 'A', 'A']) | 
 |  | 
 |   def test_math_expression_syntax(self): | 
 |     operator = LABEL('op', OR('+', '-', '/', '*')) | 
 |     literal = LABEL('num', re.compile(r'[0-9]+')) | 
 |  | 
 |     def _exp(): | 
 |       return MANY(OR(literal, [TOKEN('('), _exp, TOKEN(')')]), | 
 |             separator=operator) | 
 |  | 
 |     self._run_test(grammar=_exp, | 
 |       text='(1-2)+3*((4*5)*6)+(7+8/9)-10', | 
 |       expected=[[('num', '1'), ('op', '-'), ('num', '2')], | 
 |         ('op', '+'), | 
 |         ('num', '3'), | 
 |         ('op', '*'), | 
 |         [[('num', '4'), ('op', '*'), ('num', '5')], | 
 |           ('op', '*'), ('num', '6')], | 
 |         ('op', '+'), | 
 |         [('num', '7'), ('op', '+'), ('num', '8'), | 
 |          ('op', '/'), ('num', '9')], | 
 |         ('op', '-'), | 
 |         ('num', '10')]) | 
 |  | 
 |   def test_mini_language(self): | 
 |     def name(): | 
 |       return re.compile(r'[a-z]+') | 
 |  | 
 |     def var_decl(): | 
 |       return ['var', name, ';'] | 
 |  | 
 |     def func_invoke(): | 
 |       return [name, '(', ')', ';'] | 
 |  | 
 |     def func_body(): | 
 |       return MANY(OR(var_decl, func_invoke)) | 
 |  | 
 |     def func_decl(): | 
 |       return ['function', name, '(', ')', '{', func_body, '}'] | 
 |  | 
 |     def args(): | 
 |       return MANY(name, ',') | 
 |  | 
 |     def program(): | 
 |       return MANY(OR(var_decl, func_decl)) | 
 |  | 
 |     self._run_test(grammar=program, | 
 |       whitespace_rule=OR('\n', ' '), | 
 |       strings_are_tokens=True, | 
 |       text='var x;\nfunction f(){\n  var y;\n  g();\n}\n', | 
 |       expected=('program',[ | 
 |              ('var_decl', [('name', 'x')]), | 
 |              ('func_decl', [('name', 'f'), ('func_body', [ | 
 |               ('var_decl', [('name', 'y')]), | 
 |               ('func_invoke', [('name', 'g')])])])])) | 
 |  | 
 |  | 
 | if __name__ == "__main__": | 
 |   logging.config.fileConfig("logging.conf") | 
 |   if __name__ == '__main__': | 
 |     unittest.main() |