spacetime-dsl 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: spacetime-dsl
3
+ Version: 0.1.0
4
+ Summary: Compiler front-end for a DSL describing Lamport space-time diagrams
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: lark==1.3.1
8
+
9
+ ## Setup
10
+
11
+ Requires Python 3.10 or newer (developed and tested on 3.13).
12
+
13
+ Run `./setup.sh` to create the virtual environment and install the project.
14
+ It performs an editable install (`pip install --editable . --group dev`), so
15
+ `import spacetime` resolves to this checkout and the `spacetime` command is
16
+ available inside `.venv/bin/`. Dependencies live in `pyproject.toml`.
17
+
18
+ The environment lives **only** in `.venv/`. Do not create one in place at the
19
+ repository root (`python3 -m venv .`): `setup.sh` refuses to run in that
20
+ situation, and the stray `bin/`, `lib/`, `include/` and `pyvenv.cfg` would
21
+ shadow the real environment.
22
+
23
+ ## Running
24
+
25
+ Compile a DSL file and print its SVG space-time diagram to stdout:
26
+
27
+ ```bash
28
+ source .venv/bin/activate
29
+ spacetime example.txt # equivalently: python -m spacetime example.txt
30
+ ```
31
+
32
+ To inspect every compiler stage instead, add `--print-stages`. Only the
33
+ frontend runs: it prints the Lark parse tree, the intermediate `Ast`, and
34
+ the refined AST, each under a header, and no SVG is rendered:
35
+
36
+ ```bash
37
+ spacetime --print-stages example.txt
38
+ ```
39
+
40
+ Run the test suite:
41
+
42
+ ```bash
43
+ source .venv/bin/activate
44
+ pytest -q
45
+ ```
46
+
47
+ ## Specification
48
+
49
+ - [`spacetime/grammar.lark`](spacetime/grammar.lark) — the entire
50
+ specification: the concrete syntax, plus the semantic rules documented
51
+ as an attribute grammar in comments.
52
+ - [`docs/qualidade.md`](docs/qualidade.md) — language and grammar quality
53
+ evaluation (TP1, task 4), with metrics from `docs/metricas_gramatica.py`.
54
+
55
+ ## Linguagem para escrever lamport space-time diagrams
56
+
57
+ Example:
58
+ ```
59
+ Processes {
60
+ p
61
+ q
62
+ r
63
+ }
64
+
65
+ Messages {
66
+ request := "Request o que quiser"
67
+ ack := "Ack"
68
+ vote := "Votes for leader"
69
+ }
70
+
71
+ History {
72
+ m1 := Send(request) :: p -> q
73
+ Recv(m1) :: q
74
+
75
+ m2 := Send(ack) :: q -> p, q, r
76
+ Recv(m2) :: p, q
77
+
78
+ m3 := Send(request) :: r -> q
79
+ Recv(m3) :: q
80
+ }
81
+ ```
82
+
83
+
84
+ Results in:
85
+
86
+ ![Space time diagram](./docs/sample.svg)
@@ -0,0 +1,78 @@
1
+ ## Setup
2
+
3
+ Requires Python 3.10 or newer (developed and tested on 3.13).
4
+
5
+ Run `./setup.sh` to create the virtual environment and install the project.
6
+ It performs an editable install (`pip install --editable . --group dev`), so
7
+ `import spacetime` resolves to this checkout and the `spacetime` command is
8
+ available inside `.venv/bin/`. Dependencies live in `pyproject.toml`.
9
+
10
+ The environment lives **only** in `.venv/`. Do not create one in place at the
11
+ repository root (`python3 -m venv .`): `setup.sh` refuses to run in that
12
+ situation, and the stray `bin/`, `lib/`, `include/` and `pyvenv.cfg` would
13
+ shadow the real environment.
14
+
15
+ ## Running
16
+
17
+ Compile a DSL file and print its SVG space-time diagram to stdout:
18
+
19
+ ```bash
20
+ source .venv/bin/activate
21
+ spacetime example.txt # equivalently: python -m spacetime example.txt
22
+ ```
23
+
24
+ To inspect every compiler stage instead, add `--print-stages`. Only the
25
+ frontend runs: it prints the Lark parse tree, the intermediate `Ast`, and
26
+ the refined AST, each under a header, and no SVG is rendered:
27
+
28
+ ```bash
29
+ spacetime --print-stages example.txt
30
+ ```
31
+
32
+ Run the test suite:
33
+
34
+ ```bash
35
+ source .venv/bin/activate
36
+ pytest -q
37
+ ```
38
+
39
+ ## Specification
40
+
41
+ - [`spacetime/grammar.lark`](spacetime/grammar.lark) — the entire
42
+ specification: the concrete syntax, plus the semantic rules documented
43
+ as an attribute grammar in comments.
44
+ - [`docs/qualidade.md`](docs/qualidade.md) — language and grammar quality
45
+ evaluation (TP1, task 4), with metrics from `docs/metricas_gramatica.py`.
46
+
47
+ ## Linguagem para escrever lamport space-time diagrams
48
+
49
+ Example:
50
+ ```
51
+ Processes {
52
+ p
53
+ q
54
+ r
55
+ }
56
+
57
+ Messages {
58
+ request := "Request o que quiser"
59
+ ack := "Ack"
60
+ vote := "Votes for leader"
61
+ }
62
+
63
+ History {
64
+ m1 := Send(request) :: p -> q
65
+ Recv(m1) :: q
66
+
67
+ m2 := Send(ack) :: q -> p, q, r
68
+ Recv(m2) :: p, q
69
+
70
+ m3 := Send(request) :: r -> q
71
+ Recv(m3) :: q
72
+ }
73
+ ```
74
+
75
+
76
+ Results in:
77
+
78
+ ![Space time diagram](./docs/sample.svg)
@@ -0,0 +1,45 @@
1
+ # Build configuration for the `spacetime-dsl` distribution.
2
+ #
3
+ # The importable package is `spacetime/` (flat layout, repo root); the
4
+ # distribution is named `spacetime-dsl` only to avoid colliding with the
5
+ # unrelated `spacetime` project on PyPI. Nothing here is published yet.
6
+
7
+ [build-system]
8
+ requires = ["setuptools>=68"]
9
+ build-backend = "setuptools.build_meta"
10
+
11
+ [project]
12
+ name = "spacetime-dsl"
13
+ version = "0.1.0"
14
+ description = "Compiler front-end for a DSL describing Lamport space-time diagrams"
15
+ readme = "README.md"
16
+ requires-python = ">=3.10"
17
+ # Runtime dependencies only. `lark` is what spacetime/parser.py imports;
18
+ # the LarkAG fork that used to be listed here was never imported.
19
+ dependencies = [
20
+ "lark==1.3.1",
21
+ ]
22
+
23
+ [project.scripts]
24
+ # `spacetime FILE` — argv is passed to cli.main, whose return value becomes
25
+ # the process exit code (0 ok, 1 DSL or I/O error).
26
+ spacetime = "spacetime.cli:main"
27
+
28
+ [dependency-groups]
29
+ # Install with: pip install --editable . --group dev
30
+ dev = [
31
+ "pytest>=8",
32
+ ]
33
+
34
+ [tool.setuptools]
35
+ # Explicit: example.txt, tests/ and exercicios_random/ also sit at the repo
36
+ # root and must not be picked up by flat-layout auto-discovery.
37
+ packages = ["spacetime"]
38
+
39
+ [tool.setuptools.package-data]
40
+ # grammar.lark is read at runtime via Path(__file__).with_name("grammar.lark"),
41
+ # so it must ship inside the package or an installed copy cannot parse anything.
42
+ spacetime = ["grammar.lark"]
43
+
44
+ [tool.pytest.ini_options]
45
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,50 @@
1
+ """Space-Time DSL: compile a message history into a refined AST.
2
+
3
+ The public surface is re-exported here; see ``__all__``. Typical use is
4
+ :func:`compile_source` or :func:`compile_file`.
5
+ """
6
+
7
+ from .ast import (
8
+ Ast,
9
+ History,
10
+ HistoryRefined,
11
+ Ident,
12
+ MessageDeclaration,
13
+ RecvEvent,
14
+ RecvEventRefined,
15
+ RefinedAst,
16
+ SendEvent,
17
+ SendEventRefined,
18
+ SourceLocation,
19
+ )
20
+ from .builder import AstBuilder, build_ast
21
+ from .compiler import StageObserver, compile_file, compile_source
22
+ from .errors import DslError, ParseError, SemanticError
23
+ from .parser import parse
24
+ from .semantics import create_refined_ast
25
+ from .svg import render_svg
26
+
27
+ __all__ = [
28
+ "Ast",
29
+ "AstBuilder",
30
+ "DslError",
31
+ "History",
32
+ "HistoryRefined",
33
+ "Ident",
34
+ "MessageDeclaration",
35
+ "ParseError",
36
+ "RecvEvent",
37
+ "RecvEventRefined",
38
+ "RefinedAst",
39
+ "SemanticError",
40
+ "SendEvent",
41
+ "SendEventRefined",
42
+ "SourceLocation",
43
+ "StageObserver",
44
+ "build_ast",
45
+ "compile_file",
46
+ "compile_source",
47
+ "create_refined_ast",
48
+ "parse",
49
+ "render_svg",
50
+ ]
@@ -0,0 +1,10 @@
1
+ """Entry point for ``python -m spacetime <file>``.
2
+
3
+ Mirrors the ``spacetime`` console script declared in pyproject.toml; both
4
+ delegate to :func:`spacetime.cli.main`, whose return value is the exit code.
5
+ """
6
+
7
+ from .cli import main
8
+
9
+ if __name__ == "__main__":
10
+ raise SystemExit(main())
@@ -0,0 +1,243 @@
1
+ """Syntax tree data structures for the Space-Time DSL.
2
+
3
+ The compiler works with two tree shapes:
4
+
5
+ ``Ast``
6
+ Raw output of :mod:`spacetime.builder`. Identifiers keep the
7
+ :class:`SourceLocation` needed to report errors, and message payloads
8
+ stay attached to their declarations.
9
+
10
+ ``RefinedAst``
11
+ Output of :mod:`spacetime.semantics`. Names are resolved to plain
12
+ strings, event names and message declarations are inlined into the
13
+ events that use them, and every event has a temporal position.
14
+
15
+ All nodes are frozen dataclasses: building a tree never mutates an existing
16
+ node, and callers may treat nodes as immutable values. Lists are still
17
+ mutable by Python semantics; treat them as read-only after construction.
18
+ """
19
+
20
+ from dataclasses import dataclass
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class SourceLocation:
25
+ """A 1-based position in the DSL source.
26
+
27
+ Invariants:
28
+ ``line >= 1`` and ``column >= 1``. The builder falls back to
29
+ ``SourceLocation(1, 1)`` when Lark did not attach position metadata
30
+ to a rule, so a location may be approximate but is never absent.
31
+
32
+ Failure modes:
33
+ None: this is a passive value and performs no validation.
34
+ """
35
+
36
+ line: int
37
+ column: int
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class Ident:
42
+ """A source identifier together with the position where it appeared.
43
+
44
+ Used for process names, message names and values, event names, and
45
+ references between them.
46
+
47
+ Invariants:
48
+ ``name`` matches the grammar's ``IDENTIFIER`` token: it starts with
49
+ a lowercase ASCII letter and continues with letters, digits, or
50
+ underscores.
51
+
52
+ Failure modes:
53
+ None. Whether the name is declared is checked in semantics.
54
+ """
55
+
56
+ name: str
57
+ location: SourceLocation
58
+
59
+
60
+ @dataclass(frozen=True)
61
+ class MessageDeclaration:
62
+ """A ``name := "value"`` entry in a ``Messages`` block.
63
+
64
+ Invariants:
65
+ ``value`` is the payload text with the surrounding double quotes
66
+ removed. No escape processing is applied, so it may contain any
67
+ character the grammar allows between the quotes (no unescaped
68
+ double quote and no line terminator).
69
+
70
+ Failure modes:
71
+ Duplicate names are rejected while building the block, with a
72
+ :class:`spacetime.errors.SemanticError` at the second declaration.
73
+ """
74
+
75
+ name: Ident
76
+ value: str
77
+
78
+
79
+ @dataclass(frozen=True)
80
+ class SendEvent:
81
+ """A named send event from a ``History`` block.
82
+
83
+ ``name`` is the event identifier bound with ``:=`` (for example
84
+ ``m1``), not the message name. ``processes_to_ids`` may list several
85
+ recipients, which models a multicast as a single event.
86
+
87
+ Invariants:
88
+ - ``name`` is unique among send events; duplicates are rejected in
89
+ semantics.
90
+ - ``processes_to_ids`` is non-empty because the grammar requires at
91
+ least one recipient.
92
+ - Referenced message and process names may still be undeclared at
93
+ this stage.
94
+
95
+ Failure modes:
96
+ No validation here. :func:`spacetime.semantics.create_refined_ast`
97
+ raises :class:`spacetime.errors.SemanticError` for undeclared
98
+ messages or processes and for duplicate event names.
99
+ """
100
+
101
+ name: Ident
102
+ message_id: Ident
103
+ process_from_id: Ident
104
+ processes_to_ids: list[Ident]
105
+
106
+
107
+ @dataclass(frozen=True)
108
+ class RecvEvent:
109
+ """A ``Recv(event) :: p, q`` entry in a ``History`` block.
110
+
111
+ A multicast receive stays one event; every listed process is recorded
112
+ in ``process_ids``.
113
+
114
+ Invariants:
115
+ - ``send_event_ref`` names a send event that appears *earlier* in
116
+ the same history; forward references are rejected.
117
+ - ``process_ids`` is non-empty and every entry must be a recipient
118
+ of the referenced send event.
119
+
120
+ Failure modes:
121
+ Declaration order and recipient membership are validated in
122
+ :func:`spacetime.semantics.create_refined_ast`.
123
+ """
124
+
125
+ send_event_ref: Ident
126
+ process_ids: list[Ident]
127
+
128
+
129
+ @dataclass(frozen=True)
130
+ class History:
131
+ """The ordered event list of a ``History`` block.
132
+
133
+ Invariants:
134
+ - Order is source order, the only notion of time in the DSL; there
135
+ is no explicit timestamp.
136
+ - An empty list is valid and represents a program without events.
137
+
138
+ Failure modes:
139
+ None at this layer; cross-event rules live in semantics.
140
+ """
141
+
142
+ events: list[SendEvent | RecvEvent]
143
+
144
+
145
+ @dataclass(frozen=True)
146
+ class Ast:
147
+ """Raw syntax tree produced by :func:`spacetime.builder.build_ast`.
148
+
149
+ Invariants:
150
+ - ``process_declarations`` preserves source order and contains no
151
+ duplicate names.
152
+ - ``message_declarations`` preserves source order and contains no
153
+ duplicate names.
154
+ - Absent blocks become empty lists, so an empty program is a valid
155
+ ``Ast`` with no declarations and no history.
156
+
157
+ Failure modes:
158
+ Constructing an ``Ast`` directly performs no checks; violations are
159
+ only detected when semantics runs.
160
+ """
161
+
162
+ process_declarations: list[Ident]
163
+ message_declarations: list[MessageDeclaration]
164
+ history: History
165
+
166
+
167
+ @dataclass(frozen=True)
168
+ class SendEventRefined:
169
+ """A send event after name resolution.
170
+
171
+ ``message`` is the payload string from the matching declaration and
172
+ ``message_name`` is the declared message name itself (kept for the SVG
173
+ caption legend, which decodes arrow colors by message name). The
174
+ event's own name is intentionally dropped.
175
+
176
+ Invariants:
177
+ - ``temporal_pos`` is the 0-based index of the event in
178
+ ``HistoryRefined.events``.
179
+ - ``message_name`` names a declaration in the ``Messages`` block.
180
+ - ``message_type`` is the 0-based declaration index of the message
181
+ in the ``Messages`` block.
182
+ - ``process_from_id`` and every entry in ``processes_to_ids`` name
183
+ declared processes.
184
+ - ``processes_to_ids`` is non-empty and may repeat a process if the
185
+ source did.
186
+
187
+ Failure modes:
188
+ None; the instance only exists after semantic validation succeeded.
189
+ """
190
+
191
+ temporal_pos: int
192
+ message: str
193
+ message_name: str
194
+ message_type: int
195
+ process_from_id: str
196
+ processes_to_ids: list[str]
197
+
198
+
199
+ @dataclass(frozen=True)
200
+ class RecvEventRefined:
201
+ """A receive event after name resolution.
202
+
203
+ Invariants:
204
+ - ``related_send_event`` is the same ``SendEventRefined`` instance
205
+ stored earlier in the refined history.
206
+ - ``temporal_pos`` is greater than the related send's position.
207
+ - ``process_ids`` is a non-empty subset of
208
+ ``related_send_event.processes_to_ids``.
209
+
210
+ Failure modes:
211
+ None; the instance only exists after semantic validation succeeded.
212
+ """
213
+
214
+ temporal_pos: int
215
+ related_send_event: SendEventRefined
216
+ process_ids: list[str]
217
+
218
+
219
+ @dataclass(frozen=True)
220
+ class HistoryRefined:
221
+ """A resolved history whose events are ordered by ``temporal_pos``."""
222
+
223
+ events: list[SendEventRefined | RecvEventRefined]
224
+
225
+
226
+ @dataclass(frozen=True)
227
+ class RefinedAst:
228
+ """Fully resolved program returned by
229
+ :func:`spacetime.compiler.compile_source`.
230
+
231
+ Message declarations are not kept: payloads are inlined in
232
+ ``SendEventRefined.message``.
233
+
234
+ Invariants:
235
+ - ``process_declarations`` preserves source order as plain names.
236
+ - ``history.events`` is ordered by ``temporal_pos``.
237
+
238
+ Failure modes:
239
+ None; constructing one directly is not validated.
240
+ """
241
+
242
+ process_declarations: list[str]
243
+ history: HistoryRefined