@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 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.17";
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.17';
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 every plain tree is already an expression tree: a list whose first
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, so nothing needs refusing. What the
67
- ; round trip changes is what the render's conventions change (the
68
- ; manifest's loss list): an operator's description is written as its
69
- ; source text, a negative number reads back as the operator \`-\` applied
70
- ; to its magnitude, and a number that is not finite as a string.
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 first] a list written as it is read; first
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] an object written as it is read, its
150
- ; next key or its end due
151
- ; [:map :stream :value] the same, a member's value due
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
- def expr-is-op [op]
194
- match op
195
- case :none false
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
- ; The operator an object describes, from its members' texts: the one its
199
- ; \`src\` member names, as the reader's description of an operator does, or
200
- ; :none.
201
- def expr-described [parts]
202
- let [ops (filter expr-is-op (map expr-src-op parts))]
203
- match (count ops)
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 _ (top ops)
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 first]
320
- match first
321
- case true (expr-push-or-done (expr-mark [:list :stream false] s) inner [text])
322
- case false (expr-push-or-done (expr-mark [:list :stream false] s) inner ["," text])
323
- case [:map :stream :value] (expr-push-or-done (expr-mark [:map :stream false :key] s) inner [text])
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 false] s) ["[" text])
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 true] s) "["]
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 true] s) "["]
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 true :key])
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
- match first
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
- expr-held (pop s) [text :atom text (expr-described parts)]
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
 
@@ -1 +1 @@
1
- {"version":3,"file":"translate.js","sourceRoot":"","sources":["../src/translate.ts"],"names":[],"mappings":";AAAA,yDAAyD;;QA8evD,SAAS;AA7dX,MAAM,WAAW,GAAqB,MAAM,CAAC,MAAM,CAAC;IAClD,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CX;IACC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BrD,EAAE,CAAC;IACF,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0YvD,EAAE,CAAC;CACH,CAAC,CAAA;AAEF,SAAS,SAAS;IAChB,OAAO,WAAW,CAAA;AACpB,CAAC"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tabnas/expr",
3
- "version": "0.5.17",
3
+ "version": "0.5.19",
4
4
  "description": "This plugin allows the [Tabnas](https://github.com/tabnas/jsonic) JSON parser to support expression syntax.",
5
5
  "main": "dist/expr.js",
6
6
  "type": "commonjs",
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.17'
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 every plain tree is already an expression tree: a list whose first
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, 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.
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 first] a list written as it is read; first
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] 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
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
- def expr-is-op [op]
207
- match op
208
- case :none false
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
- ; 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)
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 _ (top ops)
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 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])
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 false] s) ["[" text])
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 true] s) "["]
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 true] s) "["]
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 true :key])
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
- 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) ":"])
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
- expr-held (pop s) [text :atom text (expr-described parts)]
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