Parse choices
This commit is contained in:
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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")));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user