def make_jinja_parser(config, content): jinja_structured_elements_names = ( DEFAULT_JINJA_STRUCTURED_ELEMENTS_NAMES + config.get('jinja_custom_elements_names', [])) jinja_structured_element = P.alt(*[ make_jinja_element_parser( [P.string(name) for name in names], content=content, ) for names in jinja_structured_elements_names ]) # These tag names can't begin a Jinja element jinja_intermediate_tag_names = [ n for _, *sublist in jinja_structured_elements_names for n in sublist ] jinja_intermediate_tag_name = P.alt( *(P.string(n) for n in jinja_intermediate_tag_names)) jinja_element_single = make_jinja_element_parser( [ jinja_intermediate_tag_name.should_fail( 'not an intermediate Jinja tag name').then(jinja_name) ], content=content, ) jinja_element = jinja_structured_element | jinja_element_single return jinja_variable | jinja_comment | jinja_element
def test_alt(self): self.assertRaises(ParseError, alt().parse, '') self.assertEqual(alt(letter, digit).parse('a'), 'a') self.assertEqual(alt(letter, digit).parse('1'), '1') self.assertRaises(ParseError, alt(letter, digit).parse, '.')
def make_jinja_parser(config, content): # Allow to override elements with the configuration jinja_structured_elements_names = dict( (names[0], names) for names in ( DEFAULT_JINJA_STRUCTURED_ELEMENTS_NAMES + config.get('jinja_custom_elements_names', []) ) ).values() jinja_structured_element = P.alt(*[ make_jinja_element_parser( [P.string(name) for name in names], content=content, ) for names in jinja_structured_elements_names ]) # These tag names can't begin a Jinja element jinja_intermediate_tag_names = set( n for _, *sublist in jinja_structured_elements_names for n in sublist ) jinja_intermediate_tag_name = P.alt(*( P.string(n) for n in jinja_intermediate_tag_names )) jinja_element_single = make_jinja_element_parser( [ P.alt( # HACK: If we allow `{% if %}`s without `{% endif %}`s here, # `make_jinja_optional_container_parser` doesn’t work. It # is probably better to reject any structured tag name here. P.string('if'), jinja_intermediate_tag_name ) .should_fail('not an intermediate Jinja tag name') .then(jinja_name) ], content=content, ) jinja_element = jinja_structured_element | jinja_element_single return jinja_variable | jinja_comment | jinja_element
def op(): e1 = yield exp yield whitespace op = yield parsy.alt(*map(parsy.string, ['+', '-', '==', '<'])) yield whitespace e2 = yield exp return {'Kind': kind(op), 'Left': e1, 'Right': e2}
def varchar_or_char(): yield p.alt( spaceless_string("varchar"), spaceless_string("char") ).then( lparen.then(p.digit.at_least(1).concat()).skip(rparen).optional() ) return String()
def dwarfdump(): import parsy as p def dbg(parser): return parser.mark().map(lambda x: print(repr(x))) newline = p.regex(r'[\r\n]').desc('newline') newlines = newline.times(min=1).desc('newlines') blank_line = newline.times(min=2).desc('blank line') rest_of_line = p.regex(r'.*$', flags=re.MULTILINE) quoted_string = p.string('"') >> p.regex(r'[^"]*') << p.string('"') hex_number = p.regex(r'0x[0-9a-fA-F]+').map(lambda x: int(x, 0)).desc( 'hex number') decimal_number = p.regex(r'-?[0-9]+').map(lambda x: int(x, 0)).desc( 'decimal number') boolean = (p.string('true') | p.string('false')).map(lambda b: b == 'true').desc('boolean') dwarf_code = p.string('DW_') >> p.regex(r'\w+') null = p.string('NULL') attribute_contents = p.alt( quoted_string, p.seq(hex_number, p.whitespace >> quoted_string).combine(_DwarfRawAttributeRef), hex_number, decimal_number, boolean, p.seq( p.string('DW_') >> p.regex(r'OP_\w+'), (p.whitespace >> (hex_number | decimal_number)).optional()).combine(DwarfAttributeOp), dwarf_code.map(DwarfAttributeVal), ) attribute = p.seq( p.whitespace >> dwarf_code, p.whitespace >> p.string('(') >> attribute_contents << p.string(')'), ) die = p.seq( address=hex_number << p.string(': '), indent=p.regex('( )*').map(lambda i: len(i) // 2), tag=null | dwarf_code, attributes=(p.string('\n') >> attribute).many().map(dict), ).combine_dict(_DwarfRawDie) compilation_unit = p.seq( addr_size=(p.regex(r'.*addr_size = ') >> hex_number << rest_of_line).desc('compilation unit header'), children=blank_line >> die.sep_by(blank_line, min=1), ).combine_dict(DwarfCompilationUnit) return (yield (p.regex(r'.*:\s*file format.*\n\n') >> p.regex(r'.*\.debug_info contents:.*\n') >> compilation_unit.sep_by(blank_line) << newline.many()).many())
def internal_to_parser(idx): rule_def = rule_defs[idx] if isinstance(rule_def, str): return string(rule_def) else: return alt(*[ seq(*list(map(internal_to_parser, to_seq))).map(lambda l: ''.join(l)) for to_seq in rule_def ])
def single_expression(): unary = yield unary_operator.optional() # ordering below: variable after parens_expression to parse function calls correctly! elt = yield P.alt(int_literal, bool_literal, string_literal, parens_expression, variable) if unary is not None: unary = var_from_op(unary) return ast.Application(start=unary.start, end=elt.end, function=unary, args=[elt]) else: return elt
v = yield ident yield dot body = yield expr yield rparen return EQuantified(b, v, body) @generate def binop(): op = yield ident if op not in e.bin_ops: yield fail("{} is not a binary operator".format(op)) else: return op @generate def expr_cont_expr1(): e1 = yield expr p1 = seq(binop, expr << rparen).combine(lambda op, e2: EApply(EApply(EIdent(op), e1), e2)) p2 = (expr << rparen).map(lambda e2: EApply(e1, e2)) return (yield (p1 | p2)) expr = alt(ident.map(EIdent), (lparen >> (expr_cont_quantified | expr_cont_expr1))) thm = seq(expr.sep_by(lexme(string(","))), lexme(string("|-")) >> expr).combine(Thm)
def spaceless_string(*strings: str): return spaceless( p.alt(*(p.string(string, transform=str.lower) for string in strings)) )
from basic import Sym, WS, Punc from parsy import alt, decimal_digit, regex, seq, string def make_number(sign, int_part, frac_part): result = float(int_part + '.' + frac_part) if frac_part else int(int_part) return result * -1 if sign else result number_literal = seq(string('-').optional(), decimal_digit.at_least(1).concat(), string('.').then(decimal_digit.at_least(1).concat()).optional()).combine(make_number) string_literal = string('"') >> regex(r'[^"]*') << string('"') operator = regex(r'[]+*^/.\\@d_:!-]').map(Sym) func = regex(r'sin|cos|tan|asin|acos|atan|deg|rad|pi|e|ln|log').map(Sym) variable = regex(r'[A-Z]').map(Sym) punc = regex(r'[\[]').map(Punc) whitespace = string(' ').at_least(1).map(WS) stacky_parser = alt(number_literal, operator, punc, func, variable, whitespace).many()
def addpos(p): @P.generate def addpos_impl(): beg = yield pos obj = yield p end = yield pos assert isinstance(obj, ast.Node) return attr.evolve(obj, start=beg, end=end) return addpos_impl sc = P.alt( P.regex(r"\s+", P.re.MULTILINE), P.regex(r"/\*.*?\*/", P.re.MULTILINE | P.re.DOTALL), P.regex(r"//.*\n"), P.regex(r"#.*\n"), ).desc("whitespace").many() def lexeme(p): return p << sc def symbol(str): return lexeme(P.string(str)) def rword(str): @lexeme @P.generate
def extension(parsers: ParserDict) -> None: # This parses the top level of a file. # top level = # ENCODING, (word | statement | NEWLINE)*, [ NEWLINE ], # ENDMARKER ; @parsy.generate def top_level_parser() -> Generator[parsy.Parser, Any, TopLevelNode]: encoding = yield parsers.token('ENCODING') newline = parsers.token('NEWLINE') statement = parsers['statement'] word = parsers['word'] children = yield (word | statement | newline).many() children = [ child for child in children if not isinstance(child, concat.lex.Token) ] yield parsers.token('ENDMARKER') return TopLevelNode(encoding, children) parsers['top-level'] = desc_cumulatively(top_level_parser, 'top level') # This parses one of many types of statement. # The specific statement node is returned. # statement = import statement ; parsers['statement'] = parsers.ref_parser('import-statement') ImportStatementParserGenerator = Generator[ parsy.Parser, Any, ImportStatementNode ] # This parses one of many types of word. # The specific word node is returned. # word = # push word | literal word | name word | attribute word | quote word ; # literal word = number word | string word ; parsers['word'] = parsy.alt( parsers.ref_parser('push-word'), parsers.ref_parser('quote-word'), parsers.ref_parser('literal-word'), parsers.ref_parser('name-word'), parsers.ref_parser('attribute-word'), ) parsers['literal-word'] = parsers.ref_parser( 'number-word' ) | parsers.ref_parser('string-word') parsers['name-word'] = parsers.token('NAME').map(NameWordNode) parsers['number-word'] = parsers.token('NUMBER').map(NumberWordNode) parsers['string-word'] = parsers.token('STRING').map(StringWordNode) # This parses a quotation. # quote word = LPAR, word*, RPAR ; @parsy.generate('quote word') def quote_word_parser() -> Generator[parsy.Parser, Any, QuoteWordNode]: lpar = yield parsers.token('LPAR') if 'type-sequence' in parsers: input_stack_type_parser = parsers[ 'type-sequence' ] << parsers.token('COLON') input_stack_type = yield input_stack_type_parser.optional() else: input_stack_type = None children = yield parsers['word'].many() yield parsers.token('RPAR') return QuoteWordNode(children, lpar.start, input_stack_type) parsers['quote-word'] = quote_word_parser # This parses a push word into a node. # push word = DOLLARSIGN, word ; word = parsers.ref_parser('word') dollarSign = parsers.token('DOLLARSIGN') parsers['push-word'] = dollarSign >> word.map(PushWordNode) # Parsers an attribute word. # attribute word = DOT, NAME ; dot = parsers.token('DOT') name = parsers.token('NAME') parsers['attribute-word'] = dot >> name.map(AttributeWordNode) parsers['literal-word'] |= parsy.alt( parsers.ref_parser('none-word'), parsers.ref_parser('not-impl-word'), parsers.ref_parser('ellipsis-word'), parsers.ref_parser('bytes-word'), parsers.ref_parser('tuple-word'), parsers.ref_parser('list-word'), parsers.ref_parser('set-word'), parsers.ref_parser('dict-word'), ) # This parses a none word. # none word = NONE ; parsers['none-word'] = parsers.token('NONE').map(NoneWordNode) # This parses a not-impl word. # not-impl word = NOTIMPL ; parsers['not-impl-word'] = parsers.token('NOTIMPL').map(NotImplWordNode) # This parses an ellipsis word. # ellipsis word = ELLIPSIS ; parsers['ellipsis-word'] = parsers.token('ELLIPSIS').map(EllipsisWordNode) parsers['word'] |= parsy.alt( parsers.ref_parser('subscription-word'), parsers.ref_parser('slice-word'), parsers.ref_parser('operator-word'), parsers.ref_parser('yield-word'), parsers.ref_parser('await-word'), parsers.ref_parser('assert-word'), parsers.ref_parser('raise-word'), parsers.ref_parser('try-word'), parsers.ref_parser('with-word'), ) # This parses a subscription word. # subscription word = LSQB, word*, RSQB ; parsers['subscription-word'] = ( parsers.token('LSQB') >> parsers.ref_parser('word').many().map(SubscriptionWordNode) << parsers.token('RSQB') ) # This parses a slice word. # slice word = LSQB, word*, COLON, word*, [ COLON, word* ], RSQB ; @parsy.generate('slice word') def slice_word_parser(): yield parsers.token('LSQB') start = yield parsers.ref_parser('word').many() yield parsers.token('COLON') stop = yield parsers.ref_parser('word').many() none = concat.lex.Token() none.type = 'NONE' step = [NoneWordNode(none)] if (yield parsers.token('COLON').optional()): step = yield parsers['word'].many() yield parsers.token('RSQB') return SliceWordNode([start, stop, step]) parsers['slice-word'] = slice_word_parser parsers['operator-word'] = parsy.fail('operator') from concat.operators import operators for operator_name, token_type, node_type, _ in operators: parser_name = operator_name + '-word' parsers[parser_name] = parsers.token(token_type).map(node_type) parsers['operator-word'] |= parsers.ref_parser(parser_name) # This parses a bytes word. # bytes word = BYTES ; parsers['bytes-word'] = parsers.token('BYTES').map(BytesWordNode) def iterable_word_parser( delimiter: str, cls: Type[IterableWordNode], desc: str ) -> 'parsy.Parser[Token, IterableWordNode]': @parsy.generate def parser() -> Generator: location = (yield parsers.token('L' + delimiter)).start element_words = yield word_list_parser yield parsers.token('R' + delimiter) return cls(element_words, location) return concat.parser_combinators.desc_cumulatively(parser, desc) # This parses a tuple word. # tuple word = LPAR, word list, RPAR ; parsers['tuple-word'] = iterable_word_parser( 'PAR', TupleWordNode, 'tuple word' ) # This parses a list word. # list word = LSQB, word list, RSQB ; parsers['list-word'] = iterable_word_parser( 'SQB', ListWordNode, 'list word' ) # word list = (COMMA | word+, COMMA | word+, (COMMA, word+)+, [ COMMA ]) ; @parsy.generate('word list') def word_list_parser() -> Generator: empty: 'parsy.Parser[Token, List[Words]]' = parsers.token( 'COMMA' ).result([]) singleton = parsy.seq( parsers['word'].at_least(1) << parsers.token('COMMA') ) multiple_element = ( parsers['word'].at_least(1).sep_by(parsers.token('COMMA'), min=2) << parsers.token('COMMA').optional() ) element_words = yield (multiple_element | singleton | empty) return element_words # This parses a set word. # list word = LBRACE, word list, RBRACE ; parsers['set-word'] = iterable_word_parser( 'BRACE', SetWordNode, 'set word' ) # This parses a dict word. # dict word = # LBRACE, # [ key-value pair, (COMMA, key-value pair)* ], # [ COMMA ], # RBRACE ; # key-value pair = word*, COLON, word* ; @parsy.generate('dict word') def dict_word_parser() -> Generator: location = (yield parsers.token('LBRACE')).start elements = ( key_value_pair.sep_by(parsers.token('COMMA'), min=0) << parsers.token('COMMA').optional() ) element_words = yield elements yield parsers.token('RBRACE') return DictWordNode(element_words, location) parsers['dict-word'] = dict_word_parser key_value_pair = parsy.seq( parsers.ref_parser('word').many() << parsers.token('COLON'), parsers.ref_parser('word').many(), ) parsers['yield-word'] = parsers.token('YIELD').map(YieldWordNode) parsers['await-word'] = parsers.token('AWAIT').map(AwaitWordNode) parsers['assert-word'] = parsers.token('ASSERT').map(AssertWordNode) parsers['raise-word'] = parsers.token('RAISE').map(RaiseWordNode) parsers['try-word'] = parsers.token('TRY').map(TryWordNode) parsers['with-word'] = parsers.token('WITH').map(WithWordNode) parsers['statement'] |= parsy.alt( parsers.ref_parser('del-statement'), parsers.ref_parser('async-funcdef-statement'), parsers.ref_parser('classdef-statement'), parsers.ref_parser('funcdef-statement'), ) # Parsers a del statement. # del statement = DEL, target words ; # target words = target word, (COMMA, target word)*, [ COMMA ] ; # target word = name word # | LPAR, target words, RPAR # | LSQB, target words, RQSB # | attribute word # | subscription word # | slice word ; parsers['del-statement'] = parsers.token('DEL') >> parsers.ref_parser( 'target-words' ).map(DelStatementNode) from concat.astutils import flatten parsers['target-words'] = ( parsers.ref_parser('target-word').sep_by(parsers.token('COMMA'), min=1) << parsers.token('COMMA').optional() ).map(flatten) parsers['target-word'] = parsy.alt( parsers.ref_parser('name-word'), parsers.token('LPAR') >> parsers.ref_parser('target-words') << parsers.token('RPAR'), parsers.token('LSQB') >> parsers.ref_parser('target-words') << parsers.token('RSQB'), parsers.ref_parser('attribute-word'), parsers.ref_parser('subscription-word'), parsers.ref_parser('slice-word'), ) # This parses an async function definition. # async funcdef statement = ASYNC, funcdef statement ; @parsy.generate('async funcdef statement') def async_funcdef_statement_parser() -> Generator: location = (yield parsers.token('ASYNC')).start func: FuncdefStatementNode = (yield parsers['funcdef-statement']) name = concat.lex.Token() name.value = func.name return AsyncFuncdefStatementNode( name, func.decorators, func.annotation, func.body, location, func.stack_effect, ) parsers['async-funcdef-statement'] = async_funcdef_statement_parser # This parses a function definition. # funcdef statement = DEF, NAME, [ LPAR, stack effect, RPAR ], decorator*, # [ annotation ], COLON, suite ; # decorator = AT, word ; # annotation = RARROW, word* ; # suite = NEWLINE, INDENT, (word | statement, NEWLINE)+, DEDENT | statement # | word+ ; # The stack effect syntax is defined within the typecheck module. @parsy.generate def funcdef_statement_parser() -> Generator: location = (yield parsers.token('DEF')).start name = yield parsers.token('NAME') if (yield parsers.token('LPAR').optional()): effect_ast = yield parsers['stack-effect-type'] yield parsers.token('RPAR') else: effect_ast = None decorators = yield decorator.many() annotation = yield annotation_parser.optional() yield parsers.token('COLON') body = yield suite return FuncdefStatementNode( name, decorators, annotation, body, location, effect_ast ) parsers['funcdef-statement'] = concat.parser_combinators.desc_cumulatively( funcdef_statement_parser, 'funcdef statement' ) decorator = parsers.token('AT') >> parsers.ref_parser('word') annotation_parser = ( parsers.token('RARROW') >> parsers.ref_parser('word').many() ) @parsy.generate def suite(): words = parsers['word'].at_least(1) statement = parsy.seq(parsers['statement']) block_content = ( parsers['word'] << parsers.token('NEWLINE').optional() | parsers['statement'] << parsers.token('NEWLINE') ).at_least(1) indented_block = ( parsers.token('NEWLINE').optional() >> parsers.token('INDENT') >> block_content << parsers.token('DEDENT') ) return (yield indented_block | statement | words) suite = concat.parser_combinators.desc_cumulatively(suite, 'suite') @parsy.generate('module') def module(): name = parsers.token('NAME').map(operator.attrgetter('value')) return '.'.join((yield name.sep_by(parsers.token('DOT'), min=1))) # These following parsers parse import statements. # import statement = IMPORT, module, [ AS, NAME ] # | FROM, relative module, IMPORT, NAME, [ AS, NAME ] # | FROM, module, IMPORT, STAR; # module = NAME, (DOT, NAME)* ; # relative module = DOT*, module | DOT+ ; @parsy.generate('import statement') def import_statement_parser() -> Generator: location = (yield parsers.token('IMPORT')).start module_name = yield module asname_parser = parsers.token('NAME').map(operator.attrgetter('value')) asname = None if (yield parsers.token('AS').optional()): asname = yield asname_parser return ImportStatementNode(module_name, asname, location) parsers['import-statement'] = import_statement_parser @parsy.generate('relative module') def relative_module(): dot = parsers.token('DOT').map(operator.attrgetter('value')) return (yield (dot.many().concat() + module) | dot.at_least(1)) @parsy.generate('from-import statement') def from_import_statement_parser() -> Generator: location = (yield parsers.token('FROM')).start module = yield relative_module name_parser = parsers.token('NAME').map(operator.attrgetter('value')) imported_name = yield parsers.token('IMPORT') >> name_parser asname = None if (yield parsers.token('AS').optional()): asname = yield name_parser return FromImportStatementNode(module, imported_name, asname, location) parsers['import-statement'] |= from_import_statement_parser @parsy.generate('from-import-star statement') def from_import_star_statement_parser() -> Generator: location = (yield parsers.token('FROM')).start module_name = yield module yield parsers.token('IMPORT') yield parsers.token('STAR') return FromImportStarStatementNode(module_name, location) parsers['import-statement'] |= from_import_star_statement_parser # This parses a class definition statement. # classdef statement = CLASS, NAME, decorator*, [ bases ], keyword arg*, # COLON, suite ; # bases = tuple word ; # keyword arg = NAME, EQUAL, word ; @parsy.generate('classdef statement') def classdef_statement_parser(): location = (yield parsers.token('CLASS')).start name_token = yield parsers.token('NAME') decorators = yield decorator.many() bases_list = yield bases.optional() keyword_args = yield keyword_arg.map(tuple).many() yield parsers.token('COLON') body = yield suite return ClassdefStatementNode( name_token.value, body, location, decorators, bases_list, keyword_args, ) parsers['classdef-statement'] = classdef_statement_parser bases = parsers.ref_parser('tuple-word').map( operator.attrgetter('tuple_children') ) keyword_arg = parsy.seq( parsers.token('NAME').map(operator.attrgetter('value')) << parsers.token('EQUAL'), parsers.ref_parser('word'), ) parsers['word'] |= parsers.ref_parser('cast-word') @parsy.generate def cast_word_parser() -> Generator: location = (yield parsers.token('CAST')).start yield parsers.token('LPAR') type_ast = yield parsers['type'] yield parsers.token('RPAR') return CastWordNode(type_ast, location) # This parses a cast word. # none word = LPAR, type, RPAR, CAST ; # The grammar of 'type' is defined by the typechecker. parsers['cast-word'] = concat.parser_combinators.desc_cumulatively( cast_word_parser, 'cast word' )
def typecheck_extension(parsers: concat.parse.ParserDict) -> None: @parsy.generate def attribute_type_parser() -> Generator: location = (yield parsers.token('DOT')).start name = (yield parsers.token('NAME')).value yield parsers.token('COLON') type = yield parsers['type'] raise NotImplementedError('better think about the syntax of this') @parsy.generate def named_type_parser() -> Generator: name_token = yield parsers.token('NAME') return NamedTypeNode(name_token.start, name_token.value) @parsy.generate def type_sequence_parser() -> Generator: name = parsers.token('NAME') individual_type_variable = ( # FIXME: Keep track of individual type variables parsers.token('BACKTICK') >> name >> parsy.success(None)) lpar = parsers.token('LPAR') rpar = parsers.token('RPAR') nested_stack_effect = lpar >> parsers['stack-effect-type'] << rpar type = parsers['type'] | individual_type_variable | nested_stack_effect # TODO: Allow type-only items item = parsy.seq( name, (parsers.token('COLON') >> type).optional()).map(_TypeSequenceIndividualTypeNode) items = item.many() seq_var = parsers.token('STAR') >> name seq_var_parsed, i = yield parsy.seq(seq_var.optional(), items) seq_var_value = None if seq_var_parsed is None and i: location = i[0].location elif seq_var_parsed is not None: location = seq_var_parsed.start seq_var_value = seq_var_parsed.value else: location = None return TypeSequenceNode(location, seq_var_value, i) @parsy.generate def stack_effect_type_parser() -> Generator: separator = parsers.token('MINUS').times(2) stack_effect = parsy.seq( # type: ignore parsers['type-sequence'] << separator, parsers['type-sequence']) i, o = yield stack_effect # FIXME: Get the location return StackEffectTypeNode((0, 0), i, o) @parsy.generate def intersection_type_parser() -> Generator: yield parsers.token('AMPER') type_1 = yield parsers['type'] type_2 = yield parsers['type'] return IntersectionTypeNode(type_1.location, type_1, type_2) parsers['stack-effect-type'] = concat.parser_combinators.desc_cumulatively( stack_effect_type_parser, 'stack effect type') @parsy.generate def generic_type_parser() -> Generator: type = yield parsers['nonparameterized-type'] yield parsers.token('LSQB') type_arguments = yield parsers['type'].sep_by(parsers.token('COMMA'), min=1) yield parsers.token('RSQB') return _GenericTypeNode(type.location, type, type_arguments) parsers['nonparameterized-type'] = parsy.alt( concat.parser_combinators.desc_cumulatively(intersection_type_parser, 'intersection type'), concat.parser_combinators.desc_cumulatively(attribute_type_parser, 'attribute type'), concat.parser_combinators.desc_cumulatively(named_type_parser, 'named type'), parsers.ref_parser('stack-effect-type'), ) parsers['type'] = parsy.alt( concat.parser_combinators.desc_cumulatively(generic_type_parser, 'generic type'), parsers.ref_parser('nonparameterized-type'), ) parsers['type-sequence'] = concat.parser_combinators.desc_cumulatively( type_sequence_parser, 'type sequence')
errors.Error( start=then_branch.start, end=then_branch.end, kind=errors.ParseKind.AmbiguousIf, message="There is an ambiguous if-if-else construct.")) return ast.If(cond=cond, then_branch=then_branch, else_branch=else_branch) @G.addpos @P.generate def while_stmt(): yield G.rword("while") cond = yield G.parens(E.expression) body = yield statement return ast.While(cond=cond, body=body) raw_statement = P.alt(brace_block, declaration, assignment, plusplus, minusminus, ret, if_stmt, while_stmt, free_expr) @G.addpos @P.generate def statement(): st = yield raw_statement if not isinstance(st, ast.Block): bl = ast.Block(statements=[st]) bl.attrs.generated = True return bl return st
from parsy import string, alt direction_parse = alt( string('sw').result((-1, 1)), string('se').result((-1, 0)), string('nw').result((1, 0)), string('ne').result((1, -1)), string('w').result((0, 1)), string('e').result((0, -1))) ordinal_directions = [(1, 0), (1, -1), (-1, 0), (-1, 1), (0, 1), (0, -1)] directions_parse = direction_parse.many() directions_list_parse = (directions_parse << string('\n')).many() def get_destination(dir_list): return sum(map(lambda pr: pr[0], dir_list)), sum(map(lambda pr: pr[1], dir_list)) def get_flipped_tiles(dir_list): flipped = set() for dest in map(get_destination, dir_list): if dest not in flipped: flipped.add(dest) else: flipped.remove(dest) return flipped def get_neighbour_tiles(tile_set): return {(tx + dx, ty + dy)
variable = G.addpos(G.identifier.map(lambda v: ast.Variable(var=v))) int_literal = G.addpos(G.number.map(lambda v: ast.IConstant(val=v))) bool_literal = G.addpos( (G.rword("true").result(True) | G.rword("false").result(False) ).map(lambda v: ast.BConstant(val=v)).desc("boolean literal")) string_literal = G.addpos( G.lexeme( P.regex(r'".*?"').map(lambda s: ast.SConstant(val=s[1:-1])).desc( "string literal"))) unary_operator = G.addpos( P.alt(*map(lambda k: G.symbol(k).result(prelude.unary_operator_map[k]), prelude.unary_operator_map.keys())).desc("unary operator")) binary_operator = G.addpos( P.alt(*map(lambda k: G.symbol(k).result(prelude.binary_operator_map[k]), prelude.binary_operator_map.keys())).desc("binary operator")) def var_from_op(op: ast.Operator) -> ast.Variable: return ast.Variable(start=op.start, end=op.end, var=op.name) @P.generate def parens_expression(): start = yield G.pos called_fn = yield variable.optional() params = yield G.parens(expression.sep_by(G.symbol(",")))
builder.add(start) builder.add(poss_range) break if end < start: raise RuntimeError(f"Invalid range: {start}-{end}") builder.add(start, end) start = yield any_char return Lit(builder.min, builder.max) base = alt( char_from(".").result(Lit(CHAR_MIN, CHAR_MAX)), char_from("^$"), # TODO string("\\") >> any_char.map(lambda c: Lit(c, c)), char_class, string("(") >> regex << string(")"), test_char(lambda c: c not in "?+*[()|", "").map(lambda c: Lit(c, c)), ) @attr.s(eq=False) class Node: eps_transitions = attr.ib(factory=list) matcher = attr.ib(default=None) next = attr.ib(default=None) def min_matching(self): return self.matcher.min def max_matching(self):
def spaceless_string(*strings: str): return spaceless( p.alt(*[p.string(s, transform=str.lower) for s in strings]))