@shapeshift-labs/frontier-lang-parser 0.3.46 → 0.3.47
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
CHANGED
|
@@ -52,6 +52,7 @@ function parseActionIfBlock(header, body, index, state) {
|
|
|
52
52
|
kind: 'if',
|
|
53
53
|
id: details.id,
|
|
54
54
|
name: details.name,
|
|
55
|
+
comparisonType: details.comparisonType,
|
|
55
56
|
condition: details.condition,
|
|
56
57
|
body: parseActionBodyRecords(body, state)
|
|
57
58
|
});
|
|
@@ -63,10 +64,12 @@ function parseActionIfHeader(header, index) {
|
|
|
63
64
|
const nameText = explicitCondition ? withoutId.slice(0, explicitCondition.index).trim() : '';
|
|
64
65
|
const name = firstIdentifier(nameText) ?? `if_${index}`;
|
|
65
66
|
const conditionText = explicitCondition?.[1]?.trim() || withoutId;
|
|
67
|
+
const comparisonType = readInlineComparisonType(header);
|
|
66
68
|
return {
|
|
67
69
|
id: idFrom(header, `action_body_if_${name}`),
|
|
68
70
|
name,
|
|
69
|
-
|
|
71
|
+
comparisonType,
|
|
72
|
+
condition: conditionText ? readActionValue(conditionText, { comparisonType }) : undefined
|
|
70
73
|
};
|
|
71
74
|
}
|
|
72
75
|
|
|
@@ -79,9 +82,10 @@ function parseActionBodyLine(line, index) {
|
|
|
79
82
|
if (rowKind === 'set' || rowKind === 'insert' || rowKind === 'merge') {
|
|
80
83
|
const path = readInlineWord('path', rest);
|
|
81
84
|
const valueType = readInlineType(rest);
|
|
82
|
-
const
|
|
85
|
+
const comparisonType = readInlineComparisonType(rest);
|
|
86
|
+
const value = readInlineActionValue('value', rest, { valueType, comparisonType });
|
|
83
87
|
if (!path || !value) return undefined;
|
|
84
|
-
return compactRecord({ kind: 'patch', op: rowKind, id: idFrom(rest, `action_body_${rowKind}_${name}`), name, path, valueType, value });
|
|
88
|
+
return compactRecord({ kind: 'patch', op: rowKind, id: idFrom(rest, `action_body_${rowKind}_${name}`), name, path, valueType, comparisonType, value });
|
|
85
89
|
}
|
|
86
90
|
if (rowKind === 'remove') {
|
|
87
91
|
const path = readInlineWord('path', rest);
|
|
@@ -96,9 +100,10 @@ function parseActionBodyLine(line, index) {
|
|
|
96
100
|
}
|
|
97
101
|
if (rowKind === 'let') {
|
|
98
102
|
const valueType = readInlineType(rest);
|
|
99
|
-
const
|
|
103
|
+
const comparisonType = readInlineComparisonType(rest);
|
|
104
|
+
const value = readInlineActionBindingValue('value', rest, { valueType, comparisonType });
|
|
100
105
|
if (!rawName || rawName.startsWith('@') || !isActionBindingName(name) || !value) return undefined;
|
|
101
|
-
return compactRecord({ kind: 'let', id: idFrom(rest, `action_body_let_${name}`), name, valueType, value });
|
|
106
|
+
return compactRecord({ kind: 'let', id: idFrom(rest, `action_body_let_${name}`), name, valueType, comparisonType, value });
|
|
102
107
|
}
|
|
103
108
|
if (rowKind === 'return') {
|
|
104
109
|
const valueText = stripIds(rawName?.startsWith('@') ? rest : `${rawName ?? ''}${rest ?? ''}`).trim();
|
|
@@ -189,6 +194,7 @@ function isActionBindingName(value) {
|
|
|
189
194
|
|
|
190
195
|
function idFrom(header, fallback) { return /@id\(\s*["']([^"']+)["']\s*\)/.exec(header)?.[1] ?? fallback; }
|
|
191
196
|
function readInlineType(text) { return readInlineWord('type', text) ?? readInlineWord('valueType', text); }
|
|
197
|
+
function readInlineComparisonType(text) { return readInlineWord('compare', text) ?? readInlineWord('comparisonType', text) ?? readInlineWord('compareType', text); }
|
|
192
198
|
function readInlineWord(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+([^\\s,]+)').exec(text)?.[1]?.trim(); }
|
|
193
199
|
function readInlineValue(label, text) { return new RegExp('(?:^|\\s)' + label + '\\s+(.+?)(?=\\s+[A-Za-z_$][\\w$-]*\\s+|$)').exec(text)?.[1]?.trim(); }
|
|
194
200
|
function stripIds(text) { return String(text ?? '').replace(/@id\(\s*["'][^"']+["']\s*\)/g, '').trim(); }
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const NUMERIC_OPERATORS = new Set(['+', '-', '*', '/', '%']);
|
|
2
|
+
|
|
3
|
+
const ORDERED_COMPARISON_OPERATORS = new Set(['>', '>=', '<', '<=']);
|
|
4
|
+
const NUMERIC_TYPES = new Set(['number', 'numeric', 'int', 'integer', 'float', 'double', 'decimal']);
|
|
5
|
+
|
|
6
|
+
export function hasNumericOperator(node) {
|
|
7
|
+
if (!node || typeof node !== 'object') return false;
|
|
8
|
+
if (node.kind === 'binary') return NUMERIC_OPERATORS.has(node.op) || hasNumericOperator(node.left) || hasNumericOperator(node.right);
|
|
9
|
+
if (node.kind === 'logical') return hasNumericOperator(node.left) || hasNumericOperator(node.right);
|
|
10
|
+
if (node.kind === 'unary') return hasNumericOperator(node.argument);
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function hasNonLiteralOrderedComparison(node) {
|
|
15
|
+
if (!node || typeof node !== 'object') return false;
|
|
16
|
+
if (node.kind === 'binary') {
|
|
17
|
+
const current = ORDERED_COMPARISON_OPERATORS.has(node.op) && !isLiteralNumericComparison(node);
|
|
18
|
+
return current || hasNonLiteralOrderedComparison(node.left) || hasNonLiteralOrderedComparison(node.right);
|
|
19
|
+
}
|
|
20
|
+
if (node.kind === 'logical') return hasNonLiteralOrderedComparison(node.left) || hasNonLiteralOrderedComparison(node.right);
|
|
21
|
+
if (node.kind === 'unary') return hasNonLiteralOrderedComparison(node.argument);
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isNumericType(value) {
|
|
26
|
+
return NUMERIC_TYPES.has(String(value ?? '').trim().toLowerCase());
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isLiteralNumericComparison(node) {
|
|
30
|
+
return node.left?.kind === 'literal' && typeof node.left.value === 'number' && node.right?.kind === 'literal' && typeof node.right.value === 'number';
|
|
31
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { NUMERIC_OPERATORS, hasNonLiteralOrderedComparison, hasNumericOperator, isNumericType } from './action-expression-semantics.js';
|
|
2
|
+
|
|
1
3
|
const IDENTIFIER = /^[A-Za-z_$][\w$-]*$/;
|
|
2
4
|
const BLOCKED_REF_ROOTS = new Set(['globalThis', 'process', 'window', 'document', 'constructor', 'prototype', '__proto__', 'env']);
|
|
3
|
-
const NUMERIC_OPERATORS = new Set(['+', '-', '*', '/', '%']);
|
|
4
|
-
const NUMERIC_TYPES = new Set(['number', 'numeric', 'int', 'integer', 'float', 'double', 'decimal']);
|
|
5
5
|
|
|
6
6
|
export function readActionValue(value, options = {}) {
|
|
7
7
|
const parsed = parseActionValue(value, options);
|
|
@@ -16,9 +16,13 @@ export function parseActionValue(value, options = {}) {
|
|
|
16
16
|
if (hasNumericOperator(expression.ast) && !isNumericType(options.valueType ?? options.type)) {
|
|
17
17
|
return fail((options.valueType ?? options.type) ? 'unsupported-action-expression-type' : 'missing-action-expression-type');
|
|
18
18
|
}
|
|
19
|
+
if (hasNonLiteralOrderedComparison(expression.ast) && !isNumericType(options.comparisonType ?? options.compareType)) {
|
|
20
|
+
return fail((options.comparisonType ?? options.compareType) ? 'unsupported-action-comparison-type' : 'missing-action-comparison-type');
|
|
21
|
+
}
|
|
19
22
|
const valueType = options.valueType ?? options.type;
|
|
20
|
-
|
|
21
|
-
return { ok: true, value: compactRecord({
|
|
23
|
+
const comparisonType = options.comparisonType ?? options.compareType;
|
|
24
|
+
if (expression.literal) return { ok: true, value: compactRecord({ value: expression.ast.value, valueType, comparisonType }) };
|
|
25
|
+
return { ok: true, value: compactRecord({ expression: text, expressionAst: expression.ast, valueType, comparisonType }) };
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
export function parseActionExpression(value, options = {}) {
|
|
@@ -288,18 +292,6 @@ function canStartSignedNumber(tokens) {
|
|
|
288
292
|
return previous.type === 'punctuation' && previous.text === '(';
|
|
289
293
|
}
|
|
290
294
|
|
|
291
|
-
function hasNumericOperator(node) {
|
|
292
|
-
if (!node || typeof node !== 'object') return false;
|
|
293
|
-
if (node.kind === 'binary') return NUMERIC_OPERATORS.has(node.op) || hasNumericOperator(node.left) || hasNumericOperator(node.right);
|
|
294
|
-
if (node.kind === 'logical') return hasNumericOperator(node.left) || hasNumericOperator(node.right);
|
|
295
|
-
if (node.kind === 'unary') return hasNumericOperator(node.argument);
|
|
296
|
-
return false;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function isNumericType(value) {
|
|
300
|
-
return NUMERIC_TYPES.has(String(value ?? '').trim().toLowerCase());
|
|
301
|
-
}
|
|
302
|
-
|
|
303
295
|
function refNode(parts) {
|
|
304
296
|
const [root, ...rest] = parts;
|
|
305
297
|
const scope = root === 'input' || root === 'state' || root === 'patches' ? root : 'local';
|
|
@@ -91,10 +91,10 @@ function actionRowName(rowKind, rawName, rowIndex) {
|
|
|
91
91
|
|
|
92
92
|
function validateActionRow(rowKind, rawName, rest, header) {
|
|
93
93
|
if (!ACTION_BODY_ROWS.has(rowKind)) return { ok: false, reason: 'unsupported-action-body-row' };
|
|
94
|
-
if (rowKind === 'if') return validateActionExpressionText(readIfCondition(header));
|
|
94
|
+
if (rowKind === 'if') return validateActionExpressionText(readIfCondition(header), { comparisonType: readInlineComparisonType(header) });
|
|
95
95
|
if (rowKind === 'set' || rowKind === 'insert' || rowKind === 'merge') {
|
|
96
96
|
if (!readInlineWord('path', rest)) return { ok: false, reason: 'missing-action-path' };
|
|
97
|
-
return validateActionExpressionText(readInlineValue('value', rest), { valueType: readInlineType(rest) });
|
|
97
|
+
return validateActionExpressionText(readInlineValue('value', rest), { valueType: readInlineType(rest), comparisonType: readInlineComparisonType(rest) });
|
|
98
98
|
}
|
|
99
99
|
if (rowKind === 'remove') {
|
|
100
100
|
return readInlineWord('path', rest) ? { ok: true } : { ok: false, reason: 'missing-action-path' };
|
|
@@ -112,9 +112,9 @@ function validateActionRow(rowKind, rawName, rest, header) {
|
|
|
112
112
|
return { ok: false, reason: 'unsupported-action-binding-name' };
|
|
113
113
|
}
|
|
114
114
|
const value = readInlineValue('value', rest);
|
|
115
|
-
const parsed = value ? parseActionValue(value, { valueType: readInlineType(rest) }) : undefined;
|
|
115
|
+
const parsed = value ? parseActionValue(value, { valueType: readInlineType(rest), comparisonType: readInlineComparisonType(rest) }) : undefined;
|
|
116
116
|
if (parsed?.ok) return { ok: true };
|
|
117
|
-
if (parsed?.reason === 'missing-action-expression-type' || parsed?.reason === 'unsupported-action-expression-type') {
|
|
117
|
+
if (parsed?.reason === 'missing-action-expression-type' || parsed?.reason === 'unsupported-action-expression-type' || parsed?.reason === 'missing-action-comparison-type' || parsed?.reason === 'unsupported-action-comparison-type') {
|
|
118
118
|
return { ok: false, reason: parsed.reason };
|
|
119
119
|
}
|
|
120
120
|
return { ok: false, reason: 'unsupported-action-binding-value' };
|
|
@@ -134,6 +134,10 @@ function readInlineType(text) {
|
|
|
134
134
|
return readInlineWord('type', text) ?? readInlineWord('valueType', text);
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
function readInlineComparisonType(text) {
|
|
138
|
+
return readInlineWord('compare', text) ?? readInlineWord('comparisonType', text) ?? readInlineWord('compareType', text);
|
|
139
|
+
}
|
|
140
|
+
|
|
137
141
|
function readIfCondition(header) {
|
|
138
142
|
const text = header.replace(/^if\b/, '').replace(/\{\s*$/, '').replace(/@id\(\s*["'][^"']+["']\s*\)/g, '').trim();
|
|
139
143
|
const explicit = /\bcondition\s+(.+)$/.exec(text);
|