Castle: The best Real-Time/Embedded/HighTech language EVER. Attempt 2
修訂 | fb3240ad99ab8cc046e3393637ed4d0dd1ef7dde (tree) |
---|---|
時間 | 2023-10-05 05:31:58 |
作者 | Albert Mietus < albert AT mietus DOT nl > |
Commiter | Albert Mietus < albert AT mietus DOT nl > |
Added ProtocolWrapper (and to many small txt changed)
@@ -16,11 +16,11 @@ | ||
16 | 16 | pytst/aigr/test_2b_protocol.py \ |
17 | 17 | pytst/writers/RPy/test_0_templating.py \ |
18 | 18 | pytst/writers/RPy/test_1_EventIndexes.py \ |
19 | + pytst/writers/RPy/test_2_ProtocolDataStructures.py \ | |
20 | + pytst/writers/RPy/test_99_SieveMoats.py \ | |
19 | 21 | # |
20 | 22 | rPY_CURRENT = \ |
21 | - pytst/writers/RPy/test_2_ProtocolDataStructures.py \ | |
22 | - pytst/writers/RPy/test_99_SieveMoats.py \ | |
23 | - pytst/aigr/test_2b_protocol.py \ | |
23 | + pytst/aigr/test_2c_GenericProtocols.py \ | |
24 | 24 | # |
25 | 25 | CC2CPy_TODO = \ |
26 | 26 | pytst/writers/RPy/test_999.py \ |
@@ -1,6 +1,6 @@ | ||
1 | 1 | # (C) Albert Mietus, 2023. Part of Castle/CCastle project |
2 | 2 | |
3 | -from castle.aigr.types import TypedParameter | |
3 | +from castle.aigr.aid import TypedParameter | |
4 | 4 | from castle.aigr import EventProtocol, Event |
5 | 5 | |
6 | 6 | import pytest |
@@ -1,6 +1,6 @@ | ||
1 | 1 | # (C) Albert Mietus, 2023. Part of Castle/CCastle project |
2 | 2 | |
3 | -from castle.aigr.types import TypedParameter | |
3 | +from castle.aigr.aid import TypedParameter | |
4 | 4 | from castle.aigr import EventProtocol, Event |
5 | 5 | |
6 | 6 | from .Sieve import * |
@@ -0,0 +1,36 @@ | ||
1 | +# (C) Albert Mietus, 2023. Part of Castle/CCastle project | |
2 | + | |
3 | +""" This file is based (a fork) on `../writers/CC2Cpy/Protocol.py`, | |
4 | + - the rendering part is removed | |
5 | + - the prefixed are gone | |
6 | + - better | |
7 | +TODO: update the CC2Cpy parts to use this generic AIGR layer | |
8 | +""" | |
9 | + | |
10 | +import typing as PTH # Python TypeHints | |
11 | +from enum import Enum | |
12 | +from dataclasses import dataclass, KW_ONLY | |
13 | +from . import AIGR | |
14 | + | |
15 | +# XXX__all__ = [ | |
16 | + | |
17 | +@dataclass | |
18 | +class TypedParameter(AIGR): | |
19 | + """This is many a helper class/struct to combine a parameter: a name and an type""" | |
20 | + name: str | |
21 | + type: type | |
22 | + | |
23 | +@dataclass | |
24 | +class Argument(AIGR): | |
25 | + """This is many a helper class/struct to combine a argument: a value and optional a name""" | |
26 | + value: PTH.Any | |
27 | + _: KW_ONLY | |
28 | + name: PTH.Optional[str]=None | |
29 | + | |
30 | +#@dataclass | |
31 | +#class Invoke(AIGR): | |
32 | +# """call a callable: a callable and a list of Arguments""" | |
33 | +# _: KW_ONLY | |
34 | +# callable: PTH.Any # XXX | |
35 | +# arguments: PTH.Sequence[Argument]=() | |
36 | + |
@@ -7,17 +7,17 @@ | ||
7 | 7 | TODO: update the CC2Cpy parts to use this generic AIGR layer |
8 | 8 | """ |
9 | 9 | |
10 | -import typing as PTH # Python TypeHints | |
10 | +import typing as PTH # Python TypeHints | |
11 | 11 | from dataclasses import dataclass, KW_ONLY |
12 | 12 | from . import AIGR |
13 | -from .types import TypedParameter # Castle/AIGR types | |
13 | +from .aid import TypedParameter # Castle/AIGR type | |
14 | 14 | |
15 | 15 | __all__ = ['Event'] |
16 | 16 | |
17 | 17 | |
18 | 18 | |
19 | 19 | |
20 | -@dataclass # pragma: no mutate | |
20 | +@dataclass # pragma: no mutate | |
21 | 21 | class Event(AIGR): |
22 | 22 | """An event is like a (remote) function-call |
23 | 23 |
@@ -8,13 +8,13 @@ | ||
8 | 8 | """ |
9 | 9 | |
10 | 10 | from __future__ import annotations |
11 | -import typing as PTH # Python TypeHints | |
11 | +import typing as PTH # Python TypeHints | |
12 | 12 | from enum import Enum |
13 | 13 | from dataclasses import dataclass, KW_ONLY |
14 | 14 | from dataclasses import field as dc_field |
15 | 15 | from . import AIGR |
16 | 16 | from .events import Event |
17 | -from .types import TypedParameter # Castle/AIGR types | |
17 | +from .aid import TypedParameter # Castle/AIGR types | |
18 | 18 | |
19 | 19 | __all__ = ['ProtocolKind', 'Protocol', 'EventProtocol'] |
20 | 20 | # DataProtocol, StreamProtocol are added eventually |
@@ -30,14 +30,13 @@ | ||
30 | 30 | Data = 2 |
31 | 31 | Stream = 3 |
32 | 32 | |
33 | - | |
34 | 33 | @dataclass |
35 | 34 | class Protocol(AIGR): |
36 | 35 | """ .. note:: Use one of the subclasses -- Only Event is defined yet |
37 | 36 | .. todo:: Design: What is the `kind` self and the inherited ones are not the same? |
38 | 37 | overriding ProtocolKind.Unknown is always allowed |
39 | 38 | """ |
40 | - _BASE: PTH.ClassVar=None # pragma: no mutate | |
39 | + _BASE: PTH.ClassVar=None # pragma: no mutate | |
41 | 40 | |
42 | 41 | name: str |
43 | 42 | kind: ProtocolKind |
@@ -45,20 +44,32 @@ | ||
45 | 44 | typedParameters: PTH.Optional[PTH.Sequence[TypedParameter]]=() |
46 | 45 | |
47 | 46 | |
48 | -@dataclass # pragma: no mutate | |
47 | +@dataclass # pragma: no mutate | |
49 | 48 | class _RootProtocol(Protocol): |
50 | 49 | """This is the base protocol; it exist as we can't instantiate Protocol""" |
51 | 50 | |
52 | -baseProtocol = _RootProtocol("Protocol", kind=ProtocolKind.Unknown, based_on=None) # pragma: no mutate | |
51 | +baseProtocol = _RootProtocol("Protocol", kind=ProtocolKind.Unknown, based_on=None) # pragma: no mutate | |
53 | 52 | Protocol._BASE=baseProtocol |
54 | 53 | |
55 | -@dataclass # pragma: no mutate | |
54 | +@dataclass # pragma: no mutate | |
56 | 55 | class DataProtocol(Protocol): pass ### XXX ToDo (not exported) |
57 | -@dataclass # pragma: no mutate | |
56 | +@dataclass # pragma: no mutate | |
58 | 57 | class StreamProtocol(Protocol): pass ### XXX ToDo (not exported) |
59 | 58 | |
59 | +@dataclass # pragma: no mutate | |
60 | +class ProtocolWrapper(Protocol): | |
61 | + name: str="" | |
62 | + kind : ProtocolKind=None | |
63 | + _: KW_ONLY | |
64 | + arguments: PTH.Sequence[Argument]=() | |
60 | 65 | |
61 | -@dataclass # pragma: no mutate | |
66 | + def __post_init__(self): | |
67 | + if not self.kind: | |
68 | + self.kind = self.based_on.kind | |
69 | + if self.name == "": | |
70 | + self.name = f"Wrapper for {self.based_on.name}({self.arguments})" | |
71 | + | |
72 | +@dataclass # pragma: no mutate | |
62 | 73 | class EventProtocol(Protocol): |
63 | 74 | """An event-based protocol is basically a set of events. |
64 | 75 |
@@ -1,24 +0,0 @@ | ||
1 | -# (C) Albert Mietus, 2023. Part of Castle/CCastle project | |
2 | - | |
3 | -""" This file is based (a fork) on `../writers/CC2Cpy/Protocol.py`, | |
4 | - - the rendering part is removed | |
5 | - - the prefixed are gone | |
6 | - - better | |
7 | -TODO: update the CC2Cpy parts to use this generic AIGR layer | |
8 | -""" | |
9 | - | |
10 | -import typing as PTH # Python TypeHints | |
11 | -from enum import Enum | |
12 | -from dataclasses import dataclass, KW_ONLY | |
13 | -from . import AIGR | |
14 | - | |
15 | -# XXX__all__ = [ | |
16 | -@dataclass | |
17 | -class TypedParameter(AIGR): | |
18 | - """This is many a helper class/struct to combine a parameter: a name and an type""" | |
19 | - name: str | |
20 | - type: type | |
21 | - | |
22 | - | |
23 | -#not needed/used? TypedParameterTuple: PTH.TypeAlias = PTH.Sequence[TypedParameter] | |
24 | - |
@@ -1,7 +1,7 @@ | ||
1 | 1 | # (C) Albert Mietus, 2023. Part of Castle/CCastle project |
2 | 2 | |
3 | 3 | from castle.aigr import Event |
4 | -from castle.aigr.types import TypedParameter | |
4 | +from castle.aigr.aid import TypedParameter | |
5 | 5 | |
6 | 6 | |
7 | 7 | def test_0_Event_empty(): |
@@ -5,7 +5,8 @@ | ||
5 | 5 | |
6 | 6 | from castle.aigr import Protocol, ProtocolKind |
7 | 7 | from castle.aigr import Event, EventProtocol |
8 | -from castle.aigr.types import TypedParameter | |
8 | +from castle.aigr.aid import TypedParameter | |
9 | + | |
9 | 10 | |
10 | 11 | @pytest.fixture |
11 | 12 | def emptyProtocol(): |
@@ -58,6 +59,7 @@ | ||
58 | 59 | def test_protocol_with_Noparms(emptyProtocol): |
59 | 60 | assert emptyProtocol.typedParameters == () |
60 | 61 | |
62 | + | |
61 | 63 | def test_protocol_with_aParm(): |
62 | 64 | e = EventProtocol("With_a_parm", events=[], based_on=None, |
63 | 65 | typedParameters=[TypedParameter(name='p', type=float)]) |
@@ -65,6 +67,7 @@ | ||
65 | 67 | assert e.typedParameters[0].name == 'p' |
66 | 68 | assert e.typedParameters[0].type == float |
67 | 69 | |
70 | + | |
68 | 71 | def test_protocol_with_4Parms(): |
69 | 72 | e = EventProtocol("With_4_Parms", events=[], based_on=None, |
70 | 73 | typedParameters=( |
@@ -80,6 +83,5 @@ | ||
80 | 83 | assert (e.typedParameters[3].name, e.typedParameters[3].type) == ('p3', None) |
81 | 84 | |
82 | 85 | |
83 | -@pytest.mark.xfail(reason="Implementation is needed (test & prod)") | |
84 | -def test_inherit_base_withParms(): | |
85 | - assert False | |
86 | +#Note: for more complicated cases, see :file:`test_2c_WrappedProtocols.py` | |
87 | + |
@@ -0,0 +1,90 @@ | ||
1 | +# (C) Albert Mietus, 2023. Part of Castle/CCastle project | |
2 | + | |
3 | +""" With :file:`test_2b_protocol.py` most/all normal cases are verified. This file focus on **GenericProtocols**: | |
4 | + GenericProtocols are protocols that inherited from a protocol with parameters. We focus on EventProtocols, but it | |
5 | + will be entende to all protocols. | |
6 | + | |
7 | + .. code-block: Castle: | |
8 | + | |
9 | + protocol SlowStart(queue_max:int): EventProtocol ... | |
10 | + protocol SimpleSieve : SlowStart(1) ... | |
11 | + | |
12 | + * `SlowStart` is a **Generic Protocol** with a (1) parameter: the (initial) (max) size of the queue. | |
13 | + * `SimpleSieve` used that protocol, and set that parameter to 1. | |
14 | + | |
15 | + The parameter to SlowStart, in SimpleSieve, can be seen as a template-specialisation as it is called in C++ | |
16 | + | |
17 | + .. code-block: C++ | |
18 | + | |
19 | + // A C++ Template approximation of above: using classes not protocols ... | |
20 | + template <int queue_max> | |
21 | + class SlowStart {...} | |
22 | + | |
23 | + class SimpleSieve : SlowStart<1> { ...} | |
24 | + | |
25 | + In both examples the value ``1`` is filled in (aka hard-coded) into the implementation of SlowStart: Wherever | |
26 | + `queue_max` is used the value `1` is used -- as if the source always has had a `1`.... | |
27 | + | |
28 | + .. note:: The Castle syntax uses parentheses, as for normal (formal) arguments, not chevrons <angle brackets> as in C++ | |
29 | + | |
30 | + It read as-if SlowStart is instantiated (as in Python: calling ``SlowStart()``), but actually it is | |
31 | + **specialised*, by filling in the template/parentheses. The result is a not an instance of SlowStart, but a "new | |
32 | + Protocol, which kind-of inherits from SlowStart -- and SimpleSieve inherits from that one. | |
33 | + | |
34 | + This *syntax detail* is handled in the parser! | |
35 | + | |
36 | + In the AIGR, the specialised *SlowStart(1)* protocol is modeled by a ProtocolWrapper; which in placed in-between | |
37 | + (the generic) Slowstart and SimpleSieve. """ | |
38 | + | |
39 | +import logging; logger = logging.getLogger(__name__) | |
40 | +import pytest | |
41 | + | |
42 | +from castle.aigr import Protocol, ProtocolKind | |
43 | +from castle.aigr import Event, EventProtocol | |
44 | +from castle.aigr.aid import TypedParameter, Argument | |
45 | +from castle.aigr.protocols import ProtocolWrapper | |
46 | + | |
47 | +""" There are a few cases | |
48 | +///CastleCode | |
49 | + protocol Base(queue_max:int): EventProtocol | |
50 | + protocol Sub_a: Base(queue_max=1) # (a): named argument | |
51 | + protocol Sub_b: Base(1) # (b): positional arg | |
52 | + | |
53 | +""" | |
54 | + | |
55 | + | |
56 | +@pytest.fixture | |
57 | +def base(): | |
58 | + return EventProtocol("Base", events=[], typedParameters=[TypedParameter(name='queue_max', type=int)]) | |
59 | + | |
60 | +@pytest.fixture | |
61 | +def sub_a(base): | |
62 | + return EventProtocol("Sub_a", events=[], based_on=ProtocolWrapper(based_on=base, arguments=(Argument(name='queue_max', value=1),))) | |
63 | + | |
64 | +@pytest.fixture | |
65 | +def sub_b(base): | |
66 | + return EventProtocol("Sub_b", events=[], based_on=ProtocolWrapper(based_on=base, arguments=(Argument(value=1),))) | |
67 | + | |
68 | +def assert_GP_kind(base, sub): | |
69 | + assert sub.kind == base.kind | |
70 | + assert sub.based_on.kind == base.kind | |
71 | + assert sub.based_on.based_on is base | |
72 | + | |
73 | + | |
74 | +def test_GenericProtocol_kind_a(base, sub_a): | |
75 | + assert_GP_kind(base, sub_a) | |
76 | + | |
77 | +def test_GenericProtocol_kind_a(base, sub_b): | |
78 | + assert_GP_kind(base, sub_b) | |
79 | + | |
80 | + | |
81 | +def assert_GP_name(base, sub): | |
82 | + assert "Wrapper" in sub.based_on.name | |
83 | + assert "Base" in sub.based_on.name | |
84 | + | |
85 | +def test_GenericProtocol_name(base, sub_a): | |
86 | + assert_GP_name(base, sub_a) | |
87 | + assert "queue_max" in sub.based_on.name | |
88 | + | |
89 | +def test_GenericProtocol_name(base, sub_b): | |
90 | + assert_GP_name(base, sub_b) |
@@ -3,7 +3,7 @@ | ||
3 | 3 | import logging; logger = logging.getLogger(__name__) |
4 | 4 | import pytest |
5 | 5 | |
6 | -from castle.aigr.types import TypedParameter | |
6 | +from castle.aigr.aid import TypedParameter | |
7 | 7 | |
8 | 8 | from castle.aigr import EventProtocol, Event |
9 | 9 | from . import T_EventIndexes |
@@ -4,7 +4,7 @@ | ||
4 | 4 | import pytest |
5 | 5 | |
6 | 6 | from castle.aigr import EventProtocol, Event |
7 | -from castle.aigr.types import TypedParameter | |
7 | +from castle.aigr.aid import TypedParameter | |
8 | 8 | |
9 | 9 | from . import T_ProtocolDataStructures |
10 | 10 | from . import T_Protocol |