diagramo 0.2.0 → 0.5.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/DIAGRAMO_GRAMMAR.md +479 -0
- package/LAYOUT_HEURISTICS.md +507 -0
- package/LICENSE +18 -4
- package/NCF_GRAMMAR.md +97 -0
- package/README.md +142 -122
- package/examples/basic.html +83 -0
- package/examples/devtool.html +2427 -0
- package/package.json +17 -27
- package/src/browser/index.js +190 -0
- package/src/compiler/compile.js +245 -0
- package/src/compiler/parse.js +212 -0
- package/src/compiler/tokenize.js +84 -0
- package/src/index.js +32 -0
- package/src/index.shared.js +10 -0
- package/src/ncf/index.js +220 -0
- package/src/render/render-svg.js +1525 -0
- package/tools/dev-server.mjs +66 -0
- package/dist/diagramo.browser.js +0 -7286
- package/dist/diagramo.js +0 -1588
- package/examples/static-page.html +0 -22
- package/scripts/build.mjs +0 -26
- package/src/browser-entry.js +0 -18
- package/src/diagramo.js +0 -1588
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
Here’s a clean **Diagramo surface-language grammar spec** for the user-friendly lispy language that compiles to NCF.
|
|
2
|
+
|
|
3
|
+
## Diagramo grammar
|
|
4
|
+
|
|
5
|
+
Diagramo is a small lispy language with two main forms:
|
|
6
|
+
|
|
7
|
+
* **tree forms** for nodes and parent/child structure
|
|
8
|
+
* **arrow forms** for arrows between nodes
|
|
9
|
+
|
|
10
|
+
A document may be either:
|
|
11
|
+
|
|
12
|
+
* a sequence of forms, or
|
|
13
|
+
* wrapped in a top-level `(diagramo ...)`
|
|
14
|
+
|
|
15
|
+
## EBNF
|
|
16
|
+
|
|
17
|
+
```ebnf
|
|
18
|
+
document = { ws_or_comment , form } , ws_or_comment
|
|
19
|
+
| "(" , "diagramo" , { ws_or_comment , form } , ws_or_comment , ")" ;
|
|
20
|
+
|
|
21
|
+
form = tree_form | arrow_form ;
|
|
22
|
+
|
|
23
|
+
tree_form = "(" , ws ,
|
|
24
|
+
node_ref ,
|
|
25
|
+
{ ws , tree_item } ,
|
|
26
|
+
ws , ")" ;
|
|
27
|
+
|
|
28
|
+
tree_item = node_ref | tree_form ;
|
|
29
|
+
|
|
30
|
+
arrow_form = "(" , ws ,
|
|
31
|
+
"->" , ws ,
|
|
32
|
+
edge_ref , ws ,
|
|
33
|
+
endpoint , ws ,
|
|
34
|
+
endpoint ,
|
|
35
|
+
ws , ")" ;
|
|
36
|
+
|
|
37
|
+
endpoint = node_ref | vector ;
|
|
38
|
+
|
|
39
|
+
vector = "[" , ws ,
|
|
40
|
+
node_ref ,
|
|
41
|
+
{ ws , node_ref } ,
|
|
42
|
+
ws , "]" ;
|
|
43
|
+
|
|
44
|
+
node_ref = symbol | labeled_symbol ;
|
|
45
|
+
edge_ref = symbol | labeled_symbol ;
|
|
46
|
+
|
|
47
|
+
labeled_symbol = symbol , "{" , label_text , "}" ;
|
|
48
|
+
|
|
49
|
+
label_text = quoted_string | bare_label ;
|
|
50
|
+
|
|
51
|
+
symbol = initial_char , { symbol_char } ;
|
|
52
|
+
|
|
53
|
+
initial_char = letter | "_" ;
|
|
54
|
+
symbol_char = letter | digit | "_" | "-" | "." ;
|
|
55
|
+
|
|
56
|
+
bare_label = { any_char_except_brace_or_newline } ;
|
|
57
|
+
|
|
58
|
+
quoted_string = '"' , { string_char } , '"' ;
|
|
59
|
+
|
|
60
|
+
string_char = any_char_except_quote_or_backslash
|
|
61
|
+
| "\" , '"'
|
|
62
|
+
| "\" , "\"
|
|
63
|
+
| "\" , "n"
|
|
64
|
+
| "\" , "t" ;
|
|
65
|
+
|
|
66
|
+
ws = { " " | "\t" | "\r" | "\n" } ;
|
|
67
|
+
comment = ";" , { any_char_except_newline } ;
|
|
68
|
+
ws_or_comment = ws | comment ;
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Meaning of each form
|
|
72
|
+
|
|
73
|
+
### 1. Node declaration
|
|
74
|
+
|
|
75
|
+
```diagramo
|
|
76
|
+
(P)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Ensures node `P` exists.
|
|
80
|
+
|
|
81
|
+
Default label is the node id, so this behaves like:
|
|
82
|
+
|
|
83
|
+
* id = `P`
|
|
84
|
+
* label = `P`
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
|
|
88
|
+
```diagramo
|
|
89
|
+
(A)
|
|
90
|
+
(B)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Creates two nodes: `A`, `B`.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### 2. Node with label
|
|
98
|
+
|
|
99
|
+
```diagramo
|
|
100
|
+
(P{hi})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Ensures node `P` exists with label `hi`.
|
|
104
|
+
|
|
105
|
+
Example:
|
|
106
|
+
|
|
107
|
+
```diagramo
|
|
108
|
+
(P{Person})
|
|
109
|
+
(Q{"Hello world"})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Creates:
|
|
113
|
+
|
|
114
|
+
* node `P` labeled `Person`
|
|
115
|
+
* node `Q` labeled `Hello world`
|
|
116
|
+
|
|
117
|
+
Use quotes when the label contains spaces.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### 3. Parent with children
|
|
122
|
+
|
|
123
|
+
```diagramo
|
|
124
|
+
(X Y Z)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Means:
|
|
128
|
+
|
|
129
|
+
* `X` exists
|
|
130
|
+
* `Y` exists and is a child of `X`
|
|
131
|
+
* `Z` exists and is a child of `X`
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
|
|
135
|
+
```diagramo
|
|
136
|
+
(Folder File1 File2)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`Folder` is the container, `File1` and `File2` are inside it.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
### 4. Nested parent structure
|
|
144
|
+
|
|
145
|
+
```diagramo
|
|
146
|
+
(X Y (Z P))
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Means:
|
|
150
|
+
|
|
151
|
+
* `Y` is a child of `X`
|
|
152
|
+
* `Z` is a child of `X`
|
|
153
|
+
* `P` is a child of `Z`
|
|
154
|
+
|
|
155
|
+
Example:
|
|
156
|
+
|
|
157
|
+
```diagramo
|
|
158
|
+
(App Header (Body Sidebar))
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
This gives:
|
|
162
|
+
|
|
163
|
+
* `Header` inside `App`
|
|
164
|
+
* `Body` inside `App`
|
|
165
|
+
* `Sidebar` inside `Body`
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
### 5. Chained nesting
|
|
170
|
+
|
|
171
|
+
```diagramo
|
|
172
|
+
(X (Y (Z P)))
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Means:
|
|
176
|
+
|
|
177
|
+
* `Y` is a child of `X`
|
|
178
|
+
* `Z` is a child of `Y`
|
|
179
|
+
* `P` is a child of `Z`
|
|
180
|
+
|
|
181
|
+
Example:
|
|
182
|
+
|
|
183
|
+
```diagramo
|
|
184
|
+
(World (Continent (Country City)))
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
This creates a straight nesting chain.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### 6. Arrow between two nodes
|
|
192
|
+
|
|
193
|
+
```diagramo
|
|
194
|
+
(-> e A B)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Creates arrow `e` from `A` to `B`.
|
|
198
|
+
|
|
199
|
+
If `A` or `B` do not already exist, they are created automatically.
|
|
200
|
+
|
|
201
|
+
Example:
|
|
202
|
+
|
|
203
|
+
```diagramo
|
|
204
|
+
(-> f Login Dashboard)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Creates:
|
|
208
|
+
|
|
209
|
+
* node `Login`
|
|
210
|
+
* node `Dashboard`
|
|
211
|
+
* arrow `f` from `Login` to `Dashboard`
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
### 7. Arrow with label
|
|
216
|
+
|
|
217
|
+
```diagramo
|
|
218
|
+
(-> e{hi} A B)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Creates arrow `e` from `A` to `B` with label `hi`.
|
|
222
|
+
|
|
223
|
+
Example:
|
|
224
|
+
|
|
225
|
+
```diagramo
|
|
226
|
+
(-> auth{"sign in"} User Session)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Creates arrow `auth` labeled `sign in`.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### 8. Fan-out arrow
|
|
234
|
+
|
|
235
|
+
```diagramo
|
|
236
|
+
(-> e A [B C D])
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Means one source, many targets.
|
|
240
|
+
|
|
241
|
+
This expands to multiple arrows:
|
|
242
|
+
|
|
243
|
+
* `e_1` from `A` to `B`
|
|
244
|
+
* `e_2` from `A` to `C`
|
|
245
|
+
* `e_3` from `A` to `D`
|
|
246
|
+
|
|
247
|
+
Example:
|
|
248
|
+
|
|
249
|
+
```diagramo
|
|
250
|
+
(-> send Server [Client1 Client2 Client3])
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
### 9. Fan-in arrow
|
|
256
|
+
|
|
257
|
+
```diagramo
|
|
258
|
+
(-> e [A B C] D)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Means many sources, one target.
|
|
262
|
+
|
|
263
|
+
This expands to:
|
|
264
|
+
|
|
265
|
+
* `e_1` from `A` to `D`
|
|
266
|
+
* `e_2` from `B` to `D`
|
|
267
|
+
* `e_3` from `C` to `D`
|
|
268
|
+
|
|
269
|
+
Example:
|
|
270
|
+
|
|
271
|
+
```diagramo
|
|
272
|
+
(-> collect [Sensor1 Sensor2 Sensor3] Database)
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Full examples
|
|
278
|
+
|
|
279
|
+
### Minimal document
|
|
280
|
+
|
|
281
|
+
```diagramo
|
|
282
|
+
(A)
|
|
283
|
+
(B)
|
|
284
|
+
(-> f A B)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### With grouping
|
|
288
|
+
|
|
289
|
+
```diagramo
|
|
290
|
+
(App Header Footer)
|
|
291
|
+
(App (Main Sidebar))
|
|
292
|
+
(-> nav Header Main)
|
|
293
|
+
(-> info Sidebar Footer)
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### With top-level wrapper
|
|
297
|
+
|
|
298
|
+
```diagramo
|
|
299
|
+
(diagramo
|
|
300
|
+
(A{Start})
|
|
301
|
+
(B{End})
|
|
302
|
+
(-> flow A B))
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Mixed hierarchy and arrows
|
|
306
|
+
|
|
307
|
+
```diagramo
|
|
308
|
+
(diagramo
|
|
309
|
+
(System
|
|
310
|
+
API
|
|
311
|
+
(UI Button Panel)
|
|
312
|
+
(DB Table))
|
|
313
|
+
|
|
314
|
+
(-> req UI API)
|
|
315
|
+
(-> read API DB)
|
|
316
|
+
(-> write API DB)
|
|
317
|
+
(-> click Button API))
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Fan-out and fan-in
|
|
321
|
+
|
|
322
|
+
```diagramo
|
|
323
|
+
(diagramo
|
|
324
|
+
(Hub)
|
|
325
|
+
(A)
|
|
326
|
+
(B)
|
|
327
|
+
(C)
|
|
328
|
+
(D)
|
|
329
|
+
|
|
330
|
+
(-> out Hub [A B C])
|
|
331
|
+
(-> in [A B C] D))
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Semantic rules
|
|
337
|
+
|
|
338
|
+
These are the intended compile-time rules.
|
|
339
|
+
|
|
340
|
+
### Auto-creation
|
|
341
|
+
|
|
342
|
+
Any node mentioned in a tree or arrow form is created if it does not already exist.
|
|
343
|
+
|
|
344
|
+
Example:
|
|
345
|
+
|
|
346
|
+
```diagramo
|
|
347
|
+
(-> f X Y)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Creates `X` and `Y` automatically.
|
|
351
|
+
|
|
352
|
+
### Labels
|
|
353
|
+
|
|
354
|
+
A node or arrow may be given a label with `{...}`.
|
|
355
|
+
|
|
356
|
+
Example:
|
|
357
|
+
|
|
358
|
+
```diagramo
|
|
359
|
+
(A{Alpha})
|
|
360
|
+
(-> e{"goes to"} A B)
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
If no label is provided, the id is used as the default label.
|
|
364
|
+
|
|
365
|
+
### Unique ids
|
|
366
|
+
|
|
367
|
+
Node ids must be unique among nodes.
|
|
368
|
+
Arrow ids must be unique among arrows.
|
|
369
|
+
|
|
370
|
+
Fan expansion reserves generated ids like:
|
|
371
|
+
|
|
372
|
+
```diagramo
|
|
373
|
+
e_1
|
|
374
|
+
e_2
|
|
375
|
+
e_3
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Parenting
|
|
379
|
+
|
|
380
|
+
A node may have at most one parent.
|
|
381
|
+
|
|
382
|
+
This is valid:
|
|
383
|
+
|
|
384
|
+
```diagramo
|
|
385
|
+
(X Y)
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
This should be rejected:
|
|
389
|
+
|
|
390
|
+
```diagramo
|
|
391
|
+
(X Y)
|
|
392
|
+
(Z Y)
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
because `Y` would have two parents.
|
|
396
|
+
|
|
397
|
+
### Repeated references
|
|
398
|
+
|
|
399
|
+
Referring to the same node multiple times is allowed.
|
|
400
|
+
|
|
401
|
+
Example:
|
|
402
|
+
|
|
403
|
+
```diagramo
|
|
404
|
+
(A)
|
|
405
|
+
(-> f A B)
|
|
406
|
+
(-> g B A)
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Conflicting labels
|
|
410
|
+
|
|
411
|
+
This should be treated as an error:
|
|
412
|
+
|
|
413
|
+
```diagramo
|
|
414
|
+
(A{One})
|
|
415
|
+
(A{Two})
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
because node `A` gets two different labels.
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Lexical notes
|
|
423
|
+
|
|
424
|
+
### Comments
|
|
425
|
+
|
|
426
|
+
A semicolon starts a comment to the end of the line.
|
|
427
|
+
|
|
428
|
+
```diagramo
|
|
429
|
+
; this is a comment
|
|
430
|
+
(A)
|
|
431
|
+
(-> f A B) ; inline comment
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Whitespace
|
|
435
|
+
|
|
436
|
+
Whitespace is ignored except as a separator.
|
|
437
|
+
|
|
438
|
+
So these are equivalent:
|
|
439
|
+
|
|
440
|
+
```diagramo
|
|
441
|
+
(A B C)
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
```diagramo
|
|
445
|
+
(
|
|
446
|
+
A
|
|
447
|
+
B
|
|
448
|
+
C
|
|
449
|
+
)
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Reserved tokens
|
|
453
|
+
|
|
454
|
+
These should be reserved:
|
|
455
|
+
|
|
456
|
+
* `diagramo`
|
|
457
|
+
* `->`
|
|
458
|
+
|
|
459
|
+
### Labels with spaces
|
|
460
|
+
|
|
461
|
+
Prefer quoted labels when they contain spaces:
|
|
462
|
+
|
|
463
|
+
```diagramo
|
|
464
|
+
(Node{"Hello world"})
|
|
465
|
+
(-> e{"edge label"} A B)
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
## Practical summary
|
|
471
|
+
|
|
472
|
+
The language has only four ideas:
|
|
473
|
+
|
|
474
|
+
* `(A)` → create a node
|
|
475
|
+
* `(A B C)` → make `B` and `C` children of `A`
|
|
476
|
+
* `(-> e A B)` → create an arrow from `A` to `B`
|
|
477
|
+
* `[A B C]` inside an arrow → fan-in or fan-out
|
|
478
|
+
|
|
479
|
+
If you want, I can next turn this into a package-ready `GRAMMAR.md` file.
|