kni 4.0.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +9 -7
- package/console.js +33 -35
- package/describe.js +54 -89
- package/document.js +103 -72
- package/engine.js +436 -407
- package/entry.js +88 -0
- package/evaluate.js +221 -228
- package/excerpt.js +117 -115
- package/grammar.js +1025 -785
- package/html.js +174 -167
- package/inline-lexer.js +155 -125
- package/kni.js +286 -279
- package/link.js +50 -52
- package/outline-lexer.js +64 -37
- package/package.json +27 -34
- package/parser.js +32 -20
- package/path.js +34 -46
- package/readline.js +89 -79
- package/scanner.js +101 -78
- package/scope.js +32 -36
- package/story.js +174 -165
- package/test.js +6 -0
- package/translate-json.js +3 -5
- package/tsconfig.json +11 -0
- package/verify.js +121 -117
- package/wrapper.js +37 -41
- package/template.js +0 -69
package/entry.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import Engine from './engine.js';
|
|
2
|
+
import Document from './document.js';
|
|
3
|
+
import story from 'virtual:story';
|
|
4
|
+
|
|
5
|
+
const handler = {
|
|
6
|
+
storageKey: 'kni',
|
|
7
|
+
|
|
8
|
+
shouldLog: true,
|
|
9
|
+
log(...args) {
|
|
10
|
+
if (this.shouldLog) {
|
|
11
|
+
console.log(...args);
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
load() {
|
|
16
|
+
if (window.location.hash.length > 1) {
|
|
17
|
+
const json = atob(window.location.hash.slice(1));
|
|
18
|
+
return JSON.parse(json);
|
|
19
|
+
}
|
|
20
|
+
const json = window.localStorage.getItem(this.storageKey);
|
|
21
|
+
if (json) {
|
|
22
|
+
const state = JSON.parse(json);
|
|
23
|
+
window.history.replaceState(state, '', `#${btoa(json)}`);
|
|
24
|
+
return state;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
},
|
|
28
|
+
waypoint(waypoint) {
|
|
29
|
+
const json = JSON.stringify(waypoint);
|
|
30
|
+
window.history.pushState(waypoint, '', `#${btoa(json)}`);
|
|
31
|
+
localStorage.setItem(this.storageKey, json);
|
|
32
|
+
},
|
|
33
|
+
goto(label) {
|
|
34
|
+
this.log(label);
|
|
35
|
+
},
|
|
36
|
+
answer(text) {
|
|
37
|
+
this.log('>', text);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const meterFaultButton = document.createElement('a');
|
|
42
|
+
meterFaultButton.innerText = 'Continue…';
|
|
43
|
+
meterFaultButton.addEventListener('click', () => {
|
|
44
|
+
engine.clearMeterFault();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const doc = new Document(document.body, {
|
|
48
|
+
meterFaultButton,
|
|
49
|
+
pageTurnBehavior: 'fade',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const engine = new Engine({
|
|
53
|
+
story: story,
|
|
54
|
+
render: doc,
|
|
55
|
+
dialog: doc,
|
|
56
|
+
handler,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
window.onpopstate = event => {
|
|
60
|
+
handler.log('>', 'back');
|
|
61
|
+
engine.resume(event.state);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
window.onkeypress = event => {
|
|
65
|
+
const code = event.code;
|
|
66
|
+
const match = /^Digit(\d+)$/.exec(code);
|
|
67
|
+
if (match) {
|
|
68
|
+
engine.answer(match[1]);
|
|
69
|
+
} else if (code === 'KeyR') {
|
|
70
|
+
engine.reset();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const reset = document.querySelector('.reset');
|
|
75
|
+
if (reset) {
|
|
76
|
+
reset.onclick = () => {
|
|
77
|
+
engine.reset();
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
doc.clear();
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
engine.resume(handler.load());
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error('unable to load prior state, restarting', error);
|
|
87
|
+
engine.resume(null);
|
|
88
|
+
}
|
package/evaluate.js
CHANGED
|
@@ -1,249 +1,242 @@
|
|
|
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
|
-
var name = args[1][1];
|
|
29
|
-
var f = functions[name];
|
|
30
|
-
if (!f) {
|
|
31
|
-
// TODO thread line number for containing instruction
|
|
32
|
-
throw new Error('No function named ' + name);
|
|
33
|
-
}
|
|
34
|
-
var values = [];
|
|
35
|
-
for (var i = 2; i < args.length; i++) {
|
|
36
|
-
values.push(evaluate(scope, randomer, args[i]));
|
|
37
|
-
}
|
|
38
|
-
return f.apply(null, values);
|
|
39
|
-
} else {
|
|
40
|
-
throw new Error('Unexpected operator ' + JSON.stringify(args));
|
|
1
|
+
const evaluate = (scope, randomer, args) => {
|
|
2
|
+
const name = args[0];
|
|
3
|
+
if (unary[name] && args.length === 2) {
|
|
4
|
+
return unary[name](evaluate(scope, randomer, args[1]), scope, randomer);
|
|
5
|
+
} else if (binary[name] && args.length === 3) {
|
|
6
|
+
return binary[name](
|
|
7
|
+
evaluate(scope, randomer, args[1]),
|
|
8
|
+
evaluate(scope, randomer, args[2]),
|
|
9
|
+
scope,
|
|
10
|
+
randomer
|
|
11
|
+
);
|
|
12
|
+
} else if (name === 'val') {
|
|
13
|
+
return args[1];
|
|
14
|
+
} else if (name === 'get') {
|
|
15
|
+
return scope.get(args[1]);
|
|
16
|
+
} else if (name === 'var') {
|
|
17
|
+
return scope.get(nominate(scope, randomer, args));
|
|
18
|
+
} else if (name === 'call') {
|
|
19
|
+
const func = args[1][1];
|
|
20
|
+
const f = functions[func];
|
|
21
|
+
if (!f) {
|
|
22
|
+
// TODO thread line number for containing instruction
|
|
23
|
+
throw new Error(`No function named ${func}`);
|
|
24
|
+
}
|
|
25
|
+
const values = [];
|
|
26
|
+
for (let i = 2; i < args.length; i++) {
|
|
27
|
+
values.push(evaluate(scope, randomer, args[i]));
|
|
41
28
|
}
|
|
42
|
-
|
|
29
|
+
return f.apply(null, values);
|
|
30
|
+
} else {
|
|
31
|
+
throw new Error(`Unexpected operator ${JSON.stringify(args)}`);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
43
34
|
|
|
35
|
+
const nominate = (scope, randomer, args) => {
|
|
36
|
+
if (args[0] === 'get') {
|
|
37
|
+
return args[1];
|
|
38
|
+
}
|
|
39
|
+
const literals = args[1];
|
|
40
|
+
const variables = args[2];
|
|
41
|
+
let name = '';
|
|
42
|
+
let i;
|
|
43
|
+
for (i = 0; i < variables.length; i++) {
|
|
44
|
+
name += literals[i] + evaluate(scope, randomer, variables[i]);
|
|
45
|
+
}
|
|
46
|
+
name += literals[i];
|
|
47
|
+
return name;
|
|
48
|
+
};
|
|
44
49
|
evaluate.nominate = nominate;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
|
|
51
|
+
const functions = {
|
|
52
|
+
abs: Math.abs,
|
|
53
|
+
acos: Math.acos,
|
|
54
|
+
asin: Math.asin,
|
|
55
|
+
atan2: Math.atan2,
|
|
56
|
+
atan: Math.atan,
|
|
57
|
+
exp: Math.exp,
|
|
58
|
+
log: Math.log,
|
|
59
|
+
max: Math.max,
|
|
60
|
+
min: Math.min,
|
|
61
|
+
pow: Math.pow,
|
|
62
|
+
sin: Math.sin,
|
|
63
|
+
tan: Math.tan,
|
|
64
|
+
|
|
65
|
+
floor: Math.floor,
|
|
66
|
+
ceil: Math.ceil,
|
|
67
|
+
round: Math.round,
|
|
68
|
+
|
|
69
|
+
sign: x => {
|
|
70
|
+
if (x < 0) {
|
|
71
|
+
return -1;
|
|
48
72
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
var name = '';
|
|
52
|
-
for (var i = 0; i < variables.length; i++) {
|
|
53
|
-
name += literals[i] + evaluate(scope, randomer, variables[i]);
|
|
73
|
+
if (x > 0) {
|
|
74
|
+
return 1;
|
|
54
75
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
log: Math.log,
|
|
67
|
-
max: Math.max,
|
|
68
|
-
min: Math.min,
|
|
69
|
-
pow: Math.pow,
|
|
70
|
-
sin: Math.sin,
|
|
71
|
-
tan: Math.tan,
|
|
72
|
-
|
|
73
|
-
floor: Math.floor,
|
|
74
|
-
ceil: Math.floor,
|
|
75
|
-
round: Math.floor,
|
|
76
|
-
|
|
77
|
-
sign: function (x) {
|
|
78
|
-
if (x < 0) {
|
|
79
|
-
return -1;
|
|
80
|
-
}
|
|
81
|
-
if (x > 0) {
|
|
82
|
-
return 1;
|
|
83
|
-
}
|
|
84
|
-
return 0;
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
mean: function () {
|
|
88
|
-
var mean = 0;
|
|
89
|
-
for (var i = 0; i < arguments.length; i++) {
|
|
90
|
-
mean += arguments[i];
|
|
91
|
-
}
|
|
92
|
-
return mean / i;
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
root: function root(x, y) {
|
|
96
|
-
if (y === 2 || y == null) {
|
|
97
|
-
return Math.sqrt(x);
|
|
98
|
-
}
|
|
99
|
-
return Math.pow(x, 1 / y);
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
distance: function distance(x1, y1, x2, y2) {
|
|
103
|
-
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
manhattan: function distance(x1, y1, x2, y2) {
|
|
107
|
-
return Math.abs(x2 - x1, 2) + Math.abs(y2 - y1);
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
// TODO parameterize these functions in terms of the expected turns to
|
|
111
|
-
// go from 25% to 75% of capacity, to adjust the rate. This will maybe
|
|
112
|
-
// almost make them understandable.
|
|
113
|
-
//
|
|
114
|
-
// sigmoid: function (steps, cap) {
|
|
115
|
-
// if (steps === -Infinity) {
|
|
116
|
-
// return 0;
|
|
117
|
-
// } else if (steps === Infinity) {
|
|
118
|
-
// return cap;
|
|
119
|
-
// } else {
|
|
120
|
-
// return cap / (1 + Math.pow(Math.E, -steps));
|
|
121
|
-
// }
|
|
122
|
-
// },
|
|
123
|
-
|
|
124
|
-
// diomgis: function (pop, cap) {
|
|
125
|
-
// if (pop <= 0) {
|
|
126
|
-
// return -Infinity;
|
|
127
|
-
// }
|
|
128
|
-
// var ratio = cap / pop - 1;
|
|
129
|
-
// if (ratio === 0) {
|
|
130
|
-
// return Infinity;
|
|
131
|
-
// }
|
|
132
|
-
// return -Math.log(ratio, Math.E);
|
|
133
|
-
// },
|
|
76
|
+
return 0;
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
mean: function () {
|
|
80
|
+
let mean = 0;
|
|
81
|
+
let i;
|
|
82
|
+
for (i = 0; i < arguments.length; i++) {
|
|
83
|
+
mean += arguments[i];
|
|
84
|
+
}
|
|
85
|
+
return mean / i;
|
|
86
|
+
},
|
|
134
87
|
|
|
88
|
+
root: (x, y) => {
|
|
89
|
+
if (y === 2 || y == null) {
|
|
90
|
+
return Math.sqrt(x);
|
|
91
|
+
}
|
|
92
|
+
return Math.pow(x, 1 / y);
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
distance: (x1, y1, x2, y2) => {
|
|
96
|
+
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
manhattan: (x1, y1, x2, y2) => {
|
|
100
|
+
return Math.abs(x2 - x1) + Math.abs(y2 - y1);
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
// TODO parameterize these functions in terms of the expected turns to
|
|
104
|
+
// go from 25% to 75% of capacity, to adjust the rate. This will maybe
|
|
105
|
+
// almost make them understandable.
|
|
106
|
+
//
|
|
107
|
+
// sigmoid: (steps, cap) => {
|
|
108
|
+
// if (steps === -Infinity) {
|
|
109
|
+
// return 0;
|
|
110
|
+
// } else if (steps === Infinity) {
|
|
111
|
+
// return cap;
|
|
112
|
+
// } else {
|
|
113
|
+
// return cap / (1 + Math.pow(Math.E, -steps));
|
|
114
|
+
// }
|
|
115
|
+
// },
|
|
116
|
+
|
|
117
|
+
// diomgis: (pop, cap) => {
|
|
118
|
+
// if (pop <= 0) {
|
|
119
|
+
// return -Infinity;
|
|
120
|
+
// }
|
|
121
|
+
// const ratio = cap / pop - 1;
|
|
122
|
+
// if (ratio === 0) {
|
|
123
|
+
// return Infinity;
|
|
124
|
+
// }
|
|
125
|
+
// return -Math.log(ratio, Math.E);
|
|
126
|
+
// },
|
|
135
127
|
};
|
|
136
128
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
return Math.floor(r);
|
|
129
|
+
const binary = {
|
|
130
|
+
'+': (x, y) => {
|
|
131
|
+
return x + y;
|
|
132
|
+
},
|
|
133
|
+
'-': (x, y) => {
|
|
134
|
+
return x - y;
|
|
135
|
+
},
|
|
136
|
+
'*': (x, y) => {
|
|
137
|
+
return x * y;
|
|
138
|
+
},
|
|
139
|
+
'/': (x, y) => {
|
|
140
|
+
return (x / y) >> 0;
|
|
141
|
+
},
|
|
142
|
+
'%': (x, y) => {
|
|
143
|
+
return ((x % y) + y) % y;
|
|
144
|
+
},
|
|
145
|
+
'**': (x, y) => {
|
|
146
|
+
return Math.pow(x, y);
|
|
147
|
+
},
|
|
148
|
+
or: (x, y) => {
|
|
149
|
+
return x || y ? 1 : 0;
|
|
150
|
+
},
|
|
151
|
+
and: (x, y) => {
|
|
152
|
+
return x && y ? 1 : 0;
|
|
153
|
+
},
|
|
154
|
+
'>=': (x, y) => {
|
|
155
|
+
return x >= y ? 1 : 0;
|
|
156
|
+
},
|
|
157
|
+
'>': (x, y) => {
|
|
158
|
+
return x > y ? 1 : 0;
|
|
159
|
+
},
|
|
160
|
+
'<=': (x, y) => {
|
|
161
|
+
return x <= y ? 1 : 0;
|
|
162
|
+
},
|
|
163
|
+
'<': (x, y) => {
|
|
164
|
+
return x < y ? 1 : 0;
|
|
165
|
+
},
|
|
166
|
+
'==': (x, y) => {
|
|
167
|
+
return x === y ? 1 : 0;
|
|
168
|
+
},
|
|
169
|
+
'<>': (x, y) => {
|
|
170
|
+
return x != y ? 1 : 0;
|
|
171
|
+
},
|
|
172
|
+
'#': (x, y) => {
|
|
173
|
+
return hilbert(x, y);
|
|
174
|
+
},
|
|
175
|
+
'~': (x, y, _scope, randomer) => {
|
|
176
|
+
let r = 0;
|
|
177
|
+
for (let i = 0; i < x; i++) {
|
|
178
|
+
r += randomer.random() * y;
|
|
189
179
|
}
|
|
180
|
+
return Math.floor(r);
|
|
181
|
+
},
|
|
190
182
|
};
|
|
191
183
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
184
|
+
const unary = {
|
|
185
|
+
not: x => {
|
|
186
|
+
return x ? 0 : 1;
|
|
187
|
+
},
|
|
188
|
+
'-': x => {
|
|
189
|
+
return -x;
|
|
190
|
+
},
|
|
191
|
+
'~': (x, _scope, randomer) => {
|
|
192
|
+
return Math.floor(randomer.random() * x);
|
|
193
|
+
},
|
|
194
|
+
'#': x => {
|
|
195
|
+
return hash(x);
|
|
196
|
+
},
|
|
206
197
|
};
|
|
207
198
|
|
|
208
199
|
// Robert Jenkins's 32 bit hash function
|
|
209
200
|
// https://gist.github.com/badboy/6267743
|
|
201
|
+
const hash = a => {
|
|
202
|
+
a = a + 0x7ed55d16 + (a << 12);
|
|
203
|
+
a = a ^ 0xc761c23c ^ (a >>> 19);
|
|
204
|
+
a = a + 0x165667b1 + (a << 5);
|
|
205
|
+
a = (a + 0xd3a2646c) ^ (a << 9);
|
|
206
|
+
a = a + 0xfd7046c5 + (a << 3);
|
|
207
|
+
a = a ^ 0xb55a4f09 ^ (a >>> 16);
|
|
208
|
+
return a;
|
|
209
|
+
};
|
|
210
210
|
evaluate.hash = hash;
|
|
211
|
-
function hash(a) {
|
|
212
|
-
a = (a+0x7ed55d16) + (a<<12);
|
|
213
|
-
a = (a^0xc761c23c) ^ (a>>>19);
|
|
214
|
-
a = (a+0x165667b1) + (a<<5);
|
|
215
|
-
a = (a+0xd3a2646c) ^ (a<<9);
|
|
216
|
-
a = (a+0xfd7046c5) + (a<<3);
|
|
217
|
-
a = (a^0xb55a4f09) ^ (a>>>16);
|
|
218
|
-
return a;
|
|
219
|
-
}
|
|
220
211
|
|
|
221
212
|
// hilbert in range from 0 to 2^32
|
|
222
213
|
// x and y in range from 0 to 2^16
|
|
223
214
|
// each dimension has origin at 2^15
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
215
|
+
const dimensionWidth = (-1 >>> 16) + 1;
|
|
216
|
+
const halfDimensionWidth = dimensionWidth / 2;
|
|
217
|
+
const hilbert = (x, y) => {
|
|
218
|
+
x += halfDimensionWidth;
|
|
219
|
+
y += halfDimensionWidth;
|
|
220
|
+
let rx = 0;
|
|
221
|
+
let ry = y;
|
|
222
|
+
let scalar = 0;
|
|
223
|
+
for (let scale = dimensionWidth; scale > 0; scale /= 2) {
|
|
224
|
+
rx = x & scale;
|
|
225
|
+
ry = y & scale;
|
|
226
|
+
scalar += scale * ((3 * rx) ^ ry);
|
|
227
|
+
// rotate
|
|
228
|
+
if (!ry) {
|
|
229
|
+
if (rx) {
|
|
230
|
+
x = scale - 1 - x;
|
|
231
|
+
y = scale - 1 - y;
|
|
232
|
+
}
|
|
233
|
+
// transpose
|
|
234
|
+
const t = x;
|
|
235
|
+
x = y;
|
|
236
|
+
y = t;
|
|
247
237
|
}
|
|
248
|
-
|
|
249
|
-
|
|
238
|
+
}
|
|
239
|
+
return scalar;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export default evaluate;
|