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
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
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
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)
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)
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
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
def make_combinator(grammar: Grammar, content: str, location: Location = None): location = location or py_location(2) return convert_node(grammar, parse_combinator(content), location)