@shapeshift-labs/frontier-lang-parser 0.3.76 → 0.3.78
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
CHANGED
|
@@ -97,6 +97,12 @@ The published Frontier package family is generated from one shared package catal
|
|
|
97
97
|
- [`@shapeshift-labs/frontier-documentation`](https://www.npmjs.com/package/@shapeshift-labs/frontier-documentation): Frontier-native documentation manifests, generated documentation books, package/API/source discovery, Vite virtual modules, standalone browser docs shells, inspector bridges, search indexes, and documentation harness evidence for Frontier apps and packages.
|
|
98
98
|
- [`@shapeshift-labs/frontier-ast-walk`](https://www.npmjs.com/package/@shapeshift-labs/frontier-ast-walk): Dependency-light source graph, import/export/declaration/call analysis, Frontier package-use discovery, and business-logic placement findings for Frontier tools, apps, docs, fuzzers, benchmarks, and agent evidence.
|
|
99
99
|
- [`@shapeshift-labs/frontier-history`](https://www.npmjs.com/package/@shapeshift-labs/frontier-history): Serializable temporal explanation and causality records for Frontier apps, including field-change explanations, action/workflow/policy/effect/trace/test provenance, audit windows, undo planning, registry/provenance graph output, JSONL replay bundles, and proof hashes.
|
|
100
|
+
- [`@shapeshift-labs/frontier-explain`](https://www.npmjs.com/package/@shapeshift-labs/frontier-explain): Renderer-neutral interactive explanation contracts for Frontier apps, including explanation manifests, narrative graphs, figure specs, model/state records, controls, semantic trace and replay records, structure projections, invariants, accessibility metadata, provenance, and evidence/probe contracts.
|
|
101
|
+
- [`@shapeshift-labs/frontier-explain-graph`](https://www.npmjs.com/package/@shapeshift-labs/frontier-explain-graph): Graph, grid, tree, table, matrix, and algorithm/data-structure explanation builders for Frontier interactive explanations, including stable projections, worklist/frontier panels, pseudocode links, operation vocabularies, trace fixtures, counters, and invariant witnesses.
|
|
102
|
+
- [`@shapeshift-labs/frontier-explain-sim`](https://www.npmjs.com/package/@shapeshift-labs/frontier-explain-sim): Simulation explanation contracts for Frontier interactive explanations, including deterministic clocks, phases, sampled fields, constraint records, particle/advection frames, curve samples, plot materialization records, and replayable simulation evidence without owning domain-specific solvers.
|
|
103
|
+
- [`@shapeshift-labs/frontier-explain-render`](https://www.npmjs.com/package/@shapeshift-labs/frontier-explain-render): Renderer-neutral materialization for Frontier explanations, including frame records, mark/layer/label layout records, hit regions, linked representation frames, static fallback frames, coordinate spaces, cameras, and renderer adapter records.
|
|
104
|
+
- [`@shapeshift-labs/frontier-explain-test`](https://www.npmjs.com/package/@shapeshift-labs/frontier-explain-test): Reusable test and evidence contracts for Frontier explanations, including deterministic replay checks, figure probes, control replay plans, accessibility expectations, static fallback checks, trace assertions, visual evidence records, and evidence summaries.
|
|
105
|
+
- [`@shapeshift-labs/frontier-explain-author`](https://www.npmjs.com/package/@shapeshift-labs/frontier-explain-author): Serializable authoring contracts for Frontier explanations, including explanation documents, narrative graphs, control presets, citation/provenance records, authoring diagnostics, stale-link checks, gallery records, and publication planning records.
|
|
100
106
|
- [`@shapeshift-labs/frontier-application`](https://www.npmjs.com/package/@shapeshift-labs/frontier-application): Serializable whole-application graph and impact queries for Frontier apps, including features, owners, packages, routes, views, actions, mutations, state paths, effects, workers, assets, tests, traces, policies, workflows, migrations, benchmarks, registry graph output, feature maps, JSONL bundles, and proof hashes.
|
|
101
107
|
- [`@shapeshift-labs/frontier-linter`](https://www.npmjs.com/package/@shapeshift-labs/frontier-linter): Serializable Frontier lint rules, diagnostics, fixes, reports, and fast rule execution for package catalogs, registry graphs, application maps, manifests, traces, policies, workflows, workers, assets, tests, benchmarks, and source snippets.
|
|
102
108
|
- [`@shapeshift-labs/frontier-framework`](https://www.npmjs.com/package/@shapeshift-labs/frontier-framework): High-level app framework package for Frontier applications, including configuration, CLI scaffolding, Vite builds, monorepo layout, TSX route builds, split frontend/backend deploy artifacts, backend-neutral Fetch handler and sync transport contracts, runtime data-source migrations, devtools, harness gates, agent MCP/tool manifests, CI evidence gates, workflow manifests, SARIF/linter output, replay scripts, and evidence manifest output.
|
|
@@ -204,6 +210,12 @@ Package source repositories:
|
|
|
204
210
|
- [`siliconjungle/-shapeshift-labs-frontier-documentation`](https://github.com/siliconjungle/-shapeshift-labs-frontier-documentation)
|
|
205
211
|
- [`siliconjungle/-shapeshift-labs-frontier-ast-walk`](https://github.com/siliconjungle/-shapeshift-labs-frontier-ast-walk)
|
|
206
212
|
- [`siliconjungle/-shapeshift-labs-frontier-history`](https://github.com/siliconjungle/-shapeshift-labs-frontier-history)
|
|
213
|
+
- [`siliconjungle/-shapeshift-labs-frontier-explain`](https://github.com/siliconjungle/-shapeshift-labs-frontier-explain)
|
|
214
|
+
- [`siliconjungle/-shapeshift-labs-frontier-explain-graph`](https://github.com/siliconjungle/-shapeshift-labs-frontier-explain-graph)
|
|
215
|
+
- [`siliconjungle/-shapeshift-labs-frontier-explain-sim`](https://github.com/siliconjungle/-shapeshift-labs-frontier-explain-sim)
|
|
216
|
+
- [`siliconjungle/-shapeshift-labs-frontier-explain-render`](https://github.com/siliconjungle/-shapeshift-labs-frontier-explain-render)
|
|
217
|
+
- [`siliconjungle/-shapeshift-labs-frontier-explain-test`](https://github.com/siliconjungle/-shapeshift-labs-frontier-explain-test)
|
|
218
|
+
- [`siliconjungle/-shapeshift-labs-frontier-explain-author`](https://github.com/siliconjungle/-shapeshift-labs-frontier-explain-author)
|
|
207
219
|
- [`siliconjungle/-shapeshift-labs-frontier-application`](https://github.com/siliconjungle/-shapeshift-labs-frontier-application)
|
|
208
220
|
- [`siliconjungle/-shapeshift-labs-frontier-linter`](https://github.com/siliconjungle/-shapeshift-labs-frontier-linter)
|
|
209
221
|
- [`siliconjungle/-shapeshift-labs-frontier-framework`](https://github.com/siliconjungle/-shapeshift-labs-frontier-framework)
|
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,14 @@ export interface FrontierSourceChildSyntaxRecord {
|
|
|
29
29
|
readonly id?: string;
|
|
30
30
|
readonly family?: string;
|
|
31
31
|
readonly role?: string;
|
|
32
|
+
readonly parentRenderId?: string;
|
|
33
|
+
readonly typeSource?: string;
|
|
34
|
+
readonly action?: string;
|
|
35
|
+
readonly value?: string;
|
|
36
|
+
readonly values?: readonly string[];
|
|
37
|
+
readonly expression?: string;
|
|
38
|
+
readonly viewRenderKind?: string;
|
|
39
|
+
readonly optional?: boolean;
|
|
32
40
|
readonly reason?: string;
|
|
33
41
|
readonly header: string;
|
|
34
42
|
readonly startOffset: number;
|
|
@@ -89,6 +97,12 @@ export interface FrontierSourceSyntaxReport {
|
|
|
89
97
|
readonly recognizedChildKinds: readonly string[];
|
|
90
98
|
readonly unknownKinds: readonly string[];
|
|
91
99
|
readonly unknownChildKinds: readonly string[];
|
|
100
|
+
readonly sourceSyntaxBlockFamilies: readonly string[];
|
|
101
|
+
readonly sourceSyntaxBlockFamilyCounts: Readonly<Record<string, number>>;
|
|
102
|
+
readonly sourceSyntaxRowFamilies: readonly string[];
|
|
103
|
+
readonly sourceSyntaxRowFamilyCounts: Readonly<Record<string, number>>;
|
|
104
|
+
readonly sourceSyntaxRowFamiliesByBlockFamily: Readonly<Record<string, readonly string[]>>;
|
|
105
|
+
readonly sourceSyntaxRowFamilyCountsByBlockFamily: Readonly<Record<string, Readonly<Record<string, number>>>>;
|
|
92
106
|
readonly failClosed: boolean;
|
|
93
107
|
readonly unsupportedSyntax: boolean;
|
|
94
108
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { readActionSyntaxChildren } from './action-syntax-children.js';
|
|
2
2
|
import { ROW_SYNTAX_CONFIG } from './source-syntax-row-config.js';
|
|
3
3
|
import { readTypeExpressionSyntaxChildren } from './source-syntax-type-expressions.js';
|
|
4
|
+
import { readViewSyntaxChildren } from './source-syntax-view-children.js';
|
|
4
5
|
import { inspectTypeExpressionSyntax } from './type-expressions.js';
|
|
5
6
|
import { inspectVariantPayload } from './type-variants.js';
|
|
6
|
-
|
|
7
7
|
const ROW_NAME_PATTERN = '([A-Za-z_$@./:*+-][\\w$./@:*+-]*)';
|
|
8
8
|
|
|
9
9
|
export function readSourceSyntaxChildren(source, block, options = {}) {
|
|
@@ -31,6 +31,8 @@ export function readSourceSyntaxChildren(source, block, options = {}) {
|
|
|
31
31
|
duplicateNameReason: 'duplicate-state-collection-name',
|
|
32
32
|
duplicateIdReason: 'duplicate-state-collection-id'
|
|
33
33
|
});
|
|
34
|
+
} else if (block.kind === 'view') {
|
|
35
|
+
children = readViewSyntaxChildren(source, block, options);
|
|
34
36
|
} else {
|
|
35
37
|
const rowConfig = ROW_SYNTAX_CONFIG[block.kind];
|
|
36
38
|
if (rowConfig) children = readGenericRowSyntaxChildren(source, block, options, rowConfig);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export function summarizeSourceSyntaxFamilies(blocks) {
|
|
2
|
+
const childEntries = blocks.flatMap((block) => (block.children ?? []).map((child) => ({
|
|
3
|
+
blockFamily: sourceSyntaxBlockFamily(block),
|
|
4
|
+
rowFamily: sourceSyntaxRowFamily(child)
|
|
5
|
+
})));
|
|
6
|
+
const blockFamilies = unique(blocks.map(sourceSyntaxBlockFamily));
|
|
7
|
+
return {
|
|
8
|
+
sourceSyntaxBlockFamilies: blockFamilies,
|
|
9
|
+
sourceSyntaxBlockFamilyCounts: countBy(blocks, sourceSyntaxBlockFamily),
|
|
10
|
+
sourceSyntaxRowFamilies: unique(childEntries.map((entry) => entry.rowFamily)),
|
|
11
|
+
sourceSyntaxRowFamilyCounts: countBy(childEntries, (entry) => entry.rowFamily),
|
|
12
|
+
sourceSyntaxRowFamiliesByBlockFamily: groupUniqueByKnownKeys(blockFamilies, childEntries, (entry) => entry.blockFamily, (entry) => entry.rowFamily),
|
|
13
|
+
sourceSyntaxRowFamilyCountsByBlockFamily: groupCountsByKnownKeys(blockFamilies, childEntries, (entry) => entry.blockFamily, (entry) => entry.rowFamily)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function sourceSyntaxBlockFamily(block) {
|
|
18
|
+
return block.kind ?? 'unknown';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function sourceSyntaxRowFamily(child) {
|
|
22
|
+
return child.recognized
|
|
23
|
+
? child.family ?? child.normalizedRowKind ?? child.rowKind ?? child.kind ?? 'unknown'
|
|
24
|
+
: child.rowKind ?? child.normalizedRowKind ?? child.family ?? child.kind ?? 'unknown';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function countBy(values, keyFor) {
|
|
28
|
+
const counts = {};
|
|
29
|
+
for (const value of values) {
|
|
30
|
+
const key = keyFor(value);
|
|
31
|
+
if (!key) continue;
|
|
32
|
+
counts[key] = (counts[key] ?? 0) + 1;
|
|
33
|
+
}
|
|
34
|
+
return counts;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function groupUniqueByKnownKeys(keys, values, keyFor, valueFor) {
|
|
38
|
+
const groups = Object.fromEntries(keys.map((key) => [key, []]));
|
|
39
|
+
for (const value of values) {
|
|
40
|
+
const key = keyFor(value);
|
|
41
|
+
const item = valueFor(value);
|
|
42
|
+
if (!key || !item) continue;
|
|
43
|
+
groups[key] ??= [];
|
|
44
|
+
if (!groups[key].includes(item)) groups[key].push(item);
|
|
45
|
+
}
|
|
46
|
+
return groups;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function groupCountsByKnownKeys(keys, values, keyFor, valueFor) {
|
|
50
|
+
const groups = Object.fromEntries(keys.map((key) => [key, {}]));
|
|
51
|
+
for (const value of values) {
|
|
52
|
+
const key = keyFor(value);
|
|
53
|
+
const item = valueFor(value);
|
|
54
|
+
if (!key || !item) continue;
|
|
55
|
+
groups[key] ??= {};
|
|
56
|
+
groups[key][item] = (groups[key][item] ?? 0) + 1;
|
|
57
|
+
}
|
|
58
|
+
return groups;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function unique(values) {
|
|
62
|
+
return [...new Set(values.filter(Boolean))];
|
|
63
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readSourceSyntaxChildren } from './source-syntax-children.js';
|
|
2
|
+
import { summarizeSourceSyntaxFamilies } from './source-syntax-family-summary.js';
|
|
2
3
|
import { FrontierSourceBlockKinds } from './source-block-kinds.js';
|
|
3
4
|
|
|
4
5
|
export { FrontierSourceBlockKinds };
|
|
@@ -43,6 +44,7 @@ export function inspectFrontierSourceSyntax(source, options = {}) {
|
|
|
43
44
|
];
|
|
44
45
|
const malformedBlocks = blocks.filter((block) => block.malformed);
|
|
45
46
|
const failClosed = unknownBlocks.length > 0 || unknownChildren.length > 0 || diagnostics.length > 0;
|
|
47
|
+
const familySummary = summarizeSourceSyntaxFamilies(blocks);
|
|
46
48
|
return {
|
|
47
49
|
kind: 'frontier.lang.sourceSyntaxReport',
|
|
48
50
|
version: 1,
|
|
@@ -65,6 +67,7 @@ export function inspectFrontierSourceSyntax(source, options = {}) {
|
|
|
65
67
|
recognizedChildKinds: unique(childRecords.filter((child) => child.recognized).map((child) => child.kind)),
|
|
66
68
|
unknownKinds: unique(unknownBlocks.map((block) => block.kind)),
|
|
67
69
|
unknownChildKinds: unique(unknownChildren.map((child) => child.rowKind ?? child.kind)),
|
|
70
|
+
...familySummary,
|
|
68
71
|
failClosed,
|
|
69
72
|
unsupportedSyntax: unknownBlocks.length > 0 || unknownChildren.length > 0
|
|
70
73
|
},
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
export function readViewSyntaxChildren(source, block, options = {}) {
|
|
2
|
+
const children = [];
|
|
3
|
+
const ranges = readRenderRanges(source.slice(block.bodyStartOffset, block.bodyEndOffset), block.bodyStartOffset);
|
|
4
|
+
for (const line of readBodyLines(source, block.bodyStartOffset, block.bodyEndOffset)) {
|
|
5
|
+
if (!line.text || line.text.startsWith('#') || inRange(line.startOffset, ranges)) continue;
|
|
6
|
+
const child = viewRowChild(source, block, options, line);
|
|
7
|
+
children.push(child ?? unknownViewChild(source, block, options, line, 'unsupported-view-row'));
|
|
8
|
+
}
|
|
9
|
+
for (const range of ranges) {
|
|
10
|
+
children.push(renderHeaderChild(source, block, options, range));
|
|
11
|
+
const nested = ranges.filter((candidate) => candidate.parentRenderId === range.id);
|
|
12
|
+
for (const line of readBodyLines(source, range.bodyStartOffset, range.bodyEndOffset)) {
|
|
13
|
+
if (!line.text || line.text.startsWith('#') || inRange(line.startOffset, nested)) continue;
|
|
14
|
+
const child = renderRowChild(source, block, options, line, range);
|
|
15
|
+
children.push(child ?? unknownViewChild(source, block, options, line, 'unsupported-view-render-row', range.id));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return children;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function viewRowChild(source, block, options, line) {
|
|
22
|
+
const reads = /^(reads?)\s+(.+)$/.exec(line.text);
|
|
23
|
+
if (reads) return childRecord(source, block, options, line, { kind: 'viewRead', rowKind: reads[1], name: reads[2].trim(), id: `view_read_${safeId(block.name)}_${line.startOffset}`, values: readList(reads[2]) });
|
|
24
|
+
const dispatch = /^(dispatch(?:es)?)\s+(.+)$/.exec(line.text);
|
|
25
|
+
if (dispatch) return childRecord(source, block, options, line, { kind: 'viewDispatch', rowKind: dispatch[1], name: dispatch[2].trim(), id: `view_dispatch_${safeId(block.name)}_${line.startOffset}`, values: readList(dispatch[2]) });
|
|
26
|
+
const prop = /^prop\s+([A-Za-z_$][\w$.-]*)(?:\s+@id\(\s*["']([^"']+)["']\s*\))?\s*:\s*(.+)$/.exec(line.text);
|
|
27
|
+
if (prop) return childRecord(source, block, options, line, { kind: 'viewProp', rowKind: 'prop', name: prop[1], id: prop[2] ?? `view_prop_${prop[1]}`, typeSource: prop[3].replace(/\s+optional\s*$/, '').trim(), optional: /\soptional\s*$/.test(prop[3]) || undefined });
|
|
28
|
+
const event = /^event\s+([A-Za-z_$][\w$.-]*)(?:\s+@id\(\s*["']([^"']+)["']\s*\))?(.*)$/.exec(line.text);
|
|
29
|
+
if (event) return childRecord(source, block, options, line, { kind: 'viewEvent', rowKind: 'event', name: event[1], id: event[2] ?? `view_event_${event[1]}`, action: readInlineWord('action', event[3]), typeSource: readInlineType('input', event[3]) });
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function renderHeaderChild(source, block, options, range) {
|
|
34
|
+
return childRecord(source, block, options, range, {
|
|
35
|
+
kind: 'viewRender',
|
|
36
|
+
rowKind: 'render',
|
|
37
|
+
name: range.name,
|
|
38
|
+
id: range.id,
|
|
39
|
+
viewRenderKind: readInlineWord('kind', range.header) ?? 'element',
|
|
40
|
+
parentRenderId: range.parentRenderId
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function renderRowChild(source, block, options, line, range) {
|
|
45
|
+
const identity = /^(identity|key)\s+(.+)$/.exec(line.text);
|
|
46
|
+
if (identity) return childRecord(source, block, options, line, { kind: 'viewRenderIdentity', rowKind: identity[1], name: identity[2].trim(), id: `${range.id}_${identity[1]}`, value: identity[2].trim(), parentRenderId: range.id });
|
|
47
|
+
const text = /^text\s+(.+)$/.exec(line.text);
|
|
48
|
+
if (text) return childRecord(source, block, options, line, { kind: 'viewRenderText', rowKind: 'text', name: 'text', id: `${range.id}_text`, ...readRenderValue(text[1]), parentRenderId: range.id });
|
|
49
|
+
const attr = /^(component|tag|tagName|kind)\s+(.+)$/.exec(line.text);
|
|
50
|
+
if (attr) return childRecord(source, block, options, line, { kind: 'viewRenderAttribute', rowKind: attr[1], name: attr[1], id: `${range.id}_${safeId(attr[1])}`, value: attr[2].trim(), parentRenderId: range.id });
|
|
51
|
+
const prop = /^prop\s+([A-Za-z_$][\w$.-]*)\s+(.+)$/.exec(line.text);
|
|
52
|
+
if (prop) return childRecord(source, block, options, line, { kind: 'viewRenderProp', rowKind: 'prop', name: prop[1], id: `${range.id}_prop_${safeId(prop[1])}`, ...readRenderValue(prop[2]), parentRenderId: range.id });
|
|
53
|
+
const event = /^on\s+([A-Za-z_$][\w$.-]*)\s+([A-Za-z_$][\w$.-]*)/.exec(line.text);
|
|
54
|
+
if (event) return childRecord(source, block, options, line, { kind: 'viewRenderEvent', rowKind: 'on', name: event[1], id: `${range.id}_on_${safeId(event[1])}`, action: event[2], parentRenderId: range.id });
|
|
55
|
+
const child = /^(children?|child)\s+(.+)$/.exec(line.text);
|
|
56
|
+
if (child) return childRecord(source, block, options, line, { kind: 'viewRenderChildren', rowKind: child[1], name: 'children', id: `${range.id}_children`, values: readList(child[2]), parentRenderId: range.id });
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function readRenderRanges(body, baseOffset, parentRenderId) {
|
|
61
|
+
const ranges = [];
|
|
62
|
+
for (const range of readTopLevelRenderRanges(body, baseOffset, parentRenderId)) {
|
|
63
|
+
ranges.push(range, ...readRenderRanges(body.slice(range.bodyStartOffset - baseOffset, range.bodyEndOffset - baseOffset), range.bodyStartOffset, range.id));
|
|
64
|
+
}
|
|
65
|
+
return ranges;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function readTopLevelRenderRanges(body, baseOffset, parentRenderId) {
|
|
69
|
+
const structure = scanStructure(body), ranges = [];
|
|
70
|
+
const header = /(^|\n)\s*render\s+([^{}\n]+)\{/g;
|
|
71
|
+
let match;
|
|
72
|
+
while ((match = header.exec(body))) {
|
|
73
|
+
const start = match.index + match[1].length + (/^\s*/.exec(body.slice(match.index + match[1].length))?.[0].length ?? 0);
|
|
74
|
+
const open = header.lastIndex - 1;
|
|
75
|
+
if (!structure.codeOffsets[start] || !structure.codeOffsets[open] || structure.depthBefore[start] !== 0) continue;
|
|
76
|
+
const close = structure.bracePairs.get(open);
|
|
77
|
+
if (close === undefined) continue;
|
|
78
|
+
const headerText = (match[2] ?? '').trim(), name = nameFrom(headerText);
|
|
79
|
+
ranges.push({ header: headerText, name, id: idFrom(headerText, `render_${name}`), startOffset: baseOffset + start, endOffset: baseOffset + close + 1, bodyStartOffset: baseOffset + open + 1, bodyEndOffset: baseOffset + close, parentRenderId });
|
|
80
|
+
header.lastIndex = close + 1;
|
|
81
|
+
}
|
|
82
|
+
return ranges;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function childRecord(source, block, options, line, input) {
|
|
86
|
+
return cleanRecord({
|
|
87
|
+
...input,
|
|
88
|
+
normalizedRowKind: input.normalizedRowKind ?? input.rowKind,
|
|
89
|
+
header: line.header ?? line.text ?? source.slice(line.startOffset, line.endOffset).trim(),
|
|
90
|
+
startOffset: line.startOffset,
|
|
91
|
+
endOffset: line.endOffset,
|
|
92
|
+
location: sourcePosition(source, line.startOffset),
|
|
93
|
+
parentKind: block.kind,
|
|
94
|
+
parentId: block.id,
|
|
95
|
+
parentName: block.name,
|
|
96
|
+
moduleId: block.moduleId,
|
|
97
|
+
moduleName: block.moduleName,
|
|
98
|
+
sourceSpan: sourceSpan(source, block, line.startOffset, line.endOffset, options),
|
|
99
|
+
recognized: input.recognized ?? true
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function unknownViewChild(source, block, options, line, reason, parentRenderId) {
|
|
104
|
+
const rowKind = /^([A-Za-z_$][\w$-]*)\b/.exec(line.text)?.[1];
|
|
105
|
+
return childRecord(source, block, options, line, { kind: 'viewUnknownRow', rowKind, normalizedRowKind: 'unknown', name: rowKind ?? 'unknown', id: `view_unknown_${safeId(rowKind ?? 'row')}_${line.startOffset}`, parentRenderId, recognized: false, reason });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function readBodyLines(source, startOffset, endOffset) {
|
|
109
|
+
const lines = source.slice(startOffset, endOffset).split('\n');
|
|
110
|
+
const records = [];
|
|
111
|
+
let lineStart = startOffset;
|
|
112
|
+
for (const rawLine of lines) {
|
|
113
|
+
const rawEnd = lineStart + rawLine.length, leading = /^\s*/.exec(rawLine)?.[0].length ?? 0, trailing = /\s*$/.exec(rawLine)?.[0].length ?? 0;
|
|
114
|
+
const start = lineStart + leading, end = Math.max(start, rawEnd - trailing);
|
|
115
|
+
records.push({ text: rawLine.trim(), startOffset: start, endOffset: end });
|
|
116
|
+
lineStart = rawEnd + 1;
|
|
117
|
+
}
|
|
118
|
+
return records;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function scanStructure(source) {
|
|
122
|
+
const depthBefore = new Int32Array(source.length + 1), codeOffsets = new Uint8Array(source.length), stack = [], bracePairs = new Map();
|
|
123
|
+
let depth = 0, state = 'code', quote = '';
|
|
124
|
+
for (let index = 0; index < source.length; index++) {
|
|
125
|
+
depthBefore[index] = depth;
|
|
126
|
+
const char = source[index], next = source[index + 1];
|
|
127
|
+
if (state === 'line-comment') { if (char === '\n') state = 'code'; continue; }
|
|
128
|
+
if (state === 'block-comment') { if (char === '*' && next === '/') { index++; state = 'code'; } continue; }
|
|
129
|
+
if (state === 'string') { if (char === '\\') { index++; continue; } if (char === quote) state = 'code'; continue; }
|
|
130
|
+
codeOffsets[index] = 1;
|
|
131
|
+
if (char === '#') { state = 'line-comment'; continue; }
|
|
132
|
+
if (char === '"' || char === "'" || char === '`') { state = 'string'; quote = char; continue; }
|
|
133
|
+
if (char === '/' && next === '*') { index++; state = 'block-comment'; continue; }
|
|
134
|
+
if (char === '/' && next === '/') { index++; state = 'line-comment'; continue; }
|
|
135
|
+
if (char === '{') { stack.push(index); depth++; continue; }
|
|
136
|
+
if (char === '}') { const open = stack.pop(); if (open !== undefined) { depth = Math.max(0, depth - 1); bracePairs.set(open, index); } }
|
|
137
|
+
}
|
|
138
|
+
depthBefore[source.length] = depth;
|
|
139
|
+
return { depthBefore, codeOffsets, bracePairs };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function inRange(offset, ranges) { return ranges.some((range) => offset >= range.startOffset && offset < range.endOffset); }
|
|
143
|
+
function readList(value) { return value.split(',').map((item) => item.trim()).filter(Boolean); }
|
|
144
|
+
function readInlineWord(label, text = '') { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(text)?.[1]?.trim(); }
|
|
145
|
+
function readInlineType(label, text = '') { return new RegExp('(?:^|\\s)' + label + '\\s+(.+)$').exec(text)?.[1]?.trim(); }
|
|
146
|
+
function readRenderValue(value) { const quoted = /^["']([^"']+)["']$/.exec(value.trim()); return quoted ? { value: quoted[1] } : { expression: value.trim() }; }
|
|
147
|
+
function idFrom(header, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(header)?.[1] ?? fallback; }
|
|
148
|
+
function nameFrom(header) { return /^([A-Za-z_$][\w$.-]*)/.exec(header)?.[1] ?? 'Unnamed'; }
|
|
149
|
+
function sourcePosition(source, offset) { const lines = source.slice(0, offset).split('\n'); return { line: lines.length, column: lines[lines.length - 1].length + 1, offset }; }
|
|
150
|
+
function sourceSpan(source, block, startOffset, endOffset, options = {}) { return cleanRecord({ sourceId: options.documentId, path: options.sourcePath, blockId: block.id, blockKind: block.kind, startOffset, endOffset, start: sourcePosition(source, startOffset), end: sourcePosition(source, endOffset) }); }
|
|
151
|
+
function safeId(value) { return String(value).replace(/[^A-Za-z0-9_$-]+/g, '_').replace(/^_+|_+$/g, '') || 'row'; }
|
|
152
|
+
function cleanRecord(record) { return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined && (!Array.isArray(value) || value.length > 0))); }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shapeshift-labs/frontier-lang-parser",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.78",
|
|
4
4
|
"description": "Parser for the first Frontier Lang .frontier syntax slice.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
],
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "node scripts/build.mjs",
|
|
25
|
-
"test": "npm run build && node test/smoke.mjs && node test/member-identity-smoke.mjs && node test/generic-row-parse-identity-smoke.mjs && node test/type-variant-payload-smoke.mjs && node test/type-generic-ref-smoke.mjs && node test/type-parameter-constraints-smoke.mjs && node test/type-structural-expression-smoke.mjs && node test/action-body-smoke.mjs && node test/action-structured-literals-smoke.mjs && node test/action-return-smoke.mjs && node test/action-else-smoke.mjs && node test/action-match-smoke.mjs && node test/action-for-in-smoke.mjs && node test/action-repeat-smoke.mjs && node test/action-call-smoke.mjs && node test/semantic-operation-edit-smoke.mjs && node test/target-projection-aggregate-smoke.mjs && node test/conversion-constraint-fields-smoke.mjs && node test/conversion-evidence-smoke.mjs && node test/gate-admission-evidence-smoke.mjs && node test/package-canvas-surface-smoke.mjs && node test/view-render-graph-smoke.mjs && node test/resource-graph-smoke.mjs && node test/machine-graph-smoke.mjs && node test/interlingua-smoke.mjs && node test/dialect-registry-smoke.mjs",
|
|
25
|
+
"test": "npm run build && node test/smoke.mjs && node test/source-syntax-family-summary-smoke.mjs && node test/member-identity-smoke.mjs && node test/generic-row-parse-identity-smoke.mjs && node test/type-variant-payload-smoke.mjs && node test/type-generic-ref-smoke.mjs && node test/type-parameter-constraints-smoke.mjs && node test/type-structural-expression-smoke.mjs && node test/action-body-smoke.mjs && node test/action-structured-literals-smoke.mjs && node test/action-return-smoke.mjs && node test/action-else-smoke.mjs && node test/action-match-smoke.mjs && node test/action-for-in-smoke.mjs && node test/action-repeat-smoke.mjs && node test/action-call-smoke.mjs && node test/semantic-operation-edit-smoke.mjs && node test/target-projection-aggregate-smoke.mjs && node test/conversion-constraint-fields-smoke.mjs && node test/conversion-evidence-smoke.mjs && node test/gate-admission-evidence-smoke.mjs && node test/package-canvas-surface-smoke.mjs && node test/view-render-graph-smoke.mjs && node test/view-source-syntax-smoke.mjs && node test/resource-graph-smoke.mjs && node test/machine-graph-smoke.mjs && node test/interlingua-smoke.mjs && node test/dialect-registry-smoke.mjs",
|
|
26
26
|
"typecheck": "node ./node_modules/typescript/bin/tsc --noEmit -p test/tsconfig.json",
|
|
27
27
|
"fuzz": "npm run build && node fuzz/smoke.mjs",
|
|
28
28
|
"bench": "npm run build && node bench/smoke.mjs",
|