@shapeshift-labs/frontier-lang-parser 0.3.57 → 0.3.59

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/index.js CHANGED
@@ -16,6 +16,8 @@ import { parseResourceGraphBlock } from './resource-graph.js';
16
16
  import { parseRuntimeCapabilityBlock } from './runtime-capability.js';
17
17
  import { parseNativeSourceBlock } from './source-evidence.js';
18
18
  import { parseTargetProjectionMetadata } from './target-projection.js';
19
+ import { parseOptionalTypeExpression, parseTypeExpression } from './type-expressions.js';
20
+ import { readTypeParameterNames, readTypeParameterRecords } from './type-parameters.js';
19
21
  import { readVariantPayloadFields } from './type-variants.js';
20
22
  import { parseViewBlock } from './view.js';
21
23
  import { FrontierSourceBlockKinds, readFrontierSourceBlocks } from './source-syntax-report.js';
@@ -167,7 +169,17 @@ function parseCapability(block) {
167
169
  function parseType(block) {
168
170
  const name = nameFrom(block.header);
169
171
  const alias = /^\s*=\s*([^\n]+)/m.exec(block.body)?.[1]?.trim();
170
- return typeNode({ id: idFrom(block.header, `type_${name}`), name, parameters: readTypeParameters(block.header), type: alias ? parseTypeExpression(alias) : undefined, fields: readTypeFields(block.body), variants: readVariants(block.body), invariants: readList('invariants', block.body) });
172
+ const typeParameters = readTypeParameterRecords(block.header);
173
+ return typeNode({
174
+ id: idFrom(block.header, `type_${name}`),
175
+ name,
176
+ parameters: typeParameters?.map((parameter) => parameter.name) ?? readTypeParameterNames(block.header),
177
+ ...(typeParameters ? { typeParameters } : {}),
178
+ type: alias ? parseTypeExpression(alias) : undefined,
179
+ fields: readTypeFields(block.body),
180
+ variants: readVariants(block.body),
181
+ invariants: readList('invariants', block.body)
182
+ });
171
183
  }
172
184
  function parseExtern(block) {
173
185
  const name = nameFrom(block.header);
@@ -280,18 +292,6 @@ function parseSemantic(text) {
280
292
  if (latticeId) return { kind: 'lattice', latticeId };
281
293
  return undefined;
282
294
  }
283
- function parseOptionalTypeExpression(value) { return value ? parseTypeExpression(value.trim()) : undefined; }
284
- function parseTypeExpression(value) {
285
- const text = value.trim();
286
- if (/^Set<.+>$/.test(text)) return { kind: 'set', item: parseTypeExpression(text.slice(4, -1)) };
287
- if (/^List<.+>$/.test(text)) return { kind: 'list', item: parseTypeExpression(text.slice(5, -1)) };
288
- const map = /^Map<(.+),\s*(.+)>$/.exec(text);
289
- if (map) return { kind: 'map', key: parseTypeExpression(map[1]), value: parseTypeExpression(map[2]) };
290
- return text;
291
- }
292
- function readTypeParameters(header) {
293
- return /<([^>]+)>/.exec(header)?.[1]?.split(',').map((item) => item.trim()).filter(Boolean);
294
- }
295
295
  function readTypeFields(body) {
296
296
  const fields = [];
297
297
  const re = /^\s*([A-Za-z_$][\w$]*)(?:\s+@id\(\s*["']([^"']+)["']\s*\))?\s*:\s*([^\n]+)/gm;
@@ -0,0 +1,33 @@
1
+ import { splitTopLevelCommaList } from './type-variants.js';
2
+
3
+ export function parseOptionalTypeExpression(value) {
4
+ return value ? parseTypeExpression(value.trim()) : undefined;
5
+ }
6
+
7
+ export function parseTypeExpression(value) {
8
+ const text = value.trim();
9
+ const application = readTypeApplication(text);
10
+ if (!application) return text;
11
+ const args = splitTopLevelCommaList(application.body);
12
+ if (args.some((arg) => !arg)) return text;
13
+ const parsedArgs = args.map(parseTypeExpression);
14
+ if (application.name === 'Set' && parsedArgs.length === 1) return { kind: 'set', item: parsedArgs[0] };
15
+ if (application.name === 'List' && parsedArgs.length === 1) return { kind: 'list', item: parsedArgs[0] };
16
+ if (application.name === 'Map' && parsedArgs.length === 2) return { kind: 'map', key: parsedArgs[0], value: parsedArgs[1] };
17
+ return { kind: 'ref', name: application.name, args: parsedArgs };
18
+ }
19
+
20
+ function readTypeApplication(text) {
21
+ const match = /^([A-Za-z_$][\w$]*)</.exec(text);
22
+ if (!match || !text.endsWith('>')) return undefined;
23
+ let depth = 0;
24
+ for (let index = match[1].length; index < text.length; index++) {
25
+ const char = text[index];
26
+ if (char === '<') depth++;
27
+ if (char === '>') depth--;
28
+ if (depth < 0) return undefined;
29
+ if (depth === 0 && index !== text.length - 1) return undefined;
30
+ }
31
+ if (depth !== 0) return undefined;
32
+ return { name: match[1], body: text.slice(match[1].length + 1, -1).trim() };
33
+ }
@@ -0,0 +1,53 @@
1
+ import { parseTypeExpression } from './type-expressions.js';
2
+ import { splitTopLevelCommaList } from './type-variants.js';
3
+
4
+ export function readTypeParameterNames(header) {
5
+ return readTypeParameterRecords(header)?.map((parameter) => parameter.name);
6
+ }
7
+
8
+ export function readTypeParameterRecords(header) {
9
+ const source = readTypeParameterSource(header);
10
+ if (!source) return undefined;
11
+ const records = [];
12
+ for (const item of splitTopLevelCommaList(source)) {
13
+ const record = readTypeParameterRecord(item);
14
+ if (!record) return undefined;
15
+ records.push(record);
16
+ }
17
+ return records.length ? records : undefined;
18
+ }
19
+
20
+ function readTypeParameterRecord(source) {
21
+ const [withoutDefault, defaultSource] = splitTopLevelAssignment(source);
22
+ const match = /^\s*([A-Za-z_$][\w$]*)(?:(?:\s+extends\s+|\s*:\s*)(.+))?\s*$/.exec(withoutDefault);
23
+ if (!match) return undefined;
24
+ return {
25
+ name: match[1],
26
+ ...(match[2]?.trim() ? { constraint: parseTypeExpression(match[2].trim()) } : {}),
27
+ ...(defaultSource?.trim() ? { default: parseTypeExpression(defaultSource.trim()) } : {})
28
+ };
29
+ }
30
+
31
+ function readTypeParameterSource(header) {
32
+ const start = header.indexOf('<');
33
+ if (start < 0) return undefined;
34
+ let depth = 0;
35
+ for (let index = start; index < header.length; index++) {
36
+ const char = header[index];
37
+ if (char === '<') depth++;
38
+ if (char === '>') depth--;
39
+ if (depth === 0) return header.slice(start + 1, index).trim();
40
+ }
41
+ return undefined;
42
+ }
43
+
44
+ function splitTopLevelAssignment(source) {
45
+ let depth = 0;
46
+ for (let index = 0; index < source.length; index++) {
47
+ const char = source[index];
48
+ if (char === '<') depth++;
49
+ if (char === '>') depth--;
50
+ if (char === '=' && depth === 0) return [source.slice(0, index).trim(), source.slice(index + 1).trim()];
51
+ }
52
+ return [source.trim(), undefined];
53
+ }
@@ -41,7 +41,7 @@ function parseVariantPayloadField(source, variantName, parseTypeExpression) {
41
41
  };
42
42
  }
43
43
 
44
- function splitTopLevelCommaList(source) {
44
+ export function splitTopLevelCommaList(source) {
45
45
  const parts = [];
46
46
  let depth = 0;
47
47
  let start = 0;
package/dist/view.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { viewNode } from '@shapeshift-labs/frontier-lang-kernel';
2
2
  import { readFrontierNestedBlocks } from './source-syntax-report.js';
3
+ import { parseOptionalTypeExpression, parseTypeExpression } from './type-expressions.js';
3
4
 
4
5
  export function parseViewBlock(block) {
5
6
  const name = nameFrom(block.header);
@@ -30,7 +31,7 @@ function readViewEvents(body) {
30
31
  const events = [];
31
32
  const re = /^\s*event\s+([A-Za-z_$][\w$.-]*)(?:\s+@id\(\s*["']([^"']+)["']\s*\))?([^\n]*)$/gm;
32
33
  let match;
33
- while ((match = re.exec(body))) events.push({ id: match[2] ?? `view_event_${match[1]}`, name: match[1], action: readInlineWord('action', match[3]), input: parseOptionalTypeExpression(readInlineWord('input', match[3])) });
34
+ while ((match = re.exec(body))) events.push({ id: match[2] ?? `view_event_${match[1]}`, name: match[1], action: readInlineWord('action', match[3]), input: parseOptionalTypeExpression(readInlineType('input', match[3])) });
34
35
  return events;
35
36
  }
36
37
 
@@ -95,15 +96,7 @@ function readList(label, body) { const line = new RegExp('^\\s*' + label + '\\s+
95
96
  function readLine(label, body) { return new RegExp('^\\s*' + label + '\\s+([^\\n]+)', 'm').exec(body)?.[1]?.trim(); }
96
97
  function readQuotedLine(label, body) { return new RegExp(`^\\s*${label}\\s+["']([^"']+)["']`, 'm').exec(body)?.[1]; }
97
98
  function readInlineWord(label, text = '') { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(text)?.[1]?.trim(); }
99
+ function readInlineType(label, text = '') { return new RegExp('(?:^|\\s)' + label + '\\s+(.+)$').exec(text)?.[1]?.trim(); }
98
100
  function readRenderValue(value) { const quoted = /^["']([^"']+)["']$/.exec(value.trim()); return quoted ? { value: quoted[1] } : { expression: value.trim() }; }
99
101
  function compactRecord(record) { return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined)); }
100
102
  function uniqueStrings(values) { const result = []; for (const value of values) if (value && !result.includes(value)) result.push(value); return result.length ? result : undefined; }
101
- function parseOptionalTypeExpression(value) { return value ? parseTypeExpression(value.trim()) : undefined; }
102
- function parseTypeExpression(value) {
103
- const text = value.trim();
104
- if (/^Set<.+>$/.test(text)) return { kind: 'set', item: parseTypeExpression(text.slice(4, -1)) };
105
- if (/^List<.+>$/.test(text)) return { kind: 'list', item: parseTypeExpression(text.slice(5, -1)) };
106
- const map = /^Map<(.+),\s*(.+)>$/.exec(text);
107
- if (map) return { kind: 'map', key: parseTypeExpression(map[1]), value: parseTypeExpression(map[2]) };
108
- return text;
109
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-parser",
3
- "version": "0.3.57",
3
+ "version": "0.3.59",
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/type-variant-payload-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/target-projection-aggregate-smoke.mjs && node test/conversion-constraint-fields-smoke.mjs && node test/conversion-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/interlingua-smoke.mjs && node test/dialect-registry-smoke.mjs",
25
+ "test": "npm run build && node test/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/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/target-projection-aggregate-smoke.mjs && node test/conversion-constraint-fields-smoke.mjs && node test/conversion-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/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",
@@ -54,7 +54,7 @@
54
54
  "access": "public"
55
55
  },
56
56
  "dependencies": {
57
- "@shapeshift-labs/frontier-lang-kernel": "0.3.13"
57
+ "@shapeshift-labs/frontier-lang-kernel": "0.3.14"
58
58
  },
59
59
  "devDependencies": {
60
60
  "typescript": "^5.9.3"