Skip to content

Commit bd9bebc

Browse files
committed
Implement miscellaneous fixes and test a handful of setups for precision implementation
1 parent e3b45f5 commit bd9bebc

File tree

5 files changed

+103
-23
lines changed

5 files changed

+103
-23
lines changed

compatibility.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import defaultMethods from './defaultMethods.js'
2+
import { Sync } from './constants.js'
23
const oldAll = defaultMethods.all
34

45
const all = {
6+
[Sync]: defaultMethods.all[Sync],
57
method: (args, context, above, engine) => {
68
if (Array.isArray(args)) {
79
const first = engine.run(args[0], context, above)

defaultMethods.js

+11-5
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ const defaultMethods = {
224224
// Why "executeInLoop"? Because if it needs to execute to get an array, I do not want to execute the arguments,
225225
// Both for performance and safety reasons.
226226
or: {
227+
[Sync]: (data, buildState) => isSyncDeep(data, buildState.engine, buildState),
227228
method: (arr, _1, _2, engine) => {
228229
// See "executeInLoop" above
229230
const executeInLoop = Array.isArray(arr)
@@ -262,6 +263,7 @@ const defaultMethods = {
262263
traverse: false
263264
},
264265
and: {
266+
[Sync]: (data, buildState) => isSyncDeep(data, buildState.engine, buildState),
265267
method: (arr, _1, _2, engine) => {
266268
// See "executeInLoop" above
267269
const executeInLoop = Array.isArray(arr)
@@ -310,6 +312,7 @@ const defaultMethods = {
310312
return 0
311313
},
312314
get: {
315+
[Sync]: true,
313316
method: ([data, key, defaultValue], context, above, engine) => {
314317
const notFound = defaultValue === undefined ? null : defaultValue
315318

@@ -391,6 +394,7 @@ const defaultMethods = {
391394
some: createArrayIterativeMethod('some', true),
392395
all: createArrayIterativeMethod('every', true),
393396
none: {
397+
[Sync]: (data, buildState) => isSyncDeep(data, buildState.engine, buildState),
394398
traverse: false,
395399
// todo: add async build & build
396400
method: (val, context, above, engine) => {
@@ -411,7 +415,7 @@ const defaultMethods = {
411415
},
412416
merge: (arrays) => (Array.isArray(arrays) ? [].concat(...arrays) : [arrays]),
413417
every: createArrayIterativeMethod('every'),
414-
filter: createArrayIterativeMethod('filter'),
418+
filter: createArrayIterativeMethod('filter', true),
415419
reduce: {
416420
deterministic: (data, buildState) => {
417421
return (
@@ -521,6 +525,7 @@ const defaultMethods = {
521525
'!': (value, _1, _2, engine) => Array.isArray(value) ? !engine.truthy(value[0]) : !engine.truthy(value),
522526
'!!': (value, _1, _2, engine) => Boolean(Array.isArray(value) ? engine.truthy(value[0]) : engine.truthy(value)),
523527
cat: {
528+
[Sync]: true,
524529
method: (arr) => {
525530
if (typeof arr === 'string') return arr
526531
if (!Array.isArray(arr)) return arr.toString()
@@ -659,8 +664,8 @@ function createArrayIterativeMethod (name, useTruthy = false) {
659664
(await engine.run(selector, context, {
660665
above
661666
})) || []
662-
return asyncIterators[name](selector, (i, index) => {
663-
const result = engine.run(mapper, i, {
667+
return asyncIterators[name](selector, async (i, index) => {
668+
const result = await engine.run(mapper, i, {
664669
above: [{ iterator: selector, index }, context, above]
665670
})
666671
return useTruthy ? engine.truthy(result) : result
@@ -680,15 +685,16 @@ function createArrayIterativeMethod (name, useTruthy = false) {
680685

681686
const method = build(mapper, mapState)
682687
const aboveArray = method.aboveDetected ? buildState.compile`[{ iterator: z, index: x }, context, above]` : buildState.compile`null`
688+
const useTruthyMethod = useTruthy ? buildState.compile`engine.truthy` : buildState.compile``
683689

684690
if (async) {
685691
if (!isSyncDeep(mapper, buildState.engine, buildState)) {
686692
buildState.detectAsync = true
687-
return buildState.compile`await asyncIterators[${name}](${selector} || [], async (i, x, z) => ${method}(i, x, ${aboveArray}))`
693+
return buildState.compile`await asyncIterators[${name}](${selector} || [], async (i, x, z) => ${useTruthyMethod}(${method}(i, x, ${aboveArray})))`
688694
}
689695
}
690696

691-
return buildState.compile`(${selector} || [])[${name}]((i, x, z) => ${method}(i, x, ${aboveArray}))`
697+
return buildState.compile`(${selector} || [])[${name}]((i, x, z) => ${useTruthyMethod}(${method}(i, x, ${aboveArray})))`
692698
},
693699
traverse: false
694700
}

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
},
3838
"jest": {
3939
"testPathIgnorePatterns": [
40-
"./bench"
40+
"./bench",
41+
"./precision"
4142
]
4243
},
4344
"exports": {

precision/index.js

+54-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
1-
export function configurePrecision (engine, constructor) {
1+
2+
export function configurePrecision (engine, constructor, compatible = true) {
23
engine.precision = constructor
4+
5+
engine.truthy = (data) => {
6+
if ((data || false).toNumber) return Number(data)
7+
if (compatible && Array.isArray(data) && data.length === 0) return false
8+
return data
9+
}
10+
11+
if (engine.fallback) engine.fallback.truthy = engine.truthy
12+
313
engine.addMethod('+', {
414
method: (data) => {
515
if (typeof data === 'string') return constructor(data)
@@ -15,30 +25,28 @@ export function configurePrecision (engine, constructor) {
1525
return res
1626
}
1727
return false
18-
},
19-
optimizeUnary: true,
20-
deterministic: true
21-
})
28+
}
29+
}, { optimizeUnary: true, sync: true, deterministic: true })
2230

2331
engine.addMethod('-', {
2432
method: (data) => {
2533
if (typeof data === 'string') return constructor(data).mul(-1)
2634
if (typeof data === 'number') return constructor(data).mul(-1)
2735
let res = constructor(data[0])
36+
if (data.length === 1) return res.mul(-1)
2837
for (let i = 1; i < data.length; i++) res = res.minus(data[i])
2938
return res
3039
},
3140
compile: (args, buildState) => {
3241
if (Array.isArray(args)) {
3342
let res = buildState.compile`(engine.precision(${args[0]}))`
43+
if (args.length === 1) return buildState.compile`(${res}.mul(-1))`
3444
for (let i = 1; i < args.length; i++) res = buildState.compile`(${res}.minus(${args[i]}))`
3545
return res
3646
}
3747
return false
38-
},
39-
optimizeUnary: true,
40-
deterministic: true
41-
})
48+
}
49+
}, { optimizeUnary: true, sync: true, deterministic: true })
4250

4351
engine.addMethod('*', {
4452
method: (data) => {
@@ -53,10 +61,8 @@ export function configurePrecision (engine, constructor) {
5361
return res
5462
}
5563
return false
56-
},
57-
deterministic: true,
58-
precise: true
59-
})
64+
}
65+
}, { sync: true, deterministic: true })
6066

6167
engine.addMethod('/', {
6268
method: (data) => {
@@ -71,9 +77,8 @@ export function configurePrecision (engine, constructor) {
7177
return res
7278
}
7379
return false
74-
},
75-
deterministic: true
76-
})
80+
}
81+
}, { sync: true, deterministic: true })
7782

7883
engine.addMethod('%', {
7984
method: (data) => {
@@ -88,7 +93,39 @@ export function configurePrecision (engine, constructor) {
8893
return res
8994
}
9095
return false
96+
}
97+
}, { sync: true, deterministic: true })
98+
99+
engine.addMethod('===', {
100+
method: (args) => {
101+
if (args.length === 2) {
102+
if (args[0].eq) return args[0].eq(args[1])
103+
if (args[1].eq) return args[1].eq(args[0])
104+
return args[0] === args[1]
105+
}
106+
for (let i = 1; i < args.length; i++) {
107+
if (args[i - 1].eq && !args[i - 1].eq(args[i])) return false
108+
if (args[i].eq && !args[i].eq(args[i - 1])) return false
109+
if (args[i - 1] !== args[i]) return false
110+
}
111+
return true
112+
}
113+
}, { sync: true, deterministic: true })
114+
115+
engine.addMethod('!==', {
116+
method: (args) => {
117+
if (args.length === 2) {
118+
if (args[0].eq) return !args[0].eq(args[1])
119+
if (args[1].eq) return !args[1].eq(args[0])
120+
return args[0] !== args[1]
121+
}
122+
for (let i = 1; i < args.length; i++) {
123+
if (args[i - 1].eq && args[i - 1].eq(args[i])) return false
124+
if (args[i].eq && args[i].eq(args[i - 1])) return false
125+
if (args[i - 1] !== args[i]) return true
126+
}
127+
return false
91128
},
92129
deterministic: true
93-
})
130+
}, { sync: true, deterministic: true })
94131
}

precision/test.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { AsyncLogicEngine } from '../index.js'
2+
import { Decimal } from 'decimal.js'
3+
import { configurePrecision } from './index.js'
4+
import { isDeepStrictEqual } from 'util'
5+
6+
import fs from 'fs'
7+
8+
const tests = JSON.parse(fs.readFileSync('../bench/tests.json').toString())
9+
10+
Decimal.prototype.toString = function () {
11+
return this.toFixed()
12+
}
13+
14+
Decimal.prototype.valueOf = function () {
15+
return this.toFixed()
16+
}
17+
18+
const decimalEngine = new AsyncLogicEngine(undefined, { compatible: true })
19+
configurePrecision(decimalEngine, Decimal.clone({ precision: 100 }))
20+
21+
let count = 0
22+
for (const test of tests) {
23+
if (typeof test !== 'string') {
24+
let result = await decimalEngine.run(test[0], test[1])
25+
if (result && result.toNumber) result = Number(result)
26+
if (Array.isArray(result)) result = result.map((x) => (x && x.toNumber ? Number(x) : x))
27+
if (!isDeepStrictEqual(result, test[2])) {
28+
count++
29+
console.log(test[0], test[2], result)
30+
}
31+
}
32+
}
33+
34+
console.log(count, 'Wrong')

0 commit comments

Comments
 (0)