Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 72da425df3 |
@@ -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;
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user