Skip to content

Commit eaffdec

Browse files
Merge pull request #42 from json-logic/proposal/error
[Draft] Experimenting with Soft Errors
2 parents 20cccd8 + 1287baf commit eaffdec

17 files changed

+373
-163
lines changed

asyncLogic.js

+8-9
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,8 @@ class AsyncLogicEngine {
8989
}
9090

9191
if (typeof this.methods[func] === 'object') {
92-
const { asyncMethod, method, traverse } = this.methods[func]
93-
const shouldTraverse = typeof traverse === 'undefined' ? true : traverse
94-
const parsedData = shouldTraverse ? ((!data || typeof data !== 'object') ? [data] : coerceArray(await this.run(data, context, { above }))) : data
92+
const { asyncMethod, method, lazy } = this.methods[func]
93+
const parsedData = !lazy ? ((!data || typeof data !== 'object') ? [data] : coerceArray(await this.run(data, context, { above }))) : data
9594
const result = await (asyncMethod || method)(parsedData, context, above, this)
9695
return Array.isArray(result) ? Promise.all(result) : result
9796
}
@@ -102,7 +101,7 @@ class AsyncLogicEngine {
102101
/**
103102
*
104103
* @param {String} name The name of the method being added.
105-
* @param {((args: any, context: any, above: any[], engine: AsyncLogicEngine) => any) | { traverse?: Boolean, method?: (args: any, context: any, above: any[], engine: AsyncLogicEngine) => any, asyncMethod?: (args: any, context: any, above: any[], engine: AsyncLogicEngine) => Promise<any>, deterministic?: Function | Boolean }} method
104+
* @param {((args: any, context: any, above: any[], engine: AsyncLogicEngine) => any) | { lazy?: Boolean, traverse?: Boolean, method?: (args: any, context: any, above: any[], engine: AsyncLogicEngine) => any, asyncMethod?: (args: any, context: any, above: any[], engine: AsyncLogicEngine) => Promise<any>, deterministic?: Function | Boolean }} method
106105
* @param {{ deterministic?: Boolean, async?: Boolean, sync?: Boolean, optimizeUnary?: boolean }} annotations This is used by the compiler to help determine if it can optimize the function being generated.
107106
*/
108107
addMethod (
@@ -115,9 +114,9 @@ class AsyncLogicEngine {
115114
if (typeof async !== 'undefined') sync = !async
116115

117116
if (typeof method === 'function') {
118-
if (async) method = { asyncMethod: method, traverse: true }
119-
else method = { method, traverse: true }
120-
} else method = { ...method }
117+
if (async) method = { asyncMethod: method, lazy: false }
118+
else method = { method, lazy: false }
119+
} else method = { ...method, lazy: typeof method.traverse !== 'undefined' ? !method.traverse : method.lazy }
121120

122121
Object.assign(method, omitUndefined({ deterministic, optimizeUnary }))
123122
// @ts-ignore
@@ -174,10 +173,10 @@ class AsyncLogicEngine {
174173
// END OPTIMIZER BLOCK //
175174

176175
if (Array.isArray(logic)) {
177-
const res = []
176+
const res = new Array(logic.length)
178177
// Note: In the past, it used .map and Promise.all; this can be changed in the future
179178
// if we want it to run concurrently.
180-
for (let i = 0; i < logic.length; i++) res.push(await this.run(logic[i], data, { above }))
179+
for (let i = 0; i < logic.length; i++) res[i] = await this.run(logic[i], data, { above })
181180
return res
182181
}
183182

async_optimizer.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function getMethod (logic, engine, methodName, above) {
1717
const method = engine.methods[methodName]
1818
const called = method.asyncMethod ? method.asyncMethod : method.method ? method.method : method
1919

20-
if (method.traverse === false) {
20+
if (method.lazy) {
2121
if (typeof method[Sync] === 'function' && method[Sync](logic, { engine })) {
2222
const called = method.method ? method.method : method
2323
return declareSync((data, abv) => called(logic[methodName], data, abv || above, engine.fallback), true)

compatibility.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ const all = {
1919
return oldAll.asyncMethod(args, context, above, engine)
2020
},
2121
deterministic: oldAll.deterministic,
22-
traverse: oldAll.traverse
22+
lazy: oldAll.lazy
2323
}
2424

2525
function truthy (value) {
2626
if (Array.isArray(value) && value.length === 0) return false
27+
if (Number.isNaN(value)) return true
2728
return value
2829
}
2930

compatible.test.js

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ for (const file of files) {
1616
function correction (x) {
1717
// eslint-disable-next-line no-compare-neg-zero
1818
if (x === -0) return 0
19+
if (Number.isNaN(x)) return { error: 'NaN' }
1920
return x
2021
}
2122

compiler.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import asyncIterators from './async_iterators.js'
1212
import { coerceArray } from './utilities/coerceArray.js'
1313
import { countArguments } from './utilities/countArguments.js'
14+
import { downgrade, precoerceNumber } from './utilities/downgrade.js'
1415

1516
/**
1617
* Provides a simple way to compile logic into a function that can be run.
@@ -87,7 +88,7 @@ export function isDeterministic (method, engine, buildState) {
8788
if (lower === undefined) return true
8889
if (!engine.methods[func]) throw new Error(`Method '${func}' was not found in the Logic Engine.`)
8990

90-
if (engine.methods[func].traverse === false) {
91+
if (engine.methods[func].lazy) {
9192
return typeof engine.methods[func].deterministic === 'function'
9293
? engine.methods[func].deterministic(lower, buildState)
9394
: engine.methods[func].deterministic
@@ -118,7 +119,7 @@ function isDeepSync (method, engine) {
118119
const lower = method[func]
119120
if (!isSync(engine.methods[func])) return false
120121

121-
if (engine.methods[func].traverse === false) {
122+
if (engine.methods[func].lazy) {
122123
if (typeof engine.methods[func][Sync] === 'function' && engine.methods[func][Sync](method, { engine })) return true
123124
return false
124125
}
@@ -193,7 +194,7 @@ function buildString (method, buildState = {}) {
193194
}
194195

195196
let lower = method[func]
196-
if (!lower || typeof lower !== 'object') lower = [lower]
197+
if ((!lower || typeof lower !== 'object') && (!engine.methods[func].lazy)) lower = [lower]
197198

198199
if (engine.methods[func] && engine.methods[func].compile) {
199200
let str = engine.methods[func].compile(lower, buildState)
@@ -219,7 +220,7 @@ function buildString (method, buildState = {}) {
219220
const argCount = countArguments(asyncDetected ? engine.methods[func].asyncMethod : engine.methods[func].method)
220221
const argumentsNeeded = argumentsDict[argCount - 1] || argumentsDict[2]
221222

222-
if (engine.methods[func] && (typeof engine.methods[func].traverse === 'undefined' ? true : engine.methods[func].traverse)) {
223+
if (engine.methods[func] && !engine.methods[func].lazy) {
223224
return makeAsync(`engine.methods["${func}"]${asyncDetected ? '.asyncMethod' : '.method'}(${coerce}(` + buildString(lower, buildState) + ')' + argumentsNeeded + ')')
224225
} else {
225226
notTraversed.push(lower)
@@ -307,12 +308,12 @@ function processBuiltString (method, str, buildState) {
307308
str = str.replace(`__%%%${x}%%%__`, item)
308309
})
309310

310-
const final = `(values, methods, notTraversed, asyncIterators, engine, above, coerceArray) => ${buildState.asyncDetected ? 'async' : ''} (context ${buildState.extraArguments ? ',' + buildState.extraArguments : ''}) => { let prev; const result = ${str}; return result }`
311+
const final = `(values, methods, notTraversed, asyncIterators, engine, above, coerceArray, downgrade, precoerceNumber) => ${buildState.asyncDetected ? 'async' : ''} (context ${buildState.extraArguments ? ',' + buildState.extraArguments : ''}) => { ${str.includes('prev') ? 'let prev;' : ''} const result = ${str}; return result }`
311312
// console.log(str)
312313
// console.log(final)
313314
// eslint-disable-next-line no-eval
314315
return Object.assign(
315-
(typeof globalThis !== 'undefined' ? globalThis : global).eval(final)(values, methods, notTraversed, asyncIterators, engine, above, coerceArray), {
316+
(typeof globalThis !== 'undefined' ? globalThis : global).eval(final)(values, methods, notTraversed, asyncIterators, engine, above, coerceArray, downgrade, precoerceNumber), {
316317
[Sync]: !buildState.asyncDetected,
317318
aboveDetected: typeof str === 'string' && str.includes(', above')
318319
})

0 commit comments

Comments
 (0)