parseman 0.50.1 → 0.50.3
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/README.md +14 -10
- package/dist/analysis/diagnostics.js +431 -219
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +421 -184
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/choice.d.ts.map +1 -1
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/keywords.d.ts.map +1 -1
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/combinators/map.d.ts.map +1 -1
- package/dist/combinators/ref.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/combinators/scalar.d.ts +6 -0
- package/dist/combinators/scalar.d.ts.map +1 -0
- package/dist/combinators/sequence.d.ts.map +1 -1
- package/dist/combinators/trivia-skip.d.ts +2 -0
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/compiler/token-capability.js +0 -3
- package/dist/compiler/token-capability.js.map +2 -2
- package/dist/index.js +422 -185
- package/dist/index.js.map +3 -3
- package/dist/language-service/index.js +68 -172
- package/dist/language-service/index.js.map +3 -3
- package/dist/parse-context.d.ts.map +1 -1
- package/dist/plugin/index.js +467 -299
- package/dist/plugin/index.js.map +3 -3
- package/dist/run/index.js +1 -0
- package/dist/run/index.js.map +1 -1
- package/dist/spec/index.js +399 -277
- package/dist/spec/index.js.map +3 -3
- package/dist/table/emit-assembly.d.ts.map +1 -1
- package/dist/table/index.js +307 -181
- package/dist/table/index.js.map +3 -3
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +4 -2
- package/src/combinators/choice.ts +34 -1
- package/src/combinators/grammar.ts +39 -0
- package/src/combinators/keywords.ts +14 -3
- package/src/combinators/literal.ts +26 -0
- package/src/combinators/map.ts +60 -0
- package/src/combinators/ref.ts +26 -7
- package/src/combinators/regex.ts +23 -0
- package/src/combinators/repeat.ts +109 -16
- package/src/combinators/scalar.ts +30 -0
- package/src/combinators/sequence.ts +76 -161
- package/src/combinators/trivia-skip.ts +14 -1
- package/src/parse-context.ts +1 -0
- package/src/table/emit-assembly.ts +39 -16
- package/src/types.ts +8 -0
- package/src/version.ts +1 -1
|
@@ -259,6 +259,16 @@ function q(s: string): string {
|
|
|
259
259
|
return JSON.stringify(s)
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
/**
|
|
263
|
+
* A code-unit lookup is a complete first-code-point decision only for a class
|
|
264
|
+
* with no non-ASCII range. At EOF `charCodeAt` yields NaN; lone surrogates and
|
|
265
|
+
* the leading surrogate of an astral point are all outside the 128-slot array,
|
|
266
|
+
* so each reads `undefined` and declines exactly as `classHas(lead(...))` does.
|
|
267
|
+
*/
|
|
268
|
+
function isAsciiOnlyClass(cls: ResolvedClass | null | undefined): cls is ResolvedClass {
|
|
269
|
+
return cls !== null && cls !== undefined && cls.hi.length === 0
|
|
270
|
+
}
|
|
271
|
+
|
|
262
272
|
/**
|
|
263
273
|
* WHICH SIDE SINKS A MARK IN THIS ASSEMBLY HAS TO COVER.
|
|
264
274
|
*
|
|
@@ -1259,12 +1269,15 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)\n` : ''}return FAIL
|
|
|
1259
1269
|
|
|
1260
1270
|
case OP_GATE: {
|
|
1261
1271
|
const child = link(code[ip + 2]!)
|
|
1262
|
-
const
|
|
1272
|
+
const resolved = t.cc[code[ip + 1]!]!
|
|
1273
|
+
const ci = classes.push([resolved]) - 1
|
|
1263
1274
|
classPlan.push([code[ip + 1]!])
|
|
1264
1275
|
const cls = hoist('cl', `CLS[${ci}][0]`)
|
|
1265
1276
|
const xf = fxRef(code[ip + 3]!)
|
|
1266
1277
|
return `${head}
|
|
1267
|
-
if(
|
|
1278
|
+
if(${staticBuild && isAsciiOnlyClass(resolved)
|
|
1279
|
+
? `${cls}.ascii[input.charCodeAt(pos)]!==1`
|
|
1280
|
+
: `!classHas(${cls},lead(input,pos))`}){ctx._fe=pos;ctx._fx=${xf};return FAIL}
|
|
1268
1281
|
return ${child}(input,pos,ctx)
|
|
1269
1282
|
}`
|
|
1270
1283
|
}
|
|
@@ -1700,10 +1713,11 @@ return v
|
|
|
1700
1713
|
// array index feeding ONE call site, which is the megamorphic site
|
|
1701
1714
|
// this unit exists to remove.
|
|
1702
1715
|
const armSwitch = arms.map((a, i) => `case ${i}:v=${a}(input,pos,ctx);break`).join('\n')
|
|
1716
|
+
const directAscii = staticBuild && table.hi.length === 0
|
|
1703
1717
|
return `${head}
|
|
1704
|
-
const c
|
|
1718
|
+
const c=${directAscii ? 'input.charCodeAt(pos)' : 'lead(input,pos)'}
|
|
1705
1719
|
let arm=-1
|
|
1706
|
-
if(c>=0&&c<128){const a=${asc}[c];if(a!==0)arm=a-1}
|
|
1720
|
+
if(${directAscii ? 'c<128' : 'c>=0&&c<128'}){const a=${asc}[c];if(a!==0)arm=a-1}
|
|
1707
1721
|
${hiArr.length === 0 ? '' : `else if(c>=128){const h=${hoist('hi', `DISP[${di}].hi`)}
|
|
1708
1722
|
for(let i=0;i<h.length;i+=3){if(c>=h[i]&&c<=h[i+1]){arm=h[i+2];break}}}`}
|
|
1709
1723
|
if(arm>=0){
|
|
@@ -1729,7 +1743,11 @@ return FAIL
|
|
|
1729
1743
|
const di = code[ip + 1]!
|
|
1730
1744
|
const expected = Array.from({ length: n }, (_, i) => fxRef(code[base + n + i]!))
|
|
1731
1745
|
const gates = Array.from({ length: n }, (_, i) => table.armCls[i] ?? null)
|
|
1732
|
-
const
|
|
1746
|
+
const maskable = n <= 32
|
|
1747
|
+
const directAsciiMask = staticBuild && maskable && gates.every(isAsciiOnlyClass)
|
|
1748
|
+
const gateRefs = directAsciiMask
|
|
1749
|
+
? []
|
|
1750
|
+
: Array.from({ length: n }, (_, i) => hoist('g', `DISP[${di}].armCls[${i}]`))
|
|
1733
1751
|
type ChoicePretest = {
|
|
1734
1752
|
readonly scalar?: readonly string[]
|
|
1735
1753
|
readonly sequence?: ScalarSequenceRef
|
|
@@ -1754,7 +1772,6 @@ return FAIL
|
|
|
1754
1772
|
const sequence = largeChoiceScalarSequences.get(code[base + i]!)
|
|
1755
1773
|
return sequence === undefined ? undefined : { sequence: scalarSequenceRef(sequence) }
|
|
1756
1774
|
})
|
|
1757
|
-
const maskable = n <= 32
|
|
1758
1775
|
const maskName = maskable
|
|
1759
1776
|
? hoist('mk', `MASK[${masks.push(maskForClassRow(gates)) - 1}]`)
|
|
1760
1777
|
: ''
|
|
@@ -1856,16 +1873,16 @@ ${merge}
|
|
|
1856
1873
|
ctx._fe=pos;ctx._fx=acc??${choiceFx}
|
|
1857
1874
|
return FAIL`
|
|
1858
1875
|
return `${head}
|
|
1859
|
-
const c
|
|
1876
|
+
const c=${directAsciiMask ? 'input.charCodeAt(pos)' : 'lead(input,pos)'}
|
|
1860
1877
|
${hasRollback ? emitMark(p, L.buf, L.raw, sinks) : ''}
|
|
1861
1878
|
${deepAt.map((at, i) => `let ${at},${deepFx[i]}`).join('\n')}
|
|
1862
1879
|
${maskable ? `if(c<128){
|
|
1863
|
-
const bits=${maskName}[c<0?128:c]
|
|
1880
|
+
const bits=${maskName}[${directAsciiMask ? 'c' : 'c<0?128:c'}]
|
|
1864
1881
|
if(bits===0){ctx._fe=pos;ctx._fx=${choiceFx};return FAIL}
|
|
1865
1882
|
${lazyArms(true)}
|
|
1866
|
-
}else{
|
|
1883
|
+
}${directAsciiMask ? '' : `else{
|
|
1867
1884
|
${lazyArms(false)}
|
|
1868
|
-
}
|
|
1885
|
+
}`}
|
|
1869
1886
|
` : lazyArms(false)}
|
|
1870
1887
|
${finish}
|
|
1871
1888
|
}`
|
|
@@ -1920,7 +1937,7 @@ if(ctx._fc===true){${startFailureExact ? `if(best===pos){acc=${catchName}(${i},0
|
|
|
1920
1937
|
${rollbackFor(i)}
|
|
1921
1938
|
}${routeMiss}${sequenceMiss}`
|
|
1922
1939
|
}).join('\n') : ''
|
|
1923
|
-
const generalArms = arms.map((arm, i) => {
|
|
1940
|
+
const generalArms = directAsciiMask ? '' : arms.map((arm, i) => {
|
|
1924
1941
|
const pretest = pretests[i]
|
|
1925
1942
|
const decision = pretest?.token === undefined && pretest?.sequence === undefined ? '' : tmp()
|
|
1926
1943
|
const scalarCondition = pretest?.scalar === undefined ? ''
|
|
@@ -1963,12 +1980,12 @@ ${rollbackFor(i)}
|
|
|
1963
1980
|
// arm can run, so the choice's encoded flat expected set is already
|
|
1964
1981
|
// the exact result. Do not walk the arm ladder merely to rebuild it.
|
|
1965
1982
|
return `${head}
|
|
1966
|
-
const c
|
|
1983
|
+
const c=${directAsciiMask ? 'input.charCodeAt(pos)' : 'lead(input,pos)'}
|
|
1967
1984
|
${hasRollback ? emitMark(p, L.buf, L.raw, sinks) : ''}
|
|
1968
1985
|
let acc
|
|
1969
1986
|
let best=pos
|
|
1970
1987
|
${maskable ? `if(c<128){
|
|
1971
|
-
const bits=${maskName}[c<0?128:c]
|
|
1988
|
+
const bits=${maskName}[${directAsciiMask ? 'c' : 'c<0?128:c'}]
|
|
1972
1989
|
if(bits===0){ctx._fe=pos;ctx._fx=${choiceFx};return FAIL}
|
|
1973
1990
|
${startFailureExact ? '' : 'let prev=0'}
|
|
1974
1991
|
${maskArms}
|
|
@@ -2076,14 +2093,20 @@ return [key,v]
|
|
|
2076
2093
|
const itemFx = reportItem ? fxRef(code[ip + 6]!) : 'EMPTY_FX'
|
|
2077
2094
|
const collect = op === OP_REP
|
|
2078
2095
|
let itemCls: string | undefined
|
|
2096
|
+
let itemAscii = false
|
|
2079
2097
|
if (!REC) {
|
|
2080
2098
|
const itemClassIndex = sepIp < 0 ? code[ip + 7]! : -1
|
|
2081
2099
|
if (itemClassIndex >= 0) {
|
|
2082
|
-
const
|
|
2100
|
+
const resolved = t.cc[itemClassIndex]!
|
|
2101
|
+
const ci = classes.push([resolved]) - 1
|
|
2083
2102
|
classPlan.push([itemClassIndex])
|
|
2084
2103
|
itemCls = hoist('ri', `CLS[${ci}][0]`)
|
|
2104
|
+
itemAscii = staticBuild && isAsciiOnlyClass(resolved)
|
|
2085
2105
|
}
|
|
2086
2106
|
}
|
|
2107
|
+
const itemMiss = (at: string): string => itemAscii
|
|
2108
|
+
? `${itemCls}.ascii[input.charCodeAt(${at})]!==1`
|
|
2109
|
+
: `!classHas(${itemCls},lead(input,${at}))`
|
|
2087
2110
|
const skipBeforeFirst = sepIp < 0 && min === 0
|
|
2088
2111
|
const p = tmp()
|
|
2089
2112
|
// `viaRepItem` is `sep === undefined && count >= min && (count > 0 ||
|
|
@@ -2199,7 +2222,7 @@ const out=${collect ? '[]' : 'undefined'}
|
|
|
2199
2222
|
${knownTrivia === undefined ? 'const hasTrivia=ctx.trivia!==undefined\n' : ''}${L.buf ? '' : 'const needMark=_rollbackNeeded(ctx)\n'}${REC ? `const ${my}=ctx._sync\n` : ''}${itemCls === undefined ? '' : `const ${p}gate=ctx._probe===undefined\n`}let cur=pos
|
|
2200
2223
|
let count=0
|
|
2201
2224
|
for(;;){
|
|
2202
|
-
${max >= 0 ? `if(count>=${max})break\n` : ''}${sep !== undefined ? `if(count>0&&count>=${min}&&cur>=input.length)break\n` : ''}${itemCls !== undefined && sep === undefined ? `if(count>=${min}&&${p}gate&&${hasTrivia === 'false' ? 'true' : hasTrivia === 'true' ? 'false' : '!hasTrivia'}
|
|
2225
|
+
${max >= 0 ? `if(count>=${max})break\n` : ''}${sep !== undefined ? `if(count>0&&count>=${min}&&cur>=input.length)break\n` : ''}${itemCls !== undefined && sep === undefined ? `if(count>=${min}&&${p}gate&&${hasTrivia === 'false' ? 'true' : hasTrivia === 'true' ? 'false' : '!hasTrivia'}&&${itemMiss('cur')})break\n` : ''}let ${p}raw=0,${p}tl=0,${p}lv=0,${p}lg=0,${p}rt=0${sinks.fd ? `,${p}fd=0` : ''}${sinks.er ? `,${p}er=0` : ''}
|
|
2203
2226
|
${markBody}
|
|
2204
2227
|
let itemStart=cur
|
|
2205
2228
|
let sepEnd=-1
|
|
@@ -2221,7 +2244,7 @@ itemStart=${hasTrivia === 'false' ? 'EC.e' : hasTrivia === 'true' ? `${skip}(inp
|
|
|
2221
2244
|
${rb}
|
|
2222
2245
|
${trailingAllowed ? 'if(sepEnd>=0)cur=sepEnd\n' : ''}break
|
|
2223
2246
|
}
|
|
2224
|
-
${itemCls === undefined ? '' : `if(count>=${min}&&${p}gate
|
|
2247
|
+
${itemCls === undefined ? '' : `if(count>=${min}&&${p}gate&&${itemMiss('itemStart')}){
|
|
2225
2248
|
${rb}
|
|
2226
2249
|
${trailingAllowed ? 'if(sepEnd>=0)cur=sepEnd\n' : ''}break
|
|
2227
2250
|
}
|
package/src/types.ts
CHANGED
|
@@ -115,6 +115,10 @@ export type Combinator<T> = {
|
|
|
115
115
|
readonly _tag: string
|
|
116
116
|
readonly _meta: ParserMeta
|
|
117
117
|
readonly _def: ParserDef
|
|
118
|
+
/** Experimental strict interpreter ABI: end >= 0 succeeds, ~failurePos fails. */
|
|
119
|
+
readonly _parseScalar?: ((input: string, pos: number, ctx: ParseContext) => number) | undefined
|
|
120
|
+
/** Construction-owned sticky matcher for exact terminal fusion. */
|
|
121
|
+
readonly _stickyRegex?: RegExp
|
|
118
122
|
parse(input: string, pos: number, ctx: ParseContext): ParseResult<T>
|
|
119
123
|
}
|
|
120
124
|
|
|
@@ -311,6 +315,8 @@ export type ParseContext = {
|
|
|
311
315
|
_fx?: string[] | undefined
|
|
312
316
|
/** Framework-internal compiled-output committed-failure flag. */
|
|
313
317
|
_fc?: boolean | undefined
|
|
318
|
+
/** Experimental strict interpreter semantic-value register. */
|
|
319
|
+
_sv?: unknown
|
|
314
320
|
/**
|
|
315
321
|
* When set by completionsAt(), tracks the highest-position ParseFail seen
|
|
316
322
|
* during parsing up to _probe.offset. Used to return completions at the cursor
|
|
@@ -427,6 +433,8 @@ export type ParserMeta = {
|
|
|
427
433
|
isTrivia: boolean
|
|
428
434
|
/** User-defined labels for labeled trivia arms (`label(name, parser)`). */
|
|
429
435
|
triviaKindLabels?: readonly string[]
|
|
436
|
+
/** Preclassified lightweight trivia scanner for the ordinary skip path. */
|
|
437
|
+
triviaScanner?: ((input: string, cur: number) => number) | null
|
|
430
438
|
/** Set only by `classifiedTrivia()`: each root-visible category is a separate
|
|
431
439
|
* grammar arm rather than an arbitrary label on a broad recognizer. */
|
|
432
440
|
rootTriviaClassified?: true
|
package/src/version.ts
CHANGED