parseman 0.58.2 → 0.59.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.
- package/dist/analysis/diagnostics.js +344 -130
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +346 -131
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/combinators/field-trivia.d.ts.map +1 -1
- package/dist/combinators/first-set.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/node.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/scanTo.d.ts.map +1 -1
- package/dist/combinators/token.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/compiler/token-capability.js +28 -4
- package/dist/compiler/token-capability.js.map +2 -2
- package/dist/compiler/token-scanner.d.ts.map +1 -1
- package/dist/cst/capture-buffer.d.ts.map +1 -1
- package/dist/cst/plain.js +69 -0
- package/dist/cst/plain.js.map +6 -0
- package/dist/index.js +350 -138
- package/dist/index.js.map +3 -3
- package/dist/language-service/index.js +29 -5
- package/dist/language-service/index.js.map +3 -3
- package/dist/lead-ranges.d.ts +26 -0
- package/dist/lead-ranges.d.ts.map +1 -0
- package/dist/line-index.d.ts +13 -0
- package/dist/line-index.d.ts.map +1 -1
- package/dist/plugin/entry-supercompiler.d.ts.map +1 -1
- package/dist/plugin/index.js +352 -140
- package/dist/plugin/index.js.map +3 -3
- package/dist/recovery/scan.d.ts.map +1 -1
- package/dist/regex/classes.d.ts +5 -3
- package/dist/regex/classes.d.ts.map +1 -1
- package/dist/regex/first-set.d.ts +7 -1
- package/dist/regex/first-set.d.ts.map +1 -1
- package/dist/run/index.js +14 -4
- package/dist/run/index.js.map +3 -3
- package/dist/spec/index.js +18 -1
- package/dist/spec/index.js.map +2 -2
- package/dist/table/assemble.d.ts.map +1 -1
- package/dist/table/compile-rule-map.d.ts.map +1 -1
- package/dist/table/emit-assembly.d.ts +9 -2
- package/dist/table/emit-assembly.d.ts.map +1 -1
- package/dist/table/emit.d.ts +6 -0
- package/dist/table/emit.d.ts.map +1 -1
- package/dist/table/encode-baseline.d.ts.map +1 -1
- package/dist/table/index.js +330 -129
- package/dist/table/index.js.map +3 -3
- package/dist/table/program.d.ts.map +1 -1
- package/dist/table/run-support.d.ts.map +1 -1
- package/dist/table/scan-shapes.d.ts +1 -1
- package/dist/table/scan-shapes.d.ts.map +1 -1
- package/dist/table/site-labels.d.ts.map +1 -1
- package/dist/table/trivia-gap.d.ts +11 -1
- package/dist/table/trivia-gap.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +2 -1
- package/src/combinators/choice.ts +4 -0
- package/src/combinators/dispatch.ts +2 -1
- package/src/combinators/field-trivia.ts +6 -5
- package/src/combinators/first-set.ts +13 -4
- package/src/combinators/keywords.ts +3 -2
- package/src/combinators/literal.ts +5 -4
- package/src/combinators/node.ts +3 -1
- package/src/combinators/regex.ts +33 -13
- package/src/combinators/scanTo.ts +2 -1
- package/src/combinators/token.ts +3 -6
- package/src/compiler/linker.ts +4 -2
- package/src/compiler/token-scanner.ts +4 -1
- package/src/cst/capture-buffer.ts +13 -5
- package/src/cst/plain.d.ts +35 -0
- package/src/cst/plain.js +74 -0
- package/src/lead-ranges.ts +53 -0
- package/src/line-index.ts +37 -0
- package/src/plugin/entry-supercompiler.ts +3 -2
- package/src/recovery/scan.ts +4 -4
- package/src/regex/classes.ts +22 -7
- package/src/regex/first-set.ts +67 -12
- package/src/table/assemble.ts +75 -38
- package/src/table/compile-rule-map.ts +5 -3
- package/src/table/emit-assembly.ts +69 -32
- package/src/table/emit.ts +9 -1
- package/src/table/encode-baseline.ts +2 -1
- package/src/table/program.ts +2 -1
- package/src/table/run-support.ts +4 -7
- package/src/table/scan-shapes.ts +5 -2
- package/src/table/site-labels.ts +2 -1
- package/src/table/trivia-gap.ts +19 -0
- package/src/version.ts +1 -1
|
@@ -142,6 +142,9 @@ export const EMITTED_PARAMS = [
|
|
|
142
142
|
// Appended for the pass-through collapse (`run-support.ts`); old factories
|
|
143
143
|
// ignore it.
|
|
144
144
|
'collapseEmpty',
|
|
145
|
+
// Appended for constructor-built tree objects (`cst/plain.d.ts`): a `new` call has
|
|
146
|
+
// no allocation site, so V8 cannot pretenure them. Old factories ignore them.
|
|
147
|
+
'_SP', '_LF', '_ND', '_FC', '_BUF',
|
|
145
148
|
] as const
|
|
146
149
|
|
|
147
150
|
/**
|
|
@@ -180,9 +183,9 @@ if(s!==null)return skipTriviaScanned(s,input,cur,ctx)
|
|
|
180
183
|
if(needsDeferredTriviaCommit(ctx))return skipDeferredTrivia(input,cur,ctx)
|
|
181
184
|
return advanceTrivia(input,cur,ctx)
|
|
182
185
|
}
|
|
183
|
-
function _pushLeaf(ctx,value,s,e){pushCstLeaf(ctx,
|
|
186
|
+
function _pushLeaf(ctx,value,s,e){pushCstLeaf(ctx,new _LF(value,new _SP(s,e)))}
|
|
184
187
|
function _pushLeafBuf(ctx,value,s,e){
|
|
185
|
-
const l=
|
|
188
|
+
const l=new _LF(value,new _SP(s,e))
|
|
186
189
|
const b=ctx._cstBuf
|
|
187
190
|
if(b.ch!==undefined)b.ch.push(l)
|
|
188
191
|
else if(b.single!==undefined){b.ch=[b.single,l];b.single=undefined}
|
|
@@ -242,7 +245,7 @@ if(ctx._rootTriviaLog!==undefined&&ctx._rootTriviaLog.length!==rt)ctx._rootTrivi
|
|
|
242
245
|
/** Printed only into assemblies that open a count-only raw buffer. */
|
|
243
246
|
const NO_RAW_RUNTIME_PRELUDE = `
|
|
244
247
|
function _pushLeafNoRawBuf(ctx,value,s,e){
|
|
245
|
-
const l=
|
|
248
|
+
const l=new _LF(value,new _SP(s,e))
|
|
246
249
|
const b=ctx._cstBuf
|
|
247
250
|
if(b.ch!==undefined)b.ch.push(l)
|
|
248
251
|
else if(b.single!==undefined){b.ch=[b.single,l];b.single=undefined}
|
|
@@ -414,7 +417,7 @@ function emitRollback(t: string, buf: boolean, rawMode: number, s: Sinks = NO_SI
|
|
|
414
417
|
* only the first branch, a known slot keeps only the second (with the scope's own
|
|
415
418
|
* scanner already bound into `skip`), and `TRI_UNKNOWN` keeps both.
|
|
416
419
|
*/
|
|
417
|
-
function emitTerm(callee: string, dst: string, t: string, l: SiteLabel, skip: string): string {
|
|
420
|
+
function emitTerm(callee: string, dst: string, t: string, l: SiteLabel, skip: string, miss?: (c: string) => string): string {
|
|
418
421
|
const fast = `const ${t}v=${callee}(input,cur,ctx)
|
|
419
422
|
if(${t}v===FAIL)return FAIL
|
|
420
423
|
${dst}=${t}v
|
|
@@ -432,7 +435,14 @@ if(${t}v===FAIL)return FAIL
|
|
|
432
435
|
${dst}=${t}v
|
|
433
436
|
if(EC.e>${t}s)cur=EC.e
|
|
434
437
|
else{rollbackScannedTriviaAt(ctx,${t}tl,${t}stl,${t}lg,${t}slg,${t}rt,${t}srt)}`
|
|
435
|
-
|
|
438
|
+
// A character no trivia of this slot can start opens the empty gap, which
|
|
439
|
+
// records nothing, so the term runs at `cur` with no marks around a scan.
|
|
440
|
+
if (l.tri !== TRI_UNKNOWN) return miss === undefined ? scanned : `const ${t}c=input.charCodeAt(cur)
|
|
441
|
+
if(${miss(`${t}c`)}){
|
|
442
|
+
${fast}
|
|
443
|
+
}else{
|
|
444
|
+
${scanned}
|
|
445
|
+
}`
|
|
436
446
|
return `if(ctx.trivia===undefined){
|
|
437
447
|
${fast}
|
|
438
448
|
}else{
|
|
@@ -588,6 +598,13 @@ export function emitAssemblySource(
|
|
|
588
598
|
extraIps: readonly number[] = [],
|
|
589
599
|
staticBuild = false,
|
|
590
600
|
select?: ReadonlySet<number>,
|
|
601
|
+
/**
|
|
602
|
+
* Leave the `_errors` bit of a lexical program over a scan out of the side-sink
|
|
603
|
+
* census below. A budgeted selection is priced and chosen on this lowering, so
|
|
604
|
+
* the marks that bit adds move no site in or out of a budget; the artifact is
|
|
605
|
+
* then emitted with them.
|
|
606
|
+
*/
|
|
607
|
+
scanSinkNeutral = false,
|
|
591
608
|
): EmitResult {
|
|
592
609
|
const { code, k, fx, disp, dsp, triviaLabelled } = t
|
|
593
610
|
const swapLegal = !cfg.trackLines
|
|
@@ -733,13 +750,15 @@ export function emitAssemblySource(
|
|
|
733
750
|
// that otherwise matched byte for byte.
|
|
734
751
|
//
|
|
735
752
|
// Any future row whose child is not an offset in `code` belongs here for the
|
|
736
|
-
// same reason. `OP_LIVE` is the other such row, and it is unemittable.
|
|
753
|
+
// same reason. `OP_LIVE` is the other such row, and it is unemittable. An
|
|
754
|
+
// `OP_LEX_PROGRAM` over a scan (`t.lexPrograms[id].scan`, a whole `balanced()`
|
|
755
|
+
// lowered as one recognizer) runs that same interpreted scan, so it is one too.
|
|
737
756
|
const sinkSites = reachable
|
|
738
757
|
const sinkBits = new Map<number, number>()
|
|
739
758
|
for (const ip of sinkSites) {
|
|
740
759
|
const op = code[ip]
|
|
741
760
|
sinkBits.set(ip,
|
|
742
|
-
op === OP_SCAN ? 3
|
|
761
|
+
op === OP_SCAN || (!scanSinkNeutral && op === OP_LEX_PROGRAM && t.lexPrograms[code[ip + 1]!]!.scan !== undefined) ? 3
|
|
743
762
|
: (op === OP_FIELD ? 1 : 0)
|
|
744
763
|
| (op === OP_EXPECT || (REC && (op === OP_REP || op === OP_REPV)) ? 2 : 0))
|
|
745
764
|
}
|
|
@@ -974,6 +993,19 @@ return 1
|
|
|
974
993
|
return `if(${gateMissTest(child)}){${miss}}\n`
|
|
975
994
|
}
|
|
976
995
|
|
|
996
|
+
/**
|
|
997
|
+
* The test that no trivia of slot `ki` can start at the code unit `c`: the
|
|
998
|
+
* slot's first characters, as code points, decide every code unit below the
|
|
999
|
+
* surrogates; a surrogate, or `NaN` past the end of input, is left to the scan.
|
|
1000
|
+
* `undefined` where the first set is unbounded or too wide to print.
|
|
1001
|
+
*/
|
|
1002
|
+
function triviaMiss(ki: number): ((c: string) => string) | undefined {
|
|
1003
|
+
const leads = triviaLeadRanges(ki, t.trivia)
|
|
1004
|
+
if (leads === undefined || leads.length > 16) return undefined
|
|
1005
|
+
if (leads.length === 0) return c => `${c}<55296`
|
|
1006
|
+
return c => `${c}<55296&&!(${leads.map(r => r.lo === r.hi ? `${c}===${r.lo}` : `(${c}>=${r.lo}&&${c}<=${r.hi})`).join('||')})`
|
|
1007
|
+
}
|
|
1008
|
+
|
|
977
1009
|
function skipFor(l: SiteLabel): string {
|
|
978
1010
|
if (l.tri < 0) return '_skipTrivia'
|
|
979
1011
|
const ki = l.tri
|
|
@@ -991,11 +1023,16 @@ return 1
|
|
|
991
1023
|
if (!hasScan) {
|
|
992
1024
|
// No installed scanner: the labelled/`trackLines` path, which records
|
|
993
1025
|
// through `scanTrivia`. `needsDeferredTriviaCommit` is implied by an open
|
|
994
|
-
// buffer, so an in-node site skips the call that asks.
|
|
1026
|
+
// buffer, so an in-node site skips the call that asks. A character no gap
|
|
1027
|
+
// can start answers first, with no call (`triviaMiss`).
|
|
1028
|
+
const miss = triviaMiss(ki)
|
|
1029
|
+
const pre = miss === undefined ? '' : `const c=input.charCodeAt(cur)
|
|
1030
|
+
if(${miss('c')})return cur
|
|
1031
|
+
`
|
|
995
1032
|
skipDefs.push(l.buf
|
|
996
|
-
? `function ${nm}(input,cur,ctx){return skipDeferredTrivia(input,cur,ctx)}`
|
|
1033
|
+
? `function ${nm}(input,cur,ctx){${pre === '' ? '' : `\n${pre}`}return skipDeferredTrivia(input,cur,ctx)}`
|
|
997
1034
|
: `function ${nm}(input,cur,ctx){
|
|
998
|
-
if(needsDeferredTriviaCommit(ctx))return skipDeferredTrivia(input,cur,ctx)
|
|
1035
|
+
${pre}if(needsDeferredTriviaCommit(ctx))return skipDeferredTrivia(input,cur,ctx)
|
|
999
1036
|
return advanceTrivia(input,cur,ctx)}`)
|
|
1000
1037
|
return nm
|
|
1001
1038
|
}
|
|
@@ -1397,7 +1434,7 @@ try{return ${child}(input,pos,ctx)}finally{ctx.state=saved}
|
|
|
1397
1434
|
return `${head}
|
|
1398
1435
|
const v=${child}(input,pos,ctx)
|
|
1399
1436
|
if(v===FAIL)return FAIL
|
|
1400
|
-
return ${fn}(v,
|
|
1437
|
+
return ${fn}(v,new _SP(pos,EC.e))
|
|
1401
1438
|
}`
|
|
1402
1439
|
}
|
|
1403
1440
|
|
|
@@ -1427,7 +1464,7 @@ return v
|
|
|
1427
1464
|
const v=${child}(input,pos,ctx)
|
|
1428
1465
|
if(v===FAIL)return FAIL
|
|
1429
1466
|
const f=ctx._fields
|
|
1430
|
-
if(f!==undefined)f.push({name
|
|
1467
|
+
if(f!==undefined)f.push(new _FC(${name},v,new _SP(pos,EC.e)))
|
|
1431
1468
|
return v
|
|
1432
1469
|
}`
|
|
1433
1470
|
}
|
|
@@ -1463,7 +1500,7 @@ return v
|
|
|
1463
1500
|
return `${head}
|
|
1464
1501
|
const v=${child}(input,pos,ctx)
|
|
1465
1502
|
if(v!==FAIL)return v
|
|
1466
|
-
const err={_tag:'parseError',span:${prog.lines === 1 ? 'spanLines(ctx,pos,pos)' : '
|
|
1503
|
+
const err={_tag:'parseError',span:${prog.lines === 1 ? 'spanLines(ctx,pos,pos)' : 'new _SP(pos,pos)'},expected:${xf}}
|
|
1467
1504
|
const es=ctx._errors
|
|
1468
1505
|
if(es!==undefined)es.push(err)
|
|
1469
1506
|
${REC ? 'if(ctx._tolerant===true)captureError(ctx,err)\n' : ''}ctx._fc=false
|
|
@@ -1478,7 +1515,7 @@ return err
|
|
|
1478
1515
|
// The SPAN is the routed item's own object, not a copy — `assemble.ts`
|
|
1479
1516
|
// hands the same one to the leaf, and a fresh `{start,end}` here would
|
|
1480
1517
|
// be a different tree for any consumer comparing by identity.
|
|
1481
|
-
const push = `pushCstLeaf(ctx,
|
|
1518
|
+
const push = `pushCstLeaf(ctx,new _LF(it.value,it.span))`
|
|
1482
1519
|
return `${head}
|
|
1483
1520
|
const it=ctx._routed
|
|
1484
1521
|
if(it===undefined||pos!==it.span.start){
|
|
@@ -1587,8 +1624,8 @@ ctx._triviaLog=sOtl
|
|
|
1587
1624
|
}
|
|
1588
1625
|
if(v===FAIL)return FAIL
|
|
1589
1626
|
const e=EC.e
|
|
1590
|
-
const out=${isToken ? 'input.slice(pos,e)' : `${fn}(v,
|
|
1591
|
-
if(wasCap)pushCstLeaf(ctx,{
|
|
1627
|
+
const out=${isToken ? 'input.slice(pos,e)' : `${fn}(v,new _SP(pos,e))`}
|
|
1628
|
+
if(wasCap)pushCstLeaf(ctx,new _LF(${capturesSource ? 'input.slice(pos,e)' : 'out'},new _SP(pos,e)))
|
|
1592
1629
|
EC.e=e
|
|
1593
1630
|
return out
|
|
1594
1631
|
}`
|
|
@@ -1613,7 +1650,7 @@ ${hasSuffix && (lineFlags & 2) !== 0 ? 'if(sm)_trackLines(ctx,input,e)' : ''}
|
|
|
1613
1650
|
${hasSuffix ? `if(!sm){ctx._fe=e;ctx._fx=${suffixExpected};if(ctx._probe!==undefined)failAt(ctx,${suffixExpected},e)}` : ''}
|
|
1614
1651
|
const v=${pending ? 'tp?_pfTokValue:' : ''}input.slice(pos,e)
|
|
1615
1652
|
${pending ? 'if(tp){_pfTokBody=-1;_pfTokValue=undefined}' : ''}
|
|
1616
|
-
if(ctx._cstBuf!==undefined||ctx._cstLeaves!==undefined)pushCstLeaf(ctx,
|
|
1653
|
+
if(ctx._cstBuf!==undefined||ctx._cstLeaves!==undefined)pushCstLeaf(ctx,new _LF(v,new _SP(pos,e)))
|
|
1617
1654
|
EC.e=e
|
|
1618
1655
|
return v
|
|
1619
1656
|
}`
|
|
@@ -1653,7 +1690,7 @@ ctx._rootTriviaLog=sRtl
|
|
|
1653
1690
|
}
|
|
1654
1691
|
if(e<0)return FAIL
|
|
1655
1692
|
const v=input.slice(pos,e)
|
|
1656
|
-
if(wasCap)pushCstLeaf(ctx,
|
|
1693
|
+
if(wasCap)pushCstLeaf(ctx,new _LF(v,new _SP(pos,e)))
|
|
1657
1694
|
EC.e=e
|
|
1658
1695
|
return v
|
|
1659
1696
|
}`
|
|
@@ -1661,7 +1698,7 @@ return v
|
|
|
1661
1698
|
const e=${run}(input,pos,ctx)
|
|
1662
1699
|
if(e<0)return FAIL
|
|
1663
1700
|
const v=input.slice(pos,e)
|
|
1664
|
-
if(ctx._cstBuf!==undefined||ctx._cstLeaves!==undefined)pushCstLeaf(ctx,
|
|
1701
|
+
if(ctx._cstBuf!==undefined||ctx._cstLeaves!==undefined)pushCstLeaf(ctx,new _LF(v,new _SP(pos,e)))
|
|
1665
1702
|
EC.e=e
|
|
1666
1703
|
return v
|
|
1667
1704
|
}`
|
|
@@ -1803,7 +1840,7 @@ return v
|
|
|
1803
1840
|
const close = (): string => REC ? `${parts.join('\n')}\n}finally{ctx._sync=${sy}}\n}` : `${parts.join('\n')}\n}`
|
|
1804
1841
|
if (n === 1) {
|
|
1805
1842
|
parts.push(fused
|
|
1806
|
-
? projection === 0 ? 'return v0' : `return ${fn}([v0],
|
|
1843
|
+
? projection === 0 ? 'return v0' : `return ${fn}([v0],new _SP(pos,EC.e))`
|
|
1807
1844
|
: wantValues ? 'return [v0]' : 'return undefined')
|
|
1808
1845
|
return close()
|
|
1809
1846
|
}
|
|
@@ -1830,13 +1867,13 @@ return v
|
|
|
1830
1867
|
const callee = leadingTriviaFieldAt(code, k, childIp) ? `(LEAD.at=cur,${kids[i]!})` : kids[i]!
|
|
1831
1868
|
parts.push(holdsGap(code, k, base, n, i, nullable)
|
|
1832
1869
|
? emitHoldTerm(callee, vn, tmp(), L, skipFor(L))
|
|
1833
|
-
: emitTerm(callee, vn, tmp(), L, skipFor(L)))
|
|
1870
|
+
: emitTerm(callee, vn, tmp(), L, skipFor(L), L.tri >= 0 ? triviaMiss(L.tri) : undefined))
|
|
1834
1871
|
}
|
|
1835
1872
|
names.push(vn)
|
|
1836
1873
|
}
|
|
1837
1874
|
parts.push('EC.e=cur')
|
|
1838
1875
|
parts.push(fused
|
|
1839
|
-
? projection >= 0 ? `return ${names[projection]}` : `return ${fn}([${names.join(',')}],
|
|
1876
|
+
? projection >= 0 ? `return ${names[projection]}` : `return ${fn}([${names.join(',')}],new _SP(pos,cur))`
|
|
1840
1877
|
: wantValues ? `return [${names.join(',')}]` : 'return undefined')
|
|
1841
1878
|
return close()
|
|
1842
1879
|
}
|
|
@@ -2176,7 +2213,7 @@ return FAIL
|
|
|
2176
2213
|
const routedCall = (target: string): string => `{const savedRouted=ctx._routed
|
|
2177
2214
|
${emitRollback(m1, L.buf, L.raw, sinks)}
|
|
2178
2215
|
${emitMark(m2, L.buf, L.raw, sinks, false)}
|
|
2179
|
-
ctx._routed={value:key,span:
|
|
2216
|
+
ctx._routed={value:key,span:new _SP(pos,selEnd)}
|
|
2180
2217
|
try{v=${target}(input,pos,ctx)}finally{ctx._routed=savedRouted}
|
|
2181
2218
|
break}`
|
|
2182
2219
|
const plainCall = (target: string): string => `v=${target}(input,selEnd,ctx);break`
|
|
@@ -2445,8 +2482,8 @@ return out
|
|
|
2445
2482
|
const end=${recognize}(input,pos)
|
|
2446
2483
|
if(end<0){ctx._fe=pos;ctx._fx=${xf};return FAIL}
|
|
2447
2484
|
const value=${value}
|
|
2448
|
-
const leaf=
|
|
2449
|
-
const kids=[leaf],rawKids=[leaf],span=
|
|
2485
|
+
const leaf=new _LF(value,new _SP(pos,end))
|
|
2486
|
+
const kids=[leaf],rawKids=[leaf],span=new _SP(pos,end)
|
|
2450
2487
|
EC.e=end
|
|
2451
2488
|
const nd=${build}(kids,undefined,span,rawKids,EMPTY_TL,undefined)
|
|
2452
2489
|
${L.buf && L.raw === RAW_OMIT
|
|
@@ -2574,8 +2611,8 @@ ctx.captureTrivia=sCap
|
|
|
2574
2611
|
if(v===FAIL)return FAIL
|
|
2575
2612
|
const end=EC.e
|
|
2576
2613
|
${collapseChildren
|
|
2577
|
-
? `const nd=captured.length===1?captured[0]:${build}(captured,undefined,
|
|
2578
|
-
: `const nd=${build}(captured,${fields ? 'fieldMap' : 'undefined'},
|
|
2614
|
+
? `const nd=captured.length===1?captured[0]:${build}(captured,undefined,new _SP(pos,end),EMPTY_CH,EMPTY_TL,undefined)`
|
|
2615
|
+
: `const nd=${build}(captured,${fields ? 'fieldMap' : 'undefined'},new _SP(pos,end),EMPTY_CH,EMPTY_TL,undefined)`}
|
|
2579
2616
|
${publish}
|
|
2580
2617
|
EC.e=end
|
|
2581
2618
|
return nd
|
|
@@ -2605,26 +2642,26 @@ return nd
|
|
|
2605
2642
|
const direct = `${build}(kids,fieldMap,span,rawKids,${captureWide ? 'tlog' : 'EMPTY_TL'},st)`
|
|
2606
2643
|
value = hostCst ? `nd=_pfHost!==undefined?${hostCall}:${direct}` : `nd=${direct}`
|
|
2607
2644
|
} else {
|
|
2608
|
-
value = `nd=_pfHost!==undefined?_pfHost(${ty},hostKids,fieldMap,span,rawKids,tlog,st,${tags}):{
|
|
2645
|
+
value = `nd=_pfHost!==undefined?_pfHost(${ty},hostKids,fieldMap,span,rawKids,tlog,st,${tags}):new _ND(${ty},span,st??null,kids===EMPTY_CH?kids:[...kids])`
|
|
2609
2646
|
}
|
|
2610
2647
|
// HOST COLLAPSE applies wherever the node's VALUE comes from the host —
|
|
2611
2648
|
// any node under a CST host, not only the builder-less ones.
|
|
2612
2649
|
const collapsible = keepChildren && (hostCst || (build === undefined && proj < 0))
|
|
2613
2650
|
const openCapture = omitsRaw
|
|
2614
|
-
? `const buf=
|
|
2651
|
+
? `const buf=new _BUF(undefined,true,0)
|
|
2615
2652
|
ctx._cstBuf=buf
|
|
2616
2653
|
ctx._cstChildren=undefined
|
|
2617
2654
|
ctx._cstLeaves=undefined
|
|
2618
2655
|
ctx._cstRawChildren=undefined
|
|
2619
2656
|
ctx._cstTriviaLog=undefined`
|
|
2620
2657
|
: keepChildren
|
|
2621
|
-
? `const buf=
|
|
2658
|
+
? `const buf=new _BUF(undefined,undefined,0)
|
|
2622
2659
|
ctx._cstBuf=buf
|
|
2623
2660
|
ctx._cstChildren=undefined
|
|
2624
2661
|
ctx._cstLeaves=undefined
|
|
2625
2662
|
ctx._cstRawChildren=undefined
|
|
2626
2663
|
ctx._cstTriviaLog=undefined`
|
|
2627
|
-
: `const buf=
|
|
2664
|
+
: `const buf=new _BUF(true,undefined,0)
|
|
2628
2665
|
ctx._cstBuf=buf
|
|
2629
2666
|
ctx._cstChildren=undefined
|
|
2630
2667
|
ctx._cstLeaves=undefined
|
|
@@ -2667,7 +2704,7 @@ ctx._cstTriviaLog=sTl
|
|
|
2667
2704
|
ctx.captureTrivia=sCap
|
|
2668
2705
|
if(v===FAIL)return FAIL
|
|
2669
2706
|
const end=EC.e
|
|
2670
|
-
const span=${tracked ? 'spanLines(ctx,pos,end)' : '
|
|
2707
|
+
const span=${tracked ? 'spanLines(ctx,pos,end)' : 'new _SP(pos,end)'}
|
|
2671
2708
|
const st=${readsState ? 'ctx.state' : 'undefined'}
|
|
2672
2709
|
let nd
|
|
2673
2710
|
${unwrap ? 'if(kids.length===1)nd=unwrapChild(kids[0])\nelse ' : ''}${collapse ? 'if(kids.length===1)nd=kids[0]\nelse ' : ''}${collapsible ? `if(_pfHost!==undefined&&_pfHost._parsemanCstCollapse!==undefined&&kids.length===1&&rawKids.length===1&&_pfHost._parsemanCstCollapse(${ty},kids[0],kids,rawKids))nd=kids[0]
|
package/src/table/emit.ts
CHANGED
|
@@ -3,6 +3,14 @@ import type { FoldedProgram, TableProgram } from './program.ts'
|
|
|
3
3
|
import { resolveTable } from './program.ts'
|
|
4
4
|
import { EMITTED_PARAMS, Unemittable, emitAssemblySource } from './emit-assembly.ts'
|
|
5
5
|
import { cfgKey, type RunCfg } from './assemble.ts'
|
|
6
|
+
import { PARSEMAN_VERSION } from '../version.ts'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Opens every printed assembly factory. A runtime older than the printer passes fewer
|
|
10
|
+
* helpers; refuse it when the assembly is built, instead of failing partway through the
|
|
11
|
+
* first parse on whichever helper is reached first. Runs once per assembly.
|
|
12
|
+
*/
|
|
13
|
+
export const FACTORY_ARITY_GUARD = `if(arguments.length<${EMITTED_PARAMS.length})throw new Error(${JSON.stringify(`parseman: this parser was printed by parseman ${PARSEMAN_VERSION}; upgrade the parseman runtime to at least that version`)});`
|
|
6
14
|
|
|
7
15
|
/**
|
|
8
16
|
* Print a program as the module a build emits.
|
|
@@ -221,7 +229,7 @@ function emitAssemblies(prog: TableProgram, cfgs: readonly AssemblyCfg[]): strin
|
|
|
221
229
|
const plan = em.plan
|
|
222
230
|
out.push('{'
|
|
223
231
|
+ `key:${key},`
|
|
224
|
-
+ `factory:function(${EMITTED_PARAMS.join(',')}){${em.source}\n},`
|
|
232
|
+
+ `factory:function(${EMITTED_PARAMS.join(',')}){${FACTORY_ARITY_GUARD}${em.source}\n},`
|
|
225
233
|
+ 'plan:{'
|
|
226
234
|
+ `classes:[${plan.classes.map(r => `[${r.join(',')}]`).join(',')}],`
|
|
227
235
|
+ `armExpected:[${plan.armExpected.map(r => `[${r.join(',')}]`).join(',')}],`
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Combinator, FirstSet, ParserDef } from '../types.ts'
|
|
2
2
|
import { firstSetOf, matchesEmpty } from '../combinators/first-set.ts'
|
|
3
|
+
import { leadRanges } from '../lead-ranges.ts'
|
|
3
4
|
import { deriveExpected } from '../combinators/expect.ts'
|
|
4
5
|
import { buildReadsState, buildReadsTrivia } from '../compiler/build-arity.ts'
|
|
5
6
|
import {
|
|
@@ -34,7 +35,7 @@ function disjointSets(sets: readonly FirstSet[]): boolean {
|
|
|
34
35
|
const seen: Array<{ lo: number; hi: number }> = []
|
|
35
36
|
for (const fs of sets) {
|
|
36
37
|
if (fs.kind !== 'ranges' || fs.ranges.length === 0) return false
|
|
37
|
-
for (const r of fs.ranges) {
|
|
38
|
+
for (const r of leadRanges(fs.ranges)) {
|
|
38
39
|
for (const p of seen) if (r.lo <= p.hi && p.lo <= r.hi) return false
|
|
39
40
|
seen.push(r)
|
|
40
41
|
}
|
package/src/table/program.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Combinator, ParseContext, ParseResult } from '../types.ts'
|
|
|
2
2
|
import { classifiedTrivia, trivia as triviaOf } from '../combinators/map.ts'
|
|
3
3
|
import { fastTriviaScanner, type FastTriviaScanner } from '../combinators/trivia-skip.ts'
|
|
4
4
|
import { regex } from '../combinators/regex.ts'
|
|
5
|
+
import { leadRanges } from '../lead-ranges.ts'
|
|
5
6
|
import { choice } from '../combinators/choice.ts'
|
|
6
7
|
import { many } from '../combinators/repeat.ts'
|
|
7
8
|
import type { EmittedFactory, PoolPlan } from './emit-assembly.ts'
|
|
@@ -642,7 +643,7 @@ export function decodeClassSpec(spec: string): Array<{ lo: number; hi: number }>
|
|
|
642
643
|
export function resolveClass(spec: string): ResolvedClass {
|
|
643
644
|
const ascii = new Uint8Array(128)
|
|
644
645
|
const hi: number[] = []
|
|
645
|
-
for (const { lo, hi: up } of decodeClassSpec(spec)) {
|
|
646
|
+
for (const { lo, hi: up } of leadRanges(decodeClassSpec(spec))) {
|
|
646
647
|
if (lo < 128) for (let c = lo; c <= Math.min(up, 127); c++) ascii[c] = 1
|
|
647
648
|
if (up >= 128) hi.push(Math.max(lo, 128), up)
|
|
648
649
|
}
|
package/src/table/run-support.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { ParseContext } from '../types.ts'
|
|
16
16
|
import { popCstChild, pushCstChild } from '../cst/capture-buffer.ts'
|
|
17
|
+
import { PlainLeaf, PlainLineSpan, PlainSpan } from '../cst/plain.js'
|
|
17
18
|
|
|
18
19
|
export type Span = { start: number; end: number }
|
|
19
20
|
export type LineSpan = Span & {
|
|
@@ -37,7 +38,7 @@ export function lineCol(ctx: ParseContext, offset: number): [number, number] {
|
|
|
37
38
|
|
|
38
39
|
export function spanLines(ctx: ParseContext, start: number, end: number): LineSpan {
|
|
39
40
|
const s = lineCol(ctx, start), e = lineCol(ctx, end)
|
|
40
|
-
return
|
|
41
|
+
return new PlainLineSpan(start, end, s[0], s[1], e[0], e[1])
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
/**
|
|
@@ -51,11 +52,7 @@ export function rawEntry(v: unknown, input: string, s: number, e: number): unkno
|
|
|
51
52
|
const tg = (v as { _tag?: string })._tag
|
|
52
53
|
if (tg === 'node' || tg === 'leaf' || tg === 'parseError') return v
|
|
53
54
|
}
|
|
54
|
-
return
|
|
55
|
-
_tag: 'leaf',
|
|
56
|
-
value: typeof v === 'string' ? v : (typeof v === 'object' && v !== null ? input.slice(s, e) : ''),
|
|
57
|
-
span: { start: s, end: e },
|
|
58
|
-
}
|
|
55
|
+
return new PlainLeaf(typeof v === 'string' ? v : (typeof v === 'object' && v !== null ? input.slice(s, e) : ''), new PlainSpan(s, e))
|
|
59
56
|
}
|
|
60
57
|
|
|
61
58
|
/** `input`'s code point at `pos`, or −1 at EOF. Surrogate-pair aware. */
|
|
@@ -95,7 +92,7 @@ export function collapseEmpty(
|
|
|
95
92
|
// A frame keeps the raw entry of the child it does not count, for a reducer
|
|
96
93
|
// that reads `rawChildren`.
|
|
97
94
|
const raw = readsRaw ? [rawEntry(undefined, input, pos, end)] : EMPTY_KIDS
|
|
98
|
-
const nd = build(EMPTY_KIDS, undefined,
|
|
95
|
+
const nd = build(EMPTY_KIDS, undefined, new PlainSpan(pos, end), raw, EMPTY_TRIVIA, undefined)
|
|
99
96
|
if (collected) pushCstChild(ctx, nd, ctx._cstBuf?.noRaw === true ? undefined : rawEntry(nd, input, pos, end))
|
|
100
97
|
return nd
|
|
101
98
|
}
|
package/src/table/scan-shapes.ts
CHANGED
|
@@ -235,6 +235,9 @@ function parseSeqParts(source: string): SeqPart[] | null {
|
|
|
235
235
|
let cp: number
|
|
236
236
|
if (uni) { cp = uni.cp; i = uni.next }
|
|
237
237
|
else if (e === undefined) return null
|
|
238
|
+
// A digit escape is a backreference or a legacy octal escape (`\012`); only a
|
|
239
|
+
// lone `\0` is NUL.
|
|
240
|
+
else if (e >= '0' && e <= '9' && (e !== '0' || (source[i + 2] !== undefined && source[i + 2]! >= '0' && source[i + 2]! <= '9'))) return null
|
|
238
241
|
else if (e in CLASS_ESCAPES) { cp = CLASS_ESCAPES[e]!; i += 2 }
|
|
239
242
|
else if ((e >= 'a' && e <= 'z') || (e >= 'A' && e <= 'Z')) return null
|
|
240
243
|
else { cp = e.codePointAt(0)!; i += 2 }
|
|
@@ -1078,11 +1081,11 @@ function parseScanShapeCore(source: string): ScanShape | null {
|
|
|
1078
1081
|
/**
|
|
1079
1082
|
* Flag-aware wrapper around `parseScanShape`. Lowering to a raw code-point scan
|
|
1080
1083
|
* assumes default regex semantics, so any flag that changes matching (`i` case
|
|
1081
|
-
* folding, `u`
|
|
1084
|
+
* folding, `u`/`v` code points and sets, `m`/`s` anchor/dot behavior) disables it.
|
|
1082
1085
|
* `g`/`y` are stickiness-only and safe.
|
|
1083
1086
|
*/
|
|
1084
1087
|
export function scanShapeFromRegex(source: string, flags: string): ScanShape | null {
|
|
1085
|
-
if (/[
|
|
1088
|
+
if (/[msuv]/.test(flags)) return null
|
|
1086
1089
|
if (/i/.test(flags)) return caseFoldShape(source)
|
|
1087
1090
|
return parseScanShape(source)
|
|
1088
1091
|
}
|
package/src/table/site-labels.ts
CHANGED
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
OP_SCAN, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, OP_SEQ, OP_SEQV, OP_SEQX, OP_TOKEN, OP_XFORM,
|
|
66
66
|
} from './ops.ts'
|
|
67
67
|
import { childSlots } from './child-slots.ts'
|
|
68
|
+
import { leadRanges } from '../lead-ranges.ts'
|
|
68
69
|
|
|
69
70
|
/** No scope on this path installs a known trivia — the lattice's top element. */
|
|
70
71
|
export const TRI_UNKNOWN = -2
|
|
@@ -380,5 +381,5 @@ export function triviaLeadRanges(
|
|
|
380
381
|
const fs = trivia[tri]?._meta.firstSet
|
|
381
382
|
if (fs === undefined) return undefined
|
|
382
383
|
if (fs.kind === 'empty') return []
|
|
383
|
-
return fs.kind === 'ranges' && fs.ranges !== undefined ? fs.ranges : undefined
|
|
384
|
+
return fs.kind === 'ranges' && fs.ranges !== undefined ? leadRanges(fs.ranges) : undefined
|
|
384
385
|
}
|
package/src/table/trivia-gap.ts
CHANGED
|
@@ -48,6 +48,25 @@ export function skipDeferredTrivia(input: string, cur: number, ctx: ParseContext
|
|
|
48
48
|
return scan
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* THE CHARACTERS A TRIVIA CAN START WITH, as a table over ASCII: `1` where a run
|
|
53
|
+
* may start, `0` where none can. `null` when the trivia's first set is unbounded.
|
|
54
|
+
*
|
|
55
|
+
* A gap at a character outside the table is the empty gap. It consumes nothing
|
|
56
|
+
* and records nothing in any sink (trivia log, root log, capture buffer, line
|
|
57
|
+
* table), so a skip there can answer `cur` without scanning. A character at or
|
|
58
|
+
* above 128, or past the end of input, is not decided by the table.
|
|
59
|
+
*/
|
|
60
|
+
export function triviaLeadTable(triviaP: Combinator<unknown>): Uint8Array | null {
|
|
61
|
+
const fs = triviaP._meta.firstSet
|
|
62
|
+
if (fs.kind === 'any') return null
|
|
63
|
+
const table = new Uint8Array(128)
|
|
64
|
+
if (fs.kind === 'ranges') {
|
|
65
|
+
for (const r of fs.ranges) for (let c = Math.max(0, r.lo); c <= Math.min(127, r.hi); c++) table[c] = 1
|
|
66
|
+
}
|
|
67
|
+
return table
|
|
68
|
+
}
|
|
69
|
+
|
|
51
70
|
/** The end of the run at `cur` when nothing root-selected lies in it, else −1. */
|
|
52
71
|
function clearRunEnd(input: string, cur: number, ctx: ParseContext, triviaP: Combinator<unknown> | undefined, plain: boolean): number {
|
|
53
72
|
const rootKinds = ctx._rootTriviaKindIndex
|
package/src/version.ts
CHANGED