예제 #1
0
    def _parse_error_catcher(self):
        pos = self._pos()
        args = []

        etype = self._tok(expected="error").value
        if etype.isnumeric():
            args.append(ErrorCatcherArg('int', etype, self._pos()))
            etype = 'generated'

        self._tok()
        while not (self._cur().name == 'keyword'
                   and self._cur().value == 'in') and self._cur().name not in [
                       'newline', 'rarrow', 'keyword'
                   ]:
            args.append(
                ErrorCatcherArg(self._cur().name,
                                self._cur().value, self._pos()))
            self._tok()

        ctx_rules = [self._tok(expected='ident').value]
        while self._tok().name == 'coma':
            ctx_rules.append(self._tok(expected='ident').value)

        self._cur(expected='rarrow')
        msg = self._tok(expected="string").value

        self._tok()
        if self._cur().name == "coma":
            self._tok()
            if not (self._cur().name == 'keyword'
                    and self._cur().value == "continue"):
                raise KombuError(
                    "{} : ',' not expected. Or do you mean ', continue' ?".
                    format(self._pos(formatted=True)))

            if not etype == "generated":
                raise KombuError(
                    "{} : ', continue' create a warning catcher, but you seem to need an "
                    "error catcher".format(self._pos(formatted=True)))
            warn = True
            self._tok()
        else:
            warn = False

        if warn:
            if len(args) > 1:
                raise KombuError(
                    "{} : 'catch !{} {}' : Warning does not need any arguments."
                    .format(self._pos(formatted=True), args[0].value,
                            " ".join([a.value for a in args[1:]])))

            return [
                WarningCatcher(msg, self.filename[:-3] + '__' + ctx_rule,
                               args[0].value, pos) for ctx_rule in ctx_rules
            ]
        else:
            return [
                ErrorCatcher(msg, etype, self.filename[:-3] + '__' + ctx_rule,
                             args, pos) for ctx_rule in ctx_rules
            ]
예제 #2
0
    def _parse_init_or_end_block(self, block_type):
        if block_type == 'init':
            block = BeforeBlock(self._pos())
        elif block_type == 'end':
            block = AfterBlock(self._pos())
        else:
            block = None

        self._tok(expected='LBlock')
        self._tok(expected='newline')
        imbrication_level = 1

        while self._cur() and imbrication_level > 0:
            added_code, imbrication_add = self._parse_init_or_end_block_part()
            imbrication_level += imbrication_add
            block.code.append(added_code)

        if not self._cur():
            if block_type == 'init':
                raise KombuError(
                    "{} : Unexpected end of file while parsing 'before' block."
                    .format(self._pos(formatted=True)))
            elif block_type == 'end':
                raise KombuError(
                    "{} : Unexpected end of file while parsing 'after' block.".
                    format(self._pos(formatted=True)))

        block.code.pop()
        block.code.pop()
        return block
예제 #3
0
 def _cur(self, expected=None):
     if len(self.tokenlist) > self.i:
         cur = self.tokenlist[self.i]
         if not expected or cur.name == expected:
             return cur
         else:
             raise KombuError("{} : '{}' : Expected {}.".format(
                 self._pos(formatted=True),
                 self._cur().value, expected))
     else:
         if expected:
             raise KombuError("{} : Expected {}, got end of file.".format(
                 self._pos(formatted=True), expected))
예제 #4
0
 def _next(self, expected=None, failmsg="Expected {expected}, got {got}."):
     failmsg = "Line {line} : '{gotvalue}' : " + failmsg
     if len(self.tokenlist) > self.i + 1:
         pcur = self.tokenlist[self.i + 1]
         if not expected or pcur.name == expected:
             return pcur
         else:
             raise KombuError(
                 replaces(failmsg, '{line}', self._pos(), '{gotvalue}',
                          self.tokenlist[self.i].value, '{expected}',
                          expected, '{gottype}', pcur.name))
     else:
         if expected:
             raise KombuError("{} : Expected {}, got end of file.".format(
                 self._pos(formatted=True), expected))
예제 #5
0
    def _check_left_recursive_loop(self, node):
        """Check that this rule definition won't provoke an endless recursive loop by rule calls.
         Raises an KomBuGrammarError if so."""
        def find_left_recursive_rule_call(rule):
            for node in rule.definition:
                if type(node) is Match:
                    return None
                elif type(node) is Choices:
                    for option in node.options:
                        for subnode in option.definition:
                            if type(subnode) is Match:
                                return None
                elif type(node) is RuleCall:
                    if not [s for s in rule.definition if type(s) is Match]:
                        return get_rule(self.code, node.whole_name)
                    return None

        left_recursive_call = find_left_recursive_rule_call(node)
        while left_recursive_call:
            if left_recursive_call.whole_name == node.whole_name:
                raise KombuError(
                    "{} : This rule definition'll provoke an endless recursive loop."
                    .format(self.formatted_pos(node.pos)))
            else:
                left_recursive_call = find_left_recursive_rule_call(
                    left_recursive_call)
예제 #6
0
 def _tok(self, expected=None):
     if len(self.tokenlist) > self.i + 1:
         self.i += 1
         return self._cur(expected=expected)
     else:
         self.i += 1
         if expected:
             raise KombuError("{} : Expected {}, got end of file.".format(
                 self._pos(formatted=True), expected))
예제 #7
0
    def _check_multiline_inspection(self, node):
        assert type(node) is MultilineInspection, "Error in the checker code."
        self._current_inspected_rule = get_rule(self.code, node.whole_name)
        if not self._current_inspected_rule:
            raise KombuError(
                "{} : 'inspect {}' : Cannot define an inspection on a not-existing rule."
                .format(self.formatted_pos(node.pos), node.name))

        inspection_definition = split(node.definition,
                                      NewLine(),
                                      include_at_beginning=True)[1:]
        base_indentation = len(inspection_definition[0][0].indentation.replace(
            '\t', ' ' * 16))
        for i, line in enumerate(inspection_definition):
            indentation = len(line[0].indentation.replace('\t', ' ' * 16))
            if indentation < base_indentation and len(line) > 1:
                raise KombuError("{} : This line is under-indented.".format(
                    self.formatted_pos(line[0].pos) + i + 1))
예제 #8
0
    def _check_uniline_inspection(self, node):
        assert type(node) is UnilineInspection, "Error in the checker code."
        self._current_inspected_rule = get_rule(self.code, node.whole_name)
        if not get_rule(self.code, node.whole_name):
            raise KombuError(
                "{} : 'inspect {}' : Cannot define an inspection on a not-existing rule."
                .format(self.formatted_pos(node.pos), node.name))

        for node in [n for n in node.definition if type(n) in [NodeCall]]:
            self._check_node(node)
예제 #9
0
 def _parse_optional_part(self):
     definition = []
     self._tok()
     while self._cur().name != 'ROptional':
         if self._cur().name == 'EOF' or self._cur().name == 'newline':
             raise KombuError(
                 "{} : Missing ']' to close the optional group.".format(
                     self._pos(formatted=True)))
         definition.append(self._parse_rule_definition_part())
     self._tok()
     return OptionalGroup(definition, self._pos())
예제 #10
0
 def _parse_block(self):
     if self._cur().name == 'constantname':
         return self._parse_constant_declaration()
     elif self._cur().name == 'ident':
         return self._parse_rule_definition()
     elif self._cur().name == 'uni_line_comment':
         return self._parse_comment()
     elif self._cur().name == 'multi_lines_comment':
         return self._parse_comment()
     elif self._cur().name == 'keyword' and self._cur().value == 'with':
         return self._parse_import()
     elif self._cur().name == 'keyword' and self._cur().value == 'block':
         return self._parse_block_definition()
     elif self._cur().name == 'keyword' and self._cur().value == 'group':
         return self._parse_group()
     elif self._cur().name == 'keyword' and self._cur().value == 'inspect':
         return self._parse_inspection()
     elif self._cur().name == 'keyword' and self._cur().value == 'before':
         return self._parse_init_or_end_block(block_type='init')
     elif self._cur().name == 'keyword' and self._cur().value == 'after':
         return self._parse_init_or_end_block(block_type='end')
     elif self._cur().name == 'keyword' and self._cur().value == 'catch':
         return self._parse_error_catcher()
     elif self._cur().name == 'newline':
         return NewLine(self._pos(), self._cur().value)
     elif self._cur().name == 'EOF':
         self._tok()
         return EOF(self._pos())
     elif re.match('R[A-Z]', self._cur().name):
         raise KombuError(
             "{} : '{}' : Got end char but there wasn't any begin char as (, or [."
             .format(self._pos(formatted=True),
                     self._cur().value))
     else:
         raise KombuError("{} : '{}' : Unexpected line syntax.".format(
             self._pos(formatted=True),
             self._cur().value))
예제 #11
0
    def _parse_rule_definition_part(self):
        if self._cur().name == 'rulecall':
            part = RuleCall(self._cur().value, self.namespace, self._pos())
            self._tok()
        elif self._cur().name == 'LOptional':
            part = self._parse_optional_part()
        elif self._cur().name == 'LGroup':
            part = self._parse_group_or_choices_group()
        elif self._cur().name == 'string':
            part = Match(self._cur().value, self._pos())
            self._tok()
        elif self._cur().name == 'regex':
            regex = self._cur().value
            self._tok()
            part = RegexMatch(regex, self._pos())
        else:
            errortoken = self._cur().value
            if errortoken == '|':
                errormsg = "{} : '|' used outside of a choices group.".format(
                    self._pos(formatted=True))
            elif errortoken == ']':
                errormsg = "{} : Found ']' to close an optional group but no '[' to begin it.".format(
                    self._pos(formatted=True))
            elif errortoken == ')':
                errormsg = "{} : Found ')' to close a choices group but no '(' to begin it.".format(
                    self._pos(formatted=True))
            else:
                errormsg = "{} : '{}' : Syntax error in the rule definition.".format(
                    self._pos(formatted=True),
                    self._cur().value)

            raise KombuError(errormsg)

        name = self._parse_name()
        if name:
            self._tok()
            part.group_name = name
            if type(part) is Choices:
                part = self._apply_choices_groupname_to_options(part)

        error_trigger = self._parse_error_trigger()
        if error_trigger:
            self._tok()
            if type(part) is OptionalGroup:
                part = WarningTrigger(error_trigger, part, self._pos())
            else:
                part = ErrorTrigger(error_trigger, part, self._pos())

        return part
예제 #12
0
    def _parse_choices_group(self):
        choices = Choices(self._pos())
        while self._cur().name != 'RGroup':
            self._tok()
            option = Option(self._pos())
            if self._cur().name == 'OptionsSeparator' or self._cur(
            ).name == 'RGroup':
                raise KombuError("{} : An option cannot be empty.".format(
                    self._pos(formatted=True)))
            option.add_code(self._parse_rule_definition_part())
            while self._cur().name != 'OptionsSeparator' and self._cur(
            ).name != 'RGroup':
                option.add_code(self._parse_rule_definition_part())
            choices += option

        self._tok()
        return choices
예제 #13
0
    def _parse_group_or_choices_group(self):
        is_choices_group = False
        i = self.i
        while not (self.tokenlist[i].name == 'RGroup'
                   or self.tokenlist[i - 1].name == 'OptionsSeparator'):
            if self.tokenlist[i].name == 'OptionsSeparator':
                is_choices_group = True
            if self.tokenlist[i].name in ["newline", 'EOF']:
                raise KombuError(
                    "{} : Missing ')' to close the choices group".format(
                        self._pos(formatted=True)))
            i += 1

        if is_choices_group:
            return self._parse_choices_group()
        else:
            return self._parse_group()
예제 #14
0
 def _check_node(self, node):
     if type(node) is RuleDefinition:
         self._check_rule_definition(node)
     elif type(node) is SetConstant:
         self._check_constant_declaration(node)
     elif type(node) is OptionalGroup:
         self._check_optional_group(node)
     elif type(node) is Choices:
         self._check_choices(node)
     elif type(node) is RuleCall:
         self._check_rulecall(node)
     elif type(node) is UnilineInspection:
         self._check_uniline_inspection(node)
     elif type(node) is MultilineInspection:
         self._check_multiline_inspection(node)
     elif type(node) is NodeCall:
         self._check_node_call(node)
     elif type(node) is Import or type(node) is FromImport:
         self._check_import(node)
     elif type(node) is Group:
         self._check_group(node)
     elif type(node) in [WarningTrigger, ErrorTrigger]:
         self._check_error_or_warning_trigger(node)
     elif type(node) is WarningCatcher:
         self._check_warning_catcher(node)
     elif type(node) is ErrorCatcher:
         self._check_error_catcher(node)
     elif type(node) is RawPythonCode:
         print(node.code.replace(' ', '[s]'))
     elif type(node) is Match:
         pass
     elif type(node) is NewLine:
         pass
     elif type(node) is RegexMatch:
         pass
     elif type(node) is BeforeBlock:
         pass
     elif type(node) is AfterBlock:
         pass
     else:
         raise KombuError("Error in the checker code.")
예제 #15
0
    def _parse_node_keyword(self):
        node_to_get = NodeCall(self._pos())

        if self._next().name == 'ident' and self._next(
        ).value == 'ast' and self.tokenlist[self.i + 2:self.i + 3][
                0] and self.tokenlist[self.i + 2:self.i +
                                      3][0].name != 'point':
            raise KombuError(
                "{} : 'node' keyword needs to be applied to a subnode of the ast."
                .format(self._pos(formatted=True)))

        self._tok(expected='ident')  # subnode

        while self._next().name == 'point':
            node_to_get.node_path.append(self._cur(expected='ident').value)
            self._tok(expected='point')
            self._tok(expected='ident')

        node_to_get.node_path.append(self._cur(expected='ident').value)
        self._tok()

        return node_to_get
예제 #16
0
 def _check_constant_declaration(self, node):
     assert type(node) is SetConstant, "Error in the Checker code."
     if node.constantname == 'axiom':
         self._check_option_value(node, True)
         found = get_rule(self.code, node.value)
         assert found, "{} : Axiom is defined by {}, but that rule doesn't exist.".format(
             self.formatted_pos(node.pos), node.value)
     elif node.constantname == 'libspath':
         self._check_option_value(node, True)
     elif node.constantname == 'name':
         self._check_option_value(node, True)
     elif node.constantname == 'keep_whitespaces':
         self._check_option_value(node, False)
     elif node.constantname == 'cut_end':
         self._check_option_value(node, False)
     elif node.constantname == 'get_time':
         self._check_option_value(node, False)
     elif node.constantname == 'show_ast':
         self._check_option_value(node, False)
     else:
         raise KombuError("{} : '{}' isn't an option name.".format(
             self.formatted_pos(node.pos), node.constantname))
예제 #17
0
    def _parse_uniline_inspection(self):

        inspection_name = self._cur().value
        self._tok(expected='keyword')
        self._tok()

        inspection_code = []

        inspection_code_part = self._parse_inspection_code_part()[1]
        if inspection_code_part == NewLine():
            raise KombuError(
                "{} : Unilines inspections need a return value. Use None if you don't want to "
                "return anything.".format(self._pos(formatted=True)))

        inspection_code.append(inspection_code_part)
        while inspection_code_part != NewLine():
            inspection_code_part = self._parse_inspection_code_part()[1]
            inspection_code.append(inspection_code_part)

        self._bef()

        return UnilineInspection(inspection_name, self.namespace,
                                 inspection_code, self._pos())
예제 #18
0
    def _parse_multiline_inspection(self):
        inspection = MultilineInspection(name=self._cur().value,
                                         namespace=self.namespace,
                                         definition=[],
                                         pos=self._pos())
        self._tok(expected='LBlock')
        self._tok()
        imbrication_level = 1

        while imbrication_level > 0 and self._cur():
            imbrication_add, inspection_code_part = self._parse_inspection_code_part(
            )
            imbrication_level += imbrication_add
            inspection.definition.append(inspection_code_part)

        if not self._cur():
            raise KombuError(
                "{} : Unexpected end of file while parsing multiline inspection."
                .format(self.tokenlist[-1].pos))

        self._cur(expected='newline')
        inspection.definition = inspection.definition[:-1]

        return inspection
예제 #19
0
 def t_double_underscored_ident(self, t):
     raise KombuError("Line {} : '{}' : Cannot use '__' in an ident name.".format(t.pos, t.value))
예제 #20
0
 def t_unexpected_option_name(self, t):
     raise KombuError("Line {} : '{}' : Option name should only be composed of alphanumerics characters.".format(
         t.pos, t.value))
예제 #21
0
 def t_missing_option_name(self, t):
     raise KombuError("Line {} : '$' : Expected an option name.".format(t.pos))
예제 #22
0
 def _check_rule_exist(self, rule):
     if rule not in [
             r.name for r in self.code.ast if type(r) is RuleDefinition
     ]:
         raise KombuError(
             "'<{}>' : Trying to invoke a non-existing rule.".format(rule))
예제 #23
0
 def _parse_block_definition(self):
     raise KombuError("{} : Blocks option isn't implemented.".format(
         self._pos(formatted=True)))
예제 #24
0
 def _parse_test(self):
     raise KombuError("{} : Tests option isn't implemented.".format(
         self._pos(formatted=True)))
예제 #25
0
    def _check_error_catcher(self, node):
        def check_args(e_catcher, expected):
            if len(e_catcher.args) > expected:
                raise ValueError(
                    "{} : '{}' : To many arguments for the error ; type !{} expects {} arguments"
                    .format(self.formatted_pos(e_catcher.args[expected].pos),
                            e_catcher.args[expected].value, e_catcher.etype,
                            expected))
            if len(e_catcher.args) < expected:
                raise ValueError(
                    '{} : To few arguments for the error : type !{} expects {} arguments'
                    .format(self.formatted_pos(e_catcher.pos), e_catcher.etype,
                            expected))

        if node.etype == "generated":
            check_args(node, 1)
            if not self._find_node(lambda n: type(n) is ErrorTrigger and n.nb
                                   == node.arg(0).value,
                                   rule=node.ctx_rule):
                raise ValueError(
                    "{} : catch !{} in {} : Cannot try to catch an error that won't be "
                    "raised.".format(self.formatted_pos(node.pos),
                                     node.arg(0).value,
                                     node.ctx_rule.split('__')[-1]))

        elif node.etype == "missing":
            check_args(node, 1)
            if node.arg(0).type == "string":
                found = self._find_node(lambda n: type(n) is Match and n.string
                                        == node.arg(0).value,
                                        rule=node.ctx_rule)
            elif node.arg(0).type == 'regex':
                found = self._find_node(lambda n: type(n) is RegexMatch and n.
                                        pattern == node.arg(0).value,
                                        rule=node.ctx_rule)
            else:
                raise KombuError(
                    "{} : Pattern or string expected after !missing, not {}".
                    format(self.formatted_pos(node.arg(0).pos),
                           node.arg(0).type))

            if not found:
                raise KombuError(
                    "{} : Define a catcher on a !missing error that cannot be raised."
                    .format(self.formatted_pos(node.pos)))

        elif node.etype == "failed":
            check_args(node, 1)
            if not node.arg(0).type == 'ident':
                raise KombuError(
                    "{} : Rule name expected for error !failed, not {}".format(
                        self.formatted_pos(node.pos),
                        node.arg(0).type))

            if not self._find_node(lambda n: type(n) is RuleCall and n.name ==
                                   node.arg(0).value,
                                   rule=node.ctx_rule):
                raise KombuError(
                    "{} : catch !failed {} : Cannot find a call to that rule in {}"
                    .format(self.formatted_pos(node.pos),
                            node.arg(0).value, node.ctx_rule))

        else:
            raise NameError("{} : catch !{} : Invalid error name.".format(
                self.formatted_pos(node.pos), node.etype))