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
|
from __future__ import annotations
import builtins
from .exceptions import SemanticError
from .objectmodel import BASE_CLASS_TOKEN, Node
from .synth import synthesize
from .util import simplify_list
class ASTSemantics:
def group(self, ast, *args):
return simplify_list(ast)
def element(self, ast, *args):
return simplify_list(ast)
def sequence(self, ast, *args):
return simplify_list(ast)
def choice(self, ast, *args):
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=None, base_type=Node, types=None):
self.ctx = context
self.base_type = base_type
self.constructors = {}
for t in types or ():
self._register_constructor(t)
def _register_constructor(self, constructor):
self.constructors[constructor.__name__] = constructor
return constructor
def _find_existing_constructor(self, typename):
constructor = builtins
for name in typename.split('.'):
try:
context = vars(constructor)
except Exception as e:
raise SemanticError(
f'Could not find constructor for {typename} ({type(constructor).__name__}): {e!s}',
) from e
if name in context:
constructor = context[name]
else:
constructor = None
break
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
|