Small fixes and dub changes.

This commit is contained in:
Kapendev 2025-01-23 08:51:42 +02:00
parent aec7a85553
commit dfe6c32a97
6 changed files with 86 additions and 57 deletions

View file

@ -14,6 +14,7 @@
], ],
"subPackages" : [ "subPackages" : [
"setup", "setup",
"web" "web",
"rin"
] ]
} }

View file

@ -1,7 +1,7 @@
# Rin # Rin
A script interpreter for Rin. A script interpreter for Rin.
It helps with error checking and debugging, making it easier to work with Parin scripts. It helps with error checking and debugging, making it easier to work with Rin scripts.
## Usage ## Usage

View file

@ -7,7 +7,7 @@ comment "#"
# Operators # Operators
color brightblue "\<(ADD|SUB|MUL|DIV|MOD|AND|OR|LESS|GREATER|EQUAL|NOT|POP|CLEAR|SWAP|COPY|COPYN|RANGE|IF|ELSE|THEN|CAT|SAME|WORD|NUMBER|LINE|DEBUG|LINEAR|ASSERT|END|ECHO|ECHON|LEAK|LEAKN|HERE|GET|GETN|SET|INIT|DROP|DROPN|INC|DEC|INCN|DECN|TOG|MENU|LOOP|SKIP|JUMP|CALL)\>" color brightblue "\<(ADD|SUB|MUL|DIV|MOD|AND|OR|LESS|GREATER|EQUAL|NOT|POP|CLEAR|SWAP|COPY|COPYN|RANGE|IF|ELSE|THEN|CAT|SAME|WORD|NUMBER|LINE|DEBUG|LINEAR|ASSERT|END|ECHO|ECHON|LEAK|LEAKN|HERE|GET|GETN|SET|INIT|DROP|DROPN|INC|DEC|INCN|DECN|TOG|MENU|LOOP|SKIP|JUMP|CALL)\>"
# Lines # Lines
color cyan "^[ \t]*\*" "^[ \t]*\|" "^[ \t]*\." "^[ \t]*\^" "^[ \t]*\$" color cyan "^[ \t]*\*" "^[ \t]*\|" "^[ \t]*\." "^[ \t]*\^" "^[ \t]*\$" "^[ \t]*\!"
# Comments # Comments
color green "^[ \t]*#.*" color green "^[ \t]*#.*"
# Trailing Whitespace # Trailing Whitespace

View file

@ -7,7 +7,7 @@
"license": "MIT", "license": "MIT",
"name": "rin", "name": "rin",
"dependencies": { "dependencies": {
"joka": "~main", "joka": "*",
"parin": {"path": ".."} "parin": {"path": ".."}
}, },
} }

View file

@ -56,8 +56,8 @@ int rinMain(string[] args) {
rinState.scriptPath = args[$ - 1]; rinState.scriptPath = args[$ - 1];
if (auto fault = readTextIntoBuffer(rinState.scriptPath, rinState.story.script)) { if (auto fault = readTextIntoBuffer(rinState.scriptPath, rinState.story.script)) {
switch (fault) { switch (fault) {
case Fault.cantOpen: println("Can't find file `{}`.".format(rinState.scriptPath)); break; case Fault.cantOpen: println("Can't open `{}`.".format(rinState.scriptPath)); break;
case Fault.cantRead: println("Can't read file `{}`.".format(rinState.scriptPath)); break; case Fault.cantRead: println("Can't read `{}`.".format(rinState.scriptPath)); break;
default: break; default: break;
} }
return 1; return 1;

View file

@ -7,19 +7,20 @@
// --- // ---
// TODO: Update all the doc comments here. // TODO: Update all the doc comments here.
// TODO: Think about lineIndex and nextLineIndex updating.
/// The `story` module provides a simple and versatile dialogue system. /// The `story` module provides a simple and versatile dialogue system.
module parin.story; module parin.story;
import joka.types;
import joka.containers;
import joka.ascii; import joka.ascii;
import joka.containers;
import joka.io; import joka.io;
import joka.types;
import joka.unions; import joka.unions;
@safe @nogc nothrow: @safe @nogc nothrow:
enum defaultStoryFixedListCapacity = 16;
enum StoryLineKind : ubyte { enum StoryLineKind : ubyte {
empty = ' ', empty = ' ',
comment = '#', comment = '#',
@ -28,6 +29,7 @@ enum StoryLineKind : ubyte {
pause = '.', pause = '.',
menu = '^', menu = '^',
expression = '$', expression = '$',
procedure = '!',
} }
enum StoryOp : ubyte { enum StoryOp : ubyte {
@ -80,7 +82,6 @@ enum StoryOp : ubyte {
LOOP, LOOP,
SKIP, SKIP,
JUMP, JUMP,
CALL,
} }
alias StoryWord = char[24]; alias StoryWord = char[24];
@ -101,22 +102,12 @@ struct StoryValue {
} }
IStr toStr() { IStr toStr() {
static char[64] buffer = void;
auto result = buffer[];
if (data.isType!StoryNumber) { if (data.isType!StoryNumber) {
result.copyStr(data.get!StoryNumber().toStr()); return format("{}", data.get!StoryNumber());
} else { } else {
auto temp = data.get!(StoryWord)()[]; auto temp = data.get!(StoryWord)()[];
foreach (i, c; temp) { return format("{}", temp[0 .. temp.findStart(char.init)]);
if (temp[i] == char.init) {
temp = temp[0 .. i];
break;
}
}
result.copyStr(temp);
} }
return result;
} }
} }
@ -134,7 +125,6 @@ struct Story {
LStr script; LStr script;
List!StoryStartEndPair pairs; List!StoryStartEndPair pairs;
List!StoryVariable labels; List!StoryVariable labels;
List!StoryValue stack;
List!StoryVariable variables; List!StoryVariable variables;
StoryNumber lineIndex; StoryNumber lineIndex;
StoryNumber nextLabelIndex; StoryNumber nextLabelIndex;
@ -155,38 +145,55 @@ struct Story {
return cast(StoryNumber) pairs.length; return cast(StoryNumber) pairs.length;
} }
bool hasKind(StoryLineKind kind) {
if (lineIndex >= lineCount) return false;
auto line = opIndex(lineIndex);
return line.length && line[0] == kind;
}
bool hasEnd() { bool hasEnd() {
return lineIndex == lineCount; return lineIndex == lineCount;
} }
bool hasPause() { bool hasPause() {
if (hasEnd) return true; if (hasEnd) return true;
if (lineIndex >= lineCount) return false; return hasKind(StoryLineKind.pause);
auto line = opIndex(lineIndex); }
return line.length && line[0] == StoryLineKind.pause;
bool hasProcedure() {
return hasKind(StoryLineKind.procedure);
} }
bool hasMenu() { bool hasMenu() {
if (lineIndex >= lineCount) return false; return hasKind(StoryLineKind.menu);
auto line = opIndex(lineIndex);
return line.length && line[0] == StoryLineKind.menu;
} }
bool hasText() { bool hasText() {
if (lineIndex >= lineCount) return false; return hasKind(StoryLineKind.text);
auto line = opIndex(lineIndex); }
return line.length && line[0] == StoryLineKind.text;
IStr[] procedure() {
static FixedList!(IStr, defaultStoryFixedListCapacity) buffer;
buffer.clear();
if (!hasProcedure) return [];
auto view = opIndex(lineIndex)[1 .. $].trimStart();
while (view.length) {
buffer.append(view.skipValue(' ').trimEnd());
view = view.trimStart();
}
return buffer[];
} }
IStr[] menu() { IStr[] menu() {
static FixedList!(IStr, 32) buffer; static FixedList!(IStr, defaultStoryFixedListCapacity) buffer;
buffer.clear(); buffer.clear();
if (!hasMenu) return []; if (!hasMenu) return [];
auto view = opIndex(lineIndex)[1 .. $].trimStart(); auto view = opIndex(lineIndex)[1 .. $].trimStart();
while (view.length) { while (view.length) {
if (buffer.length == buffer.capacity) return buffer[]; buffer.append(view.skipValue(StoryLineKind.menu).trimEnd());
buffer.append(view.skipValue(StoryLineKind.menu).trim()); view = view.trimStart();
} }
return buffer[]; return buffer[];
} }
@ -215,21 +222,29 @@ struct Story {
return -1; return -1;
} }
void setNextLabelIndex(StoryNumber value) {
nextLabelIndex = cast(StoryNumber) (value % (labels.length + 1));
}
void setLineIndex(StoryNumber value) {
lineIndex = (value) % (lineCount + 1);
}
void resetLineIndex() { void resetLineIndex() {
lineIndex = lineCount; lineIndex = lineCount;
nextLabelIndex = 0; nextLabelIndex = 0;
} }
void clear() { void jumpLineIndex(StoryNumber labelIndex) {
previousMenuResult = 0; lineIndex = labels[labelIndex].value.get!StoryNumber();
pairs.clear(); setNextLabelIndex(labelIndex + 1);
labels.clear();
variables.clear();
} }
Fault prepare() { Fault prepare() {
previousMenuResult = 0;
resetLineIndex(); resetLineIndex();
clear(); pairs.clear();
labels.clear();
if (script.isEmpty) return Fault.none; if (script.isEmpty) return Fault.none;
auto startIndex = StoryNumber.init; auto startIndex = StoryNumber.init;
auto prepareIndex = StoryNumber.init; auto prepareIndex = StoryNumber.init;
@ -242,7 +257,8 @@ struct Story {
pair.b -= line.length - line.trimEnd().length; pair.b -= line.length - line.trimEnd().length;
auto kind = toStoryLineKind(trimmedLine.length ? script[pair.a] : StoryLineKind.empty); auto kind = toStoryLineKind(trimmedLine.length ? script[pair.a] : StoryLineKind.empty);
if (kind.isNone) { if (kind.isNone) {
clear(); pairs.clear();
labels.clear();
faultPrepareIndex = prepareIndex; faultPrepareIndex = prepareIndex;
return kind.fault; return kind.fault;
} }
@ -251,7 +267,8 @@ struct Story {
auto word = StoryWord.init; auto word = StoryWord.init;
auto wordRef = word[]; auto wordRef = word[];
if (auto fault = wordRef.copyChars(name)) { if (auto fault = wordRef.copyChars(name)) {
clear(); pairs.clear();
labels.clear();
faultPrepareIndex = prepareIndex; faultPrepareIndex = prepareIndex;
return fault; return fault;
} }
@ -273,6 +290,8 @@ struct Story {
} }
Fault execute(IStr expression) { Fault execute(IStr expression) {
static FixedList!(StoryValue, defaultStoryFixedListCapacity) stack;
stack.clear(); stack.clear();
auto ifCounter = 0; auto ifCounter = 0;
while (true) with (StoryOp) { while (true) with (StoryOp) {
@ -378,7 +397,7 @@ struct Story {
auto da = stack.pop(); auto da = stack.pop();
if (!da.isType!StoryWord) return throwOpFault(op); if (!da.isType!StoryWord) return throwOpFault(op);
StoryWord word; StoryWord word;
auto data = concat(concat(da.toStr()), db.toStr()); auto data = concat(da.toStr(), db.toStr());
auto tempWordRef = word[]; auto tempWordRef = word[];
if (auto fault = tempWordRef.copyChars(data)) return fault; if (auto fault = tempWordRef.copyChars(data)) return fault;
stack.append(StoryValue(word)); stack.append(StoryValue(word));
@ -591,8 +610,7 @@ struct Story {
if (target < 0 || target >= labels.length || labels.length == 0) { if (target < 0 || target >= labels.length || labels.length == 0) {
resetLineIndex(); resetLineIndex();
} else { } else {
lineIndex = labels[target].value.get!StoryNumber(); jumpLineIndex(target);
nextLabelIndex = cast(StoryNumber) ((target + 1) % (labels.length + 1));
} }
break; break;
case SKIP: case SKIP:
@ -606,8 +624,7 @@ struct Story {
if (target < 0 || target >= labels.length || labels.length == 0) { if (target < 0 || target >= labels.length || labels.length == 0) {
resetLineIndex(); resetLineIndex();
} else { } else {
lineIndex = labels[target].value.get!StoryNumber(); jumpLineIndex(target);
nextLabelIndex = cast(StoryNumber) ((target + 1) % (labels.length + 1));
} }
break; break;
case JUMP: case JUMP:
@ -618,15 +635,11 @@ struct Story {
auto aIndex = findLabel(a); auto aIndex = findLabel(a);
if (aIndex != -1) { if (aIndex != -1) {
if (linearMode) break; if (linearMode) break;
lineIndex = labels[aIndex].value.get!StoryNumber(); jumpLineIndex(aIndex);
nextLabelIndex = cast(StoryNumber) ((aIndex + 1) % (labels.length + 1));
} else { } else {
return throwOpFault(op); return throwOpFault(op);
} }
break; break;
case CALL:
println("TODO: ", op);
return Fault.none;
} }
} else if (token.isMaybeStoryNumber) { } else if (token.isMaybeStoryNumber) {
auto number = token.toSigned(); auto number = token.toSigned();
@ -646,18 +659,18 @@ struct Story {
Fault update() { Fault update() {
if (lineCount == 0) return Fault.none; if (lineCount == 0) return Fault.none;
lineIndex = (lineIndex + 1) % (lineCount + 1); setLineIndex(lineIndex + 1);
while (lineIndex < lineCount && !hasPause && !hasMenu && !hasText) { while (lineIndex < lineCount && !hasPause && !hasProcedure && !hasMenu && !hasText) {
auto line = opIndex(lineIndex); auto line = opIndex(lineIndex);
if (line.length) { if (line.length) {
if (line[0] == StoryLineKind.expression) { if (line[0] == StoryLineKind.expression) {
auto fault = execute(line[1 .. $].trimStart()); auto fault = execute(line[1 .. $].trimStart());
if (fault) return fault; if (fault) return fault;
} else if (line[0] == StoryLineKind.label) { } else if (line[0] == StoryLineKind.label) {
nextLabelIndex = cast(StoryNumber) ((nextLabelIndex + 1) % (labels.length + 1)); setNextLabelIndex(nextLabelIndex + 1);
} }
} }
lineIndex = (lineIndex + 1) % (lineCount + 1); setLineIndex(lineIndex + 1);
} }
if (hasPause && lineIndex == lineCount) resetLineIndex(); if (hasPause && lineIndex == lineCount) resetLineIndex();
return Fault.none; return Fault.none;
@ -667,6 +680,20 @@ struct Story {
previousMenuResult = cast(StoryNumber) (i + 1); previousMenuResult = cast(StoryNumber) (i + 1);
return update(); return update();
} }
void reserve(Sz capacity) {
script.reserve(capacity);
pairs.reserve(capacity);
labels.reserve(capacity);
variables.reserve(capacity);
}
void free() {
script.free();
pairs.free();
labels.free();
variables.free();
}
} }
bool isMaybeStoryOp(IStr value) { bool isMaybeStoryOp(IStr value) {
@ -706,6 +733,7 @@ Result!StoryLineKind toStoryLineKind(char value) {
case '.': return Result!StoryLineKind(pause); case '.': return Result!StoryLineKind(pause);
case '^': return Result!StoryLineKind(menu); case '^': return Result!StoryLineKind(menu);
case '$': return Result!StoryLineKind(expression); case '$': return Result!StoryLineKind(expression);
case '!': return Result!StoryLineKind(procedure);
default: return Result!StoryLineKind(Fault.cantParse); default: return Result!StoryLineKind(Fault.cantParse);
} }
} }