-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.js
112 lines (101 loc) · 2.14 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import mapObj from "map-obj";
export const compile = (rule) => {
let bytecode = "__0__";
let step = 0;
mapObj(
rule,
(op, val) => {
if (operatorToRuntime[op] || userOperations[op]) {
let nextStep = step;
const arity = calcOperatorArity(op, val);
const bytecodeParts = [...Array(arity).keys()].map(
(idx) =>
`(${
isObject(val[idx])
? `__${++nextStep}__`
: JSON.stringify(val[idx])
})`
);
bytecode = bytecode.replace(
`__${step}__`,
operatorToRuntime[op]
? arity === 1
? `${operatorToRuntime[op]}${bytecodeParts.join()}`
: bytecodeParts.join(` ${operatorToRuntime[op]} `)
: `userOperations["${op}"](${bytecodeParts
.concat("data")
.join(", ")})`
);
} else {
throw new Error(
`Operator "${op}" is unknown. Use built-in operators or add own`
);
}
step += 1;
return [op, val];
},
{ deep: true }
);
const fn = new Function("userOperations", "data", `return ${bytecode}`).bind(
null,
userOperations
);
fn.bytecode = bytecode;
return fn;
};
export const addOperation = (name, fn) => {
userOperations[name] = fn;
};
const userOperations = {};
const isObject = (value) => {
return value != null && typeof value === "object";
};
const operatorToRuntime = {
or: "||",
and: "&&",
">": ">",
"<": "<",
">=": ">=",
"<=": "<=",
"==": "==",
"===": "===",
"!=": "!=",
"!==": "!==",
"+": "+",
"-": "-",
"*": "*",
"/": "/",
"%": "%",
"!": "!",
"!!": "!!",
};
const operatorArity = {
or: 0,
and: 0,
"+": 0,
"-": 0,
"*": 0,
"/": 0,
"!": 1,
"!!": 1,
"%": 2,
">": 2,
"<": 2,
">=": 2,
"<=": 2,
"==": 2,
"===": 2,
"!=": 2,
"!==": 2,
};
const calcOperatorArity = (operator = "", value = []) => {
if (operatorArity[operator] > 0) {
return operatorArity[operator];
}
if (operatorArity[operator] === 0) {
return value.length;
}
if (userOperations[operator]) {
return value.length;
}
};