parseman 0.57.0 → 0.58.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 +1799 -962
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +1928 -1091
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/compiler/token-alphabet.d.ts +11 -1
- package/dist/compiler/token-alphabet.d.ts.map +1 -1
- package/dist/compiler/token-capability.js +69 -16
- package/dist/compiler/token-capability.js.map +2 -2
- package/dist/cst/capture-buffer.d.ts +6 -0
- package/dist/cst/capture-buffer.d.ts.map +1 -1
- package/dist/index.js +1016 -177
- package/dist/index.js.map +3 -3
- package/dist/plugin/index.d.ts +25 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1209 -236
- package/dist/plugin/index.js.map +3 -3
- 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 +1 -1
- package/dist/table/emit-assembly.d.ts.map +1 -1
- package/dist/table/emit.d.ts.map +1 -1
- package/dist/table/encode.d.ts.map +1 -1
- package/dist/table/exec.d.ts.map +1 -1
- package/dist/table/factor-choice.d.ts +42 -0
- package/dist/table/factor-choice.d.ts.map +1 -0
- package/dist/table/fail-clean.d.ts +3 -0
- package/dist/table/fail-clean.d.ts.map +1 -0
- package/dist/table/hold-gap.d.ts +8 -0
- package/dist/table/hold-gap.d.ts.map +1 -0
- package/dist/table/index.js +1369 -259
- package/dist/table/index.js.map +3 -3
- package/dist/table/lex-program.d.ts +7 -3
- package/dist/table/lex-program.d.ts.map +1 -1
- package/dist/table/ops.d.ts +8 -0
- package/dist/table/ops.d.ts.map +1 -1
- package/dist/table/run-support.d.ts +19 -0
- package/dist/table/run-support.d.ts.map +1 -1
- package/dist/table/scalar-terminal.d.ts +2 -0
- package/dist/table/scalar-terminal.d.ts.map +1 -1
- package/dist/table/site-labels.d.ts +66 -0
- package/dist/table/site-labels.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/combinators/dispatch.ts +12 -3
- package/src/compiler/token-alphabet.ts +57 -17
- package/src/cst/capture-buffer.ts +25 -0
- package/src/plugin/index.ts +230 -35
- package/src/table/assemble.ts +252 -62
- package/src/table/child-slots.ts +2 -2
- package/src/table/compile-rule-map.ts +14 -5
- package/src/table/emit-assembly.ts +112 -23
- package/src/table/emit.ts +3 -1
- package/src/table/encode.ts +80 -14
- package/src/table/exec.ts +27 -8
- package/src/table/factor-choice.ts +173 -0
- package/src/table/fail-clean.ts +130 -0
- package/src/table/hold-gap.ts +68 -0
- package/src/table/lex-program.ts +197 -43
- package/src/table/ops.ts +9 -1
- package/src/table/run-support.ts +35 -0
- package/src/table/scalar-terminal.ts +8 -0
- package/src/table/site-labels.ts +135 -4
- package/src/version.ts +1 -1
package/src/plugin/index.ts
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
resolveDegradationLevel, recordDegradation, degradationCaptureDepth, unwindDegradationCapture,
|
|
37
37
|
} from '../compiler/degradation.ts'
|
|
38
38
|
import type { HostMode } from '../cst/host-mode.ts'
|
|
39
|
-
import { COMPOSED_PIECES } from '../compiler/linker.ts'
|
|
39
|
+
import { COMPOSED_PIECES, type IRPiece } from '../compiler/linker.ts'
|
|
40
40
|
import { serializeCombinator, serializeRuleMap } from '../compiler/ir-serialize.ts'
|
|
41
41
|
import { evalCombinatorIR, evalRulesIR, INERT_BUILDER } from './ir-eval.ts'
|
|
42
42
|
import { countSiteVisits } from '../table/assemble.ts'
|
|
@@ -65,6 +65,48 @@ import type {
|
|
|
65
65
|
*/
|
|
66
66
|
const TABLE_RUNTIME_SPECIFIER = 'parseman/table'
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* BUILD-ONLY CARRIED PIECES.
|
|
70
|
+
*
|
|
71
|
+
* Nothing at runtime reads a grammar's carried IR: `compose()` links live `rules()`
|
|
72
|
+
* maps, and every runtime reader treats an IR piece as opaque
|
|
73
|
+
* (docs/design/runtime-and-size-contract.md, rules 1 and 2). Only a downstream macro
|
|
74
|
+
* build reads it. Under a rollup-family bundler the macro therefore leaves the IR out
|
|
75
|
+
* of the artifact and writes each chunk's carried lists beside it, as
|
|
76
|
+
* `<chunk>.parseman.json`; `resolveModulePieces` reads that file before it looks for
|
|
77
|
+
* an inline literal. Inline, the IR is about a quarter of a composed grammar's
|
|
78
|
+
* artifact, and the runtime spread that forwards an ancestor's pieces keeps that
|
|
79
|
+
* ancestor's module in the import graph of every parse.
|
|
80
|
+
*
|
|
81
|
+
* An item is an IR piece, or a reference to an export of a package module, resolved
|
|
82
|
+
* from the artifact's own location.
|
|
83
|
+
*/
|
|
84
|
+
export type CarriedSidecarItem = IRPiece | { from: string; name: string }
|
|
85
|
+
/** One module's carried lists, by export name. */
|
|
86
|
+
export type CarriedSink = Map<string, CarriedSidecarItem[]>
|
|
87
|
+
export const CARRIED_SIDECAR_SUFFIX = '.parseman.json'
|
|
88
|
+
type CarriedSidecar = { parseman: string; exports: Record<string, CarriedSidecarItem[]> }
|
|
89
|
+
|
|
90
|
+
const _sidecarCache = new Map<string, { mtimeMs: number; sidecar: CarriedSidecar | null }>()
|
|
91
|
+
/** The carried-pieces sidecar written beside a compiled artifact, or null. */
|
|
92
|
+
function readCarriedSidecar(artifact: string): CarriedSidecar | null {
|
|
93
|
+
const file = artifact + CARRIED_SIDECAR_SUFFIX
|
|
94
|
+
let mtimeMs: number
|
|
95
|
+
try { mtimeMs = fs.statSync(file).mtimeMs } catch { return null }
|
|
96
|
+
const hit = _sidecarCache.get(file)
|
|
97
|
+
if (hit && hit.mtimeMs === mtimeMs) return hit.sidecar
|
|
98
|
+
let sidecar: CarriedSidecar | null = null
|
|
99
|
+
try {
|
|
100
|
+
// A file without an `exports` object is not a sidecar this plugin wrote: treat
|
|
101
|
+
// it as absent, so the inline literal is looked for and a missing one reports.
|
|
102
|
+
const parsed: unknown = JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
103
|
+
const exports = typeof parsed === 'object' && parsed !== null ? (parsed as { exports?: unknown }).exports : undefined
|
|
104
|
+
if (typeof exports === 'object' && exports !== null) sidecar = parsed as CarriedSidecar
|
|
105
|
+
} catch { /* unreadable: no sidecar */ }
|
|
106
|
+
_sidecarCache.set(file, { mtimeMs, sidecar })
|
|
107
|
+
return sidecar
|
|
108
|
+
}
|
|
109
|
+
|
|
68
110
|
export type ParsecraftPluginOptions = {
|
|
69
111
|
/** Extra module specifiers to treat as parseman re-exports */
|
|
70
112
|
moduleAliases?: string[]
|
|
@@ -320,41 +362,142 @@ function parseModuleCached(filePath: string): ParsedModule | null {
|
|
|
320
362
|
return parsed
|
|
321
363
|
}
|
|
322
364
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
for (const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
365
|
+
/** The rollup-family hook that writes each chunk's carried pieces beside it. */
|
|
366
|
+
type SidecarBundle = Record<string, { type: string; fileName: string; exports?: string[]; moduleIds?: string[]; facadeModuleId?: string | null }>
|
|
367
|
+
type SidecarEmitter = {
|
|
368
|
+
emitFile: (file: { type: 'asset'; fileName: string; source: string }) => unknown
|
|
369
|
+
getModuleInfo: (id: string) => { code: string | null } | null
|
|
370
|
+
resolve: (source: string, importer?: string) => Promise<{ id: string; external?: boolean | string } | null>
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Where a module's export `exported` comes from when the module re-exports it,
|
|
375
|
+
* possibly renamed: `export { base as renamed } from './grammar'`, or an imported
|
|
376
|
+
* binding exported under another name. Null when the module declares it itself.
|
|
377
|
+
*/
|
|
378
|
+
function reexportSource(body: AnyNode[], exported: string): { source: string; imported: string } | null {
|
|
379
|
+
for (const st of body) {
|
|
380
|
+
if (st.type !== 'ExportNamedDeclaration') continue
|
|
381
|
+
for (const sp of (st.specifiers as AnyNode[] | undefined) ?? []) {
|
|
382
|
+
const exp = sp.exported as { name?: string; value?: string } | undefined
|
|
383
|
+
if ((exp?.name ?? exp?.value) !== exported) continue
|
|
384
|
+
const local = sp.local as { name?: string; value?: string } | undefined
|
|
385
|
+
const name = local?.name ?? local?.value
|
|
386
|
+
if (name === undefined) return null
|
|
387
|
+
const from = (st.source as { value?: string } | null | undefined)?.value
|
|
388
|
+
return from !== undefined ? { source: from, imported: name } : extractImportBindings(body).get(name) ?? null
|
|
347
389
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
390
|
+
}
|
|
391
|
+
return null
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* The carried list behind an entry chunk's export, followed from the entry module
|
|
396
|
+
* through its re-exports to the module that recorded it, under the name that module
|
|
397
|
+
* exports it as. A chunk's export names are its entry module's, so an entry that
|
|
398
|
+
* renames a grammar (`export { base as renamed } from './grammar'`) still finds the
|
|
399
|
+
* pieces recorded as `base`.
|
|
400
|
+
*/
|
|
401
|
+
async function followCarried(this: SidecarEmitter, sinks: Map<string, CarriedSink>, moduleId: string, name: string): Promise<CarriedSidecarItem[] | undefined> {
|
|
402
|
+
for (let hop = 0; hop < 64; hop++) {
|
|
403
|
+
const own = sinks.get(moduleId)?.get(name)
|
|
404
|
+
if (own !== undefined) return own
|
|
405
|
+
const code = this.getModuleInfo(moduleId)?.code
|
|
406
|
+
if (!code) return undefined
|
|
407
|
+
const parsed = parseSync(moduleId, code)
|
|
408
|
+
if (parsed.errors.length > 0) return undefined
|
|
409
|
+
const next = reexportSource(parsed.program.body as unknown as AnyNode[], name)
|
|
410
|
+
if (next === null) return undefined
|
|
411
|
+
const resolved = await this.resolve(next.source, moduleId)
|
|
412
|
+
if (resolved === null || resolved.external) return undefined
|
|
413
|
+
moduleId = resolved.id
|
|
414
|
+
name = next.imported
|
|
415
|
+
}
|
|
416
|
+
return undefined
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Write `<chunk>.parseman.json` for every chunk that exports a grammar with carried
|
|
421
|
+
* pieces. An entry chunk's export is followed from its entry module to the module
|
|
422
|
+
* that recorded it (`followCarried`). Any other export is matched by name to the
|
|
423
|
+
* module that recorded it, preferring a module of the chunk itself; a name two
|
|
424
|
+
* modules of the bundle both record, and neither of them in the chunk, is ambiguous
|
|
425
|
+
* and is not written — a downstream compose of it then fails its build naming the
|
|
426
|
+
* artifact.
|
|
427
|
+
*/
|
|
428
|
+
async function emitCarriedSidecars(this: SidecarEmitter, sinks: Map<string, CarriedSink>, bundle: SidecarBundle): Promise<void> {
|
|
429
|
+
const recorders = new Map<string, string[]>()
|
|
430
|
+
for (const [moduleId, sink] of sinks) for (const name of sink.keys()) (recorders.get(name) ?? recorders.set(name, []).get(name)!).push(moduleId)
|
|
431
|
+
for (const chunk of Object.values(bundle)) {
|
|
432
|
+
if (chunk.type !== 'chunk') continue
|
|
433
|
+
const exports: Record<string, CarriedSidecarItem[]> = {}
|
|
434
|
+
for (const name of chunk.exports ?? []) {
|
|
435
|
+
const followed = chunk.facadeModuleId ? await followCarried.call(this, sinks, chunk.facadeModuleId, name) : undefined
|
|
436
|
+
if (followed !== undefined) { exports[name] = followed; continue }
|
|
437
|
+
const ids = recorders.get(name)
|
|
438
|
+
if (ids === undefined) continue
|
|
439
|
+
const own = ids.filter(m => chunk.moduleIds?.includes(m))
|
|
440
|
+
const moduleId = own.length === 1 ? own[0] : own.length === 0 && ids.length === 1 ? ids[0] : undefined
|
|
441
|
+
if (moduleId !== undefined) exports[name] = sinks.get(moduleId)!.get(name)!
|
|
442
|
+
}
|
|
443
|
+
if (Object.keys(exports).length === 0) continue
|
|
444
|
+
const sidecar: CarriedSidecar = { parseman: PARSEMAN_VERSION, exports }
|
|
445
|
+
this.emitFile({ type: 'asset', fileName: chunk.fileName + CARRIED_SIDECAR_SUFFIX, source: JSON.stringify(sidecar) })
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const SIDECAR_FRAMEWORKS = new Set(['rollup', 'rolldown', 'vite'])
|
|
450
|
+
|
|
451
|
+
export default createUnplugin((opts: ParsecraftPluginOptions = {}, meta) => {
|
|
452
|
+
// Module id → its carried lists, under a bundler that writes them beside the chunk.
|
|
453
|
+
// Any other bundler gets them inline, as before.
|
|
454
|
+
const sinks = SIDECAR_FRAMEWORKS.has(meta.framework) ? new Map<string, CarriedSink>() : undefined
|
|
455
|
+
const generateBundle = sinks === undefined ? undefined : {
|
|
456
|
+
generateBundle(this: SidecarEmitter, _options: unknown, bundle: SidecarBundle) { return emitCarriedSidecars.call(this, sinks, bundle) },
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
name: 'parseman',
|
|
460
|
+
...(generateBundle === undefined ? {} : { rollup: generateBundle, rolldown: generateBundle, vite: generateBundle }),
|
|
461
|
+
// Run BEFORE the bundler's TS/JS transform — otherwise esbuild (Vite) strips
|
|
462
|
+
// the `with { type: 'macro' }` import attribute before we ever see it, and the
|
|
463
|
+
// macro silently never fires.
|
|
464
|
+
enforce: 'pre' as const,
|
|
465
|
+
|
|
466
|
+
transformInclude(id: string) {
|
|
467
|
+
return /\.[jt]sx?$/.test(id) && !id.includes('node_modules')
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
transform(this: { warn?: (msg: string) => void; addWatchFile?: (file: string) => void }, code: string, id: string) {
|
|
471
|
+
if (!code.includes('parseman')) return null
|
|
472
|
+
if (!code.includes('macro')) return null
|
|
473
|
+
// A corpus edit changes which sites a profiled build generates, so a watch
|
|
474
|
+
// build re-lowers when one does.
|
|
475
|
+
for (const file of opts.assemblyProfile?.files ?? []) this?.addWatchFile?.(file)
|
|
476
|
+
const moduleAliases = new Set([PARSEMAN_MODULE, ...(opts.moduleAliases ?? [])])
|
|
477
|
+
const sink: CarriedSink | undefined = sinks && new Map()
|
|
478
|
+
const result = transformMacro(code, id, moduleAliases, opts.warnUnloweredRegex === true, opts.recovery === true, opts.grammarCoverage === true, opts.assemblyProfile, opts.assemblyBudget, sink)
|
|
479
|
+
if (sinks && sink) {
|
|
480
|
+
if (sink.size > 0) sinks.set(id, sink)
|
|
481
|
+
else sinks.delete(id)
|
|
482
|
+
}
|
|
483
|
+
if (result?.warnings.length) {
|
|
484
|
+
for (const w of result.warnings) {
|
|
485
|
+
if (typeof this?.warn === 'function') this.warn(`[parseman] ${w}`)
|
|
486
|
+
else console.warn(`[parseman] ${w}`)
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (!result) return null
|
|
490
|
+
return { code: result.code, map: result.map }
|
|
491
|
+
},
|
|
492
|
+
}
|
|
493
|
+
})
|
|
352
494
|
|
|
353
495
|
// ---------------------------------------------------------------------------
|
|
354
496
|
// Reading carried pieces back out of a COMPILED grammar module
|
|
355
497
|
// ---------------------------------------------------------------------------
|
|
356
|
-
//
|
|
357
|
-
//
|
|
498
|
+
// Under a rollup-family bundler the pieces are in the sidecar beside the module
|
|
499
|
+
// (`readCarriedSidecar`). Otherwise the macro embeds them inside its exported const's
|
|
500
|
+
// initializer (see `withCarriedPieces`), and to compose an imported grammar we parse
|
|
358
501
|
// its compiled module, find that const, and pull the pieces literal back out —
|
|
359
502
|
// these three pure helpers do exactly that (no runtime, no source recompile).
|
|
360
503
|
|
|
@@ -600,10 +743,11 @@ export function transformMacro(
|
|
|
600
743
|
grammarCoverage = false,
|
|
601
744
|
assemblyProfile?: AssemblyProfile,
|
|
602
745
|
assemblyBudget = DEFAULT_ASSEMBLY_BUDGET,
|
|
746
|
+
carriedSink?: CarriedSink,
|
|
603
747
|
): TransformMacroResult | null {
|
|
604
748
|
const depth = degradationCaptureDepth()
|
|
605
749
|
try {
|
|
606
|
-
return transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage, assemblyProfile, assemblyBudget)
|
|
750
|
+
return transformMacroImpl(code, id, moduleAliases, warnUnloweredRegex, recovery, grammarCoverage, assemblyProfile, assemblyBudget, carriedSink)
|
|
607
751
|
} finally {
|
|
608
752
|
setReducerResolver(null)
|
|
609
753
|
setBuilderImportResolver(null)
|
|
@@ -622,6 +766,7 @@ function transformMacroImpl(
|
|
|
622
766
|
grammarCoverage: boolean,
|
|
623
767
|
assemblyProfile: AssemblyProfile | undefined,
|
|
624
768
|
assemblyBudgetShare: number,
|
|
769
|
+
carriedSink: CarriedSink | undefined,
|
|
625
770
|
): TransformMacroResult | null {
|
|
626
771
|
let result: ReturnType<typeof parseSync>
|
|
627
772
|
try {
|
|
@@ -1653,6 +1798,23 @@ function transformMacroImpl(
|
|
|
1653
1798
|
local = false,
|
|
1654
1799
|
): RawItem[] | null => {
|
|
1655
1800
|
const { file } = module
|
|
1801
|
+
const fail = (why: string): null => { unresolved.why ??= `${exportName} in ${file}: ${why}`; return null }
|
|
1802
|
+
// A build-only sidecar wins over an inline literal: an artifact that has one
|
|
1803
|
+
// carries no literal (see `CarriedSidecarItem`).
|
|
1804
|
+
const sidecarItems = module.source || local ? undefined : readCarriedSidecar(file)?.exports[exportName]
|
|
1805
|
+
if (sidecarItems !== undefined) {
|
|
1806
|
+
const list: RawItem[] = []
|
|
1807
|
+
for (const item of sidecarItems) {
|
|
1808
|
+
if ('ir' in item) { list.push(item); continue }
|
|
1809
|
+
const sub = resolveGrammarModule(file, item.from)
|
|
1810
|
+
if (sub === null) return fail(`its carried pieces name \`${item.name}\` from '${item.from}', a module that could not be found`)
|
|
1811
|
+
if (seen.has(sub.file)) continue
|
|
1812
|
+
const pieces = resolveModulePieces(sub, item.name, new Set(seen).add(sub.file), unresolved)
|
|
1813
|
+
if (pieces === null) return fail(`its carried pieces name \`${item.name}\` from '${item.from}', which could not be resolved`)
|
|
1814
|
+
list.push(...pieces)
|
|
1815
|
+
}
|
|
1816
|
+
return list
|
|
1817
|
+
}
|
|
1656
1818
|
const mod = module.source
|
|
1657
1819
|
? lowerPrivateSourceModule(file, moduleAliases, warnUnloweredRegex, recovery)
|
|
1658
1820
|
: parseModuleCached(file)
|
|
@@ -1660,9 +1822,15 @@ function transformMacroImpl(
|
|
|
1660
1822
|
const localFor = local ? exportName : exportLocalName(mod.body as AnyNode[], exportName)
|
|
1661
1823
|
const initNode = localFor ? topLevelInit(mod.body as AnyNode[], localFor) : null
|
|
1662
1824
|
const literalRange = initNode ? findCarriedPiecesLiteral(initNode) : null
|
|
1663
|
-
if (!literalRange)
|
|
1825
|
+
if (!literalRange) {
|
|
1826
|
+
// A compiled grammar with neither a literal nor a sidecar entry was built to be
|
|
1827
|
+
// composed or it was not, and a runtime compose() cannot link it either way.
|
|
1828
|
+
return initNode !== null && !module.source && mod.src.slice(initNode.start, initNode.end).includes('tableRules(')
|
|
1829
|
+
? fail(`is a compiled grammar with no carried pieces — no inline literal, and no entry in ${path.basename(file)}${CARRIED_SIDECAR_SUFFIX}. `
|
|
1830
|
+
+ 'Rebuild it with this parseman, and keep that file beside the artifact')
|
|
1831
|
+
: null
|
|
1832
|
+
}
|
|
1664
1833
|
const literal = mod.src.slice(literalRange.start, literalRange.end)
|
|
1665
|
-
const fail = (why: string): null => { unresolved.why ??= `${exportName} in ${file}: ${why}`; return null }
|
|
1666
1834
|
|
|
1667
1835
|
const imports = extractImportBindings(mod.body as AnyNode[])
|
|
1668
1836
|
const stubNames: string[] = []
|
|
@@ -1733,6 +1901,32 @@ function transformMacroImpl(
|
|
|
1733
1901
|
return result
|
|
1734
1902
|
}
|
|
1735
1903
|
|
|
1904
|
+
/**
|
|
1905
|
+
* Record an exported grammar's carried list in the build-only sink rather than
|
|
1906
|
+
* emitting it into the runtime artifact. `false` when there is no sink, or when an
|
|
1907
|
+
* entry cannot be written as data; the caller then emits it inline, as before.
|
|
1908
|
+
*
|
|
1909
|
+
* A package import stays a reference, resolved from the artifact's location just as
|
|
1910
|
+
* the import itself is, so a descendant never re-serializes its ancestor. A relative
|
|
1911
|
+
* import names a module the bundler folds into this chunk, which has no artifact of
|
|
1912
|
+
* its own to point at, so its pieces are written out here.
|
|
1913
|
+
*/
|
|
1914
|
+
const sinkCarried = (name: string, list: CarriedItem[]): boolean => {
|
|
1915
|
+
if (carriedSink === undefined) return false
|
|
1916
|
+
const out: CarriedSidecarItem[] = []
|
|
1917
|
+
for (const it of list) {
|
|
1918
|
+
if (!isSpread(it)) { out.push(it); continue }
|
|
1919
|
+
const b = importBindings.get(it.__spreadLocal)
|
|
1920
|
+
if (b === undefined) return false
|
|
1921
|
+
if (!b.source.startsWith('.') && !path.isAbsolute(b.source)) { out.push({ from: b.source, name: b.imported }); continue }
|
|
1922
|
+
const pieces = importedPieces(it.__spreadLocal)
|
|
1923
|
+
if (pieces === null) return false
|
|
1924
|
+
out.push(...pieces)
|
|
1925
|
+
}
|
|
1926
|
+
carriedSink.set(name, out)
|
|
1927
|
+
return true
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1736
1930
|
/**
|
|
1737
1931
|
* Evaluate a carried list back to combinators, one entry per piece: its rule map
|
|
1738
1932
|
* and the `rules()` recipe it was built from. `compose()` links the recipes into
|
|
@@ -2493,6 +2687,7 @@ function transformMacroImpl(
|
|
|
2493
2687
|
else warn(init.start, `${varName}: exported grammar could not be serialized to IR, so it carries no `
|
|
2494
2688
|
+ 'composable pieces — a downstream compose() of this grammar will fall back to runtime. '
|
|
2495
2689
|
+ 'Re-run with PARSEMAN_IR_DEBUG=1 to print the combinator that blocked serialization.')
|
|
2690
|
+
if (carriedMetadata !== undefined && sinkCarried(varName, carriedMetadata)) carriedMetadata = undefined
|
|
2496
2691
|
}
|
|
2497
2692
|
const replacement = compiledRules.replacement !== null
|
|
2498
2693
|
? compiledRules.replacementWithMetadata!(staticTableMetadataSource({
|
|
@@ -2528,7 +2723,7 @@ function transformMacroImpl(
|
|
|
2528
2723
|
// grammar via `import { <name> }` (re-composition, no source) and re-lower it
|
|
2529
2724
|
// under ITS composing trivia.
|
|
2530
2725
|
localComposedCarried.set(varName, fused.carried)
|
|
2531
|
-
const replacement = exportPrefix ? fused.exportedReplacement : fused.replacement
|
|
2726
|
+
const replacement = exportPrefix && !sinkCarried(varName, fused.carried) ? fused.exportedReplacement : fused.replacement
|
|
2532
2727
|
// Coverage definitions already ride in the table program's `cv` pool;
|
|
2533
2728
|
// `tableRules` exposes that same pool through the metadata prototype.
|
|
2534
2729
|
replacements.push({ start: init.start, end: init.end, replacement })
|