From e25e35b16eb035c81dec60fa805fc62e252aeebd Mon Sep 17 00:00:00 2001 From: uzmoi <63585970+uzmoi@users.noreply.github.com> Date: Wed, 14 Jan 2026 20:38:43 +0900 Subject: [PATCH 1/4] =?UTF-8?q?std=E3=82=92=E5=88=86=E9=9B=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interpreter/index.ts | 17 +++++++---------- test/interpreter.ts | 2 ++ test/testutils.ts | 5 +++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/interpreter/index.ts b/src/interpreter/index.ts index 1ea3d16c..edf245c4 100644 --- a/src/interpreter/index.ts +++ b/src/interpreter/index.ts @@ -7,7 +7,6 @@ import { AiScriptError, NonAiScriptError, AiScriptNamespaceError, AiScriptIndexO import * as Ast from '../node.js'; import { nodeToJs } from '../utils/node-to-js.js'; import { Scope } from './scope.js'; -import { std } from './lib/std.js'; import { RETURN, unWrapRet, BREAK, CONTINUE, assertValue, isControl, type Control, unWrapLabeledBreak } from './control.js'; import { assertNumber, assertString, assertFunction, assertBoolean, assertObject, assertArray, eq, isObject, isArray, expectAny, reprValue, isFunction } from './util.js'; import { NULL, FN_NATIVE, BOOL, NUM, STR, ARR, OBJ, FN, ERROR } from './value.js'; @@ -36,12 +35,11 @@ export class Interpreter { private abortHandlers: (() => void)[] = []; private pauseHandlers: (() => void)[] = []; private unpauseHandlers: (() => void)[] = []; - private vars: Record = {}; private irqRate: number; private irqSleep: () => Promise; constructor( - consts: Record, + globals: Record, private opts: { in?(q: string): Promise; out?(value: Value): void; @@ -67,13 +65,12 @@ export class Interpreter { }), }; - this.vars = Object.fromEntries(Object.entries({ - ...consts, - ...std, - ...io, - }).map(([k, v]) => [k, Variable.const(v)])); - - this.scope = new Scope([new Map(Object.entries(this.vars))]); + this.scope = new Scope([ + new Map( + Object.entries({ ...globals, ...io }) + .map(([k, v]) => [k, Variable.const(v)]), + ), + ]); this.scope.opts.log = (type, params): void => { switch (type) { case 'add': this.log('var:add', params); break; diff --git a/test/interpreter.ts b/test/interpreter.ts index 0b8bc0ea..b295db0f 100644 --- a/test/interpreter.ts +++ b/test/interpreter.ts @@ -2,6 +2,7 @@ import * as assert from 'assert'; import { describe, expect, test, vi, beforeEach, afterEach } from 'vitest'; import { Parser, Interpreter, values, errors, utils, Ast } from '../src'; import { FALSE, NUM, OBJ, STR, TRUE, Value } from '../src/interpreter/value'; +import { std } from '../src/interpreter/lib/std'; let { FN_NATIVE } = values; let { AiScriptRuntimeError, AiScriptIndexOutOfRangeError, AiScriptHostsideError } = errors; @@ -261,6 +262,7 @@ describe('pause', () => { let count = 0; const interpreter = new Interpreter({ + ...std, count: values.FN_NATIVE(() => { count++; }), }, {}); diff --git a/test/testutils.ts b/test/testutils.ts index 0b09ac11..0a45c76d 100644 --- a/test/testutils.ts +++ b/test/testutils.ts @@ -1,11 +1,12 @@ import { expect as globalExpect } from 'vitest'; import { Parser, Interpreter } from '../src'; import { Value } from '../src/interpreter/value'; +import { std } from '../src/interpreter/lib/std'; export async function exe(script: string): Promise { const parser = new Parser(); let result = undefined; - const interpreter = new Interpreter({}, { + const interpreter = new Interpreter(std, { out(value) { if (!result) result = value; else if (!Array.isArray(result)) result = [result, value]; @@ -23,7 +24,7 @@ export async function exe(script: string): Promise { export function exeSync(script: string): Value | undefined { const parser = new Parser(); - const interpreter = new Interpreter({}, { + const interpreter = new Interpreter(std, { out(value) { }, log(type, {val}) { From bec8a2f1f2015615c4da11f5edc125ba066b03bd Mon Sep 17 00:00:00 2001 From: uzmoi <63585970+uzmoi@users.noreply.github.com> Date: Wed, 14 Jan 2026 21:02:22 +0900 Subject: [PATCH 2/4] =?UTF-8?q?std=E3=82=92=E3=82=A8=E3=82=AF=E3=82=B9?= =?UTF-8?q?=E3=83=9D=E3=83=BC=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- etc/aiscript.api.md | 5 ++++- src/index.ts | 4 ++-- src/interpreter/index.ts | 2 ++ test/interpreter.ts | 3 +-- test/testutils.ts | 3 +-- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/etc/aiscript.api.md b/etc/aiscript.api.md index bebd9ef7..2ef9823e 100644 --- a/etc/aiscript.api.md +++ b/etc/aiscript.api.md @@ -416,7 +416,7 @@ type Index = NodeBase & { // @public (undocumented) export class Interpreter { - constructor(consts: Record, opts?: { + constructor(globals: Record, opts?: { in?(q: string): Promise; out?(value: Value): void; err?(e: AiScriptError): void; @@ -713,6 +713,9 @@ export class Scope { // @public (undocumented) type Statement = Definition | Return | Each | For | Loop | Break | Continue | Assign | AddAssign | SubAssign; +// @public (undocumented) +export const std: Record; + // @public (undocumented) const STR: (str: VStr["value"]) => VStr; diff --git a/src/index.ts b/src/index.ts index ee600c18..0bdc3994 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ //export * from './interpreter/index'; //export * as utils from './interpreter/util'; //export * as values from './interpreter/value'; -import { Interpreter } from './interpreter/index.js'; +import { Interpreter, std } from './interpreter/index.js'; import { Scope } from './interpreter/scope.js'; import * as utils from './interpreter/util.js'; import * as values from './interpreter/value.js'; @@ -12,7 +12,7 @@ import * as errors from './error.js'; import * as Ast from './node.js'; import { AISCRIPT_VERSION } from './constants.js'; import type { ParserPlugin, PluginType } from './parser/index.js'; -export { Interpreter }; +export { Interpreter, std }; export { Scope }; export { utils }; export { values }; diff --git a/src/interpreter/index.ts b/src/interpreter/index.ts index edf245c4..d29c7e40 100644 --- a/src/interpreter/index.ts +++ b/src/interpreter/index.ts @@ -16,6 +16,8 @@ import { Reference } from './reference.js'; import type { JsValue } from './util.js'; import type { Value, VFn, VUserFn } from './value.js'; +export { std } from './lib/std.js'; + export type LogObject = { scope?: string; var?: string; diff --git a/test/interpreter.ts b/test/interpreter.ts index b295db0f..1b1a98e8 100644 --- a/test/interpreter.ts +++ b/test/interpreter.ts @@ -1,8 +1,7 @@ import * as assert from 'assert'; import { describe, expect, test, vi, beforeEach, afterEach } from 'vitest'; -import { Parser, Interpreter, values, errors, utils, Ast } from '../src'; +import { Parser, Interpreter, std, values, errors, utils, Ast } from '../src'; import { FALSE, NUM, OBJ, STR, TRUE, Value } from '../src/interpreter/value'; -import { std } from '../src/interpreter/lib/std'; let { FN_NATIVE } = values; let { AiScriptRuntimeError, AiScriptIndexOutOfRangeError, AiScriptHostsideError } = errors; diff --git a/test/testutils.ts b/test/testutils.ts index 0a45c76d..feedd52c 100644 --- a/test/testutils.ts +++ b/test/testutils.ts @@ -1,7 +1,6 @@ import { expect as globalExpect } from 'vitest'; -import { Parser, Interpreter } from '../src'; +import { Parser, Interpreter, std } from '../src'; import { Value } from '../src/interpreter/value'; -import { std } from '../src/interpreter/lib/std'; export async function exe(script: string): Promise { const parser = new Parser(); From 87cb57753f8bbfe3636ab499c86e584392ff5ed3 Mon Sep 17 00:00:00 2001 From: uzmoi <63585970+uzmoi@users.noreply.github.com> Date: Sun, 18 Jan 2026 05:53:58 +0900 Subject: [PATCH 3/4] =?UTF-8?q?Core=E3=82=92std=E3=81=8B=E3=82=89=E5=88=86?= =?UTF-8?q?=E9=9B=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interpreter/index.ts | 3 +- src/interpreter/lib/core.ts | 138 +++++++++++++++++++++++++++++++++++ src/interpreter/lib/std.ts | 142 ++---------------------------------- 3 files changed, 145 insertions(+), 138 deletions(-) create mode 100644 src/interpreter/lib/core.ts diff --git a/src/interpreter/index.ts b/src/interpreter/index.ts index d29c7e40..b560cbc3 100644 --- a/src/interpreter/index.ts +++ b/src/interpreter/index.ts @@ -13,6 +13,7 @@ import { NULL, FN_NATIVE, BOOL, NUM, STR, ARR, OBJ, FN, ERROR } from './value.js import { getPrimProp } from './primitive-props.js'; import { Variable } from './variable.js'; import { Reference } from './reference.js'; +import { stdCore } from './lib/core.js'; import type { JsValue } from './util.js'; import type { Value, VFn, VUserFn } from './value.js'; @@ -69,7 +70,7 @@ export class Interpreter { this.scope = new Scope([ new Map( - Object.entries({ ...globals, ...io }) + Object.entries({ ...globals,...stdCore, ...io }) .map(([k, v]) => [k, Variable.const(v)]), ), ]); diff --git a/src/interpreter/lib/core.ts b/src/interpreter/lib/core.ts new file mode 100644 index 00000000..fa21c0bd --- /dev/null +++ b/src/interpreter/lib/core.ts @@ -0,0 +1,138 @@ +import { NUM, STR, FN_NATIVE, FALSE, TRUE, ARR, NULL } from '../value.js'; +import { assertNumber, assertString, assertBoolean, eq, expectAny, reprValue } from '../util.js'; +import { AiScriptUserError } from '../../error.js'; +import { AISCRIPT_VERSION } from '../../constants.js'; +import type { Value } from '../value.js'; + +export const stdCore: Record<`Core:${string}`, Value> = { + 'Core:v': STR(AISCRIPT_VERSION), + + 'Core:ai': STR('kawaii'), + + 'Core:not': FN_NATIVE(([a]) => { + assertBoolean(a); + return a.value ? FALSE : TRUE; + }), + + 'Core:eq': FN_NATIVE(([a, b]) => { + expectAny(a); + expectAny(b); + return eq(a, b) ? TRUE : FALSE; + }), + + 'Core:neq': FN_NATIVE(([a, b]) => { + expectAny(a); + expectAny(b); + return eq(a, b) ? FALSE : TRUE; + }), + + 'Core:and': FN_NATIVE(([a, b]) => { + assertBoolean(a); + if (!a.value) return FALSE; + assertBoolean(b); + return b.value ? TRUE : FALSE; + }), + + 'Core:or': FN_NATIVE(([a, b]) => { + assertBoolean(a); + if (a.value) return TRUE; + assertBoolean(b); + return b.value ? TRUE : FALSE; + }), + + 'Core:add': FN_NATIVE(([a, b]) => { + assertNumber(a); + assertNumber(b); + return NUM(a.value + b.value); + }), + + 'Core:sub': FN_NATIVE(([a, b]) => { + assertNumber(a); + assertNumber(b); + return NUM(a.value - b.value); + }), + + 'Core:mul': FN_NATIVE(([a, b]) => { + assertNumber(a); + assertNumber(b); + return NUM(a.value * b.value); + }), + + 'Core:pow': FN_NATIVE(([a, b]) => { + assertNumber(a); + assertNumber(b); + const res = a.value ** b.value; + return NUM(res); + }), + + 'Core:div': FN_NATIVE(([a, b]) => { + assertNumber(a); + assertNumber(b); + const res = a.value / b.value; + return NUM(res); + }), + + 'Core:mod': FN_NATIVE(([a, b]) => { + assertNumber(a); + assertNumber(b); + return NUM(a.value % b.value); + }), + + 'Core:gt': FN_NATIVE(([a, b]) => { + assertNumber(a); + assertNumber(b); + return a.value > b.value ? TRUE : FALSE; + }), + + 'Core:lt': FN_NATIVE(([a, b]) => { + assertNumber(a); + assertNumber(b); + return a.value < b.value ? TRUE : FALSE; + }), + + 'Core:gteq': FN_NATIVE(([a, b]) => { + assertNumber(a); + assertNumber(b); + return a.value >= b.value ? TRUE : FALSE; + }), + + 'Core:lteq': FN_NATIVE(([a, b]) => { + assertNumber(a); + assertNumber(b); + return a.value <= b.value ? TRUE : FALSE; + }), + + 'Core:type': FN_NATIVE(([v]) => { + expectAny(v); + return STR(v.type); + }), + + 'Core:to_str': FN_NATIVE(([v]) => { + expectAny(v); + + return STR(reprValue(v)); + }), + + 'Core:range': FN_NATIVE(([a, b]) => { + assertNumber(a); + assertNumber(b); + if (a.value < b.value) { + return ARR(Array.from({ length: (b.value - a.value) + 1 }, (_, i) => NUM(i + a.value))); + } else if (a.value > b.value) { + return ARR(Array.from({ length: (a.value - b.value) + 1 }, (_, i) => NUM(a.value - i))); + } else { + return ARR([a]); + } + }), + + 'Core:sleep': FN_NATIVE(async ([delay]) => { + assertNumber(delay); + await new Promise((r) => setTimeout(r, delay.value)); + return NULL; + }), + + 'Core:abort': FN_NATIVE(async ([message]) => { + assertString(message); + throw new AiScriptUserError(message.value); + }), +}; diff --git a/src/interpreter/lib/std.ts b/src/interpreter/lib/std.ts index 13648a16..f24a13a9 100644 --- a/src/interpreter/lib/std.ts +++ b/src/interpreter/lib/std.ts @@ -1,9 +1,8 @@ /* eslint-disable no-empty-pattern */ import { v4 as uuid } from 'uuid'; -import { NUM, STR, FN_NATIVE, FALSE, TRUE, ARR, NULL, BOOL, OBJ, ERROR } from '../value.js'; -import { assertNumber, assertString, assertBoolean, valToJs, jsToVal, assertFunction, assertObject, eq, expectAny, assertArray, reprValue } from '../util.js'; -import { AiScriptRuntimeError, AiScriptUserError } from '../../error.js'; -import { AISCRIPT_VERSION } from '../../constants.js'; +import { NUM, STR, FN_NATIVE, ARR, NULL, BOOL, OBJ, ERROR } from '../value.js'; +import { assertNumber, assertString, assertBoolean, valToJs, jsToVal, assertFunction, assertObject, expectAny, assertArray } from '../util.js'; +import { AiScriptRuntimeError } from '../../error.js'; import { textDecoder } from '../../const.js'; import { stdMath } from './math.js'; import type { Value } from '../value.js'; @@ -13,137 +12,6 @@ export const std: Record = { 'help': STR('SEE: https://github.com/syuilo/aiscript/blob/master/docs/get-started.md'), - //#region Core - 'Core:v': STR(AISCRIPT_VERSION), - - 'Core:ai': STR('kawaii'), - - 'Core:not': FN_NATIVE(([a]) => { - assertBoolean(a); - return a.value ? FALSE : TRUE; - }), - - 'Core:eq': FN_NATIVE(([a, b]) => { - expectAny(a); - expectAny(b); - return eq(a, b) ? TRUE : FALSE; - }), - - 'Core:neq': FN_NATIVE(([a, b]) => { - expectAny(a); - expectAny(b); - return eq(a, b) ? FALSE : TRUE; - }), - - 'Core:and': FN_NATIVE(([a, b]) => { - assertBoolean(a); - if (!a.value) return FALSE; - assertBoolean(b); - return b.value ? TRUE : FALSE; - }), - - 'Core:or': FN_NATIVE(([a, b]) => { - assertBoolean(a); - if (a.value) return TRUE; - assertBoolean(b); - return b.value ? TRUE : FALSE; - }), - - 'Core:add': FN_NATIVE(([a, b]) => { - assertNumber(a); - assertNumber(b); - return NUM(a.value + b.value); - }), - - 'Core:sub': FN_NATIVE(([a, b]) => { - assertNumber(a); - assertNumber(b); - return NUM(a.value - b.value); - }), - - 'Core:mul': FN_NATIVE(([a, b]) => { - assertNumber(a); - assertNumber(b); - return NUM(a.value * b.value); - }), - - 'Core:pow': FN_NATIVE(([a, b]) => { - assertNumber(a); - assertNumber(b); - const res = a.value ** b.value; - return NUM(res); - }), - - 'Core:div': FN_NATIVE(([a, b]) => { - assertNumber(a); - assertNumber(b); - const res = a.value / b.value; - return NUM(res); - }), - - 'Core:mod': FN_NATIVE(([a, b]) => { - assertNumber(a); - assertNumber(b); - return NUM(a.value % b.value); - }), - - 'Core:gt': FN_NATIVE(([a, b]) => { - assertNumber(a); - assertNumber(b); - return a.value > b.value ? TRUE : FALSE; - }), - - 'Core:lt': FN_NATIVE(([a, b]) => { - assertNumber(a); - assertNumber(b); - return a.value < b.value ? TRUE : FALSE; - }), - - 'Core:gteq': FN_NATIVE(([a, b]) => { - assertNumber(a); - assertNumber(b); - return a.value >= b.value ? TRUE : FALSE; - }), - - 'Core:lteq': FN_NATIVE(([a, b]) => { - assertNumber(a); - assertNumber(b); - return a.value <= b.value ? TRUE : FALSE; - }), - - 'Core:type': FN_NATIVE(([v]) => { - expectAny(v); - return STR(v.type); - }), - - 'Core:to_str': FN_NATIVE(([v]) => { - expectAny(v); - - return STR(reprValue(v)); - }), - - 'Core:range': FN_NATIVE(([a, b]) => { - assertNumber(a); - assertNumber(b); - if (a.value < b.value) { - return ARR(Array.from({ length: (b.value - a.value) + 1 }, (_, i) => NUM(i + a.value))); - } else if (a.value > b.value) { - return ARR(Array.from({ length: (a.value - b.value) + 1 }, (_, i) => NUM(a.value - i))); - } else { - return ARR([a]); - } - }), - 'Core:sleep': FN_NATIVE(async ([delay]) => { - assertNumber(delay); - await new Promise((r) => setTimeout(r, delay.value)); - return NULL; - }), - 'Core:abort': FN_NATIVE(async ([message]) => { - assertString(message); - throw new AiScriptUserError(message.value); - }), - //#endregion - //#region Util 'Util:uuid': FN_NATIVE(() => { return STR(uuid()); @@ -318,12 +186,12 @@ export const std: Record = { 'Uri:encode_full': FN_NATIVE(([v]) => { assertString(v); return STR(encodeURI(v.value)); - }), + }), 'Uri:encode_component': FN_NATIVE(([v]) => { assertString(v); return STR(encodeURIComponent(v.value)); - }), + }), 'Uri:decode_full': FN_NATIVE(([v]) => { assertString(v); From d46f0f6aa42c102ae684ad054c037911875f4f16 Mon Sep 17 00:00:00 2001 From: uzmoi <63585970+uzmoi@users.noreply.github.com> Date: Sun, 18 Jan 2026 07:28:49 +0900 Subject: [PATCH 4/4] Update api report --- etc/aiscript.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/aiscript.api.md b/etc/aiscript.api.md index 2ef9823e..648af961 100644 --- a/etc/aiscript.api.md +++ b/etc/aiscript.api.md @@ -921,7 +921,7 @@ type VUserFn = VFnBase & { // Warnings were encountered during analysis: // -// src/interpreter/index.ts:49:4 - (ae-forgotten-export) The symbol "LogObject" needs to be exported by the entry point index.d.ts +// src/interpreter/index.ts:50:4 - (ae-forgotten-export) The symbol "LogObject" needs to be exported by the entry point index.d.ts // src/interpreter/value.ts:47:2 - (ae-forgotten-export) The symbol "Type" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package)