1 Commits
dev ... tunnels

Author SHA1 Message Date
72da425df3 WIP tunnels -- a nightmare 2019-05-13 22:16:18 -06:00
4 changed files with 63 additions and 21 deletions

View File

@@ -10,7 +10,12 @@ enum ExprType {
EOutput(o: Output); 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); EKnot(name: String);
EStitch(name: String); EStitch(name: String);
ENoOp; ENoOp;

View File

@@ -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. **/ /** 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> { public function takeUntil(terminators: Array<String>, eofTerminates: Bool = false, dropTerminator = true): Option<BufferOutput> {
return switch (peekUntil(terminators, eofTerminates)) { return switch (peekUntil(terminators, eofTerminates)) {

View File

@@ -13,6 +13,7 @@ import hank.HankBuffer;
class Parser { class Parser {
static var symbols: Array<Map<String, HankBuffer -> HankBuffer.Position -> ExprType>> = [ static var symbols: Array<Map<String, HankBuffer -> HankBuffer.Position -> ExprType>> = [
['INCLUDE ' => include], ['INCLUDE ' => include],
['->->' => function(_, _) { return ETunnelDivert; }],
['->' => divert], ['->' => divert],
['===' => knot], ['===' => knot],
['==' => knot], ['==' => knot],
@@ -141,10 +142,23 @@ class Parser {
} }
static function divert(buffer: HankBuffer, position: HankBuffer.Position) : ExprType { static function divert(buffer: HankBuffer, position: HankBuffer.Position) : ExprType {
buffer.drop('->'); var line = HankBuffer.Dummy(buffer.takeLine());
buffer.skipWhitespace();
var tokens = lineTokens(buffer, 1, position, true, true); var targets = [];
return EDivert(tokens[0]);
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 { static function output(buffer: HankBuffer, position: HankBuffer.Position) : ExprType {

View File

@@ -29,6 +29,10 @@ class Story {
var hInterface: HInterface; var hInterface: HInterface;
var random: Random; var random: Random;
var tunnelBreadcrumbs: Array<Int> = [];
var tunnelTargets: Array<StoryNode> = [];
var tunnelReturns: Bool = false;
var ast: HankAST; var ast: HankAST;
var exprIndex: Int; var exprIndex: Int;
@@ -126,9 +130,23 @@ class Story {
if (exprIndex >= ast.length) { if (exprIndex >= ast.length) {
return Finished; return Finished;
} }
// Diverts need to be handled here to support chaining tunnels
return processExpr(ast[exprIndex].expr); 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 { private function processExpr(expr: ExprType): StoryFrame {
switch (expr) { switch (expr) {
case EOutput(output): case EOutput(output):
@@ -144,11 +162,16 @@ class Story {
exprIndex += 1; exprIndex += 1;
hInterface.runEmbeddedHaxe(h, nodeScopes); hInterface.runEmbeddedHaxe(h, nodeScopes);
return nextFrame(); return nextFrame();
case EDivert(target): case ETunnelDivert:
makeNextDivert();
case EDivert(targets):
if (target.length == 0) { if (target.length == 0) {
exprIndex += 1; exprIndex += 1;
} else { } 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(); return nextFrame();
@@ -265,14 +288,13 @@ class Story {
case None: case None:
} }
} }
if (newScopes[0].astIndex == -1) {
throw 'Divert target not found: $label';
}
return newScopes; return newScopes;
} }
public function divertTo(target: String) { public function divertTo(target: StoryNode) {
// Don't try to divert to a fallback target
if (target.length == 0) {
return;
}
switch (parent) { switch (parent) {
case Some(p): case Some(p):
// A divert from inside embedded hank, must leave the embedded context // A divert from inside embedded hank, must leave the embedded context
@@ -285,15 +307,10 @@ class Story {
// trace('diverting to $target'); // trace('diverting to $target');
var newScopes = resolveNodeInScope(target); var targetIdx = target.astIndex;
var targetIdx = newScopes[0].astIndex;
if (targetIdx == -1) {
throw 'Divert target not found: $target';
}
// update the expression index // update the expression index
exprIndex = targetIdx; exprIndex = targetIdx;
var target = newScopes[0];
weaveDepth = 0; weaveDepth = 0;
// Update the view count // Update the view count
@@ -306,7 +323,7 @@ class Story {
// If a knot directly starts with a stitch, run it // If a knot directly starts with a stitch, run it
switch (ast[exprIndex].expr) { switch (ast[exprIndex].expr) {
case EStitch(label): case EStitch(label):
var firstStitch = resolveNodeInScope(label, newScopes)[0]; var firstStitch = resolveNodeInScope(label, target)[0];
viewCounts[firstStitch] += 1; viewCounts[firstStitch] += 1;
exprIndex += 1; exprIndex += 1;
default: default:
@@ -317,7 +334,7 @@ class Story {
// if it's a stitch, increase its view count // if it's a stitch, increase its view count
viewCounts[target] += 1; 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 we weren't in the stitch's containing section before, increase its viewcount
if (nodeScopes.indexOf(enclosingKnot) == -1) { if (nodeScopes.indexOf(enclosingKnot) == -1) {
viewCounts[enclosingKnot] += 1; viewCounts[enclosingKnot] += 1;
@@ -333,7 +350,7 @@ class Story {
default: default:
} }
// Update nodeScopes to point to the new scope // Update nodeScopes to point to the new scope
nodeScopes = newScopes; nodeScopes = target;
} }
public function choose(choiceIndex: Int): String { public function choose(choiceIndex: Int): String {