@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.
- package/dist/action-body.js +34 -4
- package/dist/action-else-block.js +29 -0
- package/dist/action-syntax-children.js +26 -2
- package/package.json +2 -2
package/dist/action-body.js
CHANGED
|
@@ -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.
|
|
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",
|