fable 3.1.4 → 3.1.7
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/debug/Harness.js +12 -12
- package/dist/fable.js +69 -7
- package/dist/fable.js.map +1 -1
- package/dist/fable.min.js +2 -2
- package/dist/fable.min.js.map +1 -1
- package/example_applications/mathematical_playground/Math-Solver-Harness.js +27 -23
- package/package.json +67 -67
- package/source/services/Fable-Service-DataFormat.js +134 -1
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ExpressionTokenizer.js +28 -2
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-FunctionMap.json +31 -1
- package/source/services/Fable-Service-Utility.js +50 -0
- package/test/DataFormat-StringNumberFormatting_tests.js +67 -0
- package/test/ExpressionParser_tests.js +45 -1
|
@@ -27,27 +27,27 @@ _Fable.log.info(`Beginning Run-through for Set of Test Expressions....`);
|
|
|
27
27
|
// An array of equations with expected values
|
|
28
28
|
let _Equations = require(`./Equations.json`);
|
|
29
29
|
// The application state is a plain javascript object we pass into the solver to pull variables from
|
|
30
|
-
let _AppData = require(`./AppData.json`);
|
|
31
|
-
// The manifest is a Manyfest which describes hashes for complex addresses in the application state object
|
|
32
|
-
// For example you can't use "Student[0].Age" as a variable in the expression
|
|
33
|
-
// ...but you can use "Student[0].Age" as an address in the manifest with a hash of "StudentAge"
|
|
34
|
-
// ...and then reference "StudentAge" in the expression.
|
|
35
|
-
let tmpManifestConfiguration = { "Scope":"None", "Descriptors":[] };
|
|
36
|
-
let tmpManifest = _Fable.newManyfest(tmpManifestConfiguration);
|
|
37
|
-
// Run each expression in the Equations.json file through the expression parser.
|
|
38
|
-
for (let i = 0; i < _Equations.Expressions.length; i++)
|
|
39
|
-
{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
30
|
+
// let _AppData = require(`./AppData.json`);
|
|
31
|
+
// // The manifest is a Manyfest which describes hashes for complex addresses in the application state object
|
|
32
|
+
// // For example you can't use "Student[0].Age" as a variable in the expression
|
|
33
|
+
// // ...but you can use "Student[0].Age" as an address in the manifest with a hash of "StudentAge"
|
|
34
|
+
// // ...and then reference "StudentAge" in the expression.
|
|
35
|
+
// let tmpManifestConfiguration = { "Scope":"None", "Descriptors":[] };
|
|
36
|
+
// let tmpManifest = _Fable.newManyfest(tmpManifestConfiguration);
|
|
37
|
+
// // Run each expression in the Equations.json file through the expression parser.
|
|
38
|
+
// for (let i = 0; i < _Equations.Expressions.length; i++)
|
|
39
|
+
// {
|
|
40
|
+
// let tmpResultObject = {};
|
|
41
|
+
// let tmpResultValue = _ExpressionParser.solve(_Equations.Expressions[i].Equation, _AppData, tmpResultObject, tmpManifest);
|
|
42
|
+
// console.log(`Expression [${i}]: [${_Equations.Expressions[i].Equation}] ==> ${tmpResultValue}`);
|
|
43
|
+
// console.log(` Expected: ${_Equations.Expressions[i].ExpectedResult}`);
|
|
44
|
+
// //_Fable.ExpressionParser.Messaging.logFunctionOutcome(tmpResultObject);
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
46
|
+
// if (tmpResultValue !== _Equations.Expressions[i].ExpectedResult)
|
|
47
|
+
// {
|
|
48
|
+
// console.log(`Error: Equation ${_Equations.Expressions[i].Equation} expected [${_Equations.Expressions[i].ExpectedResult}] but got [${tmpResultValue}]`);
|
|
49
|
+
// }
|
|
50
|
+
// }
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
/* * * * * * * * * * * * * * * * *
|
|
@@ -60,13 +60,16 @@ let _FruitData = require(`../data/Fruit-Data.json`);
|
|
|
60
60
|
let _FruitManifestDescription = require(`../data/Fruit-Manyfest.json`);
|
|
61
61
|
let _FruitManifest = _Fable.newManyfest(_FruitManifestDescription);
|
|
62
62
|
|
|
63
|
+
_Fable.AppData = _FruitData;
|
|
63
64
|
_Fable.log.info(`Beginning Manual Solve with Embedded Fruit Data....`);
|
|
64
65
|
|
|
65
|
-
// The expression we pass into the solver is just a string
|
|
66
|
+
// // The expression we pass into the solver is just a string
|
|
66
67
|
let tmpExpression = 'HyperMax.HealthIndex = (SUM(Calories) / SUM(Sugar)) * MEDIAN(Fat) + (SQRT(AVG(Protein)) - (PI() + 99))';
|
|
68
|
+
//tmpExpression = `aggregationhistogram("AppData.FruityVice", "family", "nutritions.calories")`;
|
|
69
|
+
tmpExpression = 'Out.Match = FindFirstValueByStringIncludes("AppData.FruityVice", "name", "uria", "id")';
|
|
67
70
|
_Fable.log.info(`Solving tmpExpression: [${tmpExpression}]`);
|
|
68
71
|
|
|
69
|
-
// This is an object where the parser will write out the results of each phase of the compiler/parser/solver
|
|
72
|
+
// // This is an object where the parser will write out the results of each phase of the compiler/parser/solver
|
|
70
73
|
let tmpExpressionParseOutcome = {};
|
|
71
74
|
let tmpSolverResultsObject = {};
|
|
72
75
|
|
|
@@ -82,7 +85,8 @@ _ExpressionParser.substituteValuesInTokenizedObjects(tmpExpressionParseOutcome.P
|
|
|
82
85
|
let tmpResultValue = _ExpressionParser.solvePostfixedExpression(tmpExpressionParseOutcome.PostfixSolveList, tmpSolverResultsObject, tmpExpressionParseOutcome, _FruitManifest);
|
|
83
86
|
|
|
84
87
|
// Now that we have a solved expression and the mapped-in values, show the user the solution
|
|
85
|
-
|
|
88
|
+
_Fable.ExpressionParser.Messaging.logFunctionOutcome(tmpExpressionParseOutcome);
|
|
89
|
+
|
|
86
90
|
|
|
87
91
|
// Step 6: Look at the results.
|
|
88
92
|
console.log(`Outcome object: ${JSON.stringify(tmpSolverResultsObject)}`);
|
package/package.json
CHANGED
|
@@ -1,70 +1,70 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
],
|
|
21
|
-
"package": "./package.json",
|
|
22
|
-
"reporter": "spec",
|
|
23
|
-
"slow": "75",
|
|
24
|
-
"timeout": "5000",
|
|
25
|
-
"ui": "tdd",
|
|
26
|
-
"watch-files": [
|
|
27
|
-
"source/**/*.js",
|
|
28
|
-
"test/**/*.js"
|
|
29
|
-
],
|
|
30
|
-
"watch-ignore": [
|
|
31
|
-
"lib/vendor"
|
|
32
|
-
]
|
|
33
|
-
},
|
|
34
|
-
"browser": {
|
|
35
|
-
"./source/service/Fable-Service-EnvironmentData.js": "./source/service/Fable-Service-EnvironmentData-Web.js",
|
|
36
|
-
"./source/service/Fable-Service-FilePersistence.js": "./source/service/Fable-Service-FilePersistence-Web.js"
|
|
37
|
-
},
|
|
38
|
-
"repository": {
|
|
39
|
-
"type": "git",
|
|
40
|
-
"url": "https://github.com/stevenvelozo/fable.git"
|
|
41
|
-
},
|
|
42
|
-
"keywords": [
|
|
43
|
-
"entity",
|
|
44
|
-
"behavior"
|
|
2
|
+
"name": "fable",
|
|
3
|
+
"version": "3.1.7",
|
|
4
|
+
"description": "A service dependency injection, configuration and logging library.",
|
|
5
|
+
"main": "source/Fable.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node source/Fable.js",
|
|
8
|
+
"coverage": "./node_modules/.bin/nyc --reporter=lcov --reporter=text-lcov ./node_modules/mocha/bin/_mocha -- -u tdd -R spec",
|
|
9
|
+
"test": "./node_modules/.bin/mocha -u tdd -R spec",
|
|
10
|
+
"build": "npx quack build",
|
|
11
|
+
"docker-dev-build": "docker build ./ -f Dockerfile_LUXURYCode -t fable-image:local",
|
|
12
|
+
"docker-dev-run": "docker run -it -d --name fable-dev -p 30001:8080 -p 38086:8086 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/fable\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" fable-image:local",
|
|
13
|
+
"docker-dev-shell": "docker exec -it fable-dev /bin/bash",
|
|
14
|
+
"tests": "./node_modules/mocha/bin/_mocha -u tdd --exit -R spec --grep"
|
|
15
|
+
},
|
|
16
|
+
"mocha": {
|
|
17
|
+
"diff": true,
|
|
18
|
+
"extension": [
|
|
19
|
+
"js"
|
|
45
20
|
],
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
21
|
+
"package": "./package.json",
|
|
22
|
+
"reporter": "spec",
|
|
23
|
+
"slow": "75",
|
|
24
|
+
"timeout": "5000",
|
|
25
|
+
"ui": "tdd",
|
|
26
|
+
"watch-files": [
|
|
27
|
+
"source/**/*.js",
|
|
28
|
+
"test/**/*.js"
|
|
29
|
+
],
|
|
30
|
+
"watch-ignore": [
|
|
31
|
+
"lib/vendor"
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
"browser": {
|
|
35
|
+
"./source/service/Fable-Service-EnvironmentData.js": "./source/service/Fable-Service-EnvironmentData-Web.js",
|
|
36
|
+
"./source/service/Fable-Service-FilePersistence.js": "./source/service/Fable-Service-FilePersistence-Web.js"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/stevenvelozo/fable.git"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"entity",
|
|
44
|
+
"behavior"
|
|
45
|
+
],
|
|
46
|
+
"author": "Steven Velozo <steven@velozo.com> (http://velozo.com/)",
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/stevenvelozo/fable/issues"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/stevenvelozo/fable",
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"quackage": "^1.0.41"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"async.eachlimit": "^0.5.2",
|
|
57
|
+
"async.waterfall": "^0.5.2",
|
|
58
|
+
"big.js": "^6.2.2",
|
|
59
|
+
"cachetrax": "^1.0.4",
|
|
60
|
+
"cookie": "^0.6.0",
|
|
61
|
+
"data-arithmatic": "^1.0.7",
|
|
62
|
+
"dayjs": "^1.11.13",
|
|
63
|
+
"fable-log": "^3.0.16",
|
|
64
|
+
"fable-serviceproviderbase": "^3.0.15",
|
|
65
|
+
"fable-settings": "^3.0.12",
|
|
66
|
+
"fable-uuid": "^3.0.11",
|
|
67
|
+
"manyfest": "^1.0.38",
|
|
68
|
+
"simple-get": "^4.0.1"
|
|
69
|
+
}
|
|
70
70
|
}
|
|
@@ -157,6 +157,139 @@ class DataFormat extends libFableServiceProviderBase
|
|
|
157
157
|
});
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Concatenate a list of strings together. Non-strings are excluded.
|
|
162
|
+
*
|
|
163
|
+
* @param {...string} pStrings - The strings to concatenate
|
|
164
|
+
* @return {string}
|
|
165
|
+
*/
|
|
166
|
+
concatenateStrings (...pStrings)
|
|
167
|
+
{
|
|
168
|
+
return this.joinStrings('', ...pStrings);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Concatenate a list of strings together. Non-strings are excluded.
|
|
173
|
+
*
|
|
174
|
+
* @param {string} pValueObjectSetAddress - The address of the array of strings to concatenate
|
|
175
|
+
* @return {string}
|
|
176
|
+
*/
|
|
177
|
+
concatenateStringsInternal (pValueObjectSetAddress)
|
|
178
|
+
{
|
|
179
|
+
if (!pValueObjectSetAddress)
|
|
180
|
+
{
|
|
181
|
+
return {};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
let tmpValueObjectSet = this.fable.Utility.getInternalValueByHash(pValueObjectSetAddress);
|
|
185
|
+
if (!Array.isArray(tmpValueObjectSet))
|
|
186
|
+
{
|
|
187
|
+
return tmpValueObjectSet || null;
|
|
188
|
+
}
|
|
189
|
+
return this.concatenateStrings(...tmpValueObjectSet);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Join a list of strings together. Non-strings are excluded.
|
|
194
|
+
*
|
|
195
|
+
* @param {string} pJoin - The string to join with
|
|
196
|
+
* @param {...string} pStrings - The strings to join
|
|
197
|
+
* @return {string}
|
|
198
|
+
*/
|
|
199
|
+
joinStrings (pJoin, ...pStrings)
|
|
200
|
+
{
|
|
201
|
+
return pStrings.filter((v) => typeof v === 'string' || typeof v === 'number').join(pJoin);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Joins a list of strings together. Non-strings are excluded.
|
|
206
|
+
*
|
|
207
|
+
* @param {string} pJoin - The string to join with
|
|
208
|
+
* @param {string} pValueObjectSetAddress - The address of the array of values to join
|
|
209
|
+
* @return {string}
|
|
210
|
+
*/
|
|
211
|
+
joinStringsInternal (pJoin, pValueObjectSetAddress)
|
|
212
|
+
{
|
|
213
|
+
if (!pValueObjectSetAddress)
|
|
214
|
+
{
|
|
215
|
+
return {};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let tmpValueObjectSet = this.fable.Utility.getInternalValueByHash(pValueObjectSetAddress);
|
|
219
|
+
if (!Array.isArray(tmpValueObjectSet))
|
|
220
|
+
{
|
|
221
|
+
return tmpValueObjectSet || null;
|
|
222
|
+
}
|
|
223
|
+
return this.joinStrings(pJoin, ...tmpValueObjectSet);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Concatenate a list of values together into a string.
|
|
229
|
+
*
|
|
230
|
+
* @param {...any} pValues - The strings to concatenate
|
|
231
|
+
* @return {string}
|
|
232
|
+
*/
|
|
233
|
+
concatenateStringsRaw (...pValues)
|
|
234
|
+
{
|
|
235
|
+
return this.joinStringsRaw('', ...pValues);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Concatenate a list of values together into a string.
|
|
240
|
+
*
|
|
241
|
+
* @param {string} pValueObjectSetAddress - The address of the array of values to concatenate
|
|
242
|
+
* @return {string}
|
|
243
|
+
*/
|
|
244
|
+
concatenateStringsRawInternal (pValueObjectSetAddress)
|
|
245
|
+
{
|
|
246
|
+
if (!pValueObjectSetAddress)
|
|
247
|
+
{
|
|
248
|
+
return {};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let tmpValueObjectSet = this.fable.Utility.getInternalValueByHash(pValueObjectSetAddress);
|
|
252
|
+
if (!Array.isArray(tmpValueObjectSet))
|
|
253
|
+
{
|
|
254
|
+
return tmpValueObjectSet || null;
|
|
255
|
+
}
|
|
256
|
+
return this.concatenateStringsRaw(...tmpValueObjectSet);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Join a list of values together into a string.
|
|
261
|
+
*
|
|
262
|
+
* @param {string} pJoin - The string to join with
|
|
263
|
+
* @param {...any} pValues - The strings to join
|
|
264
|
+
* @return {string}
|
|
265
|
+
*/
|
|
266
|
+
joinStringsRaw (pJoin, ...pValues)
|
|
267
|
+
{
|
|
268
|
+
return pValues.map(String).join(pJoin);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Joins a list of values together into a string.
|
|
273
|
+
*
|
|
274
|
+
* @param {string} pJoin - The string to join with
|
|
275
|
+
* @param {string} pValueObjectSetAddress - The address of the array of values to join
|
|
276
|
+
* @return {string}
|
|
277
|
+
*/
|
|
278
|
+
joinStringsRawInternal (pJoin, pValueObjectSetAddress)
|
|
279
|
+
{
|
|
280
|
+
if (!pValueObjectSetAddress)
|
|
281
|
+
{
|
|
282
|
+
return {};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
let tmpValueObjectSet = this.fable.Utility.getInternalValueByHash(pValueObjectSetAddress);
|
|
286
|
+
if (!Array.isArray(tmpValueObjectSet))
|
|
287
|
+
{
|
|
288
|
+
return tmpValueObjectSet || null;
|
|
289
|
+
}
|
|
290
|
+
return this.joinStringsRaw(pJoin, ...tmpValueObjectSet);
|
|
291
|
+
}
|
|
292
|
+
|
|
160
293
|
/**
|
|
161
294
|
* Clean wrapping characters if they exist consistently around the string. If they do not, the string is returned unchanged.
|
|
162
295
|
*
|
|
@@ -844,4 +977,4 @@ class DataFormat extends libFableServiceProviderBase
|
|
|
844
977
|
}
|
|
845
978
|
}
|
|
846
979
|
|
|
847
|
-
module.exports = DataFormat;
|
|
980
|
+
module.exports = DataFormat;
|
|
@@ -35,6 +35,8 @@ class ExpressionTokenizer extends libExpressionParserOperationBase
|
|
|
35
35
|
* - Token
|
|
36
36
|
* : could be an operator e.g. "+", "-", "*", "/"
|
|
37
37
|
* : could be a parenthesis e.g. "(", ")"
|
|
38
|
+
* - String
|
|
39
|
+
* : Wrapped in double quotes e.g. "Hello World", "This is a test", etc.
|
|
38
40
|
*/
|
|
39
41
|
let tmpCurrentTokenType = false;
|
|
40
42
|
let tmpCurrentToken = '';
|
|
@@ -44,7 +46,7 @@ class ExpressionTokenizer extends libExpressionParserOperationBase
|
|
|
44
46
|
|
|
45
47
|
// [ WHITESPACE ]
|
|
46
48
|
// 1. Space breaks tokens except when we're in an address that's been scoped by a {}
|
|
47
|
-
if ((tmpCharacter === ' ') && (tmpCurrentTokenType !== 'StateAddress'))
|
|
49
|
+
if ((tmpCharacter === ' ') && ((tmpCurrentTokenType !== 'StateAddress') && (tmpCurrentTokenType !== 'String')))
|
|
48
50
|
{
|
|
49
51
|
if (tmpCurrentToken.length > 0)
|
|
50
52
|
{
|
|
@@ -55,13 +57,18 @@ class ExpressionTokenizer extends libExpressionParserOperationBase
|
|
|
55
57
|
continue;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
// [ STATE ADDRESS BLOCKS ]
|
|
60
|
+
// [ STATE ADDRESS AND STRING BLOCKS ]
|
|
59
61
|
// 2. If we're in an address, we keep going until we hit the closing brace
|
|
60
62
|
if ((tmpCurrentTokenType === 'StateAddress') && (tmpCharacter !== '}'))
|
|
61
63
|
{
|
|
62
64
|
tmpCurrentToken += tmpCharacter;
|
|
63
65
|
continue;
|
|
64
66
|
}
|
|
67
|
+
if ((tmpCurrentTokenType === 'String') && (tmpCharacter !== '"'))
|
|
68
|
+
{
|
|
69
|
+
tmpCurrentToken += tmpCharacter;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
65
72
|
// 3. If we're in an address and we hit the closing brace, we close the token, push it and reset
|
|
66
73
|
if ((tmpCurrentTokenType === 'StateAddress') && (tmpCharacter === '}'))
|
|
67
74
|
{
|
|
@@ -71,6 +78,14 @@ class ExpressionTokenizer extends libExpressionParserOperationBase
|
|
|
71
78
|
tmpCurrentTokenType = false;
|
|
72
79
|
continue;
|
|
73
80
|
}
|
|
81
|
+
if ((tmpCurrentTokenType === 'String') && (tmpCharacter === '"'))
|
|
82
|
+
{
|
|
83
|
+
tmpCurrentToken += tmpCharacter;
|
|
84
|
+
tmpResults.RawTokens.push(tmpCurrentToken);
|
|
85
|
+
tmpCurrentToken = '';
|
|
86
|
+
tmpCurrentTokenType = false;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
74
89
|
// 4. If we're not in an address and we hit a closing brace it's a problem
|
|
75
90
|
// TODO: Should we just ignore it? We do at the moment.
|
|
76
91
|
if (tmpCharacter == '}')
|
|
@@ -97,6 +112,17 @@ class ExpressionTokenizer extends libExpressionParserOperationBase
|
|
|
97
112
|
tmpCurrentToken = tmpCharacter;
|
|
98
113
|
continue;
|
|
99
114
|
}
|
|
115
|
+
if ((tmpCurrentTokenType !== 'String') && (tmpCharacter == '"'))
|
|
116
|
+
{
|
|
117
|
+
if (tmpCurrentToken.length > 0)
|
|
118
|
+
{
|
|
119
|
+
tmpResults.RawTokens.push(tmpCurrentToken);
|
|
120
|
+
}
|
|
121
|
+
tmpCurrentToken = '';
|
|
122
|
+
tmpCurrentTokenType = 'String';
|
|
123
|
+
tmpCurrentToken = tmpCharacter;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
100
126
|
|
|
101
127
|
// [ TOKENS ]
|
|
102
128
|
if (tmpCharacter in this.ExpressionParser.tokenRadix)
|
|
@@ -129,6 +129,36 @@
|
|
|
129
129
|
"Address": "fable.Utility.getInternalValueByHash"
|
|
130
130
|
},
|
|
131
131
|
|
|
132
|
+
"findfirstvaluebyexactmatch": {
|
|
133
|
+
"Name": "find + map on array of objects?",
|
|
134
|
+
"Address": "fable.Utility.findFirstValueByExactMatchInternal"
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
"findfirstvaluebystringincludes": {
|
|
138
|
+
"Name": "find + map on array of objects?",
|
|
139
|
+
"Address": "fable.Utility.findFirstValueByStringIncludesInternal"
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
"concat": {
|
|
143
|
+
"Name": "concatenate an array of values and output a string",
|
|
144
|
+
"Address": "fable.DataFormat.concatenateStringsInternal"
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
"concatraw": {
|
|
148
|
+
"Name": "concatenate an array of values and output a string",
|
|
149
|
+
"Address": "fable.DataFormat.concatenateStringsRawInternal"
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
"join": {
|
|
153
|
+
"Name": "join an array of values and output a string",
|
|
154
|
+
"Address": "fable.DataFormat.joinStringsInternal"
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
"joinraw": {
|
|
158
|
+
"Name": "join an array of values and output a string",
|
|
159
|
+
"Address": "fable.DataFormat.joinStringsRawInternal"
|
|
160
|
+
},
|
|
161
|
+
|
|
132
162
|
"entryinset": {
|
|
133
163
|
"Name": "Entry in Set",
|
|
134
164
|
"Address": "fable.Math.entryInSet"
|
|
@@ -220,4 +250,4 @@
|
|
|
220
250
|
"Name": "Create Value Object by Hashes",
|
|
221
251
|
"Address": "fable.Utility.createValueObjectByHashes"
|
|
222
252
|
}
|
|
223
|
-
}
|
|
253
|
+
}
|
|
@@ -371,6 +371,31 @@ class FableServiceUtility extends libFableServiceBase
|
|
|
371
371
|
return undefined;
|
|
372
372
|
}
|
|
373
373
|
|
|
374
|
+
/**
|
|
375
|
+
* Find the first value in an object that contains a specific value
|
|
376
|
+
* @param {string} pFableAddress - The address in the fable object to pull the value from
|
|
377
|
+
* @param {string} pValueToMatchAddress - The manyfest hash/address of the value to match
|
|
378
|
+
* @param {string} pValueToMatch - The value to match
|
|
379
|
+
* @param {string} pValueAddress - The manyfest hash/address of the value to return
|
|
380
|
+
* @returns {any} - The value from the object
|
|
381
|
+
*/
|
|
382
|
+
findFirstValueByStringIncludesInternal(pFableAddress, pValueToMatchAddress, pValueToMatch, pValueAddress)
|
|
383
|
+
{
|
|
384
|
+
// Lazily create a manifest if it doesn't exist
|
|
385
|
+
if (!this.manifest)
|
|
386
|
+
{
|
|
387
|
+
this.manifest = this.fable.newManyfest();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (typeof(pFableAddress) != 'string')
|
|
391
|
+
{
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
let tmpObjectArray = this.manifest.getValueByHash(this.fable, pFableAddress);
|
|
395
|
+
|
|
396
|
+
return this.findFirstValueByStringIncludes(tmpObjectArray, pValueToMatchAddress, pValueToMatch, pValueAddress);
|
|
397
|
+
}
|
|
398
|
+
|
|
374
399
|
/**
|
|
375
400
|
* Find the first value in an object that contains a specific value
|
|
376
401
|
* @param {array} pObjectArray - The array of objects to search
|
|
@@ -402,6 +427,31 @@ class FableServiceUtility extends libFableServiceBase
|
|
|
402
427
|
|
|
403
428
|
return undefined;
|
|
404
429
|
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Find the first value in an object that contains a specific value
|
|
433
|
+
* @param {string} pFableAddress - The address in the fable object to pull the value from
|
|
434
|
+
* @param {string} pValueToMatchAddress - The manyfest hash/address of the value to match
|
|
435
|
+
* @param {string} pValueToMatch - The value to match
|
|
436
|
+
* @param {string} pValueAddress - The manyfest hash/address of the value to return
|
|
437
|
+
* @returns {any} - The value from the object
|
|
438
|
+
*/
|
|
439
|
+
findFirstValueByExactMatchInternal(pFableAddress, pValueToMatchAddress, pValueToMatch, pValueAddress)
|
|
440
|
+
{
|
|
441
|
+
// Lazily create a manifest if it doesn't exist
|
|
442
|
+
if (!this.manifest)
|
|
443
|
+
{
|
|
444
|
+
this.manifest = this.fable.newManyfest();
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (typeof(pFableAddress) != 'string')
|
|
448
|
+
{
|
|
449
|
+
return undefined;
|
|
450
|
+
}
|
|
451
|
+
let tmpObjectArray = this.manifest.getValueByHash(this.fable, pFableAddress);
|
|
452
|
+
|
|
453
|
+
return this.findFirstValueByExactMatch(tmpObjectArray, pValueToMatchAddress, pValueToMatch, pValueAddress);
|
|
454
|
+
}
|
|
405
455
|
}
|
|
406
456
|
|
|
407
457
|
module.exports = FableServiceUtility;
|
|
@@ -136,5 +136,72 @@ suite
|
|
|
136
136
|
);
|
|
137
137
|
}
|
|
138
138
|
);
|
|
139
|
+
suite
|
|
140
|
+
(
|
|
141
|
+
'String Manipulation',
|
|
142
|
+
()=>
|
|
143
|
+
{
|
|
144
|
+
test
|
|
145
|
+
(
|
|
146
|
+
'concatenateStrings concatenates an array of string-like objects',
|
|
147
|
+
() =>
|
|
148
|
+
{
|
|
149
|
+
let testFable = new libFable({LogStreams: false});
|
|
150
|
+
let _DataFormat = testFable.services.DataFormat;
|
|
151
|
+
Expect(_DataFormat
|
|
152
|
+
.concatenateStrings(undefined, 'test', 123))
|
|
153
|
+
.to.equal('test123');
|
|
154
|
+
Expect(_DataFormat
|
|
155
|
+
.concatenateStrings('waffle', { }, null))
|
|
156
|
+
.to.equal('waffle');
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
test
|
|
160
|
+
(
|
|
161
|
+
'concatenateStringsRaw stringifies then concatenates an array of anything',
|
|
162
|
+
() =>
|
|
163
|
+
{
|
|
164
|
+
let testFable = new libFable({LogStreams: false});
|
|
165
|
+
let _DataFormat = testFable.services.DataFormat;
|
|
166
|
+
Expect(_DataFormat
|
|
167
|
+
.concatenateStringsRaw(undefined, 'test', 123))
|
|
168
|
+
.to.equal('undefinedtest123');
|
|
169
|
+
Expect(_DataFormat
|
|
170
|
+
.concatenateStringsRaw('waffle', { }, null))
|
|
171
|
+
.to.equal('waffle[object Object]null');
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
test
|
|
175
|
+
(
|
|
176
|
+
'joinStrings joins an array of string-like objects',
|
|
177
|
+
() =>
|
|
178
|
+
{
|
|
179
|
+
let testFable = new libFable({LogStreams: false});
|
|
180
|
+
let _DataFormat = testFable.services.DataFormat;
|
|
181
|
+
Expect(_DataFormat
|
|
182
|
+
.joinStrings('/', undefined, 'test', 123))
|
|
183
|
+
.to.equal('test/123');
|
|
184
|
+
Expect(_DataFormat
|
|
185
|
+
.joinStrings('/', 'waffle', { }, null))
|
|
186
|
+
.to.equal('waffle');
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
test
|
|
190
|
+
(
|
|
191
|
+
'joinStringsRaw stringifies then joins an array of anything',
|
|
192
|
+
() =>
|
|
193
|
+
{
|
|
194
|
+
let testFable = new libFable({LogStreams: false});
|
|
195
|
+
let _DataFormat = testFable.services.DataFormat;
|
|
196
|
+
Expect(_DataFormat
|
|
197
|
+
.joinStringsRaw('/', undefined, 'test', 123))
|
|
198
|
+
.to.equal('undefined/test/123');
|
|
199
|
+
Expect(_DataFormat
|
|
200
|
+
.joinStringsRaw('/', 'waffle', { }, null))
|
|
201
|
+
.to.equal('waffle/[object Object]/null');
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
);
|
|
139
206
|
}
|
|
140
207
|
);
|
|
@@ -45,6 +45,12 @@ suite
|
|
|
45
45
|
let complexTokenizedResultsDoubleWidthOperators = _Parser.tokenize('SpecialValue ?= 5+3 - sqrt(75 / (3 + {Depth}) * Width)^ 3');
|
|
46
46
|
Expect(complexTokenizedResultsDoubleWidthOperators.length).to.equal(20);
|
|
47
47
|
Expect(complexTokenizedResultsDoubleWidthOperators).to.deep.equal(['SpecialValue', '?=', '5', '+', '3', '-', 'sqrt', '(', '75', '/', '(', '3', '+', '{Depth}', ')', '*', 'Width', ')', '^', '3']);
|
|
48
|
+
|
|
49
|
+
let tokenizedWithSymbologyInQuotes = _Parser.tokenize('5 + 2 * "Hello World"');
|
|
50
|
+
Expect(tokenizedWithSymbologyInQuotes.length).to.equal(5);
|
|
51
|
+
Expect(tokenizedWithSymbologyInQuotes).to.deep.equal(['5', '+', '2', '*', '"Hello World"']);
|
|
52
|
+
// TODO: refresh on the tokenization process and see if this is a valid test
|
|
53
|
+
//Expect(tmpResultObject.RawExpression).to.equal('5 + 2');
|
|
48
54
|
return fDone();
|
|
49
55
|
}
|
|
50
56
|
);
|
|
@@ -375,5 +381,43 @@ suite
|
|
|
375
381
|
);
|
|
376
382
|
}
|
|
377
383
|
);
|
|
384
|
+
suite
|
|
385
|
+
(
|
|
386
|
+
'Data Expressions',
|
|
387
|
+
function()
|
|
388
|
+
{
|
|
389
|
+
test
|
|
390
|
+
(
|
|
391
|
+
'Concatenates Strings',
|
|
392
|
+
() =>
|
|
393
|
+
{
|
|
394
|
+
let testFable = new libFable();
|
|
395
|
+
|
|
396
|
+
//FIXME: would be nicer to have a way of transorming the city object via the solver
|
|
397
|
+
let testCityData = require('./data/cities.json');
|
|
398
|
+
testFable.AppData = { CityNames: testCityData.slice(0, 4).map((c) => c.city) };
|
|
399
|
+
testFable.AppData.CityNames[2] = { };
|
|
400
|
+
|
|
401
|
+
// Now through the solver
|
|
402
|
+
|
|
403
|
+
let _Parser = testFable.instantiateServiceProviderIfNotExists('ExpressionParser');
|
|
404
|
+
let tmpResultsObject = {};
|
|
405
|
+
let tmpDestinationObject = {};
|
|
406
|
+
|
|
407
|
+
_Parser.solve('Names = concat("AppData.CityNames")', this.fable, tmpResultsObject, false, tmpDestinationObject);
|
|
408
|
+
Expect(tmpDestinationObject.Names).to.equal('New YorkLos AngelesHouston');
|
|
409
|
+
|
|
410
|
+
_Parser.solve('RawNames = concatRaw("AppData.CityNames")', this.fable, tmpResultsObject, false, tmpDestinationObject);
|
|
411
|
+
Expect(tmpDestinationObject.RawNames).to.equal('New YorkLos Angeles[object Object]Houston');
|
|
412
|
+
|
|
413
|
+
_Parser.solve('JoinedNames = join(", ", "AppData.CityNames")', this.fable, tmpResultsObject, false, tmpDestinationObject);
|
|
414
|
+
Expect(tmpDestinationObject.JoinedNames).to.equal('New York, Los Angeles, Houston');
|
|
415
|
+
|
|
416
|
+
_Parser.solve('RawJoinedNames = joinRaw(" ", "AppData.CityNames")', this.fable, tmpResultsObject, false, tmpDestinationObject);
|
|
417
|
+
Expect(tmpDestinationObject.RawJoinedNames).to.equal('New York Los Angeles [object Object] Houston');
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
);
|
|
378
422
|
}
|
|
379
|
-
);
|
|
423
|
+
);
|