@tabnas/expr 0.5.15 → 0.5.17

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,501 @@
1
+ /* Copyright (c) 2021-2026 Richard Rodger, MIT License */
2
+
3
+ // Generated by ../embed-translate.js. Edit tabnas.plugin.json or alchemy/*,
4
+ // then run npm run embed.
5
+
6
+ type TranslationPart = Readonly<{
7
+ entry: string
8
+ source?: string
9
+ }>
10
+
11
+ type TranslationParts = Readonly<{
12
+ manifest: string
13
+ lift?: TranslationPart
14
+ embed?: TranslationPart
15
+ render?: TranslationPart
16
+ }>
17
+
18
+ const TRANSLATION: TranslationParts = Object.freeze({
19
+ manifest: `{
20
+ "$schema": "https://tabnas.dev/schema/plugin.schema.json",
21
+ "name": "@tabnas/expr",
22
+ "go": "github.com/tabnas/expr/go",
23
+ "rust": "tabnas-expr",
24
+ "description": "Expression syntax with operators and precedence for the tabnas engine.",
25
+ "base": "@tabnas/jsonic",
26
+ "engine": "@tabnas/parser",
27
+ "languageId": "expr",
28
+ "pluginKind": "grammar",
29
+ "specDir": "test/spec",
30
+ "clib": {
31
+ "dir": "go/clib",
32
+ "library": "libtabnasexpr",
33
+ "abi": "v1",
34
+ "errorCodes": [
35
+ "usage",
36
+ "grammar",
37
+ "handle",
38
+ "internal"
39
+ ]
40
+ },
41
+ "errorCodes": [],
42
+ "docs": "https://tabnas.dev/docs/packages",
43
+ "repository": "https://github.com/tabnas/expr",
44
+ "versionSource": "ts/package.json",
45
+ "translate": {
46
+ "reads": "tree",
47
+ "writes": "tree",
48
+ "root": "any",
49
+ "schema": "expr",
50
+ "embed": "alchemy/embed.alc",
51
+ "render": "alchemy/render.alc",
52
+ "loss": [
53
+ "Comments, spacing, implicit lists and maps, and the document's own spelling of strings and numbers are not kept: every list is written in brackets, every object in braces with its keys quoted, every string double-quoted and every number as JSON writes it.",
54
+ "An operation of the default operators is written in infix, and a term that would need parentheses, which read back as a group, is written as a list instead, which reads back as the same tree.",
55
+ "A list whose first element is an object whose src member is a default operator's source text, as the reader's own value describes an operator, is written with that text in the object's place, as an operation where its count makes one: only the src member is read, and the object's other members, the token the operator was read from among them, are not kept.",
56
+ "A negative number is written with its sign, which reads back as the operator - applied to the number's magnitude.",
57
+ "A number that is not finite is written as the string Infinity, -Infinity or NaN."
58
+ ]
59
+ }
60
+ }
61
+ `,
62
+ embed: Object.freeze({ entry: "expr-embed", source: `; Expressions' embedding: a plain tree as an expression document's tree,
63
+ ; and back again. This is a library of definitions, each named
64
+ ; \`expr-...\`, which a host links with its own program and with the render
65
+ ; (alchemy's \`compile_sources\`). It has two entry points: \`expr-embed\`
66
+ ; takes a plain tree's events to an expression tree's, and \`expr-unembed\`,
67
+ ; its reverse, takes them back. Nothing else here is the host's to call.
68
+ ;
69
+ ; Both are the identity. The reader builds its trees on jsonic's values,
70
+ ; so every plain tree is already an expression tree: a list whose first
71
+ ; element is a default operator and whose count makes an operation is
72
+ ; one, and the render writes it in infix, the operator being its source
73
+ ; text (\`+\`, \`-\`, \`*\`, \`/\`, \`%\`, or \`(\` for a group) or an object that
74
+ ; describes it, whose \`src\` member is that text, as the reader's own
75
+ ; value carries it; every other value is a value of jsonic's, which the
76
+ ; render writes as jsonic. A list that only looks like an operation
77
+ ; reads back as the same tree either way, once each operator is reduced
78
+ ; to its source text as the shared fixtures' simplifier, Go's \`Simplify\`
79
+ ; and Rust's \`simplify\` reduce it, so nothing needs refusing. What the
80
+ ; round trip changes is what the render's conventions change (the
81
+ ; manifest's loss list): an operator's description is written as its
82
+ ; source text, a negative number reads back as the operator \`-\` applied
83
+ ; to its magnitude, and a number that is not finite as a string.
84
+
85
+ ; The embedding: a plain tree's events in, the same events out.
86
+ def expr-embed [input]
87
+ input
88
+
89
+ ; The reverse: an expression tree's events in, the same events out.
90
+ def expr-unembed [input]
91
+ input
92
+ ` }),
93
+ render: Object.freeze({ entry: "expr-render", source: `; Expressions' render: a tree's events written as an expression document,
94
+ ; jsonic text with the plugin's default operators. This is a library of
95
+ ; definitions, each named \`expr-...\`, which a host links with its own
96
+ ; program (alchemy's \`compile_sources\`); its entry point is \`expr-render\`,
97
+ ; and nothing else here is the host's to call.
98
+ ;
99
+ ; The tree is the one the reader builds with its default options:
100
+ ; jsonic's values, in which an operation is a list whose first element is
101
+ ; its operator and whose other elements are its terms. The reader's own
102
+ ; value puts the operator's description there, an object whose \`src\`
103
+ ; member is the operator's source text (TypeScript's parse, and Rust's
104
+ ; once realized, give it so); the shared fixtures' simplifier, Go's
105
+ ; \`Simplify\` and Rust's \`simplify\` reduce each description to that text,
106
+ ; \`+\`, \`-\`, \`*\`, \`/\` or \`%\` (\`1+2*3\` is \`["+", 1, ["*", 2, 3]]\`, \`-1\` is
107
+ ; \`["-", 1]\`) or \`(\` for a group (\`(1)\` is \`["(", 1]\`, \`()\` is \`["("]\`).
108
+ ; The render reads both: the first element of a list is the operator its
109
+ ; text is, or the operator an object describes, read by its \`src\` member
110
+ ; as those simplifiers read it, when that is a default operator's source.
111
+ ;
112
+ ; Every value is written so that the reader builds the same tree from it:
113
+ ;
114
+ ; - An operation is written in infix: \`a+b\` for a list of an infix
115
+ ; operator and two terms, \`-a\` or \`+a\` for a list of \`-\` or \`+\` and one
116
+ ; term, \`(a)\` for a group and \`()\` for an empty one. The default
117
+ ; operators' binding powers decide where a term needs grouping: \`*\`,
118
+ ; \`/\` and \`%\` bind tighter than \`+\` and \`-\`, every infix operator binds
119
+ ; to the left, and a prefix operator binds tighter than any infix one.
120
+ ; Parentheses would read back as a group, a list \`["(", ...]\`, so a
121
+ ; term that needs grouping (\`["+", 1, 2]\` under \`*\`, or on the right of
122
+ ; \`-\`) is written as a list instead, \`["+",1,2]*3\`, which the reader
123
+ ; builds as the same list.
124
+ ; - Any other list, one whose first element is no operator or which has
125
+ ; another number of terms (\`["*", 1]\`, \`["+", 1, 2, 3]\`), is written as
126
+ ; a list, \`[a,b]\`, each element in infix where it is an operation.
127
+ ; - An object is written \`{"k":v}\`, every key quoted.
128
+ ; - A string is quoted (JSON's escapes, which jsonic reads), so a word is
129
+ ; written \`"a"\`; a number by its lexeme, or as JSON writes it; a
130
+ ; boolean and null by their names. A negative number is written with
131
+ ; its sign, which the reader reads as the operator \`-\` applied to the
132
+ ; number's magnitude, and a number that is not finite, which jsonic
133
+ ; has no spelling for, as the string \`Infinity\`, \`-Infinity\` or \`NaN\`.
134
+ ;
135
+ ; No white space is written, since jsonic reads a space between two
136
+ ; values as an implicit list, and the document ends with one line break.
137
+ ;
138
+ ; Whether a list is written as an operation is known from its first
139
+ ; element and its count, so a list whose first element is a default
140
+ ; operator is held until it ends, its elements' texts with it, and then
141
+ ; written. A list whose first element is an object is held until that
142
+ ; object ends, since the object may describe an operator: if it does, the
143
+ ; list is held to its end as an operation is, and if not, the list is
144
+ ; written as it is read from then on. Every other list, and every object,
145
+ ; is written as it is read, one element at a time, so a document's own
146
+ ; lists and objects stream and only its operations, and the first object
147
+ ; of a list, are held. Events no tree has fail with PROTOCOL_ORDER_ERROR.
148
+ ;
149
+ ; The state is a stack of markers, one for each container the render is
150
+ ; inside:
151
+ ;
152
+ ; [:pending mode] a list whose first element is due, which
153
+ ; decides how it is written; mode is
154
+ ; :stream where it is written as it is
155
+ ; read and :hold inside a held list
156
+ ; [:list :first] a list whose first element, an object,
157
+ ; is being held, which decides how the
158
+ ; list is written
159
+ ; [:list :stream first] a list written as it is read; first
160
+ ; until an element is written
161
+ ; [:list :hold items] a held list, and the items read so far
162
+ ; [:map :stream first :key] an object written as it is read, its
163
+ ; next key or its end due
164
+ ; [:map :stream :value] the same, a member's value due
165
+ ; [:map :hold parts :key] an object inside a held list, or the
166
+ ; first element of a list, and its
167
+ ; members' texts
168
+ ; [:map :hold parts [:value k]] the same, the value of the key k due
169
+ ;
170
+ ; An item is \`[text class list head]\`: the text a value is written as;
171
+ ; its class, \`:infix2\` for an operation of \`+\` or \`-\`, \`:infix3\` for one
172
+ ; of \`*\`, \`/\` or \`%\`, and \`:atom\` for any other value, which binds as
173
+ ; tightly as a term can; the text of the value as a list, for a term that
174
+ ; needs grouping; and the operator a string is or an object describes, or
175
+ ; \`:none\`. A list's first item that is an operator is held as the
176
+ ; operator's text, so a description is written as its source, as the
177
+ ; simplifiers reduce it, wherever its list is written. Once the root
178
+ ; value is written the state is \`[:done]\`, and nothing may follow it.
179
+
180
+ ; The operator a value is, when it is the source of a default one.
181
+ def expr-op [value]
182
+ match (kind value)
183
+ case :string
184
+ match value
185
+ case "+" value
186
+ case "-" value
187
+ case "*" value
188
+ case "/" value
189
+ case "%" value
190
+ case "(" value
191
+ case _ :none
192
+ case _ :none
193
+
194
+ ; The operator a held object's member describes: the default operator a
195
+ ; \`src\` member's text names, or :none for any other member.
196
+ def expr-src-op [part]
197
+ match part
198
+ case "\\"src\\":\\"+\\"" "+"
199
+ case "\\"src\\":\\"-\\"" "-"
200
+ case "\\"src\\":\\"*\\"" "*"
201
+ case "\\"src\\":\\"/\\"" "/"
202
+ case "\\"src\\":\\"%\\"" "%"
203
+ case "\\"src\\":\\"(\\"" "("
204
+ case _ :none
205
+
206
+ def expr-is-op [op]
207
+ match op
208
+ case :none false
209
+ case _ true
210
+
211
+ ; The operator an object describes, from its members' texts: the one its
212
+ ; \`src\` member names, as the reader's description of an operator does, or
213
+ ; :none.
214
+ def expr-described [parts]
215
+ let [ops (filter expr-is-op (map expr-src-op parts))]
216
+ match (count ops)
217
+ case 0 :none
218
+ case _ (top ops)
219
+
220
+ ; An item as its list holds it: the first one, where it is an operator,
221
+ ; as that operator's text, so that a description is written as the
222
+ ; source it describes; any other as it is.
223
+ def expr-lead [items item]
224
+ match (count items)
225
+ case 0
226
+ match item
227
+ case [_ _ _ :none] item
228
+ case [_ _ _ op] (expr-scalar-item op)
229
+ case _ item
230
+
231
+ ; An infix operator's class.
232
+ def expr-level [op]
233
+ match op
234
+ case "+" :infix2
235
+ case "-" :infix2
236
+ case _ :infix3
237
+
238
+ ; A number: its lexeme, or as JSON writes it; one that is not finite as
239
+ ; a string.
240
+ def expr-number [value]
241
+ match (number-class value)
242
+ case :finite (scalar-text csv-options value)
243
+ case :infinity (quoted "Infinity")
244
+ case :negative-infinity (quoted "-Infinity")
245
+ case :nan (quoted "NaN")
246
+
247
+ def expr-scalar-text [value]
248
+ match (kind value)
249
+ case :string (quoted value)
250
+ case :number (expr-number value)
251
+ case :boolean (scalar-text csv-options value)
252
+ case :null "null"
253
+
254
+ def expr-scalar-item [value]
255
+ let [text (expr-scalar-text value)]
256
+ [text :atom text (expr-op value)]
257
+
258
+ def expr-text [item]
259
+ match item
260
+ case [text _ _ _] text
261
+
262
+ ; A held list's elements written as a list.
263
+ def expr-list [items]
264
+ string-join "" ["[" (string-join "," (map expr-text items)) "]"]
265
+
266
+ ; A left term of an infix operator of the class given: an operation that
267
+ ; binds as tightly or more, or the term as a list.
268
+ def expr-left [item level]
269
+ match item
270
+ case [text :atom _ _] text
271
+ case [text :infix3 _ _] text
272
+ case [text :infix2 list _]
273
+ match level
274
+ case :infix2 text
275
+ case _ list
276
+
277
+ ; A right term: an operation that binds more tightly, or the term as a
278
+ ; list.
279
+ def expr-right [item level]
280
+ match item
281
+ case [text :atom _ _] text
282
+ case [text :infix3 list _]
283
+ match level
284
+ case :infix2 text
285
+ case _ list
286
+ case [_ :infix2 list _] list
287
+
288
+ ; A prefix operator's term: one that binds as tightly, or the term as a
289
+ ; list.
290
+ def expr-term [item]
291
+ match item
292
+ case [text :atom _ _] text
293
+ case [_ _ list _] list
294
+
295
+ ; An infix operation, when the first element is an infix operator.
296
+ def expr-infix [op a b list]
297
+ match op
298
+ case :none [list :atom list :none]
299
+ case "(" [list :atom list :none]
300
+ case _
301
+ let [level (expr-level op)]
302
+ [(string-join "" [(expr-left a level) op (expr-right b level)]) level list :none]
303
+
304
+ ; A held list, ended: an operation where its first element and its count
305
+ ; make one, and a list otherwise.
306
+ def expr-array [items]
307
+ let [list (expr-list items)]
308
+ match items
309
+ case [[_ _ _ "("]] ["()" :atom list :none]
310
+ case [[_ _ _ "("] x] [(string-join "" ["(" (expr-text x) ")"]) :atom list :none]
311
+ case [[_ _ _ "+"] x] [(string-join "" ["+" (expr-term x)]) :atom list :none]
312
+ case [[_ _ _ "-"] x] [(string-join "" ["-" (expr-term x)]) :atom list :none]
313
+ case [[_ _ _ op] a b] (expr-infix op a b list)
314
+ case _ [list :atom list :none]
315
+
316
+ ; The top marker, or :none before the root value.
317
+ def expr-top [s]
318
+ match (count s)
319
+ case 0 :none
320
+ case _ (top s)
321
+
322
+ def expr-mark [marker s]
323
+ push marker (pop s)
324
+
325
+ ; A value written where it is read: at the root, which then is done, in
326
+ ; a list written as it is read, after a comma unless it is the first,
327
+ ; or as an object's member, whose next key is then due. \`text\` is what
328
+ ; the value writes now, and \`inner\` the marker it opens, or :none.
329
+ def expr-stream-value [s text inner]
330
+ match (expr-top s)
331
+ case :none (expr-push-or-done [] inner [text])
332
+ case [:list :stream first]
333
+ match first
334
+ case true (expr-push-or-done (expr-mark [:list :stream false] s) inner [text])
335
+ case false (expr-push-or-done (expr-mark [:list :stream false] s) inner ["," text])
336
+ case [:map :stream :value] (expr-push-or-done (expr-mark [:map :stream false :key] s) inner [text])
337
+
338
+ def expr-push-or-done [s inner out]
339
+ match inner
340
+ case :none
341
+ match (count s)
342
+ case 0 (transition [[:done]] (push "\\n" out))
343
+ case _ (transition s out)
344
+ case _ (transition (push inner s) out)
345
+
346
+ ; A value completed inside a held list or object: its item is added.
347
+ def expr-hold-value [s item]
348
+ match (expr-top s)
349
+ case [:list :hold items] (transition (expr-mark [:list :hold (push (expr-lead items item) items)] s) [])
350
+ case [:map :hold parts [:value k]] (transition (expr-mark [:map :hold (push (string-join "" [k ":" (expr-text item)]) parts) :key] s) [])
351
+
352
+ ; A held value has ended: it is added to the held container around it,
353
+ ; decides the list it is the first element of, or is written where
354
+ ; values are written as they are read, where what led it (a comma, a
355
+ ; key) was written when it began.
356
+ def expr-held [s item]
357
+ match (expr-top s)
358
+ case [:list :hold _] (expr-hold-value s item)
359
+ case [:map :hold _ _] (expr-hold-value s item)
360
+ case [:list :first] (expr-first-held s item)
361
+ case _ (expr-closed s (expr-text item))
362
+
363
+ ; A list whose first element, an object, has ended: one that describes
364
+ ; an operator makes the list an operation, held as one whose first
365
+ ; element is that operator's text is; any other is written, the list's
366
+ ; \`[\` first, and the rest of the list as it is read.
367
+ def expr-first-held [s item]
368
+ match item
369
+ case [text _ _ :none] (transition (expr-mark [:list :stream false] s) ["[" text])
370
+ case _ (transition (expr-mark [:list :hold [(expr-lead [] item)]] s) [])
371
+
372
+ ; Whether the top of the stack holds what is read.
373
+ def expr-holding [s]
374
+ match (expr-top s)
375
+ case [:list :hold _] true
376
+ case [:map :hold _ _] true
377
+ case [:list :first] true
378
+ case _ false
379
+
380
+ ; A list whose first element is due decides on that element's first
381
+ ; event, and answers its marker and the text it writes: one whose first
382
+ ; element is a default operator may be an operation and is held; one
383
+ ; whose first element is an object waits for the object, which may
384
+ ; describe an operator; any other is written as it is read, \`[\` first,
385
+ ; unless it is inside a held one.
386
+ def expr-decide [s mode event]
387
+ match [mode event]
388
+ case [:stream (scalar value)]
389
+ match (expr-op value)
390
+ case :none [(expr-mark [:list :stream true] s) "["]
391
+ case _ [(expr-mark [:list :hold []] s) ""]
392
+ case [:stream object-start] [(expr-mark [:list :first] s) ""]
393
+ case [:stream _] [(expr-mark [:list :stream true] s) "["]
394
+ case _ [(expr-mark [:list :hold []] s) ""]
395
+
396
+ ; A value where one is due: a scalar, or a container's start.
397
+ def expr-value [s event]
398
+ match (expr-holding s)
399
+ case true
400
+ match event
401
+ case (scalar value) (expr-hold-value s (expr-scalar-item value))
402
+ case object-start (transition (push [:map :hold [] :key] s) [])
403
+ case array-start (transition (push [:pending :hold] s) [])
404
+ case false
405
+ match event
406
+ case (scalar value) (expr-stream-value s (expr-scalar-text value) :none)
407
+ case object-start (expr-stream-value s "{" [:map :stream true :key])
408
+ case array-start (expr-stream-value s "" [:pending :stream])
409
+
410
+ ; The first element of a list, once the list has decided: what the
411
+ ; decision writes, then what the element does.
412
+ def expr-first [s mode event]
413
+ match (expr-decide s mode event)
414
+ case [s2 lead]
415
+ match (expr-value s2 event)
416
+ case (transition s3 []) (transition s3 [lead])
417
+ case (transition s3 [text]) (transition s3 [(string-join "" [lead text])])
418
+
419
+ ; A container ends; a held one's item is added or written.
420
+ def expr-close [s marker]
421
+ match marker
422
+ case [:pending :stream] (expr-closed (pop s) "[]")
423
+ case [:pending :hold] (expr-held (pop s) ["[]" :atom "[]" :none])
424
+ case [:list :stream _] (expr-closed (pop s) "]")
425
+ case [:list :hold items] (expr-held (pop s) (expr-array items))
426
+
427
+ ; A container written as it was read has ended: its closing text, and
428
+ ; the document's end at the root.
429
+ def expr-closed [s text]
430
+ match (count s)
431
+ case 0 (transition [[:done]] [text "\\n"])
432
+ case _ (transition s [text])
433
+
434
+ def expr-key [s name]
435
+ match (expr-top s)
436
+ case [:map :stream first :key]
437
+ match first
438
+ case true (transition (expr-mark [:map :stream :value] s) [(quoted name) ":"])
439
+ case false (transition (expr-mark [:map :stream :value] s) ["," (quoted name) ":"])
440
+ case [:map :hold parts :key] (transition (expr-mark [:map :hold parts [:value (quoted name)]] s) [])
441
+ case _ (fail :protocol-order "the events hold a key where a value is due, which a tree's never do")
442
+
443
+ def expr-object-end [s]
444
+ match (expr-top s)
445
+ case [:map :stream _ :key] (expr-closed (pop s) "}")
446
+ case [:map :hold parts :key]
447
+ let [text (string-join "" ["{" (string-join "," parts) "}"])]
448
+ expr-held (pop s) [text :atom text (expr-described parts)]
449
+ case _ (fail :protocol-order "the events end an object that is not open, or one whose member has no value, which a tree's never do")
450
+
451
+ def expr-array-end [s]
452
+ match (expr-top s)
453
+ case [:pending mode] (expr-close s [:pending mode])
454
+ case [:list mode x] (expr-close s [:list mode x])
455
+ case _ (fail :protocol-order "the events end an array that is not open, which a tree's never do")
456
+
457
+ ; Where a value is due: at the root, in a list, or after a key.
458
+ def expr-due [s event]
459
+ match (expr-top s)
460
+ case :none (expr-value s event)
461
+ case [:pending mode] (expr-first s mode event)
462
+ case [:list _ _] (expr-value s event)
463
+ case [:map :stream :value] (expr-value s event)
464
+ case [:map :hold _ [:value _]] (expr-value s event)
465
+ case _ (fail :protocol-order "the events hold a value where a key is due, which a tree's never do")
466
+
467
+ def expr-step [s event]
468
+ match (expr-top s)
469
+ case [:done] (fail :protocol-order "the events hold more after the root value, which a tree's never do")
470
+ case _
471
+ match event
472
+ case (key name) (expr-key s name)
473
+ case object-end (expr-object-end s)
474
+ case array-end (expr-array-end s)
475
+ case _ (expr-due s event)
476
+
477
+ def expr-finish [s]
478
+ match (expr-top s)
479
+ case [:done] []
480
+ case :none (fail :protocol-order "the events hold no value, where a tree's hold one")
481
+ case _ (fail :protocol-order "the events ended inside a container, which a tree's never do")
482
+
483
+ ; The render: a tree's events in, the expression document's text out.
484
+ def expr-render [input]
485
+ join ""
486
+ scan-emit [] expr-step expr-finish (events input)
487
+ ` })
488
+ })
489
+
490
+ function translate (): TranslationParts | undefined {
491
+ return TRANSLATION
492
+ }
493
+
494
+ export {
495
+ translate,
496
+ }
497
+
498
+ export type {
499
+ TranslationPart,
500
+ TranslationParts,
501
+ }