@tabnas/expr 0.5.17 → 0.5.19
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/dist/expr.d.ts +1 -1
- package/dist/expr.js +1 -1
- package/dist/translate.js +239 -39
- package/dist/translate.js.map +1 -1
- package/package.json +1 -1
- package/src/expr.ts +1 -1
- package/src/translate.ts +239 -39
package/dist/expr.d.ts
CHANGED
|
@@ -62,7 +62,7 @@ declare const testing: {
|
|
|
62
62
|
prattify: typeof prattify;
|
|
63
63
|
opify: (x: any) => any;
|
|
64
64
|
};
|
|
65
|
-
declare const VERSION = "0.5.
|
|
65
|
+
declare const VERSION = "0.5.19";
|
|
66
66
|
export { VERSION, Expr, evaluation, simplify, parseSimplified, testing };
|
|
67
67
|
export type { ExprOptions, OpDef, Op, Evaluate };
|
|
68
68
|
export { translate } from './translate';
|
package/dist/expr.js
CHANGED
|
@@ -1420,7 +1420,7 @@ exports.testing = testing;
|
|
|
1420
1420
|
// VERSION is this package's version. It MUST equal package.json "version":
|
|
1421
1421
|
// the release orchestrator rewrites both, and the version test fails the
|
|
1422
1422
|
// build if they drift. Mirrors `const VERSION` in go/expr.go.
|
|
1423
|
-
const VERSION = '0.5.
|
|
1423
|
+
const VERSION = '0.5.19';
|
|
1424
1424
|
exports.VERSION = VERSION;
|
|
1425
1425
|
// The translation parts a host composes a translation from (admin ADR-27):
|
|
1426
1426
|
// the manifest and the alchemy sources, generated by ../embed-translate.js.
|
package/dist/translate.js
CHANGED
|
@@ -39,10 +39,11 @@ const TRANSLATION = Object.freeze({
|
|
|
39
39
|
"loss": [
|
|
40
40
|
"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.",
|
|
41
41
|
"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.",
|
|
42
|
-
"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.",
|
|
42
|
+
"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. A list whose first element is an object with any other src that is not empty, zero, false or null, as a C syntax tree's token has, is refused with TARGET_VALUE_UNREPRESENTABLE, since the reader would read that src alone as the list's operator.",
|
|
43
43
|
"A negative number is written with its sign, which reads back as the operator - applied to the number's magnitude.",
|
|
44
44
|
"A number that is not finite is written as the string Infinity, -Infinity or NaN."
|
|
45
|
-
]
|
|
45
|
+
],
|
|
46
|
+
"whole": "A binary operator follows its left term, and a later operator can take a finished operation as its own term, as the - in 1*2-3 takes 1*2, so an expression's tree is complete only at its end: a document is read whole before anything is written."
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
`,
|
|
@@ -54,7 +55,7 @@ const TRANSLATION = Object.freeze({
|
|
|
54
55
|
; its reverse, takes them back. Nothing else here is the host's to call.
|
|
55
56
|
;
|
|
56
57
|
; Both are the identity. The reader builds its trees on jsonic's values,
|
|
57
|
-
; so
|
|
58
|
+
; so a plain tree is already an expression tree: a list whose first
|
|
58
59
|
; element is a default operator and whose count makes an operation is
|
|
59
60
|
; one, and the render writes it in infix, the operator being its source
|
|
60
61
|
; text (\`+\`, \`-\`, \`*\`, \`/\`, \`%\`, or \`(\` for a group) or an object that
|
|
@@ -63,11 +64,22 @@ const TRANSLATION = Object.freeze({
|
|
|
63
64
|
; render writes as jsonic. A list that only looks like an operation
|
|
64
65
|
; reads back as the same tree either way, once each operator is reduced
|
|
65
66
|
; to its source text as the shared fixtures' simplifier, Go's \`Simplify\`
|
|
66
|
-
; and Rust's \`simplify\` reduce it
|
|
67
|
-
;
|
|
68
|
-
;
|
|
69
|
-
;
|
|
70
|
-
;
|
|
67
|
+
; and Rust's \`simplify\` reduce it.
|
|
68
|
+
;
|
|
69
|
+
; The plain trees that are not expression trees are the lists led by an
|
|
70
|
+
; object whose \`src\` member the simplifiers read as an operator that is
|
|
71
|
+
; not a default one, as they read a C syntax tree's tokens: they keep
|
|
72
|
+
; that \`src\` alone, so no document reads back as such a list. The render
|
|
73
|
+
; refuses it, with TARGET_VALUE_UNREPRESENTABLE and the object's path,
|
|
74
|
+
; rather than this embedding, since the render meets such a list from
|
|
75
|
+
; any source, one of the schema \`expr\` too, which reaches the render
|
|
76
|
+
; without an embedding, and it already holds the first object of a list
|
|
77
|
+
; to read the operator that object describes.
|
|
78
|
+
;
|
|
79
|
+
; What the round trip changes is what the render's conventions change
|
|
80
|
+
; (the manifest's loss list): an operator's description is written as
|
|
81
|
+
; its source text, a negative number reads back as the operator \`-\`
|
|
82
|
+
; applied to its magnitude, and a number that is not finite as a string.
|
|
71
83
|
|
|
72
84
|
; The embedding: a plain tree's events in, the same events out.
|
|
73
85
|
def expr-embed [input]
|
|
@@ -96,6 +108,15 @@ def expr-unembed [input]
|
|
|
96
108
|
; text is, or the operator an object describes, read by its \`src\` member
|
|
97
109
|
; as those simplifiers read it, when that is a default operator's source.
|
|
98
110
|
;
|
|
111
|
+
; The simplifiers read any list led by an object with a \`src\` member that
|
|
112
|
+
; way, and keep that member's value alone: TypeScript's \`simplify\` where
|
|
113
|
+
; the \`src\` is not empty, zero, false or null, and Go's \`SimplifyOrdered\`
|
|
114
|
+
; and Rust's \`simplify\` where it is a string other than the empty one. So
|
|
115
|
+
; a list led by an object whose \`src\` they read and is not a default
|
|
116
|
+
; operator's source, as the tokens a C syntax tree puts first in a node's
|
|
117
|
+
; children are, is no tree of the reader's: no document reads back as
|
|
118
|
+
; it, and the render refuses it (below).
|
|
119
|
+
;
|
|
99
120
|
; Every value is written so that the reader builds the same tree from it:
|
|
100
121
|
;
|
|
101
122
|
; - An operation is written in infix: \`a+b\` for a list of an infix
|
|
@@ -118,6 +139,17 @@ def expr-unembed [input]
|
|
|
118
139
|
; its sign, which the reader reads as the operator \`-\` applied to the
|
|
119
140
|
; number's magnitude, and a number that is not finite, which jsonic
|
|
120
141
|
; has no spelling for, as the string \`Infinity\`, \`-Infinity\` or \`NaN\`.
|
|
142
|
+
; - A list whose first element is an object with a \`src\` member that the
|
|
143
|
+
; simplifiers read is written as an operation only where that \`src\` is
|
|
144
|
+
; a default operator's source text, as the reader's own value describes
|
|
145
|
+
; the operator (the manifest's loss list says the object's other
|
|
146
|
+
; members are not kept). With any other such \`src\` the list fails with
|
|
147
|
+
; TARGET_VALUE_UNREPRESENTABLE, naming the object's path, in jq's
|
|
148
|
+
; syntax, and its \`src\` as the render writes it. The \`src\` is judged as
|
|
149
|
+
; it is written, since that is what the reader reads: a number that is
|
|
150
|
+
; not finite as a string, and a negative number as the operation its
|
|
151
|
+
; sign makes, a list. An object whose \`src\` is empty, zero, false or
|
|
152
|
+
; null is written as it is, and reads back as itself.
|
|
121
153
|
;
|
|
122
154
|
; No white space is written, since jsonic reads a space between two
|
|
123
155
|
; values as an implicit list, and the document ends with one line break.
|
|
@@ -143,16 +175,25 @@ def expr-unembed [input]
|
|
|
143
175
|
; [:list :first] a list whose first element, an object,
|
|
144
176
|
; is being held, which decides how the
|
|
145
177
|
; list is written
|
|
146
|
-
; [:list :stream
|
|
147
|
-
; until an element is written
|
|
178
|
+
; [:list :stream index] a list written as it is read; index is
|
|
179
|
+
; [] until an element is written, and then
|
|
180
|
+
; the decimal digits of the last one's
|
|
181
|
+
; index, most significant first
|
|
148
182
|
; [:list :hold items] a held list, and the items read so far
|
|
149
|
-
; [:map :stream first :key]
|
|
150
|
-
;
|
|
151
|
-
; [:map :stream :value]
|
|
183
|
+
; [:map :stream :first :key] an object written as it is read, its
|
|
184
|
+
; first key or its end due
|
|
185
|
+
; [:map :stream [:value k]] the same, the value of the key k due
|
|
186
|
+
; [:map :stream [:after k] :key] the same, after the member of the key
|
|
187
|
+
; k, its next key or its end due
|
|
152
188
|
; [:map :hold parts :key] an object inside a held list, or the
|
|
153
189
|
; first element of a list, and its
|
|
154
190
|
; members' texts
|
|
155
|
-
; [:map :hold parts [:value k]] the same, the value of the key k due
|
|
191
|
+
; [:map :hold parts [:value k]] the same, the value of the key k due,
|
|
192
|
+
; k quoted
|
|
193
|
+
;
|
|
194
|
+
; The markers say where the render is, which only a refusal reads: the
|
|
195
|
+
; list a value is in, and its index, or the object and its key. alchemy
|
|
196
|
+
; has no arithmetic, so an index goes up a digit at a time.
|
|
156
197
|
;
|
|
157
198
|
; An item is \`[text class list head]\`: the text a value is written as;
|
|
158
199
|
; its class, \`:infix2\` for an operation of \`+\` or \`-\`, \`:infix3\` for one
|
|
@@ -190,19 +231,65 @@ def expr-src-op [part]
|
|
|
190
231
|
case "\\"src\\":\\"(\\"" "("
|
|
191
232
|
case _ :none
|
|
192
233
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
234
|
+
; Whether a held object's member is its \`src\`: a member's text is its
|
|
235
|
+
; quoted key, a colon and its value's text, and a quoted key ends at its
|
|
236
|
+
; first unescaped quote, so the member is \`src\` exactly when its text
|
|
237
|
+
; begins with "src":.
|
|
238
|
+
def expr-is-src [part]
|
|
239
|
+
expr-leads "\\"src\\":" part
|
|
240
|
+
|
|
241
|
+
; The text of a \`src\` member's value, what follows its key and colon.
|
|
242
|
+
def expr-src-value [part]
|
|
243
|
+
let [pieces (split "\\"src\\":" part)]
|
|
244
|
+
string-join "\\"src\\":" (map (fn [i] (expr-at pieces i)) (filter expr-after-first (indices pieces)))
|
|
245
|
+
|
|
246
|
+
def expr-after-first [i]
|
|
247
|
+
match i
|
|
248
|
+
case 0 false
|
|
196
249
|
case _ true
|
|
197
250
|
|
|
198
|
-
;
|
|
199
|
-
; \`src\` member
|
|
200
|
-
;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
251
|
+
; Whether a simplifier takes a list led by an object for an operation,
|
|
252
|
+
; the object's \`src\` member's value written as this text: TypeScript's
|
|
253
|
+
; does where the value is not empty, zero, false or null, and Go's and
|
|
254
|
+
; Rust's where it is a string other than the empty one, a narrower set.
|
|
255
|
+
; The value is judged as written, which is what the reader reads: a
|
|
256
|
+
; number that is not finite is written as a string, and a negative one
|
|
257
|
+
; with its sign, which reads back as an operation, a list.
|
|
258
|
+
def expr-src-read [text]
|
|
259
|
+
match text
|
|
260
|
+
case "\\"\\"" false
|
|
261
|
+
case "false" false
|
|
262
|
+
case "null" false
|
|
263
|
+
case _
|
|
264
|
+
match (is-number text)
|
|
265
|
+
case false true
|
|
266
|
+
case true
|
|
267
|
+
match (expr-at (split "-" text) 0)
|
|
268
|
+
case "" true
|
|
269
|
+
case _
|
|
270
|
+
match (compare (number text) 0)
|
|
271
|
+
case :equal false
|
|
272
|
+
case _ true
|
|
273
|
+
|
|
274
|
+
; What a held object makes of a list it is the first element of, from its
|
|
275
|
+
; members' texts: the default operator its \`src\` member names, as the
|
|
276
|
+
; reader's description of an operator does; :none where it has no \`src\`,
|
|
277
|
+
; or one no simplifier reads; and \`[:foreign text]\` for any other \`src\`,
|
|
278
|
+
; text its value as written. Where a key repeats, the last member is the
|
|
279
|
+
; one the reader keeps.
|
|
280
|
+
def expr-src-of [parts]
|
|
281
|
+
let [srcs (filter expr-is-src parts)]
|
|
282
|
+
match (count srcs)
|
|
204
283
|
case 0 :none
|
|
205
|
-
case _
|
|
284
|
+
case _
|
|
285
|
+
let [part (top srcs)]
|
|
286
|
+
match (expr-src-op part)
|
|
287
|
+
case :none
|
|
288
|
+
let [text (expr-src-value part)]
|
|
289
|
+
match (expr-src-read text)
|
|
290
|
+
case true [:foreign text]
|
|
291
|
+
case false :none
|
|
292
|
+
case op op
|
|
206
293
|
|
|
207
294
|
; An item as its list holds it: the first one, where it is an operator,
|
|
208
295
|
; as that operator's text, so that a description is written as the
|
|
@@ -306,6 +393,10 @@ def expr-top [s]
|
|
|
306
393
|
case 0 :none
|
|
307
394
|
case _ (top s)
|
|
308
395
|
|
|
396
|
+
; A vector's item at a position.
|
|
397
|
+
def expr-at [v i]
|
|
398
|
+
get-path (path i) v
|
|
399
|
+
|
|
309
400
|
def expr-mark [marker s]
|
|
310
401
|
push marker (pop s)
|
|
311
402
|
|
|
@@ -316,11 +407,11 @@ def expr-mark [marker s]
|
|
|
316
407
|
def expr-stream-value [s text inner]
|
|
317
408
|
match (expr-top s)
|
|
318
409
|
case :none (expr-push-or-done [] inner [text])
|
|
319
|
-
case [:list :stream
|
|
320
|
-
match
|
|
321
|
-
case
|
|
322
|
-
case
|
|
323
|
-
case [:map :stream :value] (expr-push-or-done (expr-mark [:map :stream
|
|
410
|
+
case [:list :stream index]
|
|
411
|
+
match index
|
|
412
|
+
case [] (expr-push-or-done (expr-mark [:list :stream ["0"]] s) inner [text])
|
|
413
|
+
case _ (expr-push-or-done (expr-mark [:list :stream (expr-count-up index)] s) inner ["," text])
|
|
414
|
+
case [:map :stream [:value k]] (expr-push-or-done (expr-mark [:map :stream [:after k] :key] s) inner [text])
|
|
324
415
|
|
|
325
416
|
def expr-push-or-done [s inner out]
|
|
326
417
|
match inner
|
|
@@ -353,7 +444,7 @@ def expr-held [s item]
|
|
|
353
444
|
; \`[\` first, and the rest of the list as it is read.
|
|
354
445
|
def expr-first-held [s item]
|
|
355
446
|
match item
|
|
356
|
-
case [text _ _ :none] (transition (expr-mark [:list :stream
|
|
447
|
+
case [text _ _ :none] (transition (expr-mark [:list :stream ["0"]] s) ["[" text])
|
|
357
448
|
case _ (transition (expr-mark [:list :hold [(expr-lead [] item)]] s) [])
|
|
358
449
|
|
|
359
450
|
; Whether the top of the stack holds what is read.
|
|
@@ -374,10 +465,10 @@ def expr-decide [s mode event]
|
|
|
374
465
|
match [mode event]
|
|
375
466
|
case [:stream (scalar value)]
|
|
376
467
|
match (expr-op value)
|
|
377
|
-
case :none [(expr-mark [:list :stream
|
|
468
|
+
case :none [(expr-mark [:list :stream []] s) "["]
|
|
378
469
|
case _ [(expr-mark [:list :hold []] s) ""]
|
|
379
470
|
case [:stream object-start] [(expr-mark [:list :first] s) ""]
|
|
380
|
-
case [:stream _] [(expr-mark [:list :stream
|
|
471
|
+
case [:stream _] [(expr-mark [:list :stream []] s) "["]
|
|
381
472
|
case _ [(expr-mark [:list :hold []] s) ""]
|
|
382
473
|
|
|
383
474
|
; A value where one is due: a scalar, or a container's start.
|
|
@@ -391,7 +482,7 @@ def expr-value [s event]
|
|
|
391
482
|
case false
|
|
392
483
|
match event
|
|
393
484
|
case (scalar value) (expr-stream-value s (expr-scalar-text value) :none)
|
|
394
|
-
case object-start (expr-stream-value s "{" [:map :stream
|
|
485
|
+
case object-start (expr-stream-value s "{" [:map :stream :first :key])
|
|
395
486
|
case array-start (expr-stream-value s "" [:pending :stream])
|
|
396
487
|
|
|
397
488
|
; The first element of a list, once the list has decided: what the
|
|
@@ -420,21 +511,130 @@ def expr-closed [s text]
|
|
|
420
511
|
|
|
421
512
|
def expr-key [s name]
|
|
422
513
|
match (expr-top s)
|
|
423
|
-
case [:map :stream first :key]
|
|
424
|
-
|
|
425
|
-
case true (transition (expr-mark [:map :stream :value] s) [(quoted name) ":"])
|
|
426
|
-
case false (transition (expr-mark [:map :stream :value] s) ["," (quoted name) ":"])
|
|
514
|
+
case [:map :stream :first :key] (transition (expr-mark [:map :stream [:value name]] s) [(quoted name) ":"])
|
|
515
|
+
case [:map :stream [:after _] :key] (transition (expr-mark [:map :stream [:value name]] s) ["," (quoted name) ":"])
|
|
427
516
|
case [:map :hold parts :key] (transition (expr-mark [:map :hold parts [:value (quoted name)]] s) [])
|
|
428
517
|
case _ (fail :protocol-order "the events hold a key where a value is due, which a tree's never do")
|
|
429
518
|
|
|
519
|
+
; An object ends. A held one becomes an item, which records the operator
|
|
520
|
+
; it describes; one that is the first element of a list, with a \`src\` a
|
|
521
|
+
; simplifier would read as that list's operator and no default one, is
|
|
522
|
+
; refused.
|
|
430
523
|
def expr-object-end [s]
|
|
431
524
|
match (expr-top s)
|
|
432
525
|
case [:map :stream _ :key] (expr-closed (pop s) "}")
|
|
433
526
|
case [:map :hold parts :key]
|
|
434
527
|
let [text (string-join "" ["{" (string-join "," parts) "}"])]
|
|
435
|
-
|
|
528
|
+
match (expr-src-of parts)
|
|
529
|
+
case [:foreign src]
|
|
530
|
+
match (expr-leads-list (pop s))
|
|
531
|
+
case true (expr-refuse (pop s) src)
|
|
532
|
+
case false (expr-held (pop s) [text :atom text :none])
|
|
533
|
+
case described (expr-held (pop s) [text :atom text described])
|
|
436
534
|
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")
|
|
437
535
|
|
|
536
|
+
; Whether the top of the stack is a list whose first element is due, so
|
|
537
|
+
; that an object ending there is its first element.
|
|
538
|
+
def expr-leads-list [s]
|
|
539
|
+
match (expr-top s)
|
|
540
|
+
case [:list :first] true
|
|
541
|
+
case [:list :hold []] true
|
|
542
|
+
case _ false
|
|
543
|
+
|
|
544
|
+
; A list led by an object whose \`src\` a simplifier would read as the
|
|
545
|
+
; list's operator, and which is no default operator's source text: no
|
|
546
|
+
; document reads back as the tree, so it is refused, naming the object,
|
|
547
|
+
; by the markers of the containers it is in, and its \`src\` as the render
|
|
548
|
+
; writes it.
|
|
549
|
+
def expr-refuse [s src]
|
|
550
|
+
fail :unrepresentable (string-join "" ["the object at " (expr-path-text s) " is the first element of a list and has the src " src ", which expr's reader would read back as the list's operator, keeping only the src; the render writes such a list only where the src is a default operator's source text"])
|
|
551
|
+
|
|
552
|
+
; The path to the value being read, from the markers of the containers it
|
|
553
|
+
; is in, in jq's syntax as aless prints and reads one: \`.name\` for a
|
|
554
|
+
; member whose key is a word, \`."key"\` for one whose key is not, and \`[i]\`
|
|
555
|
+
; for an element, \`.[i]\` at the root. While a value is read, the list it
|
|
556
|
+
; is in is held, written as it is read or deciding on its first element,
|
|
557
|
+
; and the object it is in has the value's key, held or written.
|
|
558
|
+
def expr-path-text [s]
|
|
559
|
+
let [text (string-join "" (map expr-path-part s))]
|
|
560
|
+
match (expr-at s 0)
|
|
561
|
+
case [:map _ _ _] text
|
|
562
|
+
case _ (string-join "" ["." text])
|
|
563
|
+
|
|
564
|
+
def expr-path-part [marker]
|
|
565
|
+
match marker
|
|
566
|
+
case [:list :first] "[0]"
|
|
567
|
+
case [:list :stream index] (string-join "" ["[" (string-join "" index) "]"])
|
|
568
|
+
case [:list :hold items] (string-join "" ["[" (scalar-text csv-options (count items)) "]"])
|
|
569
|
+
case [:map :stream [:after k] :key] (expr-path-key k)
|
|
570
|
+
case [:map :hold _ [:value k]] (expr-path-key (unquoted k))
|
|
571
|
+
|
|
572
|
+
; A key as a path writes it: bare where it is a word, letters, digits and
|
|
573
|
+
; underscores not led by a digit, and quoted otherwise.
|
|
574
|
+
def expr-path-key [name]
|
|
575
|
+
match [(length name) (chars-within [[48 57] [65 90] [95 95] [97 122]] name)]
|
|
576
|
+
case [0 _] (string-join "" ["." (quoted name)])
|
|
577
|
+
case [_ false] (string-join "" ["." (quoted name)])
|
|
578
|
+
case _
|
|
579
|
+
match (count (filter (fn [digit] (expr-leads digit name)) ["0" "1" "2" "3" "4" "5" "6" "7" "8" "9"]))
|
|
580
|
+
case 0 (string-join "" ["." name])
|
|
581
|
+
case _ (string-join "" ["." (quoted name)])
|
|
582
|
+
|
|
583
|
+
; Whether a string that is not empty begins with another.
|
|
584
|
+
def expr-leads [lead text]
|
|
585
|
+
match (expr-at (split lead text) 0)
|
|
586
|
+
case "" true
|
|
587
|
+
case _ false
|
|
588
|
+
|
|
589
|
+
; The digits of the next index: the last digit up by one, and a 9
|
|
590
|
+
; carried.
|
|
591
|
+
def expr-count-up [digits]
|
|
592
|
+
match (top digits)
|
|
593
|
+
case "9" (expr-carry digits)
|
|
594
|
+
case digit (push (expr-digit-up digit) (pop digits))
|
|
595
|
+
|
|
596
|
+
def expr-digit-up [digit]
|
|
597
|
+
match digit
|
|
598
|
+
case "0" "1"
|
|
599
|
+
case "1" "2"
|
|
600
|
+
case "2" "3"
|
|
601
|
+
case "3" "4"
|
|
602
|
+
case "4" "5"
|
|
603
|
+
case "5" "6"
|
|
604
|
+
case "6" "7"
|
|
605
|
+
case "7" "8"
|
|
606
|
+
case "8" "9"
|
|
607
|
+
|
|
608
|
+
; The digits after ones that end in a 9: the nines at the end become
|
|
609
|
+
; zeros and the digit before them goes up, or, where every digit is a 9, a
|
|
610
|
+
; 1 leads zeros one more than there were nines. Written as one string, a
|
|
611
|
+
; 9 for each nine and a bar for any other digit, the digits split at the
|
|
612
|
+
; bars, and what comes before the last bar is as long as the position of
|
|
613
|
+
; the digit that goes up.
|
|
614
|
+
def expr-carry [digits]
|
|
615
|
+
let [runs (split "|" (string-join "" (map expr-nine-or-bar digits)))]
|
|
616
|
+
match (count runs)
|
|
617
|
+
case 1 (push "0" (map expr-one-then-zero (indices digits)))
|
|
618
|
+
case _
|
|
619
|
+
let [up (length (string-join "|" (pop runs)))]
|
|
620
|
+
map (fn [i] (expr-carried digits up i)) (indices digits)
|
|
621
|
+
|
|
622
|
+
def expr-nine-or-bar [digit]
|
|
623
|
+
match digit
|
|
624
|
+
case "9" "9"
|
|
625
|
+
case _ "|"
|
|
626
|
+
|
|
627
|
+
def expr-one-then-zero [i]
|
|
628
|
+
match i
|
|
629
|
+
case 0 "1"
|
|
630
|
+
case _ "0"
|
|
631
|
+
|
|
632
|
+
def expr-carried [digits up i]
|
|
633
|
+
match (compare i up)
|
|
634
|
+
case :less (expr-at digits i)
|
|
635
|
+
case :equal (expr-digit-up (expr-at digits i))
|
|
636
|
+
case _ "0"
|
|
637
|
+
|
|
438
638
|
def expr-array-end [s]
|
|
439
639
|
match (expr-top s)
|
|
440
640
|
case [:pending mode] (expr-close s [:pending mode])
|
|
@@ -447,7 +647,7 @@ def expr-due [s event]
|
|
|
447
647
|
case :none (expr-value s event)
|
|
448
648
|
case [:pending mode] (expr-first s mode event)
|
|
449
649
|
case [:list _ _] (expr-value s event)
|
|
450
|
-
case [:map :stream :value] (expr-value s event)
|
|
650
|
+
case [:map :stream [:value _]] (expr-value s event)
|
|
451
651
|
case [:map :hold _ [:value _]] (expr-value s event)
|
|
452
652
|
case _ (fail :protocol-order "the events hold a value where a key is due, which a tree's never do")
|
|
453
653
|
|
package/dist/translate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../src/translate.ts"],"names":[],"mappings":";AAAA,yDAAyD;;
|
|
1
|
+
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../src/translate.ts"],"names":[],"mappings":";AAAA,yDAAyD;;QAsrBvD,SAAS;AArqBX,MAAM,WAAW,GAAqB,MAAM,CAAC,MAAM,CAAC;IAClD,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2CX;IACC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyCrD,EAAE,CAAC;IACF,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAskBvD,EAAE,CAAC;CACH,CAAC,CAAA;AAEF,SAAS,SAAS;IAChB,OAAO,WAAW,CAAA;AACpB,CAAC"}
|
package/package.json
CHANGED
package/src/expr.ts
CHANGED
|
@@ -1700,7 +1700,7 @@ const testing = {
|
|
|
1700
1700
|
// VERSION is this package's version. It MUST equal package.json "version":
|
|
1701
1701
|
// the release orchestrator rewrites both, and the version test fails the
|
|
1702
1702
|
// build if they drift. Mirrors `const VERSION` in go/expr.go.
|
|
1703
|
-
const VERSION = '0.5.
|
|
1703
|
+
const VERSION = '0.5.19'
|
|
1704
1704
|
|
|
1705
1705
|
export { VERSION, Expr, evaluation, simplify, parseSimplified, testing }
|
|
1706
1706
|
|
package/src/translate.ts
CHANGED
|
@@ -52,10 +52,11 @@ const TRANSLATION: TranslationParts = Object.freeze({
|
|
|
52
52
|
"loss": [
|
|
53
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
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.",
|
|
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. A list whose first element is an object with any other src that is not empty, zero, false or null, as a C syntax tree's token has, is refused with TARGET_VALUE_UNREPRESENTABLE, since the reader would read that src alone as the list's operator.",
|
|
56
56
|
"A negative number is written with its sign, which reads back as the operator - applied to the number's magnitude.",
|
|
57
57
|
"A number that is not finite is written as the string Infinity, -Infinity or NaN."
|
|
58
|
-
]
|
|
58
|
+
],
|
|
59
|
+
"whole": "A binary operator follows its left term, and a later operator can take a finished operation as its own term, as the - in 1*2-3 takes 1*2, so an expression's tree is complete only at its end: a document is read whole before anything is written."
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
`,
|
|
@@ -67,7 +68,7 @@ const TRANSLATION: TranslationParts = Object.freeze({
|
|
|
67
68
|
; its reverse, takes them back. Nothing else here is the host's to call.
|
|
68
69
|
;
|
|
69
70
|
; Both are the identity. The reader builds its trees on jsonic's values,
|
|
70
|
-
; so
|
|
71
|
+
; so a plain tree is already an expression tree: a list whose first
|
|
71
72
|
; element is a default operator and whose count makes an operation is
|
|
72
73
|
; one, and the render writes it in infix, the operator being its source
|
|
73
74
|
; text (\`+\`, \`-\`, \`*\`, \`/\`, \`%\`, or \`(\` for a group) or an object that
|
|
@@ -76,11 +77,22 @@ const TRANSLATION: TranslationParts = Object.freeze({
|
|
|
76
77
|
; render writes as jsonic. A list that only looks like an operation
|
|
77
78
|
; reads back as the same tree either way, once each operator is reduced
|
|
78
79
|
; to its source text as the shared fixtures' simplifier, Go's \`Simplify\`
|
|
79
|
-
; and Rust's \`simplify\` reduce it
|
|
80
|
-
;
|
|
81
|
-
;
|
|
82
|
-
;
|
|
83
|
-
;
|
|
80
|
+
; and Rust's \`simplify\` reduce it.
|
|
81
|
+
;
|
|
82
|
+
; The plain trees that are not expression trees are the lists led by an
|
|
83
|
+
; object whose \`src\` member the simplifiers read as an operator that is
|
|
84
|
+
; not a default one, as they read a C syntax tree's tokens: they keep
|
|
85
|
+
; that \`src\` alone, so no document reads back as such a list. The render
|
|
86
|
+
; refuses it, with TARGET_VALUE_UNREPRESENTABLE and the object's path,
|
|
87
|
+
; rather than this embedding, since the render meets such a list from
|
|
88
|
+
; any source, one of the schema \`expr\` too, which reaches the render
|
|
89
|
+
; without an embedding, and it already holds the first object of a list
|
|
90
|
+
; to read the operator that object describes.
|
|
91
|
+
;
|
|
92
|
+
; What the round trip changes is what the render's conventions change
|
|
93
|
+
; (the manifest's loss list): an operator's description is written as
|
|
94
|
+
; its source text, a negative number reads back as the operator \`-\`
|
|
95
|
+
; applied to its magnitude, and a number that is not finite as a string.
|
|
84
96
|
|
|
85
97
|
; The embedding: a plain tree's events in, the same events out.
|
|
86
98
|
def expr-embed [input]
|
|
@@ -109,6 +121,15 @@ def expr-unembed [input]
|
|
|
109
121
|
; text is, or the operator an object describes, read by its \`src\` member
|
|
110
122
|
; as those simplifiers read it, when that is a default operator's source.
|
|
111
123
|
;
|
|
124
|
+
; The simplifiers read any list led by an object with a \`src\` member that
|
|
125
|
+
; way, and keep that member's value alone: TypeScript's \`simplify\` where
|
|
126
|
+
; the \`src\` is not empty, zero, false or null, and Go's \`SimplifyOrdered\`
|
|
127
|
+
; and Rust's \`simplify\` where it is a string other than the empty one. So
|
|
128
|
+
; a list led by an object whose \`src\` they read and is not a default
|
|
129
|
+
; operator's source, as the tokens a C syntax tree puts first in a node's
|
|
130
|
+
; children are, is no tree of the reader's: no document reads back as
|
|
131
|
+
; it, and the render refuses it (below).
|
|
132
|
+
;
|
|
112
133
|
; Every value is written so that the reader builds the same tree from it:
|
|
113
134
|
;
|
|
114
135
|
; - An operation is written in infix: \`a+b\` for a list of an infix
|
|
@@ -131,6 +152,17 @@ def expr-unembed [input]
|
|
|
131
152
|
; its sign, which the reader reads as the operator \`-\` applied to the
|
|
132
153
|
; number's magnitude, and a number that is not finite, which jsonic
|
|
133
154
|
; has no spelling for, as the string \`Infinity\`, \`-Infinity\` or \`NaN\`.
|
|
155
|
+
; - A list whose first element is an object with a \`src\` member that the
|
|
156
|
+
; simplifiers read is written as an operation only where that \`src\` is
|
|
157
|
+
; a default operator's source text, as the reader's own value describes
|
|
158
|
+
; the operator (the manifest's loss list says the object's other
|
|
159
|
+
; members are not kept). With any other such \`src\` the list fails with
|
|
160
|
+
; TARGET_VALUE_UNREPRESENTABLE, naming the object's path, in jq's
|
|
161
|
+
; syntax, and its \`src\` as the render writes it. The \`src\` is judged as
|
|
162
|
+
; it is written, since that is what the reader reads: a number that is
|
|
163
|
+
; not finite as a string, and a negative number as the operation its
|
|
164
|
+
; sign makes, a list. An object whose \`src\` is empty, zero, false or
|
|
165
|
+
; null is written as it is, and reads back as itself.
|
|
134
166
|
;
|
|
135
167
|
; No white space is written, since jsonic reads a space between two
|
|
136
168
|
; values as an implicit list, and the document ends with one line break.
|
|
@@ -156,16 +188,25 @@ def expr-unembed [input]
|
|
|
156
188
|
; [:list :first] a list whose first element, an object,
|
|
157
189
|
; is being held, which decides how the
|
|
158
190
|
; list is written
|
|
159
|
-
; [:list :stream
|
|
160
|
-
; until an element is written
|
|
191
|
+
; [:list :stream index] a list written as it is read; index is
|
|
192
|
+
; [] until an element is written, and then
|
|
193
|
+
; the decimal digits of the last one's
|
|
194
|
+
; index, most significant first
|
|
161
195
|
; [:list :hold items] a held list, and the items read so far
|
|
162
|
-
; [:map :stream first :key]
|
|
163
|
-
;
|
|
164
|
-
; [:map :stream :value]
|
|
196
|
+
; [:map :stream :first :key] an object written as it is read, its
|
|
197
|
+
; first key or its end due
|
|
198
|
+
; [:map :stream [:value k]] the same, the value of the key k due
|
|
199
|
+
; [:map :stream [:after k] :key] the same, after the member of the key
|
|
200
|
+
; k, its next key or its end due
|
|
165
201
|
; [:map :hold parts :key] an object inside a held list, or the
|
|
166
202
|
; first element of a list, and its
|
|
167
203
|
; members' texts
|
|
168
|
-
; [:map :hold parts [:value k]] the same, the value of the key k due
|
|
204
|
+
; [:map :hold parts [:value k]] the same, the value of the key k due,
|
|
205
|
+
; k quoted
|
|
206
|
+
;
|
|
207
|
+
; The markers say where the render is, which only a refusal reads: the
|
|
208
|
+
; list a value is in, and its index, or the object and its key. alchemy
|
|
209
|
+
; has no arithmetic, so an index goes up a digit at a time.
|
|
169
210
|
;
|
|
170
211
|
; An item is \`[text class list head]\`: the text a value is written as;
|
|
171
212
|
; its class, \`:infix2\` for an operation of \`+\` or \`-\`, \`:infix3\` for one
|
|
@@ -203,19 +244,65 @@ def expr-src-op [part]
|
|
|
203
244
|
case "\\"src\\":\\"(\\"" "("
|
|
204
245
|
case _ :none
|
|
205
246
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
247
|
+
; Whether a held object's member is its \`src\`: a member's text is its
|
|
248
|
+
; quoted key, a colon and its value's text, and a quoted key ends at its
|
|
249
|
+
; first unescaped quote, so the member is \`src\` exactly when its text
|
|
250
|
+
; begins with "src":.
|
|
251
|
+
def expr-is-src [part]
|
|
252
|
+
expr-leads "\\"src\\":" part
|
|
253
|
+
|
|
254
|
+
; The text of a \`src\` member's value, what follows its key and colon.
|
|
255
|
+
def expr-src-value [part]
|
|
256
|
+
let [pieces (split "\\"src\\":" part)]
|
|
257
|
+
string-join "\\"src\\":" (map (fn [i] (expr-at pieces i)) (filter expr-after-first (indices pieces)))
|
|
258
|
+
|
|
259
|
+
def expr-after-first [i]
|
|
260
|
+
match i
|
|
261
|
+
case 0 false
|
|
209
262
|
case _ true
|
|
210
263
|
|
|
211
|
-
;
|
|
212
|
-
; \`src\` member
|
|
213
|
-
;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
264
|
+
; Whether a simplifier takes a list led by an object for an operation,
|
|
265
|
+
; the object's \`src\` member's value written as this text: TypeScript's
|
|
266
|
+
; does where the value is not empty, zero, false or null, and Go's and
|
|
267
|
+
; Rust's where it is a string other than the empty one, a narrower set.
|
|
268
|
+
; The value is judged as written, which is what the reader reads: a
|
|
269
|
+
; number that is not finite is written as a string, and a negative one
|
|
270
|
+
; with its sign, which reads back as an operation, a list.
|
|
271
|
+
def expr-src-read [text]
|
|
272
|
+
match text
|
|
273
|
+
case "\\"\\"" false
|
|
274
|
+
case "false" false
|
|
275
|
+
case "null" false
|
|
276
|
+
case _
|
|
277
|
+
match (is-number text)
|
|
278
|
+
case false true
|
|
279
|
+
case true
|
|
280
|
+
match (expr-at (split "-" text) 0)
|
|
281
|
+
case "" true
|
|
282
|
+
case _
|
|
283
|
+
match (compare (number text) 0)
|
|
284
|
+
case :equal false
|
|
285
|
+
case _ true
|
|
286
|
+
|
|
287
|
+
; What a held object makes of a list it is the first element of, from its
|
|
288
|
+
; members' texts: the default operator its \`src\` member names, as the
|
|
289
|
+
; reader's description of an operator does; :none where it has no \`src\`,
|
|
290
|
+
; or one no simplifier reads; and \`[:foreign text]\` for any other \`src\`,
|
|
291
|
+
; text its value as written. Where a key repeats, the last member is the
|
|
292
|
+
; one the reader keeps.
|
|
293
|
+
def expr-src-of [parts]
|
|
294
|
+
let [srcs (filter expr-is-src parts)]
|
|
295
|
+
match (count srcs)
|
|
217
296
|
case 0 :none
|
|
218
|
-
case _
|
|
297
|
+
case _
|
|
298
|
+
let [part (top srcs)]
|
|
299
|
+
match (expr-src-op part)
|
|
300
|
+
case :none
|
|
301
|
+
let [text (expr-src-value part)]
|
|
302
|
+
match (expr-src-read text)
|
|
303
|
+
case true [:foreign text]
|
|
304
|
+
case false :none
|
|
305
|
+
case op op
|
|
219
306
|
|
|
220
307
|
; An item as its list holds it: the first one, where it is an operator,
|
|
221
308
|
; as that operator's text, so that a description is written as the
|
|
@@ -319,6 +406,10 @@ def expr-top [s]
|
|
|
319
406
|
case 0 :none
|
|
320
407
|
case _ (top s)
|
|
321
408
|
|
|
409
|
+
; A vector's item at a position.
|
|
410
|
+
def expr-at [v i]
|
|
411
|
+
get-path (path i) v
|
|
412
|
+
|
|
322
413
|
def expr-mark [marker s]
|
|
323
414
|
push marker (pop s)
|
|
324
415
|
|
|
@@ -329,11 +420,11 @@ def expr-mark [marker s]
|
|
|
329
420
|
def expr-stream-value [s text inner]
|
|
330
421
|
match (expr-top s)
|
|
331
422
|
case :none (expr-push-or-done [] inner [text])
|
|
332
|
-
case [:list :stream
|
|
333
|
-
match
|
|
334
|
-
case
|
|
335
|
-
case
|
|
336
|
-
case [:map :stream :value] (expr-push-or-done (expr-mark [:map :stream
|
|
423
|
+
case [:list :stream index]
|
|
424
|
+
match index
|
|
425
|
+
case [] (expr-push-or-done (expr-mark [:list :stream ["0"]] s) inner [text])
|
|
426
|
+
case _ (expr-push-or-done (expr-mark [:list :stream (expr-count-up index)] s) inner ["," text])
|
|
427
|
+
case [:map :stream [:value k]] (expr-push-or-done (expr-mark [:map :stream [:after k] :key] s) inner [text])
|
|
337
428
|
|
|
338
429
|
def expr-push-or-done [s inner out]
|
|
339
430
|
match inner
|
|
@@ -366,7 +457,7 @@ def expr-held [s item]
|
|
|
366
457
|
; \`[\` first, and the rest of the list as it is read.
|
|
367
458
|
def expr-first-held [s item]
|
|
368
459
|
match item
|
|
369
|
-
case [text _ _ :none] (transition (expr-mark [:list :stream
|
|
460
|
+
case [text _ _ :none] (transition (expr-mark [:list :stream ["0"]] s) ["[" text])
|
|
370
461
|
case _ (transition (expr-mark [:list :hold [(expr-lead [] item)]] s) [])
|
|
371
462
|
|
|
372
463
|
; Whether the top of the stack holds what is read.
|
|
@@ -387,10 +478,10 @@ def expr-decide [s mode event]
|
|
|
387
478
|
match [mode event]
|
|
388
479
|
case [:stream (scalar value)]
|
|
389
480
|
match (expr-op value)
|
|
390
|
-
case :none [(expr-mark [:list :stream
|
|
481
|
+
case :none [(expr-mark [:list :stream []] s) "["]
|
|
391
482
|
case _ [(expr-mark [:list :hold []] s) ""]
|
|
392
483
|
case [:stream object-start] [(expr-mark [:list :first] s) ""]
|
|
393
|
-
case [:stream _] [(expr-mark [:list :stream
|
|
484
|
+
case [:stream _] [(expr-mark [:list :stream []] s) "["]
|
|
394
485
|
case _ [(expr-mark [:list :hold []] s) ""]
|
|
395
486
|
|
|
396
487
|
; A value where one is due: a scalar, or a container's start.
|
|
@@ -404,7 +495,7 @@ def expr-value [s event]
|
|
|
404
495
|
case false
|
|
405
496
|
match event
|
|
406
497
|
case (scalar value) (expr-stream-value s (expr-scalar-text value) :none)
|
|
407
|
-
case object-start (expr-stream-value s "{" [:map :stream
|
|
498
|
+
case object-start (expr-stream-value s "{" [:map :stream :first :key])
|
|
408
499
|
case array-start (expr-stream-value s "" [:pending :stream])
|
|
409
500
|
|
|
410
501
|
; The first element of a list, once the list has decided: what the
|
|
@@ -433,21 +524,130 @@ def expr-closed [s text]
|
|
|
433
524
|
|
|
434
525
|
def expr-key [s name]
|
|
435
526
|
match (expr-top s)
|
|
436
|
-
case [:map :stream first :key]
|
|
437
|
-
|
|
438
|
-
case true (transition (expr-mark [:map :stream :value] s) [(quoted name) ":"])
|
|
439
|
-
case false (transition (expr-mark [:map :stream :value] s) ["," (quoted name) ":"])
|
|
527
|
+
case [:map :stream :first :key] (transition (expr-mark [:map :stream [:value name]] s) [(quoted name) ":"])
|
|
528
|
+
case [:map :stream [:after _] :key] (transition (expr-mark [:map :stream [:value name]] s) ["," (quoted name) ":"])
|
|
440
529
|
case [:map :hold parts :key] (transition (expr-mark [:map :hold parts [:value (quoted name)]] s) [])
|
|
441
530
|
case _ (fail :protocol-order "the events hold a key where a value is due, which a tree's never do")
|
|
442
531
|
|
|
532
|
+
; An object ends. A held one becomes an item, which records the operator
|
|
533
|
+
; it describes; one that is the first element of a list, with a \`src\` a
|
|
534
|
+
; simplifier would read as that list's operator and no default one, is
|
|
535
|
+
; refused.
|
|
443
536
|
def expr-object-end [s]
|
|
444
537
|
match (expr-top s)
|
|
445
538
|
case [:map :stream _ :key] (expr-closed (pop s) "}")
|
|
446
539
|
case [:map :hold parts :key]
|
|
447
540
|
let [text (string-join "" ["{" (string-join "," parts) "}"])]
|
|
448
|
-
|
|
541
|
+
match (expr-src-of parts)
|
|
542
|
+
case [:foreign src]
|
|
543
|
+
match (expr-leads-list (pop s))
|
|
544
|
+
case true (expr-refuse (pop s) src)
|
|
545
|
+
case false (expr-held (pop s) [text :atom text :none])
|
|
546
|
+
case described (expr-held (pop s) [text :atom text described])
|
|
449
547
|
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
548
|
|
|
549
|
+
; Whether the top of the stack is a list whose first element is due, so
|
|
550
|
+
; that an object ending there is its first element.
|
|
551
|
+
def expr-leads-list [s]
|
|
552
|
+
match (expr-top s)
|
|
553
|
+
case [:list :first] true
|
|
554
|
+
case [:list :hold []] true
|
|
555
|
+
case _ false
|
|
556
|
+
|
|
557
|
+
; A list led by an object whose \`src\` a simplifier would read as the
|
|
558
|
+
; list's operator, and which is no default operator's source text: no
|
|
559
|
+
; document reads back as the tree, so it is refused, naming the object,
|
|
560
|
+
; by the markers of the containers it is in, and its \`src\` as the render
|
|
561
|
+
; writes it.
|
|
562
|
+
def expr-refuse [s src]
|
|
563
|
+
fail :unrepresentable (string-join "" ["the object at " (expr-path-text s) " is the first element of a list and has the src " src ", which expr's reader would read back as the list's operator, keeping only the src; the render writes such a list only where the src is a default operator's source text"])
|
|
564
|
+
|
|
565
|
+
; The path to the value being read, from the markers of the containers it
|
|
566
|
+
; is in, in jq's syntax as aless prints and reads one: \`.name\` for a
|
|
567
|
+
; member whose key is a word, \`."key"\` for one whose key is not, and \`[i]\`
|
|
568
|
+
; for an element, \`.[i]\` at the root. While a value is read, the list it
|
|
569
|
+
; is in is held, written as it is read or deciding on its first element,
|
|
570
|
+
; and the object it is in has the value's key, held or written.
|
|
571
|
+
def expr-path-text [s]
|
|
572
|
+
let [text (string-join "" (map expr-path-part s))]
|
|
573
|
+
match (expr-at s 0)
|
|
574
|
+
case [:map _ _ _] text
|
|
575
|
+
case _ (string-join "" ["." text])
|
|
576
|
+
|
|
577
|
+
def expr-path-part [marker]
|
|
578
|
+
match marker
|
|
579
|
+
case [:list :first] "[0]"
|
|
580
|
+
case [:list :stream index] (string-join "" ["[" (string-join "" index) "]"])
|
|
581
|
+
case [:list :hold items] (string-join "" ["[" (scalar-text csv-options (count items)) "]"])
|
|
582
|
+
case [:map :stream [:after k] :key] (expr-path-key k)
|
|
583
|
+
case [:map :hold _ [:value k]] (expr-path-key (unquoted k))
|
|
584
|
+
|
|
585
|
+
; A key as a path writes it: bare where it is a word, letters, digits and
|
|
586
|
+
; underscores not led by a digit, and quoted otherwise.
|
|
587
|
+
def expr-path-key [name]
|
|
588
|
+
match [(length name) (chars-within [[48 57] [65 90] [95 95] [97 122]] name)]
|
|
589
|
+
case [0 _] (string-join "" ["." (quoted name)])
|
|
590
|
+
case [_ false] (string-join "" ["." (quoted name)])
|
|
591
|
+
case _
|
|
592
|
+
match (count (filter (fn [digit] (expr-leads digit name)) ["0" "1" "2" "3" "4" "5" "6" "7" "8" "9"]))
|
|
593
|
+
case 0 (string-join "" ["." name])
|
|
594
|
+
case _ (string-join "" ["." (quoted name)])
|
|
595
|
+
|
|
596
|
+
; Whether a string that is not empty begins with another.
|
|
597
|
+
def expr-leads [lead text]
|
|
598
|
+
match (expr-at (split lead text) 0)
|
|
599
|
+
case "" true
|
|
600
|
+
case _ false
|
|
601
|
+
|
|
602
|
+
; The digits of the next index: the last digit up by one, and a 9
|
|
603
|
+
; carried.
|
|
604
|
+
def expr-count-up [digits]
|
|
605
|
+
match (top digits)
|
|
606
|
+
case "9" (expr-carry digits)
|
|
607
|
+
case digit (push (expr-digit-up digit) (pop digits))
|
|
608
|
+
|
|
609
|
+
def expr-digit-up [digit]
|
|
610
|
+
match digit
|
|
611
|
+
case "0" "1"
|
|
612
|
+
case "1" "2"
|
|
613
|
+
case "2" "3"
|
|
614
|
+
case "3" "4"
|
|
615
|
+
case "4" "5"
|
|
616
|
+
case "5" "6"
|
|
617
|
+
case "6" "7"
|
|
618
|
+
case "7" "8"
|
|
619
|
+
case "8" "9"
|
|
620
|
+
|
|
621
|
+
; The digits after ones that end in a 9: the nines at the end become
|
|
622
|
+
; zeros and the digit before them goes up, or, where every digit is a 9, a
|
|
623
|
+
; 1 leads zeros one more than there were nines. Written as one string, a
|
|
624
|
+
; 9 for each nine and a bar for any other digit, the digits split at the
|
|
625
|
+
; bars, and what comes before the last bar is as long as the position of
|
|
626
|
+
; the digit that goes up.
|
|
627
|
+
def expr-carry [digits]
|
|
628
|
+
let [runs (split "|" (string-join "" (map expr-nine-or-bar digits)))]
|
|
629
|
+
match (count runs)
|
|
630
|
+
case 1 (push "0" (map expr-one-then-zero (indices digits)))
|
|
631
|
+
case _
|
|
632
|
+
let [up (length (string-join "|" (pop runs)))]
|
|
633
|
+
map (fn [i] (expr-carried digits up i)) (indices digits)
|
|
634
|
+
|
|
635
|
+
def expr-nine-or-bar [digit]
|
|
636
|
+
match digit
|
|
637
|
+
case "9" "9"
|
|
638
|
+
case _ "|"
|
|
639
|
+
|
|
640
|
+
def expr-one-then-zero [i]
|
|
641
|
+
match i
|
|
642
|
+
case 0 "1"
|
|
643
|
+
case _ "0"
|
|
644
|
+
|
|
645
|
+
def expr-carried [digits up i]
|
|
646
|
+
match (compare i up)
|
|
647
|
+
case :less (expr-at digits i)
|
|
648
|
+
case :equal (expr-digit-up (expr-at digits i))
|
|
649
|
+
case _ "0"
|
|
650
|
+
|
|
451
651
|
def expr-array-end [s]
|
|
452
652
|
match (expr-top s)
|
|
453
653
|
case [:pending mode] (expr-close s [:pending mode])
|
|
@@ -460,7 +660,7 @@ def expr-due [s event]
|
|
|
460
660
|
case :none (expr-value s event)
|
|
461
661
|
case [:pending mode] (expr-first s mode event)
|
|
462
662
|
case [:list _ _] (expr-value s event)
|
|
463
|
-
case [:map :stream :value] (expr-value s event)
|
|
663
|
+
case [:map :stream [:value _]] (expr-value s event)
|
|
464
664
|
case [:map :hold _ [:value _]] (expr-value s event)
|
|
465
665
|
case _ (fail :protocol-order "the events hold a value where a key is due, which a tree's never do")
|
|
466
666
|
|