@thomasfosterau/effect-parse 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +550 -2
  3. package/dist/Ascii.d.ts +392 -0
  4. package/dist/Ascii.d.ts.map +1 -0
  5. package/dist/Ascii.js +610 -0
  6. package/dist/Ascii.js.map +1 -0
  7. package/dist/Binary.d.ts +153 -0
  8. package/dist/Binary.d.ts.map +1 -0
  9. package/dist/Binary.js +171 -0
  10. package/dist/Binary.js.map +1 -0
  11. package/dist/Bits.d.ts +60 -0
  12. package/dist/Bits.d.ts.map +1 -0
  13. package/dist/Bits.js +144 -0
  14. package/dist/Bits.js.map +1 -0
  15. package/dist/Encoding.d.ts +116 -0
  16. package/dist/Encoding.d.ts.map +1 -0
  17. package/dist/Encoding.js +85 -0
  18. package/dist/Encoding.js.map +1 -0
  19. package/dist/Expression.d.ts +124 -0
  20. package/dist/Expression.d.ts.map +1 -0
  21. package/dist/Expression.js +139 -0
  22. package/dist/Expression.js.map +1 -0
  23. package/dist/Input.d.ts +301 -0
  24. package/dist/Input.d.ts.map +1 -0
  25. package/dist/Input.js +312 -0
  26. package/dist/Input.js.map +1 -0
  27. package/dist/Nom.d.ts +518 -0
  28. package/dist/Nom.d.ts.map +1 -0
  29. package/dist/Nom.js +519 -0
  30. package/dist/Nom.js.map +1 -0
  31. package/dist/ParseError.d.ts +120 -0
  32. package/dist/ParseError.d.ts.map +1 -0
  33. package/dist/ParseError.js +301 -0
  34. package/dist/ParseError.js.map +1 -0
  35. package/dist/Parser.d.ts +1312 -0
  36. package/dist/Parser.d.ts.map +1 -0
  37. package/dist/Parser.js +1936 -0
  38. package/dist/Parser.js.map +1 -0
  39. package/dist/ParserSchema.d.ts +31 -0
  40. package/dist/ParserSchema.d.ts.map +1 -0
  41. package/dist/ParserSchema.js +56 -0
  42. package/dist/ParserSchema.js.map +1 -0
  43. package/dist/ParserStream.d.ts +127 -0
  44. package/dist/ParserStream.d.ts.map +1 -0
  45. package/dist/ParserStream.js +270 -0
  46. package/dist/ParserStream.js.map +1 -0
  47. package/dist/Text.d.ts +330 -0
  48. package/dist/Text.d.ts.map +1 -0
  49. package/dist/Text.js +529 -0
  50. package/dist/Text.js.map +1 -0
  51. package/dist/Token.d.ts +225 -0
  52. package/dist/Token.d.ts.map +1 -0
  53. package/dist/Token.js +294 -0
  54. package/dist/Token.js.map +1 -0
  55. package/dist/index.d.ts +17 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +17 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/internal/compiler/closure.d.ts +25 -0
  60. package/dist/internal/compiler/closure.d.ts.map +1 -0
  61. package/dist/internal/compiler/closure.js +1668 -0
  62. package/dist/internal/compiler/closure.js.map +1 -0
  63. package/dist/internal/compiler/entries.d.ts +19 -0
  64. package/dist/internal/compiler/entries.d.ts.map +1 -0
  65. package/dist/internal/compiler/entries.js +32 -0
  66. package/dist/internal/compiler/entries.js.map +1 -0
  67. package/dist/internal/compiler/first.d.ts +33 -0
  68. package/dist/internal/compiler/first.d.ts.map +1 -0
  69. package/dist/internal/compiler/first.js +235 -0
  70. package/dist/internal/compiler/first.js.map +1 -0
  71. package/dist/internal/compiler/jit.d.ts +20 -0
  72. package/dist/internal/compiler/jit.d.ts.map +1 -0
  73. package/dist/internal/compiler/jit.js +1030 -0
  74. package/dist/internal/compiler/jit.js.map +1 -0
  75. package/dist/internal/compiler/registry.d.ts +34 -0
  76. package/dist/internal/compiler/registry.d.ts.map +1 -0
  77. package/dist/internal/compiler/registry.js +46 -0
  78. package/dist/internal/compiler/registry.js.map +1 -0
  79. package/dist/internal/compiler/sets.d.ts +29 -0
  80. package/dist/internal/compiler/sets.d.ts.map +1 -0
  81. package/dist/internal/compiler/sets.js +85 -0
  82. package/dist/internal/compiler/sets.js.map +1 -0
  83. package/dist/internal/entries.d.ts +27 -0
  84. package/dist/internal/entries.d.ts.map +1 -0
  85. package/dist/internal/entries.js +42 -0
  86. package/dist/internal/entries.js.map +1 -0
  87. package/dist/internal/node.d.ts +251 -0
  88. package/dist/internal/node.d.ts.map +1 -0
  89. package/dist/internal/node.js +73 -0
  90. package/dist/internal/node.js.map +1 -0
  91. package/dist/internal/numbers.d.ts +41 -0
  92. package/dist/internal/numbers.d.ts.map +1 -0
  93. package/dist/internal/numbers.js +302 -0
  94. package/dist/internal/numbers.js.map +1 -0
  95. package/dist/internal/resume.d.ts +39 -0
  96. package/dist/internal/resume.d.ts.map +1 -0
  97. package/dist/internal/resume.js +37 -0
  98. package/dist/internal/resume.js.map +1 -0
  99. package/dist/internal/text.d.ts +72 -0
  100. package/dist/internal/text.d.ts.map +1 -0
  101. package/dist/internal/text.js +391 -0
  102. package/dist/internal/text.js.map +1 -0
  103. package/dist/internal/tokens.d.ts +93 -0
  104. package/dist/internal/tokens.d.ts.map +1 -0
  105. package/dist/internal/tokens.js +400 -0
  106. package/dist/internal/tokens.js.map +1 -0
  107. package/package.json +115 -3
  108. package/src/Ascii.ts +741 -0
  109. package/src/Binary.ts +228 -0
  110. package/src/Bits.ts +192 -0
  111. package/src/Encoding.ts +128 -0
  112. package/src/Expression.ts +245 -0
  113. package/src/Input.ts +429 -0
  114. package/src/Nom.ts +642 -0
  115. package/src/ParseError.ts +378 -0
  116. package/src/Parser.ts +2763 -0
  117. package/src/ParserSchema.ts +87 -0
  118. package/src/ParserStream.ts +390 -0
  119. package/src/Text.ts +757 -0
  120. package/src/Token.ts +416 -0
  121. package/src/index.ts +16 -0
  122. package/src/internal/compiler/closure.ts +1583 -0
  123. package/src/internal/compiler/entries.ts +36 -0
  124. package/src/internal/compiler/first.ts +252 -0
  125. package/src/internal/compiler/jit.ts +1091 -0
  126. package/src/internal/compiler/registry.ts +79 -0
  127. package/src/internal/compiler/sets.ts +100 -0
  128. package/src/internal/entries.ts +51 -0
  129. package/src/internal/node.ts +207 -0
  130. package/src/internal/numbers.ts +291 -0
  131. package/src/internal/resume.ts +55 -0
  132. package/src/internal/text.ts +411 -0
  133. package/src/internal/tokens.ts +409 -0
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The shapes of `map(p, Object.fromEntries)` that the compilers build
3
+ * directly (see `internal/entries.ts`).
4
+ */
5
+ import type { Node, NodeOf, ParserLike } from "../node.js"
6
+ import { nodeOf } from "../node.js"
7
+
8
+ /** A repeated pair for `Object.fromEntries`: the sequence and the indices of its key and value. */
9
+ export const pairOf = (p: ParserLike): { readonly parsers: ReadonlyArray<ParserLike>; readonly key: number; readonly value: number } | undefined => {
10
+ let node = nodeOf(p)
11
+ while (isDiagnostic(node)) node = nodeOf(node.self)
12
+ if (node._tag !== "Seq") return undefined
13
+ const output = node.output
14
+ if (output === "tuple") return node.parsers.length === 2 ? { parsers: node.parsers, key: 0, value: 1 } : undefined
15
+ if (typeof output === "number" || output[0] === output[1]) return undefined
16
+ return { parsers: node.parsers, key: output[0], value: output[1] }
17
+ }
18
+
19
+ const isDiagnostic = (
20
+ node: Node
21
+ ): node is NodeOf<"Context" | "Expected" | "MapError" | "OrFail" | "Trace" | "RetryAfter"> =>
22
+ node._tag === "Context" || node._tag === "Expected" || node._tag === "MapError" || node._tag === "OrFail" ||
23
+ node._tag === "Trace" || node._tag === "RetryAfter"
24
+
25
+ /**
26
+ * Whether `map(p, Object.fromEntries)` can build its object directly: `p`
27
+ * is a `repeat` or `separated` of a pair, possibly inside `cut`, the kept
28
+ * element of a sequence (`preceded`, `terminated`, `delimited`) and
29
+ * diagnostics.
30
+ */
31
+ export const entriesShape = (p: ParserLike): boolean => {
32
+ const node = nodeOf(p)
33
+ if (isDiagnostic(node) || node._tag === "Mode") return entriesShape(node.self)
34
+ if (node._tag === "Seq") return typeof node.output === "number" && entriesShape(node.parsers[node.output]!)
35
+ return (node._tag === "Repeat" || node._tag === "Separated") && pairOf(node.self) !== undefined
36
+ }
@@ -0,0 +1,252 @@
1
+ /**
2
+ * First sets: which input units can start a match of a parser.
3
+ *
4
+ * A first set has a mode:
5
+ *
6
+ * - `Strict`: when the next unit is not in `units` (or the input has
7
+ * ended), the parser fails with `Backtrack`.
8
+ * - `Optional`: in that case, the parser either fails with `Backtrack` or
9
+ * succeeds without consuming input.
10
+ * - `Unknown`: no guarantee.
11
+ *
12
+ * Units are UTF-16 code units for strings and bytes for byte inputs; units
13
+ * from `TableSize` up are summarized by `high`. Array inputs have no units.
14
+ *
15
+ * Skipping a parser that is certain to fail also skips the callbacks it
16
+ * would have run before failing, which is why callbacks must be pure.
17
+ */
18
+ import type { Encoding } from "../../Encoding.js"
19
+ import type { Node, ParserLike } from "../node.js"
20
+ import { nodeOf } from "../node.js"
21
+ import { fastLimit } from "../text.js"
22
+ import type { CharSet } from "./sets.js"
23
+ import { kindOfEncoding, reachesAbove, TableSize, textSet, tokenSet } from "./sets.js"
24
+
25
+ export const Strict = 0
26
+ export const Optional = 1
27
+ export const Unknown = 2
28
+
29
+ export interface First {
30
+ readonly mode: 0 | 1 | 2
31
+ readonly units: Uint8Array
32
+ readonly high: boolean
33
+ }
34
+
35
+ const none = new Uint8Array(TableSize)
36
+ const all = new Uint8Array(TableSize).fill(1)
37
+
38
+ const unknown: First = { mode: Unknown, units: all, high: true }
39
+ const emptyOptional: First = { mode: Optional, units: none, high: false }
40
+ const failing: First = { mode: Strict, units: none, high: false }
41
+ const anyUnit: First = { mode: Strict, units: all, high: true }
42
+
43
+ const union = (a: First, b: First, mode: 0 | 1 | 2): First => {
44
+ const units = new Uint8Array(TableSize)
45
+ for (let i = 0; i < TableSize; i++) units[i] = a.units[i]! | b.units[i]!
46
+ return { mode, units, high: a.high || b.high }
47
+ }
48
+
49
+ const withMode = (f: First, mode: 0 | 1 | 2): First => f.mode === mode ? f : { ...f, mode }
50
+
51
+ const lowerAscii = (c: number): number => c >= 65 && c <= 90 ? c + 32 : c
52
+ const upperAscii = (c: number): number => c >= 97 && c <= 122 ? c - 32 : c
53
+
54
+ const ofUnits = (codes: ReadonlyArray<number>, mode: 0 | 1 | 2 = Strict): First => {
55
+ const units = new Uint8Array(TableSize)
56
+ let high = false
57
+ for (const c of codes) {
58
+ if (c < TableSize) units[c] = 1
59
+ else high = true
60
+ }
61
+ return { mode, units, high }
62
+ }
63
+
64
+ export class FirstSets {
65
+ private readonly memo = new Map<ParserLike, First | undefined>()
66
+ constructor(readonly kind: 0 | 1 | 2) {}
67
+
68
+ of(p: ParserLike): First {
69
+ if (this.kind === 2) return unknown
70
+ if (this.memo.has(p)) return this.memo.get(p) ?? unknown // in progress: a cycle
71
+ this.memo.set(p, undefined)
72
+ const f = this.compute(nodeOf(p))
73
+ this.memo.set(p, f)
74
+ return f
75
+ }
76
+
77
+ /** Units of the code points of a set, as an encoding (or `Token`) reads them. */
78
+ private setUnits(set: CharSet, enc: Encoding<any> | undefined, mode: 0 | 1): First | undefined {
79
+ if (enc === undefined || kindOfEncoding(enc) === 0) {
80
+ if (this.kind === 1) return { mode, units: set.table, high: false }
81
+ return { mode, units: set.table, high: reachesAbove(set, TableSize) }
82
+ }
83
+ if (enc.unit !== 1) return undefined
84
+ const fast = fastLimit(enc)
85
+ if (fast === 0) return undefined
86
+ const units = set.table.slice()
87
+ // Multi-unit sequences start with a unit at or above `fast`
88
+ if (fast < TableSize) units.fill(reachesAbove(set, fast) ? 1 : 0, fast)
89
+ return { mode, units, high: false }
90
+ }
91
+
92
+ private compute(node: Node): First {
93
+ const kind = this.kind as 0 | 1
94
+ switch (node._tag) {
95
+ case "Succeed":
96
+ case "Sync":
97
+ case "Location":
98
+ case "RestLen":
99
+ case "Eof":
100
+ return emptyOptional
101
+ case "Fail":
102
+ return node.mode === "Backtrack" ? failing : unknown
103
+ case "Any":
104
+ return node.encoding !== undefined && kindOfEncoding(node.encoding) !== kind ? unknown : anyUnit
105
+ case "Take":
106
+ return node.count === 0 ? emptyOptional : anyUnit
107
+ case "Literal": {
108
+ const lit = node.literal
109
+ if (node.encoding !== undefined) {
110
+ if (kindOfEncoding(node.encoding) !== kind) return unknown
111
+ if (node.caseless) return lit.length === 0 ? unknown : anyUnit
112
+ const encoded = node.encoding.encode(lit as string)
113
+ if (encoded.length === 0) return emptyOptional
114
+ return ofUnits([typeof encoded === "string" ? encoded.charCodeAt(0) : encoded[0]!])
115
+ }
116
+ if (lit.length === 0) return emptyOptional
117
+ let first: number
118
+ if (typeof lit === "string") {
119
+ first = kind === 0 ? lit.charCodeAt(0) : new TextEncoder().encode(lit)[0]!
120
+ } else if (lit instanceof Uint8Array && kind === 1) {
121
+ first = lit[0]!
122
+ } else {
123
+ return unknown
124
+ }
125
+ return ofUnits(node.caseless ? [first, lowerAscii(first), upperAscii(first)] : [first])
126
+ }
127
+ case "OneOf":
128
+ case "TakeWhile": {
129
+ if (node.encoding !== undefined && kindOfEncoding(node.encoding) !== kind) return unknown
130
+ const min = node._tag === "OneOf" ? 1 : node.min
131
+ if (node._tag === "TakeWhile" && node.max === 0) return emptyOptional
132
+ const mode = min > 0 ? Strict : Optional
133
+ if (node.negate) return mode === Strict ? anyUnit : withMode(anyUnit, Optional)
134
+ const set = node.encoding === undefined ? tokenSet(node.set, kind) : textSet(node.set)
135
+ return this.setUnits(set, node.encoding, mode) ?? (mode === Strict ? anyUnit : unknown)
136
+ }
137
+ case "Map":
138
+ case "As":
139
+ case "Tap":
140
+ case "TryMap":
141
+ case "FilterMap":
142
+ case "Filter":
143
+ case "Taken":
144
+ case "WithTaken":
145
+ case "Span":
146
+ case "WithSpan":
147
+ case "Context":
148
+ case "Expected":
149
+ case "MapError":
150
+ case "OrFail":
151
+ case "Peek":
152
+ case "Trace":
153
+ case "ResumeAfter":
154
+ case "RetryAfter":
155
+ return this.of(node.self)
156
+ case "FlatMap":
157
+ case "SeparatedFold":
158
+ case "Dispatch": {
159
+ const f = this.of(node._tag === "Dispatch" ? node.selector : node.self)
160
+ return f.mode === Strict ? f : unknown
161
+ }
162
+ case "Mode": {
163
+ const f = this.of(node.self)
164
+ return node.from === "Backtrack" ? (f.mode === Unknown ? f : unknown) : f
165
+ }
166
+ case "Opt":
167
+ case "OrElseSucceed": {
168
+ const f = this.of(node.self)
169
+ return f.mode === Unknown ? f : withMode(f, Optional)
170
+ }
171
+ case "Seq":
172
+ case "Struct":
173
+ return this.sequence(node.parsers)
174
+ case "ZipWith":
175
+ return this.sequence([node.self, node.that])
176
+ case "Alt": {
177
+ if (node.parsers.length === 0) return failing
178
+ let acc: First | undefined
179
+ let mode: 0 | 1 | 2 = Strict
180
+ for (const p of node.parsers) {
181
+ const f = this.of(p)
182
+ if (f.mode === Unknown) return unknown
183
+ if (f.mode === Optional) mode = Optional
184
+ acc = acc === undefined ? f : union(acc, f, mode)
185
+ }
186
+ return withMode(acc!, mode)
187
+ }
188
+ case "Repeat":
189
+ case "Fold":
190
+ case "Separated": {
191
+ if (node.max === 0) return emptyOptional
192
+ const f = this.of(node.self)
193
+ if (f.mode !== Strict) return unknown
194
+ return node.min === 0 ? withMode(f, Optional) : f
195
+ }
196
+ case "Suspend":
197
+ return this.of(node.resolve())
198
+ default:
199
+ return unknown
200
+ }
201
+ }
202
+
203
+ private sequence(parsers: ReadonlyArray<ParserLike>): First {
204
+ let acc: First | undefined
205
+ for (const p of parsers) {
206
+ const f = this.of(p)
207
+ if (f.mode === Unknown) return unknown
208
+ acc = acc === undefined ? f : union(acc, f, f.mode)
209
+ if (f.mode === Strict) return acc
210
+ }
211
+ return acc ?? emptyOptional
212
+ }
213
+ }
214
+
215
+ /**
216
+ * For an `Alt`: the branches worth trying for each next unit, each next
217
+ * high unit, and at the end of the input, or `undefined` when pruning
218
+ * would never skip a branch.
219
+ */
220
+ export interface AltPlan {
221
+ /** Indexed by unit: indexes into `lists`. */
222
+ readonly byUnit: Uint8Array
223
+ readonly high: number
224
+ readonly end: number
225
+ readonly lists: ReadonlyArray<ReadonlyArray<number>>
226
+ }
227
+
228
+ export const planAlt = (firsts: ReadonlyArray<First>): AltPlan | undefined => {
229
+ const n = firsts.length
230
+ if (n < 2 || n > 64) return undefined
231
+ const lists: Array<ReadonlyArray<number>> = []
232
+ const index = new Map<string, number>()
233
+ const listOf = (keep: (f: First) => boolean): number => {
234
+ const list: Array<number> = []
235
+ for (let i = 0; i < n; i++) if (keep(firsts[i]!)) list.push(i)
236
+ const key = list.join(",")
237
+ let k = index.get(key)
238
+ if (k === undefined) {
239
+ k = lists.length
240
+ lists.push(list)
241
+ index.set(key, k)
242
+ }
243
+ return k
244
+ }
245
+ const byUnit = new Uint8Array(TableSize)
246
+ for (let u = 0; u < TableSize; u++) byUnit[u] = listOf((f) => f.mode !== Strict || f.units[u] === 1)
247
+ const high = listOf((f) => f.mode !== Strict || f.high)
248
+ const end = listOf((f) => f.mode !== Strict)
249
+ if (lists.length === 1 && lists[0]!.length === n) return undefined
250
+ if (lists.length > 255) return undefined
251
+ return { byUnit, high, end, lists }
252
+ }