80 lines
2.5 KiB
Python
80 lines
2.5 KiB
Python
# scopes.core.core
|
|
|
|
"""A concurrent cybernetic communication system"""
|
|
|
|
from __future__ import annotations # no check of type annotations at runtime
|
|
import asyncio
|
|
from collections.abc import Awaitable, Callable, Coroutine
|
|
from dataclasses import dataclass, field
|
|
from typing import Any, Self
|
|
|
|
from scopes.core import actor
|
|
from scopes.core.message import Message
|
|
from scopes import util
|
|
from scopes.util import keywords as k, Symbol
|
|
|
|
type Mailbox = asyncio.Queue
|
|
type Categ = tuple[Symbol, Symbol]
|
|
type Proc = Callable[[Message, Scope], Awaitable[Scope | None]]
|
|
type Program = Callable[[Scope], Proc]
|
|
type Syn = Callable[[Message], Message]
|
|
|
|
type Handler = Callable[[Message, Scope],
|
|
Awaitable[Message | None | tuple[Message | None, Scope | None]]]
|
|
type Handlers = Handler | None | list[Handlers]
|
|
type Actions = dict[Symbol, Handlers]
|
|
|
|
async def proc_dummy(msg: Message, scope: Scope) -> None:
|
|
await scope.environ.put(msg)
|
|
|
|
global_mailbox: Mailbox = asyncio.Queue()
|
|
|
|
@dataclass
|
|
class Scope:
|
|
|
|
value: Any = 0
|
|
categ: Categ = (k.csys, k.c00)
|
|
stage: Symbol = k.initial
|
|
proc: Proc = field(init=False)
|
|
program: Program = lambda x: proc_dummy
|
|
syns: list[Syn] = field(default_factory=list)
|
|
environ: Mailbox = global_mailbox
|
|
|
|
def __post_init__(self):
|
|
self.set_proc()
|
|
|
|
def set_proc(self) -> Self:
|
|
self.proc = self.program(self)
|
|
return self
|
|
|
|
def create_scope(env: Mailbox, cat: Categ = (k.csys, k.c00), **kw) -> Scope:
|
|
return Scope(environ=env, categ=cat, **kw)
|
|
|
|
def neuron(scope: Scope) -> Mailbox:
|
|
async def process(msg: Message) -> actor.Behavior | None:
|
|
nonlocal scope
|
|
scope = await scope.proc(msg, scope) or scope
|
|
return process
|
|
return actor.create(process)
|
|
|
|
def create_zero(env: Mailbox) -> Mailbox:
|
|
scope = create_scope(env)
|
|
zero = neuron(scope)
|
|
return zero
|
|
|
|
def gen_proc(actions: Actions, default: Handlers = None,
|
|
before: Handlers = None, after: Handlers = None) -> Proc:
|
|
async def proc(msg: Message, scope: Scope) -> Scope:
|
|
acts = actions.get(msg.head['action'], actions.get(k.default, default))
|
|
acts = util.flatten(before, acts, after, ignore_none=True)
|
|
for act in acts:
|
|
msg = await act(msg, scope)
|
|
if isinstance(msg, tuple):
|
|
msg, new_scope = msg
|
|
if new_scope is not None:
|
|
scope = new_scope
|
|
if msg is None:
|
|
break
|
|
return scope
|
|
return proc
|
|
|