def function_type_args() -> GrammarType: return Optional([ # The `Not` rule is necessary to prevent the full_type rule to match, that prevents Arpeggio # to try the variadic dots rule Sequence(OneOrMore(full_type, Not('...'), sep=','), Optional(',', Optional(full_type), function_variadic_dots)), Sequence(Optional(full_type), function_variadic_dots) ]), trailing_comma
def repeatable_expr_SA(parser, node, children): expr = children[0] rule = expr repeat_op = False suppress = False if len(children) > 1: # We can have repeat operator and/or suppression operator if len(children) > 2: repeat_op = children[1] suppress = True else: if children[1] == '-': suppress = True else: repeat_op = children[1] if repeat_op: if len(repeat_op) > 1: repeat_op, modifiers = repeat_op else: repeat_op = repeat_op[0] modifiers = None if repeat_op == '?': rule = Optional(nodes=[expr]) elif repeat_op == '*': rule = ZeroOrMore(nodes=[expr]) else: rule = OneOrMore(nodes=[expr]) if modifiers: modifiers, position = modifiers # Sanity check. Modifiers do not make # sense for ? operator at the moment. if repeat_op == '?': line, col = parser.pos_to_linecol(position) raise TextXSyntaxError( 'Modifiers are not allowed for "?" operator at {}'. format(text((line, col))), line, col) # Separator modifier if 'sep' in modifiers: sep = modifiers['sep'] rule = Sequence(nodes=[ expr, ZeroOrMore(nodes=[Sequence(nodes=[sep, expr])]) ]) if repeat_op == '*': rule = Optional(nodes=[rule]) # End of line termination modifier if 'eolterm' in modifiers: rule.eolterm = True # Mark rule for suppression rule.suppress = suppress return rule
def textx_rule_SA(parser, node, children): if len(children) > 2: rule_name, rule_params, rule = children else: rule_name, rule = children rule_params = {} if rule.rule_name.startswith('__asgn') or\ (isinstance(rule, Match) and rule_params): # If it is assignment node it must be kept because it could be # e.g., single assignment in the rule. # Also, handle a special case where rule consists only of a single match # and there are rule modifiers defined. rule = Sequence(nodes=[rule], rule_name=rule_name, root=True, **rule_params) else: if not isinstance(rule, RuleCrossRef): # Promote rule node to root node. rule.rule_name = rule_name rule.root = True for param in rule_params: setattr(rule, param, rule_params[param]) # Connect meta-class and the PEG rule parser.metamodel._set_rule(rule_name, rule) return rule
def long_eq_arg(): """long argument, equal sign and operand without whitespace : '--file=foobar.txt' """ return Sequence((long_no_arg, eq, operand), rule_name='long_eq_arg', skipws=False)
def ol_first_option(): # semantic analysis : # - disallow short_stacked # - is this a continuation of the prior option-list ? # - disallow bar without a prior option-line return Sequence((Optional(bar), wx, option), rule_name='ol_first_option', skipws=False)
def required(): return Sequence(( l_paren, choice, r_paren, ), rule_name="required", skipws=True)
def optional(): return Sequence(( l_bracket, choice, r_bracket, ), rule_name="optional", skipws=True)
def option_description_section(): return Sequence( ( Optional(option_description_intro), OneOrMore(option_line), # [ EOF, blank_line ], ), rule_name='option_description_section', skipws=False)
def quoted_string(): def double_quoted(): return '"', _(r'((\\")|[^"])*'), '"' def single_quoted(): return "'", _(r"((\\')|[^'])*"), "'" # a sequence containing a single item -- the ordered choice return Sequence([double_quoted, single_quoted], skipws=False)
def option_list(): # semantic analysis : # - validate that series of option, operand, ol_bar, ol_comma and ol_space # form a meaninful comma/bar/space option-list # - disallow bar without a prior option-line return Sequence( (ol_first_option, ZeroOrMore(ol_term_with_separator)), # , Optional(EOF) ), rule_name='option_list', skipws=False)
def option_line(): # It should not be necessary to incorporate EOF so often. # arpeggio.NoMatch: Expected comma or bar or space or ' ' or # newline at position (1, 6) => '-f -x*'. return Sequence( (wx, option_list, Optional( (option_line_gap(), Optional(option_help()))), [EOF, newline]), rule_name='option_line', skipws=False)
def visit_rule(self, node, children): rule_name = children[0] if len(children) > 2: retval = Sequence(nodes=children[1:]) else: retval = children[1] retval.rule_name = rule_name retval.root = True # Keep a map of parser rules for cross reference # resolving. self.peg_rules[rule_name] = retval return retval
def textx_rule_SA(parser, node, children): if len(children[0]) > 2: rule_name, rule_params, rule = children[0] else: rule_name, rule = children[0] rule_params = {} rule = Sequence(nodes=[rule], rule_name=rule_name, root=True, **rule_params) # Add PEG rule to the meta-class parser.metamodel.set_rule(rule_name, rule) return rule
def _resolve_rule(rule): """ Recursively resolve peg rule references. Args: rule(ParsingExpression or RuleCrossRef) """ if not isinstance(rule, RuleCrossRef) and rule in resolved_rules: return rule resolved_rules.add(rule) if grammar_parser.debug: grammar_parser.dprint("Resolving rule: {}".format(rule)) if type(rule) is RuleCrossRef: rule_name = rule.rule_name suppress = rule.suppress if rule_name in model_parser.metamodel: rule = model_parser.metamodel[rule_name]._tx_peg_rule if type(rule) is RuleCrossRef: rule = _resolve_rule(rule) model_parser.metamodel[rule_name]._tx_peg_rule = rule if suppress: # Special case. Suppression on rule reference. _tx_class = rule._tx_class rule = Sequence(nodes=[rule], rule_name=rule_name, suppress=suppress) rule._tx_class = _tx_class else: line, col = grammar_parser.pos_to_linecol(rule.position) raise TextXSemanticError( 'Unexisting rule "{}" at position {}.'.format( rule.rule_name, (line, col)), line, col, filename=model_parser.metamodel.file_name) assert isinstance(rule, ParsingExpression),\ "{}:{}".format(type(rule), text(rule)) # Recurse into subrules, and resolve rules. for idx, child in enumerate(rule.nodes): if child not in resolved_rules: child = _resolve_rule(child) rule.nodes[idx] = child return rule
def __init__(self, *args, **kwargs): super(TextXModelParser, self).__init__(*args, **kwargs) # By default first rule is starting rule # and must be followed by the EOF self.parser_model = Sequence(nodes=[top_rule, EOF()], rule_name='Model', root=True) self.comments_model = comments_model # Stack for metaclass instances self._inst_stack = [] # Dict for cross-ref resolving # { id(class): { obj.name: obj}} self._instances = {}
def __init__(self, *args, **kwargs): super(TextXModelParser, self).__init__(*args, **kwargs) # By default first rule is starting rule # and must be followed by the EOF self.parser_model = Sequence( nodes=[top_rule, EOF()], rule_name='Model', root=True) self.comments_model = comments_model # Stack for metaclass instances self._inst_stack = [] # Dict for cross-ref resolving # { id(class): { obj.name: obj}} self._instances = {} # List to keep track of all cross-ref that need to be resolved # Contained elements are tuples: (instance, metaattr, cross-ref) self._crossrefs = []
def ol_term_with_separator(): return Sequence((ol_separator, ol_term), rule_name='ol_term_with_separator', skipws=False)
def ol_comma(): return Sequence(Optional(space), comma, Optional(space), rule_name='ol_comma', skipws=False)
def ol_bar(): return Sequence(Optional(space), bar, Optional(space), rule_name='ol_bar', skipws=False)
def line(): return Sequence(text, newline, rule_name='line', skipws=False)
def ol_space(): return Sequence(space, Not(space), rule_name='ol_space', skipws=False)
def assignment_SA(parser, node, children): """ Create parser rule for assignments and register attribute types on metaclass. """ attr_name = children[0] op = children[1] rhs_rule, modifiers = children[2] cls = parser._current_cls target_cls = None if parser.debug: parser.dprint("Processing assignment {}{}...".format(attr_name, op)) if parser.debug: parser.dprint("Creating attribute {}:{}".format( cls.__name__, attr_name)) parser.dprint("Assignment operation = {}".format(op)) if attr_name in cls._tx_attrs: # If attribute already exists in the metamodel it is # multiple assignment to the same attribute. # Cannot use operator ?= on multiple assignments if op == '?=': line, col = parser.pos_to_linecol(node.position) raise TextXSemanticError( 'Cannot use "?=" operator on multiple' ' assignments for attribute "{}" at {}'.format( attr_name, (line, col)), line, col) cls_attr = cls._tx_attrs[attr_name] # Must be a many multiplicity. # OneOrMore is "stronger" constraint. if cls_attr.mult is not MULT_ONEORMORE: cls_attr.mult = MULT_ZEROORMORE else: cls_attr = parser.metamodel._new_cls_attr(cls, name=attr_name, position=node.position) # Keep track of metaclass references and containments if type(rhs_rule) is tuple and rhs_rule[0] == "obj_ref": cls_attr.cont = False cls_attr.ref = True # Override rhs by its PEG rule for further processing rhs_rule = rhs_rule[1] # Target class is not the same as target rule target_cls = rhs_rule.cls base_rule_name = rhs_rule.rule_name if op == '+=': assignment_rule = OneOrMore(nodes=[rhs_rule], rule_name='__asgn_oneormore', root=True) cls_attr.mult = MULT_ONEORMORE elif op == '*=': assignment_rule = ZeroOrMore(nodes=[rhs_rule], rule_name='__asgn_zeroormore', root=True) if cls_attr.mult is not MULT_ONEORMORE: cls_attr.mult = MULT_ZEROORMORE elif op == '?=': assignment_rule = Optional(nodes=[rhs_rule], rule_name='__asgn_optional', root=True) cls_attr.mult = MULT_OPTIONAL base_rule_name = 'BOOL' # ?= assigment should have default value of False. # so we shall mark it as such. cls_attr.bool_assignment = True else: assignment_rule = Sequence(nodes=[rhs_rule], rule_name='__asgn_plain', root=True) # Modifiers if modifiers: modifiers, position = modifiers # Sanity check. Modifiers do not make # sense for ?= and = operator at the moment. if op == '?=' or op == '=': line, col = parser.pos_to_linecol(position) raise TextXSyntaxError( 'Modifiers are not allowed for "{}" operator at {}'.format( op, text((line, col))), line, col) # Separator modifier if 'sep' in modifiers: sep = modifiers['sep'] assignment_rule = Sequence(nodes=[ rhs_rule, ZeroOrMore(nodes=[Sequence(nodes=[sep, rhs_rule])]) ], rule_name='__asgn_list', root=True) if op == "*=": assignment_rule.root = False assignment_rule = Optional(nodes=[assignment_rule], rule_name='__asgn_list', root=True) # End of line termination modifier if 'eolterm' in modifiers: assignment_rule.eolterm = True if target_cls: attr_type = target_cls else: # Use STRING as default attr class attr_type = base_rule_name if base_rule_name else 'STRING' cls_attr.cls = ClassCrossRef(cls_name=attr_type, position=node.position) if parser.debug: parser.dprint("Created attribute {}:{}[cls={}, cont={}, " "ref={}, mult={}, pos={}]".format( cls.__name__, attr_name, cls_attr.cls.cls_name, cls_attr.cont, cls_attr.ref, cls_attr.mult, cls_attr.position)) assignment_rule._attr_name = attr_name assignment_rule._exp_str = attr_name # For nice error reporting return assignment_rule
def visit_textx_rule(self, node, children): if len(children) > 2: rule_name, rule_params, root_rule = children else: rule_name, root_rule = children rule_params = {} if root_rule.rule_name.startswith('__asgn') or \ ((isinstance(root_rule, Match) or isinstance(root_rule, RuleCrossRef)) and rule_params): # If it is assignment node it must be kept because it could be # e.g. single assignment in the rule. # Also, handle a special case where rule consists only of a single # match or single rule reference and there are rule modifiers # defined. root_rule = Sequence(nodes=[root_rule], rule_name=rule_name, root=True, **rule_params) else: if not isinstance(root_rule, RuleCrossRef): # Promote rule node to root node. root_rule.rule_name = rule_name root_rule.root = True for param in rule_params: setattr(root_rule, param, rule_params[param]) # Connect meta-class and the PEG rule cls = self.metamodel[rule_name] cls._tx_peg_rule = root_rule root_rule._tx_class = cls # Update end position for this rule. cls._tx_position_end = node.position_end # Update multiplicities of attributes based on their parent # expressions. def _update_attr_multiplicities(rule, oc_branch_set, mult=MULT_ONE): if isinstance(rule, RuleCrossRef): return if isinstance(rule, OrderedChoice): for on in rule.nodes: oc_branch_set = set() _update_attr_multiplicities(on, oc_branch_set, mult) else: if isinstance(rule, OneOrMore): mult = MULT_ONEORMORE elif isinstance(rule, ZeroOrMore): if mult != MULT_ONEORMORE: mult = MULT_ZEROORMORE if rule.rule_name.startswith('__asgn'): cls_attr = cls._tx_attrs[rule._attr_name] if mult in [MULT_ZEROORMORE, MULT_ONEORMORE]: if rule.rule_name == '__asgn_optional': raise TextXSemanticError( 'Can\'t use bool assignment ' 'inside repetition in rule "{}" at {}.'.format( rule_name, self.grammar_parser.pos_to_linecol( node.position))) if mult_lt(cls_attr.mult, mult): cls_attr.mult = mult # If multiplicity is not "many" still we can have # "many" multiplicity if same attribute has been # assigned multiple times in the same OrderedChoice # branch. elif rule._attr_name in oc_branch_set: cls_attr.mult = MULT_ONEORMORE else: # Keep track of assignments in the current OC # branch. oc_branch_set.add(rule._attr_name) if rule is root_rule or not rule.root: for n in rule.nodes: _update_attr_multiplicities(n, oc_branch_set, mult) _update_attr_multiplicities(root_rule, set()) return root_rule
def sequence_SA(parser, node, children): if len(children) > 1: return Sequence(nodes=children[:]) else: return children[0]
def visit_sequence(self, node, children): if len(children) == 1: return children[0] return Sequence(nodes=children[:])
def textx_rule_body_SA(parser, node, children): if len(children) > 1: return Sequence(nodes=children[:]) else: return children[0]
def option_description_intro(): # return OneOrMore ( Sequence ( ( Not(option_line_start), wx, text_line ) ), return OneOrMore(Sequence( (Not(option_line_start), wx, any_until_end_of_line)), rule_name='option_description_intro', skipws=False)
def _inner_resolve(rule, cls_rule_name=None): if grammar_parser.debug: grammar_parser.dprint("Resolving rule: {}".format(rule)) # Save initial rule name to detect special abstract rule case. initial_rule_name = cls_rule_name \ if cls_rule_name else rule.rule_name if type(rule) is RuleCrossRef: rule_name = rule.rule_name suppress = rule.suppress if rule_name in model_parser.metamodel: rule = model_parser.metamodel[rule_name]._tx_peg_rule if type(rule) is RuleCrossRef: rule = _inner_resolve(rule) model_parser.metamodel[rule_name]\ ._tx_peg_rule = rule if suppress: # Special case. Suppression on rule reference. _tx_class = rule._tx_class rule = Sequence(nodes=[rule], rule_name=rule_name, suppress=suppress) rule._tx_class = _tx_class else: line, col = grammar_parser.pos_to_linecol( rule.position) raise TextXSemanticError( 'Unexisting rule "{}" at position {}.'.format( rule.rule_name, (line, col)), line, col) assert isinstance(rule, ParsingExpression),\ "{}:{}".format(type(rule), text(rule)) # If there is meta-class attributes collected than this # is a common rule if hasattr(rule, '_tx_class') and \ len(rule._tx_class._tx_attrs) > 0: rule._tx_class._tx_type = RULE_COMMON # Recurse into subrules, resolve and determine rule types. for idx, child in enumerate(rule.nodes): if child not in resolved_set: resolved_set.add(rule) child = _inner_resolve(child) rule.nodes[idx] = child # Check if this rule is abstract # Abstract are root rules which haven't got any attributes # and reference at least one non-match rule. if initial_rule_name in model_parser.metamodel: cls = model_parser.metamodel[initial_rule_name] abstract = False if rule.rule_name and initial_rule_name != rule.rule_name: abstract = model_parser.metamodel[ rule.rule_name]._tx_type != RULE_MATCH else: abstract = not cls._tx_attrs and \ any([c._tx_class._tx_type != RULE_MATCH for c in rule.nodes if hasattr(c, '_tx_class')]) if abstract: cls._tx_type = RULE_ABSTRACT # Add inherited classes to this rule's meta-class if rule.rule_name and initial_rule_name != rule.rule_name: if rule._tx_class not in cls._tx_inh_by: cls._tx_inh_by.append(rule._tx_class) else: for idx, child in enumerate(rule.nodes): if child.root and hasattr(child, '_tx_class'): if child._tx_class not in cls._tx_inh_by: cls._tx_inh_by.append(child._tx_class) return rule
def single_quoted_term(): # Match everything that falls under a '' and also match escaped ' (\') return u"'", Sequence(OneOrMore( [_r(r'[^\x27\\]'), (u"\\", _r(r'.'))]), skipws=False), u"'"
def document(): return Sequence( body, EOF, rule_name='document' )