fable 3.1.72 → 3.1.74

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.
Files changed (40) hide show
  1. package/docs/README.md +30 -6
  2. package/docs/_brand.json +18 -0
  3. package/docs/_playground.json +10 -0
  4. package/docs/_sidebar.md +2 -0
  5. package/docs/_version.json +3 -3
  6. package/docs/architecture.md +201 -39
  7. package/docs/index.html +6 -7
  8. package/docs/pict-docuserve.min.js +91 -0
  9. package/docs/pict-docuserve.min.js.map +1 -0
  10. package/docs/playground.md +38 -0
  11. package/docs/retold-catalog.json +1 -1
  12. package/docs/retold-keyword-index.json +8721 -8105
  13. package/docs/services/README.md +26 -9
  14. package/docs/services/anticipate.md +104 -40
  15. package/docs/services/csv-parser.md +63 -35
  16. package/docs/services/data-format.md +154 -49
  17. package/docs/services/data-generation.md +77 -16
  18. package/docs/services/dates.md +103 -36
  19. package/docs/services/environment-data.md +13 -2
  20. package/docs/services/expression-parser.md +280 -68
  21. package/docs/services/file-persistence.md +142 -150
  22. package/docs/services/logging.md +93 -37
  23. package/docs/services/logic.md +70 -22
  24. package/docs/services/manifest.md +114 -26
  25. package/docs/services/math.md +168 -63
  26. package/docs/services/meta-template.md +312 -158
  27. package/docs/services/object-cache.md +94 -11
  28. package/docs/services/operation.md +68 -6
  29. package/docs/services/progress-time.md +74 -13
  30. package/docs/services/progress-tracker-set.md +101 -3
  31. package/docs/services/rest-client.md +136 -104
  32. package/docs/services/settings-manager.md +133 -40
  33. package/docs/services/template.md +71 -22
  34. package/docs/services/utility.md +121 -29
  35. package/docs/services/uuid.md +58 -10
  36. package/package.json +2 -2
  37. package/source/services/Fable-Service-MetaTemplate/MetaTemplate-StringParser.js +6 -0
  38. package/test/MetaTemplating_tests.js +77 -0
  39. package/.claude/settings.local.json +0 -8
  40. package/docs/css/docuserve.css +0 -327
@@ -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
- parser.solve('1 + 1'); // Returns '2'
20
- parser.solve('10 * 2'); // Returns '20'
21
- parser.solve('100 / 4'); // Returns '25'
22
- parser.solve('2 ^ 10'); // Returns '1024'
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
- parser.solve(expression, dataObject, resultObject, manifest, destinationObject);
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
- // Only sets dest.Name if it was not previously set
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
- // dest.Result === '1'
119
+ console.log("after '5 > 3':", dest.Result);
85
120
 
86
121
  parser.solve('Result = 2 + 3 > 1 + 2', {}, {}, false, dest);
87
- // dest.Result === '1' (evaluates as (2+3) > (1+2), i.e. 5 > 3)
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
- // dest.Result === '1'
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
- // dest.Result === '100'
146
+ console.log('5>3 ? 100 :: 200 =>', dest.Result);
106
147
 
107
148
  parser.solve('Result = 3 > 5 ? 100 :: 200', {}, {}, false, dest);
108
- // dest.Result === '200'
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
- // dest.Winner === '10'
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
- // dest.Result === '11'
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
- // dest.Result === '1'
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
- parser.solve('5 + 2 * 10'); // Returns '25'
133
- parser.solve('3.5 + 5 + 10 * 10 / 5'); // Returns '28.5'
134
- parser.solve('(100 - 10)'); // Returns '90'
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
- // dest.Value === '-3'
191
+ console.log('dest.Value:', dest.Value);
142
192
 
143
193
  parser.solve('Value2 = (4 + -3)', {}, {}, false, dest);
144
- // dest.Value2 === '1'
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
- parser.solve('X * Y * Z', { X: 5, Y: 3.1, Z: 75 }); // Returns '1162.5'
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
- // dest.Area === '1364.0775'
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")', {}, {}, false, dest);
165
- // dest.PitSize === 'Bottomless'
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]")', {}, {}, false, dest);
169
- // dest.Name === 'Jim'
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
- parser.solve('sqrt(16)'); // '4'
178
- parser.solve('sin(rad(60))'); // '0.8660254037844386'
179
- parser.solve('1.5 * sqrt(8 * 2.423782342^2) / 10'); // '1.02832375808904701855'
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
- parser.solve('ROUND(X * Y * Z)', { X: 5.867, Y: 3.1, Z: 75 }); // '1364'
186
- parser.solve('ROUND(X * Y * Z, 2)', { X: 5.867, Y: 3.1, Z: 75 }); // '1364.08'
187
- parser.solve('ROUND(X * Y * Z, 3, 3)', { X: 5.867, Y: 3.5, Z: 75.248923423 }); // '1545.2'
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
- parser.solve('SUM(ItemCosts)', { ItemCosts: [100, 200, 50, 45, 5] }); // '400'
194
- parser.solve('MEAN(ItemCosts)', { ItemCosts: [100, 200, 50, 45, 5] }); // '80'
195
- parser.solve('MEDIAN(ItemCosts)', { ItemCosts: [100, 200, 50, 45, 5] }); // '50'
196
- parser.solve('COUNT(ItemCosts)', { ItemCosts: [100, 200, 50, 45, 5] }); // '5'
197
- parser.solve('STDEV(Values)', { Values: [1,2,3,4,5,6,7,8,9,10,11] }); // ~3.3166
198
- parser.solve('STDEVP(Values)', { Values: [1,2,3,4,5,6,7,8,9,10,11] }); // ~3.1623
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
- parser.solve('Names = concat(AppData.Cities[].city)', fable);
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
- // dest.Label === 'Pass'
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
- // If first arg is truthy, returns second arg; otherwise returns ''
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
- // If latitude < 50, returns 'west', else 'east'
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
- parser.solve('Result = datemilliseconddifference("2023-08-10T05:00:00.000Z", "2023-08-11T05:00:00.000Z")');
248
- // Returns '86400000'
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('DATEADDDAYS(DATEFROMPARTS(2025, 4, 1, 13, 03, 51, 761), 87)');
257
- // Returns '2025-06-27T13:03:51.761Z'
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
- // dest.Result = { Alabama: 12, Colorado: 21, ... }
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
- // dest.Result = { Alabama: '1279813', ... }
383
+ console.log('aggregation:', dest.Result);
272
384
  ```
273
385
 
274
386
  ### Data Generation Functions
275
387
 
276
388
  ```javascript
277
- parser.solve('RandomIntValue = RANDOMINTEGER()');
278
- parser.solve('RandomIntValueBetween = RANDOMINTEGERBETWEEN(10, 13)');
279
- parser.solve('RandomFloatValue = randomFloat()');
280
- parser.solve('RandomFloatValueBetween = randomFloatBetween(10.5, 13.78)');
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
- parser.solve('SLICE(W, 0, 1)', { W: ['3', '4', '5'] }); // Returns ['5'] (first element)
287
- parser.solve('FLATTEN(AppData.Cities[].population, AppData.Cities[].latitude)', fable);
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
- // dest.Result === ['101', '102', '103', '104', '105']
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
- // dest.Result[0] === '1006.6'
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
- // dest.Result.Samples.length === 1000
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
- // dest.Areas === ['200', '1200']
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
- parser.solve('Result = ITERATIVESERIES(Values, "Value", "Resultant", 1, "add")',
404
- { Values: [{ Value: 10 }, { Value: 20 }, { Value: 5 }] });
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
- // dest.Result === 'Monkey says hello to Jerry'
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