Exemplo n.º 1
0
    def add_parser(self, parser_id: Union[str, ParseletID], combinator: Union[Combinator, str, SymbolID],
                   generator: ActionGenerator = None, *, priority: int = PRIORITY_MAX, location: Location = None) \
            -> ParseletID:
        location = location or py_location(2)
        # convert input combinator to instance of Combinator
        if isinstance(combinator, str):
            from gvm.language.helpers import make_combinator
            combinator = make_combinator(self, combinator, location)
        else:
            combinator = flat_combinator(combinator)

        # convert action to combinator action
        generator = generator or make_return_result()
        action = generator(combinator)

        # check parser type
        if isinstance(parser_id, str):
            parser_id = self.add_parselet(parser_id,
                                          location=location,
                                          result_type=action.result_type)
        else:
            # check result of action with ret
            if not is_subclass(action.result_type, parser_id.result_type):
                raise GrammarError(
                    location,
                    f'Can not add parser to parselet because return types is different: '
                    f'{action.result_type} and {parser_id.result_type}')

        # add parser tot table
        self.tables[parser_id].add_parser(combinator, action, priority,
                                          location)
        return parser_id
Exemplo n.º 2
0
 def merge(cls, *grammars: Grammar, location: Location = None) -> Grammar:
     """ Merge grammars in one """
     location = location or py_location(2)
     result = cls()
     for grammar in grammars:
         result.extend(grammar, location=location)
     return result
Exemplo n.º 3
0
    def add_parselet(self,
                     name: str,
                     *,
                     result_type: Type = None,
                     kind: ParseletKind = ParseletKind.Packrat,
                     location: Location = None) -> ParseletID:
        result_type = result_type or SyntaxNode
        location = location or py_location(2)
        if not RE_PARSELET.match(name):
            raise GrammarError(
                location,
                f'Symbol id for parselet must be: {RE_PARSELET.pattern}')
        if name in self.__parselets:
            parser_id = self.__parselets[name]
            if parser_id.kind != kind:
                raise GrammarError(
                    location,
                    f'Can not define parser {parser_id} with different kind')
            if parser_id.result_type != result_type:
                raise GrammarError(
                    location,
                    f'Can not define parser {parser_id} with different return type'
                )
            return parser_id
        if name in self.__symbols:
            raise GrammarError(location,
                               f'Already registered symbol id: {name}')

        parser_id = ParseletID(len(self.__symbols), name, location, kind,
                               result_type)
        self.__parselets[name] = self.__symbols[name] = parser_id
        self.__tables[parser_id] = (PackratTable
                                    if kind == ParseletKind.Packrat else
                                    PrattTable)(parser_id)
        return parser_id
Exemplo n.º 4
0
 def add_implicit(self,
                  pattern: str,
                  *,
                  location: Location = None) -> TokenID:
     location = location or py_location(2)
     token_id = self.add_token(pattern, is_implicit=True, location=location)
     return self.add_pattern(token_id,
                             re.escape(pattern),
                             priority=-len(pattern),
                             location=location,
                             is_implicit=True)
Exemplo n.º 5
0
    def extend(self, grammar: Grammar, *, location: Location = None):
        """
        Merge current grammar with another
        """
        location = location or py_location(2)
        symbols: MutableMapping[SymbolID, SymbolID] = {}

        # merge tokens
        for token_id in grammar.tokens.values():
            try:
                symbols[token_id] = self.add_token(
                    token_id.name,
                    token_id.description,
                    location=token_id.location,
                    is_implicit=token_id.is_implicit)
            except GrammarError as ex:
                raise attr.evolve(ex, location=location)

        # merge parsers
        for parser_id in grammar.parselets.values():
            try:
                symbols[parser_id] = self.add_parselet(
                    parser_id.name,
                    kind=parser_id.kind,
                    location=parser_id.location)
            except GrammarError as ex:
                raise attr.evolve(ex, location=location)

        # merge trivia
        for token_id in grammar.trivia:
            self.add_trivia(cast(TokenID, symbols[token_id]))

        # merge brackets
        for open_id, close_id in grammar.brackets:
            self.add_brackets(open_id, close_id)

        # merge token patterns
        for pattern in grammar.patterns:
            if pattern not in self.__patterns:
                token_id: TokenID = cast(TokenID, symbols[pattern.token_id])
                bisect.insort_right(
                    self.__patterns,
                    SyntaxPattern(token_id, pattern.pattern, pattern.priority,
                                  pattern.location, pattern.is_implicit))

        # merge parser tables
        for table in grammar.tables.values():
            parser_id = cast(ParseletID, symbols[table.parser_id])
            new_table: ParseletTable = self.tables[parser_id]
            for parselet in table.parselets:
                combinator = parselet.combinator.clone(symbols)
                new_table.add_parser(combinator, parselet.action,
                                     parselet.priority, parselet.location)
Exemplo n.º 6
0
 def add_pattern(self,
                 token_id: TokenID,
                 pattern: str,
                 *,
                 priority: int = PRIORITY_MAX,
                 location: Location = None,
                 is_implicit: bool = False) -> TokenID:
     location = location or py_location(2)
     bisect.insort_right(
         self.__patterns,
         SyntaxPattern(token_id, re.compile(pattern), priority, location,
                       is_implicit))
     return token_id
Exemplo n.º 7
0
    def add_token(self,
                  name: str,
                  description: str = None,
                  *,
                  is_implicit: bool = False,
                  location: Location = None) -> TokenID:
        location = location or py_location(2)
        if not is_implicit and not RE_TOKEN.match(name):
            raise GrammarError(
                location, f'Symbol id for token must be: {RE_TOKEN.pattern}')
        if name in self.__tokens:
            token_id = self.__tokens[name]
            return token_id
        if name in self.__symbols:
            raise GrammarError(location,
                               f'Already registered symbol id: {name}')

        description = description or (name if is_implicit else
                                      camel_case_to_lower(name))
        token_id = TokenID(
            len(self.__symbols) + 1, name, location, description, is_implicit)
        self.__tokens[name] = self.__symbols[name] = token_id
        return token_id
Exemplo n.º 8
0
def make_combinator(grammar: Grammar, content: str, location: Location = None):
    location = location or py_location(2)
    return convert_node(grammar, parse_combinator(content), location)