def test_error(self): #basicConfig(level=INFO) class Term(Node): pass class Factor(Node): pass class Expression(Node): pass expression = Delayed() number = Digit()[1:, ...] > 'number' term = Or( AnyBut(Space() | Digit() | '(')[1:, ...] ^ 'unexpected text: {results[0]}', number > Term, number**make_error("no ( before '{stream_out}'") / ')' >> node_throw, '(' / expression / ')' > Term, ('(' / expression / Eos())**make_error("no ) for '{stream_in}'") >> node_throw) muldiv = Any('*/') > 'operator' factor = (term / (muldiv / term)[0:, r'\s*']) > Factor addsub = Any('+-') > 'operator' expression += (factor / (addsub / factor)[0:, r'\s*']) > Expression line = expression / Eos() parser = line.get_parse_string() try: parser('1 + 2 * 3 + 4 - 5)')[0] assert False, 'expected error' except SyntaxError as e: assert e.msg == "no ( before ')'", e.msg try: parser('1 + 2 * (3 + 4 - 5') assert False, 'expected error' except SyntaxError as e: assert e.msg == "no ) for '(3 + 4 - 5'", e.msg try: parser('1 + 2 * foo') assert False, 'expected error' except SyntaxError as e: assert e.msg == "unexpected text: foo", e.msg
def test_error(self): #basicConfig(level=INFO) class Term(Node): pass class Factor(Node): pass class Expression(Node): pass expression = Delayed() number = Digit()[1:,...] > 'number' term = Or( AnyBut(Space() | Digit() | '(')[1:,...] ^ 'unexpected text: {results[0]}', number > Term, number ** make_error("no ( before '{stream_out}'") / ')' >> node_throw, '(' / expression / ')' > Term, ('(' / expression / Eos()) ** make_error("no ) for '{stream_in}'") >> node_throw) muldiv = Any('*/') > 'operator' factor = (term / (muldiv / term)[0:,r'\s*']) > Factor addsub = Any('+-') > 'operator' expression += (factor / (addsub / factor)[0:,r'\s*']) > Expression line = expression / Eos() parser = line.get_parse_string() try: parser('1 + 2 * 3 + 4 - 5)')[0] assert False, 'expected error' except SyntaxError as e: assert e.msg == "no ( before ')'", e.msg try: parser('1 + 2 * (3 + 4 - 5') assert False, 'expected error' except SyntaxError as e: assert e.msg == "no ) for '(3 + 4 - 5'", e.msg try: parser('1 + 2 * foo') assert False, 'expected error' except SyntaxError as e: assert e.msg == "unexpected text: foo", e.msg
array_type = Or(*generate_type_tokens(symboltable.array_types)) array_element_type = Or(*generate_type_tokens(symboltable.array_element_types)) type_ = scalar_type | array_type | array_element_type real = Token(UnsignedReal()) >> Real integer = Token(UnsignedInteger()) >> Integer number = integer | real boolean = keyword("yes") >> Bool | keyword("no") >> Bool | keyword("true") >> Bool | keyword("false") >> Bool width = integer height = integer depth = integer unopened_size_block = (width & Optional(~comma & height & Optional(~comma & depth)) & symbol("]")) ** make_error( "no [ before {out_rest!s}" ) & symbol("]") unclosed_size_block = (symbol("[") & width & Optional(~comma & height & Optional(~comma & depth))) ** make_error( "Array size specification is missing a closing ]" ) size = Or( (~symbol("[") & width & Optional(~comma & height & Optional(~comma & depth)) & ~symbol("]")) ** with_line(Size), unopened_size_block, unclosed_size_block, ) #### Expression Parsing #### # Operator precedence, inside to outside # 1 parentheses () # 2 not, unary minus (!, -)
def missing(label): """Provides feedback when a matcher fails. This function is commonly applied using the :class:`First` operator (short-hand: ``\%``). We'll consider the following example phrase: >>> example_phrase ['John' (W), 'Smith' (W), ',' (D), 16 (I), 'M' (W)] The following matcher illustrates the usage: >>> matcher = Word[1:] & lepl.Optional( ... Delimiter & (Number & Word) % missing('age and sex')) Omitting the last token from our example phrase, we get the following feedback: >>> parse(matcher, example_phrase[:-1])[0] u'age and sex' To improve feedback, we can provide help for each case individually: >>> matcher = Word[1:] % missing('name') & lepl.Optional( ... Delimiter & Number % missing('age') & Word % missing('sex')) The ``\%`` operator binds more strongly and parentheses are unnecessary for the most part. >>> parse(matcher, example_phrase[:-2])[0] u'age' >>> parse(matcher, example_phrase[:-1])[0] u'sex' Note that the full phrase still returns a match: >>> parse(matcher, example_phrase) ['John' (W), 'Smith' (W), 16 (I), 'M' (W)] So far we've dealt with missing input only; the function also guards against incorrect input: >>> phrase = example_phrase[:-2] + example_phrase[:1] >>> parse(matcher, phrase)[0] u'age' Finally, the function operates within a more complex environment of multiple paths. >>> matcher = Word[1:] % missing('name') & \\ ... lepl.Optional( ... Delimiter & Number % missing('age') & Word % missing('sex')) & \\ ... lepl.Optional( ... Delimiter & Number % missing('age')) >>> parse(matcher, example_phrase[:-2])[0] u'age' Omitting only the last token, this particular matcher chooses the path through the second optional clause and succeeds: >>> parse(matcher, example_phrase[:-1]) ['John' (W), 'Smith' (W), 16 (I)] """ return lepl.Any()[:] ** lepl.make_error(label)
type_ = scalar_type | array_type | array_element_type real = Token(UnsignedReal()) >> Real integer = Token(UnsignedInteger()) >> Integer number = integer | real boolean = keyword('yes') >> Bool | keyword('no') >> Bool | keyword( 'true') >> Bool | keyword('false') >> Bool width = integer height = integer depth = integer unopened_size_block = ( width & Optional(~comma & height & Optional(~comma & depth)) & symbol(']'))**make_error('no [ before {out_rest!s}') & symbol(']') unclosed_size_block = ( symbol('[') & width & Optional(~comma & height & Optional(~comma & depth)) )**make_error('Array size specification is missing a closing ]') size = Or((~symbol('[') & width & Optional(~comma & height & Optional(~comma & depth)) & ~symbol(']'))**with_line(Size), unopened_size_block, unclosed_size_block) #### Expression Parsing #### # Operator precedence, inside to outside # 1 parentheses () # 2 not, unary minus (!, -) # 3 multiplication, division (*, /, %)