mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
Rename to blueprint-compiler
This isn't an official GTK project so better to avoid using "GTK" in the name.
This commit is contained in:
parent
be3c0de670
commit
544d152fb6
37 changed files with 33 additions and 33 deletions
|
@ -1,202 +0,0 @@
|
|||
# ast_utils.py
|
||||
#
|
||||
# Copyright 2021 James Westman <james@jwestman.net>
|
||||
#
|
||||
# This file is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as
|
||||
# published by the Free Software Foundation; either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This file is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
import typing as T
|
||||
from collections import ChainMap, defaultdict
|
||||
|
||||
from . import ast
|
||||
from .errors import *
|
||||
from .lsp_utils import SemanticToken
|
||||
from .utils import lazy_prop
|
||||
from .xml_emitter import XmlEmitter
|
||||
|
||||
|
||||
class Children:
|
||||
""" Allows accessing children by type using array syntax. """
|
||||
def __init__(self, children):
|
||||
self._children = children
|
||||
def __iter__(self):
|
||||
return iter(self._children)
|
||||
def __getitem__(self, key):
|
||||
return [child for child in self._children if isinstance(child, key)]
|
||||
|
||||
|
||||
class AstNode:
|
||||
""" Base class for nodes in the abstract syntax tree. """
|
||||
|
||||
completers: T.List = []
|
||||
|
||||
def __init__(self, group, children, tokens, incomplete=False):
|
||||
self.group = group
|
||||
self.children = Children(children)
|
||||
self.tokens = ChainMap(tokens, defaultdict(lambda: None))
|
||||
self.incomplete = incomplete
|
||||
|
||||
self.parent = None
|
||||
for child in self.children:
|
||||
child.parent = self
|
||||
|
||||
def __init_subclass__(cls):
|
||||
cls.completers = []
|
||||
cls.validators = [getattr(cls, f) for f in dir(cls) if hasattr(getattr(cls, f), "_validator")]
|
||||
|
||||
|
||||
@property
|
||||
def root(self):
|
||||
if self.parent is None:
|
||||
return self
|
||||
else:
|
||||
return self.parent.root
|
||||
|
||||
def parent_by_type(self, type):
|
||||
if self.parent is None:
|
||||
return None
|
||||
elif isinstance(self.parent, type):
|
||||
return self.parent
|
||||
else:
|
||||
return self.parent.parent_by_type(type)
|
||||
|
||||
def validate_parent_type(self, ns: str, name: str, err_msg: str):
|
||||
parent = self.root.gir.get_type(name, ns)
|
||||
container_type = self.parent_by_type(ast.Object).gir_class
|
||||
if container_type and not container_type.assignable_to(parent):
|
||||
raise CompileError(f"{container_type.full_name} is not a {parent.full_name}, so it doesn't have {err_msg}")
|
||||
|
||||
@lazy_prop
|
||||
def errors(self):
|
||||
return list(self._get_errors())
|
||||
|
||||
def _get_errors(self):
|
||||
for validator in self.validators:
|
||||
try:
|
||||
validator(self)
|
||||
except AlreadyCaughtError:
|
||||
pass
|
||||
except CompileError as e:
|
||||
yield e
|
||||
|
||||
for child in self.children:
|
||||
yield from child._get_errors()
|
||||
|
||||
def _attrs_by_type(self, attr_type):
|
||||
for name in dir(type(self)):
|
||||
item = getattr(type(self), name)
|
||||
if isinstance(item, attr_type):
|
||||
yield name, item
|
||||
|
||||
def generate(self) -> str:
|
||||
""" Generates an XML string from the node. """
|
||||
xml = XmlEmitter()
|
||||
self.emit_xml(xml)
|
||||
return xml.result
|
||||
|
||||
def emit_xml(self, xml: XmlEmitter):
|
||||
""" Emits the XML representation of this AST node to the XmlEmitter. """
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_docs(self, idx: int) -> T.Optional[str]:
|
||||
for name, attr in self._attrs_by_type(Docs):
|
||||
if attr.token_name:
|
||||
token = self.group.tokens.get(attr.token_name)
|
||||
if token and token.start <= idx < token.end:
|
||||
return getattr(self, name)
|
||||
else:
|
||||
return getattr(self, name)
|
||||
|
||||
for child in self.children:
|
||||
if child.group.start <= idx < child.group.end:
|
||||
docs = child.get_docs(idx)
|
||||
if docs is not None:
|
||||
return docs
|
||||
|
||||
return None
|
||||
|
||||
def get_semantic_tokens(self) -> T.Iterator[SemanticToken]:
|
||||
for child in self.children:
|
||||
yield from child.get_semantic_tokens()
|
||||
|
||||
|
||||
def iterate_children_recursive(self) -> T.Iterator["AstNode"]:
|
||||
yield self
|
||||
for child in self.children:
|
||||
yield from child.iterate_children_recursive()
|
||||
|
||||
|
||||
def validate(token_name=None, end_token_name=None, skip_incomplete=False):
|
||||
""" Decorator for functions that validate an AST node. Exceptions raised
|
||||
during validation are marked with range information from the tokens. Also
|
||||
creates a cached property out of the function. """
|
||||
|
||||
def decorator(func):
|
||||
def inner(self):
|
||||
if skip_incomplete and self.incomplete:
|
||||
return
|
||||
|
||||
try:
|
||||
func(self)
|
||||
except CompileError as e:
|
||||
# If the node is only partially complete, then an error must
|
||||
# have already been reported at the parsing stage
|
||||
if self.incomplete:
|
||||
return
|
||||
|
||||
# This mess of code sets the error's start and end positions
|
||||
# from the tokens passed to the decorator, if they have not
|
||||
# already been set
|
||||
if e.start is None:
|
||||
if token := self.group.tokens.get(token_name):
|
||||
e.start = token.start
|
||||
else:
|
||||
e.start = self.group.start
|
||||
|
||||
if e.end is None:
|
||||
if token := self.group.tokens.get(end_token_name):
|
||||
e.end = token.end
|
||||
elif token := self.group.tokens.get(token_name):
|
||||
e.end = token.end
|
||||
else:
|
||||
e.end = self.group.end
|
||||
|
||||
# Re-raise the exception
|
||||
raise e
|
||||
|
||||
inner._validator = True
|
||||
return inner
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
class Docs:
|
||||
def __init__(self, func, token_name=None):
|
||||
self.func = func
|
||||
self.token_name = token_name
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
if instance is None:
|
||||
return self
|
||||
return self.func(instance)
|
||||
|
||||
|
||||
def docs(*args, **kwargs):
|
||||
""" Decorator for functions that return documentation for tokens. """
|
||||
|
||||
def decorator(func):
|
||||
return Docs(func, *args, **kwargs)
|
||||
|
||||
return decorator
|
Loading…
Add table
Add a link
Reference in a new issue