File: synth.py

package info (click to toggle)
python-tatsu 5.17.1%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,516 kB
  • sloc: python: 13,185; makefile: 127
file content (63 lines) | stat: -rw-r--r-- 1,916 bytes parent folder | download
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
# Copyright (c) 2017-2026 Juancarlo AƱez (apalala@gmail.com)
# SPDX-License-Identifier: BSD-4-Clause
from __future__ import annotations

import types
from typing import Any

from .ast import AST
from .objectmodel import BaseNode

__all__ = ['SynthNode', 'registered_synthetics', 'synthesize']

# NOTE:
#   __registry is an alias for the modules vars()/ __dict__.
#   This allows for synthesized types to reside within this module.
__registry: dict[str, Any] = vars()


class SynthNode(BaseNode):
    def __init__(self, ast: Any = None, **attributes: Any):
        super().__init__(ast=ast, **attributes)
        if not isinstance(ast, AST):
            return
        # NOTE:
        #   synthetic objects have no attributes prior to this __init__()
        #   During parsing, the Synth is known at the start of a rule invocation,
        #   and the possible attributes known at the end
        for name, value in ast.items():
            setattr(self, name, value)
        self.ast = None


def synthesize(name: str, bases: tuple[type, ...], **kwargs: Any) -> type:
    # by Apalala 2026/02/16 <- 2017
    # by Gemini  2026/02/16
    if not isinstance(bases, tuple):
        raise TypeError(f'bases must be a tuple, not {type(bases)}')

    if SynthNode not in bases:
        bases = (*bases, SynthNode)

    found = __registry.get(name)
    if isinstance(found, type):
        return found
    elif found:
        raise TypeError(f'Found {name!r} in context but its type is {type(found)!r}')

    def build_body(ns: dict[str, Any]) -> None:
        ns.update({"__module__": __name__})
        ns.update(kwargs)

    newcls: type = types.new_class(name, bases, exec_body=build_body)
    __registry[name] = newcls

    return newcls


def registered_synthetics() -> dict[str, SynthNode]:
    return {
        name: value
        for name, value in __registry.items()
        if isinstance(value, SynthNode)
    }