@tabnas/abnf 0.4.21 → 0.4.22

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/src/translate.ts CHANGED
@@ -56,12 +56,13 @@ const TRANSLATION: TranslationParts = Object.freeze({
56
56
  "Where the compiler reordered a rule's alternatives to tell them apart (a longer lookahead ahead of a shorter one that begins with the same character, a keyword ahead of a character class that can take it), the alternatives that push a helper of the compiler's are put back in the order its numbers record, and the others are written in the compiled order: the rule compiles back the same, and the spec compiled back can differ in the order of the alternates other rules build through it, in the numbers of the compiler's helpers and in the order of its tokens, which is the order the lexer tries two tokens a place expects, so where a keyword and a class that can take it trade places the grammar compiled back accepts what the spec refused (start = ident / \\"if\\" ident with ident = %x61-7A: the spec refuses ifx, and the text compiles back to one that accepts it).",
57
57
  "A left recursion that ran through another rule (P = Q, Q = P \\"+\\" / NR) is written as the compiler rewrote it, with the leading reference that closed the cycle left in place, and compiles back to another spec, since the compiler now substitutes that reference.",
58
58
  "The start rule is written first, as ABNF's start rule is its first; a spec whose start rule was not its first rule compiles back with that rule moved first.",
59
- "Terminals are written in RFC 5234's forms: a literal as a quoted string (with %s where it matches exactly and holds a letter) or, where a quoted string cannot hold it, as a numeric value of its code points, a range as %x and its ends, and a rule the compiler made a token of (CR = %x0D, referenced as CR) as that rule, after the others.",
59
+ "Terminals are written in RFC 5234's forms: a literal as a quoted string (with %s where it matches exactly and holds a letter) or, where a quoted string cannot hold it, as a numeric value of its code points, a range as %x and its ends, the class of every code point (GBNF's dot) as %x0-10FFFF, and a rule the compiler made a token of (CR = %x0D, referenced as CR) as that rule, after the others.",
60
60
  "A rule the compiler made a token of, whose name is one the compiler could give its literal by itself (B = \\"b\\", or any name of capitals, digits and underscores where the literal holds a character past ASCII), cannot be told from that literal and is written as the literal: it compiles back with the token later in the token table, or under the name the literal gives it.",
61
61
  "Repetitions are written in RFC 5234's forms, *A, 1*A, m*nA, [ A ] for an option over a group and 0*1A for an option over anything else; a repetition of a repetition, which another notation can write and RFC 5234 cannot, is put in a group of its own and compiles back with one more group.",
62
62
  "An empty alternative, which RFC 5234 has no syntax for, is written as nothing between its slashes, which the compiler reads as one; in a rule whose alternatives the compiler emits as its open alternates, where it puts the empty one last wherever it stood, it is written first.",
63
63
  "A spec compiled from another notation compiles back under ABNF's own settings: the group tag on every alternate, and lexer options the other notation adds (GBNF's exact lexing), are ABNF's, and a class of several members, which RFC 5234 cannot write, is written as the alternation of its members, each range a range and each character a string or a range of one, and compiles back as that alternation.",
64
- "A spec the render cannot write as ABNF is refused with TARGET_VALUE_UNREPRESENTABLE, naming what it met: an action (a value annotation's builders, a user action, the probe dispatcher of an optional prefix), a condition or a counter other than a repetition's, an error generator or an alternate modifier, a function reference where a rule or a count is due, a set of tokens at one place, a removal, a clear or the form that edits a rule already installed, a token no ABNF terminal matches (a negated class, a class holding a character past ASCII alone, a class whose flags change what it matches, a pattern that is neither one class nor an escaped literal, a case-sensitive literal holding a letter and a character no quoted string holds, a character past U+FFFF), a rule name ABNF cannot spell, the empty name among them, a repetition bounded past what the compiler numbers, a repetition of no copies, whose item the spec does not keep, or a spec that gives its match tokens' order as a list of its own (Go's tokenOrder), whose rules' order, which ranks the tokens, is lost."
64
+ "The tree builders every compiled grammar carries (@node$, @capture$, @bubble$ and @fold$, with their k settings) and its marks are not written, since the compiler makes them again from the rules: a spec whose builders or marks are others compiles back with the compiler's own, and recognises what it recognised.",
65
+ "A spec the render cannot write as ABNF is refused with TARGET_VALUE_UNREPRESENTABLE, naming what it met: an action (a value annotation's builders, a user action, the probe dispatcher of an optional prefix), a condition or a counter other than a repetition's, an error generator or an alternate modifier, a function reference where a rule or a count is due, a set of tokens at one place, a removal, a clear or the form that edits a rule already installed, a token no ABNF terminal matches (a negated class, a class holding a character past ASCII alone, a class whose flags change what it matches (no u on a class that is negated, reaches past U+FFFF or holds every code point, or u on one that holds a surrogate), a pattern that is neither one class nor an escaped literal, a case-sensitive literal holding a letter and a character no quoted string holds, a case-insensitive literal holding a character past ASCII and no ASCII letter, whose numeric value would match one case alone, a character past U+FFFF), a token set whose tokens are not the class its name gives, a rule with no alternate to open with, a sequence's step with more alternates than a step, a rule the compiler lifted to a token whose name another rule holds, a start that is not the start wrapper every grammar text compiles to (one open alternate that matches nothing and pushes a rule, closed by the end of the source alone), a rule name ABNF cannot spell, the empty name among them, a repetition bounded past what the compiler numbers, a repetition of no copies, whose item the spec does not keep, or a spec that gives its match tokens' order as a list of its own (Go's tokenOrder), whose rules' order, which ranks the tokens, is lost."
65
66
  ]
66
67
  }
67
68
  }
@@ -85,17 +86,20 @@ const TRANSLATION: TranslationParts = Object.freeze({
85
86
  ; reads the compiler's shapes back into the notation:
86
87
  ;
87
88
  ; - A rule the author wrote is a rule of the spec that the spec's
88
- ; \`meta.provenance\` does not name; every rule it names is the
89
- ; compiler's: a repetition's, an option's or a group's helper
90
- ; (\`_gen<n>_star_...\`, \`_plus_\`, \`_opt_\`, \`_rep_\`, \`_group\`), a
91
- ; sequence's chain steps (\`$alt<i>\`, \`$step<j>\`), a left-factored tail
92
- ; (\`$fact<k>\`) and the start wrapper. A helper is written where it is
93
- ; referenced, as the construct it compiles: a repeat loop (the replace
94
- ; loop every \`*A\` compiles to, its entry \`{c: {n.rep: 0}, r: <self>}\`)
95
- ; as \`*A\`, a plus as \`1*A\`, an option over a group as \`[ ... ]\` and over
96
- ; anything else as \`0*1A\`, a counted repetition as \`m*nA\` (its nested
97
- ; optionals counted), a group as \`( ... )\`. A factored tail is expanded
98
- ; back into the alternatives it was factored from, which the compiler
89
+ ; \`meta.provenance\` does not name (a spec without it is read by the
90
+ ; names the compiler gives its rules, a helper's only where it has
91
+ ; the shape of its kind too, and the start wrapper by its shape);
92
+ ; every rule it names is the compiler's: a repetition's, an
93
+ ; option's or a group's helper (\`_gen<n>_star_...\`, \`_plus_\`, \`_opt_\`,
94
+ ; \`_rep_\`, \`_group\`), a sequence's chain steps (\`$alt<i>\`,
95
+ ; \`$step<j>\`), a left-factored tail (\`$fact<k>\`) and the start
96
+ ; wrapper. A helper is written where it is referenced, as the
97
+ ; construct it compiles: a repeat loop (the replace loop every \`*A\`
98
+ ; compiles to, its entry \`{c: {n.rep: 0}, r: <self>}\`) as \`*A\`, a plus
99
+ ; as \`1*A\`, an option over a group as \`[ ... ]\` and over anything else
100
+ ; as \`0*1A\`, a counted repetition as \`m*nA\` (its nested optionals
101
+ ; counted), a group as \`( ... )\`. A factored tail is expanded back
102
+ ; into the alternatives it was factored from, which the compiler
99
103
  ; factors again, and alternatives that were each a group, which the
100
104
  ; compiler factored into the first, are read back as the groups they
101
105
  ; were.
@@ -115,32 +119,48 @@ const TRANSLATION: TranslationParts = Object.freeze({
115
119
  ; by one same tail, still stand together.
116
120
  ; - A token is written as the terminal it matches: a fixed token as a
117
121
  ; quoted string, \`%s"..."\` where it holds a letter, a case-folding
118
- ; match token as a quoted string, a range as \`%x<lo>-<hi>\` (a token set
119
- ; the compiler laid over a contested range read back from its name),
120
- ; and the engine's \`#TX\`, \`#NR\`, \`#ST\` and \`#VL\` by their names. A
121
- ; string a quoted string cannot hold is written as a numeric value,
122
- ; \`%x<hex>.<hex>\`. A token named for a rule the compiler lifted to it
123
- ; (\`CR = %x0D\` becomes the token \`#CR\`) is written as that rule, after
124
- ; the others, and referenced by name.
122
+ ; match token as a quoted string, a range as \`%x<lo>-<hi>\` (a token
123
+ ; set the compiler laid over a contested range read back from its
124
+ ; name, once its tokens are found to be that range), \`[\\s\\S]\` (GBNF's
125
+ ; \`.\`) as \`%x0-10FFFF\`, and the engine's \`#TX\`, \`#NR\`, \`#ST\` and \`#VL\`
126
+ ; by their names. A string a quoted string cannot hold is written as a
127
+ ; numeric value, \`%x<hex>.<hex>\`, which the compiler reads as
128
+ ; case-insensitive where it holds a letter of ASCII and as exact where
129
+ ; it holds none: a case-folding string past ASCII with no such letter,
130
+ ; whose numeric value would match one case alone, is refused. A token
131
+ ; named for a rule the
132
+ ; compiler lifted to it (\`CR = %x0D\` becomes the token \`#CR\`) is
133
+ ; written as that rule, after the others, and referenced by name.
125
134
  ; - An RFC 5234 core rule (ALPHA, DIGIT, ...) in the spec is not written,
126
135
  ; since the compiler adds it again wherever it is referenced, and a
127
136
  ; leading reference to one is written back as any other is.
128
137
  ;
129
138
  ; What a rule cannot be read back to is refused with
130
- ; TARGET_VALUE_UNREPRESENTABLE, naming the rule: an action other than the
131
- ; tree builders every compiled grammar carries (a value annotation's
139
+ ; TARGET_VALUE_UNREPRESENTABLE, naming the rule: an action other than
140
+ ; the tree builders every compiled grammar carries (a value annotation's
132
141
  ; builders, a user action, the probe dispatcher's), a condition or a
133
142
  ; counter other than a repeat loop's, an error generator or an alternate
134
143
  ; modifier (\`e\`, \`h\`), a function reference where a rule or a count is
135
144
  ; due, a set of tokens at one place, the form that edits a rule already
136
145
  ; installed (\`{alts, inject}\`), a removal, and a token no ABNF terminal
137
- ; matches (a negated class, a class holding a character past ASCII alone,
138
- ; a class whose flags change what it matches, a pattern that is neither
139
- ; one class nor an escaped literal). A spec serialized by Go, which writes
140
- ; its rules in name order and its match tokens' order as a list of its
141
- ; own (\`options.match.tokenOrder\`), is refused when that list holds two
142
- ; tokens or more: the rules' order, which ranks the tokens (the order the
143
- ; lexer tries two tokens a place expects), is lost.
146
+ ; matches (a negated class, a class holding a character past ASCII
147
+ ; alone, a class whose flags change what it matches, a pattern that is
148
+ ; neither one class nor an escaped literal). A token set whose tokens
149
+ ; are not the class its name gives, a rule with no alternate to open
150
+ ; with, a sequence's step with more alternates than a step, and a rule
151
+ ; the compiler lifted to a token whose name another rule holds are
152
+ ; refused too. A spec serialized by Go, which writes its rules in name
153
+ ; order and its match tokens' order as a list of its own
154
+ ; (\`options.match.tokenOrder\`), is refused when that list holds two
155
+ ; tokens or more: the rules' order, which ranks the tokens (the order
156
+ ; the lexer tries two tokens a place expects), is lost. The tree
157
+ ; builders and the marks are not written: the compiler makes them again
158
+ ; from the rules.
159
+ ; A spec whose start is not the start wrapper every grammar text
160
+ ; compiles to (one open alternate that matches nothing and pushes a
161
+ ; rule, closed by the end of the source alone) is refused as well: its
162
+ ; text would compile back inside such a wrapper, whose close lets the
163
+ ; end of the source come after what the lexer skips.
144
164
  ;
145
165
  ; A spec compiled from another notation is written as far as ABNF can say
146
166
  ; it: a class of several members (GBNF's and EBNF's \`[a-zA-Z_]\`) as the
@@ -357,9 +377,154 @@ def abnf-made [cx name]
357
377
  case :string true
358
378
  case _ false
359
379
  case _
360
- match (abnf-starts "_gen" name)
361
- case true true
362
- case false (abnf-some (abnf-rest (split "$" name)))
380
+ match (abnf-some (abnf-rest (split "$" name)))
381
+ case true (abnf-split-name name)
382
+ case false
383
+ match (abnf-gen-kind name)
384
+ case ""
385
+ match (abnf-same name (get :start cx))
386
+ case true (abnf-wrapper (abnf-rule cx name))
387
+ case false false
388
+ case k (abnf-helper-shape cx name k)
389
+
390
+ ; The kind of helper a name gives, as the compiler names the helpers it
391
+ ; makes, \`_gen<n>_<kind>...\` with the kind a repetition's, an option's or
392
+ ; a group's (\`_gen3_star_x\`, \`_gen4_group\`), or "" for any other name: a
393
+ ; rule the author wrote may begin with \`_gen\` too (\`_general\`).
394
+ def abnf-gen-kind [name]
395
+ match (abnf-starts "_gen" name)
396
+ case false ""
397
+ case true
398
+ let [parts (split "_" (abnf-after "_gen" name))]
399
+ match (count parts)
400
+ case 1 ""
401
+ case _
402
+ match (match (abnf-digits (abnf-at parts 0)) (case true (abnf-among (abnf-at parts 1) ["star" "plus" "opt" "rep" "group"])) (case false false))
403
+ case true (abnf-at parts 1)
404
+ case false ""
405
+
406
+ ; Whether a name is one the compiler gives a rule it splits off another,
407
+ ; every part after a \`$\` one of its own: a dispatcher's alternative
408
+ ; (\`$alt<i>\`), a chain's step (\`$step<j>\`), a left-factored tail
409
+ ; (\`$fact<k>\`), a probe dispatcher (\`$pd<i>\`) and its parts (\`$probe\`,
410
+ ; \`$with\`, \`$no\`, numbered where the name was taken).
411
+ def abnf-split-name [name]
412
+ abnf-none (filter (fn [p] (abnf-not (abnf-split-part p))) (abnf-rest (split "$" name)))
413
+
414
+ def abnf-split-part [p]
415
+ abnf-some (filter (fn [k] (abnf-split-fits k p)) ["alt" "step" "fact" "pd" "probe" "with" "no"])
416
+
417
+ def abnf-split-fits [k p]
418
+ match (abnf-starts k p)
419
+ case false false
420
+ case true
421
+ match (abnf-after k p)
422
+ case "" (abnf-among k ["probe" "with" "no"])
423
+ case rest (abnf-digits rest)
424
+
425
+ ; A rule named as the compiler names a helper, in a spec that names no
426
+ ; provenance, is the compiler's where it has the shape the compiler gives
427
+ ; that kind, and the author's otherwise: a repetition's loop has its
428
+ ; guarded entry (\`{c: {n.rep: 0}, r: <self>}\`); a plus is its item and the
429
+ ; loop after it; an option one element and the empty alternative; a
430
+ ; counted repetition copies of one element, and the option or the loop
431
+ ; after them; a group any alternatives.
432
+ def abnf-helper-shape [cx name k]
433
+ match k
434
+ case "group" true
435
+ case "star" (abnf-some (filter (fn [alt] (abnf-loop-entry name alt)) (abnf-open (abnf-rule cx name))))
436
+ case _
437
+ let [alts (abnf-raw-alts cx name)]
438
+ match k
439
+ case "plus" (abnf-plus-shape alts)
440
+ case "opt" (abnf-opt-shape alts)
441
+ case _ (abnf-rep-shape alts)
442
+
443
+ def abnf-loop-entry [name alt]
444
+ match (abnf-same (abnf-r alt) name)
445
+ case false false
446
+ case true
447
+ let [c (get "c" alt)]
448
+ match (kind c)
449
+ case :record (abnf-loop-value c "n.rep" [0])
450
+ case _ false
451
+
452
+ def abnf-plus-shape [alts]
453
+ match (count alts)
454
+ case 1
455
+ let [seq (abnf-vec (abnf-at alts 0))]
456
+ match (count seq)
457
+ case 2 (abnf-same (abnf-helper-ref (abnf-at seq 1)) "star")
458
+ case _ false
459
+ case _ false
460
+
461
+ def abnf-opt-shape [alts]
462
+ match (count alts)
463
+ case 2
464
+ let [counts (map count alts)]
465
+ match (abnf-among "0" (map abnf-num counts))
466
+ case true (abnf-among "1" (map abnf-num counts))
467
+ case false false
468
+ case _ false
469
+
470
+ def abnf-rep-shape [alts]
471
+ match (count alts)
472
+ case 1
473
+ let [seq (abnf-vec (abnf-at alts 0))]
474
+ match (count seq)
475
+ case 0 true
476
+ case _
477
+ let [lead (match (abnf-among (abnf-helper-ref (top seq)) ["star" "opt"]) (case true (pop seq)) (case false seq))]
478
+ let [keys (map abnf-el-name lead)]
479
+ abnf-none (filter (fn [key] (match key (case "" true) (case _ (abnf-not (abnf-same key (abnf-at keys 0)))))) keys)
480
+ case _ false
481
+
482
+ ; The kind of helper an element references by name, or "".
483
+ def abnf-helper-ref [el]
484
+ match (get :k el)
485
+ case :ref (abnf-gen-kind (get :name el))
486
+ case _ ""
487
+
488
+ ; A token or a reference by its name, any other element "".
489
+ def abnf-el-name [el]
490
+ match (get :k el)
491
+ case :tok (string-join "" ["#" (get :name el)])
492
+ case :ref (string-join "" ["@" (get :name el)])
493
+ case _ ""
494
+
495
+ ; The start wrapper the compiler adds, which \`options.rule.start\` names:
496
+ ; one open alternate that consumes nothing and pushes the grammar's start
497
+ ; rule, and the end of the source, and nothing else, to close it.
498
+ def abnf-wrapper [r]
499
+ let [o (abnf-open r)]
500
+ match (count o)
501
+ case 1
502
+ let [a (abnf-at o 0)]
503
+ match (match (abnf-some (abnf-s a)) (case true false) (case false (abnf-full (abnf-p a))))
504
+ case false false
505
+ case true
506
+ let [c (abnf-close r)]
507
+ match (count c)
508
+ case 0 false
509
+ case _ (abnf-none (filter (fn [x] (abnf-not (abnf-wrapper-end x))) c))
510
+ case _ false
511
+
512
+ ; A close alternate that matches the end of the source and goes nowhere.
513
+ def abnf-wrapper-end [alt]
514
+ match (abnf-same (string-join " " (abnf-s alt)) "#ZZ")
515
+ case false false
516
+ case true (abnf-empty (abnf-target alt))
517
+
518
+ ; Every grammar text compiles to a start wrapper around its start rule,
519
+ ; and the wrapper's close lets the end of the source come after what the
520
+ ; lexer skips (\`ab \` where the grammar is \`ab\`). So the rule
521
+ ; \`options.rule.start\` names must be that wrapper: a spec that starts at
522
+ ; another rule, or at a wrapper without its close, would come back
523
+ ; accepting what it refuses.
524
+ def abnf-check-start [cx]
525
+ match (abnf-wrapper (abnf-rule cx (get :start cx)))
526
+ case true true
527
+ case false (abnf-fail (string-join "" ["the start rule " (get :start cx) " is not the start wrapper every grammar text compiles to (one open alternate that matches nothing and pushes a rule, closed by the end of the source alone)"]))
363
528
 
364
529
  ; The kind the tree builders give a rule the author wrote: "user", or
365
530
  ; "core" for an RFC 5234 core rule the compiler added.
@@ -506,7 +671,9 @@ def abnf-check-rule [cx name]
506
671
  match (kind r)
507
672
  case :record
508
673
  let [phases (count (map (fn [phase] (abnf-check-phase name phase (get phase r))) ["open" "close"]))]
509
- count (map (fn [alt] (abnf-check-alt cx name alt)) (abnf-cat (abnf-open r) (abnf-close r)))
674
+ match (count (abnf-open r))
675
+ case 0 (abnf-fail (string-join "" ["rule " name " has no alternate to open with, which no grammar text writes"]))
676
+ case _ (count (map (fn [alt] (abnf-check-alt cx name alt)) (abnf-cat (abnf-open r) (abnf-close r))))
510
677
  case :null (abnf-fail (string-join "" ["rule " name " is removed, which is not written"]))
511
678
  case _ (abnf-fail (string-join "" ["rule " name " is not a rule's alternates, which is not written"]))
512
679
 
@@ -528,7 +695,9 @@ def abnf-check [spec cx]
528
695
  case true (abnf-fail "it clears the grammar it is installed on, which is not written")
529
696
  case _
530
697
  let [order (abnf-check-order spec)]
531
- count (map (fn [name] (abnf-check-rule cx name)) (keys (get :rules cx)))
698
+ let [start (abnf-check-start cx)]
699
+ let [rules (count (map (fn [name] (abnf-check-rule cx name)) (keys (get :rules cx))))]
700
+ abnf-check-sets cx (abnf-used cx)
532
701
 
533
702
  ; ---- tokens
534
703
 
@@ -567,9 +736,9 @@ def abnf-match-token [t m]
567
736
  match (abnf-class-pattern body)
568
737
  case false (abnf-fail (string-join "" ["the token " t " is the pattern " m ", which is not one class the render reads, and no ABNF terminal matches it"]))
569
738
  case true
570
- match (abnf-among flags ["" "u"])
739
+ match (abnf-flags-fit body flags)
571
740
  case true (abnf-class body flags)
572
- case false (abnf-fail (string-join "" ["the token " t " is the class " m ", whose flags (" flags ") change what it matches, and no ABNF terminal matches it"]))
741
+ case false (abnf-fail (string-join "" ["the token " t " is the class " m ", whose flags (" (match flags (case "" "none") (case _ flags)) ") change what it matches, and no ABNF terminal matches it"]))
573
742
  case false
574
743
  match flags
575
744
  case "i"
@@ -702,6 +871,193 @@ def abnf-trim-end-once [s]
702
871
  case true (abnf-before "_" s)
703
872
  case false s
704
873
 
874
+ ; ---- code points and the ranges a class holds
875
+
876
+ ; A code point, from its one to six hexadecimal digits in either case, as
877
+ ; three numbers, its bytes from the highest, each the place of its two
878
+ ; digits among \`abnf-hex-pairs\`: code points are ordered as these are.
879
+ def abnf-cp [h]
880
+ let [x (abnf-six (abnf-hex-upper h))]
881
+ let [high (abnf-pair-start x)]
882
+ let [rest (abnf-after high x)]
883
+ vector (abnf-pair-index high) (abnf-pair-index (abnf-pair-start rest)) (abnf-pair-index (abnf-pair-end rest))
884
+
885
+ def abnf-six [h]
886
+ string-join "" [(match (length h) (case 1 "00000") (case 2 "0000") (case 3 "000") (case 4 "00") (case 5 "0") (case _ "")) h]
887
+
888
+ def abnf-pair-start [x]
889
+ abnf-at (filter (fn [p] (abnf-starts p x)) abnf-hex-pairs) 0
890
+
891
+ def abnf-pair-end [x]
892
+ abnf-at (filter (fn [p] (abnf-ends p x)) abnf-hex-pairs) 0
893
+
894
+ def abnf-pair-index [p]
895
+ abnf-at (filter (fn [i] (abnf-same (abnf-at abnf-hex-pairs i) p)) (indices abnf-hex-pairs)) 0
896
+
897
+ def abnf-cp-order [a b]
898
+ match (compare (abnf-at a 0) (abnf-at b 0))
899
+ case :equal
900
+ match (compare (abnf-at a 1) (abnf-at b 1))
901
+ case :equal (compare (abnf-at a 2) (abnf-at b 2))
902
+ case other other
903
+ case other other
904
+
905
+ def abnf-cp-before [a b]
906
+ match (abnf-cp-order a b)
907
+ case :less true
908
+ case _ false
909
+
910
+ def abnf-cp-is [a b]
911
+ match (abnf-cp-order a b)
912
+ case :equal true
913
+ case _ false
914
+
915
+ ; The code point after one: its low byte one more, carried into the bytes
916
+ ; above it.
917
+ def abnf-cp-next [c]
918
+ match (abnf-at c 2)
919
+ case 255
920
+ match (abnf-at c 1)
921
+ case 255 (vector (abnf-plus (abnf-at c 0) 1) 0 0)
922
+ case _ (vector (abnf-at c 0) (abnf-plus (abnf-at c 1) 1) 0)
923
+ case _ (vector (abnf-at c 0) (abnf-at c 1) (abnf-plus (abnf-at c 2) 1))
924
+
925
+ def abnf-cp-max [cps]
926
+ abnf-at (filter (fn [c] (abnf-none (filter (fn [d] (abnf-cp-before c d)) cps))) cps) 0
927
+
928
+ def abnf-span [lo hi]
929
+ record
930
+ entry :lo lo
931
+ entry :hi hi
932
+
933
+ ; The members of a class's pattern, read as \`abnf-class-pattern\` admits
934
+ ; them: each a range, from its low end's digits to its high end's (a
935
+ ; character alone is a range of one); \`[\\s\\S]\` every code point. A
936
+ ; negated class's are the ones it excludes.
937
+ def abnf-class-hex-spans [body]
938
+ match body
939
+ case "[\\\\s\\\\S]" [(abnf-span "0" "10FFFF")]
940
+ case _
941
+ let [inner (abnf-before "]" (abnf-after "[" body))]
942
+ let [pieces (abnf-rest (split "\\\\u" (match (abnf-starts "^" inner) (case true (abnf-after "^" inner)) (case false inner))))]
943
+ let [lows (map (fn [p] (abnf-ends "-" p)) pieces)]
944
+ let [ends (abnf-cat [false] lows)]
945
+ map
946
+ fn [i]
947
+ let [lo (abnf-piece-hex (abnf-at pieces i))]
948
+ match (abnf-at lows i)
949
+ case true (abnf-span lo (abnf-piece-hex (abnf-at pieces (abnf-plus i 1))))
950
+ case false (abnf-span lo lo)
951
+ filter (fn [i] (abnf-not (abnf-at ends i))) (indices pieces)
952
+
953
+ def abnf-piece-hex [p]
954
+ let [core (match (abnf-ends "-" p) (case true (abnf-before "-" p)) (case false p))]
955
+ match (abnf-starts "{" core)
956
+ case true (abnf-before "}" (abnf-after "{" core))
957
+ case false core
958
+
959
+ def abnf-class-spans [body]
960
+ map (fn [s] (abnf-span (abnf-cp (get :lo s)) (abnf-cp (get :hi s)))) (abnf-class-hex-spans body)
961
+
962
+ ; Ranges sorted by their low ends and merged where they overlap or meet:
963
+ ; two lists of ranges hold the same code points when these are the same.
964
+ def abnf-spans-merged [spans]
965
+ let [sorted (abnf-spans-sorted spans)]
966
+ let [starts (filter (fn [i] (abnf-none (filter (fn [j] (match (abnf-less j i) (case true (abnf-not (abnf-cp-before (abnf-cp-next (get :hi (abnf-at sorted j))) (get :lo (abnf-at sorted i))))) (case false false))) (indices sorted)))) (indices sorted))]
967
+ map (fn [k] (abnf-block-span sorted starts k)) (indices starts)
968
+
969
+ def abnf-block-span [sorted starts k]
970
+ let [from (abnf-at starts k)]
971
+ let [members (filter (fn [i] (match (abnf-less i from) (case true false) (case false (abnf-before-start starts k i)))) (indices sorted))]
972
+ abnf-span (get :lo (abnf-at sorted from)) (abnf-cp-max (map (fn [i] (get :hi (abnf-at sorted i))) members))
973
+
974
+ def abnf-before-start [starts k i]
975
+ match (abnf-less (abnf-plus k 1) (count starts))
976
+ case true (abnf-less i (abnf-at starts (abnf-plus k 1)))
977
+ case false true
978
+
979
+ def abnf-spans-sorted [spans]
980
+ let [ranks (map (fn [i] (count (filter (fn [j] (match (abnf-cp-order (get :lo (abnf-at spans j)) (get :lo (abnf-at spans i))) (case :less true) (case :equal (abnf-less j i)) (case _ false))) (indices spans)))) (indices spans))]
981
+ map (fn [k] (abnf-at spans (abnf-at (filter (fn [i] (match (compare (abnf-at ranks i) k) (case :equal true) (case _ false))) (indices spans)) 0))) (indices spans)
982
+
983
+ def abnf-spans-same [a b]
984
+ match (compare (count a) (count b))
985
+ case :equal (abnf-none (filter (fn [i] (match (abnf-cp-is (get :lo (abnf-at a i)) (get :lo (abnf-at b i))) (case true (abnf-not (abnf-cp-is (get :hi (abnf-at a i)) (get :hi (abnf-at b i))))) (case false true))) (indices a)))
986
+ case _ false
987
+
988
+ ; Whether two lists of ranges hold every code point between them, each
989
+ ; once: a negated class's ranges and the ranges it matches.
990
+ def abnf-spans-tile [a b]
991
+ let [all (abnf-spans-sorted (abnf-cat (abnf-spans-merged a) (abnf-spans-merged b)))]
992
+ match (count all)
993
+ case 0 false
994
+ case _
995
+ match (match (abnf-cp-is (get :lo (abnf-at all 0)) (abnf-cp "0")) (case true (abnf-cp-is (get :hi (top all)) (abnf-cp "10FFFF"))) (case false false))
996
+ case false false
997
+ case true
998
+ let [nexts (abnf-rest all)]
999
+ abnf-none (filter (fn [i] (abnf-not (abnf-cp-is (get :lo (abnf-at nexts i)) (abnf-cp-next (get :hi (abnf-at all i)))))) (indices nexts))
1000
+
1001
+ ; Whether a class's flags are the ones it is matched with once its text is
1002
+ ; written and compiled back. Every compiler here gives \`u\` to a class that
1003
+ ; is negated, holds a character past U+FFFF or is \`[\\s\\S]\`, and to no
1004
+ ; other. Without \`u\` such a class matches one UTF-16 code unit, half of a
1005
+ ; character past U+FFFF, and with it one code point. A class of other
1006
+ ; characters matches the same either way, unless it holds a surrogate,
1007
+ ; which \`u\` matches only where it stands alone.
1008
+ def abnf-flags-fit [body flags]
1009
+ match (abnf-wants-u body)
1010
+ case true (abnf-same flags "u")
1011
+ case false
1012
+ match (abnf-holds-surrogate body)
1013
+ case true (abnf-empty flags)
1014
+ case false (abnf-among flags ["" "u"])
1015
+
1016
+ def abnf-wants-u [body]
1017
+ match body
1018
+ case "[\\\\s\\\\S]" true
1019
+ case _
1020
+ match (abnf-starts "[^" body)
1021
+ case true true
1022
+ case false (abnf-some (abnf-rest (split "\\\\u{" body)))
1023
+
1024
+ ; A class none of whose members is past U+FFFF holds a surrogate where a
1025
+ ; range's low end is not above U+DFFF and its high end not below U+D800.
1026
+ def abnf-holds-surrogate [body]
1027
+ abnf-some (filter (fn [s] (match (abnf-hex-from (get :lo s) ["E" "F"]) (case true false) (case false (abnf-hex-from (get :hi s) ["D8" "D9" "DA" "DB" "DC" "DD" "DE" "DF" "E" "F"])))) (abnf-class-hex-spans body))
1028
+
1029
+ def abnf-hex-from [h prefixes]
1030
+ let [x (abnf-hex-upper h)]
1031
+ match (length x)
1032
+ case 4 (abnf-some (filter (fn [p] (abnf-starts p x)) prefixes))
1033
+ case _ false
1034
+
1035
+ ; A token set is the class its name gives when the tokens it lays over
1036
+ ; that class are the class: their ranges, merged, are the class's ranges,
1037
+ ; or, for a negated class, every code point the class does not exclude.
1038
+ ; The compiler lays a contested class over the atoms of a partition, each
1039
+ ; a token of its own, and names the set for the class; the render writes
1040
+ ; the class the name gives, so a set whose tokens match anything else is
1041
+ ; refused.
1042
+ def abnf-check-sets [cx used]
1043
+ count (map (fn [k] (abnf-check-set cx (string-join "" ["#" k]))) (filter (fn [k] (match (abnf-starts "RX_" k) (case true (abnf-some (abnf-rest (split (string-join "" [" #" k " "]) used)))) (case false false))) (keys (get :sets cx))))
1044
+
1045
+ def abnf-check-set [cx t]
1046
+ let [cls (abnf-set-token t)]
1047
+ let [pat (get :pat cls)]
1048
+ let [spans (abnf-flat (map (fn [m] (abnf-member-spans cx t m)) (abnf-vec (get-path (as-path [(abnf-bare t)]) (get :sets cx)))))]
1049
+ let [own (abnf-class-spans pat)]
1050
+ match (match (abnf-starts "[^" pat) (case true (abnf-spans-tile own spans)) (case false (abnf-spans-same (abnf-spans-merged own) (abnf-spans-merged spans))))
1051
+ case true true
1052
+ case false (abnf-fail (string-join "" ["the token set " t " lays tokens over its class " pat " that match other characters than the class does, and the render writes the class the set's name gives"]))
1053
+
1054
+ def abnf-member-spans [cx t m]
1055
+ let [src (match (kind m) (case :string (abnf-str (get-path (as-path [m]) (get :match cx)))) (case _ ""))]
1056
+ let [body (abnf-after "^" (string-join "/" (pop (abnf-rest (split "/" src)))))]
1057
+ match (match (abnf-starts "@" src) (case true (match (abnf-class-pattern body) (case true (match (abnf-starts "[^" body) (case true false) (case false (abnf-flags-fit body (top (split "/" src)))))) (case false false))) (case false false))
1058
+ case true (abnf-class-spans body)
1059
+ case false (abnf-fail (string-join "" ["the token set " t " holds " (abnf-str m) ", which is not one class the render reads"]))
1060
+
705
1061
  def abnf-engine-token [t]
706
1062
  match t
707
1063
  case "#TX" (record (entry :k :engine) (entry :name "TX"))
@@ -765,11 +1121,23 @@ def abnf-entry-key [alt]
765
1121
  ; the empty alternative, or a guard that ends it on what may follow; two
766
1122
  ; or more bare ones (\`a ::= | \`) are as many empty alternatives.
767
1123
  def abnf-simple-alts [r]
768
- let [alts (map abnf-entry-els (abnf-unique abnf-entry-key (abnf-open r)))]
1124
+ let [alts (abnf-flat (map (fn [alt] (map (fn [i] (abnf-entry-els alt)) (indices (abnf-ones (abnf-copies (abnf-open r) alt))))) (abnf-unique abnf-entry-key (abnf-open r))))]
769
1125
  let [bare (count (filter abnf-bare-empty (abnf-open r)))]
770
1126
  let [empties (match (abnf-some (filter abnf-none alts)) (case false []) (case true (map (fn [i] []) (indices (abnf-ones (match (abnf-less bare 2) (case true 1) (case false bare)))))))]
771
1127
  abnf-cat empties (abnf-number-order (filter abnf-some alts))
772
1128
 
1129
+ ; How many times an alternative stands among the open alternates: the
1130
+ ; lookahead copies a dispatch makes of one alternative consume and push the
1131
+ ; same and look ahead at different tokens, and are one; alternates alike
1132
+ ; in everything are as many alternatives as there are of them.
1133
+ def abnf-copies [open alt]
1134
+ let [group (filter (fn [a] (abnf-same (abnf-entry-key a) (abnf-entry-key alt))) open)]
1135
+ let [counts (map (fn [a] (count (filter (fn [b] (abnf-same (abnf-whole-key b) (abnf-whole-key a))) group))) group)]
1136
+ abnf-at (filter (fn [c] (abnf-none (filter (fn [d] (abnf-less c d)) counts))) counts) 0
1137
+
1138
+ def abnf-whole-key [alt]
1139
+ string-join "" [(string-join " " (abnf-s alt)) "|" (abnf-num (abnf-b alt)) "|" (abnf-target alt)]
1140
+
773
1141
  def abnf-bare-empty [alt]
774
1142
  match (abnf-some (abnf-s alt))
775
1143
  case true false
@@ -818,7 +1186,7 @@ def abnf-filled [alts]
818
1186
  ; The steps of a chain, each a segment: the tokens its open alternate
819
1187
  ; consumes and the rule it pushes, the next step named in its close.
820
1188
  def abnf-chain-walk [self cx name depth acc]
821
- let [r (abnf-rule cx name)]
1189
+ let [r (abnf-chain-step name (abnf-rule cx name))]
822
1190
  let [els (abnf-cat acc (abnf-entry-els (abnf-at (abnf-open r) 0)))]
823
1191
  match (count (abnf-close r))
824
1192
  case 0 els
@@ -915,6 +1283,15 @@ def abnf-raw-alts [cx name]
915
1283
  case "" (abnf-simple-alts r)
916
1284
  case _ (vector (abnf-chain cx name))
917
1285
 
1286
+ ; A step of a sequence's chain, as the compiler makes one: one open
1287
+ ; alternate, a segment of the sequence, and at most one close alternate,
1288
+ ; which names the next step. A rule that replaces itself in its close and
1289
+ ; has more alternates than that is no chain the render reads back.
1290
+ def abnf-chain-step [name r]
1291
+ match (match (count (abnf-open r)) (case 1 (abnf-less (count (abnf-close r)) 2)) (case _ false))
1292
+ case true r
1293
+ case false (abnf-fail (string-join "" ["rule " name " replaces itself in its close as a sequence's step does, and has more alternates than a step, which is not read back"]))
1294
+
918
1295
  ; ---- the compiler's helpers, read back
919
1296
 
920
1297
  ; The kind of a helper the compiler named: \`_gen<n>_<kind>...\`.
@@ -1351,8 +1728,12 @@ def abnf-unsub-at [cx alts best accepted covers i]
1351
1728
 
1352
1729
  ; A literal: a quoted string where it can hold the text (\`%s\` before it
1353
1730
  ; when the match is exact and the text holds a letter), and otherwise a
1354
- ; numeric value, \`%x\` and each character's code point, which ABNF reads
1355
- ; as a case-insensitive string.
1731
+ ; numeric value, \`%x\` and each character's code point, which tabnas-abnf
1732
+ ; reads as a case-insensitive string where it holds a letter of ASCII,
1733
+ ; with the folding a case-insensitive pattern has, and as an exact one
1734
+ ; where it holds none. So a case-insensitive literal holding a character
1735
+ ; past ASCII, which may have cases, and no letter of ASCII is refused: its
1736
+ ; numeric value would match the one case it spells.
1356
1737
  def abnf-lit-text [text cs]
1357
1738
  match text
1358
1739
  case "" "\\"\\""
@@ -1371,7 +1752,10 @@ def abnf-lit-text [text cs]
1371
1752
  match (abnf-has-letter text)
1372
1753
  case true (abnf-fail (string-join "" ["the case-sensitive literal " (quoted text) " holds a character a quoted string cannot hold, and a numeric value matches case-insensitively"]))
1373
1754
  case false (string-join "" ["%x" (string-join "." (abnf-codes text))])
1374
- case false (string-join "" ["%x" (string-join "." (abnf-codes text))])
1755
+ case false
1756
+ match (match (abnf-has-letter text) (case true true) (case false (chars-within [[0 127] [65536 1114111]] text)))
1757
+ case true (string-join "" ["%x" (string-join "." (abnf-codes text))])
1758
+ case false (abnf-fail (string-join "" ["the case-insensitive literal " (quoted text) " holds a character past ASCII that may have cases and no ASCII letter, and a numeric value without a letter matches exactly"]))
1375
1759
 
1376
1760
  ; A code point of a class's pattern, \`\\uXXXX\` or \`\\u{X...}\`, as ABNF's
1377
1761
  ; hexadecimal digits.
@@ -1391,8 +1775,14 @@ def abnf-escape-hex [e]
1391
1775
  ; class of several members, which another notation writes, as the
1392
1776
  ; alternation of its members, each range a range and each character a
1393
1777
  ; string that matches it exactly, or a range of one where a string cannot
1394
- ; hold it. A negated class has no ABNF form.
1778
+ ; hold it; \`[\\s\\S]\` (GBNF's \`.\`) as the range of every code point. A
1779
+ ; negated class has no ABNF form.
1395
1780
  def abnf-class-text [pat flags]
1781
+ match pat
1782
+ case "[\\\\s\\\\S]" "%x0-10FFFF"
1783
+ case _ (abnf-range-text pat)
1784
+
1785
+ def abnf-range-text [pat]
1396
1786
  let [body (abnf-before "]" (abnf-after "[" pat))]
1397
1787
  match (abnf-starts "^" body)
1398
1788
  case true (abnf-fail (string-join "" ["the class " pat " is negated, which ABNF has no form for"]))
@@ -1595,8 +1985,24 @@ def abnf-lifted-production [cx t]
1595
1985
  let [info (abnf-token cx t)]
1596
1986
  string-join "" [(abnf-ref-text (abnf-bare t)) " = " (abnf-lit-text (get :text info) (get :cs info))]
1597
1987
 
1988
+ ; A rule the compiler lifted to a token is written under the token's name,
1989
+ ; which no rule of the spec may hold already: two productions of one name
1990
+ ; are one rule.
1991
+ def abnf-check-lifted [cx lifted]
1992
+ map
1993
+ fn [t]
1994
+ match (abnf-has cx (abnf-bare t))
1995
+ case true (abnf-fail (string-join "" ["the token " t " is written as a rule named " (abnf-bare t) ", the name of another rule the spec holds"]))
1996
+ case false t
1997
+ lifted
1998
+
1999
+ ; The grammar's start rule: the one the start wrapper pushes, or the rule
2000
+ ; \`options.rule.start\` names where it is no wrapper.
1598
2001
  def abnf-start-rule [cx]
1599
- abnf-p (abnf-at (abnf-open (abnf-rule cx (get :start cx))) 0)
2002
+ let [r (abnf-rule cx (get :start cx))]
2003
+ match (abnf-wrapper r)
2004
+ case true (abnf-p (abnf-at (abnf-open r) 0))
2005
+ case false (get :start cx)
1600
2006
 
1601
2007
  def abnf-file [spec]
1602
2008
  let [cx (abnf-context spec)]
@@ -1606,7 +2012,7 @@ def abnf-file [spec]
1606
2012
  let [ordered (abnf-cat [start] (filter (fn [n] (abnf-not (abnf-same n start))) names))]
1607
2013
  let [used (abnf-used cx)]
1608
2014
  let [tokens (abnf-cat (keys (get :fixed cx)) (keys (get :match cx)))]
1609
- let [lifted (filter (fn [t] (abnf-lifted cx used t)) tokens)]
2015
+ let [lifted (abnf-check-lifted cx (filter (fn [t] (abnf-lifted cx used t)) tokens))]
1610
2016
  let [cands (abnf-candidates cx (abnf-cat ordered (abnf-direct-core cx)))]
1611
2017
  string-join ""
1612
2018
  vector