@shapeshift-labs/frontier-lang-parser 0.3.49 → 0.3.50

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.
@@ -1,5 +1,6 @@
1
1
  import { readFrontierNestedBlocks } from './source-syntax-report.js';
2
2
  import { readActionValue } from './action-expression.js';
3
+ import { readElseHeaderBlock } from './action-else-block.js';
3
4
 
4
5
  export function readActionBodyRecords(body) {
5
6
  const records = [];
@@ -27,10 +28,18 @@ function parseActionBodyRecords(source, state) {
27
28
  const open = offset + ifHeader[0].length - 1;
28
29
  const close = findMatchingBrace(source, open);
29
30
  if (close < 0) break;
31
+ const elseBlock = readElseHeaderBlock(source, close + 1, source.length, { skipWhitespace: skipWhitespaceAndComments, findMatchingBrace });
30
32
  const index = state.index++;
31
- const record = parseActionIfBlock(ifHeader[1].trim(), source.slice(open + 1, close), index, state);
33
+ const record = parseActionIfBlock(ifHeader[1].trim(), source.slice(open + 1, close), index, state, elseBlock);
32
34
  if (record) records.push(record);
33
- offset = close + 1;
35
+ offset = elseBlock ? elseBlock.close + 1 : close + 1;
36
+ continue;
37
+ }
38
+ const elseHeader = /^else\b([^{\n]*)\{/.exec(source.slice(offset));
39
+ if (elseHeader) {
40
+ const open = offset + elseHeader[0].length - 1;
41
+ const close = findMatchingBrace(source, open);
42
+ offset = close < 0 ? source.length : close + 1;
34
43
  continue;
35
44
  }
36
45
  const lineEnd = source.indexOf('\n', offset);
@@ -45,9 +54,10 @@ function parseActionBodyRecords(source, state) {
45
54
  return records;
46
55
  }
47
56
 
48
- function parseActionIfBlock(header, body, index, state) {
57
+ function parseActionIfBlock(header, body, index, state, elseBlock) {
49
58
  const details = parseActionIfHeader(header, index);
50
59
  if (!details.condition) return undefined;
60
+ const elseDetails = elseBlock ? parseActionElseHeader(elseBlock.header, index) : undefined;
51
61
  return compactRecord({
52
62
  kind: 'if',
53
63
  id: details.id,
@@ -55,7 +65,10 @@ function parseActionIfBlock(header, body, index, state) {
55
65
  comparisonType: details.comparisonType,
56
66
  callType: details.callType,
57
67
  condition: details.condition,
58
- body: parseActionBodyRecords(body, state)
68
+ body: parseActionBodyRecords(body, state),
69
+ elseId: elseDetails?.id,
70
+ elseName: elseDetails?.name,
71
+ elseBody: elseBlock ? parseActionBodyRecords(elseBlock.body, state) : undefined
59
72
  });
60
73
  }
61
74
 
@@ -76,6 +89,15 @@ function parseActionIfHeader(header, index) {
76
89
  };
77
90
  }
78
91
 
92
+ function parseActionElseHeader(header, index) {
93
+ const withoutId = header.replace(/@id\(\s*["'][^"']+["']\s*\)/g, '').trim();
94
+ const name = firstIdentifier(withoutId) ?? `else_${index}`;
95
+ return {
96
+ id: idFrom(header, `action_body_else_${name}`),
97
+ name
98
+ };
99
+ }
100
+
79
101
  function parseActionBodyLine(line, index) {
80
102
  const statement = /^([A-Za-z_$][\w$-]*)(?:\s+([A-Za-z_$@./:*+-][\w$./@:*+-]*))?(.*)$/.exec(line);
81
103
  if (!statement) return undefined;
@@ -0,0 +1,18 @@
1
+ export function readElseHeaderBlock(source, offset, endOffset = source.length, helpers = {}) {
2
+ const skipWhitespace = helpers.skipWhitespace;
3
+ const findMatchingBrace = helpers.findMatchingBrace;
4
+ if (typeof skipWhitespace !== 'function' || typeof findMatchingBrace !== 'function') return undefined;
5
+ const elseOffset = skipWhitespace(source, offset, endOffset);
6
+ const elseHeader = /^else\b([^{\n]*)\{/.exec(source.slice(elseOffset, endOffset));
7
+ if (!elseHeader) return undefined;
8
+ const header = elseHeader[1].trim();
9
+ const open = elseOffset + elseHeader[0].length - 1;
10
+ const close = findMatchingBrace(source, open);
11
+ if (close < 0 || close > endOffset) return undefined;
12
+ return { header, start: elseOffset, open, close, body: source.slice(open + 1, close), supported: isSupportedElseHeader(header) };
13
+ }
14
+
15
+ export function isSupportedElseHeader(header) {
16
+ const withoutId = String(header ?? '').replace(/@id\(\s*["'][^"']+["']\s*\)/g, '').trim();
17
+ return !withoutId || /^[A-Za-z_$][\w$-]*$/.test(withoutId);
18
+ }
@@ -1,4 +1,5 @@
1
1
  import { parseActionValue } from './action-expression.js';
2
+ import { readElseHeaderBlock } from './action-else-block.js';
2
3
 
3
4
  const ACTION_BODY_ROWS = new Set(['set', 'insert', 'remove', 'merge', 'callEffect', 'return', 'if', 'let']);
4
5
 
@@ -33,9 +34,31 @@ function readActionSyntaxRows(source, block, options, state, startOffset, endOff
33
34
  }, state, parentActionBodyId);
34
35
  children.push(child);
35
36
  children.push(...readActionSyntaxRows(source, block, options, state, open + 1, close, child.id));
37
+ const elseBlock = readElseHeaderBlock(source, close + 1, endOffset, { skipWhitespace: skipWhitespaceAndLineComments, findMatchingBrace });
38
+ if (elseBlock) {
39
+ const elseChild = actionSyntaxChild(source, block, options, {
40
+ text: source.slice(elseBlock.start, elseBlock.open + 1).trim(),
41
+ startOffset: elseBlock.start,
42
+ endOffset: elseBlock.open + 1
43
+ }, state, child.id, { recognized: elseBlock.supported });
44
+ children.push(elseChild);
45
+ if (elseBlock.supported) children.push(...readActionSyntaxRows(source, block, options, state, elseBlock.open + 1, elseBlock.close, elseChild.id));
46
+ offset = elseBlock.close + 1;
47
+ continue;
48
+ }
36
49
  offset = close + 1;
37
50
  continue;
38
51
  }
52
+ const elseBlock = readElseHeaderBlock(source, offset, endOffset, { skipWhitespace: skipWhitespaceAndLineComments, findMatchingBrace });
53
+ if (elseBlock) {
54
+ children.push(actionSyntaxChild(source, block, options, {
55
+ text: source.slice(elseBlock.start, elseBlock.open + 1).trim(),
56
+ startOffset: elseBlock.start,
57
+ endOffset: elseBlock.open + 1
58
+ }, state, parentActionBodyId));
59
+ offset = elseBlock.close + 1;
60
+ continue;
61
+ }
39
62
  const lineEnd = source.indexOf('\n', offset);
40
63
  const rawEnd = lineEnd < 0 || lineEnd > endOffset ? endOffset : lineEnd;
41
64
  const rawLine = source.slice(offset, rawEnd);
@@ -52,14 +75,14 @@ function readActionSyntaxRows(source, block, options, state, startOffset, endOff
52
75
  return children;
53
76
  }
54
77
 
55
- function actionSyntaxChild(source, block, options, line, state, parentActionBodyId) {
78
+ function actionSyntaxChild(source, block, options, line, state, parentActionBodyId, overrides = {}) {
56
79
  const rowIndex = state.rowIndex++;
57
80
  const row = /^([A-Za-z_$][\w$-]*)(?:\s+([A-Za-z_$@./:*+-][\w$./@:*+-]*))?(.*)$/.exec(line.text);
58
81
  const rowKind = row?.[1] ?? 'unknown';
59
82
  const name = actionRowName(rowKind, row?.[2], rowIndex);
60
83
  const rest = row?.[2]?.startsWith('@') ? ` ${row[2]}${row[3] ?? ''}` : (row?.[3] ?? '');
61
84
  const validation = validateActionRow(rowKind, row?.[2], rest, line.text);
62
- const recognized = ACTION_BODY_ROWS.has(rowKind) && validation.ok;
85
+ const recognized = overrides.recognized ?? (ACTION_BODY_ROWS.has(rowKind) && validation.ok);
63
86
  return cleanRecord({
64
87
  kind: recognized ? 'actionBodyRow' : 'actionUnknownRow',
65
88
  rowKind,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-parser",
3
- "version": "0.3.49",
3
+ "version": "0.3.50",
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/action-body-smoke.mjs && node test/action-return-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/action-body-smoke.mjs && node test/action-return-smoke.mjs && node test/action-else-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",