def make_element_parser(config, content, jinja): container_element = make_container_element_parser( config, content=content, jinja=jinja, ) self_closing_element_opening_tag = make_opening_tag_parser( config, tag_name_parser=P.string_from(*SELF_CLOSING_ELEMENTS), allow_slash=True, jinja=jinja, ) self_closing_element = ( locate(P.seq( self_closing_element_opening_tag.skip(whitespace), P.success(None), # No content P.success(None), # No closing tag )) .combine(_combine_element) ) style = make_raw_text_element_parser(config, 'style', jinja=jinja) script = make_raw_text_element_parser(config, 'script', jinja=jinja) return style | script | self_closing_element | container_element
def make_opening_tag_parser(config, jinja, tag_name_parser=None, allow_slash=False): attributes = make_attributes_parser(config, jinja) if not tag_name_parser: tag_name_parser = tag_name | jinja if allow_slash: slash = ( locate( P.string('/') .skip(whitespace) ) .combine(_combine_slash) .optional() ) else: slash = P.success(None) return ( locate(P.seq( P.string('<'), tag_name_parser, attributes.skip(whitespace), slash, P.string('>'), )) .combine(_combine_opening_tag) )
def make_jinja_element_parser(name_parsers, content): """ `name_parsers` must be a list of tag name parsers. For example, `name_parsers` can be defined as follow in order to parse `if` statements: name_parsers = [P.string(n) for n in ['if', 'elif', 'else', 'endif']] """ if len(name_parsers) == 1: tag = make_jinja_tag_parser(name_parsers[0]) part = locate(P.seq( tag, P.success(None), )).combine(_combine_jinja_element_part) parts = [part] end_tag_parser = None else: part_names = name_parsers[:-1] first_part = make_jinja_element_part_parser( part_names[0], content=content) next_parts = [ make_jinja_element_part_parser(name, content=content).many() for name in part_names[1:] ] parts = [first_part] + next_parts end_tag_parser = make_jinja_tag_parser(name_parsers[-1]) content = [P.seq(*parts)] if end_tag_parser: content.append(end_tag_parser) return ( locate(P.seq(*content)) .combine(_combine_jinja_element) )
def space( p_space=char.space, p_line_comment=parsy.fail('line-comment'), p_block_comment=parsy.fail('block-comment') ): """ Produces a parser that consumes white space in general. It's expected that you create such a parser once and pass it to other functions in this package as needed (when you see `p_space_consumer` in documentation, usually it means that something like `space()` is expected there). Args: p_space_chars: is used to parse blocks of space characters. You can use 'char.space' for this purpose, or your own parser (if you don't want to automatically consume newlines, for example). Make sure the parser does not succeed on empty input though. p_line_comment: is used to parse line comments. You can use 'megaparsy.skip_line_comment` if you don't need anything special. p_block_comment: is used to parse block (multi-line) comments. You can use `megaparsy.skip_block_comment` or `skip_block_comment_nested` if you don't need anything special. If you don't want to match a kind of comment, simply pass `parsy.fail()` and `space` will just move on or finish depending on whether there is more white space for it to consume. """ return parsy.success('')\ .skip(p_space.optional())\ .skip(p_line_comment.optional())\ .skip(p_block_comment.optional())
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)
def make_jinja_element_parser(name_parsers, content): if len(name_parsers) == 1: tag = make_jinja_tag_parser(name_parsers[0]) part = locate(P.seq( tag, P.success(None), )).combine(_combine_jinja_element_part) parts = [part] end_tag_parser = None else: part_names = name_parsers[:-1] first_part = make_jinja_element_part_parser(part_names[0], content=content) next_parts = [ make_jinja_element_part_parser(name, content=content).many() for name in part_names[1:] ] parts = [first_part] + next_parts end_tag_parser = make_jinja_tag_parser(name_parsers[-1]) content = [P.seq(*parts)] if end_tag_parser: content.append(end_tag_parser) return (locate(P.seq(*content)).combine(_combine_jinja_element))
def multiplicative(): prod = yield additive while True: op = yield multiplicative_op | success(None) if not op: return prod operand = yield additive prod = BinOp(prod, op, operand)
def additive(): sum = yield simple while True: op = yield additive_op | success(None) if not op: return sum operand = yield simple sum = BinOp(sum, op, operand)
def make_element_parser(config, content, jinja): container_element = make_container_element_parser( config, content=content, jinja=jinja ) void_element_opening_tag = make_opening_tag_parser( config, tag_name_parser=P.string_from(*VOID_ELEMENTS), allow_slash=True, jinja=jinja, ) void_element = locate( P.seq( void_element_opening_tag.skip(whitespace), P.success(None), # No content P.success(None), # No closing tag ) ).combine(_combine_element) svg_self_closing_tag = make_opening_tag_parser( config, tag_name_parser=P.string_from(*SVG_SELF_CLOSING_ELEMENTS), mandate_slash=True, jinja=jinja, ) svg_self_closing_element = locate( P.seq( svg_self_closing_tag.skip(whitespace), P.success(None), # No content P.success(None), # No closing tag ) ).combine(_combine_element) style = make_raw_text_element_parser(config, "style", jinja=jinja) script = make_raw_text_element_parser(config, "script", jinja=jinja) return ( style | script | void_element | svg_self_closing_element | container_element )
def binop(): left = yield simple # print('left', left) while True: op = yield all_op | success(None) # print('binop', op) if not op: return left right = yield simple # print('right', right) left = BinOp(left, op, right)
def multiplicative(): res = yield simple op = match_item('*') | match_item('/') while True: operation = yield op | success('') if not operation: break operand = yield simple if operation == '*': res *= operand elif operation == '/': res /= operand return res
def additive(): res = yield multiplicative sign = match_item('+') | match_item('-') while True: operation = yield sign | success('') if not operation: break operand = yield multiplicative if operation == '+': res += operand elif operation == '-': res -= operand return res
def expr_add(): op = (string("+").result( lambda first: lambda rest: lambda env: first(env) + rest(env)) | string("-").result( lambda first: lambda rest: lambda env: first(env) - rest(env))) this = lambda _: lambda rest: rest ms = yield (success([this]) + expr_mul.times(1)).times(1) + \ (whitespace.optional() >> op.times(1) + expr_mul.times(1)).many() value = None for op, item in ms: value = op(value)(item) return value
Create parsers for Jinja variables and regular Jinja tags. `name` should be a parser to parse the tag name. """ end = whitespace.then(P.string('-').optional()).skip(P.string(mr + '}')) return locate(P.seq( P.string('{' + ml).then(P.string('+').optional()), P.string('-').optional().skip(whitespace), name.skip(whitespace), until(end).concat(), end )).combine(_combine_jinja_tag_like) jinja_variable = make_jinja_tag_like_parser( P.success(None), '{', '}' ).combine(_combine_jinja_variable) jinja_comment = ( locate( P.string('{#') .skip(whitespace) .then(until(whitespace + P.string('#}')).concat()) .skip(whitespace + P.string('#}')) ) .combine(_combine_jinja_comment) ) def make_jinja_tag_parser(name_parser):
`name` should be a parser to parse the tag name. """ end = whitespace.then(P.string("-").optional()).skip(P.string(mr + "}")) return locate( P.seq( P.string("{" + ml).then(P.string("+").optional()), P.string("-").optional().skip(whitespace), name.skip(whitespace), until(end).concat(), end, ) ).combine(_combine_jinja_tag_like) jinja_variable = make_jinja_tag_like_parser(P.success(None), "{", "}").combine( _combine_jinja_variable ) jinja_comment = locate( P.string("{#") .skip(whitespace) .then(until(whitespace + P.string("#}")).concat()) .skip(whitespace + P.string("#}")) ).combine(_combine_jinja_comment) def make_jinja_tag_parser(name_parser): return make_jinja_tag_like_parser(name_parser, "%", "%").combine( _combine_jinja_tag
def number(): sign = yield match_item('+') | match_item('-') | success('+') value = yield test_item(lambda x: isinstance(x, (int, float)), 'number') return value if sign == '+' else -value
`name` should be a parser to parse the tag name. """ end = whitespace.then(P.string("-").optional()).skip(P.string(mr + "}")) return locate( P.seq( P.string("{" + ml).then(P.string("+").optional()), P.string("-").optional().skip(whitespace), name.skip(whitespace), until(end).concat(), end, )).combine(_combine_jinja_tag_like) jinja_variable = make_jinja_tag_like_parser( P.success(None), "{", "}").combine(_combine_jinja_variable) jinja_comment = locate( P.string("{#").skip(whitespace).then( until(whitespace + P.string("#}")).concat()).skip( whitespace + P.string("#}"))).combine(_combine_jinja_comment) def make_jinja_tag_parser(name_parser): return make_jinja_tag_like_parser(name_parser, "%", "%").combine(_combine_jinja_tag) def _combine_jinja_element(locations, content): parts = list(flatten(content[0])) closing_tag = content[1] if len(content) == 2 else None
operand = yield simple sum = BinOp(sum, op, operand) @generate def multiplicative(): prod = yield additive while True: op = yield multiplicative_op | success(None) if not op: return prod operand = yield additive prod = BinOp(prod, op, operand) sign_op = plus | minus | success("+") @generate def number(): sign = yield sign_op num = yield int_ | float_ return num if sign == "+" else -num simple = (lparen >> multiplicative << rparen) | number expr = whitespace >> multiplicative s = 0 with puzzle_input(18, example, False) as f: