@jbrowse/jexl 5.0.0 → 5.0.2
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/README.md +4 -2
- package/dist/Expression.d.ts +0 -12
- package/dist/Expression.js +0 -16
- package/dist/Expression.js.map +1 -1
- package/dist/check.d.ts +0 -5
- package/dist/check.js +33 -211
- package/dist/check.js.map +1 -1
- package/dist/conditions.js +3 -3
- package/dist/evaluator/compile.js +18 -2
- package/dist/evaluator/compile.js.map +1 -1
- package/dist/grammar.d.ts +3 -0
- package/dist/grammar.js +18 -16
- package/dist/grammar.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/print.d.ts +9 -0
- package/dist/print.js +189 -0
- package/dist/print.js.map +1 -0
- package/esm/Expression.d.ts +0 -12
- package/esm/Expression.js +0 -16
- package/esm/Expression.js.map +1 -1
- package/esm/check.d.ts +0 -5
- package/esm/check.js +1 -178
- package/esm/check.js.map +1 -1
- package/esm/conditions.js +1 -1
- package/esm/evaluator/compile.js +18 -2
- package/esm/evaluator/compile.js.map +1 -1
- package/esm/grammar.d.ts +3 -0
- package/esm/grammar.js +14 -15
- package/esm/grammar.js.map +1 -1
- package/esm/index.d.ts +2 -1
- package/esm/index.js +2 -1
- package/esm/index.js.map +1 -1
- package/esm/print.d.ts +9 -0
- package/esm/print.js +183 -0
- package/esm/print.js.map +1 -0
- package/package.json +2 -1
- package/src/Expression.ts +0 -20
- package/src/check.ts +2 -198
- package/src/conditions.ts +1 -1
- package/src/evaluator/compile.ts +19 -2
- package/src/grammar.ts +16 -15
- package/src/index.ts +2 -1
- package/src/print.ts +205 -0
package/dist/print.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Jexl
|
|
4
|
+
* Copyright 2020 Tom Shawver
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.isName = isName;
|
|
8
|
+
exports.key = key;
|
|
9
|
+
exports.quote = quote;
|
|
10
|
+
exports.print = print;
|
|
11
|
+
const PRECEDENCE = {
|
|
12
|
+
'=': 2,
|
|
13
|
+
'||': 10,
|
|
14
|
+
'??': 10,
|
|
15
|
+
'&&': 11,
|
|
16
|
+
'==': 20,
|
|
17
|
+
'!=': 20,
|
|
18
|
+
'~': 20,
|
|
19
|
+
'!~': 20,
|
|
20
|
+
'<': 20,
|
|
21
|
+
'<=': 20,
|
|
22
|
+
'>': 20,
|
|
23
|
+
'>=': 20,
|
|
24
|
+
in: 20,
|
|
25
|
+
'+': 30,
|
|
26
|
+
'-': 30,
|
|
27
|
+
'*': 40,
|
|
28
|
+
'/': 40,
|
|
29
|
+
'//': 40,
|
|
30
|
+
'%': 50,
|
|
31
|
+
'^': 50
|
|
32
|
+
};
|
|
33
|
+
const RIGHT_ASSOCIATIVE = new Set(['^']);
|
|
34
|
+
const LOGICAL = new Set(['||', '&&']);
|
|
35
|
+
// how tightly each form binds, mirroring the Parser's levels: `;`, then `=`
|
|
36
|
+
// and lambdas, then `?:`, then binary operators, then prefix operators
|
|
37
|
+
const SEQUENCE = 0;
|
|
38
|
+
const ASSIGNMENT = 1;
|
|
39
|
+
const CONDITIONAL = 2;
|
|
40
|
+
const CUSTOM_BINARY = 3;
|
|
41
|
+
const UNARY = 100;
|
|
42
|
+
const letter = String.raw `a-zA-Zа-яА-Я_À-ÖØ-öø-ÿ$`;
|
|
43
|
+
const IDENTIFIER = new RegExp(`^[${letter}][${letter}0-9]*$`);
|
|
44
|
+
const WORDS = new Set(['in', 'true', 'false', 'null']);
|
|
45
|
+
function isName(text) {
|
|
46
|
+
return IDENTIFIER.test(text) && !WORDS.has(text);
|
|
47
|
+
}
|
|
48
|
+
function key(name) {
|
|
49
|
+
return isName(name) ? `.${name}` : `[${quote(name)}]`;
|
|
50
|
+
}
|
|
51
|
+
function quote(text) {
|
|
52
|
+
return `'${text.replaceAll('\\', '\\\\').replaceAll("'", String.raw `\'`)}'`;
|
|
53
|
+
}
|
|
54
|
+
function precedenceOf(node) {
|
|
55
|
+
switch (node.type) {
|
|
56
|
+
case 'SequenceExpression': {
|
|
57
|
+
return SEQUENCE;
|
|
58
|
+
}
|
|
59
|
+
case 'AssignmentExpression':
|
|
60
|
+
case 'Lambda': {
|
|
61
|
+
return ASSIGNMENT;
|
|
62
|
+
}
|
|
63
|
+
case 'ConditionalExpression': {
|
|
64
|
+
// with no alternate, it ends only before `)`, `]`, `}`, `,`, `;` or the end
|
|
65
|
+
return node.alternate ? CONDITIONAL : SEQUENCE;
|
|
66
|
+
}
|
|
67
|
+
case 'BinaryExpression': {
|
|
68
|
+
return PRECEDENCE[node.operator] ?? CUSTOM_BINARY;
|
|
69
|
+
}
|
|
70
|
+
case 'UnaryExpression': {
|
|
71
|
+
return UNARY;
|
|
72
|
+
}
|
|
73
|
+
default: {
|
|
74
|
+
return Infinity;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function within(ast, least) {
|
|
79
|
+
const text = print(ast);
|
|
80
|
+
return precedenceOf(ast) < least ? `(${text})` : text;
|
|
81
|
+
}
|
|
82
|
+
function binaryOperand(parent, ast, left) {
|
|
83
|
+
const own = PRECEDENCE[parent.operator];
|
|
84
|
+
if (own === undefined) {
|
|
85
|
+
return within(ast, UNARY);
|
|
86
|
+
}
|
|
87
|
+
const child = ast;
|
|
88
|
+
const theirs = precedenceOf(child);
|
|
89
|
+
const chained = child.type === 'BinaryExpression' &&
|
|
90
|
+
RIGHT_ASSOCIATIVE.has(parent.operator) &&
|
|
91
|
+
RIGHT_ASSOCIATIVE.has(child.operator);
|
|
92
|
+
const mixesNullish = child.type === 'BinaryExpression' &&
|
|
93
|
+
((parent.operator === '??' && LOGICAL.has(child.operator)) ||
|
|
94
|
+
(child.operator === '??' && LOGICAL.has(parent.operator)));
|
|
95
|
+
const text = print(ast);
|
|
96
|
+
return theirs < own || (theirs === own && left === chained) || mixesNullish
|
|
97
|
+
? `(${text})`
|
|
98
|
+
: text;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Renders a tree back to expression text, for messages and suggestions. The
|
|
102
|
+
* text parses back to the same tree.
|
|
103
|
+
*/
|
|
104
|
+
function print(ast) {
|
|
105
|
+
const node = ast;
|
|
106
|
+
switch (node.type) {
|
|
107
|
+
case 'Literal': {
|
|
108
|
+
const { value } = node;
|
|
109
|
+
return typeof value === 'string'
|
|
110
|
+
? quote(value)
|
|
111
|
+
: Object.is(value, -0)
|
|
112
|
+
? '-0'
|
|
113
|
+
: String(value);
|
|
114
|
+
}
|
|
115
|
+
case 'Identifier': {
|
|
116
|
+
return node.from
|
|
117
|
+
? within(node.from, Infinity) + key(node.value)
|
|
118
|
+
: node.value;
|
|
119
|
+
}
|
|
120
|
+
case 'FilterExpression': {
|
|
121
|
+
return `${within(node.subject, Infinity)}[${within(node.expr, ASSIGNMENT)}]`;
|
|
122
|
+
}
|
|
123
|
+
case 'BinaryExpression': {
|
|
124
|
+
return `${binaryOperand(node, node.left, true)} ${node.operator} ${binaryOperand(node, node.right, false)}`;
|
|
125
|
+
}
|
|
126
|
+
case 'UnaryExpression': {
|
|
127
|
+
const { operator } = node;
|
|
128
|
+
const right = within(node.right, UNARY);
|
|
129
|
+
// the Lexer folds a minus before a digit into the number
|
|
130
|
+
if (operator === '-' && /^\d/.test(right)) {
|
|
131
|
+
return `-(${right})`;
|
|
132
|
+
}
|
|
133
|
+
return IDENTIFIER.test(operator)
|
|
134
|
+
? `${operator} ${right}`
|
|
135
|
+
: operator + right;
|
|
136
|
+
}
|
|
137
|
+
case 'ConditionalExpression': {
|
|
138
|
+
const test = within(node.test, CUSTOM_BINARY);
|
|
139
|
+
const consequent = node.consequent
|
|
140
|
+
? ` ${within(node.consequent, ASSIGNMENT)} `
|
|
141
|
+
: '';
|
|
142
|
+
const alternate = node.alternate
|
|
143
|
+
? ` ${within(node.alternate, ASSIGNMENT)}`
|
|
144
|
+
: '';
|
|
145
|
+
return `${test} ?${consequent}:${alternate}`;
|
|
146
|
+
}
|
|
147
|
+
case 'FunctionCall': {
|
|
148
|
+
const args = node.args.map((arg) => within(arg, ASSIGNMENT));
|
|
149
|
+
return `${node.name}(${args.join(', ')})`;
|
|
150
|
+
}
|
|
151
|
+
case 'ArrayLiteral': {
|
|
152
|
+
const items = node.value.map((item) => within(item, ASSIGNMENT));
|
|
153
|
+
return `[${items.join(', ')}]`;
|
|
154
|
+
}
|
|
155
|
+
case 'ObjectLiteral': {
|
|
156
|
+
const entries = Object.entries(node.value).map(([name, value]) => `${isName(name) ? name : quote(name)}: ${within(value, ASSIGNMENT)}`);
|
|
157
|
+
return `{${entries.join(', ')}}`;
|
|
158
|
+
}
|
|
159
|
+
case 'TemplateLiteral': {
|
|
160
|
+
const parts = node.parts.map((part) => part.type === 'static'
|
|
161
|
+
? part.value
|
|
162
|
+
.replaceAll('\\', '\\\\')
|
|
163
|
+
.replaceAll('`', '\\`')
|
|
164
|
+
.replaceAll('${', '\\${')
|
|
165
|
+
: `\${${print(part.value)}}`);
|
|
166
|
+
return `\`${parts.join('')}\``;
|
|
167
|
+
}
|
|
168
|
+
case 'SequenceExpression': {
|
|
169
|
+
const { expressions } = node;
|
|
170
|
+
const text = expressions
|
|
171
|
+
.map((expr) => within(expr, ASSIGNMENT))
|
|
172
|
+
.join('; ');
|
|
173
|
+
return expressions.length === 1 ? `${text};` : text;
|
|
174
|
+
}
|
|
175
|
+
case 'AssignmentExpression': {
|
|
176
|
+
return `${node.left.value} = ${within(node.right, ASSIGNMENT)}`;
|
|
177
|
+
}
|
|
178
|
+
case 'Lambda': {
|
|
179
|
+
const params = node.params.length === 1
|
|
180
|
+
? node.params[0]
|
|
181
|
+
: `(${node.params.join(', ')})`;
|
|
182
|
+
return `${params} => ${within(node.body, ASSIGNMENT)}`;
|
|
183
|
+
}
|
|
184
|
+
default: {
|
|
185
|
+
return '?';
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=print.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"print.js","sourceRoot":"","sources":["../src/print.ts"],"names":[],"mappings":"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"}
|
package/esm/Expression.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import Lexer from './Lexer.ts';
|
|
2
|
-
import type { AnalyzeOptions } from './analyze.ts';
|
|
3
|
-
import type { CheckOptions } from './check.ts';
|
|
4
2
|
import type { CompiledNode } from './evaluator/compile.ts';
|
|
5
3
|
import type { Grammar } from './grammar.ts';
|
|
6
4
|
import type { AstNode } from './types.ts';
|
|
@@ -42,15 +40,5 @@ declare class Expression {
|
|
|
42
40
|
get source(): string;
|
|
43
41
|
/** The parsed tree, or null for an expression with no tokens. */
|
|
44
42
|
get ast(): AstNode | null;
|
|
45
|
-
/**
|
|
46
|
-
* Lists what the expression reads from its context, without evaluating it.
|
|
47
|
-
* See {@link analyze}.
|
|
48
|
-
*/
|
|
49
|
-
analyze(options?: AnalyzeOptions): import("./analyze.ts").Analysis;
|
|
50
|
-
/**
|
|
51
|
-
* Checks the expression against a host's fields and functions, without
|
|
52
|
-
* evaluating it. See {@link check}.
|
|
53
|
-
*/
|
|
54
|
-
check(options?: CheckOptions): import("./check.ts").CheckResult;
|
|
55
43
|
}
|
|
56
44
|
export default Expression;
|
package/esm/Expression.js
CHANGED
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
* Copyright 2020 Tom Shawver
|
|
4
4
|
*/
|
|
5
5
|
import Lexer from "./Lexer.js";
|
|
6
|
-
import { analyze } from "./analyze.js";
|
|
7
|
-
import { check } from "./check.js";
|
|
8
6
|
import { compileAst } from "./evaluator/compile.js";
|
|
9
7
|
import Parser from "./parser/Parser.js";
|
|
10
8
|
class Expression {
|
|
@@ -73,20 +71,6 @@ class Expression {
|
|
|
73
71
|
}
|
|
74
72
|
return this._ast;
|
|
75
73
|
}
|
|
76
|
-
/**
|
|
77
|
-
* Lists what the expression reads from its context, without evaluating it.
|
|
78
|
-
* See {@link analyze}.
|
|
79
|
-
*/
|
|
80
|
-
analyze(options) {
|
|
81
|
-
return analyze(this.ast, options);
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Checks the expression against a host's fields and functions, without
|
|
85
|
-
* evaluating it. See {@link check}.
|
|
86
|
-
*/
|
|
87
|
-
check(options) {
|
|
88
|
-
return check(this.ast, options);
|
|
89
|
-
}
|
|
90
74
|
}
|
|
91
75
|
export default Expression;
|
|
92
76
|
//# sourceMappingURL=Expression.js.map
|
package/esm/Expression.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Expression.js","sourceRoot":"","sources":["../src/Expression.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,YAAY,CAAA;AAC9B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"Expression.js","sourceRoot":"","sources":["../src/Expression.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,YAAY,CAAA;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,MAAM,MAAM,oBAAoB,CAAA;AAMvC,MAAM,UAAU;IACd,QAAQ,CAAS;IACjB,QAAQ,CAAQ;IAChB;;oDAEgD;IAChD,IAAI,CAAgB;IACpB,MAAM,CAAO;IACb,SAAS,GAAG,KAAK,CAAA;IACjB,2EAA2E;IAC3E,GAAG,GAAwB,IAAI,CAAA;IAE/B;;;;;;;OAOG;IACH,YAAY,OAAgB,EAAE,OAAe,EAAE,KAAa;QAC1D,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAA;QACvB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAA;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,MAAM,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,CAAA;IAC3C,CAAC;IAED;;;;;OAKG;IACH,OAAO;QACL,IAAI,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACvE,uEAAuE;QACvE,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QAClE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;QACrB,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;;;;OAMG;IACH,IAAI,CAAC,OAAO,GAAG,EAAE;QACf,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;QACD,qEAAqE;QACrE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACd,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC1B,CAAC;IAED,yDAAyD;IACzD,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;IAED,iEAAiE;IACjE,IAAI,GAAG;QACL,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAA;IAClB,CAAC;CACF;AAED,eAAe,UAAU,CAAA"}
|
package/esm/check.d.ts
CHANGED
|
@@ -93,11 +93,6 @@ export interface CheckResult {
|
|
|
93
93
|
type: Type;
|
|
94
94
|
}
|
|
95
95
|
export declare const UNKNOWN: Type;
|
|
96
|
-
/**
|
|
97
|
-
* Renders a tree back to expression text, for messages and suggestions. The
|
|
98
|
-
* text parses back to the same tree.
|
|
99
|
-
*/
|
|
100
|
-
export declare function print(ast: AstNode): string;
|
|
101
96
|
/** The type of a row whose fields `schema` declares. */
|
|
102
97
|
export declare function recordOf(schema: readonly FieldSchema[]): Type;
|
|
103
98
|
export declare function union(a: Type, b: Type): Type;
|
package/esm/check.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Jexl
|
|
3
3
|
* Copyright 2020 Tom Shawver
|
|
4
4
|
*/
|
|
5
|
+
import { isName, key, print, quote } from "./print.js";
|
|
5
6
|
export const UNKNOWN = { kind: 'unknown' };
|
|
6
7
|
const UNDEFINED = { kind: 'undefined' };
|
|
7
8
|
const BOOLEAN = { kind: 'boolean' };
|
|
@@ -11,184 +12,6 @@ const MAX_VALUES = 64;
|
|
|
11
12
|
const ARITHMETIC = new Set(['-', '*', '/', '//', '%', '^']);
|
|
12
13
|
const ORDER = new Set(['<', '<=', '>', '>=']);
|
|
13
14
|
const EQUALITY = new Set(['==', '!=']);
|
|
14
|
-
const PRECEDENCE = {
|
|
15
|
-
'=': 2,
|
|
16
|
-
'||': 10,
|
|
17
|
-
'??': 10,
|
|
18
|
-
'&&': 11,
|
|
19
|
-
'==': 20,
|
|
20
|
-
'!=': 20,
|
|
21
|
-
'~': 20,
|
|
22
|
-
'!~': 20,
|
|
23
|
-
'<': 20,
|
|
24
|
-
'<=': 20,
|
|
25
|
-
'>': 20,
|
|
26
|
-
'>=': 20,
|
|
27
|
-
in: 20,
|
|
28
|
-
'+': 30,
|
|
29
|
-
'-': 30,
|
|
30
|
-
'*': 40,
|
|
31
|
-
'/': 40,
|
|
32
|
-
'//': 40,
|
|
33
|
-
'%': 50,
|
|
34
|
-
'^': 50
|
|
35
|
-
};
|
|
36
|
-
const RIGHT_ASSOCIATIVE = new Set(['^']);
|
|
37
|
-
const LOGICAL = new Set(['||', '&&']);
|
|
38
|
-
// how tightly each form binds, mirroring the Parser's levels: `;`, then `=`
|
|
39
|
-
// and lambdas, then `?:`, then binary operators, then prefix operators
|
|
40
|
-
const SEQUENCE = 0;
|
|
41
|
-
const ASSIGNMENT = 1;
|
|
42
|
-
const CONDITIONAL = 2;
|
|
43
|
-
const CUSTOM_BINARY = 3;
|
|
44
|
-
const UNARY = 100;
|
|
45
|
-
const letter = String.raw `a-zA-Zа-яА-Я_À-ÖØ-öø-ÿ$`;
|
|
46
|
-
const IDENTIFIER = new RegExp(`^[${letter}][${letter}0-9]*$`);
|
|
47
|
-
const WORDS = new Set(['in', 'true', 'false', 'null']);
|
|
48
|
-
function isName(text) {
|
|
49
|
-
return IDENTIFIER.test(text) && !WORDS.has(text);
|
|
50
|
-
}
|
|
51
|
-
function key(name) {
|
|
52
|
-
return isName(name) ? `.${name}` : `[${quote(name)}]`;
|
|
53
|
-
}
|
|
54
|
-
function quote(text) {
|
|
55
|
-
return `'${text.replaceAll('\\', '\\\\').replaceAll("'", String.raw `\'`)}'`;
|
|
56
|
-
}
|
|
57
|
-
function precedenceOf(node) {
|
|
58
|
-
switch (node.type) {
|
|
59
|
-
case 'SequenceExpression': {
|
|
60
|
-
return SEQUENCE;
|
|
61
|
-
}
|
|
62
|
-
case 'AssignmentExpression':
|
|
63
|
-
case 'Lambda': {
|
|
64
|
-
return ASSIGNMENT;
|
|
65
|
-
}
|
|
66
|
-
case 'ConditionalExpression': {
|
|
67
|
-
// with no alternate, it ends only before `)`, `]`, `}`, `,`, `;` or the end
|
|
68
|
-
return node.alternate ? CONDITIONAL : SEQUENCE;
|
|
69
|
-
}
|
|
70
|
-
case 'BinaryExpression': {
|
|
71
|
-
return PRECEDENCE[node.operator] ?? CUSTOM_BINARY;
|
|
72
|
-
}
|
|
73
|
-
case 'UnaryExpression': {
|
|
74
|
-
return UNARY;
|
|
75
|
-
}
|
|
76
|
-
default: {
|
|
77
|
-
return Infinity;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
function within(ast, least) {
|
|
82
|
-
const text = print(ast);
|
|
83
|
-
return precedenceOf(ast) < least ? `(${text})` : text;
|
|
84
|
-
}
|
|
85
|
-
function binaryOperand(parent, ast, left) {
|
|
86
|
-
const own = PRECEDENCE[parent.operator];
|
|
87
|
-
if (own === undefined) {
|
|
88
|
-
return within(ast, UNARY);
|
|
89
|
-
}
|
|
90
|
-
const child = ast;
|
|
91
|
-
const theirs = precedenceOf(child);
|
|
92
|
-
const chained = child.type === 'BinaryExpression' &&
|
|
93
|
-
RIGHT_ASSOCIATIVE.has(parent.operator) &&
|
|
94
|
-
RIGHT_ASSOCIATIVE.has(child.operator);
|
|
95
|
-
const mixesNullish = child.type === 'BinaryExpression' &&
|
|
96
|
-
((parent.operator === '??' && LOGICAL.has(child.operator)) ||
|
|
97
|
-
(child.operator === '??' && LOGICAL.has(parent.operator)));
|
|
98
|
-
const text = print(ast);
|
|
99
|
-
return theirs < own || (theirs === own && left === chained) || mixesNullish
|
|
100
|
-
? `(${text})`
|
|
101
|
-
: text;
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Renders a tree back to expression text, for messages and suggestions. The
|
|
105
|
-
* text parses back to the same tree.
|
|
106
|
-
*/
|
|
107
|
-
export function print(ast) {
|
|
108
|
-
const node = ast;
|
|
109
|
-
switch (node.type) {
|
|
110
|
-
case 'Literal': {
|
|
111
|
-
const { value } = node;
|
|
112
|
-
return typeof value === 'string'
|
|
113
|
-
? quote(value)
|
|
114
|
-
: Object.is(value, -0)
|
|
115
|
-
? '-0'
|
|
116
|
-
: String(value);
|
|
117
|
-
}
|
|
118
|
-
case 'Identifier': {
|
|
119
|
-
return node.from
|
|
120
|
-
? within(node.from, Infinity) + key(node.value)
|
|
121
|
-
: node.value;
|
|
122
|
-
}
|
|
123
|
-
case 'FilterExpression': {
|
|
124
|
-
return `${within(node.subject, Infinity)}[${within(node.expr, ASSIGNMENT)}]`;
|
|
125
|
-
}
|
|
126
|
-
case 'BinaryExpression': {
|
|
127
|
-
return `${binaryOperand(node, node.left, true)} ${node.operator} ${binaryOperand(node, node.right, false)}`;
|
|
128
|
-
}
|
|
129
|
-
case 'UnaryExpression': {
|
|
130
|
-
const { operator } = node;
|
|
131
|
-
const right = within(node.right, UNARY);
|
|
132
|
-
// the Lexer folds a minus before a digit into the number
|
|
133
|
-
if (operator === '-' && /^\d/.test(right)) {
|
|
134
|
-
return `-(${right})`;
|
|
135
|
-
}
|
|
136
|
-
return IDENTIFIER.test(operator)
|
|
137
|
-
? `${operator} ${right}`
|
|
138
|
-
: operator + right;
|
|
139
|
-
}
|
|
140
|
-
case 'ConditionalExpression': {
|
|
141
|
-
const test = within(node.test, CUSTOM_BINARY);
|
|
142
|
-
const consequent = node.consequent
|
|
143
|
-
? ` ${within(node.consequent, ASSIGNMENT)} `
|
|
144
|
-
: '';
|
|
145
|
-
const alternate = node.alternate
|
|
146
|
-
? ` ${within(node.alternate, ASSIGNMENT)}`
|
|
147
|
-
: '';
|
|
148
|
-
return `${test} ?${consequent}:${alternate}`;
|
|
149
|
-
}
|
|
150
|
-
case 'FunctionCall': {
|
|
151
|
-
const args = node.args.map((arg) => within(arg, ASSIGNMENT));
|
|
152
|
-
return `${node.name}(${args.join(', ')})`;
|
|
153
|
-
}
|
|
154
|
-
case 'ArrayLiteral': {
|
|
155
|
-
const items = node.value.map((item) => within(item, ASSIGNMENT));
|
|
156
|
-
return `[${items.join(', ')}]`;
|
|
157
|
-
}
|
|
158
|
-
case 'ObjectLiteral': {
|
|
159
|
-
const entries = Object.entries(node.value).map(([name, value]) => `${isName(name) ? name : quote(name)}: ${within(value, ASSIGNMENT)}`);
|
|
160
|
-
return `{${entries.join(', ')}}`;
|
|
161
|
-
}
|
|
162
|
-
case 'TemplateLiteral': {
|
|
163
|
-
const parts = node.parts.map((part) => part.type === 'static'
|
|
164
|
-
? part.value
|
|
165
|
-
.replaceAll('\\', '\\\\')
|
|
166
|
-
.replaceAll('`', '\\`')
|
|
167
|
-
.replaceAll('${', '\\${')
|
|
168
|
-
: `\${${print(part.value)}}`);
|
|
169
|
-
return `\`${parts.join('')}\``;
|
|
170
|
-
}
|
|
171
|
-
case 'SequenceExpression': {
|
|
172
|
-
const { expressions } = node;
|
|
173
|
-
const text = expressions
|
|
174
|
-
.map((expr) => within(expr, ASSIGNMENT))
|
|
175
|
-
.join('; ');
|
|
176
|
-
return expressions.length === 1 ? `${text};` : text;
|
|
177
|
-
}
|
|
178
|
-
case 'AssignmentExpression': {
|
|
179
|
-
return `${node.left.value} = ${within(node.right, ASSIGNMENT)}`;
|
|
180
|
-
}
|
|
181
|
-
case 'Lambda': {
|
|
182
|
-
const params = node.params.length === 1
|
|
183
|
-
? node.params[0]
|
|
184
|
-
: `(${node.params.join(', ')})`;
|
|
185
|
-
return `${params} => ${within(node.body, ASSIGNMENT)}`;
|
|
186
|
-
}
|
|
187
|
-
default: {
|
|
188
|
-
return '?';
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
15
|
function distance(a, b) {
|
|
193
16
|
let before = [];
|
|
194
17
|
let previous = Array.from({ length: b.length + 1 }, (_, i) => i);
|