def terminal(self, text): """terminal = '"' . (printable - '"') + . '"' | "'" . (printable - "'") + . "'" ;""" self._attempting(text) return alternation([ concatenation([ '"', one_or_more( exclusion( self.printable, '"' ), ignore_whitespace=False ), '"', ], ignore_whitespace=False), concatenation([ "'", one_or_more( exclusion( self.printable, "'" ), ignore_whitespace=False ), "'", ], ignore_whitespace=False), ])(text).compressed(TokenType.terminal)
def number(self, text): """number = ["-"] . ("0" | digit - "0" . {digit}) . ["." . digit +] ;""" self._attempting(text) return concatenation([ option( "-" ), alternation([ "0", concatenation([ exclusion( self.digit, "0" ), zero_or_more( self.digit, ignore_whitespace=False ), ], ignore_whitespace=False), ]), option( concatenation([ ".", one_or_more( self.digit, ignore_whitespace=False ), ], ignore_whitespace=False) ), ], ignore_whitespace=False)(text).compressed(TokenType.number)
def test_concatenation(self): c0 = P.concatenation(["a", "b", "c"], ignore_whitespace=True) t0 = "abc" n0 = c0(t0) self.assertEqual(n0.node_type, P.ParseNodeType.concatenation) self.assertFalse(n0.is_value) self.assertEqual(n0.position, -len(t0)) self.assertEqual(len(n0.children), 3) self.assertEqual(len(n0), 3) with self.assertRaises(P.DeadEnd): c0("bcd") t1 = """ a\tb\nc""" n1 = c0(t1) # The position excludes skipped whitespace. self.assertEqual(n1.position, -len(t1) + 1) self.assertEqual(len(n1), 3) self.assertEqual(n1.consumed, 6) c1 = P.concatenation(["a", "b", "c"], ignore_whitespace=False) n2 = c1(t0) self.assertEqual(len(n2.children), 3) # c1 raises on t1 because t1 has whitespace but c1 doesn't ignore whitespace. with self.assertRaises(P.DeadEnd): c1(t1)
def function_call(self, text): """function_call = function_name . "(" , [function_args] , ")" ;""" self._attempting(text) return concatenation([ self.function_name, concatenation([ "(", option( self.function_args ), ")", ], ignore_whitespace=True), ], ignore_whitespace=False)(text).retyped(TokenType.function_call)
def function_args(self, text): """function_args = expression , {"," , function_args} ;""" self._attempting(text) return concatenation([ self.expression, zero_or_more( concatenation([ ",", self.function_args, ], ignore_whitespace=True), ignore_whitespace=True ), ], ignore_whitespace=True)(text)
def switch_subject(self, text): """switch_subject = "switch" , expression ;""" self._attempting(text) return concatenation([ "switch", self.expression, ], ignore_whitespace=True)(text).retyped(TokenType.switch_subject)
def grouping_group(self, text): """grouping_group = "(" , expression , ")" ;""" self._attempting(text) return concatenation([ "(", self.expression, ")", ], ignore_whitespace=True)(text).retyped(TokenType.grouping_group)
def subexpression(self, text): """subexpression = "(" , expression , ")" ;""" self._attempting(text) return concatenation([ "(", self.expression, ")", ], ignore_whitespace=True)(text).retyped(TokenType.subexpression)
def special_handling(self, text): """special_handling = "?" , identifier , "?" ;""" self._attempting(text) return concatenation([ "?", self.identifier, "?", ], ignore_whitespace=True)(text).retyped(TokenType.special_handling)
def switch_default(self, text): """switch_default = "else" , ":" , expression ;""" self._attempting(text) return concatenation([ "else", ":", self.expression, ], ignore_whitespace=True)(text).retyped(TokenType.switch_default)
def option_group(self, text): """option_group = "[" , expression , "]" ;""" self._attempting(text) return concatenation([ "[", self.expression, "]", ], ignore_whitespace=True)(text).retyped(TokenType.option_group)
def repetition_group(self, text): """repetition_group = "{" , expression , "}" ;""" self._attempting(text) return concatenation([ "{", self.expression, "}", ], ignore_whitespace=True)(text).retyped(TokenType.repetition_group)
def branch_else(self, text): """branch_else = "else" , ":" , expression ;""" self._attempting(text) return concatenation([ "else", ":", self.expression, ], ignore_whitespace=True)(text).retyped(TokenType.branch_else)
def switch_case(self, text): """switch_case = "when" , expression , ":" , expression ;""" self._attempting(text) return concatenation([ "when", self.expression, ":", self.expression, ], ignore_whitespace=True)(text).retyped(TokenType.switch_case)
def rule(self, text): """rule = identifier , "=" , expression , ";" ;""" self._attempting(text) return concatenation([ self.identifier, "=", self.expression, ";", ], ignore_whitespace=True)(text).retyped(TokenType.rule)
def statement(self, text): """statement = comment | assignment , ";" ;""" self._attempting(text) return alternation([ self.comment, concatenation([ self.assignment, ";", ], ignore_whitespace=True), ])(text)
def assignment(self, text): """assignment = variable , (":=" | "<-") , expression ;""" self._attempting(text) return concatenation([ self.variable, alternation([ ":=", "<-", ]), self.expression, ], ignore_whitespace=True)(text).retyped(TokenType.assignment)
def expression(self, text): """expression = {operator} , expression_terminal , {operator + , expression} ;""" self._attempting(text) return concatenation([ zero_or_more( self.operator, ignore_whitespace=True ), self.expression_terminal, zero_or_more( concatenation([ one_or_more( self.operator, ignore_whitespace=True ), self.expression, ], ignore_whitespace=True), ignore_whitespace=True ), ], ignore_whitespace=True)(text).retyped(TokenType.expression)
def return_statement(self, text): """return_statement = ["return"] , expression , [";"] ;""" self._attempting(text) return concatenation([ option( "return" ), self.expression, option( ";" ), ], ignore_whitespace=True)(text).retyped(TokenType.return_statement)
def branch(self, text): """branch = branch_if , {branch_elif} , branch_else ; b""" self._attempting(text) return concatenation([ self.branch_if, zero_or_more( self.branch_elif, ignore_whitespace=True ), self.branch_else, ], ignore_whitespace=True)(text).retyped(TokenType.branch)
def string(self, text): """string = '"' . {double_string_char} . '"' | "'" . {single_string_char} . "'" ;""" self._attempting(text) return alternation([ concatenation([ '"', zero_or_more( self.double_string_char, ignore_whitespace=False ), '"', ], ignore_whitespace=False), concatenation([ "'", zero_or_more( self.single_string_char, ignore_whitespace=False ), "'", ], ignore_whitespace=False), ])(text).compressed(TokenType.string)
def eol(self, text): """eol = { whitespace - "\n" } . "\n" ;""" self._attempting(text) return concatenation([ zero_or_more( exclusion( self.whitespace, "\n" ), ignore_whitespace=False ), "\n", ], ignore_whitespace=False)(text)
def double_string_char(self, text): """double_string_char = "\\" . all_characters - '"' | "\" . '"' | all_characters - '"' ;""" self._attempting(text) return alternation([ concatenation([ "\\\\", exclusion( self.all_characters, '"' ), ], ignore_whitespace=False), concatenation([ "\\", '"', ], ignore_whitespace=False), exclusion( self.all_characters, '"' ), ])(text).retyped(TokenType.double_string_char)
def single_string_char(self, text): """single_string_char = "\\" . all_characters - "'" | "\" . "'" | all_characters - "'" ;""" self._attempting(text) return alternation([ concatenation([ "\\\\", exclusion( self.all_characters, "'" ), ], ignore_whitespace=False), concatenation([ "\\", "'", ], ignore_whitespace=False), exclusion( self.all_characters, "'" ), ])(text).retyped(TokenType.single_string_char)
def number(self, text): """number = digit - "0" . {digit} ;""" self._attempting(text) return concatenation([ exclusion( self.digit, "0" ), zero_or_more( self.digit, ignore_whitespace=False ), ], ignore_whitespace=False)(text).compressed(TokenType.number)
def complex_identifier(self, text): """complex_identifier = "[" . (all_characters - "]") + . "]" ;""" self._attempting(text) return concatenation([ "[", one_or_more( exclusion( self.all_characters, "]" ), ignore_whitespace=False ), "]", ], ignore_whitespace=False)(text).retyped(TokenType.complex_identifier)
def comment(self, text): """comment = "/*" . {all_characters - "*" | "*" . all_characters - "/"} . "*/" ;""" self._attempting(text) return concatenation([ "/*", zero_or_more( alternation([ exclusion( self.all_characters, "*" ), concatenation([ "*", exclusion( self.all_characters, "/" ), ], ignore_whitespace=False), ]), ignore_whitespace=False ), "*/", ], ignore_whitespace=False)(text).compressed(TokenType.comment)
def directive(self, text): """directive = "#" . { all_characters - "\n" } . eol ;""" self._attempting(text) return concatenation([ "#", zero_or_more( exclusion( self.all_characters, "\n" ), ignore_whitespace=False ), self.eol, ], ignore_whitespace=False)(text).compressed(TokenType.directive)
def comment(self, text): """comment = "(*" . {printable - "*" | "*" . printable - ")"} . "*)" ;""" self._attempting(text) return concatenation([ "(*", zero_or_more( alternation([ exclusion( self.printable, "*" ), concatenation([ "*", exclusion( self.printable, ")" ), ], ignore_whitespace=False), ]), ignore_whitespace=False ), "*)", ], ignore_whitespace=False)(text).compressed(TokenType.comment)
def switch(self, text): """switch = switch_subject , switch_case + , [switch_default] ; s""" self._attempting(text) return concatenation([ self.switch_subject, one_or_more( self.switch_case, ignore_whitespace=True ), option( self.switch_default ), ], ignore_whitespace=True)(text).retyped(TokenType.switch)