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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pacc",
3
- "version": "8.9.3",
3
+ "version": "8.10.0",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true
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;
@@ -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
- function expect(expected) {
34
- if (token !== expected) {
35
- throw new Error(
36
- `unexpected '${token?.str || token}' expecting '${expected.str}'`,
37
- { cause: token }
38
- );
39
- }
40
- advance();
41
- }
42
-
43
- function nud(last, left) {
44
- switch (last) {
45
- case OPEN_ROUND: {
46
- const sequence = [];
47
-
48
- while (token !== CLOSE_ROUND) {
49
- sequence.push(expression(0));
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
- switch (last) {
108
- case OPEN_ROUND: {
109
- const args = [];
110
- while (token !== CLOSE_ROUND) {
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
- function expression(precedence) {
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
- import { binop } from "./ast.mjs";
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(str, precedence = 0, type, led) {
23
+ function createToken(
24
+ str,
25
+ precedence = 0,
26
+ type,
27
+ led = () => {},
28
+ nud = () => {}
29
+ ) {
20
30
  const token = { str, precedence, type };
21
- if (led) {
22
- token.led = led;
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
- export /** @type {Token} */ const PLUS = createToken(
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
- export /** @type {Token} */ const MINUS = createToken(
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 = createToken(
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 = createToken(
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 = createToken(
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 = createToken(
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 = createToken(
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 = createToken(
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 = createToken(
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 = createToken(
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("(", 40, "prefix");
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("[", 10, "prefix");
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 = createToken(
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 = createToken(
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("IDENTIFIER", 0);
134
- export /** @type {Token} */ const EOF = createToken("EOF", -1, "eof");
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
- export function binop(token: any, left: any, right: any, fallback: any): any;
2
- export function ASTBinop(token: any, left: any, right: any): any;
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 {