Skip to content

Commit

Permalink
feat(conditions): new elseif conditions feature (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
liana-p committed Jun 10, 2023
1 parent 9f78b5f commit 1704e14
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 42 deletions.
4 changes: 3 additions & 1 deletion docs/commands/if-function.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ The `if` command is the main method of doing conditions in narrat. It can be use
```
if [condition]:
"This code is run on condition success"
elseif [condition]:
"This code is run on condition success"
else:
"This code is run on condition failure"
```

::: tip
`else` is optional
you can have 0 or any amount of `elseif`, and `else` is optional
:::

The condition should be a boolean. You can directly pass a value (`if $data.someValue` ), or an expression that returns a boolean (for example `if (> $data.player.age 18)`
Expand Down
9 changes: 9 additions & 0 deletions packages/narrat/examples/games/default/scripts/default.nar
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ main:
// if (== $global.counter 2):
// unlock_achievement win_game
// talk player idle "Global counter is %{$global.counter}"
set data.n 5
if (== $data.n 1):
"Test 1"
elseif (== $data.n 2):
"test 2"
elseif (== $data.n 5):
"test 5"
else:
"test else"
jump quest_demo

achievements_demo:
Expand Down
74 changes: 64 additions & 10 deletions packages/narrat/src/vm/commands/if.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,33 @@ import { getLine, runConditionCommand } from '@/vm/vm-helpers';
import { CommandPlugin, generateParser } from './command-plugin';
import { Parser } from '@/types/parser';
import { MachineBlock, useVM } from '@/stores/vm-store';
import { parseExpression } from '../vm-parser';
import { runExpression } from '../vm';

export interface IfOptions {
condition: boolean;
}
export interface ElseIfOptions {
branch: Parser.Branch;
condition: Parser.ParsedExpression;
}
export interface IfStaticOptions {
success: Parser.Branch;
elseifs: ElseIfOptions[];
failure?: Parser.Branch;
}

export const ifCommand = new CommandPlugin<IfOptions, IfStaticOptions>(
'if',
[{ name: 'condition', type: 'boolean' }],
async (cmd) => {
const newBranch = runConditionCommand(cmd);
const elseIfResults: boolean[] = [];
for (const elseif of cmd.staticOptions.elseifs) {
const condition = elseif.condition;
const finalCondition = await runExpression(condition);
elseIfResults.push(finalCondition);
}
const newBranch = runConditionCommand(cmd, elseIfResults);
const vmStore = useVM();
if (newBranch) {
const newBlock: MachineBlock = {
Expand All @@ -36,24 +49,65 @@ export const ifCommand = new CommandPlugin<IfOptions, IfStaticOptions>(
const { lines, currentLine, line } = ctx;
const command = parsed.command;
let failure: Parser.Branch | undefined;
const nextLine = getLine(lines, currentLine + 1);
if (nextLine && nextLine.code === 'else:') {
failure = ctx.processCommandsFunction(
ctx.parserContext,
nextLine.branch!,
line,
);
newLine++;
let lineToTest = currentLine;
let foundOtherThing = false;
const elseifs: ElseIfOptions[] = [];
while (!foundOtherThing) {
lineToTest++;
const nextLine = getLine(lines, lineToTest);
if (nextLine && nextLine.code.startsWith('elseif')) {
const expression = nextLine.expression;
console.log(nextLine);
if (!Array.isArray(expression)) {
ctx.parserContext.error(
nextLine.line,
'Expected an expression after elseif',
);
foundOtherThing = true;
break;
}
if (expression.length > 2) {
ctx.parserContext.error(
nextLine.line,
'Expected only one argument after elseif',
);
foundOtherThing = true;
break;
}
elseifs.push({
branch: ctx.processCommandsFunction(
ctx.parserContext,
nextLine.branch!,
line,
),
condition: parseExpression(
ctx.parserContext,
nextLine,
expression[1] as Parser.Expression,
),
});
} else if (nextLine && nextLine.code === 'else:') {
failure = ctx.processCommandsFunction(
ctx.parserContext,
nextLine.branch!,
line,
);
} else {
foundOtherThing = true;
lineToTest--;
}
}
command.staticOptions = {
success: ctx.processCommandsFunction(
ctx.parserContext,
line.branch!,
line,
),
elseifs,
failure,
};
newLine++;
lineToTest++;
newLine = lineToTest;
return {
newLine,
};
Expand Down
8 changes: 8 additions & 0 deletions packages/narrat/src/vm/vm-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,21 @@ export function runSkillCheck(params: SkillCheckParams): SkillCheckState {

export function runConditionCommand(
command: Parser.Command<IfOptions, IfStaticOptions>,
elseIfResults: boolean[] = [],
): Parser.Branch | undefined {
const options = command.options;
const staticOptions = command.staticOptions;
const result = !!options.condition;
if (result) {
return staticOptions.success;
}
if (!result) {
for (const [index, elseIf] of staticOptions.elseifs.entries()) {
if (elseIfResults[index] === true) {
return elseIf.branch;
}
}
}
if (!result && staticOptions.failure) {
return staticOptions.failure;
}
Expand Down
18 changes: 9 additions & 9 deletions packages/narrat/src/vm/vm-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,6 @@ function processCommands(
parentLine: Parser.Line | undefined,
): Parser.Branch {
const startLine = ctx.currentLine;
const branchContext: CommandParsingContext = {
processCommandsFunction: processCommands,
parserContext: ctx,
lines,
currentLine: 0,
line: lines[0],
};
const branch: Parser.Branch = [];
if (!lines) {
let lineNumber = 0;
if (parentLine) {
Expand All @@ -91,6 +83,14 @@ function processCommands(
);
return [];
}
const branchContext: CommandParsingContext = {
processCommandsFunction: processCommands,
parserContext: ctx,
lines,
currentLine: 0,
line: lines[0],
};
const branch: Parser.Branch = [];
while (branchContext.currentLine < lines.length) {
const line = lines[branchContext.currentLine];
branchContext.line = line;
Expand All @@ -110,7 +110,7 @@ function processCommands(
return branch;
}

function parseExpression(
export function parseExpression(
ctx: ParserContext,
line: Parser.Line,
expression: Parser.Expression,
Expand Down
34 changes: 12 additions & 22 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1704e14

Please sign in to comment.