Parse choices

This commit is contained in:
2019-03-17 15:22:28 -06:00
parent 1c0cca61da
commit 5e6018fadb
5 changed files with 79 additions and 22 deletions

View File

@@ -28,4 +28,7 @@ var test2 = 5;
```
- no label gather on an output
--(labeled) deep gather
--(labeled) deep gather
* Simplest possible choice
++ {condition} Choice that ends with a divert -> target

View File

@@ -268,6 +268,30 @@ class HankBuffer {
return data;
}
/** Count consecutive occurrence of the given string at the current buffer position, dropping the counted sequence **/
public function countConsecutive(s: String) {
var num = 0;
while (cleanBuffer.substr(0, s.length) == s) {
num += 1;
drop(s);
}
return num;
}
/** If the given expression comes next in the buffer, take its contents. Otherwise, return None **/
public function expressionIfNext(o: String, c: String): Option<String> {
if (StringTools.startsWith(cleanBuffer, o)) {
drop(o);
var end = cleanBuffer.indexOf(c);
var content = take(end);
drop(c);
return Some(content);
}
return None;
}
/** DRY Helper for peekLine() and takeLine() **/
function getLine(trimmed: String, retriever: Array<String> -> Bool -> Bool -> Option<BufferOutput>, dropNewline: Bool): Option<String> {
var nextLine = retriever(['\n'], true, false);

View File

@@ -1,5 +1,6 @@
package hank;
import haxe.ds.Option;
using Extensions.OptionExtender;
enum OutputType {
@@ -73,6 +74,11 @@ class Output {
}
}
parts = updateLastPart(parts);
return new Output(parts);
}
private static function updateLastPart(parts: Array<OutputType>) {
// If the last output is Text, it could contain optional text or an inline divert. Or just need rtrimming.
if (parts.length > 0) {
var lastPart = parts[parts.length - 1];
@@ -83,12 +89,10 @@ class Output {
default:
}
}
// TODO parse out optional text parts
return new Output(parts);
return parts;
}
/** The last part of an output expression outside of braces can include an inline divert -> like_so **/
public static function parseLastText(text: String): Array<OutputType> {
var parts = [];
@@ -124,4 +128,19 @@ class Output {
buffer.take(rawExpression.length);
return HExpression(hExpression);
}
/** If an instance of Output ends in an inline divert, remove that divert from this Output and return its target **/
public function takeInlineDivert(): Option<String> {
if (parts.length == 0) return None;
var lastPart = parts[parts.length-1];
switch (lastPart) {
case InlineDivert(target):
parts.remove(lastPart);
parts = updateLastPart(parts);
return Some(target);
default:
return None;
}
}
}

View File

@@ -16,6 +16,8 @@ enum ExprType {
EHaxeBlock(haxe: String);
EGather(label: Option<String>, depth: Int, expr: ExprType);
// Choices are the most complicated expressions
EChoice(id: Int, onceOnly: Bool, label: Option<String>, condition: Option<String>, depth: Int, output: Output, divertTarget: Option<String>);
}
typedef HankExpr = {
@@ -38,7 +40,9 @@ class Parser {
['=' => stitch],
['~' => haxeLine],
['```' => haxeBlock],
['-' => gather]
['-' => gather],
['*' => choice],
['+' => choice]
];
var buffers: Array<HankBuffer> = [];
@@ -162,26 +166,30 @@ class Parser {
}
static function gather(buffer: HankBuffer, position: HankBuffer.Position) : ExprType {
var depth = 0;
var c = '';
do {
c = buffer.peek(1);
if (c == '-') {
buffer.take(1);
depth += 1;
}
} while (c == '-');
var depth = buffer.countConsecutive('-');
buffer.skipWhitespace();
var label = buffer.expressionIfNext('(', ')');
buffer.skipWhitespace();
var label = if (buffer.peek(1) == '(') {
var text = buffer.takeUntil([')'], true).unwrap().output.substr(1);
buffer.skipWhitespace();
Some(text);
} else {
None;
};
return EGather(label, depth, parseExpr(buffer, buffer.position()));
}
static var choices: Int = 0;
static function choice(buffer: HankBuffer, position: HankBuffer.Position): ExprType {
var symbol = buffer.peek(1);
var onceOnly = symbol == '*';
var depth = buffer.countConsecutive(symbol);
buffer.skipWhitespace();
var label = buffer.expressionIfNext('(', ')');
buffer.skipWhitespace();
var condition = buffer.expressionIfNext('{','}');
buffer.skipWhitespace();
var output = Output.parse(buffer);
var divertTarget = output.takeInlineDivert();
return EChoice(choices++, onceOnly, label, condition, depth, output, divertTarget);
}
static function haxeBlock(buffer: HankBuffer, position: HankBuffer.Position): ExprType {
buffer.drop('```');
var rawContents = buffer.takeUntil(['```'], false, true).unwrap().output;

View File

@@ -103,5 +103,8 @@ class ParserTest extends utest.Test {
assertNextExpr(EHaxeBlock('story.runEmbeddedHank("Output \\"this\\"");'));
assertNextExpr(EGather(None, 1, EOutput(new Output([Text("no label gather on an output")]))));
assertNextExpr(EGather(Some('labeled'), 2, EOutput(new Output([Text("deep gather")]))));
assertNextExpr(EChoice(0, true, None, None, 1, new Output([Text("Simplest possible choice")]),None));
assertNextExpr(EChoice(1, false, None, Some("condition"), 2, new Output([Text("Choice that ends with a divert")]),Some("target")));
}
}