3 Commits

Author SHA1 Message Date
99c64198c8 hank-vscode show errors
Some checks failed
/ test (push) Failing after 47s
2025-10-29 19:03:55 -05:00
e8f4d12ba3 fix parser white space error 2025-10-29 19:01:47 -05:00
6fbba748e9 put tags in choices 2025-10-29 19:01:32 -05:00
6 changed files with 46 additions and 43 deletions

View File

@@ -1,6 +1,5 @@
# @install: lix --silent download "git:https://k7izh9.gitea.cloud/NQN/hank#a7bd12074c88b29a58d68eba720c657f6a267cc6" into hank/0.0.8/git/a7bd12074c88b29a58d68eba720c657f6a267cc6
# @run: haxelib run-dir hank "${HAXE_LIBCACHE}/hank/0.0.8/git/a7bd12074c88b29a58d68eba720c657f6a267cc6"
-lib hscript
-lib utest
-cp ${HAXE_LIBCACHE}/hank/0.0.8/git/a7bd12074c88b29a58d68eba720c657f6a267cc6/
-D hank=0.0.8
-cp /Users/nat/repos/hank
-D hank=0.0.8
--macro Sys.println("haxe_libraries/hank.hxml:4: [Warning] Using dev version of library hank")

View File

@@ -13,7 +13,9 @@
(localFunction elementFor [:Story story :Dynamic document]
(let [:Dynamic d (document.createElement "div")]
(loop
(case (story.nextFrame)
(case (try
(story.nextFrame)
(catch [e] (errorMessage "$e") Finished))
((HasText t) (let [p (document.createElement "p")]
(set p.innerHTML t)
(d.appendChild p)))
@@ -37,7 +39,9 @@
->[document type data]
(case type
("choose"
(story.choose data)
(try
(story.choose data)
(catch [e] (errorMessage "$e")))
(set document.body.innerHTML "")
(document.body.appendChild (elementFor story document)))
(never otherwise))))

View File

@@ -2,10 +2,9 @@ package hank;
import haxe.ds.Option;
typedef Choice = {id:Int, onceOnly:Bool, label:Option<String>, condition:Option<String>, depth:Int, output:Output, divertTarget:Option<String>};
typedef ChoiceInfo = {choice: Choice, tags: Array<String>};
typedef ChoicePointInfo = {choices:Array<ChoiceInfo>, fallbackIndex:Int};
typedef FallbackChoiceInfo = {choiceInfo:ChoiceInfo, index:Int};
typedef Choice = {id:Int, onceOnly:Bool, label:Option<String>, condition:Option<String>, depth:Int, output:Output, divertTarget:Option<String>, tags:Array<String>};
typedef ChoicePointInfo = {choices:Array<Choice>, fallbackIndex:Int};
typedef FallbackChoiceInfo = {choiceInfo:Choice, index:Int};
class ChoiceExtension {
public static function toString(choice:Choice):String {

View File

@@ -2,7 +2,6 @@ package hank;
import haxe.ds.Option;
import hank.Alt.AltInstance;
import hank.Choice.ChoiceInfo;
import hank.Choice.ChoicePointInfo;
enum ExprType {
@@ -19,8 +18,6 @@ enum ExprType {
EGather(label:Option<String>, depth:Int, expr:ExprType);
// Hank pre-tag-implementation: Choices are the most complicated expressions
EChoice(c:Choice);
// Tags: Hold my beer
ETagged(e: ExprType, tags:Array<String>);
}
typedef HankExpr = {
@@ -56,9 +53,9 @@ class ASTExtension {
return -1;
}
static function tryAddFunc(choices: Array<ChoiceInfo>, expectedDepth: Int, c: Choice, tags: Array<String>) {
static function tryAddFunc(choices: Array<Choice>, expectedDepth: Int, c: Choice, tags: Array<String>) {
var valid = (c.depth == expectedDepth);
if (valid) choices.push({choice:c,tags:tags});
if (valid) choices.push(c);
return valid;
}
@@ -66,7 +63,7 @@ class ASTExtension {
Collect every choice in the choice point starting at the given index.
**/
public static function collectChoices(ast:HankAST, startingIndex:Int, depth:Int):ChoicePointInfo {
var choices = new Array<ChoiceInfo>();
var choices = new Array<Choice>();
var lastChoiceIndex = 0;
var tryAdd = tryAddFunc.bind(choices, depth);
if (startingIndex > ast.length || startingIndex < 0) {
@@ -79,8 +76,6 @@ class ASTExtension {
// Gather choices of the current depth
case EChoice(choice) if (tryAdd(choice, [])):
lastChoiceIndex = i;
case ETagged(EChoice(choice), tags) if (tryAdd(choice, tags)):
lastChoiceIndex = i;
// Stop at the next gather of this depth
case EGather(_, d, _) if (d == depth):
break;
@@ -97,7 +92,7 @@ class ASTExtension {
public static function findNextGather(ast:HankAST, path:String, startingIndex:Int, maxDepth:Int):Int {
for (i in startingIndex...findEOF(ast, path)) {
switch (ast[i].expr) {
case EGather(_, depth, _) | ETagged(EGather(_, depth, _), _):
case EGather(_, depth, _):
if (depth <= maxDepth)
return i;
default:
@@ -111,7 +106,7 @@ class ASTExtension {
for (i in 0...ast.length) {
var expr = ast[i].expr;
switch (expr) {
case EChoice(c) | ETagged(EChoice(c), _) | EGather(_, _, EChoice(c)):
case EChoice(c):
if (c.id == id)
return i;
default:

View File

@@ -115,7 +115,8 @@ class Parser {
for (rule in symbols) {
var symbol = rule.keys().next();
var rule = rule[symbol];
if (line.startsWith(symbol)) {
if (line.trim().startsWith(symbol)) {
buffer.skipWhitespace();
return rule(buffer, position);
}
}
@@ -216,7 +217,8 @@ class Parser {
condition: condition,
depth: depth,
output: output,
divertTarget: divertTarget
divertTarget: divertTarget,
tags: []
});
}
@@ -224,7 +226,14 @@ class Parser {
buffer.drop('#');
var tagLine = buffer.takeLine('lr').unwrap();
var tags = tagLine.tokenize();
return ETagged(parseExpr(buffer, position), tags);
var expr = parseExpr(buffer, position);
switch (expr) {
case EChoice(c):
c.tags = tags;
default:
trace("Warning! Can't apply tags to " + expr);
}
return expr;
}
static function haxeBlock(buffer:HankBuffer, position:HankBuffer.Position):ExprType {

View File

@@ -12,7 +12,6 @@ using hank.Extensions;
using HankAST.ASTExtension;
import hank.Choice;
import hank.Choice.ChoiceInfo;
import hank.Choice.FallbackChoiceInfo;
using Choice.ChoiceExtension;
@@ -239,12 +238,12 @@ class Story {
nextExpr = ast[nextIdx].expr;
// trace(nextExpr);
switch (nextExpr) {
case EGather(_, _, exp) | ETagged(EGather(_,_,exp), _):
case EGather(_, _, exp):
nextExpr = exp;
default:
}
switch (nextExpr) {
case EOutput(output) | ETagged(EOutput(output), _):
case EOutput(output):
if (output.startsWithGlue())
text = Output.appendNextText(this, text + " ", Output.GLUE_ERROR);
default:
@@ -322,7 +321,7 @@ class Story {
++exprIndex;
return nextFrame();
case EChoice(choice) | ETagged(EChoice(choice), _):
case EChoice(choice):
if (choice.depth > weaveDepth) {
weaveDepth = choice.depth;
} else if (choice.depth < weaveDepth) {
@@ -343,7 +342,7 @@ class Story {
var choices = availableChoices();
var optionsText = [
for (choiceInfo in choices)
choiceInfo.choice.output.format(this, hInterface, random, altInstances, nodeScopes, false)
choiceInfo.output.format(this, hInterface, random, altInstances, nodeScopes, false)
];
var tags = [
for (choiceInfo in choices)
@@ -353,30 +352,30 @@ class Story {
return finalChoiceProcessing(optionsText, tags);
} else {
var fallback = fallbackChoice();
switch (fallback.choiceInfo.choice.divertTarget) {
switch (fallback.choiceInfo.divertTarget) {
case Some(t) if (t.length > 0):
var fallbackText = evaluateChoice(fallback.choiceInfo.choice);
var fallbackText = evaluateChoice(fallback.choiceInfo);
if (fallbackText.length > 0) {
throw 'For some reason a fallback choice evaluated to text!';
}
return nextFrame();
default:
exprIndex = fallback.index + 1;
weaveDepth = fallback.choiceInfo.choice.depth + 1;
weaveDepth = fallback.choiceInfo.depth + 1;
return nextFrame();
}
}
}
private function traceChoiceArray(choices:Array<ChoiceInfo>) {
private function traceChoiceArray(choices:Array<Choice>) {
for (choiceInfo in choices) {
trace('${choiceInfo.choice.toString()}: #${choiceInfo.tags.join(" #")}');
trace('${choiceInfo.toString()}: #${choiceInfo.tags.join(" #")}');
}
trace('---');
}
private function availableChoices():Array<ChoiceInfo> {
var choices = new Array<ChoiceInfo>();
private function availableChoices():Array<Choice> {
var choices = new Array<Choice>();
// If we're threading, collect all the childrens' choices, too.
if (embedMode == Thread) {
@@ -389,20 +388,18 @@ class Story {
}
}
if (exprIndex < ast.length &&
(ast[exprIndex].expr.match(EChoice(_))
|| ast[exprIndex].expr.match(ETagged(EChoice(_), _)))) {
if (exprIndex < ast.length && ast[exprIndex].expr.match(EChoice(_))) {
var allChoiceInfo = ast.collectChoices(exprIndex, weaveDepth).choices;
for (choiceInfo in allChoiceInfo) {
if (choicesTaken.indexOf(choiceInfo.choice.id) == -1 || !choiceInfo.choice.onceOnly) {
switch (choiceInfo.choice.condition) {
if (choicesTaken.indexOf(choiceInfo.id) == -1 || !choiceInfo.onceOnly) {
switch (choiceInfo.condition) {
case Some(expr):
if (!hInterface.cond(expr, nodeScopes)) {
continue;
}
case None:
}
if (!choiceInfo.choice.output.isEmpty()) {
if (!choiceInfo.output.isEmpty()) {
choices.push(choiceInfo);
}
}
@@ -418,7 +415,7 @@ class Story {
private function fallbackChoice():FallbackChoiceInfo {
var choiceInfo = ast.collectChoices(exprIndex, weaveDepth);
var lastChoice = choiceInfo.choices[choiceInfo.choices.length - 1];
if (lastChoice.choice.output.isEmpty()) {
if (lastChoice.output.isEmpty()) {
return {choiceInfo: lastChoice, index: choiceInfo.fallbackIndex};
} else {
throw 'there is no fallback choice!';
@@ -574,7 +571,7 @@ class Story {
return embeddedBlocks[0].choose(choiceIndex);
} else {
// if not embedded, actually make the choice. availableChoices() accounts for aggregating threaded choices
var output = evaluateChoice(availableChoices()[choiceIndex].choice);
var output = evaluateChoice(availableChoices()[choiceIndex]);
if (embedMode == Thread) {
embedMode = Tunnel;
embeddedBlocks = [];