FileBuffer-> HankBuffer for arbitrary strings
This commit is contained in:
30
hank/Alt.hx
30
hank/Alt.hx
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -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
|
||||
|
||||
104
hank/Parser.hx
104
hank/Parser.hx
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
@@ -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()]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user