pacc 8.9.3 → 8.10.0
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/package.json +1 -1
- package/src/ast.mjs +15 -40
- package/src/expression.mjs +30 -126
- package/src/filter.mjs +4 -1
- package/src/tokens.mjs +153 -19
- package/types/ast.d.mts +4 -2
package/package.json
CHANGED
package/src/ast.mjs
CHANGED
|
@@ -1,48 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DOUBLE_BAR,
|
|
3
|
+
DOUBLE_AMPERSAND,
|
|
4
|
+
EQUAL,
|
|
5
|
+
NOT_EQUAL,
|
|
6
|
+
LESS,
|
|
7
|
+
LESS_EQUAL,
|
|
8
|
+
GREATER,
|
|
9
|
+
GREATER_EQUAL,
|
|
10
|
+
STAR,
|
|
11
|
+
DIVIDE,
|
|
12
|
+
PLUS,
|
|
13
|
+
MINUS
|
|
14
|
+
} from "./tokens.mjs";
|
|
15
|
+
|
|
1
16
|
/**
|
|
2
17
|
* @typedef {Object} AST
|
|
3
18
|
* @property {Function} [eval]
|
|
4
19
|
*/
|
|
5
20
|
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
8
|
-
* @param {Token} token
|
|
9
|
-
* @param {AST} left
|
|
10
|
-
* @param {AST} right
|
|
11
|
-
*
|
|
12
|
-
*/
|
|
13
|
-
function binopError(token, left, right) {
|
|
14
|
-
throw new Error(`Unexpected '${token.str || token}'`, { cause: token });
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function binop(token, left, right, fallback) {
|
|
18
|
-
if(token.led) { return token.led(left,right); }
|
|
19
|
-
|
|
20
|
-
return fallback(token, left, right);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function binopEval(node, current, context) {
|
|
24
|
-
return binop(
|
|
25
|
-
node.token,
|
|
26
|
-
node.left.eval ? node.left.eval(node.left, current, context) : node.left,
|
|
27
|
-
node.right.eval
|
|
28
|
-
? node.right.eval(node.right, current, context)
|
|
29
|
-
: node.right,
|
|
30
|
-
binopError
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function ASTBinop(token, left, right) {
|
|
35
|
-
if (!left.eval && !right.eval) {
|
|
36
|
-
return binop(token, left, right, binopError);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
eval: binopEval,
|
|
41
|
-
token,
|
|
42
|
-
left,
|
|
43
|
-
right
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
21
|
|
|
47
22
|
export function pathEval(node, current, context) {
|
|
48
23
|
let collection = false;
|
package/src/expression.mjs
CHANGED
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
tokens,
|
|
3
|
-
DOT,
|
|
4
|
-
OPEN_ROUND,
|
|
5
|
-
CLOSE_ROUND,
|
|
6
|
-
OPEN_BRACKET,
|
|
7
|
-
CLOSE_BRACKET,
|
|
8
|
-
IDENTIFIER,
|
|
9
|
-
COMMA,
|
|
10
|
-
EOF
|
|
11
|
-
} from "./tokens.mjs";
|
|
12
|
-
import { pathEval, functionEval, ASTTrue, ASTBinop } from "./ast.mjs";
|
|
1
|
+
import { tokens, EOF } from "./tokens.mjs";
|
|
13
2
|
|
|
14
3
|
export function parseOnly(input, context = {}) {
|
|
15
4
|
context.getGlobal ||= a => globals[a];
|
|
@@ -30,129 +19,44 @@ export function parseOnly(input, context = {}) {
|
|
|
30
19
|
}
|
|
31
20
|
}
|
|
32
21
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (token === COMMA) {
|
|
51
|
-
advance();
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
expect(CLOSE_ROUND);
|
|
55
|
-
|
|
56
|
-
// TODO always a sequence ?
|
|
57
|
-
return sequence.length > 1 ? sequence : sequence[0];
|
|
58
|
-
}
|
|
59
|
-
case OPEN_BRACKET: {
|
|
60
|
-
if (token === CLOSE_BRACKET) {
|
|
61
|
-
advance();
|
|
62
|
-
return ASTTrue;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const node = expression(0);
|
|
66
|
-
expect(CLOSE_BRACKET);
|
|
67
|
-
|
|
68
|
-
switch (typeof node) {
|
|
69
|
-
case "string":
|
|
70
|
-
case "number":
|
|
71
|
-
return { eval: pathEval, path: [node] };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return node;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
case IDENTIFIER:
|
|
78
|
-
return { eval: pathEval, path: [value] };
|
|
79
|
-
|
|
80
|
-
case EOF:
|
|
81
|
-
throw new Error("unexpected EOF");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (last.type === "prefix") {
|
|
85
|
-
return { token: last, left, right: expression(last.precedence) };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return last;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function led(last, left) {
|
|
92
|
-
switch (last.type) {
|
|
93
|
-
case "infixr":
|
|
94
|
-
return ASTBinop(last, left, expression(last.precedence - 1));
|
|
95
|
-
|
|
96
|
-
case "infix": {
|
|
97
|
-
const right = expression(last.precedence);
|
|
98
|
-
|
|
99
|
-
if (last === DOT) {
|
|
100
|
-
return last.led(left, right);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return ASTBinop(last, left, right);
|
|
22
|
+
const parser = {
|
|
23
|
+
get node() {
|
|
24
|
+
return node;
|
|
25
|
+
},
|
|
26
|
+
get token() {
|
|
27
|
+
return token;
|
|
28
|
+
},
|
|
29
|
+
get value() {
|
|
30
|
+
return value;
|
|
31
|
+
},
|
|
32
|
+
advance,
|
|
33
|
+
expect(expected) {
|
|
34
|
+
if (token !== expected) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`unexpected '${token?.str || token}' expecting '${expected.str}'`,
|
|
37
|
+
{ cause: token }
|
|
38
|
+
);
|
|
104
39
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
args.push(expression(0));
|
|
112
|
-
if (token === COMMA) {
|
|
113
|
-
advance();
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
left.args = args;
|
|
117
|
-
left.eval = functionEval;
|
|
40
|
+
advance();
|
|
41
|
+
},
|
|
42
|
+
expression(precedence) {
|
|
43
|
+
const last = token;
|
|
44
|
+
advance();
|
|
45
|
+
node = last.nud ? last.nud(parser) : last;
|
|
118
46
|
|
|
47
|
+
while (token.precedence > precedence) {
|
|
48
|
+
const last = token;
|
|
119
49
|
advance();
|
|
120
|
-
|
|
121
|
-
return left;
|
|
50
|
+
node = last.led(parser, node);
|
|
122
51
|
}
|
|
123
|
-
case OPEN_BRACKET: {
|
|
124
|
-
if (token === CLOSE_BRACKET) {
|
|
125
|
-
advance();
|
|
126
|
-
left.path.push(ASTTrue);
|
|
127
|
-
} else {
|
|
128
|
-
const predicate = expression(0);
|
|
129
|
-
expect(CLOSE_BRACKET);
|
|
130
|
-
left.path.push(predicate);
|
|
131
|
-
}
|
|
132
|
-
return left;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return { token };
|
|
137
|
-
}
|
|
138
52
|
|
|
139
|
-
|
|
140
|
-
const last = token;
|
|
141
|
-
advance();
|
|
142
|
-
node = nud(last, node);
|
|
143
|
-
|
|
144
|
-
while (token.precedence > precedence) {
|
|
145
|
-
const last = token;
|
|
146
|
-
advance();
|
|
147
|
-
node = led(last, node);
|
|
53
|
+
return node;
|
|
148
54
|
}
|
|
149
|
-
|
|
150
|
-
return node;
|
|
151
|
-
}
|
|
55
|
+
};
|
|
152
56
|
|
|
153
57
|
advance();
|
|
154
58
|
|
|
155
|
-
return expression(token.precedence ?? 0);
|
|
59
|
+
return parser.expression(token.precedence ?? 0);
|
|
156
60
|
}
|
|
157
61
|
|
|
158
62
|
export function parse(input, context) {
|
package/src/filter.mjs
CHANGED
|
@@ -7,7 +7,10 @@ import {
|
|
|
7
7
|
GREATER,
|
|
8
8
|
GREATER_EQUAL
|
|
9
9
|
} from "pacc";
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
function binop(token, left, right) {
|
|
12
|
+
return token.binop(left, right);
|
|
13
|
+
}
|
|
11
14
|
|
|
12
15
|
function dateOp(op, value, against) {
|
|
13
16
|
return binop(op, value.getTime(), against.getTime());
|
package/src/tokens.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { pathEval, ASTTrue, functionEval } from "./ast.mjs";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Token lookup
|
|
3
5
|
*/
|
|
@@ -8,6 +10,8 @@ const lookup = {};
|
|
|
8
10
|
* @property {string} str
|
|
9
11
|
*/
|
|
10
12
|
|
|
13
|
+
function infix() {}
|
|
14
|
+
|
|
11
15
|
/**
|
|
12
16
|
*
|
|
13
17
|
* @param {string} str
|
|
@@ -16,16 +20,61 @@ const lookup = {};
|
|
|
16
20
|
* @param {Function} [led]
|
|
17
21
|
* @returns {Token}
|
|
18
22
|
*/
|
|
19
|
-
function createToken(
|
|
23
|
+
function createToken(
|
|
24
|
+
str,
|
|
25
|
+
precedence = 0,
|
|
26
|
+
type,
|
|
27
|
+
led = () => {},
|
|
28
|
+
nud = () => {}
|
|
29
|
+
) {
|
|
20
30
|
const token = { str, precedence, type };
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
|
|
32
|
+
switch (type) {
|
|
33
|
+
case "infix":
|
|
34
|
+
token.led = (parser, left) =>
|
|
35
|
+
led(left, parser.expression(token.precedence));
|
|
36
|
+
break;
|
|
37
|
+
case "infixr":
|
|
38
|
+
token.led = (parser, left) =>
|
|
39
|
+
led(left, parser.expression(token.precedence - 1));
|
|
40
|
+
break;
|
|
41
|
+
default:
|
|
42
|
+
token.led = led;
|
|
23
43
|
}
|
|
44
|
+
|
|
45
|
+
token.nud = nud;
|
|
24
46
|
lookup[str] = [token];
|
|
25
47
|
return token;
|
|
26
48
|
}
|
|
27
49
|
|
|
28
|
-
|
|
50
|
+
function createBinopToken(str, precedence, type, binop) {
|
|
51
|
+
const token = createToken(str, precedence, type, (left, right) => {
|
|
52
|
+
if (!left.eval && !right.eval) {
|
|
53
|
+
return binop(left, right);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
eval: (node, current, context) =>
|
|
58
|
+
binop(
|
|
59
|
+
node.left.eval
|
|
60
|
+
? node.left.eval(node.left, current, context)
|
|
61
|
+
: node.left,
|
|
62
|
+
node.right.eval
|
|
63
|
+
? node.right.eval(node.right, current, context)
|
|
64
|
+
: node.right
|
|
65
|
+
),
|
|
66
|
+
token,
|
|
67
|
+
left,
|
|
68
|
+
right
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
token.binop = binop;
|
|
73
|
+
|
|
74
|
+
return token;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export /** @type {Token} */ const PLUS = createBinopToken(
|
|
29
78
|
"+",
|
|
30
79
|
50,
|
|
31
80
|
"infix",
|
|
@@ -38,64 +87,132 @@ export /** @type {Token} */ const PLUS = createToken(
|
|
|
38
87
|
return left + right;
|
|
39
88
|
}
|
|
40
89
|
);
|
|
41
|
-
|
|
90
|
+
|
|
91
|
+
export /** @type {Token} */ const MINUS = createBinopToken(
|
|
42
92
|
"-",
|
|
43
93
|
50,
|
|
44
94
|
"infix",
|
|
45
95
|
(left, right) => left - right
|
|
46
96
|
);
|
|
47
|
-
export /** @type {Token} */ const STAR =
|
|
97
|
+
export /** @type {Token} */ const STAR = createBinopToken(
|
|
48
98
|
"*",
|
|
49
99
|
60,
|
|
50
100
|
"infix",
|
|
51
101
|
(left, right) => left * right
|
|
52
102
|
);
|
|
53
|
-
export /** @type {Token} */ const DIVIDE =
|
|
103
|
+
export /** @type {Token} */ const DIVIDE = createBinopToken(
|
|
54
104
|
"/",
|
|
55
105
|
60,
|
|
56
106
|
"infix",
|
|
57
107
|
(left, right) => left / right
|
|
58
108
|
);
|
|
59
109
|
export /** @type {Token} */ const NOT = createToken("!");
|
|
60
|
-
export /** @type {Token} */ const NOT_EQUAL =
|
|
110
|
+
export /** @type {Token} */ const NOT_EQUAL = createBinopToken(
|
|
61
111
|
"!=",
|
|
62
112
|
40,
|
|
63
113
|
"infixr",
|
|
64
114
|
(left, right) => left != right
|
|
65
115
|
);
|
|
66
|
-
export /** @type {Token} */ const EQUAL =
|
|
116
|
+
export /** @type {Token} */ const EQUAL = createBinopToken(
|
|
67
117
|
"=",
|
|
68
118
|
40,
|
|
69
119
|
"infixr",
|
|
70
120
|
(left, right) => left == right
|
|
71
121
|
);
|
|
72
|
-
export /** @type {Token} */ const GREATER =
|
|
122
|
+
export /** @type {Token} */ const GREATER = createBinopToken(
|
|
73
123
|
">",
|
|
74
124
|
40,
|
|
75
125
|
"infixr",
|
|
76
126
|
(left, right) => left > right
|
|
77
127
|
);
|
|
78
|
-
export /** @type {Token} */ const GREATER_EQUAL =
|
|
128
|
+
export /** @type {Token} */ const GREATER_EQUAL = createBinopToken(
|
|
79
129
|
">=",
|
|
80
130
|
40,
|
|
81
131
|
"infixr",
|
|
82
132
|
(left, right) => left >= right
|
|
83
133
|
);
|
|
84
|
-
export /** @type {Token} */ const LESS =
|
|
134
|
+
export /** @type {Token} */ const LESS = createBinopToken(
|
|
85
135
|
"<",
|
|
86
136
|
40,
|
|
87
137
|
"infixr",
|
|
88
138
|
(left, right) => left < right
|
|
89
139
|
);
|
|
90
|
-
export /** @type {Token} */ const LESS_EQUAL =
|
|
140
|
+
export /** @type {Token} */ const LESS_EQUAL = createBinopToken(
|
|
91
141
|
"<=",
|
|
92
142
|
40,
|
|
93
143
|
"infixr",
|
|
94
144
|
(left, right) => left <= right
|
|
95
145
|
);
|
|
96
|
-
export /** @type {Token} */ const OPEN_ROUND = createToken(
|
|
146
|
+
export /** @type {Token} */ const OPEN_ROUND = createToken(
|
|
147
|
+
"(",
|
|
148
|
+
40,
|
|
149
|
+
"prefix",
|
|
150
|
+
(parser, left) => {
|
|
151
|
+
const args = [];
|
|
152
|
+
while (parser.token !== CLOSE_ROUND) {
|
|
153
|
+
args.push(parser.expression(0));
|
|
154
|
+
if (parser.token === COMMA) {
|
|
155
|
+
parser.advance();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
left.args = args;
|
|
159
|
+
left.eval = functionEval;
|
|
160
|
+
|
|
161
|
+
parser.advance();
|
|
162
|
+
|
|
163
|
+
return left;
|
|
164
|
+
},
|
|
165
|
+
parser => {
|
|
166
|
+
const sequence = [];
|
|
167
|
+
|
|
168
|
+
while (parser.token !== CLOSE_ROUND) {
|
|
169
|
+
sequence.push(parser.expression(0));
|
|
170
|
+
if (parser.token === COMMA) {
|
|
171
|
+
parser.advance();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
parser.expect(CLOSE_ROUND);
|
|
175
|
+
|
|
176
|
+
// TODO always a sequence ?
|
|
177
|
+
return sequence.length > 1 ? sequence : sequence[0];
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
|
|
97
181
|
export /** @type {Token} */ const CLOSE_ROUND = createToken(")", 0, "infix");
|
|
98
|
-
export /** @type {Token} */ const OPEN_BRACKET = createToken(
|
|
182
|
+
export /** @type {Token} */ const OPEN_BRACKET = createToken(
|
|
183
|
+
"[",
|
|
184
|
+
10,
|
|
185
|
+
"prefix",
|
|
186
|
+
(parser, left) => {
|
|
187
|
+
if (parser.token === CLOSE_BRACKET) {
|
|
188
|
+
parser.advance();
|
|
189
|
+
left.path.push(ASTTrue);
|
|
190
|
+
} else {
|
|
191
|
+
const predicate = parser.expression(0);
|
|
192
|
+
parser.expect(CLOSE_BRACKET);
|
|
193
|
+
left.path.push(predicate);
|
|
194
|
+
}
|
|
195
|
+
return left;
|
|
196
|
+
},
|
|
197
|
+
parser => {
|
|
198
|
+
if (parser.token === CLOSE_BRACKET) {
|
|
199
|
+
parser.advance();
|
|
200
|
+
return ASTTrue;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const node = parser.expression(0);
|
|
204
|
+
parser.expect(CLOSE_BRACKET);
|
|
205
|
+
|
|
206
|
+
switch (typeof node) {
|
|
207
|
+
case "string":
|
|
208
|
+
case "number":
|
|
209
|
+
return { eval: pathEval, path: [node] };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return node;
|
|
213
|
+
}
|
|
214
|
+
);
|
|
215
|
+
|
|
99
216
|
export /** @type {Token} */ const CLOSE_BRACKET = createToken("]", 0, "infix");
|
|
100
217
|
export /** @type {Token} */ const OPEN_CURLY = createToken("{");
|
|
101
218
|
export /** @type {Token} */ const CLOSE_CURLY = createToken("}");
|
|
@@ -117,21 +234,38 @@ export /** @type {Token} */ const DOT = createToken(
|
|
|
117
234
|
}
|
|
118
235
|
);
|
|
119
236
|
export /** @type {Token} */ const AMPERSAND = createToken("&");
|
|
120
|
-
export /** @type {Token} */ const DOUBLE_AMPERSAND =
|
|
237
|
+
export /** @type {Token} */ const DOUBLE_AMPERSAND = createBinopToken(
|
|
121
238
|
"&&",
|
|
122
239
|
30,
|
|
123
240
|
"infixr",
|
|
124
241
|
(left, right) => left && right
|
|
125
242
|
);
|
|
126
243
|
export /** @type {Token} */ const BAR = createToken("|");
|
|
127
|
-
export /** @type {Token} */ const DOUBLE_BAR =
|
|
244
|
+
export /** @type {Token} */ const DOUBLE_BAR = createBinopToken(
|
|
128
245
|
"||",
|
|
129
246
|
30,
|
|
130
247
|
"infixr",
|
|
131
248
|
(left, right) => left || right
|
|
132
249
|
);
|
|
133
|
-
export /** @type {Token} */ const IDENTIFIER = createToken(
|
|
134
|
-
|
|
250
|
+
export /** @type {Token} */ const IDENTIFIER = createToken(
|
|
251
|
+
"IDENTIFIER",
|
|
252
|
+
0,
|
|
253
|
+
undefined,
|
|
254
|
+
undefined,
|
|
255
|
+
parser => {
|
|
256
|
+
return { eval: pathEval, path: [parser.value] };
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
export /** @type {Token} */ const EOF = createToken(
|
|
261
|
+
"EOF",
|
|
262
|
+
-1,
|
|
263
|
+
"eof",
|
|
264
|
+
undefined,
|
|
265
|
+
parser => {
|
|
266
|
+
throw new Error("unexpected EOF");
|
|
267
|
+
}
|
|
268
|
+
);
|
|
135
269
|
|
|
136
270
|
export const keywords = {
|
|
137
271
|
true: [true],
|
package/types/ast.d.mts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} AST
|
|
3
|
+
* @property {Function} [eval]
|
|
4
|
+
*/
|
|
3
5
|
export function pathEval(node: any, current: any, context: any): any;
|
|
4
6
|
export function functionEval(node: any, current: any, context: any): any;
|
|
5
7
|
export namespace ASTTrue {
|