| #!/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() |