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/story.js
CHANGED
|
@@ -1,178 +1,187 @@
|
|
|
1
|
-
|
|
1
|
+
import * as Path from './path.js';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var constructors = {};
|
|
6
|
-
|
|
7
|
-
module.exports = Story;
|
|
8
|
-
|
|
9
|
-
function Story() {
|
|
3
|
+
export default class Story {
|
|
4
|
+
constructor() {
|
|
10
5
|
this.states = {};
|
|
11
6
|
this.errors = [];
|
|
12
7
|
Object.seal(this);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
Story.constructors = constructors;
|
|
8
|
+
}
|
|
16
9
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// istanbul ignore if
|
|
10
|
+
create(path, type, arg, position) {
|
|
11
|
+
const name = Path.toName(path);
|
|
12
|
+
const Node = this.constructors[type];
|
|
21
13
|
if (!Node) {
|
|
22
|
-
|
|
14
|
+
throw new Error(`No node constructor for type: ${type}`);
|
|
23
15
|
}
|
|
24
|
-
|
|
16
|
+
const node = new Node(arg);
|
|
25
17
|
node.position = position;
|
|
26
18
|
this.states[name] = node;
|
|
27
19
|
return node;
|
|
28
|
-
}
|
|
20
|
+
}
|
|
29
21
|
|
|
30
|
-
|
|
31
|
-
Story.prototype.error = function _error(error) {
|
|
22
|
+
error(error) {
|
|
32
23
|
this.errors.push(error);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
constructors
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
constructors = {
|
|
27
|
+
text: class Text {
|
|
28
|
+
constructor(text) {
|
|
29
|
+
this.type = 'text';
|
|
30
|
+
this.text = text;
|
|
31
|
+
this.lift = ' ';
|
|
32
|
+
this.drop = ' ';
|
|
33
|
+
this.next = 'RET';
|
|
34
|
+
this.position = null;
|
|
35
|
+
Object.seal(this);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
echo: class Echo {
|
|
40
|
+
constructor(expression) {
|
|
41
|
+
this.type = 'echo';
|
|
42
|
+
this.expression = expression;
|
|
43
|
+
this.lift = '';
|
|
44
|
+
this.drop = '';
|
|
45
|
+
this.next = 'RET';
|
|
46
|
+
this.position = null;
|
|
47
|
+
Object.seal(this);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
option: class Option {
|
|
52
|
+
constructor(_label) {
|
|
53
|
+
this.type = 'opt';
|
|
54
|
+
this.question = [];
|
|
55
|
+
this.answer = [];
|
|
56
|
+
this.keywords = null;
|
|
57
|
+
this.next = 'RET';
|
|
58
|
+
this.position = null;
|
|
59
|
+
Object.seal(this);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
goto: class Goto {
|
|
64
|
+
constructor(next) {
|
|
65
|
+
this.type = 'goto';
|
|
66
|
+
this.next = next;
|
|
67
|
+
this.position = null;
|
|
68
|
+
Object.seal(this);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
call: class Call {
|
|
73
|
+
constructor(label) {
|
|
74
|
+
this.type = 'call';
|
|
75
|
+
this.label = label;
|
|
76
|
+
this.args = null;
|
|
77
|
+
this.next = 'RET';
|
|
78
|
+
this.branch = 'RET';
|
|
79
|
+
this.position = null;
|
|
80
|
+
Object.seal(this);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
cue: class Cue {
|
|
85
|
+
constructor(cue) {
|
|
86
|
+
this.type = 'cue';
|
|
87
|
+
this.cue = cue;
|
|
88
|
+
this.next = 'RET';
|
|
89
|
+
this.position = null;
|
|
90
|
+
Object.seal(this);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
def: class Def {
|
|
95
|
+
constructor(locals) {
|
|
96
|
+
this.type = 'def';
|
|
97
|
+
this.locals = locals;
|
|
98
|
+
this.next = 'RET';
|
|
99
|
+
this.position = null;
|
|
100
|
+
Object.seal(this);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
jump: class Jump {
|
|
105
|
+
constructor(condition) {
|
|
106
|
+
this.type = 'jump';
|
|
107
|
+
this.condition = condition;
|
|
108
|
+
this.branch = 'RET';
|
|
109
|
+
this.next = 'RET';
|
|
110
|
+
this.position = null;
|
|
111
|
+
Object.seal(this);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
switch: class Switch {
|
|
116
|
+
constructor(expression) {
|
|
117
|
+
this.type = 'switch';
|
|
118
|
+
this.expression = expression;
|
|
119
|
+
this.variable = null;
|
|
120
|
+
this.value = 0;
|
|
121
|
+
this.mode = null;
|
|
122
|
+
this.branches = [];
|
|
123
|
+
this.weights = [];
|
|
124
|
+
this.next = 'RET';
|
|
125
|
+
this.position = null;
|
|
126
|
+
Object.seal(this);
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
move: class Move {
|
|
131
|
+
constructor() {
|
|
132
|
+
this.type = 'move';
|
|
133
|
+
this.source = null;
|
|
134
|
+
this.target = null;
|
|
135
|
+
this.next = 'RET';
|
|
136
|
+
this.position = null;
|
|
137
|
+
Object.seal(this);
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
break: class Break {
|
|
142
|
+
constructor() {
|
|
143
|
+
this.type = 'br';
|
|
144
|
+
this.next = 'RET';
|
|
145
|
+
this.position = null;
|
|
146
|
+
Object.seal(this);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
paragraph: class Paragraph {
|
|
151
|
+
constructor() {
|
|
152
|
+
this.type = 'par';
|
|
153
|
+
this.next = 'RET';
|
|
154
|
+
this.position = null;
|
|
155
|
+
Object.seal(this);
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
rule: class Rule {
|
|
160
|
+
constructor() {
|
|
161
|
+
this.type = 'rule';
|
|
162
|
+
this.next = 'RET';
|
|
163
|
+
this.position = null;
|
|
164
|
+
Object.seal(this);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
ask: class Ask {
|
|
169
|
+
constructor() {
|
|
170
|
+
this.type = 'ask';
|
|
171
|
+
this.position = null;
|
|
172
|
+
Object.seal(this);
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
read: class Read {
|
|
177
|
+
constructor(variable) {
|
|
178
|
+
this.type = 'read';
|
|
179
|
+
this.next = 'RET';
|
|
180
|
+
this.variable = variable;
|
|
181
|
+
this.cue = null;
|
|
182
|
+
this.position = null;
|
|
183
|
+
Object.seal(this);
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
};
|
|
178
187
|
}
|
package/test.js
ADDED
package/translate-json.js
CHANGED
package/tsconfig.json
ADDED
package/verify.js
CHANGED
|
@@ -1,143 +1,147 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
var answers = [];
|
|
22
|
-
for (var i = 0; i < lines.length; i++) {
|
|
23
|
-
var line = lines[i];
|
|
24
|
-
if (line.lastIndexOf('>', 0) === 0) {
|
|
25
|
-
answers.push(line.slice(1).trim());
|
|
26
|
-
}
|
|
1
|
+
import xorshift from 'xorshift';
|
|
2
|
+
import Engine from './engine.js';
|
|
3
|
+
import Console from './console.js';
|
|
4
|
+
import Scanner from './scanner.js';
|
|
5
|
+
import OutlineLexer from './outline-lexer.js';
|
|
6
|
+
import InlineLexer from './inline-lexer.js';
|
|
7
|
+
import Parser from './parser.js';
|
|
8
|
+
import Story from './story.js';
|
|
9
|
+
import * as Path from './path.js';
|
|
10
|
+
import start from './grammar.js';
|
|
11
|
+
import link from './link.js';
|
|
12
|
+
|
|
13
|
+
const verify = (kni, trans, handler, kniscript) => {
|
|
14
|
+
const lines = trans.split('\n');
|
|
15
|
+
|
|
16
|
+
// filter the transcript for given answers
|
|
17
|
+
const answers = [];
|
|
18
|
+
for (const line of lines) {
|
|
19
|
+
if (line.lastIndexOf('>', 0) === 0) {
|
|
20
|
+
answers.push(line.slice(1).trim());
|
|
27
21
|
}
|
|
22
|
+
}
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
errors += story.errors[i] + '\n';
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (errors === trans) {
|
|
52
|
-
return {
|
|
53
|
-
pass: true,
|
|
54
|
-
expected: 'errors',
|
|
55
|
-
actual: 'errors',
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
for (var i = 0; i < story.errors.length; i++) {
|
|
60
|
-
console.error(story.errors[i]);
|
|
61
|
-
}
|
|
62
|
-
return {
|
|
63
|
-
pass: false,
|
|
64
|
-
expected: trans,
|
|
65
|
-
actual: errors,
|
|
66
|
-
};
|
|
24
|
+
const path = Path.start();
|
|
25
|
+
const base = [];
|
|
26
|
+
|
|
27
|
+
// build a story from the kni
|
|
28
|
+
const story = new Story();
|
|
29
|
+
const p = new Parser(start(story, path, base));
|
|
30
|
+
const il = new InlineLexer(p);
|
|
31
|
+
const ol = new OutlineLexer(il);
|
|
32
|
+
const s = new Scanner(ol, kniscript);
|
|
33
|
+
|
|
34
|
+
s.next(kni);
|
|
35
|
+
s.return();
|
|
36
|
+
|
|
37
|
+
link(story);
|
|
38
|
+
|
|
39
|
+
if (story.errors.length) {
|
|
40
|
+
let errors = '';
|
|
41
|
+
for (const err of story.errors) {
|
|
42
|
+
errors += `${err}\n`;
|
|
67
43
|
}
|
|
68
44
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
var writer = new StringWriter();
|
|
82
|
-
var render = new Console(writer);
|
|
83
|
-
var readline = new FakeReadline(writer, answers);
|
|
84
|
-
var engine = new Engine({
|
|
85
|
-
story: states,
|
|
86
|
-
start: 'start',
|
|
87
|
-
handler: handler,
|
|
88
|
-
render: render,
|
|
89
|
-
dialog: readline,
|
|
90
|
-
randomer: randomer
|
|
91
|
-
});
|
|
92
|
-
readline.engine = engine;
|
|
93
|
-
engine.reset();
|
|
94
|
-
|
|
95
|
-
var expected = trans.trim();
|
|
96
|
-
var actual = writer.string.trim();
|
|
45
|
+
if (errors === trans) {
|
|
46
|
+
return {
|
|
47
|
+
pass: true,
|
|
48
|
+
expected: 'errors',
|
|
49
|
+
actual: 'errors',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (const err of story.errors) {
|
|
54
|
+
console.error(err);
|
|
55
|
+
}
|
|
97
56
|
return {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
57
|
+
pass: false,
|
|
58
|
+
expected: trans,
|
|
59
|
+
actual: errors,
|
|
101
60
|
};
|
|
102
|
-
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const states = story.states;
|
|
64
|
+
|
|
65
|
+
// TODO support alternate seeds
|
|
66
|
+
const seed = 0;
|
|
67
|
+
// I rolled 4d64k this morning, for kni.js
|
|
68
|
+
const randomer = new xorshift.constructor([
|
|
69
|
+
37615 ^ seed,
|
|
70
|
+
54552 ^ seed,
|
|
71
|
+
59156 ^ seed,
|
|
72
|
+
24695 ^ seed,
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
const writer = new StringWriter();
|
|
76
|
+
const render = new Console(writer);
|
|
77
|
+
const readline = new FakeReadline(writer, answers, kniscript);
|
|
78
|
+
const engine = new Engine({
|
|
79
|
+
story: states,
|
|
80
|
+
start: 'start',
|
|
81
|
+
handler: handler,
|
|
82
|
+
render: render,
|
|
83
|
+
dialog: readline,
|
|
84
|
+
randomer: randomer,
|
|
85
|
+
});
|
|
86
|
+
readline.engine = engine;
|
|
87
|
+
engine.reset();
|
|
88
|
+
|
|
89
|
+
const expected = trans.trim();
|
|
90
|
+
const actual = writer.string.trim();
|
|
91
|
+
return {
|
|
92
|
+
pass: expected === actual,
|
|
93
|
+
expected: expected,
|
|
94
|
+
actual: actual,
|
|
95
|
+
};
|
|
96
|
+
};
|
|
103
97
|
|
|
104
|
-
|
|
98
|
+
export default verify;
|
|
99
|
+
|
|
100
|
+
class FakeReadline {
|
|
101
|
+
constructor(writer, answers, kniscript) {
|
|
105
102
|
this.writer = writer;
|
|
106
103
|
this.answers = answers;
|
|
104
|
+
this.kniscript = kniscript;
|
|
107
105
|
this.engine = null;
|
|
108
106
|
this.history = [];
|
|
109
107
|
Object.seal(this);
|
|
110
|
-
}
|
|
108
|
+
}
|
|
111
109
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// istanbul ignore if
|
|
110
|
+
ask(_question) {
|
|
111
|
+
const answer = this.answers.shift();
|
|
115
112
|
if (answer == null) {
|
|
116
|
-
|
|
113
|
+
return;
|
|
117
114
|
}
|
|
118
|
-
this.writer.write(
|
|
115
|
+
this.writer.write(`${`> ${answer}`.trim()}\n`);
|
|
119
116
|
|
|
120
117
|
if (answer === 'quit') {
|
|
118
|
+
// noop
|
|
121
119
|
} else if (answer === 'replay') {
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
this.writer.write('\n');
|
|
121
|
+
this.engine.resume(this.engine.waypoint);
|
|
124
122
|
} else if (answer === 'back') {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
this.writer.write('\n');
|
|
124
|
+
this.engine.waypoint = this.history.pop();
|
|
125
|
+
this.engine.resume(this.engine.waypoint);
|
|
128
126
|
} else {
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
this.history.push(this.engine.waypoint);
|
|
128
|
+
this.engine.answer(answer);
|
|
131
129
|
}
|
|
132
|
-
}
|
|
130
|
+
}
|
|
133
131
|
|
|
134
|
-
|
|
135
|
-
};
|
|
132
|
+
close() {}
|
|
136
133
|
|
|
137
|
-
|
|
138
|
-
this.
|
|
134
|
+
meterFault() {
|
|
135
|
+
throw new Error(`meter fault in ${this.kniscript}`);
|
|
136
|
+
}
|
|
139
137
|
}
|
|
140
138
|
|
|
141
|
-
StringWriter
|
|
139
|
+
class StringWriter {
|
|
140
|
+
constructor() {
|
|
141
|
+
this.string = '';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
write(string) {
|
|
142
145
|
this.string += string;
|
|
143
|
-
}
|
|
146
|
+
}
|
|
147
|
+
}
|