add support for named arguments for closures

This commit is contained in:
LenAnderson
2024-03-29 10:04:25 -04:00
parent 65edba98a6
commit 59646ea4f9
2 changed files with 81 additions and 6 deletions

View File

@ -6,6 +6,10 @@ import { SlashCommandScope } from './SlashCommandScope.js';
export class SlashCommandClosure { export class SlashCommandClosure {
/**@type {SlashCommandScope}*/ scope; /**@type {SlashCommandScope}*/ scope;
/**@type {Boolean}*/ executeNow = false; /**@type {Boolean}*/ executeNow = false;
// @ts-ignore
/**@type {Map<string,string|SlashCommandClosure>}*/ arguments = {};
// @ts-ignore
/**@type {Map<string,string|SlashCommandClosure>}*/ providedArguments = {};
/**@type {SlashCommandExecutor[]}*/ executorList = []; /**@type {SlashCommandExecutor[]}*/ executorList = [];
/**@type {String}*/ keptText; /**@type {String}*/ keptText;
@ -28,6 +32,8 @@ export class SlashCommandClosure {
const closure = new SlashCommandClosure(); const closure = new SlashCommandClosure();
closure.scope = this.scope.getCopy(); closure.scope = this.scope.getCopy();
closure.executeNow = this.executeNow; closure.executeNow = this.executeNow;
closure.arguments = this.arguments;
closure.providedArguments = this.providedArguments;
closure.executorList = this.executorList; closure.executorList = this.executorList;
closure.keptText = this.keptText; closure.keptText = this.keptText;
return closure; return closure;
@ -41,6 +47,56 @@ export class SlashCommandClosure {
async executeDirect() { async executeDirect() {
let interrupt = false; let interrupt = false;
// closure arguments
for (const key of Object.keys(this.arguments)) {
let v = this.arguments[key];
if (v instanceof SlashCommandClosure) {
/**@type {SlashCommandClosure}*/
const closure = v;
closure.scope.parent = this.scope;
if (closure.executeNow) {
v = (await closure.execute())?.pipe;
} else {
v = closure;
}
} else {
v = this.substituteParams(v);
}
// unescape value
if (typeof v == 'string') {
v = v
?.replace(/\\\|/g, '|')
?.replace(/\\\{/g, '{')
?.replace(/\\\}/g, '}')
;
}
this.scope.letVariable(key, v);
}
for (const key of Object.keys(this.providedArguments)) {
let v = this.providedArguments[key];
if (v instanceof SlashCommandClosure) {
/**@type {SlashCommandClosure}*/
const closure = v;
closure.scope.parent = this.scope;
if (closure.executeNow) {
v = (await closure.execute())?.pipe;
} else {
v = closure;
}
} else {
v = this.substituteParams(v);
}
// unescape value
if (typeof v == 'string') {
v = v
?.replace(/\\\|/g, '|')
?.replace(/\\\{/g, '{')
?.replace(/\\\}/g, '}')
;
}
this.scope.setVariable(key, v);
}
for (const executor of this.executorList) { for (const executor of this.executorList) {
interrupt = executor.command.interruptsGeneration; interrupt = executor.command.interruptsGeneration;
let args = { let args = {

View File

@ -95,6 +95,9 @@ export class SlashCommandParser {
if (keep) this.keptText += content; if (keep) this.keptText += content;
return content; return content;
} }
discardWhitespace() {
while (/\s/.test(this.char)) this.take(); // discard whitespace
}
parse(text, verifyCommandNames = true) { parse(text, verifyCommandNames = true) {
@ -121,7 +124,12 @@ export class SlashCommandParser {
this.take(2); // discard opening {: this.take(2); // discard opening {:
let closure = new SlashCommandClosure(this.scope); let closure = new SlashCommandClosure(this.scope);
this.scope = closure.scope; this.scope = closure.scope;
while (/\s/.test(this.char)) this.take(); // discard whitespace this.discardWhitespace();
while (this.testNamedArgument()) {
const arg = this.parseNamedArgument();
closure.arguments[arg.key] = arg.value;
this.discardWhitespace();
}
while (!this.testClosureEnd()) { while (!this.testClosureEnd()) {
if (this.testCommand()) { if (this.testCommand()) {
closure.executorList.push(this.parseCommand()); closure.executorList.push(this.parseCommand());
@ -131,9 +139,20 @@ export class SlashCommandParser {
while (/\s|\|/.test(this.char)) this.take(); // discard whitespace and pipe (command separator) while (/\s|\|/.test(this.char)) this.take(); // discard whitespace and pipe (command separator)
} }
this.take(2); // discard closing :} this.take(2); // discard closing :}
if (this.char == '(' && this.ahead[0] == ')') { this.discardWhitespace();
this.take(2); // discard () if (this.char == '(') {
this.take(); // discard opening (
closure.executeNow = true; closure.executeNow = true;
this.discardWhitespace();
while (this.testNamedArgument()) {
const arg = this.parseNamedArgument();
if (!Object.keys(closure.arguments).includes(arg.key)) throw new SlashCommandParserError(`Invalid named argument for closure "${arg.key}" at position ${this.index - 2}`, this.text, this.index);
closure.providedArguments[arg.key] = arg.value;
this.discardWhitespace();
}
// @ts-ignore
if (this.char != ')') throw new SlashCommandParserError(`Missing closing ")" at position ${this.index - 2}.`, this.text, this.index);
this.take(); // discard closing )
} }
while (/\s/.test(this.char)) this.take(); // discard trailing whitespace while (/\s/.test(this.char)) this.take(); // discard trailing whitespace
this.scope = closure.scope.parent; this.scope = closure.scope.parent;
@ -152,15 +171,15 @@ export class SlashCommandParser {
this.take(); // discard "/" this.take(); // discard "/"
this.commandIndex.push(cmd); this.commandIndex.push(cmd);
while (!/\s/.test(this.char) && !this.testCommandEnd()) cmd.name += this.take(); // take chars until whitespace or end while (!/\s/.test(this.char) && !this.testCommandEnd()) cmd.name += this.take(); // take chars until whitespace or end
while (/\s/.test(this.char)) this.take(); // discard whitespace this.discardWhitespace();
if (this.verifyCommandNames && !this.commands[cmd.name]) throw new SlashCommandParserError(`Unknown command at position ${this.index - cmd.name.length - 2}: "/${cmd.name}"`, this.text, this.index - cmd.name.length); if (this.verifyCommandNames && !this.commands[cmd.name]) throw new SlashCommandParserError(`Unknown command at position ${this.index - cmd.name.length - 2}: "/${cmd.name}"`, this.text, this.index - cmd.name.length);
cmd.command = this.commands[cmd.name]; cmd.command = this.commands[cmd.name];
while (this.testNamedArgument()) { while (this.testNamedArgument()) {
const arg = this.parseNamedArgument(); const arg = this.parseNamedArgument();
cmd.args[arg.key] = arg.value; cmd.args[arg.key] = arg.value;
while (/\s/.test(this.char)) this.take(); // discard whitespace this.discardWhitespace();
} }
while (/\s/.test(this.char)) this.take(); // discard whitespace this.discardWhitespace();
if (this.testUnnamedArgument()) { if (this.testUnnamedArgument()) {
cmd.value = this.parseUnnamedArgument(); cmd.value = this.parseUnnamedArgument();
} }