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

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,16 @@ 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.end : close + 1;
36
+ continue;
37
+ }
38
+ const elseBlock = readElseHeaderBlock(source, offset, source.length, { skipWhitespace: skipWhitespaceAndComments, findMatchingBrace });
39
+ if (elseBlock) {
40
+ offset = elseBlock.end;
34
41
  continue;
35
42
  }
36
43
  const lineEnd = source.indexOf('\n', offset);
@@ -45,9 +52,10 @@ function parseActionBodyRecords(source, state) {
45
52
  return records;
46
53
  }
47
54
 
48
- function parseActionIfBlock(header, body, index, state) {
55
+ function parseActionIfBlock(header, body, index, state, elseBlock) {
49
56
  const details = parseActionIfHeader(header, index);
50
57
  if (!details.condition) return undefined;
58
+ const elseDetails = parseActionElseBlock(elseBlock, state, index);
51
59
  return compactRecord({
52
60
  kind: 'if',
53
61
  id: details.id,
@@ -55,10 +63,23 @@ function parseActionIfBlock(header, body, index, state) {
55
63
  comparisonType: details.comparisonType,
56
64
  callType: details.callType,
57
65
  condition: details.condition,
58
- body: parseActionBodyRecords(body, state)
66
+ body: parseActionBodyRecords(body, state),
67
+ elseId: elseDetails?.id,
68
+ elseName: elseDetails?.name,
69
+ elseBody: elseDetails?.body
59
70
  });
60
71
  }
61
72
 
73
+ function parseActionElseBlock(elseBlock, state, index) {
74
+ if (!elseBlock) return undefined;
75
+ if (elseBlock.isElseIf) {
76
+ const nested = parseActionIfBlock(elseBlock.header, elseBlock.body, state.index++, state, elseBlock.tail);
77
+ return nested ? { id: nested.id, name: nested.name, body: [nested] } : undefined;
78
+ }
79
+ const details = parseActionElseHeader(elseBlock.header, index);
80
+ return { ...details, body: parseActionBodyRecords(elseBlock.body, state) };
81
+ }
82
+
62
83
  function parseActionIfHeader(header, index) {
63
84
  const withoutId = header.replace(/@id\(\s*["'][^"']+["']\s*\)/g, '').trim();
64
85
  const explicitCondition = /\bcondition\s+(.+)$/.exec(withoutId);
@@ -76,6 +97,15 @@ function parseActionIfHeader(header, index) {
76
97
  };
77
98
  }
78
99
 
100
+ function parseActionElseHeader(header, index) {
101
+ const withoutId = header.replace(/@id\(\s*["'][^"']+["']\s*\)/g, '').trim();
102
+ const name = firstIdentifier(withoutId) ?? `else_${index}`;
103
+ return {
104
+ id: idFrom(header, `action_body_else_${name}`),
105
+ name
106
+ };
107
+ }
108
+
79
109
  function parseActionBodyLine(line, index) {
80
110
  const statement = /^([A-Za-z_$][\w$-]*)(?:\s+([A-Za-z_$@./:*+-][\w$./@:*+-]*))?(.*)$/.exec(line);
81
111
  if (!statement) return undefined;
@@ -0,0 +1,29 @@
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 elseIfHeader = /^else\s+if\b([^{\n]*)\{/.exec(source.slice(elseOffset, endOffset));
7
+ if (elseIfHeader) return readBlock(source, elseOffset, elseIfHeader, endOffset, findMatchingBrace, true, helpers);
8
+ const elseHeader = /^else\b([^{\n]*)\{/.exec(source.slice(elseOffset, endOffset));
9
+ if (!elseHeader) return undefined;
10
+ return readBlock(source, elseOffset, elseHeader, endOffset, findMatchingBrace, false, helpers);
11
+ }
12
+
13
+ function readBlock(source, elseOffset, elseHeader, endOffset, findMatchingBrace, isElseIf, helpers) {
14
+ const header = elseHeader[1].trim();
15
+ const open = elseOffset + elseHeader[0].length - 1;
16
+ const close = findMatchingBrace(source, open);
17
+ if (close < 0 || close > endOffset) return undefined;
18
+ const tail = isElseIf ? readElseHeaderBlock(source, close + 1, endOffset, helpers) : undefined;
19
+ return { header, start: elseOffset, open, close, end: tail?.end ?? close + 1, body: source.slice(open + 1, close), isElseIf, tail, supported: isElseIf ? isSupportedElseIfHeader(header) : isSupportedElseHeader(header) };
20
+ }
21
+
22
+ export function isSupportedElseHeader(header) {
23
+ const withoutId = String(header ?? '').replace(/@id\(\s*["'][^"']+["']\s*\)/g, '').trim();
24
+ return !withoutId || /^[A-Za-z_$][\w$-]*$/.test(withoutId);
25
+ }
26
+
27
+ function isSupportedElseIfHeader(header) {
28
+ return String(header ?? '').replace(/@id\(\s*["'][^"']+["']\s*\)/g, '').trim().length > 0;
29
+ }
@@ -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,32 @@ 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
+ let branch = elseBlock, parentId = child.id;
40
+ while (branch) {
41
+ const elseChild = actionSyntaxChild(source, block, options, { text: source.slice(branch.start, branch.open + 1).trim(), startOffset: branch.start, endOffset: branch.open + 1 }, state, parentId, { recognized: branch.supported });
42
+ children.push(elseChild);
43
+ if (branch.supported) children.push(...readActionSyntaxRows(source, block, options, state, branch.open + 1, branch.close, elseChild.id));
44
+ parentId = elseChild.id;
45
+ branch = branch.tail;
46
+ }
47
+ offset = elseBlock.end;
48
+ continue;
49
+ }
36
50
  offset = close + 1;
37
51
  continue;
38
52
  }
53
+ const elseBlock = readElseHeaderBlock(source, offset, endOffset, { skipWhitespace: skipWhitespaceAndLineComments, findMatchingBrace });
54
+ if (elseBlock) {
55
+ children.push(actionSyntaxChild(source, block, options, {
56
+ text: source.slice(elseBlock.start, elseBlock.open + 1).trim(),
57
+ startOffset: elseBlock.start,
58
+ endOffset: elseBlock.open + 1
59
+ }, state, parentActionBodyId));
60
+ offset = elseBlock.end;
61
+ continue;
62
+ }
39
63
  const lineEnd = source.indexOf('\n', offset);
40
64
  const rawEnd = lineEnd < 0 || lineEnd > endOffset ? endOffset : lineEnd;
41
65
  const rawLine = source.slice(offset, rawEnd);
@@ -52,14 +76,14 @@ function readActionSyntaxRows(source, block, options, state, startOffset, endOff
52
76
  return children;
53
77
  }
54
78
 
55
- function actionSyntaxChild(source, block, options, line, state, parentActionBodyId) {
79
+ function actionSyntaxChild(source, block, options, line, state, parentActionBodyId, overrides = {}) {
56
80
  const rowIndex = state.rowIndex++;
57
81
  const row = /^([A-Za-z_$][\w$-]*)(?:\s+([A-Za-z_$@./:*+-][\w$./@:*+-]*))?(.*)$/.exec(line.text);
58
82
  const rowKind = row?.[1] ?? 'unknown';
59
83
  const name = actionRowName(rowKind, row?.[2], rowIndex);
60
84
  const rest = row?.[2]?.startsWith('@') ? ` ${row[2]}${row[3] ?? ''}` : (row?.[3] ?? '');
61
85
  const validation = validateActionRow(rowKind, row?.[2], rest, line.text);
62
- const recognized = ACTION_BODY_ROWS.has(rowKind) && validation.ok;
86
+ const recognized = overrides.recognized ?? (ACTION_BODY_ROWS.has(rowKind) && validation.ok);
63
87
  return cleanRecord({
64
88
  kind: recognized ? 'actionBodyRow' : 'actionUnknownRow',
65
89
  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.51",
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",