1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
|
from __future__ import annotations
import builtins
from collections.abc import Callable, Iterable, Mapping, MutableMapping
from typing import Any
from .contexts import ParseContext
from .exceptions import SemanticError
from .objectmodel import BASE_CLASS_TOKEN, Node
from .synth import registered_symthetics, synthesize
from .util import simplify_list
class ASTSemantics:
def group(self, ast: Any, *args) -> Any:
return simplify_list(ast)
def element(self, ast: Any, *args) -> Any:
return simplify_list(ast)
def sequence(self, ast: Any, *args) -> Any:
return simplify_list(ast)
def choice(self, ast: Any, *args) -> Any:
if len(ast) == 1:
return simplify_list(ast[0])
return ast
class ModelBuilderSemantics:
"""Intended as a semantic action for parsing, a ModelBuilderSemantics creates
nodes using the class name given as first parameter to a grammar
rule, and synthesizes the class/type if it's not known.
"""
def __init__(
self,
context: ParseContext | None = None,
base_type: type[Node] = Node,
types: Iterable[Callable] | None = None):
self.ctx = context
self.base_type = base_type
self.constructors: MutableMapping[str, Callable] = {}
for t in types or ():
self._register_constructor(t)
def _register_constructor(self, constructor: Callable) -> Callable:
if hasattr(constructor, '__name__') and isinstance(constructor.__name__, str):
self.constructors[str(constructor.__name__)] = constructor
return constructor
def _find_existing_constructor(self, typename: str) -> Callable | None:
context: Mapping[Any, Any] = vars(builtins) | registered_symthetics()
constructor = context.get(typename)
if constructor is not None:
return constructor
for name in typename.split('.'):
if name not in context:
return None
constructor = context[name]
if hasattr(constructor, '__dict__'):
context = vars(constructor)
else:
context = {}
return constructor
def _get_constructor(self, typename, base):
typename = str(typename)
if typename in self.constructors:
return self.constructors[typename]
constructor = self._find_existing_constructor(typename)
if not constructor:
constructor = synthesize(typename, base)
return self._register_constructor(constructor)
def _default(self, ast, *args, **kwargs):
if not args:
return ast
typespec = args[0].split(BASE_CLASS_TOKEN)
typename = typespec[0]
bases = typespec[-1:0:-1]
base = self.base_type
for base_ in bases:
base = self._get_constructor(base_, base)
constructor = self._get_constructor(typename, base)
try:
if isinstance(constructor, type) and issubclass(constructor, Node):
return constructor(ast=ast, ctx=self.ctx, **kwargs)
else:
return constructor(ast, *args[1:], **kwargs)
except Exception as e:
raise SemanticError(
f'Could not call constructor for {typename}: {e!s}',
) from e
|