@openpowershift/logic-diagram-language 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/examples.d.ts +2 -0
- package/dist/examples.js +469 -0
- package/dist/export-image.d.ts +10 -0
- package/dist/export-image.js +47 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.html +13 -0
- package/dist/index.js +18 -0
- package/dist/parser/ast.d.ts +118 -0
- package/dist/parser/ast.js +79 -0
- package/dist/parser/index.d.ts +3 -0
- package/dist/parser/index.js +2 -0
- package/dist/parser/parser.d.ts +2 -0
- package/dist/parser/parser.js +635 -0
- package/dist/renderer/astar-router.d.ts +16 -0
- package/dist/renderer/astar-router.js +532 -0
- package/dist/renderer/gates.d.ts +19 -0
- package/dist/renderer/gates.js +92 -0
- package/dist/renderer/graph.d.ts +31 -0
- package/dist/renderer/graph.js +403 -0
- package/dist/renderer/layout.d.ts +76 -0
- package/dist/renderer/layout.js +3121 -0
- package/dist/renderer/math-renderer.d.ts +16 -0
- package/dist/renderer/math-renderer.js +119 -0
- package/dist/renderer/svg-renderer.d.ts +3 -0
- package/dist/renderer/svg-renderer.js +599 -0
- package/dist/renderer/wires.d.ts +5 -0
- package/dist/renderer/wires.js +12 -0
- package/dist/theme/themes.d.ts +58 -0
- package/dist/theme/themes.js +104 -0
- package/docs/api.adoc +196 -0
- package/docs/ldl-for-llms.md +163 -0
- package/docs/user-guide.adoc +318 -0
- package/package.json +78 -0
- package/spec/render.sh +22 -0
- package/spec/sections/attributes.adoc +80 -0
- package/spec/sections/connections.adoc +39 -0
- package/spec/sections/examples.adoc +212 -0
- package/spec/sections/expressions.adoc +182 -0
- package/spec/sections/file-extension.adoc +5 -0
- package/spec/sections/function-blocks.adoc +120 -0
- package/spec/sections/grammar.adoc +64 -0
- package/spec/sections/hyperlinks.adoc +18 -0
- package/spec/sections/introduction.adoc +16 -0
- package/spec/sections/layout-rules.adoc +491 -0
- package/spec/sections/lexical-conventions.adoc +68 -0
- package/spec/sections/objects.adoc +31 -0
- package/spec/sections/options.adoc +146 -0
- package/spec/sections/ports.adoc +77 -0
- package/spec/sections/rendering-contract.adoc +11 -0
- package/spec/sections/revision-history.adoc +9 -0
- package/spec/sections/styling.adoc +83 -0
- package/spec/sections/svg-symbol-specification.adoc +149 -0
- package/spec/sections/symbol-definitions.adoc +143 -0
- package/spec/sections/templates.adoc +31 -0
- package/spec/spec.adoc +49 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
== Options
|
|
2
|
+
|
|
3
|
+
*Options* are document-level directives that control rendering behaviour.
|
|
4
|
+
They MUST appear before any expressions, object declarations, or style blocks.
|
|
5
|
+
|
|
6
|
+
=== Syntax
|
|
7
|
+
|
|
8
|
+
[source]
|
|
9
|
+
OPTION NAME = VALUE
|
|
10
|
+
|
|
11
|
+
Options are set once per document. If the same option appears multiple times,
|
|
12
|
+
the last occurrence wins.
|
|
13
|
+
|
|
14
|
+
=== Available Options
|
|
15
|
+
|
|
16
|
+
[cols="1m,2m,2m,1m"]
|
|
17
|
+
|===
|
|
18
|
+
| Option | Values | Description | Default
|
|
19
|
+
|
|
20
|
+
| INVERSION
|
|
21
|
+
| GATES
|
|
22
|
+
| NOT operations render as separate NOT gate symbols (current behaviour)
|
|
23
|
+
| GATES
|
|
24
|
+
|
|
25
|
+
| INVERSION
|
|
26
|
+
| BUBBLES
|
|
27
|
+
| NOT operations render as inversion circles (bubbles) on the input port of the downstream gate
|
|
28
|
+
| --
|
|
29
|
+
|
|
30
|
+
| PORT_STYLE
|
|
31
|
+
| CIRCLE
|
|
32
|
+
| Port markers are rendered as circles (r=3)
|
|
33
|
+
| CIRCLE
|
|
34
|
+
|
|
35
|
+
| PORT_STYLE
|
|
36
|
+
| SQUARE
|
|
37
|
+
| Port markers are rendered as squares (5×5)
|
|
38
|
+
| --
|
|
39
|
+
|
|
40
|
+
| GATE_INPUT_STYLE
|
|
41
|
+
| EXPAND
|
|
42
|
+
| Multi-input gates expand vertically to accommodate all input ports with even spacing
|
|
43
|
+
| EXPAND
|
|
44
|
+
|
|
45
|
+
| GATE_INPUT_STYLE
|
|
46
|
+
| BARS
|
|
47
|
+
| Multi-input gates remain at base size (2-input height). Inputs beyond the first two connect via vertical input bars on the left side of the gate body
|
|
48
|
+
| --
|
|
49
|
+
|
|
50
|
+
| LABEL_STYLE
|
|
51
|
+
| SIDE
|
|
52
|
+
| Input labels appear to the left of the input port; output labels to the right (current behaviour)
|
|
53
|
+
| SIDE
|
|
54
|
+
|
|
55
|
+
| LABEL_STYLE
|
|
56
|
+
| ABOVE_BELOW
|
|
57
|
+
| Input labels appear above/below the port, closer to the gate body
|
|
58
|
+
| --
|
|
59
|
+
|
|
60
|
+
| OUTPUT_ORDER
|
|
61
|
+
| AUTO
|
|
62
|
+
| Output ports are reordered vertically by the position of the gate that drives them, so output wires fan out without crossing (the barycentre method already used for inputs)
|
|
63
|
+
| AUTO
|
|
64
|
+
|
|
65
|
+
| OUTPUT_ORDER
|
|
66
|
+
| DECLARATION
|
|
67
|
+
| Output ports are stacked top-to-bottom in the order the outputs are declared (O1, O2, …), each aligned to its source where possible
|
|
68
|
+
| --
|
|
69
|
+
|
|
70
|
+
| INPUT_ORDER
|
|
71
|
+
| AUTO
|
|
72
|
+
| Input ports are reordered vertically using the Sugiyama barycentre method to minimise wire crossings
|
|
73
|
+
| AUTO
|
|
74
|
+
|
|
75
|
+
| INPUT_ORDER
|
|
76
|
+
| DECLARATION
|
|
77
|
+
| Input ports keep their declared (natural-sorted) order; gate rows still follow from that fixed order
|
|
78
|
+
| --
|
|
79
|
+
|
|
80
|
+
| COMPACTNESS
|
|
81
|
+
| NORMAL
|
|
82
|
+
| Standard spacing on both axes
|
|
83
|
+
| NORMAL
|
|
84
|
+
|
|
85
|
+
| COMPACTNESS
|
|
86
|
+
| COMPACT_V
|
|
87
|
+
| Tighter vertical (row) spacing for a denser diagram (still respects minimum gaps, so wires never overlap)
|
|
88
|
+
| --
|
|
89
|
+
|
|
90
|
+
| COMPACTNESS
|
|
91
|
+
| COMPACT_H
|
|
92
|
+
| Tighter horizontal (column) spacing
|
|
93
|
+
| --
|
|
94
|
+
|
|
95
|
+
| COMPACTNESS
|
|
96
|
+
| COMPACT
|
|
97
|
+
| Tighter spacing on both axes (COMPACT_V + COMPACT_H)
|
|
98
|
+
| --
|
|
99
|
+
|
|
100
|
+
| COMPACTNESS
|
|
101
|
+
| SPACIOUS
|
|
102
|
+
| Looser vertical spacing for an airier diagram
|
|
103
|
+
| --
|
|
104
|
+
|
|
105
|
+
| COMPACTNESS
|
|
106
|
+
| _factors_
|
|
107
|
+
| Explicit spacing factors `vertical,horizontal` (e.g. `70,70` or `[60,60]`); a value > 3 is a percentage (70 → 0.70), otherwise a raw factor. A single value applies to both axes.
|
|
108
|
+
| --
|
|
109
|
+
|
|
110
|
+
| COLUMN_SPACING
|
|
111
|
+
| ADAPTIVE
|
|
112
|
+
| Size each inter-column gap to its content (gate widths + the fan-in channels that column needs), so simple columns pack tighter and the diagram is materially narrower. Never wider than UNIFORM, and validated so it introduces no dogleg, entrance violation, overlap or crossing.
|
|
113
|
+
| ADAPTIVE
|
|
114
|
+
|
|
115
|
+
| COLUMN_SPACING
|
|
116
|
+
| UNIFORM
|
|
117
|
+
| Fixed column pitch — every inter-column gap is the same width.
|
|
118
|
+
| --
|
|
119
|
+
|
|
120
|
+
| MARGIN
|
|
121
|
+
| _integer px_
|
|
122
|
+
| Blank border (in pixels) drawn around the diagram content in the rendered/exported SVG. Labels are already inside the content bounds, so this is a pure outer frame.
|
|
123
|
+
| 8
|
|
124
|
+
|
|
125
|
+
| WIRE_LABEL_LEADER
|
|
126
|
+
| TRUE / FALSE
|
|
127
|
+
| Draw a dashed leader line (with an endpoint dot on the wire) from each consumed-intermediate net label to the net it names. The label is seated a readable distance off the wire so the connector is visible; useful where a wide label cannot sit directly on a congested fan-out junction.
|
|
128
|
+
| TRUE
|
|
129
|
+
|===
|
|
130
|
+
|
|
131
|
+
=== Examples
|
|
132
|
+
|
|
133
|
+
[source]
|
|
134
|
+
OPTION INVERSION = BUBBLES
|
|
135
|
+
OPTION PORT_STYLE = SQUARE
|
|
136
|
+
OPTION GATE_INPUT_STYLE = BARS
|
|
137
|
+
|
|
138
|
+
An LDL file may combine options freely:
|
|
139
|
+
|
|
140
|
+
[source]
|
|
141
|
+
OPTION INVERSION = BUBBLES
|
|
142
|
+
OPTION GATE_INPUT_STYLE = BARS
|
|
143
|
+
I1.Name = "Start"
|
|
144
|
+
I2.Name = "Stop"
|
|
145
|
+
I3.Name = "Reset"
|
|
146
|
+
O1 = I1 AND I2 AND NOT I3
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
== Ports
|
|
2
|
+
|
|
3
|
+
A *port* is a named connection point on an object. Ports are either inputs, outputs, or bidirectional.
|
|
4
|
+
|
|
5
|
+
=== Port Types
|
|
6
|
+
|
|
7
|
+
[cols="1m,3m,3m"]
|
|
8
|
+
|===
|
|
9
|
+
| Keyword | Direction | Semantics
|
|
10
|
+
| `INPUT` | Inward | Receives a signal
|
|
11
|
+
| `OUTPUT` | Outward | Emits a signal
|
|
12
|
+
| `BIDI` | Both | Bidirectional (for Bus or analog)
|
|
13
|
+
|===
|
|
14
|
+
|
|
15
|
+
=== Port Declarations
|
|
16
|
+
|
|
17
|
+
Ports are declared within a symbol definition (see <<symbol-definitions>>) or explicitly on an object:
|
|
18
|
+
|
|
19
|
+
[literal]
|
|
20
|
+
....
|
|
21
|
+
SVT#1.INPUT = A OR B
|
|
22
|
+
SVT#1.OUTPUT = C
|
|
23
|
+
....
|
|
24
|
+
|
|
25
|
+
The dot notation `OBJECT.PORT` references a specific port.
|
|
26
|
+
|
|
27
|
+
=== Port Metadata
|
|
28
|
+
|
|
29
|
+
Ports may carry human-readable metadata using `.Name` and `.Description` attributes:
|
|
30
|
+
|
|
31
|
+
[literal]
|
|
32
|
+
....
|
|
33
|
+
I1.Name = "CBQ 00 Open"
|
|
34
|
+
I1.Description = "(BI 3.1)"
|
|
35
|
+
O1.Name = "Output"
|
|
36
|
+
O1.Description = "(BO 3.2)"
|
|
37
|
+
....
|
|
38
|
+
|
|
39
|
+
* `.Name` -- A human-readable label for the port. Displayed as the primary label on the diagram when the labels layer is active.
|
|
40
|
+
* `.Description` -- Supplementary text (e.g. a signal reference). Displayed as a secondary annotation when the labels layer is active.
|
|
41
|
+
|
|
42
|
+
When labels are **not** shown, the bare identifier (e.g. `I1`, `O1`) is used as a compact reference. When labels **are** shown, the `.Name` replaces the identifier on the diagram, and `.Description` is shown in a smaller font below it.
|
|
43
|
+
|
|
44
|
+
=== Implicit Ports
|
|
45
|
+
|
|
46
|
+
For built-in gate symbols, ports are implicit based on position:
|
|
47
|
+
|
|
48
|
+
* **AND**, **NAND**, **OR**, **NOR**, **XOR**, **XNOR**: Left-side inputs, right-side output.
|
|
49
|
+
* **NOT**: Left-side input, right-side output.
|
|
50
|
+
|
|
51
|
+
The number of inputs on gate symbols is determined by the expression arity.
|
|
52
|
+
|
|
53
|
+
=== Port Naming Convention
|
|
54
|
+
|
|
55
|
+
When the expression `CBFPS = A AND B` is given, the renderer creates an AND gate with two implicit inputs labeled `A` and `B` and one implicit output labeled `CBFPS`.
|
|
56
|
+
|
|
57
|
+
When a port has a `.Name` attribute, that name replaces the identifier as the display label:
|
|
58
|
+
|
|
59
|
+
[literal]
|
|
60
|
+
....
|
|
61
|
+
I1.Name = "Close Switch"
|
|
62
|
+
O1 = I1 AND I2
|
|
63
|
+
....
|
|
64
|
+
|
|
65
|
+
In this case the input port is shown as "Close Switch" rather than `I1`.
|
|
66
|
+
|
|
67
|
+
=== Port Style Attribute
|
|
68
|
+
|
|
69
|
+
An individual port's marker shape can be overridden from the document default set by `OPTION PORT_STYLE`:
|
|
70
|
+
|
|
71
|
+
[literal]
|
|
72
|
+
....
|
|
73
|
+
I1.Style = SQUARE
|
|
74
|
+
O1.Style = CIRCLE
|
|
75
|
+
....
|
|
76
|
+
|
|
77
|
+
When set, this overrides the `OPTION PORT_STYLE` for that specific port. Valid values are `CIRCLE` and `SQUARE`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
== Rendering Contract
|
|
2
|
+
|
|
3
|
+
The renderer (TypeScript toolchain) is responsible for:
|
|
4
|
+
|
|
5
|
+
. Parsing LDL source.
|
|
6
|
+
. Instantiating objects and resolving expressions into gate networks.
|
|
7
|
+
. Loading SVG symbols and performing attribute substitution.
|
|
8
|
+
. Routing wires between ports.
|
|
9
|
+
. Emitting SVG and PDF output.
|
|
10
|
+
|
|
11
|
+
The renderer is NOT specified by this document; only the language it consumes.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
== Revision History
|
|
2
|
+
|
|
3
|
+
[cols="1m,3m,5m"]
|
|
4
|
+
|===
|
|
5
|
+
| Version | Date | Description
|
|
6
|
+
| 0.1.0-draft | 2026-06-13 | Initial specification
|
|
7
|
+
| 0.2.0-draft | 2026-06-13 | Added port metadata (.Name, .Description), layout rules, layer visibility
|
|
8
|
+
| 0.3.0-draft | 2026-06-14 | Added OPTION directives, inversion bubbles, input bars, port styles, minimum dogleg height, wire-wire crossing avoidance
|
|
9
|
+
|===
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
== Styling
|
|
2
|
+
|
|
3
|
+
=== ID-Based Selection
|
|
4
|
+
|
|
5
|
+
Every object declared with `#ID` can be styled via CSS:
|
|
6
|
+
|
|
7
|
+
[literal]
|
|
8
|
+
....
|
|
9
|
+
AND#G1 { stroke: red; }
|
|
10
|
+
SVT#1 { fill: yellow; }
|
|
11
|
+
....
|
|
12
|
+
|
|
13
|
+
=== CSS Injection
|
|
14
|
+
|
|
15
|
+
Style rules are provided in the LDL source:
|
|
16
|
+
|
|
17
|
+
[literal]
|
|
18
|
+
....
|
|
19
|
+
STYLE
|
|
20
|
+
#G1 { stroke: red; stroke-width: 3; }
|
|
21
|
+
#T2 { fill: #ffffcc; }
|
|
22
|
+
END STYLE
|
|
23
|
+
....
|
|
24
|
+
|
|
25
|
+
Or loaded from an external stylesheet:
|
|
26
|
+
|
|
27
|
+
[literal]
|
|
28
|
+
....
|
|
29
|
+
STYLESHEET "path/to/styles.css"
|
|
30
|
+
....
|
|
31
|
+
|
|
32
|
+
=== Default CSS Classes
|
|
33
|
+
|
|
34
|
+
The renderer MUST apply the following CSS classes:
|
|
35
|
+
|
|
36
|
+
[cols="1m,3m"]
|
|
37
|
+
|===
|
|
38
|
+
| Class | Applied to
|
|
39
|
+
| `ldl-symbol` | Every symbol `<g>` element
|
|
40
|
+
| `ldl-port` | Every port marker
|
|
41
|
+
| `ldl-input` | Input port markers
|
|
42
|
+
| `ldl-output` | Output port markers
|
|
43
|
+
| `ldl-attribute` | Attribute text elements
|
|
44
|
+
| `ldl-id` | ID/identifier label text elements (ids layer)
|
|
45
|
+
| `ldl-label` | `.Name` and `.Description` text elements (labels layer)
|
|
46
|
+
| `ldl-name` | Port `.Name` text elements
|
|
47
|
+
| `ldl-description` | Port `.Description` text elements
|
|
48
|
+
| `ldl-wire` | Wire/connection path elements
|
|
49
|
+
| `ldl-gate-and` | AND gate symbols
|
|
50
|
+
| `ldl-gate-or` | OR gate symbols
|
|
51
|
+
| `ldl-gate-not` | NOT gate symbols
|
|
52
|
+
| `ldl-bubble` | Inversion circle on a gate input port (BUBBLES mode)
|
|
53
|
+
| `ldl-input-bar` | Vertical input bar on a multi-input gate (BARS mode)
|
|
54
|
+
| `ldl-bar-stub` | Horizontal stub connecting a bar tap to the gate body (BARS mode)
|
|
55
|
+
| `ldl-port-circle` | Circular port marker (PORT_STYLE = CIRCLE)
|
|
56
|
+
| `ldl-port-square` | Square port marker (PORT_STYLE = SQUARE)
|
|
57
|
+
| `ldl-math` | Inline TeX math `<g>` group within a label
|
|
58
|
+
|===
|
|
59
|
+
|
|
60
|
+
Selectors combining these classes with `#ID` provide fine-grained control:
|
|
61
|
+
|
|
62
|
+
[source,css]
|
|
63
|
+
----
|
|
64
|
+
.ldl-gate-and#G1 { fill: blue; }
|
|
65
|
+
.ldl-port.ldl-output { fill: green; }
|
|
66
|
+
----
|
|
67
|
+
|
|
68
|
+
=== Layer Visibility
|
|
69
|
+
|
|
70
|
+
Layers are toggled via CSS on the root `<svg>` element:
|
|
71
|
+
|
|
72
|
+
[source,css]
|
|
73
|
+
----
|
|
74
|
+
/* Show labels, hide bare IDs */
|
|
75
|
+
svg.ldl-layer-labels .ldl-label { display: block; }
|
|
76
|
+
svg.ldl-layer-labels .ldl-id { display: none; }
|
|
77
|
+
|
|
78
|
+
/* Hide labels, show bare IDs */
|
|
79
|
+
svg:not(.ldl-layer-labels) .ldl-label { display: none; }
|
|
80
|
+
svg:not(.ldl-layer-labels) .ldl-id { display: block; }
|
|
81
|
+
----
|
|
82
|
+
|
|
83
|
+
The renderer provides UI controls to toggle these layers.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
== SVG Symbol Specification
|
|
2
|
+
|
|
3
|
+
=== Structure
|
|
4
|
+
|
|
5
|
+
Each SVG symbol file MUST contain:
|
|
6
|
+
|
|
7
|
+
. A `<g>` element with class `ldl-symbol`.
|
|
8
|
+
. One `<circle>` or `<rect>` per port, with class `ldl-port` and a `data-port` attribute matching the port name.
|
|
9
|
+
. Optional `<text>` elements with `{{PLACEHOLDER}}` for attribute substitution.
|
|
10
|
+
. A `<text>` element with `{{ID}}` to show the object's identifier label.
|
|
11
|
+
|
|
12
|
+
=== Port Markers
|
|
13
|
+
|
|
14
|
+
Port markers indicate where connections attach:
|
|
15
|
+
|
|
16
|
+
[literal]
|
|
17
|
+
....
|
|
18
|
+
<circle class="ldl-port ldl-input" data-port="in" cx="0" cy="20" r="3"/>
|
|
19
|
+
<circle class="ldl-port ldl-output" data-port="out" cx="80" cy="20" r="3"/>
|
|
20
|
+
....
|
|
21
|
+
|
|
22
|
+
=== Attribute Placeholders
|
|
23
|
+
|
|
24
|
+
[literal]
|
|
25
|
+
....
|
|
26
|
+
<text class="ldl-attribute" x="40" y="30">{{PICKUP}}</text>
|
|
27
|
+
<text class="ldl-attribute" x="40" y="45">{{DROPOUT}}</text>
|
|
28
|
+
....
|
|
29
|
+
|
|
30
|
+
=== ID Label Placeholder
|
|
31
|
+
|
|
32
|
+
[literal]
|
|
33
|
+
....
|
|
34
|
+
<text class="ldl-id" x="40" y="10">{{ID}}</text>
|
|
35
|
+
....
|
|
36
|
+
|
|
37
|
+
=== Name and Description Labels
|
|
38
|
+
|
|
39
|
+
For input and output ports, the renderer generates labels from `.Name` and `.Description` attributes:
|
|
40
|
+
|
|
41
|
+
[literal]
|
|
42
|
+
....
|
|
43
|
+
<text class="ldl-label ldl-name" x="0" y="-5">{{NAME}}</text>
|
|
44
|
+
<text class="ldl-label ldl-description" x="0" y="12" font-size="9">{{DESCRIPTION}}</text>
|
|
45
|
+
....
|
|
46
|
+
|
|
47
|
+
Elements with class `ldl-label` are part of the *labels layer* and controlled by the `ldl-layer-labels` CSS class.
|
|
48
|
+
Elements with class `ldl-id` are part of the *ids layer* and controlled by the `ldl-layer-ids` CSS class.
|
|
49
|
+
|
|
50
|
+
=== SVG File Example
|
|
51
|
+
|
|
52
|
+
A minimal AND gate symbol (`symbols/and.svg`):
|
|
53
|
+
|
|
54
|
+
[source,xml]
|
|
55
|
+
----
|
|
56
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 60">
|
|
57
|
+
<g class="ldl-symbol">
|
|
58
|
+
<path d="M0,0 L40,0 A30,30 0 0,1 40,60 L0,60 Z"
|
|
59
|
+
fill="white" stroke="black" stroke-width="2"/>
|
|
60
|
+
<text class="ldl-id" x="20" y="-5">{{ID}}</text>
|
|
61
|
+
<circle class="ldl-port ldl-input" data-port="in1" cx="0" cy="15" r="3"/>
|
|
62
|
+
<circle class="ldl-port ldl-input" data-port="in2" cx="0" cy="45" r="3"/>
|
|
63
|
+
<circle class="ldl-port ldl-output" data-port="out" cx="100" cy="30" r="3"/>
|
|
64
|
+
</g>
|
|
65
|
+
</svg>
|
|
66
|
+
----
|
|
67
|
+
|
|
68
|
+
=== Inversion Bubble Rendering
|
|
69
|
+
|
|
70
|
+
When `OPTION INVERSION = BUBBLES` is set, each bubbled port renders as an open circle with radius `BUBBLE_R` (4px).
|
|
71
|
+
|
|
72
|
+
==== Input-Side Bubble
|
|
73
|
+
|
|
74
|
+
An input-side bubble (on a gate input port or output node's input port) renders as:
|
|
75
|
+
|
|
76
|
+
. An open circle (stroke, no fill) with radius `BUBBLE_R`.
|
|
77
|
+
. Position: centered on the port Y, offset left of the gate body edge. The bubbled input port shifts left by `BUBBLE_R * 2` so that the bubble sits between the wire endpoint and the gate body.
|
|
78
|
+
. The wire connects to the left edge of the bubble.
|
|
79
|
+
. The bubble's right edge touches the gate body edge.
|
|
80
|
+
|
|
81
|
+
SVG example (input-side bubble on a gate):
|
|
82
|
+
|
|
83
|
+
[source,xml]
|
|
84
|
+
----
|
|
85
|
+
<circle class="ldl-bubble ldl-input" data-port="in_1"
|
|
86
|
+
cx="-8" cy="30" r="4"
|
|
87
|
+
fill="none" stroke="#2c3e50" stroke-width="1.5"/>
|
|
88
|
+
----
|
|
89
|
+
|
|
90
|
+
==== Output-Side Bubble
|
|
91
|
+
|
|
92
|
+
An output-side bubble (on a gate output port) renders as:
|
|
93
|
+
|
|
94
|
+
. An open circle (stroke, no fill) with radius `BUBBLE_R`.
|
|
95
|
+
. Position: centered on the port Y, offset right of the gate body edge. The bubbled output port shifts right by `BUBBLE_R * 2` so that the bubble sits between the gate body and the wire start point.
|
|
96
|
+
. The gate body's right edge connects to the bubble's left edge (conceptually; the wire starts from the bubble's right edge).
|
|
97
|
+
. The wire starts from the right edge of the bubble.
|
|
98
|
+
|
|
99
|
+
SVG example (output-side bubble on a gate):
|
|
100
|
+
|
|
101
|
+
[source,xml]
|
|
102
|
+
----
|
|
103
|
+
<circle class="ldl-bubble ldl-output" data-port="out"
|
|
104
|
+
cx="108" cy="30" r="4"
|
|
105
|
+
fill="none" stroke="#2c3e50" stroke-width="1.5"/>
|
|
106
|
+
----
|
|
107
|
+
|
|
108
|
+
The wire from a bubbled output port starts at the bubble's right edge (`cx + BUBBLE_R`), not at the gate body edge.
|
|
109
|
+
|
|
110
|
+
=== Input Bar Rendering
|
|
111
|
+
|
|
112
|
+
When `OPTION GATE_INPUT_STYLE = BARS` is set, multi-input gates (3+ inputs) render a vertical input bar:
|
|
113
|
+
|
|
114
|
+
. A vertical line from `(barX, gateTop)` to `(barX, gateBottom)`, where `barX = gateX - 12`.
|
|
115
|
+
. Stroke-width: 2.5, stroke: `#2c3e50`.
|
|
116
|
+
. Horizontal stubs at each bar tap Y position, from `barX` to `gateX - 6`.
|
|
117
|
+
. Input wires connect at the left end of each stub (`barX`, tapY).
|
|
118
|
+
|
|
119
|
+
SVG example:
|
|
120
|
+
|
|
121
|
+
[source,xml]
|
|
122
|
+
----
|
|
123
|
+
<!-- Vertical input bar -->
|
|
124
|
+
<line class="ldl-input-bar" x1="-12" y1="0" x2="-12" y2="60"
|
|
125
|
+
stroke="#2c3e50" stroke-width="2.5"/>
|
|
126
|
+
<!-- Horizontal stub for 3rd input -->
|
|
127
|
+
<line class="ldl-bar-stub" x1="-12" y1="30" x2="-6" y2="30"
|
|
128
|
+
stroke="#34495e" stroke-width="2.5"/>
|
|
129
|
+
----
|
|
130
|
+
|
|
131
|
+
=== Port Style Rendering
|
|
132
|
+
|
|
133
|
+
When `OPTION PORT_STYLE = CIRCLE` (default), ports render as circles with `r=3`:
|
|
134
|
+
|
|
135
|
+
[source,xml]
|
|
136
|
+
----
|
|
137
|
+
<circle class="ldl-port ldl-input ldl-port-circle" cx="0" cy="30" r="3"
|
|
138
|
+
fill="#2c3e50"/>
|
|
139
|
+
----
|
|
140
|
+
|
|
141
|
+
When `OPTION PORT_STYLE = SQUARE`, ports render as 5×5 squares:
|
|
142
|
+
|
|
143
|
+
[source,xml]
|
|
144
|
+
----
|
|
145
|
+
<rect class="ldl-port ldl-input ldl-port-square" x="-2.5" y="27.5" width="5" height="5"
|
|
146
|
+
fill="#2c3e50"/>
|
|
147
|
+
----
|
|
148
|
+
|
|
149
|
+
Individual ports can override the document default via `.Style = SQUARE` or `.Style = CIRCLE`.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
[[symbol-definitions]]
|
|
2
|
+
== Symbol Definitions
|
|
3
|
+
|
|
4
|
+
=== Overview
|
|
5
|
+
|
|
6
|
+
A symbol definition describes a reusable SVG component. It specifies the SVG asset, its ports, its default attributes, and any logic it encapsulates.
|
|
7
|
+
|
|
8
|
+
=== Symbol Definition Syntax
|
|
9
|
+
|
|
10
|
+
[literal]
|
|
11
|
+
....
|
|
12
|
+
SYMBOL SYMBOL_NAME
|
|
13
|
+
SVG = "path/to/symbol.svg"
|
|
14
|
+
PORT INPUT name1
|
|
15
|
+
PORT INPUT name2
|
|
16
|
+
PORT OUTPUT name3
|
|
17
|
+
ATTRIBUTE name1 = default_value1
|
|
18
|
+
ATTRIBUTE name2 = default_value2
|
|
19
|
+
LOGIC = boolean_expression
|
|
20
|
+
END SYMBOL
|
|
21
|
+
....
|
|
22
|
+
|
|
23
|
+
=== Fields
|
|
24
|
+
|
|
25
|
+
[cols="1m,4m,3m"]
|
|
26
|
+
|===
|
|
27
|
+
| Field | Description | Required
|
|
28
|
+
| `SVG` | Path to the SVG file for this symbol | Yes
|
|
29
|
+
| `PORT` | A port declaration with direction and name | Yes (at least one)
|
|
30
|
+
| `ATTRIBUTE` | A default attribute with name and value | No
|
|
31
|
+
| `LOGIC` | An embedded boolean expression defining the symbol's internal logic | No
|
|
32
|
+
|===
|
|
33
|
+
|
|
34
|
+
=== Port Declaration in Symbol
|
|
35
|
+
|
|
36
|
+
[literal]
|
|
37
|
+
....
|
|
38
|
+
PORT DIRECTION NAME [: POSITION]
|
|
39
|
+
....
|
|
40
|
+
|
|
41
|
+
Where:
|
|
42
|
+
|
|
43
|
+
* `DIRECTION` is `INPUT`, `OUTPUT`, or `BIDI`.
|
|
44
|
+
* `NAME` is the port identifier.
|
|
45
|
+
* `POSITION` is an optional side descriptor: `LEFT`, `RIGHT`, `TOP`, `BOTTOM`. Default is `LEFT` for inputs, `RIGHT` for outputs.
|
|
46
|
+
|
|
47
|
+
=== Built-in Symbols
|
|
48
|
+
|
|
49
|
+
The following symbols do not require explicit definition; they are always available.
|
|
50
|
+
|
|
51
|
+
==== AND Gate
|
|
52
|
+
|
|
53
|
+
[literal]
|
|
54
|
+
....
|
|
55
|
+
SYMBOL AND
|
|
56
|
+
SVG = "builtin:and"
|
|
57
|
+
PORT INPUT in
|
|
58
|
+
PORT OUTPUT out
|
|
59
|
+
LOGIC = out = in_0 AND in_1 AND ... AND in_n
|
|
60
|
+
END SYMBOL
|
|
61
|
+
....
|
|
62
|
+
|
|
63
|
+
The AND symbol supports variable fan-in. The SVG is scaled accordingly.
|
|
64
|
+
|
|
65
|
+
==== OR Gate
|
|
66
|
+
|
|
67
|
+
[literal]
|
|
68
|
+
....
|
|
69
|
+
SYMBOL OR
|
|
70
|
+
SVG = "builtin:or"
|
|
71
|
+
PORT INPUT in
|
|
72
|
+
PORT OUTPUT out
|
|
73
|
+
LOGIC = out = in_0 OR in_1 OR ... OR in_n
|
|
74
|
+
END SYMBOL
|
|
75
|
+
....
|
|
76
|
+
|
|
77
|
+
==== NOT Gate
|
|
78
|
+
|
|
79
|
+
[literal]
|
|
80
|
+
....
|
|
81
|
+
SYMBOL NOT
|
|
82
|
+
SVG = "builtin:not"
|
|
83
|
+
PORT INPUT in
|
|
84
|
+
PORT OUTPUT out
|
|
85
|
+
LOGIC = out = NOT in
|
|
86
|
+
END SYMBOL
|
|
87
|
+
....
|
|
88
|
+
|
|
89
|
+
==== NAND, NOR, XOR, XNOR Gates
|
|
90
|
+
|
|
91
|
+
Each has the same port convention as AND/OR, with the corresponding logic.
|
|
92
|
+
|
|
93
|
+
=== Inversion Bubbles
|
|
94
|
+
|
|
95
|
+
When `OPTION INVERSION = BUBBLES` is set, `NOT` operations are rendered as inversion bubbles instead of separate NOT gate symbols. Bubbles may appear on **input ports** or **output ports** depending on where the inversion occurs in the expression.
|
|
96
|
+
|
|
97
|
+
==== Input-Side Bubbles
|
|
98
|
+
|
|
99
|
+
Any input port that receives the output of a `NOT` operation is marked with an inversion circle (bubble). The bubble is drawn at the input port position with a radius of `BUBBLE_R` (4px). The NOT gate symbol is not rendered; instead, the wire from the NOT's source connects directly to the bubbled input.
|
|
100
|
+
|
|
101
|
+
For AND and OR gates with bubbled inputs, the gate body remains unchanged. The bubble is rendered as an open circle (stroke only, no fill) overlapping the input port position on the left side of the gate.
|
|
102
|
+
|
|
103
|
+
==== Output-Side Bubbles
|
|
104
|
+
|
|
105
|
+
When a `NOT` operation wraps a gate's output (e.g., `O1 = NOT (I1 AND I2)`), the bubble appears on the gate's output port. The bubble is drawn at the output port position with radius `BUBBLE_R` (4px), offset to the right of the gate body edge. The wire from the bubbled output connects directly to the downstream port.
|
|
106
|
+
|
|
107
|
+
Similarly, when a `NOT` operation feeds directly into an output (e.g., `O1 = NOT I1`), the bubble appears on the output node's input port.
|
|
108
|
+
|
|
109
|
+
The bubble on an output port is rendered as an open circle (stroke only, no fill) to the right of the gate body.
|
|
110
|
+
|
|
111
|
+
==== Double-Inversion Cancellation
|
|
112
|
+
|
|
113
|
+
Consecutive `NOT` operations are simplified before rendering:
|
|
114
|
+
|
|
115
|
+
* An even number of `NOT` operations cancels out entirely (no bubble rendered).
|
|
116
|
+
* An odd number of `NOT` operations reduces to a single inversion (one bubble rendered).
|
|
117
|
+
|
|
118
|
+
=== Input Bars
|
|
119
|
+
|
|
120
|
+
When `OPTION GATE_INPUT_STYLE = BARS` is set, gates with more than two inputs render as follows:
|
|
121
|
+
|
|
122
|
+
. The gate body stays at the 2-input base height (44px for AND/OR).
|
|
123
|
+
. The first two inputs render as normal input ports on the left side of the gate.
|
|
124
|
+
. Additional inputs connect via a *vertical input bar* drawn along the left edge of the gate body.
|
|
125
|
+
. The vertical bar extends from the top of the gate body to the bottom.
|
|
126
|
+
. Additional input wires tap into the bar at evenly-spaced Y positions along the bar.
|
|
127
|
+
. The bar is drawn as a vertical line segment (stroke-width 2.5) with short horizontal stubs connecting it to the gate body edge.
|
|
128
|
+
|
|
129
|
+
The vertical bar is a visual convenience: it allows multi-input logic without expanding the gate symbol. The logical wiring is unchanged -- each input still connects to its source signal.
|
|
130
|
+
|
|
131
|
+
=== Custom Symbol Example
|
|
132
|
+
|
|
133
|
+
[literal]
|
|
134
|
+
....
|
|
135
|
+
SYMBOL SVT
|
|
136
|
+
SVG = "symbols/svt.svg"
|
|
137
|
+
PORT INPUT in
|
|
138
|
+
PORT OUTPUT out
|
|
139
|
+
ATTRIBUTE PICKUP = "0 s"
|
|
140
|
+
ATTRIBUTE DROPOUT = "0 s"
|
|
141
|
+
LOGIC = out = TIMER(in, PICKUP, DROPOUT)
|
|
142
|
+
END SYMBOL
|
|
143
|
+
....
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
== Templates
|
|
2
|
+
|
|
3
|
+
A *template* is a pre-packaged symbol definition bundled with an SVG asset. Templates allow common patterns to be reused across projects.
|
|
4
|
+
|
|
5
|
+
=== Template Declaration
|
|
6
|
+
|
|
7
|
+
[literal]
|
|
8
|
+
....
|
|
9
|
+
IMPORT TEMPLATE "path/to/template.ldlt" AS name
|
|
10
|
+
....
|
|
11
|
+
|
|
12
|
+
This loads a template file which contains one or more symbol definitions.
|
|
13
|
+
|
|
14
|
+
=== Template File Format
|
|
15
|
+
|
|
16
|
+
A template file (`.ldlt`) contains symbol definitions:
|
|
17
|
+
|
|
18
|
+
[literal]
|
|
19
|
+
....
|
|
20
|
+
SYMBOL TIMER
|
|
21
|
+
SVG = "timer.svg"
|
|
22
|
+
PORT INPUT in
|
|
23
|
+
PORT OUTPUT out
|
|
24
|
+
ATTRIBUTE PICKUP = "0 s"
|
|
25
|
+
ATTRIBUTE DROPOUT = "0 s"
|
|
26
|
+
END SYMBOL
|
|
27
|
+
....
|
|
28
|
+
|
|
29
|
+
=== Template Resolution
|
|
30
|
+
|
|
31
|
+
The renderer resolves SVG paths relative to the template's directory. Built-in symbols (`builtin:*`) are resolved by the renderer itself.
|
package/spec/spec.adoc
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
= Logic Diagram Language (LDL) Specification
|
|
2
|
+
:toc: macro
|
|
3
|
+
:toclevels: 4
|
|
4
|
+
:sectnums:
|
|
5
|
+
:sectlinks
|
|
6
|
+
:version: 0.3.0-draft
|
|
7
|
+
:revdate: 2026-06-14
|
|
8
|
+
|
|
9
|
+
toc::[]
|
|
10
|
+
|
|
11
|
+
include::sections/introduction.adoc[]
|
|
12
|
+
|
|
13
|
+
include::sections/lexical-conventions.adoc[]
|
|
14
|
+
|
|
15
|
+
include::sections/options.adoc[]
|
|
16
|
+
|
|
17
|
+
include::sections/expressions.adoc[]
|
|
18
|
+
|
|
19
|
+
include::sections/function-blocks.adoc[]
|
|
20
|
+
|
|
21
|
+
include::sections/objects.adoc[]
|
|
22
|
+
|
|
23
|
+
include::sections/ports.adoc[]
|
|
24
|
+
|
|
25
|
+
include::sections/attributes.adoc[]
|
|
26
|
+
|
|
27
|
+
include::sections/symbol-definitions.adoc[]
|
|
28
|
+
|
|
29
|
+
include::sections/templates.adoc[]
|
|
30
|
+
|
|
31
|
+
include::sections/svg-symbol-specification.adoc[]
|
|
32
|
+
|
|
33
|
+
include::sections/styling.adoc[]
|
|
34
|
+
|
|
35
|
+
include::sections/layout-rules.adoc[]
|
|
36
|
+
|
|
37
|
+
include::sections/hyperlinks.adoc[]
|
|
38
|
+
|
|
39
|
+
include::sections/connections.adoc[]
|
|
40
|
+
|
|
41
|
+
include::sections/examples.adoc[]
|
|
42
|
+
|
|
43
|
+
include::sections/grammar.adoc[]
|
|
44
|
+
|
|
45
|
+
include::sections/file-extension.adoc[]
|
|
46
|
+
|
|
47
|
+
include::sections/rendering-contract.adoc[]
|
|
48
|
+
|
|
49
|
+
include::sections/revision-history.adoc[]
|