diff --git a/.gitignore b/.gitignore index c62ae4a..73285d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -*.hanktest -examples/shave/ \ No newline at end of file +examples/shave/ +transcript.hlog +validating.hlog \ No newline at end of file diff --git a/examples/TheIntercept.hank b/examples/TheIntercept/main.hank similarity index 100% rename from examples/TheIntercept.hank rename to examples/TheIntercept/main.hank diff --git a/examples/tests/interceptDebug1.hanktest b/examples/TheIntercept/partialdebugtest1.hlog similarity index 90% rename from examples/tests/interceptDebug1.hanktest rename to examples/TheIntercept/partialdebugtest1.hlog index f2c2a83..a686a2a 100644 --- a/examples/tests/interceptDebug1.hanktest +++ b/examples/TheIntercept/partialdebugtest1.hlog @@ -3,5 +3,6 @@ IN DEBUG MODE! * Framing Hooper... * In with Hooper... >>> 2 + Harris opens the door and pushes me inside. "Captain," he calls. "Could I have a moment?" The Captain, looking puzzled, steps out. The door is closed. Hooper stares at me, open—mouthed, about to say something. I probably have less than a minute before the Captain storms back in and declares this plan to be bunkum. \ No newline at end of file diff --git a/examples/tests/intercept1.hanktest b/examples/TheIntercept/partialtest1.hlog similarity index 87% rename from examples/tests/intercept1.hanktest rename to examples/TheIntercept/partialtest1.hlog index 885ac1f..b59151d 100644 --- a/examples/tests/intercept1.hanktest +++ b/examples/TheIntercept/partialtest1.hlog @@ -10,6 +10,7 @@ I am not a machine, whatever they say about me. >>> 0 They suspect me to be a traitor. They think I stole the component from the calculating machine. They will be searching my bunk and cases. +When they don't find it, they'll come back and demand I talk. I rattle my fingers on the field table. * Plan * Wait diff --git a/examples/tests/conditionalDebug1.hanktest b/examples/conditional/debugtest1.hlog similarity index 100% rename from examples/tests/conditionalDebug1.hanktest rename to examples/conditional/debugtest1.hlog diff --git a/examples/conditional.hank b/examples/conditional/main.hank similarity index 100% rename from examples/conditional.hank rename to examples/conditional/main.hank diff --git a/examples/tests/conditional1.hanktest b/examples/conditional/test1.hlog similarity index 100% rename from examples/tests/conditional1.hanktest rename to examples/conditional/test1.hlog diff --git a/examples/hello.hank b/examples/hello/main.hank similarity index 100% rename from examples/hello.hank rename to examples/hello/main.hank diff --git a/examples/labels.hank b/examples/labels/main.hank similarity index 100% rename from examples/labels.hank rename to examples/labels/main.hank diff --git a/examples/tests/labels1.hanktest b/examples/labels/test1.hlog similarity index 100% rename from examples/tests/labels1.hanktest rename to examples/labels/test1.hlog diff --git a/examples/tests/labels2.hanktest b/examples/labels/test2.hlog similarity index 100% rename from examples/tests/labels2.hanktest rename to examples/labels/test2.hlog diff --git a/examples/extra.hank b/examples/main/extra.hank similarity index 100% rename from examples/extra.hank rename to examples/main/extra.hank diff --git a/examples/main.hank b/examples/main/main.hank similarity index 100% rename from examples/main.hank rename to examples/main/main.hank diff --git a/examples/tests/main1.hanktest b/examples/main/test1.hlog similarity index 100% rename from examples/tests/main1.hanktest rename to examples/main/test1.hlog diff --git a/examples/tests/main2.hanktest b/examples/main/test2.hlog similarity index 100% rename from examples/tests/main2.hanktest rename to examples/main/test2.hlog diff --git a/examples/mindfuck.hank b/examples/mindfuck/main.hank similarity index 100% rename from examples/mindfuck.hank rename to examples/mindfuck/main.hank diff --git a/examples/tests/mindfuck.hanktest b/examples/mindfuck/test1.hlog similarity index 100% rename from examples/tests/mindfuck.hanktest rename to examples/mindfuck/test1.hlog diff --git a/src/Story.hx b/src/Story.hx index 3d3e859..cd553ca 100644 --- a/src/Story.hx +++ b/src/Story.hx @@ -433,16 +433,16 @@ class Story { if (linesArray.length > 1) { // debugTrace('Parsing haxe block "${preprocessedLines}"'); } - var program = parser.parseString(preprocessedLines); - interp.execute(program); + + executeHaxeBlock(preprocessedLines); var currentLineId = { sourceFile: scriptLines[currentLine].sourceFile, lineNumber: scriptLines[currentLine].lineNumber }; - trace(startingId); - trace(currentLineId); + // trace(startingId); + // trace(currentLineId); if (startingId.lineNumber == currentLineId.lineNumber && startingId.sourceFile == currentLineId.sourceFile) {stepLine();} } @@ -465,13 +465,13 @@ class Story { var line = scriptLines[idx++]; if (line.sourceFile == id.sourceFile && line.lineNumber == id.lineNumber) { gotoLine(idx); - debugTrace('Found the right line'); + // debugTrace('Found the right line'); } } } private function stepLine() { - debugTrace('stepLine called'); + // debugTrace('stepLine called'); if (!finished) { // debugTrace('Stepping to line ${Std.string(scriptLines[currentLine+1])}'); gotoLine(currentLine+1); @@ -541,7 +541,7 @@ class Story { private function queueEmbeddedBlock(dummyFile: String) { if (embeddedBlocksQueued.length == 0) { - trace('${dummyFile} got here first'); + // trace('${dummyFile} got here first'); gotoFile(dummyFile); } embeddedBlocksQueued.push(dummyFile); @@ -551,14 +551,14 @@ class Story { private function processEmbeddedLines(parent: LineID, childNumber: Int, lines: String) { embeddedEntryPoint = parent; var dummyFile = 'EMBEDDED BLOCK ${childNumber}/${Std.string(parent)}'; - debugTrace('processing ${dummyFile}'); + // debugTrace('processing ${dummyFile}'); queueEmbeddedBlock(dummyFile); } /** Execute a parsed script statement **/ private function processLine(line: HankLine): StoryFrame { if (line.type != NoOp) { - debugTrace('Processing ${Std.string(line)}'); + // debugTrace('Processing ${Std.string(line)}'); } var file = line.sourceFile; @@ -604,7 +604,7 @@ class Story { stepLine(); embeddedEntryPoint = null; } else { - trace('another block was queued'); + // trace('another block was queued'); var nextBlock = embeddedBlocksQueued[0]; gotoFile(nextBlock); } @@ -641,7 +641,7 @@ class Story { return Error("Trust me, you'd rather not use a triple-nested Haxe block"); } processHaxeBlock(code); - trace('done that'); + // trace('done that'); return Empty; // Execute choice declarations by collecting the set of choices and presenting valid ones to the player @@ -846,12 +846,33 @@ class Story { private function checkChoiceCondition(choice: Choice): Bool { return if (Util.startsWithEnclosure(choice.text, "{", "}")) { var conditionExpression = Util.findEnclosure(choice.text, "{", "}"); - var parsed = parser.parseString(conditionExpression); - var conditionValue = interp.expr(parsed); + var conditionValue = evaluateHaxeExpression(conditionExpression); conditionValue; } else true; } + private function evaluateHaxeExpression(expression: String) { + // It's common to want to && or || section/label names, but they're integers, not bools. Allow this. + var expandedExpression = expression; + + var parsed = parser.parseString(expandedExpression); + try { + var value = interp.expr(parsed); + return value; + } catch (e: Dynamic) { + throw 'Error evaluating expression ${expression}: ${Std.string(e)}'; + } + } + + private function executeHaxeBlock(block: String) { + try { + var program = parser.parseString(block); + interp.execute(program); + } catch (e: Dynamic) { + throw 'Error evaluating haxe block: ${Std.string(e)}\n${block}'; + } + } + /** Process a choice into the desired form to show the player @param chosen Whether to show the choice's "before" or "after" text to the player @@ -901,9 +922,8 @@ class Story { Skip script execution to the desired target **/ public function divert(target: String): StoryFrame { - debugTrace('diverting to ${target}'); + // debugTrace('diverting to ${target}'); // TODO check the current section for a subsection by the name of target, or else parse . notation for subsections - // TODO if going to a labeled gather, set the choice depth to that gather's depth - 1 // debugTrace('going to section ${section}'); // Update this section's view count if (!interp.variables.exists(target)) { diff --git a/src/StoryTestCase.hx b/src/StoryTestCase.hx index 8eda1a4..a8b21ec 100644 --- a/src/StoryTestCase.hx +++ b/src/StoryTestCase.hx @@ -14,22 +14,19 @@ class StoryTestCase extends utest.Test { private function validateAgainstTranscript(storyFile: String, transcriptFile: String, fullTranscript: Bool = true, debug: Bool = false, debugPrints: Bool = false) { - trace('validating ${storyFile}'); + if (debugPrints) trace('validating ${storyFile}'); - var story: Story = new Story(debug,'validating.hanktest', debugPrints); + var story: Story = new Story(debug,'validating.hlog', debugPrints); story.loadScript(storyFile); var transcriptLines = sys.io.File.getContent(transcriptFile).split('\n'); var i = 0; while (i < transcriptLines.length) { var line = transcriptLines[i]; - trace(line); var frame = story.nextFrame(); if (debugPrints) { - if (debugPrints) { - trace('Frame ${i}: ${Std.string(frame)}'); - } + trace('Frame ${i}: ${Std.string(frame)}'); } if (StringTools.startsWith(line, "*")) { @@ -76,6 +73,6 @@ class StoryTestCase extends utest.Test { // After all transcript lines are validated, there should be nothing left in the story flow! Assert.equals(StoryFrame.Finished, story.nextFrame()); } - trace('done with ${storyFile}'); + if (debugPrints) trace('done with ${storyFile}'); } } \ No newline at end of file diff --git a/tests/StoryTest.hx b/tests/StoryTest.hx index edf88b2..882d781 100644 --- a/tests/StoryTest.hx +++ b/tests/StoryTest.hx @@ -13,23 +13,35 @@ class StoryTest extends src.StoryTestCase { public function testParseHelloWorld() { var story: Story = new Story(); - story.loadScript("examples/hello.hank"); assertComplexEquals(OutputText('Hello, world!'), story.scriptLines[0].type); + story.loadScript("examples/hello/main.hank"); assertComplexEquals(OutputText('Hello, world!'), story.scriptLines[0].type); } public function testHelloWorld() { var story: Story = new Story(); - story.loadScript("examples/hello.hank"); + story.loadScript("examples/hello/main.hank"); assertComplexEquals(HasText("Hello, world!"), story.nextFrame()); Assert.equals(StoryFrame.Finished, story.nextFrame()); Assert.equals(StoryFrame.Finished, story.nextFrame()); } - public function testRunFullSpec2() { - validateAgainstTranscript("examples/main.hank", "examples/tests/main1.hanktest"); - } + public function testAllExamples() { + var exampleFolders = sys.FileSystem.readDirectory('examples'); - public function testRunFullSpec3() { - validateAgainstTranscript("examples/main.hank", "examples/tests/main2.hanktest"); + // Iterate through every example in the examples folder + for (folder in exampleFolders) { + trace('Running tests for example "${folder}"'); + var files = sys.FileSystem.readDirectory('examples/${folder}'); + + for (file in files) { + if (StringTools.endsWith(file, '.hlog')) { + trace(' Running ${file}'); + + var debug = file.indexOf("debug") != -1; + var partial = file.indexOf("partial") != -1; + validateAgainstTranscript('examples/${folder}/main.hank', 'examples/${folder}/${file}', !partial, debug, false); + } + } + } } /** @@ -37,8 +49,8 @@ class StoryTest extends src.StoryTestCase { **/ public function testRunFullSpec1() { trace('testing the main hank example manually'); - var story: Story = new Story(true, "transcript.hanktest", true); - story.loadScript("examples/main.hank"); + var story: Story = new Story(true, "transcript.hlog", true); + story.loadScript("examples/main/main.hank"); var frame1 = story.nextFrame(); // This calls the INCLUDE statement. Ensure that all lines // were included @@ -82,12 +94,12 @@ class StoryTest extends src.StoryTestCase { Assert.equals(StoryFrame.Finished, story.nextFrame()); // Validate the transcript that was produced - validateAgainstTranscript("examples/main.hank", "transcript.hanktest"); + validateAgainstTranscript("examples/main/main.hank", "transcript.hlog"); } public function testViewCounts() { var story = new Story(true); - story.loadScript("examples/main.hank"); + story.loadScript("examples/main/main.hank"); Assert.equals(0, story.interp.variables['start']); Assert.equals(0, story.interp.variables['choice_example']); @@ -106,7 +118,7 @@ class StoryTest extends src.StoryTestCase { public function testParseFullSpec() { // Parse the main.hank script and test that all lines are correctly parsed var story = new Story(true); - story.loadScript("examples/main.hank"); + story.loadScript("examples/main/main.hank"); Assert.equals(40+22, story.lineCount); // TODO test a few line numbers from the script to make sure the parsed versions match. Especially block line numbers @@ -116,7 +128,7 @@ class StoryTest extends src.StoryTestCase { var lineTypes = [ // TODO the 22 lines of the extra.hank file - IncludeFile('examples/extra.hank'), + IncludeFile('examples/main/extra.hank'), NoOp, Divert('start'), NoOp, @@ -156,7 +168,7 @@ class StoryTest extends src.StoryTestCase { OutputText("These should all say 'mouse':"), OutputText("{what_happened}"), OutputText("{variable_declared_in_block}"), - EOF('examples/main.hank') + EOF('examples/main/main.hank') ]; var idx = 23; @@ -165,47 +177,4 @@ class StoryTest extends src.StoryTestCase { assertComplexEquals(lineTypes[i++], story.scriptLines[idx++].type); } } - - @Ignored - public function testRunIntercept1() { - validateAgainstTranscript("examples/TheIntercept.hank", "examples/tests/intercept1.hanktest", false, - false, - true); - } - - @Ignored - public function testRunInterceptDebug1() { - validateAgainstTranscript( - "examples/TheIntercept.hank", - "examples/tests/interceptDebug1.hanktest", - false, // Don't validate all of the Intercept until the port is done - true, // Set DEBUG to true - true); // Use debugTrace statements - } - - public function testEmbeddedHankMindfuck() { - // Test the situation where embedded Hank lines are triggered, and later the story loops to the same Haxe block with different conditions - validateAgainstTranscript("examples/mindfuck.hank", "examples/tests/mindfuck.hanktest", true, true, true); - } - - public function testConditionalBlocks() { - trace('Testing conditional example in production mode'); - validateAgainstTranscript("examples/conditional.hank", "examples/tests/conditional1.hanktest", true, false, true); - trace('testing conditional example in debug mode'); - validateAgainstTranscript("examples/conditional.hank", "examples/tests/conditionalDebug1.hanktest", true, true, true); - } - - public function testLabels() { - validateAgainstTranscript("examples/labels.hank", "examples/tests/labels1.hanktest"); - validateAgainstTranscript("examples/labels.hank", "examples/tests/labels2.hanktest"); - } - - /** Test one of Nat's private WIP Hank stories **/ - public function testPrivateStories() { - if (sys.FileSystem.exists('examples/shave')) { - validateAgainstTranscript('examples/shave/shave-draft2.hank', 'examples/shave/tests/1.hanktest'); - } else { - Assert.isTrue(true); - } - } } \ No newline at end of file