forked from mozilla/fathom
-
Notifications
You must be signed in to change notification settings - Fork 0
/
side.mjs
133 lines (107 loc) · 2.94 KB
/
side.mjs
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
126
127
128
129
130
131
132
133
import {Lhs} from './lhs';
import {InwardRhs} from './rhs';
function props(callback) {
return new Side({method: 'props', args: [callback]});
}
/** Constrain to an input type on the LHS, or apply a type on the RHS. */
function type(theType) {
return new Side({method: 'type', args: [theType]});
}
function note(callback) {
return new Side({method: 'note', args: [callback]});
}
function score(scoreOrCallback) {
return new Side({method: 'score', args: [scoreOrCallback]});
}
function atMost(score) {
return new Side({method: 'atMost', args: [score]});
}
function typeIn(...types) {
return new Side({method: 'typeIn', args: types});
}
function conserveScore() {
return new Side({method: 'conserveScore', args: []});
}
/**
* Experimental. Pull nodes that conform to multiple conditions at once.
*
* For example: ``and(type('title'), type('english'))``
*
* Caveats: ``and`` supports only simple ``type`` calls as arguments for now,
* and it may fire off more rules as prerequisites than strictly necessary.
* ``not`` and ``or`` don't exist yet, but you can express ``or`` the long way
* around by having 2 rules with identical RHSs.
*/
function and(...lhss) {
return new Side({method: 'and', args: lhss});
}
/**
* A chain of calls that can be compiled into a Rhs or Lhs, depending on its
* position in a Rule. This lets us use type() as a leading call for both RHSs
* and LHSs. I would prefer to do this dynamically, but that wouldn't compile
* down to old versions of ES.
*/
class Side {
constructor(...calls) {
// A "call" is like {method: 'dom', args: ['p.smoo']}.
this._calls = calls;
}
max() {
return this._and('max');
}
bestCluster(options) {
return this._and('bestCluster', options);
}
props(callback) {
return this._and('props', callback);
}
type(...types) {
return this._and('type', ...types);
}
note(callback) {
return this._and('note', callback);
}
score(scoreOrCallback) {
return this._and('score', scoreOrCallback);
}
atMost(score) {
return this._and('atMost', score);
}
typeIn(...types) {
return this._and('typeIn', ...types);
}
conserveScore() {
return this._and('conserveScore');
}
and(...lhss) {
return this._and('and', lhss);
}
_and(method, ...args) {
return new this.constructor(...this._calls.concat({method, args}));
}
asLhs() {
return this._asSide(Lhs.fromFirstCall(this._calls[0]), this._calls.slice(1));
}
asRhs() {
return this._asSide(new InwardRhs(), this._calls);
}
_asSide(side, calls) {
for (let call of calls) {
side = side[call.method](...call.args);
}
return side;
}
when(pred) {
return this._and('when', pred);
}
}
export {
and,
atMost,
conserveScore,
note,
props,
score,
type,
typeIn
};