fable 3.1.71 → 3.1.73
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/docs/README.md +30 -6
- package/docs/_brand.json +18 -0
- package/docs/_playground.json +10 -0
- package/docs/_sidebar.md +2 -0
- package/docs/_version.json +3 -3
- package/docs/architecture.md +201 -39
- package/docs/index.html +6 -7
- package/docs/pict-docuserve.min.js +91 -0
- package/docs/pict-docuserve.min.js.map +1 -0
- package/docs/playground.md +38 -0
- package/docs/retold-catalog.json +1 -1
- package/docs/retold-keyword-index.json +8721 -8105
- package/docs/services/README.md +26 -9
- package/docs/services/anticipate.md +104 -40
- package/docs/services/csv-parser.md +63 -35
- package/docs/services/data-format.md +154 -49
- package/docs/services/data-generation.md +77 -16
- package/docs/services/dates.md +103 -36
- package/docs/services/environment-data.md +13 -2
- package/docs/services/expression-parser.md +280 -68
- package/docs/services/file-persistence.md +142 -150
- package/docs/services/logging.md +93 -37
- package/docs/services/logic.md +70 -22
- package/docs/services/manifest.md +114 -26
- package/docs/services/math.md +168 -63
- package/docs/services/meta-template.md +312 -158
- package/docs/services/object-cache.md +94 -11
- package/docs/services/operation.md +68 -6
- package/docs/services/progress-time.md +74 -13
- package/docs/services/progress-tracker-set.md +101 -3
- package/docs/services/rest-client.md +136 -104
- package/docs/services/settings-manager.md +133 -40
- package/docs/services/template.md +71 -22
- package/docs/services/utility.md +121 -29
- package/docs/services/uuid.md +58 -10
- package/package.json +4 -4
- package/source/services/Fable-Service-RestClient.js +204 -7
- package/test/RestClient_test.js +342 -0
- package/.claude/settings.local.json +0 -8
|
@@ -5,8 +5,12 @@ The ExpressionParser service provides mathematical expression parsing and evalua
|
|
|
5
5
|
## Access
|
|
6
6
|
|
|
7
7
|
```javascript
|
|
8
|
+
const libFable = require('fable');
|
|
9
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
10
|
+
|
|
8
11
|
// On-demand service - instantiate when needed
|
|
9
12
|
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
13
|
+
console.log('parser:', typeof parser);
|
|
10
14
|
```
|
|
11
15
|
|
|
12
16
|
## Basic Usage
|
|
@@ -16,16 +20,26 @@ const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
|
16
20
|
The primary method is `solve()`, which parses and evaluates expressions:
|
|
17
21
|
|
|
18
22
|
```javascript
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
parser.
|
|
22
|
-
|
|
23
|
+
const libFable = require('fable');
|
|
24
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
25
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
26
|
+
|
|
27
|
+
console.log(parser.solve('1 + 1')); // Returns '2'
|
|
28
|
+
console.log(parser.solve('10 * 2')); // Returns '20'
|
|
29
|
+
console.log(parser.solve('100 / 4')); // Returns '25'
|
|
30
|
+
console.log(parser.solve('2 ^ 10')); // Returns '1024'
|
|
23
31
|
```
|
|
24
32
|
|
|
25
33
|
### Full Signature
|
|
26
34
|
|
|
27
35
|
```javascript
|
|
28
|
-
|
|
36
|
+
const libFable = require('fable');
|
|
37
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
38
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
39
|
+
|
|
40
|
+
// Signature shape — see the runnable examples below for real invocations.
|
|
41
|
+
console.log('solve signature: (expression, dataObject, resultObject, manifest, destinationObject) =>',
|
|
42
|
+
typeof parser.solve);
|
|
29
43
|
```
|
|
30
44
|
|
|
31
45
|
- **expression** -- the expression string to evaluate
|
|
@@ -39,8 +53,13 @@ parser.solve(expression, dataObject, resultObject, manifest, destinationObject);
|
|
|
39
53
|
Use `=` to assign a result to a named destination:
|
|
40
54
|
|
|
41
55
|
```javascript
|
|
56
|
+
const libFable = require('fable');
|
|
57
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
58
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
59
|
+
|
|
42
60
|
const dest = {};
|
|
43
61
|
parser.solve('Area = 5 * 10', {}, {}, false, dest);
|
|
62
|
+
console.log(dest);
|
|
44
63
|
// dest.Area === '50'
|
|
45
64
|
```
|
|
46
65
|
|
|
@@ -49,8 +68,19 @@ parser.solve('Area = 5 * 10', {}, {}, false, dest);
|
|
|
49
68
|
Use `?=` to only assign if the destination property doesn't already exist:
|
|
50
69
|
|
|
51
70
|
```javascript
|
|
71
|
+
const libFable = require('fable');
|
|
72
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
73
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
74
|
+
|
|
75
|
+
fable.AppData = { Students: ['Alice', 'Bob'] };
|
|
76
|
+
const data = fable;
|
|
77
|
+
const dest = {};
|
|
52
78
|
parser.solve('Name ?= GETVALUE("AppData.Students[0]")', data, {}, false, dest);
|
|
53
|
-
|
|
79
|
+
console.log('dest.Name (first run):', dest.Name);
|
|
80
|
+
|
|
81
|
+
// Second run with Name already set — ?= leaves it alone:
|
|
82
|
+
parser.solve('Name ?= "OverwriteAttempt"', data, {}, false, dest);
|
|
83
|
+
console.log('dest.Name (after ?= retry):', dest.Name);
|
|
54
84
|
```
|
|
55
85
|
|
|
56
86
|
## Operators
|
|
@@ -80,14 +110,19 @@ Comparison operators evaluate to `'1'` (true) or `'0'` (false). They bind looser
|
|
|
80
110
|
| `!=` | Not equal | `5 != 3` -> `'1'` |
|
|
81
111
|
|
|
82
112
|
```javascript
|
|
113
|
+
const libFable = require('fable');
|
|
114
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
115
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
116
|
+
|
|
117
|
+
const dest = {};
|
|
83
118
|
parser.solve('Result = 5 > 3', {}, {}, false, dest);
|
|
84
|
-
|
|
119
|
+
console.log("after '5 > 3':", dest.Result);
|
|
85
120
|
|
|
86
121
|
parser.solve('Result = 2 + 3 > 1 + 2', {}, {}, false, dest);
|
|
87
|
-
|
|
122
|
+
console.log("after '2 + 3 > 1 + 2':", dest.Result);
|
|
88
123
|
|
|
89
124
|
parser.solve('Result = Height > Width', { Height: 10, Width: 5 }, {}, false, dest);
|
|
90
|
-
|
|
125
|
+
console.log("after 'Height > Width':", dest.Result);
|
|
91
126
|
```
|
|
92
127
|
|
|
93
128
|
### Ternary Operator
|
|
@@ -101,25 +136,31 @@ condition ? trueValue :: falseValue
|
|
|
101
136
|
The condition is typically a comparison expression. If it evaluates to a truthy value (non-zero, non-empty), the true branch is returned; otherwise the false branch is returned.
|
|
102
137
|
|
|
103
138
|
```javascript
|
|
139
|
+
const libFable = require('fable');
|
|
140
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
141
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
142
|
+
|
|
143
|
+
const dest = {};
|
|
144
|
+
|
|
104
145
|
parser.solve('Result = 5 > 3 ? 100 :: 200', {}, {}, false, dest);
|
|
105
|
-
|
|
146
|
+
console.log('5>3 ? 100 :: 200 =>', dest.Result);
|
|
106
147
|
|
|
107
148
|
parser.solve('Result = 3 > 5 ? 100 :: 200', {}, {}, false, dest);
|
|
108
|
-
|
|
149
|
+
console.log('3>5 ? 100 :: 200 =>', dest.Result);
|
|
109
150
|
|
|
110
151
|
parser.solve('Winner = Height > Width ? Height :: Width',
|
|
111
152
|
{ Height: 10, Width: 5 }, {}, false, dest);
|
|
112
|
-
|
|
153
|
+
console.log('Winner =', dest.Winner);
|
|
113
154
|
|
|
114
155
|
// Arithmetic in branches
|
|
115
156
|
parser.solve('Result = A > B ? A + 1 :: B + 1',
|
|
116
157
|
{ A: 10, B: 5 }, {}, false, dest);
|
|
117
|
-
|
|
158
|
+
console.log('Branch arithmetic =', dest.Result);
|
|
118
159
|
|
|
119
160
|
// Nested ternary (use parentheses for inner ternary)
|
|
120
161
|
parser.solve('Result = A > 0 ? (B > 0 ? 1 :: 2) :: 3',
|
|
121
162
|
{ A: 1, B: 1 }, {}, false, dest);
|
|
122
|
-
|
|
163
|
+
console.log('Nested ternary =', dest.Result);
|
|
123
164
|
```
|
|
124
165
|
|
|
125
166
|
The ternary operator uses `::` (double colon) instead of `:` for the false branch separator because `:` is already used as the Expression Begin token in directives like `MAP`, `SERIES`, etc.
|
|
@@ -129,19 +170,28 @@ Internally, `condition ? trueValue :: falseValue` is desugared to `ternary((cond
|
|
|
129
170
|
### Order of Operations
|
|
130
171
|
|
|
131
172
|
```javascript
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
parser.
|
|
173
|
+
const libFable = require('fable');
|
|
174
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
175
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
176
|
+
|
|
177
|
+
console.log(parser.solve('5 + 2 * 10')); // Returns '25'
|
|
178
|
+
console.log(parser.solve('3.5 + 5 + 10 * 10 / 5')); // Returns '28.5'
|
|
179
|
+
console.log(parser.solve('(100 - 10)')); // Returns '90'
|
|
135
180
|
```
|
|
136
181
|
|
|
137
182
|
### Negative Numbers
|
|
138
183
|
|
|
139
184
|
```javascript
|
|
185
|
+
const libFable = require('fable');
|
|
186
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
187
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
188
|
+
|
|
189
|
+
const dest = {};
|
|
140
190
|
parser.solve('Value = -3', {}, {}, false, dest);
|
|
141
|
-
|
|
191
|
+
console.log('dest.Value:', dest.Value);
|
|
142
192
|
|
|
143
193
|
parser.solve('Value2 = (4 + -3)', {}, {}, false, dest);
|
|
144
|
-
|
|
194
|
+
console.log('dest.Value2:', dest.Value2);
|
|
145
195
|
```
|
|
146
196
|
|
|
147
197
|
## Variables
|
|
@@ -149,10 +199,15 @@ parser.solve('Value2 = (4 + -3)', {}, {}, false, dest);
|
|
|
149
199
|
### Using Data Objects
|
|
150
200
|
|
|
151
201
|
```javascript
|
|
152
|
-
|
|
202
|
+
const libFable = require('fable');
|
|
203
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
204
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
205
|
+
|
|
206
|
+
console.log(parser.solve('X * Y * Z', { X: 5, Y: 3.1, Z: 75 }));
|
|
153
207
|
|
|
208
|
+
const dest = {};
|
|
154
209
|
parser.solve('Area = X * Y * Z', { X: 5.867, Y: 3.1, Z: 75 }, {}, false, dest);
|
|
155
|
-
|
|
210
|
+
console.log('dest.Area:', dest.Area);
|
|
156
211
|
```
|
|
157
212
|
|
|
158
213
|
### Accessing Fable AppData
|
|
@@ -160,13 +215,19 @@ parser.solve('Area = X * Y * Z', { X: 5.867, Y: 3.1, Z: 75 }, {}, false, dest);
|
|
|
160
215
|
Use `GETVALUE()` to read from `fable.AppData`:
|
|
161
216
|
|
|
162
217
|
```javascript
|
|
218
|
+
const libFable = require('fable');
|
|
219
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
220
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
221
|
+
|
|
222
|
+
const dest = {};
|
|
223
|
+
|
|
163
224
|
fable.AppData = { Pit: 'Bottomless' };
|
|
164
|
-
parser.solve('PitSize = getvalue("AppData.Pit")',
|
|
165
|
-
|
|
225
|
+
parser.solve('PitSize = getvalue("AppData.Pit")', fable, {}, false, dest);
|
|
226
|
+
console.log('PitSize:', dest.PitSize);
|
|
166
227
|
|
|
167
228
|
fable.AppData = { Students: ['Kim', 'Jim', 'Joan Jett', 'Tank Girl'] };
|
|
168
|
-
parser.solve('Name = GETVALUE("AppData.Students[1]")',
|
|
169
|
-
|
|
229
|
+
parser.solve('Name = GETVALUE("AppData.Students[1]")', fable, {}, false, dest);
|
|
230
|
+
console.log('Name:', dest.Name);
|
|
170
231
|
```
|
|
171
232
|
|
|
172
233
|
## Built-in Functions
|
|
@@ -174,40 +235,61 @@ parser.solve('Name = GETVALUE("AppData.Students[1]")', {}, {}, false, dest);
|
|
|
174
235
|
### Math Functions
|
|
175
236
|
|
|
176
237
|
```javascript
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
parser
|
|
238
|
+
const libFable = require('fable');
|
|
239
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
240
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
241
|
+
|
|
242
|
+
console.log(parser.solve('sqrt(16)'));
|
|
243
|
+
console.log(parser.solve('sin(rad(60))'));
|
|
244
|
+
console.log(parser.solve('1.5 * sqrt(8 * 2.423782342^2) / 10'));
|
|
180
245
|
```
|
|
181
246
|
|
|
182
247
|
### Rounding
|
|
183
248
|
|
|
184
249
|
```javascript
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
parser.
|
|
250
|
+
const libFable = require('fable');
|
|
251
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
252
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
253
|
+
|
|
254
|
+
console.log(parser.solve('ROUND(X * Y * Z)', { X: 5.867, Y: 3.1, Z: 75 }));
|
|
255
|
+
console.log(parser.solve('ROUND(X * Y * Z, 2)', { X: 5.867, Y: 3.1, Z: 75 }));
|
|
256
|
+
console.log(parser.solve('ROUND(X * Y * Z, 3, 3)', { X: 5.867, Y: 3.5, Z: 75.248923423 }));
|
|
188
257
|
```
|
|
189
258
|
|
|
190
259
|
### Aggregate Functions (on Arrays)
|
|
191
260
|
|
|
192
261
|
```javascript
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
parser.
|
|
196
|
-
|
|
197
|
-
parser.solve('
|
|
198
|
-
parser.solve('
|
|
262
|
+
const libFable = require('fable');
|
|
263
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
264
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
265
|
+
|
|
266
|
+
console.log(parser.solve('SUM(ItemCosts)', { ItemCosts: [100, 200, 50, 45, 5] }));
|
|
267
|
+
console.log(parser.solve('MEAN(ItemCosts)', { ItemCosts: [100, 200, 50, 45, 5] }));
|
|
268
|
+
console.log(parser.solve('MEDIAN(ItemCosts)', { ItemCosts: [100, 200, 50, 45, 5] }));
|
|
269
|
+
console.log(parser.solve('COUNT(ItemCosts)', { ItemCosts: [100, 200, 50, 45, 5] }));
|
|
270
|
+
console.log(parser.solve('STDEV(Values)', { Values: [1,2,3,4,5,6,7,8,9,10,11] }));
|
|
271
|
+
console.log(parser.solve('STDEVP(Values)', { Values: [1,2,3,4,5,6,7,8,9,10,11] }));
|
|
199
272
|
```
|
|
200
273
|
|
|
201
274
|
### String Functions
|
|
202
275
|
|
|
203
276
|
```javascript
|
|
204
|
-
|
|
277
|
+
const libFable = require('fable');
|
|
278
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
279
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
280
|
+
|
|
281
|
+
fable.AppData = {
|
|
282
|
+
Cities: [{ city: 'Phoenix' }, { city: 'Denver' }, { city: 'Boston' }],
|
|
283
|
+
CityNames: ['Phoenix', 'Denver', 'Boston']
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
console.log(parser.solve('Names = concat(AppData.Cities[].city)', fable));
|
|
205
287
|
// Concatenates all city names (skipping non-string values)
|
|
206
288
|
|
|
207
|
-
parser.solve('JoinedNames = join(", ", AppData.CityNames)', fable);
|
|
289
|
+
console.log(parser.solve('JoinedNames = join(", ", AppData.CityNames)', fable));
|
|
208
290
|
// Joins with separator, resolving HTML entities
|
|
209
291
|
|
|
210
|
-
parser.solve('NameList = STRINGGETSEGMENTS(Names, ",")', { Names: 'Jane,John' });
|
|
292
|
+
console.log(parser.solve('NameList = STRINGGETSEGMENTS(Names, ",")', { Names: 'Jane,John' }));
|
|
211
293
|
// Returns ['Jane', 'John']
|
|
212
294
|
```
|
|
213
295
|
|
|
@@ -218,8 +300,13 @@ parser.solve('NameList = STRINGGETSEGMENTS(Names, ",")', { Names: 'Jane,John' })
|
|
|
218
300
|
The preferred way to write inline conditionals is with the ternary operator:
|
|
219
301
|
|
|
220
302
|
```javascript
|
|
303
|
+
const libFable = require('fable');
|
|
304
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
305
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
306
|
+
|
|
307
|
+
const dest = {};
|
|
221
308
|
parser.solve('Label = Score >= 70 ? "Pass" :: "Fail"', { Score: 85 }, {}, false, dest);
|
|
222
|
-
|
|
309
|
+
console.log('dest.Label:', dest.Label);
|
|
223
310
|
```
|
|
224
311
|
|
|
225
312
|
See [Ternary Operator](#ternary-operator) above and the [ternary function reference](./expression-parser-functions/ternary.md).
|
|
@@ -227,34 +314,45 @@ See [Ternary Operator](#ternary-operator) above and the [ternary function refere
|
|
|
227
314
|
#### When (truthy check)
|
|
228
315
|
|
|
229
316
|
```javascript
|
|
317
|
+
const libFable = require('fable');
|
|
318
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
319
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
320
|
+
|
|
321
|
+
fable.AppData = { Cities: [{ city: 'Phoenix' }] };
|
|
322
|
+
const dest = {};
|
|
230
323
|
parser.solve('Name = When(AppData.Cities[0].city, AppData.Cities[0].city)', fable, {}, false, dest);
|
|
231
|
-
|
|
324
|
+
console.log('dest.Name:', dest.Name);
|
|
232
325
|
```
|
|
233
326
|
|
|
234
327
|
#### If (comparison)
|
|
235
328
|
|
|
236
329
|
```javascript
|
|
330
|
+
const libFable = require('fable');
|
|
331
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
332
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
333
|
+
|
|
334
|
+
const data = { latitude: 42, city: 'New York' };
|
|
335
|
+
const dest = {};
|
|
237
336
|
parser.solve('GTE = If(latitude, "<", "50", "west", "east")', data, {}, false, dest);
|
|
238
|
-
|
|
337
|
+
console.log('dest.GTE:', dest.GTE);
|
|
239
338
|
|
|
240
339
|
// Supports ==, ===, <, >, LT, LTE, GT, GTE operators
|
|
241
340
|
parser.solve('Equals = If(city, "==", "New York", "yes", "no")', data, {}, false, dest);
|
|
341
|
+
console.log('dest.Equals:', dest.Equals);
|
|
242
342
|
```
|
|
243
343
|
|
|
244
344
|
### Date Functions
|
|
245
345
|
|
|
246
346
|
```javascript
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
parser.solve('Result = datehourdifference(StartDate, EndDate)', { StartDate: '2023-08-10T05:00:00.000Z', EndDate: '2023-08-11T05:00:00.000Z' });
|
|
251
|
-
// Returns '24'
|
|
252
|
-
|
|
253
|
-
parser.solve('DATEFROMPARTS(2025, 4, 1)');
|
|
254
|
-
// Returns '2025-04-01T00:00:00.000Z'
|
|
347
|
+
const libFable = require('fable');
|
|
348
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
349
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
255
350
|
|
|
256
|
-
parser.solve('
|
|
257
|
-
|
|
351
|
+
console.log(parser.solve('Result = datemilliseconddifference("2023-08-10T05:00:00.000Z", "2023-08-11T05:00:00.000Z")'));
|
|
352
|
+
console.log(parser.solve('Result = datehourdifference(StartDate, EndDate)',
|
|
353
|
+
{ StartDate: '2023-08-10T05:00:00.000Z', EndDate: '2023-08-11T05:00:00.000Z' }));
|
|
354
|
+
console.log(parser.solve('DATEFROMPARTS(2025, 4, 1)'));
|
|
355
|
+
console.log(parser.solve('DATEADDDAYS(DATEFROMPARTS(2025, 4, 1, 13, 03, 51, 761), 87)'));
|
|
258
356
|
```
|
|
259
357
|
|
|
260
358
|
Also available: `dateseconddifference`, `dateminutedifference`, `datedaydifference`, `dateweekdifference`, `datemonthdifference`, `dateyeardifference`.
|
|
@@ -262,29 +360,58 @@ Also available: `dateseconddifference`, `dateminutedifference`, `datedaydifferen
|
|
|
262
360
|
### Histogram and Aggregation Functions
|
|
263
361
|
|
|
264
362
|
```javascript
|
|
363
|
+
const libFable = require('fable');
|
|
364
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
365
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
366
|
+
|
|
367
|
+
fable.AppData = {
|
|
368
|
+
Cities: [
|
|
369
|
+
{ state: 'Alabama', population: 1000 },
|
|
370
|
+
{ state: 'Alabama', population: 2000 },
|
|
371
|
+
{ state: 'Colorado', population: 3000 }
|
|
372
|
+
]
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
const dest = {};
|
|
376
|
+
|
|
265
377
|
// Count distribution by field
|
|
266
378
|
parser.solve('Result = distributionhistogram("AppData.Cities", "state")', fable, {}, false, dest);
|
|
267
|
-
|
|
379
|
+
console.log('distribution:', dest.Result);
|
|
268
380
|
|
|
269
381
|
// Sum a numeric field grouped by another field
|
|
270
382
|
parser.solve('Result = aggregationHistogram("AppData.Cities", "state", "population")', fable, {}, false, dest);
|
|
271
|
-
|
|
383
|
+
console.log('aggregation:', dest.Result);
|
|
272
384
|
```
|
|
273
385
|
|
|
274
386
|
### Data Generation Functions
|
|
275
387
|
|
|
276
388
|
```javascript
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
parser
|
|
280
|
-
|
|
389
|
+
const libFable = require('fable');
|
|
390
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
391
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
392
|
+
|
|
393
|
+
console.log(parser.solve('RandomIntValue = RANDOMINTEGER()'));
|
|
394
|
+
console.log(parser.solve('RandomIntValueBetween = RANDOMINTEGERBETWEEN(10, 13)'));
|
|
395
|
+
console.log(parser.solve('RandomFloatValue = randomFloat()'));
|
|
396
|
+
console.log(parser.solve('RandomFloatValueBetween = randomFloatBetween(10.5, 13.78)'));
|
|
281
397
|
```
|
|
282
398
|
|
|
283
399
|
### Array Functions
|
|
284
400
|
|
|
285
401
|
```javascript
|
|
286
|
-
|
|
287
|
-
|
|
402
|
+
const libFable = require('fable');
|
|
403
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
404
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
405
|
+
|
|
406
|
+
fable.AppData = {
|
|
407
|
+
Cities: [
|
|
408
|
+
{ population: 100, latitude: 40 },
|
|
409
|
+
{ population: 200, latitude: 41 }
|
|
410
|
+
]
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
console.log(parser.solve('SLICE(W, 0, 1)', { W: ['3', '4', '5'] }));
|
|
414
|
+
console.log(parser.solve('FLATTEN(AppData.Cities[].population, AppData.Cities[].latitude)', fable));
|
|
288
415
|
```
|
|
289
416
|
|
|
290
417
|
## Advanced Features
|
|
@@ -294,14 +421,23 @@ parser.solve('FLATTEN(AppData.Cities[].population, AppData.Cities[].latitude)',
|
|
|
294
421
|
Map over arrays to transform values:
|
|
295
422
|
|
|
296
423
|
```javascript
|
|
424
|
+
const libFable = require('fable');
|
|
425
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
426
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
427
|
+
|
|
428
|
+
const manifest = fable.newManyfest();
|
|
429
|
+
const dest = {};
|
|
430
|
+
|
|
297
431
|
// Simple map
|
|
298
432
|
parser.solve('Result = MAP VAR x FROM Values : x + 100',
|
|
299
433
|
{ Values: [1, 2, 3, 4, 5] }, {}, manifest, dest);
|
|
300
|
-
|
|
434
|
+
console.log('Simple map:', dest.Result);
|
|
301
435
|
|
|
302
436
|
// Multi-variable map (cross product)
|
|
437
|
+
const data = { Cities: [{ population: 1 }, { population: 2 }], Values: [10, 20] };
|
|
303
438
|
parser.solve('Result = MAP VAR city FROM Cities VAR x FROM Values : city.population + (x * 1000000000000000)',
|
|
304
439
|
data, {}, manifest, dest);
|
|
440
|
+
console.log('Cross-product map:', dest.Result);
|
|
305
441
|
```
|
|
306
442
|
|
|
307
443
|
### SERIES Expressions
|
|
@@ -309,17 +445,27 @@ parser.solve('Result = MAP VAR city FROM Cities VAR x FROM Values : city.populat
|
|
|
309
445
|
Generate series of computed values:
|
|
310
446
|
|
|
311
447
|
```javascript
|
|
448
|
+
const libFable = require('fable');
|
|
449
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
450
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
451
|
+
|
|
452
|
+
const manifest = fable.newManyfest();
|
|
453
|
+
const dest = {};
|
|
454
|
+
|
|
312
455
|
parser.solve('Result = SERIES FROM 13.2 TO 25 STEP 0.2 : 1000 + (n / 2)',
|
|
313
456
|
{}, {}, manifest, dest);
|
|
314
|
-
|
|
457
|
+
console.log('Series first elt:', dest.Result[0]);
|
|
315
458
|
|
|
316
459
|
// With stepIndex variable
|
|
317
460
|
parser.solve('Result = SERIES FROM 13.2 TO 25 STEP 0.5 : (1000 * stepIndex) + n',
|
|
318
461
|
{}, {}, manifest, dest);
|
|
462
|
+
console.log('stepIndex series first elt:', dest.Result[0]);
|
|
319
463
|
|
|
320
464
|
// Variables can be used for FROM, TO, STEP
|
|
465
|
+
const appData = { StartValue: 1, EndValue: 5, StepValue: 1, BaseValue: 100 };
|
|
321
466
|
parser.solve('XValues = SERIES FROM StartValue TO EndValue STEP StepValue : (BaseValue * stepIndex) + n',
|
|
322
467
|
appData, {}, manifest, appData);
|
|
468
|
+
console.log('appData.XValues:', appData.XValues);
|
|
323
469
|
```
|
|
324
470
|
|
|
325
471
|
### MONTECARLO Expressions
|
|
@@ -327,13 +473,21 @@ parser.solve('XValues = SERIES FROM StartValue TO EndValue STEP StepValue : (Bas
|
|
|
327
473
|
Run Monte Carlo simulations:
|
|
328
474
|
|
|
329
475
|
```javascript
|
|
476
|
+
const libFable = require('fable');
|
|
477
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
478
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
479
|
+
|
|
480
|
+
const manifest = fable.newManyfest();
|
|
481
|
+
const dest = {};
|
|
482
|
+
|
|
330
483
|
parser.solve('Result = MONTECARLO SAMPLECOUNT 1000 VAR x PT x 50 PT x 100 : 10000000 + x',
|
|
331
484
|
{}, {}, manifest, dest);
|
|
332
|
-
|
|
485
|
+
console.log('Samples count:', dest.Result.Samples.length);
|
|
333
486
|
|
|
334
487
|
// Multi-variable Rosenbrock function
|
|
335
488
|
parser.solve('Result = MONTECARLO SAMPLECOUNT 5000 VAR x PT x -3 PT x 3 VAR y PT y -1 PT y 5 : (1 - x)^2 + 100 * (y - x^2)^2',
|
|
336
489
|
{}, {}, manifest, dest);
|
|
490
|
+
console.log('Rosenbrock samples:', dest.Result.Samples.length);
|
|
337
491
|
```
|
|
338
492
|
|
|
339
493
|
### MULTIROWMAP Expressions
|
|
@@ -341,6 +495,13 @@ parser.solve('Result = MONTECARLO SAMPLECOUNT 5000 VAR x PT x -3 PT x 3 VAR y PT
|
|
|
341
495
|
Iterate over an array of objects (rows) with multi-row lookback and lookahead. Each variable can reference the current row or any row at a relative offset, with configurable defaults for out-of-bounds access.
|
|
342
496
|
|
|
343
497
|
```javascript
|
|
498
|
+
const libFable = require('fable');
|
|
499
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
500
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
501
|
+
|
|
502
|
+
const manifest = fable.newManyfest();
|
|
503
|
+
const dest = {};
|
|
504
|
+
|
|
344
505
|
// Syntax:
|
|
345
506
|
// Result = MULTIROWMAP ROWS FROM <address>
|
|
346
507
|
// [SERIESSTART <n>] [SERIESSTEP <n>]
|
|
@@ -350,39 +511,54 @@ Iterate over an array of objects (rows) with multi-row lookback and lookahead. E
|
|
|
350
511
|
// Basic: compute area from each row's Width and Height
|
|
351
512
|
parser.solve('Areas = MULTIROWMAP ROWS FROM Rows VAR w FROM Width VAR h FROM Height : w * h',
|
|
352
513
|
{ Rows: [{Width:10,Height:20}, {Width:30,Height:40}] }, {}, manifest, dest);
|
|
353
|
-
|
|
514
|
+
console.log('Areas:', dest.Areas);
|
|
515
|
+
|
|
516
|
+
// Sample data for the remaining MULTIROWMAP variants
|
|
517
|
+
const data = {
|
|
518
|
+
Prices: [{ Close: 100 }, { Close: 102 }, { Close: 101 }, { Close: 105 }, { Close: 110 }],
|
|
519
|
+
FibRows: [{ Fib: 0 }, { Fib: 1 }, { Fib: 1 }, { Fib: 2 }, { Fib: 3 }, { Fib: 5 }],
|
|
520
|
+
Rows: [{ Value: 1 }, { Value: 2 }, { Value: 3 }, { Value: 4 }]
|
|
521
|
+
};
|
|
354
522
|
|
|
355
523
|
// Previous row lookback (day-over-day change)
|
|
356
524
|
parser.solve('Change = MULTIROWMAP ROWS FROM Prices VAR Today FROM Close VAR Yesterday FROM Close OFFSET -1 DEFAULT 0 : Today - Yesterday',
|
|
357
525
|
data, {}, manifest, dest);
|
|
526
|
+
console.log('Change:', dest.Change);
|
|
358
527
|
|
|
359
528
|
// Two-row lookback (Fibonacci verification)
|
|
360
529
|
parser.solve('Check = MULTIROWMAP ROWS FROM FibRows VAR Cur FROM Fib VAR P1 FROM Fib OFFSET -1 DEFAULT 0 VAR P2 FROM Fib OFFSET -2 DEFAULT 0 : Cur - (P1 + P2)',
|
|
361
530
|
data, {}, manifest, dest);
|
|
531
|
+
console.log('Fib Check:', dest.Check);
|
|
362
532
|
|
|
363
533
|
// Forward lookback (next row reference)
|
|
364
534
|
parser.solve('NextDiff = MULTIROWMAP ROWS FROM Rows VAR Cur FROM Value VAR Next FROM Value OFFSET 1 DEFAULT 0 : Next - Cur',
|
|
365
535
|
data, {}, manifest, dest);
|
|
536
|
+
console.log('NextDiff:', dest.NextDiff);
|
|
366
537
|
|
|
367
538
|
// Central difference (both directions)
|
|
368
539
|
parser.solve('Deriv = MULTIROWMAP ROWS FROM Rows VAR Prev FROM Value OFFSET -1 DEFAULT 0 VAR Next FROM Value OFFSET 1 DEFAULT 0 : (Next - Prev) / 2',
|
|
369
540
|
data, {}, manifest, dest);
|
|
541
|
+
console.log('Deriv:', dest.Deriv);
|
|
370
542
|
|
|
371
543
|
// SERIESSTART: skip initial rows (start from row 2)
|
|
372
544
|
parser.solve('MA = MULTIROWMAP ROWS FROM Prices SERIESSTART 2 VAR c0 FROM Close VAR c1 FROM Close OFFSET -1 DEFAULT 0 VAR c2 FROM Close OFFSET -2 DEFAULT 0 : (c0 + c1 + c2) / 3',
|
|
373
545
|
data, {}, manifest, dest);
|
|
546
|
+
console.log('MA:', dest.MA);
|
|
374
547
|
|
|
375
548
|
// Negative SERIESSTART: start from 2 rows before end
|
|
376
549
|
parser.solve('LastTwo = MULTIROWMAP ROWS FROM Rows SERIESSTART -2 VAR v FROM Value : v + 0',
|
|
377
550
|
data, {}, manifest, dest);
|
|
551
|
+
console.log('LastTwo:', dest.LastTwo);
|
|
378
552
|
|
|
379
553
|
// SERIESSTEP -1: iterate backwards
|
|
380
554
|
parser.solve('Reversed = MULTIROWMAP ROWS FROM Rows SERIESSTART -1 SERIESSTEP -1 VAR v FROM Value : v + 0',
|
|
381
555
|
data, {}, manifest, dest);
|
|
556
|
+
console.log('Reversed:', dest.Reversed);
|
|
382
557
|
|
|
383
558
|
// SERIESSTEP 2: every other row
|
|
384
559
|
parser.solve('Sampled = MULTIROWMAP ROWS FROM Rows SERIESSTEP 2 VAR v FROM Value : v + 0',
|
|
385
560
|
data, {}, manifest, dest);
|
|
561
|
+
console.log('Sampled:', dest.Sampled);
|
|
386
562
|
```
|
|
387
563
|
|
|
388
564
|
**Available variables in MULTIROWMAP expressions:**
|
|
@@ -400,20 +576,42 @@ parser.solve('Sampled = MULTIROWMAP ROWS FROM Rows SERIESSTEP 2 VAR v FROM Value
|
|
|
400
576
|
Run cumulative computations over arrays:
|
|
401
577
|
|
|
402
578
|
```javascript
|
|
403
|
-
|
|
404
|
-
|
|
579
|
+
const libFable = require('fable');
|
|
580
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
581
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
582
|
+
|
|
583
|
+
console.log(parser.solve('Result = ITERATIVESERIES(Values, "Value", "Resultant", 1, "add")',
|
|
584
|
+
{ Values: [{ Value: 10 }, { Value: 20 }, { Value: 5 }] }));
|
|
405
585
|
// Returns [{ Value: 10, Resultant: '10' }, { Value: 20, Resultant: '30' }, { Value: 5, Resultant: '35' }]
|
|
406
586
|
```
|
|
407
587
|
|
|
408
588
|
### Linear Regression
|
|
409
589
|
|
|
410
590
|
```javascript
|
|
591
|
+
const libFable = require('fable');
|
|
592
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
593
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
594
|
+
|
|
595
|
+
fable.AppData = {
|
|
596
|
+
Cities: [
|
|
597
|
+
{ latitude: 40, population: 100 },
|
|
598
|
+
{ latitude: 41, population: 200 },
|
|
599
|
+
{ latitude: 42, population: 300 }
|
|
600
|
+
]
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
const results = {};
|
|
604
|
+
|
|
411
605
|
// Compute regression coefficients
|
|
412
606
|
parser.solve('Coefficients = LINEST(FLATTEN(AppData.Cities[].latitude), FLATTEN(AppData.Cities[].population))',
|
|
413
607
|
fable, results, false, fable.AppData);
|
|
608
|
+
console.log('Coefficients:', fable.AppData.Coefficients);
|
|
414
609
|
|
|
415
610
|
// Predict from coefficients
|
|
611
|
+
const data = { Coefficients: fable.AppData.Coefficients, vector: 43 };
|
|
612
|
+
const dest = {};
|
|
416
613
|
parser.solve('Predicted = PREDICT(Coefficients, vector)', data, results, false, dest);
|
|
614
|
+
console.log('Predicted:', dest.Predicted);
|
|
417
615
|
```
|
|
418
616
|
|
|
419
617
|
### Custom Solver Functions
|
|
@@ -421,18 +619,32 @@ parser.solve('Predicted = PREDICT(Coefficients, vector)', data, results, false,
|
|
|
421
619
|
Register custom functions that can be called from expressions:
|
|
422
620
|
|
|
423
621
|
```javascript
|
|
622
|
+
const libFable = require('fable');
|
|
623
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
624
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
625
|
+
|
|
626
|
+
const dest = {};
|
|
627
|
+
|
|
424
628
|
fable.MonkeyFunction = (pParameter) => { return `Monkey says hello to ${pParameter}`; };
|
|
425
629
|
parser.addSolverFunction('monkeypatchedfunction', 'fable.MonkeyFunction', 'Documentation string');
|
|
426
630
|
|
|
427
631
|
parser.solve('Result = monkeypatchedfunction("Jerry")', fable, {}, false, dest);
|
|
428
|
-
|
|
632
|
+
console.log('dest.Result:', dest.Result);
|
|
429
633
|
```
|
|
430
634
|
|
|
431
635
|
## Logging and Debugging
|
|
432
636
|
|
|
433
637
|
```javascript
|
|
638
|
+
const libFable = require('fable');
|
|
639
|
+
const fable = new libFable({ Product: 'ExpressionParserDemo', ProductVersion: '1.0.0' });
|
|
640
|
+
const parser = fable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
641
|
+
|
|
642
|
+
const resultObject = {};
|
|
643
|
+
parser.solve('Z = 1 + 1', {}, resultObject, false, {});
|
|
644
|
+
|
|
434
645
|
// Access solver result logs
|
|
435
646
|
parser.Messaging.logFunctionSolve(resultObject);
|
|
647
|
+
console.log('resultObject keys:', Object.keys(resultObject));
|
|
436
648
|
```
|
|
437
649
|
|
|
438
650
|
## Notes
|