def test_bad_rule_reference(self): """ Refer to a rule that doesn't exist. """ p = ParserBase() p.new_rule('bad', 'notarule') with self.assertRaises(KeyError, msg='non-existent rule did not raise error'): p.parse('any text')
def test_recursion(self): """ Test the use of recursion to achieve repetition. """ p = ParserBase() p.new_rule('repeat', '"hello" repeat | "hello"') # standard check on a single 'hello' self.check(p, *STANDARD) # check for repeated 'hello's check = 'hello' * 10 self.check(p, check, check)
def test_flatten(self): """ Test the flatten method. """ p = ParserBase() string = 'aaaaaa' p.new_rule('as', '"a" as | "a"', main=True) token = p.parse(string) token.flatten() self.assertEqual(string, ''.join(c.value() for c in token.children), msg='flatten failed for simple example')
def test_empty_string(self): """ Check that the parser understands empty strings. """ p = ParserBase() p.new_rule('empty', '""') p.new_rule('hello', '"hello"') p.new_rule('combined', 'hello empty', main=True) # do the standard check self.check(p, *STANDARD) # check the empty case by itself p.main = 'empty' self.check(p, "", "")
def test_rule_from_function(self): """ Check the operation of custom rules. """ def parse_hello(string): """ A rule that grabs the literal 'hello'. """ w = STANDARD[0] if string.startswith(w): return w, string[len(w):] return None, string p = ParserBase() p.from_function(parse_hello, main=True) self.check(p, *STANDARD)
def test_ignore(self): """ Test the 'ignore' whitespace handling technique. """ # set up parser with new grammar and the digit_run function p = ParserBase(ws_handler=ignore) p.from_function(digit_run, ws_handling=True) p.grammar(GRAMMAR, main='programme') # trial various combinations of whitespace # single spacing p.parse('if 34 > 33 then 44 + 3') # no spacing p.parse('if34>33then44+3') # random spacing, including a tab p.parse('if34 > 33 then 44+\t3')
def test_require_with_ignore(self): """ Test the 'require' whitespace handling technique, using the 'ignore' option. """ # set up parser with new grammar and the digit_run function p = ParserBase(ws_handler=require(' ', ignore=True)) p.from_function(digit_run, ws_handling=True) p.grammar(GRAMMAR, main='programme') # trial various combinations of whitespace # single spacing - should work p.parse('if 34 > 33 then 44 + 3') # double spacing - should now work p.parse('if 34 > 33 then 44 + 3') # no spacing - should fail with self.assertRaises(DelimiterError): p.parse('if34>33then44+3')
def test_ignore_specific(self): """ Test the 'ignore_specific' whitespace handling technique. """ # set up parser with new grammar and the digit_run function p = ParserBase(ws_handler=ignore_specific(' ')) p.from_function(digit_run, ws_handling=True) p.grammar(GRAMMAR, main='programme') # trial various combinations of whitespace # single spacing p.parse('if 34 > 33 then 44 + 3') # no spacing p.parse('if34>33then44+3') # random spacing, including a tab - should fail with self.assertRaises(NotFoundError, msg='should ignore tab'): p.parse('if34 > 33 then 44+\t3')
def test_grammar(self): """ Use the grammar method, also providing an opportunity to check a more complex multi-rule example. """ p = ParserBase() p.grammar(GRAMMAR) # these should all succeed p.parse('https://www.abcabc.com') p.parse('https://www.bcaabc.co.uk') p.parse('http://www.a.fr') # these should all fail with self.assertRaises(NotFoundError): p.parse('www.abc.com') with self.assertRaises(NotFoundError): p.parse('https:/www.abcabc.com') with self.assertRaises(NotFoundError): p.parse('https://www.abcd.co.uk') with self.assertRaises(NotFoundError): p.parse('http://www..com') with self.assertRaises(IncompleteParseError): p.parse('https://www.a.fra')
def test_rule_reference(self): """ Test a custom rule that references another rule. """ p = ParserBase() p.new_rule('literal', '"hello"') p.new_rule('reference', 'literal') self.check(p, *STANDARD)
def test_branching_complex(self): """ A more complicated branching example. """ p = ParserBase() p.new_rule('end', '"llo"') p.new_rule('branch', '"hellp" | "he" end', main=True) self.check(p, *STANDARD)
def test_pipe_escaping(self): """ Try using an escaped pipe in a rule. """ p = ParserBase() p.new_rule(r'escaped', '"pipe: " "\|"') p.parse('pipe: |')
def test_long_sequence(self): """ Test a longer sequence, using literals and rules. """ p = ParserBase() p.new_rule('ls', '"l"') p.new_rule('long', '"h" "e" ls ls "o"', main=True) self.check(p, *STANDARD)
def test_branching(self): """ Test a simple 'or' example. """ p = ParserBase() p.new_rule('branch', '"null" | "hello"') self.check(p, *STANDARD)
def test_rule_sequence(self): """ Test a rule that contains a sequence of tokens. """ p = ParserBase() p.new_rule('hello', '"hel" "lo"') self.check(p, *STANDARD)
def test_quote_escaping(self): """ Try using an escaped double-quote in a rule. """ p = ParserBase() p.new_rule('quote', r'"\""') p.parse('"')
def test_backslash_escaping(self): """ Try escaping a backslash in a rule. """ p = ParserBase() p.new_rule('escape', '"\\\\"') p.parse('\\')
def test_literal(self): """ Test a custom rule that involves a literal. """ p = ParserBase() p.new_rule('hello', '"hello"', main=True) self.check(p, *STANDARD)
def test_rule_addition(self): """ Check that rules can be added to the rule dictionary. """ p = ParserBase() p.new_rule('hello', '"hello"') # check that the p has the new rule self.assertEqual(list(p.rules.keys()), ['hello'])