Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 72da425df3 |
@@ -10,7 +10,12 @@ enum ExprType {
|
||||
|
||||
EOutput(o: Output);
|
||||
|
||||
EDivert(target: String);
|
||||
ETunnelDivert;
|
||||
/**
|
||||
A divert statement that may contain multiple targets strung together (tunneling). An empty
|
||||
string means to return to the original position
|
||||
**/
|
||||
EDivert(targets: Array<String>);
|
||||
EKnot(name: String);
|
||||
EStitch(name: String);
|
||||
ENoOp;
|
||||
|
||||
@@ -268,6 +268,12 @@ class HankBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
public function takeToken() {
|
||||
var value = takeUntil([" ", "\n", "\t"]);
|
||||
skipWhitespace();
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Take data from the file until encountering one of the given terminator sequences. **/
|
||||
public function takeUntil(terminators: Array<String>, eofTerminates: Bool = false, dropTerminator = true): Option<BufferOutput> {
|
||||
return switch (peekUntil(terminators, eofTerminates)) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import hank.HankBuffer;
|
||||
class Parser {
|
||||
static var symbols: Array<Map<String, HankBuffer -> HankBuffer.Position -> ExprType>> = [
|
||||
['INCLUDE ' => include],
|
||||
['->->' => function(_, _) { return ETunnelDivert; }],
|
||||
['->' => divert],
|
||||
['===' => knot],
|
||||
['==' => knot],
|
||||
@@ -141,10 +142,23 @@ class Parser {
|
||||
}
|
||||
|
||||
static function divert(buffer: HankBuffer, position: HankBuffer.Position) : ExprType {
|
||||
buffer.drop('->');
|
||||
buffer.skipWhitespace();
|
||||
var tokens = lineTokens(buffer, 1, position, true, true);
|
||||
return EDivert(tokens[0]);
|
||||
var line = HankBuffer.Dummy(buffer.takeLine());
|
||||
|
||||
var targets = [];
|
||||
|
||||
do {
|
||||
line.drop("->");
|
||||
line.skipWhitespace();
|
||||
|
||||
switch (line.takeToken()) {
|
||||
case Some(target):
|
||||
targets.push(target);
|
||||
case None:
|
||||
targets.push('');
|
||||
}
|
||||
} while (!line.isEmpty());
|
||||
|
||||
return EDivert(targets);
|
||||
}
|
||||
|
||||
static function output(buffer: HankBuffer, position: HankBuffer.Position) : ExprType {
|
||||
|
||||
@@ -29,6 +29,10 @@ class Story {
|
||||
var hInterface: HInterface;
|
||||
var random: Random;
|
||||
|
||||
var tunnelBreadcrumbs: Array<Int> = [];
|
||||
var tunnelTargets: Array<StoryNode> = [];
|
||||
var tunnelReturns: Bool = false;
|
||||
|
||||
var ast: HankAST;
|
||||
var exprIndex: Int;
|
||||
|
||||
@@ -126,9 +130,23 @@ class Story {
|
||||
if (exprIndex >= ast.length) {
|
||||
return Finished;
|
||||
}
|
||||
|
||||
// Diverts need to be handled here to support chaining tunnels
|
||||
return processExpr(ast[exprIndex].expr);
|
||||
}
|
||||
|
||||
private function makeNextDivert() {
|
||||
if (tunnelTargets.length == 0) {
|
||||
throw 'supposed to divert to the next in tunnel but there are none!';
|
||||
}
|
||||
var next = tunnelTargets.shift();
|
||||
if (next.length > 0) {
|
||||
divertTo(next);
|
||||
} else {
|
||||
exprIndex = tunnelBreadcrumbs.pop();
|
||||
}
|
||||
}
|
||||
|
||||
private function processExpr(expr: ExprType): StoryFrame {
|
||||
switch (expr) {
|
||||
case EOutput(output):
|
||||
@@ -144,11 +162,16 @@ class Story {
|
||||
exprIndex += 1;
|
||||
hInterface.runEmbeddedHaxe(h, nodeScopes);
|
||||
return nextFrame();
|
||||
case EDivert(target):
|
||||
case ETunnelDivert:
|
||||
makeNextDivert();
|
||||
case EDivert(targets):
|
||||
if (target.length == 0) {
|
||||
exprIndex += 1;
|
||||
} else {
|
||||
divertTo(target);
|
||||
tunnelBreadcrumbs.push(exprIndex);
|
||||
tunnelTargets = tunnelTargets.concat([for(target in targets) if (target.length > 0) resolveNodeInScope(targets)]);
|
||||
tunnelReturn = (targets[targets.length-1].length == 0);
|
||||
makeNextDivert();
|
||||
}
|
||||
return nextFrame();
|
||||
|
||||
@@ -265,14 +288,13 @@ class Story {
|
||||
case None:
|
||||
}
|
||||
}
|
||||
if (newScopes[0].astIndex == -1) {
|
||||
throw 'Divert target not found: $label';
|
||||
}
|
||||
return newScopes;
|
||||
}
|
||||
|
||||
public function divertTo(target: String) {
|
||||
// Don't try to divert to a fallback target
|
||||
if (target.length == 0) {
|
||||
return;
|
||||
}
|
||||
public function divertTo(target: StoryNode) {
|
||||
switch (parent) {
|
||||
case Some(p):
|
||||
// A divert from inside embedded hank, must leave the embedded context
|
||||
@@ -285,15 +307,10 @@ class Story {
|
||||
|
||||
// trace('diverting to $target');
|
||||
|
||||
var newScopes = resolveNodeInScope(target);
|
||||
var targetIdx = newScopes[0].astIndex;
|
||||
var targetIdx = target.astIndex;
|
||||
|
||||
if (targetIdx == -1) {
|
||||
throw 'Divert target not found: $target';
|
||||
}
|
||||
// update the expression index
|
||||
exprIndex = targetIdx;
|
||||
var target = newScopes[0];
|
||||
weaveDepth = 0;
|
||||
|
||||
// Update the view count
|
||||
@@ -306,7 +323,7 @@ class Story {
|
||||
// If a knot directly starts with a stitch, run it
|
||||
switch (ast[exprIndex].expr) {
|
||||
case EStitch(label):
|
||||
var firstStitch = resolveNodeInScope(label, newScopes)[0];
|
||||
var firstStitch = resolveNodeInScope(label, target)[0];
|
||||
viewCounts[firstStitch] += 1;
|
||||
exprIndex += 1;
|
||||
default:
|
||||
@@ -317,7 +334,7 @@ class Story {
|
||||
// if it's a stitch, increase its view count
|
||||
viewCounts[target] += 1;
|
||||
|
||||
var enclosingKnot = newScopes[1];
|
||||
var enclosingKnot = target[1];
|
||||
// If we weren't in the stitch's containing section before, increase its viewcount
|
||||
if (nodeScopes.indexOf(enclosingKnot) == -1) {
|
||||
viewCounts[enclosingKnot] += 1;
|
||||
@@ -333,7 +350,7 @@ class Story {
|
||||
default:
|
||||
}
|
||||
// Update nodeScopes to point to the new scope
|
||||
nodeScopes = newScopes;
|
||||
nodeScopes = target;
|
||||
}
|
||||
|
||||
public function choose(choiceIndex: Int): String {
|
||||
|
||||
Reference in New Issue
Block a user