forked from montagejs/frb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathalgebra.js
126 lines (117 loc) · 3.75 KB
/
algebra.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
113
114
115
116
117
118
119
120
121
122
123
124
125
// TODO commute literals on the left side of a target operand, when possible
module.exports = solve;
function solve(target, source) {
return solve.semantics.solve(target, source);
}
solve.semantics = {
solve: function (target, source) {
while (true) {
// simplify the target
while (this.simplifiers.hasOwnProperty(target.type)) {
var simplification = this.simplifiers[target.type](target);
if (simplification) {
target = simplification;
} else {
break;
}
}
// solve for bindable target (rotate terms to source)
if (!this.solvers.hasOwnProperty(target.type)) {
break;
}
source = this.solvers[target.type](target, source);
target = target.args[0];
}
return [target, source];
},
simplifiers: {
// !!x -> x
not: function (syntax) {
var left = syntax.args[0];
if (left.type === "not") {
return left.args[0];
}
},
// "" + x -> x.toString()
add: function (syntax) {
var left = syntax.args[0];
if (left.type === "literal" && left.value === "") {
// "" + x
// toString(x)
// because this can be bound bidirectionally with toNumber(y)
return {
type: "toString",
args: [syntax.args[1]]
};
}
},
// DeMorgan's law applied to `some` so we only have to implement
// `every`.
// some{x} -> !every{!x}
someBlock: function (syntax) {
return {type: "not", args: [
{type: "everyBlock", args: [
syntax.args[0],
{type: "not", args: [
syntax.args[1]
]}
]}
]};
}
},
solvers: {
// e.g.,
// !y = x
// y = !x
reflect: function (target, source) {
return {type: target.type, args: [source]};
},
// e.g.,
// y + 1 = x
// y = x - 1
invert: function (target, source, operator) {
return {type: operator, args: [
source,
target.args[1]
]};
},
toNumber: function (target, source) {
return this.reflect(target, source);
},
toString: function (target, source) {
return this.reflect(target, source);
},
not: function (target, source) {
return this.reflect(target, source);
},
neg: function (target, source) {
return this.reflect(target, source);
},
add: function (target, source) {
return this.invert(target, source, 'sub');
},
sub: function (target, source) {
return this.invert(target, source, 'add');
},
mul: function (target, source) {
return this.invert(target, source, 'div');
},
div: function (target, source) {
return this.invert(target, source, 'mul');
},
pow: function (target, source) {
return this.invert(target, source, 'root');
},
root: function (target, source) {
return this.invert(target, source, 'pow');
},
// terms.join(delimiter) <- string
// terms <- string.split(delimiter)
join: function (target, source) {
return this.invert(target, source, 'split');
},
split: function (target, source) {
return this.invert(target, source, 'join');
}
}
};