Main example working

This commit is contained in:
2019-05-01 01:06:01 -06:00
parent 7f76bce4ea
commit 9889f6cf14
9 changed files with 62 additions and 26 deletions

View File

@@ -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

View File

@@ -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):

View File

@@ -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;
}
}

View File

@@ -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]);
}

View File

@@ -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()]);
}
}

View File

@@ -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("")}));