mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
add callable closure vars
This commit is contained in:
@ -1,4 +1,5 @@
|
|||||||
import { substituteParams } from '../../script.js';
|
import { substituteParams } from '../../script.js';
|
||||||
|
import { SlashCommandClosureExecutor } from './SlashCommandClosureExecutor.js';
|
||||||
import { SlashCommandClosureResult } from './SlashCommandClosureResult.js';
|
import { SlashCommandClosureResult } from './SlashCommandClosureResult.js';
|
||||||
import { SlashCommandExecutor } from './SlashCommandExecutor.js';
|
import { SlashCommandExecutor } from './SlashCommandExecutor.js';
|
||||||
import { SlashCommandScope } from './SlashCommandScope.js';
|
import { SlashCommandScope } from './SlashCommandScope.js';
|
||||||
@ -10,7 +11,7 @@ export class SlashCommandClosure {
|
|||||||
/**@type {Map<string,string|SlashCommandClosure>}*/ arguments = {};
|
/**@type {Map<string,string|SlashCommandClosure>}*/ arguments = {};
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
/**@type {Map<string,string|SlashCommandClosure>}*/ providedArguments = {};
|
/**@type {Map<string,string|SlashCommandClosure>}*/ providedArguments = {};
|
||||||
/**@type {SlashCommandExecutor[]}*/ executorList = [];
|
/**@type {(SlashCommandExecutor|SlashCommandClosureExecutor)[]}*/ executorList = [];
|
||||||
/**@type {String}*/ keptText;
|
/**@type {String}*/ keptText;
|
||||||
|
|
||||||
constructor(parent) {
|
constructor(parent) {
|
||||||
@ -39,6 +40,10 @@ export class SlashCommandClosure {
|
|||||||
return closure;
|
return closure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns Promise<SlashCommandClosureResult>
|
||||||
|
*/
|
||||||
async execute() {
|
async execute() {
|
||||||
const closure = this.getCopy();
|
const closure = this.getCopy();
|
||||||
return await closure.executeDirect();
|
return await closure.executeDirect();
|
||||||
@ -98,82 +103,94 @@ export class SlashCommandClosure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const executor of this.executorList) {
|
for (const executor of this.executorList) {
|
||||||
interrupt = executor.command.interruptsGeneration;
|
if (executor instanceof SlashCommandClosureExecutor) {
|
||||||
let args = {
|
const closure = this.scope.getVariable(executor.name);
|
||||||
_scope: this.scope,
|
if (!closure || !(closure instanceof SlashCommandClosure)) throw new Error(`${name} is not a closure.`);
|
||||||
};
|
closure.scope.parent = this.scope;
|
||||||
let value;
|
closure.providedArguments = executor.providedArguments;
|
||||||
// substitute named arguments
|
const result = await closure.execute();
|
||||||
for (const key of Object.keys(executor.args)) {
|
this.scope.pipe = result.pipe;
|
||||||
if (executor.args[key] instanceof SlashCommandClosure) {
|
interrupt = result.interrupt;
|
||||||
|
} else {
|
||||||
|
interrupt = executor.command.interruptsGeneration;
|
||||||
|
let args = {
|
||||||
|
_scope: this.scope,
|
||||||
|
};
|
||||||
|
let value;
|
||||||
|
// substitute named arguments
|
||||||
|
for (const key of Object.keys(executor.args)) {
|
||||||
|
if (executor.args[key] instanceof SlashCommandClosure) {
|
||||||
|
/**@type {SlashCommandClosure}*/
|
||||||
|
const closure = executor.args[key];
|
||||||
|
closure.scope.parent = this.scope;
|
||||||
|
if (closure.executeNow) {
|
||||||
|
args[key] = (await closure.execute())?.pipe;
|
||||||
|
} else {
|
||||||
|
args[key] = closure;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args[key] = this.substituteParams(executor.args[key]);
|
||||||
|
}
|
||||||
|
// unescape named argument
|
||||||
|
if (typeof args[key] == 'string') {
|
||||||
|
args[key] = args[key]
|
||||||
|
?.replace(/\\\|/g, '|')
|
||||||
|
?.replace(/\\\{/g, '{')
|
||||||
|
?.replace(/\\\}/g, '}')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// substitute unnamed argument
|
||||||
|
if (executor.value === undefined) {
|
||||||
|
value = this.scope.pipe;
|
||||||
|
} else if (executor.value instanceof SlashCommandClosure) {
|
||||||
/**@type {SlashCommandClosure}*/
|
/**@type {SlashCommandClosure}*/
|
||||||
const closure = executor.args[key];
|
const closure = executor.value;
|
||||||
closure.scope.parent = this.scope;
|
closure.scope.parent = this.scope;
|
||||||
if (closure.executeNow) {
|
if (closure.executeNow) {
|
||||||
args[key] = (await closure.execute())?.pipe;
|
value = (await closure.execute())?.pipe;
|
||||||
} else {
|
} else {
|
||||||
args[key] = closure;
|
value = closure;
|
||||||
|
}
|
||||||
|
} else if (Array.isArray(executor.value)) {
|
||||||
|
value = [];
|
||||||
|
for (let i = 0; i < executor.value.length; i++) {
|
||||||
|
let v = executor.value[i];
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
value[i] = v;
|
||||||
|
}
|
||||||
|
if (!value.find(it=>it instanceof SlashCommandClosure)) {
|
||||||
|
value = value.join(' ');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
args[key] = this.substituteParams(executor.args[key]);
|
value = this.substituteParams(executor.value);
|
||||||
}
|
}
|
||||||
// unescape named argument
|
// unescape unnamed argument
|
||||||
if (typeof args[key] == 'string') {
|
if (typeof value == 'string') {
|
||||||
args[key] = args[key]
|
value = value
|
||||||
?.replace(/\\\|/g, '|')
|
?.replace(/\\\|/g, '|')
|
||||||
?.replace(/\\\{/g, '{')
|
?.replace(/\\\{/g, '{')
|
||||||
?.replace(/\\\}/g, '}')
|
?.replace(/\\\}/g, '}')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// substitute unnamed argument
|
this.scope.pipe = await executor.command.callback(args, value);
|
||||||
if (executor.value === undefined) {
|
|
||||||
value = this.scope.pipe;
|
|
||||||
} else if (executor.value instanceof SlashCommandClosure) {
|
|
||||||
/**@type {SlashCommandClosure}*/
|
|
||||||
const closure = executor.value;
|
|
||||||
closure.scope.parent = this.scope;
|
|
||||||
if (closure.executeNow) {
|
|
||||||
value = (await closure.execute())?.pipe;
|
|
||||||
} else {
|
|
||||||
value = closure;
|
|
||||||
}
|
|
||||||
} else if (Array.isArray(executor.value)) {
|
|
||||||
value = [];
|
|
||||||
for (let i = 0; i < executor.value.length; i++) {
|
|
||||||
let v = executor.value[i];
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
value[i] = v;
|
|
||||||
}
|
|
||||||
if (!value.find(it=>it instanceof SlashCommandClosure)) {
|
|
||||||
value = value.join(' ');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = this.substituteParams(executor.value);
|
|
||||||
}
|
}
|
||||||
// unescape unnamed argument
|
|
||||||
if (typeof value == 'string') {
|
|
||||||
value = value
|
|
||||||
?.replace(/\\\|/g, '|')
|
|
||||||
?.replace(/\\\{/g, '{')
|
|
||||||
?.replace(/\\\}/g, '}')
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.scope.pipe = await executor.command.callback(args, value);
|
|
||||||
}
|
}
|
||||||
return Object.assign(new SlashCommandClosureResult(), { interrupt, newText: this.keptText, pipe: this.scope.pipe });
|
/**@type {SlashCommandClosureResult} */
|
||||||
|
const result = Object.assign(new SlashCommandClosureResult(), { interrupt, newText: this.keptText, pipe: this.scope.pipe });
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
export class SlashCommandClosureExecutor {
|
||||||
|
/**@type {String}*/ name = '';
|
||||||
|
// @ts-ignore
|
||||||
|
/**@type {Map<string,string|SlashCommandClosure>}*/ providedArguments = {};
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import { SlashCommand } from './SlashCommand.js';
|
import { SlashCommand } from './SlashCommand.js';
|
||||||
import { SlashCommandClosure } from './SlashCommandClosure.js';
|
import { SlashCommandClosure } from './SlashCommandClosure.js';
|
||||||
|
import { SlashCommandClosureExecutor } from './SlashCommandClosureExecutor.js';
|
||||||
import { SlashCommandExecutor } from './SlashCommandExecutor.js';
|
import { SlashCommandExecutor } from './SlashCommandExecutor.js';
|
||||||
import { SlashCommandParserError } from './SlashCommandParserError.js';
|
import { SlashCommandParserError } from './SlashCommandParserError.js';
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
@ -232,6 +233,9 @@ export class SlashCommandParser {
|
|||||||
this.discardWhitespace();
|
this.discardWhitespace();
|
||||||
}
|
}
|
||||||
while (!this.testClosureEnd()) {
|
while (!this.testClosureEnd()) {
|
||||||
|
if (this.testClosureCall()) {
|
||||||
|
closure.executorList.push(this.parseClosureCall());
|
||||||
|
}
|
||||||
if (this.testCommand()) {
|
if (this.testCommand()) {
|
||||||
closure.executorList.push(this.parseCommand());
|
closure.executorList.push(this.parseCommand());
|
||||||
} else {
|
} else {
|
||||||
@ -260,6 +264,37 @@ export class SlashCommandParser {
|
|||||||
return closure;
|
return closure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testClosureCall() {
|
||||||
|
return this.char == '/'
|
||||||
|
&& this.behind.slice(-1) != '\\'
|
||||||
|
&& !['/', '#'].includes(this.ahead[0])
|
||||||
|
&& /^\S+\(/.test(this.ahead)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
testClosureCallEnd() {
|
||||||
|
return this.char == ')' && this.behind.slice(-1) != '\\';
|
||||||
|
}
|
||||||
|
parseClosureCall() {
|
||||||
|
this.take(); // discard /
|
||||||
|
let name = '';
|
||||||
|
while (!/\s|\(/.test(this.char)) name += this.take(); // take chars until opening (
|
||||||
|
this.take(); // discard opening (
|
||||||
|
const executor = new SlashCommandClosureExecutor();
|
||||||
|
executor.name = name;
|
||||||
|
this.discardWhitespace();
|
||||||
|
while (this.testNamedArgument()) {
|
||||||
|
const arg = this.parseNamedArgument();
|
||||||
|
executor.providedArguments[arg.key] = arg.value;
|
||||||
|
this.discardWhitespace();
|
||||||
|
}
|
||||||
|
if (this.testClosureCallEnd()) {
|
||||||
|
this.take(); // discard closing )
|
||||||
|
return executor;
|
||||||
|
} else {
|
||||||
|
throw new SlashCommandParserError(`Unexpected end of closure call at position ${this.index - 2}`, this.text, this.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
testCommand() {
|
testCommand() {
|
||||||
return this.char == '/' && this.behind.slice(-1) != '\\' && !['/', '#'].includes(this.ahead[0]);
|
return this.char == '/' && this.behind.slice(-1) != '\\' && !['/', '#'].includes(this.ahead[0]);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user