FileBuffer-> HankBuffer for arbitrary strings

This commit is contained in:
2019-03-16 16:50:55 -06:00
parent d46df55389
commit 581ddc394d
6 changed files with 109 additions and 83 deletions

View File

@@ -7,33 +7,39 @@ enum AltBehavior {
Shuffle;
}
class AltState {
typedef Alt = {
var behavior: AltBehavior;
var outputs: Array<Output>;
}
class AltInstance {
var alt: Alt;
var index: Int = -1;
var alts: Array<String>;
var random: Random;
public function new(behavior: AltBehavior, alts: Array<String>, random: Random) {
this.behavior = behavior;
this.alts = alts;
public function new(behavior: AltBehavior, outputs: Array<Output>, random: Random) {
this.alt = {
behavior: behavior,
outputs: outputs
};
this.random = random;
}
public function next() {
switch (behavior) {
public function next(): Output {
switch (alt.behavior) {
case Sequence:
index = Math.floor(Math.min(alts.length-1, index+1));
index = Math.floor(Math.min(alt.outputs.length-1, index+1));
case OnceOnly:
if (index >= -1) {
index = index+1;
}
if (index >= alts.length) index = -2;
if (index >= alt.outputs.length) index = -2;
case Cycle:
index = (index + 1) % alts.length;
index = (index + 1) % alt.outputs.length;
case Shuffle:
// Pick a random index deterministically using the story's Random object.
index = random.int(0, alts.length - 1);
index = random.int(0, alt.outputs.length - 1);
}
return if (index < 0) '' else alts[index];
return if (index < 0) new Output() else alt.outputs[index];
}
}

View File

@@ -3,7 +3,7 @@ package hank;
import haxe.ds.Option;
/**
A position in a FileBuffer, used for debugging.
A position in a HankBuffer, used for debugging.
**/
typedef Position = {
var file: String;
@@ -17,23 +17,29 @@ typedef BufferOutput = {
};
/**
Helper class for reading/parsing information from a file. Completely drops comments
Helper class for reading/parsing information from a string buffer. Completely drops comments
**/
class FileBuffer {
class HankBuffer {
var path: String;
var cleanBuffer: String;
var rawBuffer: String;
var line: Int = 1;
var column: Int = 1;
public function new(path: String) {
// Keep a raw buffer of the file for tracking accurate file positions
rawBuffer = sys.io.File.getContent(path);
// Keep a clean buffer for returning data without comments getting in the way
cleanBuffer = stripComments(rawBuffer, '//', '\n', false);
cleanBuffer = stripComments(cleanBuffer, '/*', '*/', true);
var line: Int;
var column: Int;
private function new(path: String, rawBuffer: String, line: Int = 1, column: Int = 1) {
this.path = path;
this.rawBuffer = rawBuffer;
// Keep a clean buffer for returning data without comments getting in the way
this.cleanBuffer = stripComments(rawBuffer, '//', '\n', false);
this.cleanBuffer = stripComments(cleanBuffer, '/*', '*/', true);
this.line = line;
this.column = column;
}
public static function FromFile(path: String) {
// Keep a raw buffer of the file for tracking accurate file positions
var rawBuffer = sys.io.File.getContent(path);
return new HankBuffer(path, rawBuffer);
}
function stripComments(s: String, o: String, c: String, dc: Bool): String {

View File

@@ -2,7 +2,7 @@ package hank;
enum OutputType {
Text(t: String); // Pure text that is always displayed
// Alt(a: Alt); // TODO re-implement Alts recursively. Each alt branch should be an Output
AltExpression(a: Alt);
HExpression(h: String); // An embedded Haxe expression whose value will be inserted
InlineDivert(t: String); // A divert statement on the same line as an output sequence.
ToggleOutput(o: Output, invert: Bool); // Output that will sometimes be displayed (i.e. [bracketed] section in a choice text or the section following the bracketed section)
@@ -12,11 +12,15 @@ enum OutputType {
class Output {
var parts: Array<OutputType> = [];
private function new(parts: Array<OutputType>) {
this.parts = parts;
public function new(?parts: Array<OutputType>) {
this.parts = if (parts != null) {
parts;
} else {
[];
};
}
public static function parse(expression, rob: FileBuffer): Output {
public static function parse(buffer: HankBuffer): Output {
// Parse out optional text parts
// Find the first level of nested brace, expressions, and parse them out

View File

@@ -1,8 +1,12 @@
package hank;
using Extensions.OptionExtender;
enum ExprType {
EIncludeFile(path: String);
EOutput(output: Output);
EOutput(o: Output);
EDivert(target: String);
EKnot(name: String);
EStitch(name: String);
@@ -11,7 +15,7 @@ enum ExprType {
}
typedef HankExpr = {
var position: FileBuffer.Position;
var position: HankBuffer.Position;
var expr: ExprType;
}
@@ -28,7 +32,7 @@ class Parser {
'~' => haxeLine
];
var buffers: Array<FileBuffer> = [];
var buffers: Array<HankBuffer> = [];
var ast: HankAST = [];
public function new() {
@@ -43,29 +47,28 @@ class Parser {
f = f.substr(lastSlashIdx+1);
}
buffers.insert(0, new FileBuffer(directory + f));
buffers.insert(0, HankBuffer.FromFile(directory + f));
while (true) {
while (buffers.length > 0) {
var position = buffers[0].position();
var nextLine = buffers[0].takeLine('lr');
switch (nextLine) {
case Some(line):
var expr = parseLine(line, buffers[0], position);
switch (expr) {
case EIncludeFile(file):
parseFile(directory + file, true);
case ENoOp:
// Drop no-ops from the AST
default:
ast.push({
position: position,
expr: expr
buffers[0].skipWhitespace();
if (buffers[0].isEmpty()) {
buffers.remove(buffers[0]);
} else {
var expr = parseExpr(buffers[0], position);
switch(expr) {
case EIncludeFile(file):
parseFile(directory + file, true);
case ENoOp:
// Drop no-ops from the AST
default:
ast.push({
position: position,
expr: expr
});
}
case None:
break;
}
}
}
}
var parsedAST = ast;
@@ -74,27 +77,33 @@ class Parser {
ast = [];
}
buffers.remove(buffers[0]);
return parsedAST;
}
function parseLine(line: String, restOfBuffer: FileBuffer, position: FileBuffer.Position) : ExprType {
if (StringTools.trim(line).length == 0) {
return ENoOp;
function parseExpr(buffer: HankBuffer, position: HankBuffer.Position) : ExprType {
var line = buffer.peekLine();
switch (line) {
case None:
throw 'Tried to parse expr when no lines were left in file';
case Some(line):
if (StringTools.trim(line).length == 0) {
return ENoOp;
}
for (symbol in symbols.keys()) {
if (StringTools.startsWith(line, symbol)) {
return symbols[symbol](buffer, position);
}
}
return output(buffers[0], position);
}
for (symbol in symbols.keys()) {
if (StringTools.startsWith(line, symbol)) {
return symbols[symbol](line, restOfBuffer, position);
}
}
return output(line, buffers[0], position);
}
/** Split the given line into n tokens, throwing an error if there are any number of tokens other than n **/
static function lineTokens(line:String, n: Int, position: FileBuffer.Position) {
static function lineTokens(buffer: HankBuffer, n: Int, position: HankBuffer.Position): Array<String> {
var line = buffer.takeLine().unwrap();
var tokens = line.split(' ');
if (tokens.length != n) {
throw 'Include file error at ${position}: ${tokens.length} tokens provided--should be ${n}.\nLine: `${line}`';
@@ -102,32 +111,33 @@ class Parser {
return tokens;
}
static function include(line: String, rob: FileBuffer, position: FileBuffer.Position) : ExprType {
var tokens = lineTokens(line, 2, position);
static function include(buffer: HankBuffer, position: HankBuffer.Position) : ExprType {
var tokens = lineTokens(buffer, 2, position);
return EIncludeFile(tokens[1]);
}
static function divert(line: String, rob: FileBuffer, position: FileBuffer.Position) : ExprType {
var tokens = lineTokens(line, 2, position);
static function divert(buffer: HankBuffer, position: HankBuffer.Position) : ExprType {
var tokens = lineTokens(buffer, 2, position);
return EDivert("tokens[1]");
}
static function output(line: String, rob: FileBuffer, position: FileBuffer.Position) : ExprType {
return EOutput(Output.parse(line, rob));
static function output(buffer: HankBuffer, position: HankBuffer.Position) : ExprType {
return EOutput(Output.parse(buffer));
}
static function knot(line: String, rob: FileBuffer, position: FileBuffer.Position) : ExprType {
var tokens = lineTokens(line, 2, position);
static function knot(buffer: HankBuffer, position: HankBuffer.Position) : ExprType {
var tokens = lineTokens(buffer, 2, position);
return EKnot(tokens[1]);
}
static function stitch(line: String, rob: FileBuffer, position: FileBuffer.Position) : ExprType {
var tokens = lineTokens(line, 2, position);
static function stitch(buffer: HankBuffer, position: HankBuffer.Position) : ExprType {
var tokens = lineTokens(buffer, 2, position);
return EStitch("tokens[1]");
}
static function haxeLine(line: String, rob: FileBuffer, position: FileBuffer.Position) : ExprType {
return EHaxeLine(StringTools.trim(line.substr(1)));
static function haxeLine(buffer: HankBuffer, position: HankBuffer.Position) : ExprType {
buffer.drop('~');
return EHaxeLine(buffer.takeLine('lr').unwrap());
}
}

View File

@@ -1,17 +1,17 @@
package tests;
import haxe.ds.Option;
import hank.FileBuffer;
import hank.FileBuffer.Position;
import hank.HankBuffer;
import hank.HankBuffer.Position;
class FileBufferTest extends utest.Test {
var file: FileBuffer;
class HankBufferTest extends utest.Test {
var file: HankBuffer;
var path: String;
var expectedPosition: Position;
function setup() {
path = 'examples/parsing/file.txt';
file = new FileBuffer(path);
file = HankBuffer.FromFile(path);
expectedPosition = {
file: path,
line: 1,
@@ -102,7 +102,7 @@ class FileBufferTest extends utest.Test {
}
function testGetLineTrimming() {
file = new FileBuffer('examples/parsing/whitespace.txt');
file = HankBuffer.FromFile('examples/parsing/whitespace.txt');
HankAssert.equals(Some("Just give me this output."), file.peekLine("lr"));
HankAssert.equals(Some(" Just give me this output."), file.peekLine("r"));
@@ -115,7 +115,7 @@ class FileBufferTest extends utest.Test {
}
function testSkipWhitespace() {
file = new FileBuffer('examples/parsing/whitespace.txt');
file = HankBuffer.FromFile('examples/parsing/whitespace.txt');
file.skipWhitespace();
HankAssert.equals("Just", file.take(4));

View File

@@ -3,6 +3,6 @@ import utest.Test;
class TestMain extends Test {
public static function main() {
utest.UTest.run([new HInterfaceTest(), new FileBufferTest(), new ParserTest()]);
utest.UTest.run([new HInterfaceTest(), new HankBufferTest(), new ParserTest()]);
}
}