Skip to content

Commit a91fab1

Browse files
committed
Make comparators lazily evaluated
1 parent 4b09700 commit a91fab1

File tree

2 files changed

+109
-73
lines changed

2 files changed

+109
-73
lines changed

defaultMethods.js

+61-73
Original file line numberDiff line numberDiff line change
@@ -215,66 +215,16 @@ const defaultMethods = {
215215
},
216216
lazy: true
217217
},
218-
'<': (args) => {
219-
if (args.length === 2) return args[0] < args[1]
220-
for (let i = 1; i < args.length; i++) {
221-
if (args[i - 1] >= args[i]) return false
222-
}
223-
return true
224-
},
225-
'<=': (args) => {
226-
if (args.length === 2) return args[0] <= args[1]
227-
for (let i = 1; i < args.length; i++) {
228-
if (args[i - 1] > args[i]) return false
229-
}
230-
return true
231-
},
232-
'>': (args) => {
233-
if (args.length === 2) return args[0] > args[1]
234-
for (let i = 1; i < args.length; i++) {
235-
if (args[i - 1] <= args[i]) return false
236-
}
237-
return true
238-
},
239-
'>=': (args) => {
240-
if (args.length === 2) return args[0] >= args[1]
241-
for (let i = 1; i < args.length; i++) {
242-
if (args[i - 1] < args[i]) return false
243-
}
244-
return true
245-
},
246-
'==': (args) => {
247-
// eslint-disable-next-line eqeqeq
248-
if (args.length === 2) return args[0] == args[1]
249-
for (let i = 1; i < args.length; i++) {
250-
// eslint-disable-next-line eqeqeq
251-
if (args[i - 1] != args[i]) return false
252-
}
253-
return true
254-
},
255-
'===': (args) => {
256-
if (args.length === 2) return args[0] === args[1]
257-
for (let i = 1; i < args.length; i++) {
258-
if (args[i - 1] !== args[i]) return false
259-
}
260-
return true
261-
},
262-
'!=': (args) => {
263-
// eslint-disable-next-line eqeqeq
264-
if (args.length === 2) return args[0] != args[1]
265-
for (let i = 1; i < args.length; i++) {
266-
// eslint-disable-next-line eqeqeq
267-
if (args[i - 1] == args[i]) return false
268-
}
269-
return true
270-
},
271-
'!==': (args) => {
272-
if (args.length === 2) return args[0] !== args[1]
273-
for (let i = 1; i < args.length; i++) {
274-
if (args[i - 1] === args[i]) return false
275-
}
276-
return true
277-
},
218+
'<': createComparator('<', (a, b) => a < b),
219+
'<=': createComparator('<=', (a, b) => a <= b),
220+
'>': createComparator('>', (a, b) => a > b),
221+
'>=': createComparator('>=', (a, b) => a >= b),
222+
// eslint-disable-next-line eqeqeq
223+
'==': createComparator('==', (a, b) => a == b),
224+
'===': createComparator('===', (a, b) => a === b),
225+
// eslint-disable-next-line eqeqeq
226+
'!=': createComparator('!=', (a, b) => a != b),
227+
'!==': createComparator('!==', (a, b) => a !== b),
278228
// Why "executeInLoop"? Because if it needs to execute to get an array, I do not want to execute the arguments,
279229
// Both for performance and safety reasons.
280230
or: {
@@ -858,6 +808,57 @@ const defaultMethods = {
858808
}
859809
}
860810

811+
function createComparator (name, func) {
812+
const opStr = { [Compiled]: name }
813+
return {
814+
method: (args, context, above, engine) => {
815+
if (!Array.isArray(args)) {
816+
const items = runOptimizedOrFallback(args, engine, context, above)
817+
if (items.length === 2) return func(items[0], items[1])
818+
for (let i = 1; i < items.length; i++) {
819+
if (!func(items[i - 1], items[i])) return false
820+
}
821+
}
822+
if (args.length === 2) return func(runOptimizedOrFallback(args[0], engine, context, above), runOptimizedOrFallback(args[1], engine, context, above))
823+
let prev = runOptimizedOrFallback(args[0], engine, context, above)
824+
for (let i = 1; i < args.length; i++) {
825+
const current = runOptimizedOrFallback(args[i], engine, context, above)
826+
if (!func(prev, current)) return false
827+
prev = current
828+
}
829+
return true
830+
},
831+
asyncMethod: async (args, context, above, engine) => {
832+
if (!Array.isArray(args)) {
833+
const items = await runOptimizedOrFallback(args, engine, context, above)
834+
if (items.length === 2) return func(items[0], items[1])
835+
for (let i = 1; i < items.length; i++) {
836+
if (!func(items[i - 1], items[i])) return false
837+
}
838+
}
839+
if (args.length === 2) return func(await runOptimizedOrFallback(args[0], engine, context, above), await runOptimizedOrFallback(args[1], engine, context, above))
840+
let prev = await runOptimizedOrFallback(args[0], engine, context, above)
841+
for (let i = 1; i < args.length; i++) {
842+
const current = await runOptimizedOrFallback(args[i], engine, context, above)
843+
if (!func(prev, current)) return false
844+
prev = current
845+
}
846+
return true
847+
},
848+
compile: (data, buildState) => {
849+
if (!Array.isArray(data)) return false
850+
if (data.length < 2) return false
851+
if (data.length === 2) return buildState.compile`(${data[0]} ${opStr} ${data[1]})`
852+
let res = buildState.compile`(${data[0]} ${opStr} (prev = ${data[1]}))`
853+
for (let i = 2; i < data.length; i++) res = buildState.compile`(${res} && prev ${opStr} ${data[i]})`
854+
return res
855+
},
856+
[Sync]: (data, buildState) => isSyncDeep(data, buildState.engine, buildState),
857+
deterministic: (data, buildState) => isDeterministic(data, buildState.engine, buildState),
858+
lazy: true
859+
}
860+
}
861+
861862
function createArrayIterativeMethod (name, useTruthy = false) {
862863
return {
863864
deterministic: (data, buildState) => {
@@ -951,19 +952,6 @@ defaultMethods.max.compile = function (data, buildState) {
951952
.join(', ')})`
952953
}
953954

954-
for (const op of ['>', '<', '>=', '<=', '==', '!=', '!==', '===']) {
955-
const opStr = { [Compiled]: op }
956-
// @ts-ignore Allow custom attribute
957-
defaultMethods[op].compile = function (data, buildState) {
958-
if (!Array.isArray(data)) return false
959-
if (data.length < 2) return false
960-
if (data.length === 2) return buildState.compile`(${data[0]} ${opStr} ${data[1]})`
961-
let res = buildState.compile`(${data[0]} ${opStr} (prev = ${data[1]}))`
962-
for (let i = 2; i < data.length; i++) res = buildState.compile`(${res} && prev ${opStr} ${data[i]})`
963-
return res
964-
}
965-
}
966-
967955
// @ts-ignore Allow custom attribute
968956
defaultMethods.if.compile = function (data, buildState) {
969957
if (!Array.isArray(data)) return false

suites/comparison.json

+48
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,53 @@
6565
"rule": { "===": [3, 3, 2] },
6666
"data": {},
6767
"result": false
68+
},
69+
{
70+
"description": "< is lazily evaluated",
71+
"rule": { "<": [3, 2, { "throw": "Not Lazy" }] },
72+
"data": {},
73+
"result": false
74+
},
75+
{
76+
"description": "> is lazily evaluated",
77+
"rule": { ">": [2, 3, { "throw": "Not Lazy" }] },
78+
"data": {},
79+
"result": false
80+
},
81+
{
82+
"description": "<= is lazily evaluated",
83+
"rule": { "<=": [3, 2, { "throw": "Not Lazy" }] },
84+
"data": {},
85+
"result": false
86+
},
87+
{
88+
"description": ">= is lazily evaluated",
89+
"rule": { ">=": [2, 3, { "throw": "Not Lazy" }] },
90+
"data": {},
91+
"result": false
92+
},
93+
{
94+
"description": "== is lazily evaluated",
95+
"rule": { "==": [3, 2, { "throw": "Not Lazy" }] },
96+
"data": {},
97+
"result": false
98+
},
99+
{
100+
"description": "!= is lazily evaluated",
101+
"rule": { "!=": [3, 3, { "throw": "Not Lazy" }] },
102+
"data": {},
103+
"result": false
104+
},
105+
{
106+
"description": "=== is lazily evaluated",
107+
"rule": { "===": [3, 2, { "throw": "Not Lazy" }] },
108+
"data": {},
109+
"result": false
110+
},
111+
{
112+
"description": "!== is lazily evaluated",
113+
"rule": { "!==": [3, 3, { "throw": "Not Lazy" }] },
114+
"data": {},
115+
"result": false
68116
}
69117
]

0 commit comments

Comments
 (0)