Skip to content

Commit 617116a

Browse files
committed
Meter the virtual machine
1 parent 844aeb5 commit 617116a

File tree

5 files changed

+74
-8
lines changed

5 files changed

+74
-8
lines changed

document.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export default class Document {
2-
constructor(element, createPage) {
2+
constructor(element, createPage, meterFaultButton) {
33
const self = this;
44
this.document = element.ownerDocument;
55
this.parent = element;
@@ -20,6 +20,8 @@ export default class Document {
2020
self.answer(event.target.number);
2121
};
2222
this.createPage = createPage || this.createPage;
23+
this.meterFaultButton = meterFaultButton;
24+
2325
Object.seal(this);
2426
}
2527

@@ -144,6 +146,12 @@ export default class Document {
144146
}
145147
}
146148

149+
meterFault() {
150+
if (this.meterFaultButton) {
151+
this.body.appendChild(this.meterFaultButton);
152+
}
153+
}
154+
147155
ask(_cue) {}
148156

149157
answer(text) {

engine.js

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ export default class Engine {
3434
this.story = args.story;
3535
this.labels = Object.keys(this.story);
3636
this.handler = args.handler;
37+
this.meter = 0;
38+
this.limit = 10e3; // bottles.kni, for example, runs long
3739
this.options = [];
3840
this.keywords = {};
3941
this.noOption = null;
@@ -47,6 +49,7 @@ export default class Engine {
4749
this.dialog.engine = this;
4850
this.randomer = args.randomer || Math;
4951
this.waypoint = this.capture();
52+
this.answerOnClearMeterFault = [];
5053
Object.seal(this);
5154
}
5255

@@ -63,11 +66,15 @@ export default class Engine {
6366
this.resume();
6467
}
6568

69+
/**
70+
* Runs the event loop until it yields.
71+
*/
6672
continue() {
67-
let _continue;
68-
do {
73+
this.meter = 0;
74+
for (;;) {
6975
if (this.debug) {
7076
console.log(`${this.label} ${this.instruction.type} ${describe(this.instruction)}`);
77+
console.log(this.top);
7178
}
7279
if (this.instruction == null) {
7380
// TODO user error for non-console interaction.
@@ -79,8 +86,31 @@ export default class Engine {
7986
console.error(`Unexpected instruction type: ${this.instruction.type}`, this.instruction);
8087
this.resume();
8188
}
82-
_continue = this[`$${this.instruction.type}`](this.instruction);
83-
} while (_continue);
89+
const proceed = this[`$${this.instruction.type}`](this.instruction);
90+
if (!proceed) {
91+
return;
92+
}
93+
this.meter += 1;
94+
if (this.meter >= this.limit) {
95+
this.display();
96+
this.dialog.meterFault();
97+
return;
98+
}
99+
}
100+
}
101+
102+
clearMeterFault() {
103+
if (this.meter >= this.limit) {
104+
this.render.clear();
105+
this.continue();
106+
107+
// flush answers posted while faulted
108+
const count = this.answerOnClearMeterFault.length;
109+
const answers = this.answerOnClearMeterFault.splice(0, count);
110+
for (const answer of answers) {
111+
this.answer(answer);
112+
}
113+
}
84114
}
85115

86116
goto(label) {
@@ -170,6 +200,10 @@ export default class Engine {
170200
}
171201

172202
answer(text) {
203+
if (this.meter >= this.limit) {
204+
this.answerOnClearMeterFault.push(text);
205+
return;
206+
}
173207
if (this.handler && this.handler.answer) {
174208
this.handler.answer(text, this);
175209
}
@@ -256,6 +290,9 @@ export default class Engine {
256290
];
257291
}
258292

293+
/**
294+
* Resumes from a snapshot.
295+
*/
259296
resume(snapshot) {
260297
this.render.clear();
261298
this.flush();

entry.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,13 @@ const handler = {
3838
},
3939
};
4040

41-
const doc = new Document(document.body);
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, null, meterFaultButton);
4248

4349
const engine = new Engine({
4450
story: story,

readline.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ export default class Readline {
1818
Object.seal(this);
1919
}
2020

21+
meterFault() {
22+
this.readline.question(`Enter any command to continue... `, (answer) => {
23+
if (answer === 'quit') {
24+
this.close();
25+
} else {
26+
this.engine.clearMeterFault();
27+
}
28+
});
29+
}
30+
2131
ask(cue) {
2232
this.readline.question(`${cue || ''}> `, this.boundAnswer);
2333
}

verify.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ const verify = (kni, trans, handler, kniscript) => {
7474

7575
const writer = new StringWriter();
7676
const render = new Console(writer);
77-
const readline = new FakeReadline(writer, answers);
77+
const readline = new FakeReadline(writer, answers, kniscript);
7878
const engine = new Engine({
7979
story: states,
8080
start: 'start',
@@ -98,9 +98,10 @@ const verify = (kni, trans, handler, kniscript) => {
9898
export default verify;
9999

100100
class FakeReadline {
101-
constructor(writer, answers) {
101+
constructor(writer, answers, kniscript) {
102102
this.writer = writer;
103103
this.answers = answers;
104+
this.kniscript = kniscript;
104105
this.engine = null;
105106
this.history = [];
106107
Object.seal(this);
@@ -129,6 +130,10 @@ class FakeReadline {
129130
}
130131

131132
close() {}
133+
134+
meterFault() {
135+
throw new Error(`meter fault in ${this.kniscript}`);
136+
}
132137
}
133138

134139
class StringWriter {

0 commit comments

Comments
 (0)