mirror of
https://github.com/Kapendev/parin.git
synced 2025-04-27 13:39:54 +03:00
Small fixes and dub changes.
This commit is contained in:
parent
aec7a85553
commit
dfe6c32a97
6 changed files with 86 additions and 57 deletions
3
dub.json
3
dub.json
|
@ -14,6 +14,7 @@
|
||||||
],
|
],
|
||||||
"subPackages" : [
|
"subPackages" : [
|
||||||
"setup",
|
"setup",
|
||||||
"web"
|
"web",
|
||||||
|
"rin"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"name": "rin",
|
"name": "rin",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"joka": "~main",
|
"joka": "*",
|
||||||
"parin": {"path": ".."}
|
"parin": {"path": ".."}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue