mirror of
https://github.com/Kapendev/parin.git
synced 2025-05-01 07:29:55 +03:00
Made rin tool and added debug mode to scripts.
This commit is contained in:
parent
17c7b00e72
commit
85596d6fd3
7 changed files with 204 additions and 74 deletions
|
@ -49,7 +49,7 @@ $ 3 i SET
|
||||||
$ on GET i GET col_count GET 7 * + x CAT SET
|
$ on GET i GET col_count GET 7 * + x CAT SET
|
||||||
$ 4 3 i INC RANGE IF LOOP THEN
|
$ 4 3 i INC RANGE IF LOOP THEN
|
||||||
|
|
||||||
## Draw the board.
|
# Draw the board.
|
||||||
$ i INIT
|
$ i INIT
|
||||||
*
|
*
|
||||||
$ i GET x CAT item SET
|
$ i GET x CAT item SET
|
||||||
|
@ -57,3 +57,6 @@ $ col_count GET i GET % 1 col_count GET - = is_last SET
|
||||||
$ item GET GET is_last GET IF ECHO ELSE ECHON THEN
|
$ item GET GET is_last GET IF ECHO ELSE ECHON THEN
|
||||||
$ off GET i GET x CAT SET
|
$ off GET i GET x CAT SET
|
||||||
$ count GET i INC < IF LOOP THEN
|
$ count GET i INC < IF LOOP THEN
|
||||||
|
|
||||||
|
*
|
||||||
|
$ DEBUG IF ECHO
|
||||||
|
|
19
rin/.gitignore
vendored
Normal file
19
rin/.gitignore
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
.dub
|
||||||
|
dub.s*
|
||||||
|
docs.json
|
||||||
|
__dummy.html
|
||||||
|
docs/
|
||||||
|
/rin
|
||||||
|
rin.so
|
||||||
|
rin.dylib
|
||||||
|
rin.dll
|
||||||
|
rin.a
|
||||||
|
rin.lib
|
||||||
|
rin-test-*
|
||||||
|
*.exe
|
||||||
|
*.pdb
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.lst
|
||||||
|
parin*
|
||||||
|
test.rin
|
9
rin/README.md
Normal file
9
rin/README.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# Setup
|
||||||
|
|
||||||
|
A Parin script interpreter.
|
||||||
|
Works both with and without DUB.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
* With DUB: `dub run parin:rin`
|
||||||
|
* Without DUB: `dmd -run -Ijoka_path _Iparin_path source/app.d`
|
13
rin/dub.json
Normal file
13
rin/dub.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"authors": [
|
||||||
|
"Alexandros F. G. Kapretsos"
|
||||||
|
],
|
||||||
|
"copyright": "Copyright © 2024, Alexandros F. G. Kapretsos",
|
||||||
|
"description": "A Parin script interpreter.",
|
||||||
|
"license": "MIT",
|
||||||
|
"name": "rin",
|
||||||
|
"dependencies": {
|
||||||
|
"joka": "~main",
|
||||||
|
"parin": {"path": ".."}
|
||||||
|
},
|
||||||
|
}
|
61
rin/source/app.d
Normal file
61
rin/source/app.d
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/// A Parin script interpreter.
|
||||||
|
|
||||||
|
import joka;
|
||||||
|
import parin.story;
|
||||||
|
|
||||||
|
IStr path;
|
||||||
|
Story story;
|
||||||
|
|
||||||
|
void printError(Sz index, IStr text) {
|
||||||
|
printfln("{}({}): {}", path, index, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Fault prepareStory() {
|
||||||
|
if (auto fault = story.prepare()) {
|
||||||
|
auto index = story.faultPrepareIndex + 1;
|
||||||
|
printError(index, "Invalid character at the beginning of the line.");
|
||||||
|
return fault;
|
||||||
|
}
|
||||||
|
return Fault.none;
|
||||||
|
}
|
||||||
|
|
||||||
|
Fault updateStory() {
|
||||||
|
if (story.hasText) println(story.text);
|
||||||
|
if (auto fault = story.update()) {
|
||||||
|
auto index = story.lineIndex + 1;
|
||||||
|
switch (fault) with (Fault) {
|
||||||
|
case invalid: printError(index, "Invalid arguments passed to the `{}` operator.".format(story.faultOp)); break;
|
||||||
|
case overflow: printError(index, "A word or number is too long."); break;
|
||||||
|
case cantParse: printError(index, "A word, number, or operator contains invalid characters."); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return fault;
|
||||||
|
}
|
||||||
|
return Fault.none;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(string[] args) {
|
||||||
|
if (args.length == 1) {
|
||||||
|
println("Usage: rin [options] script");
|
||||||
|
println("Options: -debug");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
foreach (arg; args[1 .. $ - 1]) {
|
||||||
|
if (arg == "-debug") story.debugMode = true;
|
||||||
|
}
|
||||||
|
path = args[$ - 1];
|
||||||
|
if (auto fault = readTextIntoBuffer(path, story.script)) {
|
||||||
|
switch (fault) {
|
||||||
|
case Fault.cantOpen: println("Can't find file `{}`.".format(path)); break;
|
||||||
|
case Fault.cantRead: println("Can't read file `{}`.".format(path)); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (prepareStory()) return 1;
|
||||||
|
if (updateStory()) return 1;
|
||||||
|
while (story.lineIndex != story.lineCount) {
|
||||||
|
if (updateStory()) return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -13,5 +13,6 @@ public import joka.io;
|
||||||
public import parin.engine;
|
public import parin.engine;
|
||||||
public import parin.map;
|
public import parin.map;
|
||||||
public import parin.sprite;
|
public import parin.sprite;
|
||||||
|
public import parin.story;
|
||||||
public import parin.timer;
|
public import parin.timer;
|
||||||
public import parin.ui;
|
public import parin.ui;
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
// TODO: toStr char arrays might need some work. It has bad error messages for them.
|
// TODO: toStr char arrays might need some work. It has bad error messages for them.
|
||||||
// TODO: concat and others might need a "intoBuffer" vesion.
|
// TODO: concat and others might need a "intoBuffer" vesion.
|
||||||
// TODO: Look at CAT case and think about how to make it better with joka.
|
// TODO: Look at CAT case and think about how to make it better with joka.
|
||||||
// TODO: Make command line tool for checking a script and for simple one-line expressions.
|
|
||||||
// TODO: Change the name of StoryLine.
|
|
||||||
// TODO: Add better error info like line, reason, ...
|
|
||||||
// NOTE: Remember to update both joka and parin at the same time because there was a evil change.
|
// NOTE: Remember to update both joka and parin at the same time because there was a evil change.
|
||||||
// NOTE: I will start cleanning and then will add CALL.
|
// NOTE: I will start cleanning and then will add CALL.
|
||||||
|
|
||||||
|
@ -17,7 +14,8 @@ import joka.ascii;
|
||||||
import joka.io;
|
import joka.io;
|
||||||
import joka.unions;
|
import joka.unions;
|
||||||
|
|
||||||
enum StoryLine : ubyte {
|
enum StoryLineKind : ubyte {
|
||||||
|
empty = ' ',
|
||||||
comment = '#',
|
comment = '#',
|
||||||
label = '*',
|
label = '*',
|
||||||
text = '|',
|
text = '|',
|
||||||
|
@ -49,6 +47,7 @@ enum StoryOp : ubyte {
|
||||||
CAT,
|
CAT,
|
||||||
WORD,
|
WORD,
|
||||||
NUM,
|
NUM,
|
||||||
|
DEBUG,
|
||||||
END,
|
END,
|
||||||
ECHO,
|
ECHO,
|
||||||
ECHON,
|
ECHON,
|
||||||
|
@ -126,6 +125,9 @@ struct Story {
|
||||||
StoryNumber lineIndex;
|
StoryNumber lineIndex;
|
||||||
StoryNumber nextLabelIndex;
|
StoryNumber nextLabelIndex;
|
||||||
StoryNumber previousMenuResult;
|
StoryNumber previousMenuResult;
|
||||||
|
StoryNumber faultPrepareIndex;
|
||||||
|
StoryOp faultOp;
|
||||||
|
bool debugMode;
|
||||||
|
|
||||||
IStr opIndex(Sz i) {
|
IStr opIndex(Sz i) {
|
||||||
if (i >= lineCount) {
|
if (i >= lineCount) {
|
||||||
|
@ -135,20 +137,32 @@ struct Story {
|
||||||
return script[pair.a .. pair.b];
|
return script[pair.a .. pair.b];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Fault throwOpFault(StoryOp op) {
|
||||||
|
faultOp = op;
|
||||||
|
return Fault.invalid;
|
||||||
|
}
|
||||||
|
|
||||||
StoryNumber lineCount() {
|
StoryNumber lineCount() {
|
||||||
return cast(StoryNumber) pairs.length;
|
return cast(StoryNumber) pairs.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasPause() {
|
bool hasPause() {
|
||||||
return lineIndex == lineCount || (lineIndex < lineCount && opIndex(lineIndex)[0] == StoryLine.pause);
|
if (lineIndex == lineCount) return true;
|
||||||
|
if (lineIndex >= lineCount) return false;
|
||||||
|
auto line = opIndex(lineIndex);
|
||||||
|
return line.length && line[0] == StoryLineKind.pause;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasMenu() {
|
bool hasMenu() {
|
||||||
return lineIndex < lineCount && opIndex(lineIndex)[0] == StoryLine.menu;
|
if (lineIndex >= lineCount) return false;
|
||||||
|
auto line = opIndex(lineIndex);
|
||||||
|
return line.length && line[0] == StoryLineKind.menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasText() {
|
bool hasText() {
|
||||||
return lineIndex < lineCount && opIndex(lineIndex)[0] == StoryLine.text;
|
if (lineIndex >= lineCount) return false;
|
||||||
|
auto line = opIndex(lineIndex);
|
||||||
|
return line.length && line[0] == StoryLineKind.text;
|
||||||
}
|
}
|
||||||
|
|
||||||
IStr text() {
|
IStr text() {
|
||||||
|
@ -163,7 +177,7 @@ struct Story {
|
||||||
auto view = hasMenu ? opIndex(lineIndex)[1 .. $].trimStart() : "";
|
auto view = hasMenu ? opIndex(lineIndex)[1 .. $].trimStart() : "";
|
||||||
while (view.length) {
|
while (view.length) {
|
||||||
if (buffer.length == buffer.capacity) return buffer[];
|
if (buffer.length == buffer.capacity) return buffer[];
|
||||||
buffer.append(view.skipValue(StoryLine.menu).trim());
|
buffer.append(view.skipValue(StoryLineKind.menu).trim());
|
||||||
}
|
}
|
||||||
return buffer[];
|
return buffer[];
|
||||||
}
|
}
|
||||||
|
@ -174,23 +188,23 @@ struct Story {
|
||||||
pairs.clear();
|
pairs.clear();
|
||||||
labels.clear();
|
labels.clear();
|
||||||
if (script.isEmpty) return Fault.none;
|
if (script.isEmpty) return Fault.none;
|
||||||
auto start = 0LU;
|
auto start = 0;
|
||||||
|
auto prepareIndex = 0;
|
||||||
foreach (i, c; script) {
|
foreach (i, c; script) {
|
||||||
if (c == '\n') {
|
if (c == '\n') {
|
||||||
auto pair = StoryStartEndPair(cast(uint) start, cast(uint) i);
|
auto pair = StoryStartEndPair(start, cast(uint) i);
|
||||||
auto line = script[pair.a .. pair.b];
|
auto line = script[pair.a .. pair.b];
|
||||||
auto trimmedLine = line.trim();
|
auto trimmedLine = line.trim();
|
||||||
if (trimmedLine.length == 0) continue;
|
|
||||||
pair.a += line.length - line.trimStart().length;
|
pair.a += line.length - line.trimStart().length;
|
||||||
pair.b -= line.length - line.trimEnd().length;
|
pair.b -= line.length - line.trimEnd().length;
|
||||||
if (pair.a == pair.b) continue;
|
auto lineResult = toStoryLineKind(trimmedLine.length ? script[pair.a] : StoryLineKind.empty);
|
||||||
auto lineResult = script[pair.a].toStoryLine();
|
|
||||||
if (lineResult.isNone) {
|
if (lineResult.isNone) {
|
||||||
pairs.clear();
|
pairs.clear();
|
||||||
return Fault.invalid;
|
faultPrepareIndex = prepareIndex;
|
||||||
|
return Fault.cantParse;
|
||||||
}
|
}
|
||||||
auto kind = lineResult.value;
|
auto kind = lineResult.value;
|
||||||
if (kind == StoryLine.label) {
|
if (kind == StoryLineKind.label) {
|
||||||
// TODO: Make words easier to use doooood.
|
// TODO: Make words easier to use doooood.
|
||||||
auto name = trimmedLine[1 .. $].trimStart();
|
auto name = trimmedLine[1 .. $].trimStart();
|
||||||
auto temp = StoryWord.init;
|
auto temp = StoryWord.init;
|
||||||
|
@ -199,7 +213,8 @@ struct Story {
|
||||||
labels.append(StoryVariable(temp, StoryValue(cast(StoryNumber) pairs.length)));
|
labels.append(StoryVariable(temp, StoryValue(cast(StoryNumber) pairs.length)));
|
||||||
}
|
}
|
||||||
pairs.append(pair);
|
pairs.append(pair);
|
||||||
start = i + 1;
|
start = cast(int) (i + 1);
|
||||||
|
prepareIndex += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lineIndex = lineCount;
|
lineIndex = lineCount;
|
||||||
|
@ -239,10 +254,10 @@ struct Story {
|
||||||
case LESS:
|
case LESS:
|
||||||
case GREATER:
|
case GREATER:
|
||||||
case EQUAL:
|
case EQUAL:
|
||||||
if (stack.length < 2) return Fault.invalid;
|
if (stack.length < 2) return throwOpFault(op);
|
||||||
auto db = stack.pop();
|
auto db = stack.pop();
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!db.isType!StoryNumber || !da.isType!StoryNumber) return Fault.invalid;
|
if (!db.isType!StoryNumber || !da.isType!StoryNumber) return throwOpFault(op);
|
||||||
auto a = da.get!StoryNumber;
|
auto a = da.get!StoryNumber;
|
||||||
auto b = db.get!StoryNumber;
|
auto b = db.get!StoryNumber;
|
||||||
auto c = StoryNumber.init;
|
auto c = StoryNumber.init;
|
||||||
|
@ -262,9 +277,9 @@ struct Story {
|
||||||
stack.append(StoryValue(c));
|
stack.append(StoryValue(c));
|
||||||
break;
|
break;
|
||||||
case NOT:
|
case NOT:
|
||||||
if (stack.length < 1) return Fault.invalid;
|
if (stack.length < 1) return throwOpFault(op);
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!da.isType!StoryNumber) return Fault.invalid;
|
if (!da.isType!StoryNumber) return throwOpFault(op);
|
||||||
stack.append(StoryValue(!da.get!StoryNumber));
|
stack.append(StoryValue(!da.get!StoryNumber));
|
||||||
break;
|
break;
|
||||||
case POP:
|
case POP:
|
||||||
|
@ -274,31 +289,31 @@ struct Story {
|
||||||
stack.clear();
|
stack.clear();
|
||||||
break;
|
break;
|
||||||
case SWAP:
|
case SWAP:
|
||||||
if (stack.length < 2) return Fault.invalid;
|
if (stack.length < 2) return throwOpFault(op);
|
||||||
auto db = stack.pop();
|
auto db = stack.pop();
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
stack.append(db);
|
stack.append(db);
|
||||||
stack.append(da);
|
stack.append(da);
|
||||||
break;
|
break;
|
||||||
case COPY:
|
case COPY:
|
||||||
if (stack.length < 1) return Fault.invalid;
|
if (stack.length < 1) return throwOpFault(op);
|
||||||
stack.append(stack[$ - 1]);
|
stack.append(stack[$ - 1]);
|
||||||
break;
|
break;
|
||||||
case RANGE:
|
case RANGE:
|
||||||
if (stack.length < 3) return Fault.invalid;
|
if (stack.length < 3) return throwOpFault(op);
|
||||||
auto dc = stack.pop();
|
auto dc = stack.pop();
|
||||||
auto db = stack.pop();
|
auto db = stack.pop();
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!dc.isType!StoryNumber || !db.isType!StoryNumber || !da.isType!StoryNumber) return Fault.invalid;
|
if (!dc.isType!StoryNumber || !db.isType!StoryNumber || !da.isType!StoryNumber) return throwOpFault(op);
|
||||||
auto a = da.get!StoryNumber();
|
auto a = da.get!StoryNumber();
|
||||||
auto b = db.get!StoryNumber();
|
auto b = db.get!StoryNumber();
|
||||||
auto c = dc.get!StoryNumber();
|
auto c = dc.get!StoryNumber();
|
||||||
stack.append(StoryValue(c >= b && c <= a));
|
stack.append(StoryValue(c >= b && c <= a));
|
||||||
break;
|
break;
|
||||||
case IF:
|
case IF:
|
||||||
if (stack.length < 1) return Fault.invalid;
|
if (stack.length < 1) return throwOpFault(op);
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!da.isType!StoryNumber) return Fault.invalid;
|
if (!da.isType!StoryNumber) return throwOpFault(op);
|
||||||
if (!da.get!StoryNumber) ifCounter += 1;
|
if (!da.get!StoryNumber) ifCounter += 1;
|
||||||
break;
|
break;
|
||||||
case ELSE:
|
case ELSE:
|
||||||
|
@ -307,10 +322,10 @@ struct Story {
|
||||||
case THEN:
|
case THEN:
|
||||||
break;
|
break;
|
||||||
case CAT:
|
case CAT:
|
||||||
if (stack.length < 2) return Fault.invalid;
|
if (stack.length < 2) return throwOpFault(op);
|
||||||
auto db = stack.pop();
|
auto db = stack.pop();
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!db.isType!StoryWord) return Fault.invalid;
|
if (!db.isType!StoryWord) return throwOpFault(op);
|
||||||
StoryWord word;
|
StoryWord word;
|
||||||
auto data = concat(concat(db.toStr()), da.toStr());
|
auto data = concat(concat(db.toStr()), da.toStr());
|
||||||
if (data.length > word.length) return Fault.overflow;
|
if (data.length > word.length) return Fault.overflow;
|
||||||
|
@ -319,15 +334,18 @@ struct Story {
|
||||||
stack.append(StoryValue(word));
|
stack.append(StoryValue(word));
|
||||||
break;
|
break;
|
||||||
case WORD:
|
case WORD:
|
||||||
if (stack.length < 1) return Fault.invalid;
|
if (stack.length < 1) return throwOpFault(op);
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
stack.append(StoryValue(da.isType!StoryWord));
|
stack.append(StoryValue(da.isType!StoryWord));
|
||||||
break;
|
break;
|
||||||
case NUM:
|
case NUM:
|
||||||
if (stack.length < 1) return Fault.invalid;
|
if (stack.length < 1) return throwOpFault(op);
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
stack.append(StoryValue(da.isType!StoryNumber));
|
stack.append(StoryValue(da.isType!StoryNumber));
|
||||||
break;
|
break;
|
||||||
|
case DEBUG:
|
||||||
|
stack.append(StoryValue(debugMode));
|
||||||
|
break;
|
||||||
case END:
|
case END:
|
||||||
return Fault.none;
|
return Fault.none;
|
||||||
case ECHO:
|
case ECHO:
|
||||||
|
@ -388,9 +406,9 @@ struct Story {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case HERE:
|
case HERE:
|
||||||
if (stack.length < 1) return Fault.invalid;
|
if (stack.length < 1) return throwOpFault(op);
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!da.isType!StoryWord) return Fault.invalid;
|
if (!da.isType!StoryWord) return throwOpFault(op);
|
||||||
auto a = da.get!StoryWord();
|
auto a = da.get!StoryWord();
|
||||||
auto isNotThere = true;
|
auto isNotThere = true;
|
||||||
foreach (variable; variables) {
|
foreach (variable; variables) {
|
||||||
|
@ -402,9 +420,9 @@ struct Story {
|
||||||
stack.append(StoryValue(!isNotThere));
|
stack.append(StoryValue(!isNotThere));
|
||||||
break;
|
break;
|
||||||
case GET:
|
case GET:
|
||||||
if (stack.length < 1) return Fault.invalid;
|
if (stack.length < 1) return throwOpFault(op);
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!da.isType!StoryWord) return Fault.invalid;
|
if (!da.isType!StoryWord) return throwOpFault(op);
|
||||||
auto a = da.get!StoryWord();
|
auto a = da.get!StoryWord();
|
||||||
auto isNotThere = true;
|
auto isNotThere = true;
|
||||||
foreach (variable; variables) {
|
foreach (variable; variables) {
|
||||||
|
@ -414,13 +432,13 @@ struct Story {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isNotThere) return Fault.invalid;
|
if (isNotThere) return throwOpFault(op);
|
||||||
break;
|
break;
|
||||||
case SET:
|
case SET:
|
||||||
if (stack.length < 2) return Fault.invalid;
|
if (stack.length < 2) return throwOpFault(op);
|
||||||
auto db = stack.pop();
|
auto db = stack.pop();
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!db.isType!StoryWord) return Fault.invalid;
|
if (!db.isType!StoryWord) return throwOpFault(op);
|
||||||
auto b = db.get!StoryWord();
|
auto b = db.get!StoryWord();
|
||||||
auto isNotThere = true;
|
auto isNotThere = true;
|
||||||
foreach (ref variable; variables) {
|
foreach (ref variable; variables) {
|
||||||
|
@ -433,9 +451,9 @@ struct Story {
|
||||||
if (isNotThere) variables.append(StoryVariable(b, da));
|
if (isNotThere) variables.append(StoryVariable(b, da));
|
||||||
break;
|
break;
|
||||||
case INIT:
|
case INIT:
|
||||||
if (stack.length < 1) return Fault.invalid;
|
if (stack.length < 1) return throwOpFault(op);
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!da.isType!StoryWord) return Fault.invalid;
|
if (!da.isType!StoryWord) return throwOpFault(op);
|
||||||
auto a = da.get!StoryWord();
|
auto a = da.get!StoryWord();
|
||||||
auto isNotThere = true;
|
auto isNotThere = true;
|
||||||
foreach (ref variable; variables) {
|
foreach (ref variable; variables) {
|
||||||
|
@ -448,9 +466,9 @@ struct Story {
|
||||||
if (isNotThere) variables.append(StoryVariable(a, StoryValue(0)));
|
if (isNotThere) variables.append(StoryVariable(a, StoryValue(0)));
|
||||||
break;
|
break;
|
||||||
case DROP:
|
case DROP:
|
||||||
if (stack.length < 1) return Fault.invalid;
|
if (stack.length < 1) return throwOpFault(op);
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!da.isType!StoryWord) return Fault.invalid;
|
if (!da.isType!StoryWord) return throwOpFault(op);
|
||||||
auto a = da.get!StoryWord();
|
auto a = da.get!StoryWord();
|
||||||
auto isNotThere = true;
|
auto isNotThere = true;
|
||||||
foreach (i, variable; variables) {
|
foreach (i, variable; variables) {
|
||||||
|
@ -465,9 +483,9 @@ struct Story {
|
||||||
break;
|
break;
|
||||||
case INC:
|
case INC:
|
||||||
case DEC:
|
case DEC:
|
||||||
if (stack.length < 1) return Fault.invalid;
|
if (stack.length < 1) return throwOpFault(op);
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!da.isType!StoryWord) return Fault.invalid;
|
if (!da.isType!StoryWord) return throwOpFault(op);
|
||||||
auto a = da.get!StoryWord();
|
auto a = da.get!StoryWord();
|
||||||
auto isNotThere = true;
|
auto isNotThere = true;
|
||||||
foreach (ref variable; variables) {
|
foreach (ref variable; variables) {
|
||||||
|
@ -476,20 +494,20 @@ struct Story {
|
||||||
variable.value.get!StoryNumber() += (op == INC ? 1 : -1);
|
variable.value.get!StoryNumber() += (op == INC ? 1 : -1);
|
||||||
stack.append(variable.value);
|
stack.append(variable.value);
|
||||||
} else {
|
} else {
|
||||||
return Fault.invalid;
|
return throwOpFault(op);
|
||||||
}
|
}
|
||||||
isNotThere = false;
|
isNotThere = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isNotThere) return Fault.invalid;
|
if (isNotThere) return throwOpFault(op);
|
||||||
break;
|
break;
|
||||||
case INCN:
|
case INCN:
|
||||||
case DECN:
|
case DECN:
|
||||||
if (stack.length < 2) return Fault.invalid;
|
if (stack.length < 2) return throwOpFault(op);
|
||||||
auto db = stack.pop();
|
auto db = stack.pop();
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!db.isType!StoryWord || !da.isType!StoryNumber) return Fault.invalid;
|
if (!db.isType!StoryWord || !da.isType!StoryNumber) return throwOpFault(op);
|
||||||
auto b = db.get!StoryWord();
|
auto b = db.get!StoryWord();
|
||||||
auto a = da.get!StoryNumber();
|
auto a = da.get!StoryNumber();
|
||||||
auto isNotThere = true;
|
auto isNotThere = true;
|
||||||
|
@ -499,18 +517,18 @@ struct Story {
|
||||||
variable.value.get!StoryNumber() += a * (op == INCN ? 1 : -1);
|
variable.value.get!StoryNumber() += a * (op == INCN ? 1 : -1);
|
||||||
stack.append(variable.value);
|
stack.append(variable.value);
|
||||||
} else {
|
} else {
|
||||||
return Fault.invalid;
|
return throwOpFault(op);
|
||||||
}
|
}
|
||||||
isNotThere = false;
|
isNotThere = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isNotThere) return Fault.invalid;
|
if (isNotThere) return throwOpFault(op);
|
||||||
break;
|
break;
|
||||||
case TOG:
|
case TOG:
|
||||||
if (stack.length < 1) return Fault.invalid;
|
if (stack.length < 1) return throwOpFault(op);
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!da.isType!StoryWord) return Fault.invalid;
|
if (!da.isType!StoryWord) return throwOpFault(op);
|
||||||
auto a = da.get!StoryWord();
|
auto a = da.get!StoryWord();
|
||||||
auto isNotThere = true;
|
auto isNotThere = true;
|
||||||
foreach (ref variable; variables) {
|
foreach (ref variable; variables) {
|
||||||
|
@ -519,18 +537,19 @@ struct Story {
|
||||||
variable.value.get!StoryNumber() = !variable.value.get!StoryNumber();
|
variable.value.get!StoryNumber() = !variable.value.get!StoryNumber();
|
||||||
stack.append(variable.value);
|
stack.append(variable.value);
|
||||||
} else {
|
} else {
|
||||||
return Fault.invalid;
|
return throwOpFault(op);
|
||||||
}
|
}
|
||||||
isNotThere = false;
|
isNotThere = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isNotThere) return Fault.invalid;
|
if (isNotThere) return throwOpFault(op);
|
||||||
break;
|
break;
|
||||||
case MENU:
|
case MENU:
|
||||||
stack.append(StoryValue(previousMenuResult));
|
stack.append(StoryValue(previousMenuResult));
|
||||||
break;
|
break;
|
||||||
case LOOP:
|
case LOOP:
|
||||||
|
if (debugMode) break;
|
||||||
auto target = nextLabelIndex - 1;
|
auto target = nextLabelIndex - 1;
|
||||||
if (target < 0 || target >= labels.length || labels.length == 0) {
|
if (target < 0 || target >= labels.length || labels.length == 0) {
|
||||||
lineIndex = lineCount;
|
lineIndex = lineCount;
|
||||||
|
@ -541,11 +560,12 @@ struct Story {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SKIP:
|
case SKIP:
|
||||||
if (stack.length < 1) return Fault.invalid;
|
if (stack.length < 1) return throwOpFault(op);
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!da.isType!StoryNumber) return Fault.invalid;
|
if (!da.isType!StoryNumber) return throwOpFault(op);
|
||||||
auto a = da.get!StoryNumber();
|
auto a = da.get!StoryNumber();
|
||||||
if (a == 0) break;
|
if (a == 0) break;
|
||||||
|
if (debugMode) break;
|
||||||
auto target = nextLabelIndex + (a > 0 ? a - 1 : a);
|
auto target = nextLabelIndex + (a > 0 ? a - 1 : a);
|
||||||
if (target < 0 || target >= labels.length || labels.length == 0) {
|
if (target < 0 || target >= labels.length || labels.length == 0) {
|
||||||
lineIndex = lineCount;
|
lineIndex = lineCount;
|
||||||
|
@ -558,20 +578,21 @@ struct Story {
|
||||||
case JUMP:
|
case JUMP:
|
||||||
// TODO: Write a find function like a normal person.
|
// TODO: Write a find function like a normal person.
|
||||||
// TODO: Might need some error check for -1.
|
// TODO: Might need some error check for -1.
|
||||||
if (stack.length < 1) return Fault.invalid;
|
if (stack.length < 1) return throwOpFault(op);
|
||||||
auto da = stack.pop();
|
auto da = stack.pop();
|
||||||
if (!da.isType!StoryWord) return Fault.invalid;
|
if (!da.isType!StoryWord) return throwOpFault(op);
|
||||||
auto a = da.get!StoryWord();
|
auto a = da.get!StoryWord();
|
||||||
auto isNotThere = true;
|
auto isNotThere = true;
|
||||||
foreach (i, ref label; labels) {
|
foreach (i, ref label; labels) {
|
||||||
if (a == label.name) {
|
if (a == label.name) {
|
||||||
|
isNotThere = false;
|
||||||
|
if (debugMode) break;
|
||||||
lineIndex = label.value.get!StoryNumber();
|
lineIndex = label.value.get!StoryNumber();
|
||||||
nextLabelIndex = cast(StoryNumber) ((i + 1) % (labels.length + 1));
|
nextLabelIndex = cast(StoryNumber) ((i + 1) % (labels.length + 1));
|
||||||
isNotThere = false;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isNotThere) return Fault.invalid;
|
if (isNotThere) return throwOpFault(op);
|
||||||
break;
|
break;
|
||||||
case CALL:
|
case CALL:
|
||||||
println("TODO: ", op);
|
println("TODO: ", op);
|
||||||
|
@ -598,11 +619,13 @@ struct Story {
|
||||||
lineIndex = (lineIndex + 1) % (lineCount + 1);
|
lineIndex = (lineIndex + 1) % (lineCount + 1);
|
||||||
while (lineIndex < lineCount && !hasPause && !hasMenu && !hasText) {
|
while (lineIndex < lineCount && !hasPause && !hasMenu && !hasText) {
|
||||||
auto line = opIndex(lineIndex);
|
auto line = opIndex(lineIndex);
|
||||||
if (line[0] == StoryLine.expression) {
|
if (line.length) {
|
||||||
auto fault = execute(line[1 .. $].trimStart());
|
if (line[0] == StoryLineKind.expression) {
|
||||||
if (fault) return fault;
|
auto fault = execute(line[1 .. $].trimStart());
|
||||||
} else if (line[0] == StoryLine.label) {
|
if (fault) return fault;
|
||||||
nextLabelIndex = cast(StoryNumber) ((nextLabelIndex + 1) % (labels.length + 1));
|
} else if (line[0] == StoryLineKind.label) {
|
||||||
|
nextLabelIndex = cast(StoryNumber) ((nextLabelIndex + 1) % (labels.length + 1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lineIndex = (lineIndex + 1) % (lineCount + 1);
|
lineIndex = (lineIndex + 1) % (lineCount + 1);
|
||||||
}
|
}
|
||||||
|
@ -643,15 +666,16 @@ bool isMaybeStoryWord(IStr value) {
|
||||||
return c == '_' || (!c.isUpper && !c.isSymbol);
|
return c == '_' || (!c.isUpper && !c.isSymbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result!StoryLine toStoryLine(char value) {
|
Result!StoryLineKind toStoryLineKind(char value) {
|
||||||
switch (value) with (StoryLine) {
|
switch (value) with (StoryLineKind) {
|
||||||
case '#': return Result!StoryLine(comment);
|
case ' ': return Result!StoryLineKind(empty);
|
||||||
case '*': return Result!StoryLine(label);
|
case '#': return Result!StoryLineKind(comment);
|
||||||
case '|': return Result!StoryLine(text);
|
case '*': return Result!StoryLineKind(label);
|
||||||
case '.': return Result!StoryLine(pause);
|
case '|': return Result!StoryLineKind(text);
|
||||||
case '^': return Result!StoryLine(menu);
|
case '.': return Result!StoryLineKind(pause);
|
||||||
case '$': return Result!StoryLine(expression);
|
case '^': return Result!StoryLineKind(menu);
|
||||||
default: return Result!StoryLine(Fault.invalid);
|
case '$': return Result!StoryLineKind(expression);
|
||||||
|
default: return Result!StoryLineKind(Fault.invalid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue