Main example working
This commit is contained in:
@@ -8,10 +8,11 @@ Line breaks define the chunks of this section that will eventually get sent to y
|
||||
Your Hank scripts will contain the static content of your game, but they can also insert {demo_var}, even the result of complex {part1 + " " + part2}!
|
||||
|
||||
~ var multiline_logic = "Logic can happen on any line before a multiline comment.";
|
||||
// TODO is the next line even true anymore?
|
||||
/* Multiline block have to start at the
|
||||
beginning of their own line. And can't contain
|
||||
any other logic on the same line as the start or end. Sorry! */
|
||||
~ multiline_logic_example = "Logic can happen on any line after a multiline comment.";
|
||||
~ multiline_logic = "Logic can happen on any line after a multiline comment.";
|
||||
|
||||
-> choice_example
|
||||
|
||||
@@ -170,7 +170,10 @@ class HInterface {
|
||||
case EBinop(op, e1, e2):
|
||||
if (BOOLEAN_OPS.indexOf(op) != -1) {
|
||||
EBinop(op, boolify(e1), boolify(e2));
|
||||
} else {
|
||||
} else if (op == '=') {
|
||||
EBinop(op, e1, e2);
|
||||
}
|
||||
else {
|
||||
EBinop(op, valify(e1), valify(e2));
|
||||
}
|
||||
case EUnop(op, prefix, e):
|
||||
|
||||
@@ -93,7 +93,7 @@ class Output {
|
||||
switch(lastPart) {
|
||||
case Text(t):
|
||||
parts.remove(lastPart);
|
||||
parts = parts.concat(parseLastText(t, isPartOfAlt));
|
||||
parts = parts.concat(parseLastText(t));
|
||||
default:
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,7 @@ class Output {
|
||||
|
||||
|
||||
/** The last part of an output expression outside of braces can include an inline divert -> like_so **/
|
||||
public static function parseLastText(text: String, isPartOfAlt: Bool): Array<OutputType> {
|
||||
public static function parseLastText(text: String): Array<OutputType> {
|
||||
var parts = [];
|
||||
|
||||
var divertIndex = text.lastIndexOf('->');
|
||||
@@ -113,9 +113,6 @@ class Output {
|
||||
var target = text.substr(divertIndex+2).trim();
|
||||
parts.push(InlineDivert(target));
|
||||
} else {
|
||||
if (!isPartOfAlt) {
|
||||
text = text.rtrim();
|
||||
}
|
||||
parts.push(Text(text));
|
||||
}
|
||||
|
||||
@@ -175,7 +172,6 @@ class Output {
|
||||
break;
|
||||
}
|
||||
case HExpression(h):
|
||||
trace(h);
|
||||
var value = hInterface.evaluateExpr(h, scope);
|
||||
// HExpressions that start with ',' will be parsed and formatted as their own outputs
|
||||
if (value.startsWith(',')) {
|
||||
@@ -206,11 +202,6 @@ class Output {
|
||||
}
|
||||
}
|
||||
|
||||
// Remove double spaces from the output
|
||||
while (fullOutput.indexOf(" ") != -1) {
|
||||
fullOutput = fullOutput.replace(" ", " ");
|
||||
}
|
||||
|
||||
return fullOutput;
|
||||
}
|
||||
}
|
||||
@@ -120,11 +120,12 @@ class Parser {
|
||||
}
|
||||
|
||||
/** Split the given line into n tokens, throwing an error if there are any number of tokens other than n **/
|
||||
static function lineTokens(buffer: HankBuffer, n: Int, position: HankBuffer.Position): Array<String> {
|
||||
static function lineTokens(buffer: HankBuffer, n: Int, position: HankBuffer.Position, rtrim: Bool = true): Array<String> {
|
||||
var line = buffer.takeLine().unwrap();
|
||||
if (rtrim) line = line.rtrim();
|
||||
var tokens = line.split(' ');
|
||||
if (tokens.length != n) {
|
||||
throw 'Include file error at ${position}: ${tokens.length} tokens provided--should be ${n}.\nLine: `${line}`';
|
||||
throw 'Wrong number of tokens at ${position}: ${tokens.length} tokens provided--should be ${n}.\nLine: `${line}`\nTokens: ${tokens}';
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
@@ -137,7 +138,7 @@ class Parser {
|
||||
static function divert(buffer: HankBuffer, position: HankBuffer.Position) : ExprType {
|
||||
buffer.drop('->');
|
||||
buffer.skipWhitespace();
|
||||
var tokens = lineTokens(buffer, 1, position);
|
||||
var tokens = lineTokens(buffer, 1, position, true);
|
||||
return EDivert(tokens[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -84,10 +84,28 @@ class Story {
|
||||
var story = new Story(random, parser, ast, storyTree, nodeScopes, viewCounts, hInterface);
|
||||
hInterface.addVariable('story', story);
|
||||
|
||||
story.runRootIncludedHaxe(script);
|
||||
story.exprIndex = ast.findFile(script);
|
||||
return story;
|
||||
}
|
||||
|
||||
|
||||
/* Go through each included file executing all Haxe embedded at root level */
|
||||
private function runRootIncludedHaxe(rootFile: String) {
|
||||
var i = 0;
|
||||
while (i < ast.findFile(rootFile)) {
|
||||
var file = ast[i].position.file;
|
||||
switch (ast[i].expr) {
|
||||
case EHaxeLine(h) | EHaxeBlock(h):
|
||||
hInterface.runEmbeddedHaxe(h, nodeScopes);
|
||||
i += 1;
|
||||
default:
|
||||
i = ast.findEOF(file) + 1;
|
||||
}
|
||||
}
|
||||
// TODO when parsing an included file, make sure the first line that isn't embedded haxe (block or line form) is a Knot
|
||||
}
|
||||
|
||||
public function nextFrame(): StoryFrame {
|
||||
switch (storedFrame) {
|
||||
case Some(f):
|
||||
@@ -114,11 +132,7 @@ class Story {
|
||||
case EOutput(output):
|
||||
exprIndex += 1;
|
||||
var text = output.format(this, hInterface, random, altInstances, nodeScopes, false).trim();
|
||||
if (text.length == 0) {
|
||||
return nextFrame();
|
||||
} else {
|
||||
return HasText(text);
|
||||
}
|
||||
return finalTextProcessing(text);
|
||||
case EHaxeLine(h):
|
||||
exprIndex += 1;
|
||||
|
||||
@@ -157,7 +171,7 @@ class Story {
|
||||
|
||||
var optionsText = [for(c in availableChoices()) c.output.format(this, hInterface, random, altInstances, nodeScopes, false)];
|
||||
if (optionsText.length > 0) {
|
||||
return HasChoices(optionsText);
|
||||
return finalChoiceProcessing(optionsText);
|
||||
} else {
|
||||
var fallback = fallbackChoice();
|
||||
switch (fallback.choice.divertTarget) {
|
||||
@@ -310,7 +324,7 @@ class Story {
|
||||
weaveDepth = 0;
|
||||
|
||||
case EChoice(c):
|
||||
storedFrame = Some(HasText(evaluateChoice(c)));
|
||||
storedFrame = Some(finalTextProcessing(evaluateChoice(c)));
|
||||
return;
|
||||
|
||||
// Choices and gathers update their own view counts
|
||||
@@ -352,11 +366,34 @@ class Story {
|
||||
}
|
||||
|
||||
var output = choice.output.format(this, hInterface, random, altInstances, nodeScopes, true);
|
||||
return output;
|
||||
return finalChoiceOutputProcessing(output);
|
||||
}
|
||||
|
||||
/** Parse and run embedded Hank script on the fly. **/
|
||||
public function runEmbeddedHank(h: String) {
|
||||
embeddedBlocks.push(embeddedStory(h));
|
||||
}
|
||||
|
||||
private function removeDoubleSpaces(t: String) {
|
||||
var intermediate = t;
|
||||
while (intermediate.indexOf(' ') != -1) {
|
||||
intermediate = intermediate.replace(' ', ' ');
|
||||
}
|
||||
return intermediate;
|
||||
}
|
||||
|
||||
private function finalTextProcessing(t: String) {
|
||||
if (t.length > 0)
|
||||
return HasText(removeDoubleSpaces(t).trim());
|
||||
else
|
||||
return nextFrame();
|
||||
}
|
||||
|
||||
private function finalChoiceOutputProcessing(t: String) {
|
||||
return removeDoubleSpaces(t).trim();
|
||||
}
|
||||
|
||||
private function finalChoiceProcessing(choices: Array<String>) {
|
||||
return HasChoices([for(c in choices) removeDoubleSpaces(c).trim()]);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,9 @@ import hank.Output;
|
||||
import hank.Alt;
|
||||
import hank.HankAssert;
|
||||
|
||||
/**
|
||||
These tests are hard to maintain, and may not be relevant now that parsing largely works
|
||||
**/
|
||||
class ParserTest extends utest.Test {
|
||||
var ast: HankAST;
|
||||
|
||||
@@ -25,7 +28,7 @@ class ParserTest extends utest.Test {
|
||||
assertNextExpr(EOutput(new Output([Text("This file contains test cases for output expression parsing.")])));
|
||||
assertNextExpr(EOutput(new Output([Text("A line won't be interrupted or anything.")])));
|
||||
assertNextExpr(EOutput(new Output([Text("Multiline comments an output expression. This should parse as one line of output.")])));
|
||||
assertNextExpr(EOutput(new Output([Text("Comments at the end of lines won't parse as part of the Output.")])));
|
||||
assertNextExpr(EOutput(new Output([Text("Comments at the end of lines won't parse as part of the Output. ")])));
|
||||
assertNextExpr(EOutput(new Output([Text("You can "), HExpression("insert"), Text(" the values of expressions.")])));
|
||||
assertNextExpr(EOutput(new Output([HExpression("you"), Text(" can start an output line with an insert expression. "), HExpression("and_end_one")])));
|
||||
|
||||
@@ -117,7 +120,7 @@ class ParserTest extends utest.Test {
|
||||
assertNextExpr(EGather(Some('labeled'), 2, EOutput(new Output([Text("deep gather")]))));
|
||||
|
||||
assertNextExpr(EChoice({id: 0, onceOnly: true, label: None, condition: None, depth: 1, output: new Output([Text("Simplest possible choice")]),divertTarget: None}));
|
||||
assertNextExpr(EChoice({id: 1, onceOnly: false, label: None, condition: Some("condition"), depth: 2, output: new Output([Text("Choice that ends with a divert")]),divertTarget: Some("target")}));
|
||||
assertNextExpr(EChoice({id: 1, onceOnly: false, label: None, condition: Some("condition"), depth: 2, output: new Output([Text("Choice that ends with a divert ")]),divertTarget: Some("target")}));
|
||||
assertNextExpr(EChoice({id: 2, onceOnly: true, label: None, condition: None, depth: 1, output: new Output([]), divertTarget: Some("fallback_choice")}));
|
||||
assertNextExpr(EChoice({id: 3, onceOnly: true, label: None, condition: None, depth: 1, output: new Output([]), divertTarget: Some("")}));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user