├── .eslintrc.js ├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── package.json ├── src ├── assert.ts ├── character.ts ├── color.ts ├── config.ts ├── csv │ ├── character.ts │ ├── gamebase.ts │ ├── index.ts │ ├── item.ts │ └── varsize.ts ├── error.ts ├── fn.ts ├── index.ts ├── lazy.ts ├── parser │ ├── const.ts │ ├── erb.ts │ ├── erh.ts │ ├── expr.ts │ ├── preprocess.ts │ ├── property.ts │ └── util.ts ├── printer.ts ├── property │ ├── define.ts │ ├── dim.ts │ ├── index.ts │ ├── localsize.ts │ ├── localssize.ts │ ├── method.ts │ ├── order.ts │ └── single.ts ├── random.ts ├── savedata.ts ├── scene.ts ├── slice.ts ├── statement │ ├── assign │ │ ├── assign-form.ts │ │ ├── assign-int.ts │ │ ├── assign-op-int.ts │ │ ├── assign-op-str.ts │ │ ├── assign-postfix.ts │ │ ├── assign-prefix.ts │ │ ├── assign-str.ts │ │ └── index.ts │ ├── command │ │ ├── addchara.ts │ │ ├── addcopychara.ts │ │ ├── adddefchara.ts │ │ ├── addvoidchara.ts │ │ ├── alignment.ts │ │ ├── arrayremove.ts │ │ ├── arrayshift.ts │ │ ├── bar.ts │ │ ├── barstr.ts │ │ ├── begin.ts │ │ ├── break.ts │ │ ├── call.ts │ │ ├── callf.ts │ │ ├── callform.ts │ │ ├── callformf.ts │ │ ├── calltrain.ts │ │ ├── case.ts │ │ ├── cbgclear.ts │ │ ├── cbgclearbutton.ts │ │ ├── cbgremovebmap.ts │ │ ├── chkdata.ts │ │ ├── chkfont.ts │ │ ├── clearbit.ts │ │ ├── clearline.ts │ │ ├── cleartextbox.ts │ │ ├── continue.ts │ │ ├── copychara.ts │ │ ├── cupcheck.ts │ │ ├── currentalign.ts │ │ ├── currentredraw.ts │ │ ├── customdrawline.ts │ │ ├── cvarset.ts │ │ ├── debugclear.ts │ │ ├── debugprint.ts │ │ ├── debugprintform.ts │ │ ├── delallchara.ts │ │ ├── delchara.ts │ │ ├── deldata.ts │ │ ├── dowhile.ts │ │ ├── drawline.ts │ │ ├── drawlineform.ts │ │ ├── dumprand.ts │ │ ├── encodetouni.ts │ │ ├── escape.ts │ │ ├── fontbold.ts │ │ ├── fontitalic.ts │ │ ├── fontregular.ts │ │ ├── fontstyle.ts │ │ ├── for.ts │ │ ├── forcewait.ts │ │ ├── getexplv.ts │ │ ├── getfont.ts │ │ ├── getmillisecond.ts │ │ ├── getpalamlv.ts │ │ ├── getsecond.ts │ │ ├── getstyle.ts │ │ ├── gettime.ts │ │ ├── goto.ts │ │ ├── gotoform.ts │ │ ├── if.ts │ │ ├── initrand.ts │ │ ├── input.ts │ │ ├── inputs.ts │ │ ├── invertbit.ts │ │ ├── isactive.ts │ │ ├── isskip.ts │ │ ├── jump.ts │ │ ├── jumpform.ts │ │ ├── loaddata.ts │ │ ├── loadgame.ts │ │ ├── loadglobal.ts │ │ ├── method.ts │ │ ├── mouseskip.ts │ │ ├── mousex.ts │ │ ├── mousey.ts │ │ ├── oneinput.ts │ │ ├── oneinputs.ts │ │ ├── outputlog.ts │ │ ├── pickupchara.ts │ │ ├── print.ts │ │ ├── print_palam.ts │ │ ├── print_shopitem.ts │ │ ├── printbutton.ts │ │ ├── printc.ts │ │ ├── printcperline.ts │ │ ├── printdata.ts │ │ ├── printform.ts │ │ ├── printformc.ts │ │ ├── printforms.ts │ │ ├── printplain.ts │ │ ├── prints.ts │ │ ├── printv.ts │ │ ├── putform.ts │ │ ├── quit.ts │ │ ├── randomize.ts │ │ ├── redraw.ts │ │ ├── repeat.ts │ │ ├── reset_stain.ts │ │ ├── resetbgcolor.ts │ │ ├── resetcolor.ts │ │ ├── resetdata.ts │ │ ├── resetglobal.ts │ │ ├── restart.ts │ │ ├── return.ts │ │ ├── returnf.ts │ │ ├── reuselastline.ts │ │ ├── savedata.ts │ │ ├── savegame.ts │ │ ├── saveglobal.ts │ │ ├── setbgcolor.ts │ │ ├── setbgcolorbyname.ts │ │ ├── setbit.ts │ │ ├── setcolor.ts │ │ ├── setcolorbyname.ts │ │ ├── setfont.ts │ │ ├── skipdisp.ts │ │ ├── sortchara.ts │ │ ├── split.ts │ │ ├── stopcalltrain.ts │ │ ├── strdata.ts │ │ ├── strfind.ts │ │ ├── strfindu.ts │ │ ├── strlen.ts │ │ ├── strlenform.ts │ │ ├── strlenformu.ts │ │ ├── strlenu.ts │ │ ├── substring.ts │ │ ├── substringu.ts │ │ ├── swap.ts │ │ ├── swapchara.ts │ │ ├── throw.ts │ │ ├── times.ts │ │ ├── tinput.ts │ │ ├── tinputs.ts │ │ ├── toneinput.ts │ │ ├── toneinputs.ts │ │ ├── trycall.ts │ │ ├── trycallform.ts │ │ ├── tryccall.ts │ │ ├── tryccallform.ts │ │ ├── trycgoto.ts │ │ ├── trycgotoform.ts │ │ ├── trycjump.ts │ │ ├── trycjumpform.ts │ │ ├── trygoto.ts │ │ ├── trygotoform.ts │ │ ├── tryjump.ts │ │ ├── tryjumpform.ts │ │ ├── twait.ts │ │ ├── upcheck.ts │ │ ├── varset.ts │ │ ├── wait.ts │ │ ├── waitanykey.ts │ │ └── while.ts │ ├── expr │ │ ├── binary.ts │ │ ├── compare.ts │ │ ├── const.ts │ │ ├── form.ts │ │ ├── index.ts │ │ ├── inline-call.ts │ │ ├── ternary.ts │ │ ├── unary-op.ts │ │ ├── unary.ts │ │ └── variable.ts │ ├── index.ts │ └── method │ │ ├── abs.ts │ │ ├── barstr.ts │ │ ├── csvabl.ts │ │ ├── csvbase.ts │ │ ├── csvcallname.ts │ │ ├── csvcflag.ts │ │ ├── csvcstr.ts │ │ ├── csvequip.ts │ │ ├── csvexp.ts │ │ ├── csvjuel.ts │ │ ├── csvmark.ts │ │ ├── csvmastername.ts │ │ ├── csvname.ts │ │ ├── csvnickname.ts │ │ ├── csvrelation.ts │ │ ├── csvtalent.ts │ │ ├── existcsv.ts │ │ ├── findchara.ts │ │ ├── findlastchara.ts │ │ ├── getbgcolor.ts │ │ ├── getbit.ts │ │ ├── getchara.ts │ │ ├── getcolor.ts │ │ ├── getdefbgcolor.ts │ │ ├── getdefcolor.ts │ │ ├── getfocuscolor.ts │ │ ├── groupmatch.ts │ │ ├── inrange.ts │ │ ├── limit.ts │ │ ├── lineisempty.ts │ │ ├── match.ts │ │ ├── max.ts │ │ ├── maxarray.ts │ │ ├── min.ts │ │ ├── minarray.ts │ │ ├── power.ts │ │ ├── rand.ts │ │ ├── sign.ts │ │ ├── sqrt.ts │ │ ├── strlens.ts │ │ ├── strlensu.ts │ │ ├── sumarray.ts │ │ ├── toint.ts │ │ ├── tostr.ts │ │ ├── unicode.ts │ │ └── varsize.ts ├── thunk.ts ├── value-list.ts ├── value │ ├── index.ts │ ├── int-0d.ts │ ├── int-1d.ts │ ├── int-2d.ts │ ├── int-3d.ts │ ├── int-char-0d.ts │ ├── int-char-1d.ts │ ├── special │ │ ├── charanum.ts │ │ ├── linecount.ts │ │ └── rand.ts │ ├── str-0d.ts │ ├── str-1d.ts │ ├── str-char-0d.ts │ └── str-char-1d.ts └── vm.ts └── tsconfig.json /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: deploy 2 | on: 3 | push: 4 | tags: 5 | - src/v* 6 | jobs: 7 | deploy: 8 | runs-on: ubuntu-20.04 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - uses: actions/setup-node@v2 13 | with: 14 | node-version: '14' 15 | - run: npm install 16 | - run: npm run build 17 | 18 | - run: git config user.name "Undercrow" 19 | - run: git config user.email "undercrow@protonmail.com" 20 | - run: git add -f build 21 | - run: git rm -r src 22 | - run: git commit -am "Build files" 23 | - run: git tag ${GITHUB_REF#refs/tags/src/} 24 | - run: git push -f origin ${GITHUB_REF#refs/tags/src/} 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /local 3 | /node_modules 4 | /package-lock.json 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .eslintrc.js 2 | .gitignore 3 | src/ 4 | tsconfig.json 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2020-2021 Undercrow 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EraJS 2 | 3 | EraJS is a javascript implementation of [Emuera](https://osdn.net/projects/emuera/). This repository includes only the core runtime, and is not intended to be directly used by the end users. To run full ERB scripts, please use other programs that implements appropriate interface. 4 | 5 | ## Example 6 | ```typescript 7 | // Read scripts 8 | const erh: string[] = []; 9 | const erb: string[] = []; 10 | const csv = new Map(); 11 | for (const file of await fs.readdir(ERB_PATH)) { 12 | if (file.endsWith(".ERB") || file.endsWith(".erb")) { 13 | erb.push(await fs.readFile(path.join(ERB_PATH, file))); 14 | } else if (file.endsWith(".ERH") || file.endsWith(".erh")) { 15 | erh.push(await fs.readFile(path.join(ERB_PATH, file))); 16 | } else if (file.endsWith(".CSV") || file.endsWith(".csv")) { 17 | csv.set(file, await fs.readFile(path.join(ERB_PATH, file))); 18 | } 19 | } 20 | 21 | // Compile and create VM 22 | const vm = compile(erh, erb, csv); 23 | 24 | // Run VM until the end 25 | const runtime = vm.start(); 26 | let input = ""; 27 | while (true) { 28 | const next = runtime.next(input); 29 | console.log(next.value); 30 | if (next.value?.type === "input") { 31 | const input = getInput(); 32 | } 33 | if (next.done === true) { 34 | break; 35 | } 36 | } 37 | ``` 38 | 39 | ## API 40 | 41 | ### compile(erh: string[], erb: string[], csv: Map): VM 42 | 43 | Compiles CSV, ERH, ERB scripts and returns a VM instance. Meaning of the arguments are as follows: 44 | - `erh`: List of raw ERH scripts. 45 | - `erb`: List of raw ERB scripts. 46 | - `csv`: es6 Map of (filename -> csv content). 47 | 48 | ### VM.start() 49 | 50 | Starts the VM. This returns a generator which accepts string as an argument, and yields `Output`. See `src/statement/index.ts` for details. 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "erajs", 3 | "version": "0.7.0", 4 | "description": "Javascript implementation of emuera", 5 | "author": "Undercrow ", 6 | "license": "MIT", 7 | "main": "build/index.js", 8 | "types": "build/index.d.ts", 9 | "scripts": { 10 | "build": "tsc", 11 | "lint": "run-s lint:tsc lint:eslint", 12 | "lint:tsc": "tsc -p . --noEmit", 13 | "lint:eslint": "eslint 'src/**/*.ts'" 14 | }, 15 | "dependencies": { 16 | "dayjs": "^1.9.7", 17 | "papaparse": "^5.3.0", 18 | "parsimmon": "^1.16.0" 19 | }, 20 | "devDependencies": { 21 | "@types/papaparse": "^5.2.4", 22 | "@types/parsimmon": "^1.10.3", 23 | "@typescript-eslint/eslint-plugin": "^4.4.1", 24 | "@typescript-eslint/parser": "^4.4.1", 25 | "eslint": "^7.11.0", 26 | "npm-run-all": "^4.1.5", 27 | "ts-node": "^9.0.0", 28 | "typescript": "^4.0.3" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/color.ts: -------------------------------------------------------------------------------- 1 | type Color = { 2 | r: number; 3 | g: number; 4 | b: number; 5 | }; 6 | 7 | export function copy(color: Color): Color { 8 | return {...color}; 9 | } 10 | 11 | export function rgb(r: number, g: number, b: number): Color { 12 | return {r, g, b}; 13 | } 14 | 15 | export function hex(value: number): Color { 16 | /* eslint-disable no-bitwise */ 17 | const r = (value && 0xFF0000) >> 16; 18 | const g = (value && 0x00FF00) >> 8; 19 | const b = (value && 0x0000FF) >> 0; 20 | /* eslint-enable no-bitwise */ 21 | return {r, g, b}; 22 | } 23 | 24 | export function toHex(color: Color): number { 25 | // eslint-disable-next-line no-bitwise 26 | return (color.r << 16) + (color.g << 8) + (color.b << 0); 27 | } 28 | 29 | export default Color; 30 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | type Config = { 2 | front: string; 3 | back: string; 4 | focus: string; 5 | }; 6 | 7 | export default Config; 8 | -------------------------------------------------------------------------------- /src/csv/gamebase.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../assert"; 2 | 3 | type GameBase = { 4 | author?: string; 5 | info?: string; 6 | year?: string; 7 | title?: string; 8 | code?: number; 9 | version?: number; 10 | }; 11 | 12 | export default function parse(fileName: string, rows: string[][]): GameBase { 13 | const result: GameBase = {}; 14 | for (const row of rows) { 15 | switch (row[0]) { 16 | case "作者": result.author = row[1]; break; 17 | case "追加情報": result.info = row[1]; break; 18 | case "製作年": result.year = row[1]; break; 19 | case "タイトル": result.title = row[1]; break; 20 | case "コード": { 21 | const code = parseInt(row[1]); 22 | assert.number(code, `Code in ${fileName} should be an integer`); 23 | result.code = code; 24 | break; 25 | } 26 | case "バージョン": { 27 | const version = parseInt(row[1]); 28 | assert.number(version, `Version in ${fileName} should be an integer`); 29 | result.version = version; 30 | break; 31 | } 32 | default: break; 33 | } 34 | } 35 | 36 | return result; 37 | } 38 | -------------------------------------------------------------------------------- /src/csv/item.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../assert"; 2 | 3 | type Item = { 4 | name: string; 5 | price: number; 6 | }; 7 | 8 | export default function parse(fileName: string, rows: string[][]): Map { 9 | const result = new Map(); 10 | for (const row of rows) { 11 | const index = parseInt(row[0]); 12 | assert.number(index, `Index value in ${fileName} should be an integer`); 13 | 14 | result.set(index, { 15 | name: row[1] ?? "", 16 | price: Number(row[2] ?? "0"), 17 | }); 18 | } 19 | 20 | return result; 21 | } 22 | -------------------------------------------------------------------------------- /src/csv/varsize.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../assert"; 2 | 3 | export default function parse(fileName: string, rows: string[][]): Map { 4 | const result = new Map(); 5 | for (const row of rows) { 6 | const name = row[0]; 7 | const size = row.slice(1).map((cell) => Number(cell)); 8 | assert.numArray(size, `Size of variable in ${fileName} should be an integer`); 9 | 10 | // Note: -1 is ignored 11 | if (size.every((s) => s >= 0)) { 12 | result.set(name, size); 13 | } 14 | } 15 | 16 | return result; 17 | } 18 | -------------------------------------------------------------------------------- /src/error.ts: -------------------------------------------------------------------------------- 1 | import Slice from "./slice"; 2 | 3 | export default class EraJSError extends Error { 4 | public line: Slice; 5 | public trace: string[]; 6 | 7 | public constructor(message: string, line: Slice, trace: string[]) { 8 | super(message); 9 | this.line = line; 10 | this.trace = trace; 11 | } 12 | } 13 | 14 | export function parser(message: string) { 15 | return new Error(`Parser error found: ${message}`); 16 | } 17 | 18 | export function notFound(type: string, name: string) { 19 | return new Error(`${type} ${name} does not exist`); 20 | } 21 | 22 | export function invalidIndex(type: string, name: string, index: number[]) { 23 | return new Error(`${type} variable ${name} cannot be indexed by [${index.join(",")}]`); 24 | } 25 | 26 | export function notImpl(target: string) { 27 | return new Error(`${target} is not implemented yet`); 28 | } 29 | 30 | export function misc(message: string) { 31 | return new Error(`Runtime error found: ${message}`); 32 | } 33 | 34 | export function internal(message: string) { 35 | return new Error(`Unexpected internal error found: ${message}`); 36 | } 37 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import parseCSV from "./csv"; 2 | import parseERB from "./parser/erb"; 3 | import parseERH from "./parser/erh"; 4 | import Define from "./property/define"; 5 | import VM from "./vm"; 6 | 7 | export {default as Config} from "./config"; 8 | export {default as EraJSError} from "./error"; 9 | export { 10 | ButtonChunk, 11 | Chunk, 12 | ClearOutput, 13 | ContentOutput, 14 | InputOutput, 15 | LineOutput, 16 | Output, 17 | StringChunk, 18 | TInputOutput, 19 | WaitOutput, 20 | } from "./statement"; 21 | export type {VM}; 22 | 23 | export function compile(files: Map): VM { 24 | const csvFiles = new Map(); 25 | const erhFiles = new Map(); 26 | const erbFiles = new Map(); 27 | for (const [file, content] of files) { 28 | const FILE = file.toUpperCase(); 29 | if (FILE.endsWith(".CSV")) { 30 | csvFiles.set(file.toUpperCase(), content); 31 | } else if (FILE.endsWith(".ERH")) { 32 | erhFiles.set(file.toUpperCase(), content); 33 | } else if (FILE.endsWith(".ERB")) { 34 | erbFiles.set(file.toUpperCase(), content); 35 | } 36 | } 37 | 38 | const csv = parseCSV(csvFiles); 39 | 40 | const macros = new Set(); 41 | const header = parseERH(erhFiles, macros); 42 | for (const property of header) { 43 | if (property instanceof Define) { 44 | macros.add(property.name); 45 | } 46 | } 47 | const fnList = parseERB(erbFiles, macros); 48 | 49 | return new VM({header, fnList, csv}); 50 | } 51 | -------------------------------------------------------------------------------- /src/lazy.ts: -------------------------------------------------------------------------------- 1 | import P from "parsimmon"; 2 | 3 | import * as U from "./parser/util"; 4 | import Slice from "./slice"; 5 | 6 | export default class Lazy { 7 | public raw: Slice; 8 | public parser: P.Parser; 9 | public isCompiled: boolean; 10 | public cache?: T; 11 | 12 | public constructor(raw: Slice, parser: P.Parser) { 13 | this.parser = parser; 14 | this.raw = raw; 15 | this.isCompiled = false; 16 | } 17 | 18 | public get(): T { 19 | if (this.isCompiled) { 20 | return this.cache!; 21 | } 22 | 23 | const result = U.tryParse(this.parser, this.raw); 24 | this.isCompiled = true; 25 | this.cache = result; 26 | 27 | return result; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/parser/erh.ts: -------------------------------------------------------------------------------- 1 | import Property from "../property"; 2 | import prop from "./property"; 3 | import {normalize, preprocess, toLines} from "./preprocess"; 4 | import * as U from "./util"; 5 | 6 | export default function parseERH(files: Map, macros: Set): Property[] { 7 | const result: Property[] = []; 8 | 9 | for (const [name, content] of files) { 10 | const normalized = normalize(content); 11 | const lines = preprocess(toLines(normalized), macros); 12 | for (const line of lines) { 13 | line.file = name; 14 | } 15 | 16 | for (const line of lines) { 17 | result.push(U.tryParse(prop, line)); 18 | } 19 | } 20 | 21 | return result; 22 | } 23 | -------------------------------------------------------------------------------- /src/property/define.ts: -------------------------------------------------------------------------------- 1 | import type Expr from "../statement/expr"; 2 | 3 | export default class Define { 4 | public name: string; 5 | public expr?: Expr; 6 | 7 | public constructor(name: Define["name"], expr?: Define["expr"]) { 8 | this.name = name; 9 | this.expr = expr; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/property/index.ts: -------------------------------------------------------------------------------- 1 | import type Define from "./define"; 2 | import type Dim from "./dim"; 3 | import type LocalSize from "./localsize"; 4 | import type LocalSSize from "./localssize"; 5 | import type Method from "./method"; 6 | import type Order from "./order"; 7 | import type Single from "./single"; 8 | 9 | type Property = 10 | | Define 11 | | Dim 12 | | LocalSize 13 | | LocalSSize 14 | | Method 15 | | Order 16 | | Single; 17 | 18 | export default Property; 19 | -------------------------------------------------------------------------------- /src/property/localsize.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../assert"; 2 | import Expr from "../statement/expr"; 3 | import Int1DValue from "../value/int-1d"; 4 | import type VM from "../vm"; 5 | 6 | export default class LocalSize { 7 | public size: Expr; 8 | 9 | public constructor(size: Expr) { 10 | this.size = size; 11 | } 12 | 13 | public async apply(vm: VM, fn: string) { 14 | const size = await this.size.reduce(vm); 15 | assert.bigint(size, "Argument of LOCALSIZE should be a number"); 16 | vm.staticMap.get(fn)!.set("LOCAL", new Int1DValue("LOCAL", [Number(size)])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/property/localssize.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../assert"; 2 | import Expr from "../statement/expr"; 3 | import Str1DValue from "../value/str-1d"; 4 | import type VM from "../vm"; 5 | 6 | export default class LocalSSize { 7 | public size: Expr; 8 | 9 | public constructor(size: Expr) { 10 | this.size = size; 11 | } 12 | 13 | public async apply(vm: VM, fn: string) { 14 | const size = await this.size.reduce(vm); 15 | assert.bigint(size, "Argument of LOCALSIZE should be a number"); 16 | vm.staticMap.get(fn)!.set("LOCALS", new Str1DValue("LOCALS", [Number(size)])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/property/method.ts: -------------------------------------------------------------------------------- 1 | export default class Method {} 2 | -------------------------------------------------------------------------------- /src/property/order.ts: -------------------------------------------------------------------------------- 1 | export default class Order { 2 | public order: "PRI" | "LATER"; 3 | 4 | public constructor(order: Order["order"]) { 5 | this.order = order; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/property/single.ts: -------------------------------------------------------------------------------- 1 | export default class Single {} 2 | -------------------------------------------------------------------------------- /src/random.ts: -------------------------------------------------------------------------------- 1 | // Mulberry32 PRNG 2 | // https://gist.github.com/tommyettinger/46a874533244883189143505d203312c 3 | 4 | export default class PRNG { 5 | public state: number; 6 | 7 | public constructor() { 8 | this.state = Math.floor(Math.random() * (2 ** 32)); 9 | } 10 | 11 | /* eslint-disable no-bitwise */ 12 | public next(): number { 13 | this.state += 0x6D2B79F5; 14 | let z = this.state; 15 | z = Math.imul(z ^ z >>> 15, z | 1); 16 | z ^= z + Math.imul(z ^ z >>> 7, z | 61); 17 | return (z ^ z >>> 14) >>> 0; 18 | } 19 | /* eslint-enable no-bitwise */ 20 | } 21 | -------------------------------------------------------------------------------- /src/savedata.ts: -------------------------------------------------------------------------------- 1 | export type BaseSave = { 2 | code: number; 3 | version: number; 4 | data: unknown; 5 | }; 6 | 7 | export type GlobalSave = BaseSave & { 8 | data: Record; 9 | }; 10 | 11 | export type GameSave = BaseSave & { 12 | data: { 13 | comment: string; 14 | characters: Record[]; 15 | variables: Record; 16 | }; 17 | }; 18 | 19 | export const savefile = { 20 | global: "global.sav", 21 | game: (i: number) => "save" + i.toString().padStart(2, "0") + ".sav", 22 | }; 23 | -------------------------------------------------------------------------------- /src/slice.ts: -------------------------------------------------------------------------------- 1 | export default class Slice { 2 | public file: string; 3 | // NOTE: `line` is 0-indexed 4 | public line: number; 5 | public from: number; 6 | public to: number; 7 | public content: string; 8 | 9 | public constructor( 10 | file: string, 11 | line: number, 12 | content: string, 13 | from?: number, 14 | to?: number 15 | ) { 16 | this.file = file; 17 | this.line = line; 18 | this.content = content; 19 | this.from = from ?? 0; 20 | this.to = to ?? content.length; 21 | } 22 | 23 | public slice(from?: number, to?: number): Slice { 24 | const newFrom = this.from + (from ?? 0); 25 | let newTo: number; 26 | if (to == null) { 27 | newTo = this.to; 28 | } else { 29 | newTo = Math.min(this.to, this.from + to); 30 | } 31 | return new Slice(this.file, this.line, this.content, newFrom, newTo); 32 | } 33 | 34 | public get(): string { 35 | return this.content.slice(this.from, this.to); 36 | } 37 | 38 | public length(): number { 39 | return this.to - this.from; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/statement/assign/assign-form.ts: -------------------------------------------------------------------------------- 1 | import Lazy from "../../lazy"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import type Form from "../expr/form"; 7 | import Variable from "../expr/variable"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.sepBy0(",", X.form[","]); 11 | export default class AssignForm extends Statement { 12 | public dest: Variable; 13 | public arg: Lazy; 14 | 15 | public constructor(dest: Variable, raw: Slice) { 16 | super(raw); 17 | this.dest = dest; 18 | 19 | this.arg = new Lazy(raw, PARSER); 20 | } 21 | 22 | public async *run(vm: VM) { 23 | const dest = this.dest.getCell(vm); 24 | const index = await this.dest.reduceIndex(vm); 25 | const arg = this.arg.get(); 26 | 27 | const partialIndex = index.slice(0, -1); 28 | const lastIndex = index[index.length - 1] ?? 0; 29 | 30 | if (arg.length !== 0) { 31 | for (let i = 0; i < arg.length; ++i) { 32 | const value = await arg[i].reduce(vm); 33 | dest.set(vm, value, [...partialIndex, lastIndex + i]); 34 | } 35 | } else { 36 | dest.set(vm, "", index); 37 | } 38 | 39 | return null; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/statement/assign/assign-int.ts: -------------------------------------------------------------------------------- 1 | import Lazy from "../../lazy"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import type Expr from "../expr"; 7 | import Variable from "../expr/variable"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.sepBy0(",", X.expr); 11 | export default class AssignInt extends Statement { 12 | public dest: Variable; 13 | public arg: Lazy; 14 | 15 | public constructor(dest: Variable, raw: Slice) { 16 | super(raw); 17 | this.dest = dest; 18 | 19 | this.arg = new Lazy(raw, PARSER); 20 | } 21 | 22 | public async *run(vm: VM) { 23 | const dest = this.dest.getCell(vm); 24 | const index = await this.dest.reduceIndex(vm); 25 | const arg = this.arg.get(); 26 | 27 | const partialIndex = index.slice(0, -1); 28 | const lastIndex = index[index.length - 1] ?? 0; 29 | 30 | for (let i = 0; i < arg.length; ++i) { 31 | const value = await arg[i].reduce(vm); 32 | dest.set(vm, value, [...partialIndex, lastIndex + i]); 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/statement/assign/assign-op-int.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import Lazy from "../../lazy"; 3 | import * as X from "../../parser/expr"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import Expr from "../expr"; 7 | import Variable from "../expr/variable"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = X.expr; 11 | type Operator = "*=" | "/=" | "%=" | "+=" | "-=" | "&=" | "|=" | "^="; 12 | export default class AssignOpInt extends Statement { 13 | public dest: Variable; 14 | public operator: Operator; 15 | public arg: Lazy; 16 | 17 | public constructor(dest: Variable, operator: Operator, raw: Slice) { 18 | super(raw); 19 | this.dest = dest; 20 | this.operator = operator; 21 | 22 | this.arg = new Lazy(raw, PARSER); 23 | } 24 | 25 | public async *run(vm: VM) { 26 | const dest = this.dest.getCell(vm); 27 | const index = await this.dest.reduceIndex(vm); 28 | const original = dest.get(vm, index) as bigint; 29 | const value = await this.arg.get().reduce(vm); 30 | assert.bigint(value, `Right operand of ${this.operator} should be a number`); 31 | 32 | switch (this.operator) { 33 | case "*=": dest.set(vm, original * value, index); break; 34 | case "/=": dest.set(vm, original / value, index); break; 35 | case "%=": dest.set(vm, original % value, index); break; 36 | case "+=": dest.set(vm, original + value, index); break; 37 | case "-=": dest.set(vm, original - value, index); break; 38 | // eslint-disable-next-line no-bitwise 39 | case "&=": dest.set(vm, original & value, index); break; 40 | // eslint-disable-next-line no-bitwise 41 | case "|=": dest.set(vm, original | value, index); break; 42 | // eslint-disable-next-line no-bitwise 43 | case "^=": dest.set(vm, original ^ value, index); break; 44 | } 45 | 46 | return null; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/statement/assign/assign-op-str.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import Lazy from "../../lazy"; 3 | import * as X from "../../parser/expr"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import Expr from "../expr"; 7 | import Variable from "../expr/variable"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = X.expr; 11 | type Operator = "+="; 12 | export default class AssignOpStr extends Statement { 13 | public dest: Variable; 14 | public operator: Operator; 15 | public arg: Lazy; 16 | 17 | public constructor(dest: Variable, operator: Operator, raw: Slice) { 18 | super(raw); 19 | this.dest = dest; 20 | this.operator = operator; 21 | 22 | this.arg = new Lazy(raw, PARSER); 23 | } 24 | 25 | public async *run(vm: VM) { 26 | const dest = this.dest.getCell(vm); 27 | const index = await this.dest.reduceIndex(vm); 28 | 29 | const original = dest.get(vm, index) as string; 30 | const arg = await this.arg.get().reduce(vm); 31 | assert.string(arg, `Right operand of ${this.operator} should be a string`); 32 | 33 | switch (this.operator) { 34 | case "+=": dest.set(vm, original + arg, index); break; 35 | } 36 | 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/statement/assign/assign-postfix.ts: -------------------------------------------------------------------------------- 1 | import P from "parsimmon"; 2 | 3 | import * as assert from "../../assert"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Variable from "../expr/variable"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = P.eof; 11 | type Operator = "++" | "--"; 12 | export default class AssignPostfix extends Statement { 13 | public dest: Variable; 14 | public operator: Operator; 15 | public arg: Lazy; 16 | 17 | public constructor(dest: Variable, operator: Operator, raw: Slice) { 18 | super(raw); 19 | this.dest = dest; 20 | this.operator = operator; 21 | 22 | this.arg = new Lazy(raw, PARSER); 23 | } 24 | 25 | public async *run(vm: VM) { 26 | this.raw.get(); 27 | 28 | const dest = this.dest.getCell(vm); 29 | assert.cond(dest.type === "number", "++/-- should be used with a numeric variable"); 30 | const index = await this.dest.reduceIndex(vm); 31 | const original = dest.get(vm, index) as bigint; 32 | 33 | switch (this.operator) { 34 | case "++": dest.set(vm, original + 1n, index); break; 35 | case "--": dest.set(vm, original - 1n, index); break; 36 | } 37 | 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/statement/assign/assign-prefix.ts: -------------------------------------------------------------------------------- 1 | import P from "parsimmon"; 2 | 3 | import * as assert from "../../assert"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Variable from "../expr/variable"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = P.eof; 11 | type Operator = "++" | "--"; 12 | export default class AssignPrefix extends Statement { 13 | public dest: Variable; 14 | public operator: Operator; 15 | public arg: Lazy; 16 | 17 | public constructor(dest: Variable, operator: Operator, raw: Slice) { 18 | super(raw); 19 | this.dest = dest; 20 | this.operator = operator; 21 | 22 | this.arg = new Lazy(raw, PARSER); 23 | } 24 | 25 | public async *run(vm: VM) { 26 | this.raw.get(); 27 | 28 | const dest = this.dest.getCell(vm); 29 | assert.cond(dest.type === "number", "++/-- should be used with a numeric variable"); 30 | const index = await this.dest.reduceIndex(vm); 31 | const original = dest.get(vm, index) as bigint; 32 | 33 | switch (this.operator) { 34 | case "++": dest.set(vm, original + 1n, index); break; 35 | case "--": dest.set(vm, original - 1n, index); break; 36 | } 37 | 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/statement/assign/assign-str.ts: -------------------------------------------------------------------------------- 1 | import Lazy from "../../lazy"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import type Expr from "../expr"; 7 | import Variable from "../expr/variable"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.sepBy0(",", X.expr); 11 | export default class AssignStr extends Statement { 12 | public dest: Variable; 13 | public arg: Lazy; 14 | 15 | public constructor(dest: Variable, raw: Slice) { 16 | super(raw); 17 | this.dest = dest; 18 | 19 | this.arg = new Lazy(raw, PARSER); 20 | } 21 | 22 | public async *run(vm: VM) { 23 | const dest = this.dest.getCell(vm); 24 | const index = await this.dest.reduceIndex(vm); 25 | const arg = this.arg.get(); 26 | 27 | const partialIndex = index.slice(0, -1); 28 | const lastIndex = index[index.length - 1] ?? 0; 29 | 30 | for (let i = 0; i < arg.length; ++i) { 31 | const value = await arg[i].reduce(vm); 32 | dest.set(vm, value, [...partialIndex, lastIndex + i]); 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/statement/command/addchara.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import Character from "../../character"; 3 | import * as X from "../../parser/expr"; 4 | import * as U from "../../parser/util"; 5 | import Lazy from "../../lazy"; 6 | import Slice from "../../slice"; 7 | import type VM from "../../vm"; 8 | import type Expr from "../expr"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.argNR0(X.expr); 12 | export default class AddChara extends Statement { 13 | public arg: Lazy; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | for (const expr of this.arg.get()) { 23 | const id = await expr.reduce(vm); 24 | assert.bigint(id, "Character id should be an integer"); 25 | 26 | const template = vm.templateMap.get(Number(id)); 27 | assert.cond(template != null, `Character template with id ${id} does not exist`); 28 | 29 | vm.characterList.push(new Character(vm, template)); 30 | } 31 | 32 | return null; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/statement/command/addcopychara.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type Expr from "../expr"; 7 | import Statement from "../index"; 8 | 9 | const PARSER = U.arg1R1(X.expr); 10 | export default class AddCopyChara extends Statement { 11 | public arg: Lazy; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | // eslint-disable-next-line @typescript-eslint/require-await 20 | public async *run() { 21 | throw E.notImpl("ADDCOPYCHARA"); 22 | 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/statement/command/adddefchara.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import Character from "../../character"; 3 | import * as U from "../../parser/util"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import Statement from "../index"; 7 | 8 | const PARSER = U.arg0R0(); 9 | export default class AddDefChara extends Statement { 10 | public constructor(raw: Slice) { 11 | super(raw); 12 | 13 | U.tryParse(PARSER, raw); 14 | } 15 | 16 | // eslint-disable-next-line @typescript-eslint/require-await 17 | public async *run(vm: VM) { 18 | const template = vm.templateMap.get(0); 19 | assert.cond(template != null, "Character template with id 0 does not exist"); 20 | 21 | vm.characterList.push(new Character(vm, template)); 22 | 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/statement/command/addvoidchara.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class AddVoidChara extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("ADDVOIDCHARA"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/alignment.ts: -------------------------------------------------------------------------------- 1 | import Lazy from "../../lazy"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import type VM from "../../vm"; 5 | import Statement from "../index"; 6 | 7 | export type Align = "LEFT" | "CENTER" | "RIGHT"; 8 | 9 | const PARSER = U.arg1R1(U.alt("LEFT", "CENTER", "RIGHT")); 10 | export default class Alignment extends Statement { 11 | public arg: Lazy; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | // eslint-disable-next-line @typescript-eslint/require-await 20 | public async *run(vm: VM) { 21 | vm.printer.align = this.arg.get(); 22 | 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/statement/command/arrayremove.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import type Variable from "../expr/variable"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.arg3R3(X.variable, X.expr, X.expr); 12 | export default class ArrayRemove extends Statement { 13 | public arg: Lazy<[Variable, Expr, Expr]>; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | const [targetExpr, startExpr, countExpr] = this.arg.get(); 23 | 24 | const target = targetExpr.getCell(vm); 25 | const index = await targetExpr.reduceIndex(vm); 26 | const length = target.length(index.length); 27 | const start = await startExpr.reduce(vm); 28 | assert.bigint(start, "2nd argument of ARRAYREMOVE must be a number"); 29 | const count = await countExpr.reduce(vm); 30 | assert.bigint(count, "3rd argument of ARRAYREMOVE must be a number"); 31 | 32 | for (let i = start; i < BigInt(length) - count; ++i) { 33 | const value = target.get(vm, [...index, Number(i + count)]); 34 | target.set(vm, value, [...index, Number(i)]); 35 | } 36 | for (let i = 0n; i < count; ++i) { 37 | if (target.type === "number") { 38 | target.set(vm, 0n, [...index, length - 1 - Number(i)]); 39 | } else { 40 | target.set(vm, "", [...index, length - 1 - Number(i)]); 41 | } 42 | } 43 | 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/statement/command/arrayshift.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import type Variable from "../expr/variable"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.arg5R3(X.variable, X.expr, X.expr, X.expr, X.expr); 12 | export default class ArrayShift extends Statement { 13 | public arg: Lazy<[Variable, Expr, Expr, Expr | undefined, Expr | undefined]>; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | // TODO: 4th and 5th argument 23 | const [targetExpr, countExpr, fillExpr] = this.arg.get(); 24 | 25 | const target = targetExpr.getCell(vm); 26 | const index = await targetExpr.reduceIndex(vm); 27 | const length = target.length(index.length); 28 | const count = await countExpr.reduce(vm); 29 | assert.bigint(count, "2nd argument of ARRAYSHIFT must be a number"); 30 | const fill = await fillExpr.reduce(vm); 31 | 32 | if (count > 0) { 33 | for (let i = length - 1; i >= count; --i) { 34 | const value = target.get(vm, [...index, i - Number(count)]); 35 | target.set(vm, value, [...index, i]); 36 | } 37 | for (let i = count - 1n; i >= 0; --i) { 38 | target.set(vm, fill, [...index, Number(i)]); 39 | } 40 | } else if (count < 0) { 41 | for (let i = 0; i < length + Number(count); ++i) { 42 | const value = target.get(vm, [...index, i - Number(count)]); 43 | target.set(vm, value, [...index, i]); 44 | } 45 | for (let i = length + Number(count); i < length; ++i) { 46 | target.set(vm, fill, [...index, i]); 47 | } 48 | } 49 | 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/statement/command/bar.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg3R3(X.expr, X.expr, X.expr); 11 | export default class Bar extends Statement { 12 | public arg: Lazy<[Expr, Expr, Expr]>; 13 | public newline: boolean; 14 | 15 | public constructor(raw: Slice, newline: boolean = false) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | this.newline = newline; 20 | } 21 | 22 | public async *run(vm: VM) { 23 | if (vm.printer.skipDisp) { 24 | return null; 25 | } 26 | 27 | const [valueExpr, maxExpr, lengthExpr] = this.arg.get(); 28 | 29 | const value = await valueExpr.reduce(vm); 30 | assert.bigint(value, "1st argument of BAR must be a number"); 31 | const max = await maxExpr.reduce(vm); 32 | assert.bigint(max, "2nd argument of BAR must be a number"); 33 | const length = await lengthExpr.reduce(vm); 34 | assert.bigint(length, "3rd argument of BAR must be a number"); 35 | 36 | const filled = length * value / max; 37 | const text = "[" + "*".repeat(Number(filled)) + ".".repeat(Number(length - filled)) + "]"; 38 | yield* vm.printer.print(text, new Set()); 39 | 40 | if (this.newline) { 41 | yield* vm.printer.newline(); 42 | } 43 | 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/statement/command/barstr.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg3R3(X.expr, X.expr, X.expr); 11 | export default class BarStr extends Statement { 12 | public arg: Lazy<[Expr, Expr, Expr]>; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const [valueExpr, maxExpr, lengthExpr] = this.arg.get(); 22 | 23 | const value = await valueExpr.reduce(vm); 24 | assert.bigint(value, "1st argument of BAR must be a number"); 25 | const max = await maxExpr.reduce(vm); 26 | assert.bigint(max, "2nd argument of BAR must be a number"); 27 | const length = await lengthExpr.reduce(vm); 28 | assert.bigint(length, "3rd argument of BAR must be a number"); 29 | 30 | const filled = length * value / max; 31 | const result = "[" + "*".repeat(Number(filled)) + ".".repeat(Number(length - filled)) + "]"; 32 | vm.getValue("RESULTS").set(vm, result, [0]); 33 | 34 | return null; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/statement/command/begin.ts: -------------------------------------------------------------------------------- 1 | import Lazy from "../../lazy"; 2 | import * as C from "../../parser/const"; 3 | import * as U from "../../parser/util"; 4 | import Slice from "../../slice"; 5 | import Statement from "../index"; 6 | 7 | const PARSER = U.arg1R1(C.Identifier); 8 | export default class Begin extends Statement { 9 | public arg: Lazy; 10 | 11 | public constructor(raw: Slice) { 12 | super(raw); 13 | 14 | this.arg = new Lazy(raw, PARSER); 15 | } 16 | 17 | // eslint-disable-next-line @typescript-eslint/require-await 18 | public async *run() { 19 | return { 20 | type: "begin", 21 | keyword: this.arg.get(), 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/statement/command/break.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import Statement from "../index"; 4 | 5 | const PARSER = U.arg0R0(); 6 | export default class Break extends Statement { 7 | public constructor(raw: Slice) { 8 | super(raw); 9 | 10 | U.tryParse(PARSER, raw); 11 | } 12 | 13 | // eslint-disable-next-line @typescript-eslint/require-await 14 | public async *run() { 15 | return { 16 | type: "break", 17 | }; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/statement/command/callf.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import Lazy from "../../lazy"; 3 | import Slice from "../../slice"; 4 | import {Leaf} from "../../value"; 5 | import type VM from "../../vm"; 6 | import Expr from "../expr"; 7 | import Statement from "../index"; 8 | import Call from "./call"; 9 | 10 | export default class CallF extends Statement { 11 | public static async *exec(vm: VM, target: string, argExpr: Array) { 12 | const realTarget = target.toUpperCase(); 13 | assert.cond(vm.fnMap.has(realTarget), `Function ${realTarget} does not exist`); 14 | 15 | const arg: Array = []; 16 | for (const a of argExpr) { 17 | arg.push(await a?.reduce(vm)); 18 | } 19 | const result = yield* vm.fnMap.get(realTarget)!.run(vm, arg); 20 | switch (result?.type) { 21 | case "begin": return result; 22 | case "goto": return result; 23 | case "break": return result; 24 | case "continue": return result; 25 | case "throw": return result; 26 | case "return": return null; 27 | case "quit": return result; 28 | case undefined: return null; 29 | } 30 | } 31 | 32 | public arg: Lazy<[string, Array]>; 33 | 34 | public constructor(raw: Slice) { 35 | super(raw); 36 | 37 | this.arg = new Lazy(raw, Call.PARSER); 38 | } 39 | 40 | public async *run(vm: VM) { 41 | const [target, argExpr] = this.arg.get(); 42 | 43 | return yield* CallF.exec(vm, target, argExpr); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/statement/command/callform.ts: -------------------------------------------------------------------------------- 1 | import P from "parsimmon"; 2 | 3 | import * as assert from "../../assert"; 4 | import * as X from "../../parser/expr"; 5 | import * as U from "../../parser/util"; 6 | import Lazy from "../../lazy"; 7 | import Slice from "../../slice"; 8 | import type VM from "../../vm"; 9 | import type Expr from "../expr"; 10 | import Form from "../expr/form"; 11 | import Statement from "../index"; 12 | import Call from "./call"; 13 | 14 | export default class CallForm extends Statement { 15 | public static PARSER(exclude: keyof (typeof X.form)) { 16 | return P.alt<[Form, Array]>( 17 | U.arg1R1(P.seq(X.form[exclude], U.wrap("(", ")", U.sepBy0(",", U.optional(X.expr))))), 18 | U.argNR1(X.form[exclude], U.optional(X.expr)).map(([f, ...r]) => [f, r]), 19 | ); 20 | } 21 | 22 | public arg: Lazy<[Form, Array]>; 23 | 24 | public constructor(raw: Slice) { 25 | super(raw); 26 | 27 | this.arg = new Lazy(raw, CallForm.PARSER("(,")); 28 | } 29 | 30 | public async *run(vm: VM) { 31 | const [targetExpr, argExpr] = this.arg.get(); 32 | const target = await targetExpr.reduce(vm); 33 | assert.string(target, "1st argument of CALLFORM must be a string"); 34 | 35 | return yield* Call.exec(vm, target, argExpr); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/statement/command/callformf.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import Lazy from "../../lazy"; 3 | import Slice from "../../slice"; 4 | import type VM from "../../vm"; 5 | import type Expr from "../expr"; 6 | import Form from "../expr/form"; 7 | import Statement from "../index"; 8 | import CallF from "./callf"; 9 | import CallForm from "./callform"; 10 | 11 | export default class CallFormF extends Statement { 12 | public arg: Lazy<[Form, Array]>; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, CallForm.PARSER("(,")); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const [targetExpr, argExpr] = this.arg.get(); 22 | const target = await targetExpr.reduce(vm); 23 | assert.string(target, "1st argument of CALLFORMF must be a string"); 24 | 25 | return yield* CallF.exec(vm, target, argExpr); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/statement/command/calltrain.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.expr); 11 | export default class CallTrain extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const value = await this.arg.get().reduce(vm); 22 | assert.bigint(value, "Argument of CALLTRAIN must be a number"); 23 | 24 | // vm.getValue("CTRAIN_COUNT").set(vm, value, []); 25 | 26 | return null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/statement/command/cbgclear.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class CbgClear extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("CBGCLEAR"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/cbgclearbutton.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class CbgClearButton extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("CBGCLEARBUTTON"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/cbgremovebmap.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class CbgRemoveBmap extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("CBGREMOVEBMAP"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/chkfont.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.expr); 11 | export default class ChkFont extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const arg = await this.arg.get().reduce(vm); 22 | assert.string(arg, "1st argument of CHKFONT should be a string"); 23 | 24 | const result = vm.external.getFont(arg) ? 1n : 0n; 25 | vm.getValue("RESULT").set(vm, result, [0]); 26 | 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/statement/command/clearbit.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import type Variable from "../expr/variable"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.argNR1(X.variable, X.expr); 12 | export default class ClearBit extends Statement { 13 | public arg: Lazy<[Variable, ...Expr[]]>; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | const [destExpr, ...bitExpr] = this.arg.get(); 23 | 24 | const value = await destExpr.reduce(vm); 25 | assert.bigint(value, "1st argument of CLEARBIT must be a number"); 26 | const bitList: bigint[] = []; 27 | for (let i = 0; i < bitExpr.length; ++i) { 28 | const bit = await bitExpr[i].reduce(vm); 29 | assert.bigint(bit, `${i + 1}th Argument of CLEARBIT must be a number`); 30 | bitList.push(bit); 31 | } 32 | 33 | let result = value; 34 | for (const bit of bitList) { 35 | // eslint-disable-next-line no-bitwise 36 | result &= ~(1n << bit); 37 | } 38 | destExpr.getCell(vm).set(vm, result, await destExpr.reduceIndex(vm)); 39 | 40 | return null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/statement/command/clearline.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.expr); 11 | export default class ClearLine extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const count = await this.arg.get().reduce(vm); 22 | assert.bigint(count, "Argument of CLEARLINE must be an integer!"); 23 | 24 | yield* vm.printer.clear(Number(count)); 25 | 26 | return null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/statement/command/cleartextbox.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class ClearTextBox extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("CLEARTEXTBOX"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/continue.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import Statement from "../index"; 4 | 5 | const PARSER = U.arg0R0(); 6 | export default class Continue extends Statement { 7 | public constructor(raw: Slice) { 8 | super(raw); 9 | 10 | U.tryParse(PARSER, raw); 11 | } 12 | 13 | // eslint-disable-next-line @typescript-eslint/require-await 14 | public async *run() { 15 | return { 16 | type: "continue", 17 | }; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/statement/command/copychara.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type Expr from "../expr"; 7 | import Statement from "../index"; 8 | 9 | const PARSER = U.arg2R2(X.expr, X.expr); 10 | export default class CopyChara extends Statement { 11 | public arg: Lazy<[Expr, Expr]>; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | // eslint-disable-next-line @typescript-eslint/require-await 20 | public async *run() { 21 | throw E.notImpl("COPYCHARA"); 22 | 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/statement/command/cupcheck.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import IntChar1DValue from "../../value/int-char-1d"; 4 | import type VM from "../../vm"; 5 | import Statement from "../index"; 6 | 7 | const PARSER = U.arg0R0(); 8 | export default class CUpCheck extends Statement { 9 | public constructor(raw: Slice) { 10 | super(raw); 11 | 12 | U.tryParse(PARSER, raw); 13 | } 14 | 15 | public async *run(vm: VM) { 16 | const length = Math.min( 17 | vm.getValue("PALAM").length(1), 18 | vm.getValue("CUP").length(1), 19 | vm.getValue("CDOWN").length(1), 20 | ); 21 | 22 | for (let i = 0; i < length; ++i) { 23 | const up = vm.getValue("CUP").get(vm, [i]); 24 | const down = vm.getValue("CDOWN").get(vm, [i]); 25 | const palam = vm.getValue("PALAM").get(vm, [i]); 26 | 27 | if (up <= 0 && down <= 0) { 28 | continue; 29 | } 30 | const result = palam + up - down; 31 | vm.getValue("PALAM").set(vm, result, [i]); 32 | vm.getValue("CUP").set(vm, 0n, [i]); 33 | vm.getValue("CDOWN").set(vm, 0n, [i]); 34 | 35 | if (!vm.printer.skipDisp) { 36 | const name = vm.code.csv.palam.get(i)!; 37 | let text = `${name} ${palam}`; 38 | if (up > 0) { 39 | text += `+${up}`; 40 | } 41 | if (down > 0) { 42 | text += `-${down}`; 43 | } 44 | text += `=${result}`; 45 | 46 | yield* vm.printer.print(text, new Set(["L"])); 47 | } 48 | } 49 | 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/statement/command/currentalign.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class CurrentAlign extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("CURRENTALIGN"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/currentredraw.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class CurrentRedraw extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run(vm: VM) { 16 | vm.getValue("RESULT").set(vm, vm.printer.draw ? 1n : 0n, [0]); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/customdrawline.ts: -------------------------------------------------------------------------------- 1 | import * as C from "../../parser/const"; 2 | import * as U from "../../parser/util"; 3 | import Lazy from "../../lazy"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import Statement from "../index"; 7 | 8 | const PARSER = U.arg1R1(C.charSeq()); 9 | export default class CustomDrawLine extends Statement { 10 | public arg: Lazy; 11 | 12 | public constructor(raw: Slice) { 13 | super(raw); 14 | 15 | this.arg = new Lazy(raw, PARSER); 16 | } 17 | 18 | public async *run(vm: VM) { 19 | const value = this.arg.get(); 20 | 21 | yield* vm.printer.line(value); 22 | 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/statement/command/cvarset.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import type Variable from "../expr/variable"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.arg5R1(X.variable, X.expr, X.expr, X.expr, X.expr); 12 | export default class VarSet extends Statement { 13 | public arg: Lazy<[ 14 | Variable, 15 | Expr | undefined, 16 | Expr | undefined, 17 | Expr | undefined, 18 | Expr | undefined, 19 | ]>; 20 | 21 | public constructor(raw: Slice) { 22 | super(raw); 23 | 24 | this.arg = new Lazy(raw, PARSER); 25 | } 26 | 27 | public async *run(vm: VM) { 28 | const [destExpr, indexExpr, valueExpr, startExpr, endExpr] = this.arg.get(); 29 | 30 | const index = await indexExpr?.reduce(vm) ?? 0n; 31 | assert.bigint(index, "2nd argument of CVARSET must be a number"); 32 | const value = await valueExpr?.reduce(vm); 33 | const start = await startExpr?.reduce(vm) ?? 0n; 34 | assert.bigint(start, "4th argument of CVARSET must be a number"); 35 | const end = await endExpr?.reduce(vm) ?? BigInt(vm.characterList.length); 36 | assert.bigint(end, "5th argument of CVARSET must be a number"); 37 | 38 | for (let i = start; i < end; ++i) { 39 | const character = vm.characterList[Number(i)]; 40 | const cell = character.getValue(destExpr.name); 41 | if (value != null) { 42 | cell.set(vm, value, [Number(index)]); 43 | } else { 44 | if (cell.type === "number") { 45 | cell.set(vm, 0n, [Number(index)]); 46 | } else { 47 | cell.set(vm, "", [Number(index)]); 48 | } 49 | } 50 | } 51 | 52 | return null; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/statement/command/debugclear.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class DebugClear extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("DEBUGCLEAR"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/debugprint.ts: -------------------------------------------------------------------------------- 1 | import * as C from "../../parser/const"; 2 | import * as U from "../../parser/util"; 3 | import Lazy from "../../lazy"; 4 | import {PrintFlag} from "../../printer"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Statement from "../index"; 8 | 9 | const PARSER = U.arg1R0(C.charSeq()).map((str) => str ?? ""); 10 | export default class DebugPrint extends Statement { 11 | public flags: Set; 12 | public value: Lazy; 13 | 14 | public constructor(flags: PrintFlag[], raw: Slice) { 15 | super(raw); 16 | 17 | this.flags = new Set(flags); 18 | this.value = new Lazy(raw, PARSER); 19 | } 20 | 21 | // eslint-disable-next-line @typescript-eslint/require-await 22 | public async *run(_vm: VM) { 23 | // TODO 24 | 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/statement/command/debugprintform.ts: -------------------------------------------------------------------------------- 1 | import * as X from "../../parser/expr"; 2 | import * as U from "../../parser/util"; 3 | import Lazy from "../../lazy"; 4 | import {PrintFlag} from "../../printer"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Form from "../expr/form"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R0(X.form[""]).map((form) => form ?? new Form([{value: ""}])); 11 | export default class DebugPrintForm extends Statement { 12 | public flags: Set; 13 | public arg: Lazy
; 14 | 15 | public constructor(flags: PrintFlag[], raw: Slice) { 16 | super(raw); 17 | 18 | this.flags = new Set(flags); 19 | this.arg = new Lazy(raw, PARSER); 20 | } 21 | 22 | // eslint-disable-next-line @typescript-eslint/require-await 23 | public async *run(_vm: VM) { 24 | // TODO 25 | 26 | return null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/statement/command/delallchara.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class DelAllChara extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("DELALLCHARA"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/delchara.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.argNR0(X.expr); 11 | export default class DelChara extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const arg = this.arg.get(); 22 | const indexList: bigint[] = []; 23 | for (let i = 0; i < arg.length; ++i) { 24 | const index = await arg[i].reduce(vm); 25 | assert.bigint(index, `${i + 1}th argument of DELCHARA should be a number`); 26 | indexList.push(index); 27 | } 28 | indexList.sort(); 29 | indexList.reverse(); 30 | 31 | for (const index of indexList) { 32 | vm.characterList.splice(Number(index), 1); 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/statement/command/deldata.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as E from "../../error"; 3 | import * as X from "../../parser/expr"; 4 | import * as U from "../../parser/util"; 5 | import Lazy from "../../lazy"; 6 | import Slice from "../../slice"; 7 | import type VM from "../../vm"; 8 | import type Expr from "../expr"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.arg1R1(X.expr); 12 | export default class DelData extends Statement { 13 | public arg: Lazy; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | const index = await this.arg.get().reduce(vm); 23 | assert.bigint(index, "Argument of DELDATA must be a number"); 24 | 25 | throw E.notImpl("DELDATA"); 26 | 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/statement/command/drawline.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class DrawLine extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | public async *run(vm: VM) { 15 | yield* vm.printer.line(); 16 | 17 | return null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/statement/command/drawlineform.ts: -------------------------------------------------------------------------------- 1 | import * as X from "../../parser/expr"; 2 | import * as U from "../../parser/util"; 3 | import Lazy from "../../lazy"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import type Form from "../expr/form"; 7 | import Statement from "../index"; 8 | 9 | const PARSER = U.arg1R1(X.form[""]); 10 | export default class DrawLineForm extends Statement { 11 | public arg: Lazy; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | public async *run(vm: VM) { 20 | const value = await this.arg.get().reduce(vm); 21 | yield* vm.printer.line(value); 22 | 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/statement/command/dumprand.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import type VM from "../../vm"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class DumpRand extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run(vm: VM) { 16 | vm.getValue("RANDDATA").set(vm, BigInt(vm.random.state), []); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/encodetouni.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.form[""]); 11 | export default class EncodeToUni extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const value = await this.arg.get().reduce(vm); 22 | assert.string(value, "1st argument of ENCODETOUNI must be a string"); 23 | const buffer = Buffer.from(value, "utf8"); 24 | vm.getValue("RESULT").set(vm, BigInt(buffer.byteLength), [0]); 25 | for (let i = 0; i < buffer.byteLength; ++i) { 26 | vm.getValue("RESULT").set(vm, BigInt(buffer[i]), [i + 1]); 27 | } 28 | 29 | return null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/statement/command/escape.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.expr); 11 | export default class Escape extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const value = await this.arg.get().reduce(vm); 22 | assert.string(value, "1st argument of ESCAPE must be a string"); 23 | 24 | let result = value; 25 | result = result.replace("\\", "\\\\"); 26 | result = result.replace("*", "\\*"); 27 | result = result.replace("+", "\\+"); 28 | result = result.replace("?", "\\?"); 29 | result = result.replace("|", "\\|"); 30 | result = result.replace("{", "\\}"); 31 | result = result.replace("[", "\\["); 32 | result = result.replace("(", "\\("); 33 | result = result.replace(")", "\\)"); 34 | result = result.replace("^", "\\^"); 35 | result = result.replace("$", "\\$"); 36 | result = result.replace(".", "\\."); 37 | result = result.replace("#", "\\#"); 38 | 39 | vm.getValue("RESULTS").set(vm, result, [0]); 40 | 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/statement/command/fontbold.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class FontBold extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run(vm: VM) { 16 | vm.printer.font.bold = !vm.printer.font.bold; 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/fontitalic.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class FontItalic extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run(vm: VM) { 16 | vm.printer.font.italic = !vm.printer.font.italic; 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/fontregular.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class FontRegular extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run(vm: VM) { 16 | vm.printer.font.bold = false; 17 | vm.printer.font.italic = false; 18 | vm.printer.font.strike = false; 19 | vm.printer.font.underline = false; 20 | 21 | return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/statement/command/fontstyle.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.expr); 11 | export default class FontStyle extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const value = await this.arg.get().reduce(vm); 22 | assert.bigint(value, "Argument of FONTSTYLE must be an integer!"); 23 | 24 | /* eslint-disable no-bitwise */ 25 | vm.printer.font.bold = (value & (1n << 0n)) !== 0n; 26 | vm.printer.font.italic = (value & (1n << 1n)) !== 0n; 27 | vm.printer.font.strike = (value & (1n << 2n)) !== 0n; 28 | vm.printer.font.underline = (value & (1n << 3n)) !== 0n; 29 | /* eslint-enable no-bitwise */ 30 | 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/statement/command/forcewait.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class ForceWait extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("FORCEWAIT"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/getexplv.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg2R2(X.expr, X.expr); 11 | export default class GetExpLv extends Statement { 12 | public arg: Lazy<[Expr, Expr]>; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const [valExpr, maxExpr] = this.arg.get(); 22 | 23 | const value = await valExpr.reduce(vm); 24 | assert.bigint(value, "1st argument of GETEXPLV must be a number"); 25 | const max = await maxExpr.reduce(vm); 26 | assert.bigint(max, "2nd argument of GETEXPLV must be a number"); 27 | 28 | let result = max; 29 | for (let i = 0n; i <= max; ++i) { 30 | if (value < vm.getValue("EXPLV").get(vm, [Number(i)])) { 31 | result = i - 1n; 32 | break; 33 | } 34 | } 35 | vm.getValue("RESULT").set(vm, result, [0]); 36 | 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/statement/command/getfont.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class GetFont extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run(vm: VM) { 16 | const result = vm.printer.font.name; 17 | vm.getValue("RESULTS").set(vm, result, [0]); 18 | 19 | return null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/statement/command/getmillisecond.ts: -------------------------------------------------------------------------------- 1 | import dayjs from "dayjs"; 2 | 3 | import * as U from "../../parser/util"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import Statement from "../index"; 7 | 8 | // 719162 is number of days from 0001-01-01 to 1970-01-01 9 | const UNIX_EPOCH = 719162 * 24 * 60 * 60; 10 | const PARSER = U.arg0R0(); 11 | export default class GetMillisecond extends Statement { 12 | public constructor(raw: Slice) { 13 | super(raw); 14 | 15 | U.tryParse(PARSER, raw); 16 | } 17 | 18 | // eslint-disable-next-line @typescript-eslint/require-await 19 | public async *run(vm: VM) { 20 | const time = dayjs(vm.external.getTime()); 21 | 22 | vm.getValue("RESULT").set(vm, BigInt(time.valueOf() - UNIX_EPOCH * 1000), [0]); 23 | 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/statement/command/getpalamlv.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg2R2(X.expr, X.expr); 11 | export default class GetPalamLv extends Statement { 12 | public arg: Lazy<[Expr, Expr]>; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const [valExpr, maxExpr] = this.arg.get(); 22 | 23 | const value = await valExpr.reduce(vm); 24 | assert.bigint(value, "1st argument of GETPALAMLV must be a number"); 25 | const max = await maxExpr.reduce(vm); 26 | assert.bigint(max, "2nd argument of GETPALAMLV must be a number"); 27 | 28 | let result = max; 29 | for (let i = 0n; i <= max; ++i) { 30 | if (value < vm.getValue("PALAMLV").get(vm, [Number(i)])) { 31 | result = i - 1n; 32 | break; 33 | } 34 | } 35 | vm.getValue("RESULT").set(vm, result, [0]); 36 | 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/statement/command/getsecond.ts: -------------------------------------------------------------------------------- 1 | import dayjs from "dayjs"; 2 | 3 | import * as U from "../../parser/util"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import Statement from "../index"; 7 | 8 | // 719162 is number of days from 0001-01-01 to 1970-01-01 9 | const UNIX_EPOCH = 719162 * 24 * 60 * 60; 10 | const PARSER = U.arg0R0(); 11 | export default class GetSecond extends Statement { 12 | public constructor(raw: Slice) { 13 | super(raw); 14 | 15 | U.tryParse(PARSER, raw); 16 | } 17 | 18 | // eslint-disable-next-line @typescript-eslint/require-await 19 | public async *run(vm: VM) { 20 | const time = dayjs(vm.external.getTime()); 21 | 22 | vm.getValue("RESULT").set(vm, BigInt(time.unix() - UNIX_EPOCH), [0]); 23 | 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/statement/command/getstyle.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class GetStyle extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("GETSTYLE"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/gettime.ts: -------------------------------------------------------------------------------- 1 | import dayjs from "dayjs"; 2 | 3 | import * as U from "../../parser/util"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import Statement from "../index"; 7 | 8 | const PARSER = U.arg0R0(); 9 | export default class GetTime extends Statement { 10 | public constructor(raw: Slice) { 11 | super(raw); 12 | 13 | U.tryParse(PARSER, raw); 14 | } 15 | 16 | // eslint-disable-next-line @typescript-eslint/require-await 17 | public async *run(vm: VM) { 18 | const time = dayjs(vm.external.getTime()); 19 | 20 | vm.getValue("RESULT").set(vm, BigInt(parseInt(time.format("YYYYMMDDHHmmssSSS"))), [0]); 21 | vm.getValue("RESULTS").set(vm, time.format("YYYY年MM月DD日 HH:mm:ss"), [0]); 22 | 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/statement/command/goto.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as C from "../../parser/const"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Statement from "../index"; 8 | 9 | const PARSER = U.arg1R1(C.Identifier); 10 | export default class Goto extends Statement { 11 | public static *exec(vm: VM, target: string) { 12 | const realTarget = target.toUpperCase(); 13 | const context = vm.context(); 14 | assert.cond( 15 | context.fn.thunk.labelMap.has(realTarget), 16 | `Label ${realTarget} does not exist`, 17 | ); 18 | 19 | return { 20 | type: "goto", 21 | label: target, 22 | }; 23 | } 24 | 25 | public arg: Lazy; 26 | 27 | public constructor(raw: Slice) { 28 | super(raw); 29 | 30 | this.arg = new Lazy(raw, PARSER); 31 | } 32 | 33 | // eslint-disable-next-line @typescript-eslint/require-await 34 | public async *run(vm: VM) { 35 | const target = this.arg.get(); 36 | return yield* Goto.exec(vm, target); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/statement/command/gotoform.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Form from "../expr/form"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.form[""]); 11 | export default class GotoForm extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const arg = await this.arg.get().reduce(vm); 22 | const target = arg.toUpperCase(); 23 | 24 | const context = vm.context(); 25 | if (!context.fn.thunk.labelMap.has(target)) { 26 | throw E.notFound("Label", target); 27 | } 28 | 29 | return { 30 | type: "goto", 31 | label: target, 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/statement/command/initrand.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class InitRand extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run(vm: VM) { 16 | vm.random.state = Number(vm.getValue("RANDDATA").get(vm, [])); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/input.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as C from "../../parser/const"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Statement, {EraGenerator} from "../index"; 8 | 9 | const PARSER = U.arg1R0(C.Int); 10 | export default class Input extends Statement { 11 | public arg: Lazy; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | public async *run(vm: VM): EraGenerator { 20 | const arg = this.arg.get(); 21 | 22 | const input = yield* vm.printer.input(true, arg != null); 23 | assert.cond(input != null, "Input value for INPUT should be a valid number"); 24 | 25 | let value = Number(input); 26 | if (arg != null && input === "") { 27 | value = arg; 28 | } 29 | assert.number(value, "Input value for INPUT should be a valid number"); 30 | yield* vm.printer.print(value.toString(), new Set(["S"])); 31 | 32 | vm.getValue("RESULT").set(vm, BigInt(value), [0]); 33 | 34 | return null; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/statement/command/inputs.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as C from "../../parser/const"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Statement, {EraGenerator} from "../index"; 8 | 9 | const PARSER = U.arg1R0(C.charSeq()); 10 | export default class InputS extends Statement { 11 | public arg: Lazy; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | public async *run(vm: VM): EraGenerator { 20 | const arg = this.arg.get(); 21 | 22 | let input = yield* vm.printer.input(false, arg != null); 23 | assert.string(input, "Input value for INPUTS should be a valid string"); 24 | if (arg != null && input === "") { 25 | input = arg; 26 | } 27 | yield* vm.printer.print(input, new Set(["S"])); 28 | vm.getValue("RESULTS").set(vm, input, [0]); 29 | 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/statement/command/invertbit.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import type Variable from "../expr/variable"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.argNR1(X.variable, X.expr); 12 | export default class InvertBit extends Statement { 13 | public arg: Lazy<[Variable, ...Expr[]]>; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | const [destExpr, ...bitExpr] = this.arg.get(); 23 | 24 | const value = await destExpr.reduce(vm); 25 | assert.bigint(value, "1st argument of INVERTBIT must be a number"); 26 | const bitList: bigint[] = []; 27 | for (let i = 0; i < bitExpr.length; ++i) { 28 | const bit = await bitExpr[i].reduce(vm); 29 | assert.bigint(bit, `${i + 2}th argument of INVERTBIT must be a number`); 30 | bitList.push(bit); 31 | } 32 | 33 | let result = value; 34 | for (const bit of bitList) { 35 | // eslint-disable-next-line no-bitwise 36 | result ^= 1n << bit; 37 | } 38 | destExpr.getCell(vm).set(vm, result, await destExpr.reduceIndex(vm)); 39 | 40 | return null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/statement/command/isactive.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class IsActive extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("ISACTIVE"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/isskip.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class IsSkip extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("ISSKIP"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/jump.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import Lazy from "../../lazy"; 3 | import Slice from "../../slice"; 4 | import {Leaf} from "../../value"; 5 | import type VM from "../../vm"; 6 | import Expr from "../expr"; 7 | import Statement from "../index"; 8 | import Call from "./call"; 9 | 10 | export default class Jump extends Statement { 11 | public static async *exec(vm: VM, target: string, argExpr: Array) { 12 | const realTarget = target.toUpperCase(); 13 | assert.cond(vm.fnMap.has(realTarget), `Function ${realTarget} does not exist`); 14 | 15 | const arg: Array = []; 16 | for (const a of argExpr) { 17 | arg.push(await a?.reduce(vm)); 18 | } 19 | return yield* vm.fnMap.get(realTarget)!.run(vm, arg); 20 | } 21 | 22 | public arg: Lazy<[string, Array]>; 23 | 24 | public constructor(raw: Slice) { 25 | super(raw); 26 | 27 | this.arg = new Lazy(raw, Call.PARSER); 28 | } 29 | 30 | public async *run(vm: VM) { 31 | const [target, argExpr] = this.arg.get(); 32 | return yield* Jump.exec(vm, target, argExpr); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/statement/command/jumpform.ts: -------------------------------------------------------------------------------- 1 | import Lazy from "../../lazy"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Expr from "../expr"; 5 | import Form from "../expr/form"; 6 | import Statement from "../index"; 7 | import CallForm from "./callform"; 8 | import Jump from "./jump"; 9 | 10 | export default class JumpForm extends Statement { 11 | public arg: Lazy<[Form, Array]>; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, CallForm.PARSER("(")); 17 | } 18 | 19 | public async *run(vm: VM) { 20 | const [targetExpr, argExpr] = this.arg.get(); 21 | const target = await targetExpr.reduce(vm); 22 | return yield* Jump.exec(vm, target, argExpr); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/statement/command/loadgame.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import Statement, {EraGenerator} from "../index"; 4 | 5 | const PARSER = U.arg0R0(); 6 | export default class LoadGame extends Statement { 7 | public constructor(raw: Slice) { 8 | super(raw); 9 | 10 | U.tryParse(PARSER, raw); 11 | } 12 | 13 | // eslint-disable-next-line @typescript-eslint/require-await 14 | public async *run(): EraGenerator { 15 | return { 16 | type: "begin", 17 | keyword: "LOADGAME", 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/mouseskip.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class MouseSkip extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("MOUSESKIP"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/mousex.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class MouseX extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("MOUSEX"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/mousey.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class MouseY extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("mousey"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/oneinput.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as C from "../../parser/const"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Statement, {EraGenerator} from "../index"; 8 | 9 | const PARSER = U.arg1R0(C.Int); 10 | export default class OneInput extends Statement { 11 | public arg: Lazy; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | // TODO: use only the first character of argument 20 | public async *run(vm: VM): EraGenerator { 21 | const arg = this.arg.get(); 22 | 23 | const input = yield* vm.printer.input(true, arg != null); 24 | assert.cond(input != null, "First value of input for ONEINPUT should be a valid number"); 25 | 26 | let value = Number(input[0]); 27 | if (arg != null && input === "") { 28 | value = arg; 29 | } 30 | assert.number(value, "First value of input for ONEINPUT should be a valid number"); 31 | yield* vm.printer.print(value.toString(), new Set(["S"])); 32 | 33 | vm.getValue("RESULT").set(vm, BigInt(value), [0]); 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/statement/command/oneinputs.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as C from "../../parser/const"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Statement, {EraGenerator} from "../index"; 8 | 9 | const PARSER = U.arg1R0(C.charSeq()); 10 | export default class OneInputS extends Statement { 11 | public arg: Lazy; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | public async *run(vm: VM): EraGenerator { 20 | const arg = this.arg.get(); 21 | 22 | let input = yield* vm.printer.input(false, arg != null); 23 | assert.string(input, "Input value for ONEINPUTS should be a valid string"); 24 | if (arg != null && input === "") { 25 | input = arg; 26 | } 27 | yield* vm.printer.print(input, new Set(["S"])); 28 | vm.getValue("RESULTS").set(vm, input[0], [0]); 29 | 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/statement/command/outputlog.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class OutputLog extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("OUTPUTLOG"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/pickupchara.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type Character from "../../character"; 3 | import * as X from "../../parser/expr"; 4 | import * as U from "../../parser/util"; 5 | import Lazy from "../../lazy"; 6 | import Slice from "../../slice"; 7 | import type VM from "../../vm"; 8 | import type Expr from "../expr"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.argNR1(X.expr, X.expr); 12 | export default class PickupChara extends Statement { 13 | public arg: Lazy; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | const argExpr = this.arg.get(); 23 | const arg: bigint[] = []; 24 | for (let i = 0; i < argExpr.length; ++i) { 25 | const value = await argExpr[i].reduce(vm); 26 | assert.bigint(value, `${i + 1}th argument of PICKUPCHARA should be a number`); 27 | assert.cond( 28 | value >= 0 && value < vm.characterList.length, 29 | `${i + 1}th argument of PICKUPCHARA is out of range`, 30 | ); 31 | arg.push(value); 32 | } 33 | 34 | let target = -1n; 35 | let assi = -1n; 36 | let master = -1n; 37 | const characterList: Character[] = []; 38 | for (let i = 0n; i < arg.length; ++i) { 39 | const index = arg[Number(i)]; 40 | if (index === vm.getValue("TARGET").get(vm, [])) { 41 | target = i; 42 | } 43 | if (index === vm.getValue("ASSI").get(vm, [])) { 44 | assi = i; 45 | } 46 | if (index === vm.getValue("MASTER").get(vm, [])) { 47 | master = i; 48 | } 49 | characterList.push(vm.characterList[Number(i)]); 50 | } 51 | vm.getValue("TARGET").set(vm, target, []); 52 | vm.getValue("ASSI").set(vm, assi, []); 53 | vm.getValue("MASTER").set(vm, master, []); 54 | vm.characterList = characterList; 55 | 56 | return null; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/statement/command/print.ts: -------------------------------------------------------------------------------- 1 | import * as C from "../../parser/const"; 2 | import * as U from "../../parser/util"; 3 | import Lazy from "../../lazy"; 4 | import {PrintFlag} from "../../printer"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Statement from "../index"; 8 | 9 | const PARSER = U.arg1R0(C.charSeq()).map((str) => str ?? ""); 10 | export default class Print extends Statement { 11 | public flags: Set; 12 | public value: Lazy; 13 | 14 | public constructor(flags: PrintFlag[], raw: Slice) { 15 | super(raw); 16 | 17 | this.flags = new Set(flags); 18 | this.value = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | if (vm.printer.skipDisp) { 23 | return null; 24 | } 25 | 26 | yield* vm.printer.print(this.value.get(), this.flags); 27 | 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/statement/command/print_shopitem.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import Int1DValue from "../../value/int-1d"; 4 | import Str1DValue from "../../value/str-1d"; 5 | import type VM from "../../vm"; 6 | import Statement from "../index"; 7 | 8 | const PARSER = U.arg0R0(); 9 | export default class PrintShopItem extends Statement { 10 | public constructor(raw: Slice) { 11 | super(raw); 12 | 13 | U.tryParse(PARSER, raw); 14 | } 15 | 16 | public async *run(vm: VM) { 17 | if (vm.printer.skipDisp) { 18 | return null; 19 | } 20 | 21 | const itemName = vm.getValue("ITEMNAME"); 22 | const validItem: number[] = []; 23 | for (let i = 0; i < itemName.length(0); ++i) { 24 | const name = itemName.get(vm, [i]); 25 | if (name !== "") { 26 | validItem.push(i); 27 | } 28 | } 29 | 30 | for (let i = 0; i < validItem.length; ++i) { 31 | const index = validItem[i]; 32 | const name = itemName.get(vm, [index]); 33 | const price = vm.getValue("ITEMPRICE").get(vm, [index]); 34 | const text = `[${index}] ${name}(${price}$)`; 35 | 36 | yield* vm.printer.print(text, new Set(), "LEFT"); 37 | if ((i + 1) % vm.printCPerLine === 0) { 38 | yield* vm.printer.newline(); 39 | } 40 | } 41 | yield* vm.printer.newline(); 42 | 43 | return null; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/statement/command/printbutton.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg2R2(X.expr, X.expr); 11 | export default class PrintButton extends Statement { 12 | public align?: "LEFT" | "RIGHT"; 13 | public arg: Lazy<[Expr, Expr]>; 14 | 15 | public constructor(raw: Slice, align?: PrintButton["align"]) { 16 | super(raw); 17 | 18 | this.align = align; 19 | this.arg = new Lazy(raw, PARSER); 20 | } 21 | 22 | public async *run(vm: VM) { 23 | const [textExpr, valueExpr] = this.arg.get(); 24 | 25 | const text = await textExpr.reduce(vm); 26 | assert.string(text, "1st argument of PRINTBUTTON must be a string"); 27 | const value = await valueExpr.reduce(vm); 28 | 29 | yield* vm.printer.button( 30 | text, 31 | typeof value === "string" ? value : value.toString(), 32 | this.align, 33 | ); 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/statement/command/printc.ts: -------------------------------------------------------------------------------- 1 | import * as C from "../../parser/const"; 2 | import * as U from "../../parser/util"; 3 | import Lazy from "../../lazy"; 4 | import {PrintFlag} from "../../printer"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Statement from "../index"; 8 | 9 | const PARSER = U.arg1R0(C.charSeq()).map((str) => str ?? ""); 10 | export default class PrintC extends Statement { 11 | public align: "LEFT" | "RIGHT"; 12 | public flags: Set; 13 | public value: Lazy; 14 | 15 | public constructor(align: PrintC["align"], flags: PrintFlag[], raw: Slice) { 16 | super(raw); 17 | 18 | this.align = align; 19 | this.flags = new Set(flags); 20 | this.value = new Lazy(raw, PARSER); 21 | } 22 | 23 | public async *run(vm: VM) { 24 | if (vm.printer.skipDisp) { 25 | return null; 26 | } 27 | 28 | const value = this.value.get(); 29 | yield* vm.printer.print(value, this.flags, this.align); 30 | 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/statement/command/printcperline.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import VM from "../../vm"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class PrintCPerLine extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run(vm: VM) { 16 | vm.getValue("RESULT").set(vm, BigInt(vm.printCPerLine), [0]); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/printform.ts: -------------------------------------------------------------------------------- 1 | import * as X from "../../parser/expr"; 2 | import * as U from "../../parser/util"; 3 | import Lazy from "../../lazy"; 4 | import {PrintFlag} from "../../printer"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Form from "../expr/form"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R0(X.form[""]).map((form) => form ?? new Form([{value: ""}])); 11 | export default class PrintForm extends Statement { 12 | public flags: Set; 13 | public arg: Lazy; 14 | 15 | public constructor(flags: PrintFlag[], raw: Slice) { 16 | super(raw); 17 | 18 | this.flags = new Set(flags); 19 | this.arg = new Lazy(raw, PARSER); 20 | } 21 | 22 | public async *run(vm: VM) { 23 | if (vm.printer.skipDisp) { 24 | return null; 25 | } 26 | 27 | const value = await this.arg.get().reduce(vm); 28 | yield* vm.printer.print(value, this.flags); 29 | 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/statement/command/printformc.ts: -------------------------------------------------------------------------------- 1 | import * as X from "../../parser/expr"; 2 | import * as U from "../../parser/util"; 3 | import Lazy from "../../lazy"; 4 | import {PrintFlag} from "../../printer"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Form from "../expr/form"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R0(X.form[""]).map((form) => form ?? new Form([{value: ""}])); 11 | export default class PrintFormC extends Statement { 12 | public align: "LEFT" | "RIGHT"; 13 | public flags: Set; 14 | public arg: Lazy; 15 | 16 | public constructor(align: PrintFormC["align"], flags: PrintFlag[], raw: Slice) { 17 | super(raw); 18 | 19 | this.align = align; 20 | this.flags = new Set(flags); 21 | this.arg = new Lazy(raw, PARSER); 22 | } 23 | 24 | public async *run(vm: VM) { 25 | if (vm.printer.skipDisp) { 26 | return null; 27 | } 28 | 29 | const value = await this.arg.get().reduce(vm); 30 | yield* vm.printer.print(value, this.flags, this.align); 31 | 32 | return null; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/statement/command/printforms.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import {PrintFlag} from "../../printer"; 6 | import Slice from "../../slice"; 7 | import type VM from "../../vm"; 8 | import type Expr from "../expr"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.arg1R1(X.expr); 12 | export default class PrintFormS extends Statement { 13 | public flags: Set; 14 | public arg: Lazy; 15 | 16 | public constructor(flags: PrintFlag[], raw: Slice) { 17 | super(raw); 18 | 19 | this.flags = new Set(flags); 20 | this.arg = new Lazy(raw, PARSER); 21 | } 22 | 23 | public async *run(vm: VM) { 24 | if (vm.printer.skipDisp) { 25 | return null; 26 | } 27 | 28 | const form = await this.arg.get().reduce(vm); 29 | assert.string(form, "1st argument of PRINTFORMS must be a string"); 30 | const text = await X.form[""].tryParse(form).reduce(vm); 31 | assert.string(text, "1st argument of PRINTFORMS must be reduced to a string"); 32 | yield* vm.printer.print(text, this.flags); 33 | 34 | return null; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/statement/command/printplain.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as C from "../../parser/const"; 3 | import * as X from "../../parser/expr"; 4 | import * as U from "../../parser/util"; 5 | import Lazy from "../../lazy"; 6 | import Slice from "../../slice"; 7 | import type VM from "../../vm"; 8 | import type Expr from "../expr"; 9 | import Const from "../expr/const"; 10 | import Statement from "../index"; 11 | 12 | const PARSER_CONST = U.arg1R0(C.charSeq()).map((str) => new Const(str ?? "")); 13 | const PARSER_FORM = U.arg1R0(X.form[""]).map((form) => form ?? new Const("")); 14 | export default class PrintPlain extends Statement { 15 | public arg: Lazy; 16 | 17 | public constructor(postfix: "FORM" | null, raw: Slice) { 18 | super(raw); 19 | 20 | switch (postfix) { 21 | case null: { 22 | this.arg = new Lazy(raw, PARSER_CONST); 23 | break; 24 | } 25 | case "FORM": { 26 | this.arg = new Lazy(raw, PARSER_FORM); 27 | } 28 | } 29 | } 30 | 31 | public async *run(vm: VM) { 32 | if (vm.printer.skipDisp) { 33 | return null; 34 | } 35 | 36 | const text = await this.arg.get().reduce(vm); 37 | assert.string(text, "1st argument of PRINTPLAIN must be a string"); 38 | 39 | yield* vm.printer.print(text, new Set()); 40 | 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/statement/command/prints.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import {PrintFlag} from "../../printer"; 6 | import Slice from "../../slice"; 7 | import type VM from "../../vm"; 8 | import type Expr from "../expr"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.arg1R1(X.expr); 12 | export default class PrintS extends Statement { 13 | public flags: Set; 14 | public arg: Lazy; 15 | 16 | public constructor(flags: PrintFlag[], raw: Slice) { 17 | super(raw); 18 | 19 | this.flags = new Set(flags); 20 | this.arg = new Lazy(raw, PARSER); 21 | } 22 | 23 | public async *run(vm: VM) { 24 | if (vm.printer.skipDisp) { 25 | return null; 26 | } 27 | 28 | const value = await this.arg.get().reduce(vm); 29 | assert.string(value, "1st argument of PRINTS must be a string"); 30 | yield* vm.printer.print(value, this.flags); 31 | 32 | return null; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/statement/command/printv.ts: -------------------------------------------------------------------------------- 1 | import P from "parsimmon"; 2 | 3 | import * as C from "../../parser/const"; 4 | import * as X from "../../parser/expr"; 5 | import * as U from "../../parser/util"; 6 | import Lazy from "../../lazy"; 7 | import {PrintFlag} from "../../printer"; 8 | import Slice from "../../slice"; 9 | import type VM from "../../vm"; 10 | import type Expr from "../expr"; 11 | import Const from "../expr/const"; 12 | import Statement from "../index"; 13 | 14 | const PARSER = U.argNR0(P.alt( 15 | P.string("'").then(C.charSeq(",").map((str) => new Const(str))), 16 | X.expr, 17 | )); 18 | export default class PrintV extends Statement { 19 | public flags: Set; 20 | public value: Lazy; 21 | 22 | public constructor(flags: PrintFlag[], raw: Slice) { 23 | super(raw); 24 | 25 | this.flags = new Set(flags); 26 | this.value = new Lazy(raw, PARSER); 27 | } 28 | 29 | public async *run(vm: VM) { 30 | if (vm.printer.skipDisp) { 31 | return null; 32 | } 33 | 34 | let text = ""; 35 | for (const value of this.value.get()) { 36 | text += (await value.reduce(vm)).toString(); 37 | } 38 | yield* vm.printer.print(text, this.flags); 39 | 40 | return null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/statement/command/putform.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import Str0DValue from "../../value/str-0d"; 7 | import type VM from "../../vm"; 8 | import type Expr from "../expr"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.arg1R1(X.form[""]); 12 | export default class PutForm extends Statement { 13 | public arg: Lazy; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | const value = await this.arg.get().reduce(vm); 23 | assert.string(value, "1st argument of PUTFORM should be a number"); 24 | 25 | const cell = vm.getValue("SAVEDATA_TEXT"); 26 | cell.set(vm, cell.get(vm, []) + value, []); 27 | 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/statement/command/quit.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import Statement from "../index"; 4 | 5 | const PARSER = U.arg0R0(); 6 | export default class Quit extends Statement { 7 | public constructor(raw: Slice) { 8 | super(raw); 9 | 10 | U.tryParse(PARSER, raw); 11 | } 12 | 13 | // eslint-disable-next-line @typescript-eslint/require-await 14 | public async *run() { 15 | return { 16 | type: "quit", 17 | }; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/statement/command/randomize.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.expr); 11 | export default class Randomize extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const seed = await this.arg.get().reduce(vm); 22 | assert.bigint(seed, "1st argument of RANDOMIZE must be a number"); 23 | 24 | vm.random.state = Number(seed); 25 | 26 | return null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/statement/command/redraw.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.expr); 11 | export default class Redraw extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const value = await this.arg.get().reduce(vm); 22 | assert.bigint(value, "Argument of REDRAW must be a number"); 23 | assert.cond(value > 0 && value <= 3, "Argument of REDRAW must be between 0 and 3"); 24 | 25 | switch (value) { 26 | case 0n: vm.printer.draw = false; break; 27 | case 1n: vm.printer.draw = true; break; 28 | case 2n: vm.printer.draw = false; yield* vm.printer.flush(); break; 29 | case 3n: vm.printer.draw = true; yield* vm.printer.flush(); break; 30 | } 31 | 32 | return null; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/statement/command/reset_stain.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import Int1DValue from "../../value/int-1d"; 7 | import type VM from "../../vm"; 8 | import type Expr from "../expr"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.arg1R1(X.expr); 12 | export default class ResetStain extends Statement { 13 | public arg: Lazy; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | const num = await this.arg.get().reduce(vm); 23 | assert.bigint(num, "1st Argument of RESET_STAIN should be an integer"); 24 | 25 | assert.cond(vm.characterList.length > num, `Character #${num} does not exist`); 26 | const character = vm.characterList[Number(num)]; 27 | 28 | character.getValue("STAIN").reset([0, 0, 2, 1, 8]); 29 | 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/statement/command/resetbgcolor.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class ResetBgColor extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run(vm: VM) { 16 | vm.printer.background = vm.printer.defaultBackground; 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/resetcolor.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class ResetColor extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run(vm: VM) { 16 | vm.printer.color = vm.printer.defaultColor; 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/resetdata.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class ResetData extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | public async *run(vm: VM) { 15 | await vm.reset(); 16 | 17 | return null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/statement/command/resetglobal.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class ResetGlobal extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("RESETGLOBAL"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/restart.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Fn from "../../fn"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class Restart extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | return { 17 | type: "goto", 18 | label: Fn.START_OF_FN, 19 | }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/statement/command/return.ts: -------------------------------------------------------------------------------- 1 | import * as X from "../../parser/expr"; 2 | import * as U from "../../parser/util"; 3 | import Lazy from "../../lazy"; 4 | import Slice from "../../slice"; 5 | import type {Leaf} from "../../value"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.argNR0(X.expr); 11 | export default class Return extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const result: Array = []; 22 | for (const expr of this.arg.get()) { 23 | result.push(await expr.reduce(vm)); 24 | } 25 | 26 | return { 27 | type: "return", 28 | value: result, 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/statement/command/returnf.ts: -------------------------------------------------------------------------------- 1 | import * as X from "../../parser/expr"; 2 | import * as U from "../../parser/util"; 3 | import Lazy from "../../lazy"; 4 | import Slice from "../../slice"; 5 | import type {Leaf} from "../../value"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.expr); 11 | export default class ReturnF extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | return { 22 | type: "return", 23 | value: [await this.arg.get().reduce(vm)] as Array, 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/statement/command/reuselastline.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Form from "../expr/form"; 8 | import Statement, {EraGenerator} from "../index"; 9 | 10 | const PARSER = U.arg1R0(X.form[""]); 11 | export default class ReuseLastLine extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM): EraGenerator { 21 | const value = await this.arg.get()?.reduce(vm) ?? ""; 22 | assert.string(value, "Argument of REUSELASTLINE must be a string"); 23 | 24 | yield* vm.printer.print(value, new Set(["S"])); 25 | vm.printer.isLineTemp = true; 26 | 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/statement/command/savegame.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import Statement, {EraGenerator} from "../index"; 4 | 5 | const PARSER = U.arg0R0(); 6 | export default class SaveGame extends Statement { 7 | public constructor(raw: Slice) { 8 | super(raw); 9 | 10 | U.tryParse(PARSER, raw); 11 | } 12 | 13 | // eslint-disable-next-line @typescript-eslint/require-await 14 | public async *run(): EraGenerator { 15 | return { 16 | type: "begin", 17 | keyword: "SAVEGAME", 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/setbgcolor.ts: -------------------------------------------------------------------------------- 1 | import P from "parsimmon"; 2 | 3 | import * as assert from "../../assert"; 4 | import * as X from "../../parser/expr"; 5 | import * as U from "../../parser/util"; 6 | import Lazy from "../../lazy"; 7 | import Slice from "../../slice"; 8 | import type VM from "../../vm"; 9 | import type Expr from "../expr"; 10 | import Statement from "../index"; 11 | 12 | const PARSER = P.alt( 13 | U.arg3R3(X.expr, X.expr, X.expr), 14 | U.arg1R1(X.expr), 15 | ); 16 | export default class SetBgColor extends Statement { 17 | public arg: Lazy; 18 | 19 | public constructor(raw: Slice) { 20 | super(raw); 21 | 22 | this.arg = new Lazy(raw, PARSER); 23 | } 24 | 25 | public async *run(vm: VM) { 26 | const parsed = this.arg.get(); 27 | let color: string; 28 | if (Array.isArray(parsed)) { 29 | const r = await parsed[0].reduce(vm); 30 | const g = await parsed[1].reduce(vm); 31 | const b = await parsed[2].reduce(vm); 32 | assert.bigint(r, "1st argument of SETBGCOLOR must be an integer"); 33 | assert.bigint(g, "2nd argument of SETBGCOLOR must be an integer"); 34 | assert.bigint(b, "3rd argument of SETBGCOLOR must be an integer"); 35 | color = 36 | r.toString(16).padStart(2, "0") + 37 | g.toString(16).padStart(2, "0") + 38 | b.toString(16).padStart(2, "0"); 39 | } else { 40 | const rgb = await parsed.reduce(vm); 41 | assert.bigint(rgb, "Argument of SETBGCOLOR must be an integer"); 42 | color = rgb.toString(16).padStart(6, "0"); 43 | } 44 | 45 | vm.printer.background = color; 46 | 47 | return null; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/statement/command/setbgcolorbyname.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as C from "../../parser/const"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Statement from "../index"; 8 | 9 | const PARSER = U.arg1R1(C.charSeq()); 10 | export default class SetBgColorByName extends Statement { 11 | public arg: Lazy; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | // eslint-disable-next-line @typescript-eslint/require-await 20 | public async *run(_vm: VM) { 21 | throw E.notImpl("SETBGCOLORBYNAME"); 22 | 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/statement/command/setbit.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import type Variable from "../expr/variable"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.argNR1(X.variable, X.expr); 12 | export default class SetBit extends Statement { 13 | public arg: Lazy<[Variable, ...Expr[]]>; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | const [destExpr, ...bitExpr] = this.arg.get(); 23 | 24 | const dest = destExpr.getCell(vm); 25 | const value = await destExpr.reduce(vm); 26 | assert.bigint(value, "1st argument of SETBIT must be a number"); 27 | const bitList: bigint[] = []; 28 | for (let i = 0; i < bitExpr.length; ++i) { 29 | const bit = await bitExpr[i].reduce(vm); 30 | assert.bigint(bit, `${i + 2}th argument of INVERTBIT must be a number`); 31 | bitList.push(bit); 32 | } 33 | 34 | let result = value; 35 | for (const bit of bitList) { 36 | // eslint-disable-next-line no-bitwise 37 | result |= 1n << bit; 38 | } 39 | 40 | dest.set(vm, result, await destExpr.reduceIndex(vm)); 41 | 42 | return null; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/statement/command/setcolor.ts: -------------------------------------------------------------------------------- 1 | import P from "parsimmon"; 2 | 3 | import * as assert from "../../assert"; 4 | import * as X from "../../parser/expr"; 5 | import * as U from "../../parser/util"; 6 | import Lazy from "../../lazy"; 7 | import Slice from "../../slice"; 8 | import type VM from "../../vm"; 9 | import type Expr from "../expr"; 10 | import Statement from "../index"; 11 | 12 | const PARSER = P.alt( 13 | U.arg3R3(X.expr, X.expr, X.expr), 14 | U.arg1R1(X.expr), 15 | ); 16 | export default class SetColor extends Statement { 17 | public arg: Lazy; 18 | 19 | public constructor(raw: Slice) { 20 | super(raw); 21 | 22 | this.arg = new Lazy(raw, PARSER); 23 | } 24 | 25 | public async *run(vm: VM) { 26 | const parsed = this.arg.get(); 27 | let color: string; 28 | if (Array.isArray(parsed)) { 29 | const r = await parsed[0].reduce(vm); 30 | const g = await parsed[1].reduce(vm); 31 | const b = await parsed[2].reduce(vm); 32 | assert.bigint(r, "1st argument of SETCOLOR must be an integer"); 33 | assert.bigint(g, "2nd argument of SETCOLOR must be an integer"); 34 | assert.bigint(b, "3rd argument of SETCOLOR must be an integer"); 35 | color = 36 | r.toString(16).padStart(2, "0") + 37 | g.toString(16).padStart(2, "0") + 38 | b.toString(16).padStart(2, "0"); 39 | } else { 40 | const rgb = await parsed.reduce(vm); 41 | assert.bigint(rgb, "Argument of SETCOLOR must be an integer"); 42 | color = rgb.toString(16).padStart(6, "0"); 43 | } 44 | 45 | vm.printer.color = color; 46 | 47 | return null; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/statement/command/setcolorbyname.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as C from "../../parser/const"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Statement from "../index"; 8 | 9 | const PARSER = U.arg1R1(C.charSeq()); 10 | export default class SetColorByName extends Statement { 11 | public arg: Lazy; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | // eslint-disable-next-line @typescript-eslint/require-await 20 | public async *run(_vm: VM) { 21 | throw E.notImpl("SETCOLORBYNAME"); 22 | 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/statement/command/setfont.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R0(X.expr); 11 | export default class SetFont extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | // TODO: use default font 22 | const font = await this.arg.get()?.reduce(vm) ?? ""; 23 | assert.string(font, "Argument of SETFONT must be a string"); 24 | 25 | vm.printer.font.name = font; 26 | 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/statement/command/skipdisp.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.expr); 11 | export default class SkipDisp extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const value = await this.arg.get().reduce(vm); 22 | assert.bigint(value, "Argument of SKIPDISP must be a number"); 23 | 24 | vm.printer.skipDisp = value !== 0n; 25 | 26 | return null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/statement/command/split.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import type Variable from "../expr/variable"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.arg3R3(X.expr, X.expr, X.variable); 12 | export default class Split extends Statement { 13 | public arg: Lazy<[Expr, Expr, Variable]>; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | const [valueExpr, sepExpr, destExpr] = this.arg.get(); 23 | 24 | const value = await valueExpr.reduce(vm); 25 | assert.string(value, "1st argument of SPLIT must be a string!"); 26 | const sep = await sepExpr.reduce(vm); 27 | assert.string(sep, "2nd argument of SPLIT must be a number!"); 28 | const dest = destExpr.getCell(vm); 29 | const index = await destExpr.reduceIndex(vm); 30 | 31 | const chunkList = value.split(sep); 32 | for (let i = 0; i < chunkList.length; ++i) { 33 | dest.set(vm, chunkList[i], [...index, i]); 34 | } 35 | vm.getValue("RESULT").set(vm, BigInt(chunkList.length), [0]); 36 | 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/statement/command/stopcalltrain.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class StopCallTrain extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("STOPCALLTRAIN"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/command/strfind.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg2R2(X.expr, X.expr); 11 | export default class StrFind extends Statement { 12 | public arg: Lazy<[Expr, Expr]>; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const [valueExpr, searchExpr] = this.arg.get(); 22 | 23 | const value = await valueExpr.reduce(vm); 24 | assert.string(value, "1st argument of STRFIND must be a string!"); 25 | const search = await searchExpr.reduce(vm); 26 | assert.string(search, "2nd argument of STRFIND must be a string!"); 27 | vm.getValue("RESULT").set(vm, BigInt(value.indexOf(search)), [0]); 28 | 29 | return null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/statement/command/strfindu.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg2R2(X.expr, X.expr); 11 | export default class StrFindU extends Statement { 12 | public arg: Lazy<[Expr, Expr]>; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const [valueExpr, searchExpr] = this.arg.get(); 22 | 23 | const value = await valueExpr.reduce(vm); 24 | assert.string(value, "1st argument of STRFINDU must be a string!"); 25 | const search = await searchExpr.reduce(vm); 26 | assert.string(search, "2nd argument of STRFINDU must be a string!"); 27 | // TODO: unicode 28 | vm.getValue("RESULT").set(vm, BigInt(value.indexOf(search)), [0]); 29 | 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/statement/command/strlen.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as C from "../../parser/const"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Statement from "../index"; 8 | 9 | const PARSER = U.arg1R1(C.charSeq()); 10 | export default class StrLen extends Statement { 11 | public arg: Lazy; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | // eslint-disable-next-line @typescript-eslint/require-await 20 | public async *run(vm: VM) { 21 | const value = this.arg.get(); 22 | assert.string(value, "Argument of STRLEN must be a string!"); 23 | vm.getValue("RESULT").set(vm, BigInt(value.length), [0]); 24 | 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/statement/command/strlenform.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Form from "../expr/form"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.form[""]); 11 | export default class StrLenForm extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const value = await this.arg.get().reduce(vm); 22 | assert.string(value, "Argument of STRLENFORM must be a string!"); 23 | vm.getValue("RESULT").set(vm, BigInt(value.length), [0]); 24 | 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/statement/command/strlenformu.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Form from "../expr/form"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.form[""]); 11 | export default class StrLenFormU extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const value = await this.arg.get().reduce(vm); 22 | assert.string(value, "Argument of STRLENFORMU must be a string!"); 23 | vm.getValue("RESULT").set(vm, BigInt(value.length), [0]); 24 | 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/statement/command/strlenu.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as C from "../../parser/const"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import Statement from "../index"; 8 | 9 | const PARSER = U.arg1R1(C.charSeq()); 10 | export default class StrLen extends Statement { 11 | public arg: Lazy; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | // eslint-disable-next-line @typescript-eslint/require-await 20 | public async *run(vm: VM) { 21 | const value = this.arg.get(); 22 | assert.string(value, "Argument of STRLENU must be a string!"); 23 | vm.getValue("RESULT").set(vm, BigInt(value.length), [0]); 24 | 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/statement/command/substring.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg3R3(X.expr, X.expr, X.expr); 11 | export default class Substring extends Statement { 12 | public arg: Lazy<[Expr, Expr, Expr]>; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const [valueExpr, startExpr, endExpr] = this.arg.get(); 22 | 23 | const value = await valueExpr.reduce(vm); 24 | assert.string(value, "1st argument of SUBSTRING must be a string!"); 25 | const start = await startExpr.reduce(vm); 26 | assert.bigint(start, "2nd argument of SUBSTRING must be a number!"); 27 | const end = await endExpr.reduce(vm); 28 | assert.bigint(end, "3rd argument of SUBSTRING must be a number!"); 29 | if (end < 0) { 30 | vm.getValue("RESULTS").set(vm, value.slice(Number(start)), [0]); 31 | } else { 32 | vm.getValue("RESULTS").set(vm, value.slice(Number(start), Number(end)), [0]); 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/statement/command/substringu.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg3R3(X.expr, X.expr, X.expr); 11 | export default class SubstringU extends Statement { 12 | public arg: Lazy<[Expr, Expr, Expr]>; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const [valueExpr, startExpr, endExpr] = this.arg.get(); 22 | 23 | const value = await valueExpr.reduce(vm); 24 | assert.string(value, "1st argument of SUBSTRINGU must be a string!"); 25 | const start = await startExpr.reduce(vm); 26 | assert.bigint(start, "2nd argument of SUBSTRINGU must be a number!"); 27 | const end = await endExpr.reduce(vm); 28 | assert.bigint(end, "3rd argument of SUBSTRINGU must be a number!"); 29 | if (end < 0) { 30 | vm.getValue("RESULTS").set(vm, value.slice(Number(start)), [0]); 31 | } else { 32 | vm.getValue("RESULTS").set(vm, value.slice(Number(start), Number(end)), [0]); 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/statement/command/swap.ts: -------------------------------------------------------------------------------- 1 | import * as X from "../../parser/expr"; 2 | import * as U from "../../parser/util"; 3 | import Lazy from "../../lazy"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import type Variable from "../expr/variable"; 7 | import Statement from "../index"; 8 | 9 | const PARSER = U.arg2R2(X.variable, X.variable); 10 | export default class Swap extends Statement { 11 | public arg: Lazy<[Variable, Variable]>; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | public async *run(vm: VM) { 20 | const [leftExpr, rightExpr] = this.arg.get(); 21 | const left = leftExpr.getCell(vm); 22 | const leftIndex = await leftExpr.reduceIndex(vm); 23 | const right = rightExpr.getCell(vm); 24 | const rightIndex = await rightExpr.reduceIndex(vm); 25 | 26 | const leftValue = left.get(vm, leftIndex); 27 | const rightValue = right.get(vm, rightIndex); 28 | left.set(vm, rightValue, leftIndex); 29 | right.set(vm, leftValue, rightIndex); 30 | 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/statement/command/swapchara.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg2R2(X.expr, X.expr); 11 | export default class SwapChara extends Statement { 12 | public arg: Lazy<[Expr, Expr]>; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const [leftExpr, rightExpr] = this.arg.get(); 22 | const left = await leftExpr.reduce(vm); 23 | assert.bigint(left, "1st argument of SWAPCHARA must be a number"); 24 | const right = await rightExpr.reduce(vm); 25 | assert.bigint(right, "2nd argument of SWAPCHARA must be a number"); 26 | 27 | const temp = vm.characterList[Number(left)]; 28 | vm.characterList[Number(left)] = vm.characterList[Number(right)]; 29 | vm.characterList[Number(right)] = temp; 30 | 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/statement/command/throw.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import Statement from "../index"; 9 | 10 | const PARSER = U.arg1R1(X.form[""]); 11 | export default class Throw extends Statement { 12 | public arg: Lazy; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM) { 21 | const value = await this.arg.get().reduce(vm); 22 | assert.string(value, "Argument of THROW must be a string"); 23 | 24 | return { 25 | type: "throw", 26 | value, 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/statement/command/times.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as C from "../../parser/const"; 3 | import * as X from "../../parser/expr"; 4 | import * as U from "../../parser/util"; 5 | import Lazy from "../../lazy"; 6 | import Slice from "../../slice"; 7 | import type VM from "../../vm"; 8 | import type Variable from "../expr/variable"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.arg2R2(X.variable, C.Float); 12 | export default class Times extends Statement { 13 | public arg: Lazy<[Variable, number]>; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | const [dest, value] = this.arg.get(); 23 | 24 | const original = await dest.reduce(vm); 25 | assert.bigint(original, "1st argument of TIMES must be a number"); 26 | const index = await dest.reduceIndex(vm); 27 | 28 | let result = 0n; 29 | let remaining = value; 30 | for (let i = 0; i < 100; ++i) { 31 | if (remaining === 0) { 32 | break; 33 | } 34 | const high = Math.floor(remaining); 35 | result += original * BigInt(high) / (100n ** BigInt(i)); 36 | remaining = (remaining - high) * 100; 37 | } 38 | dest.getCell(vm).set(vm, result, index); 39 | 40 | return null; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/statement/command/tinput.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as C from "../../parser/const"; 3 | import * as X from "../../parser/expr"; 4 | import * as U from "../../parser/util"; 5 | import Lazy from "../../lazy"; 6 | import Slice from "../../slice"; 7 | import type Expr from "../expr"; 8 | import type VM from "../../vm"; 9 | import Statement, {EraGenerator} from "../index"; 10 | 11 | const PARSER = U.arg4R2(X.expr, X.expr, X.expr, C.charSeq()); 12 | export default class TInput extends Statement { 13 | public arg: Lazy<[Expr, Expr, Expr | undefined, string | undefined]>; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM): EraGenerator { 22 | const [timeoutExpr, defExpr, showExpr, message] = this.arg.get(); 23 | const timeout = await timeoutExpr.reduce(vm); 24 | assert.bigint(timeout, "1st argument of TINPUT should be a number"); 25 | const def = await defExpr.reduce(vm); 26 | assert.bigint(def, "2nd argument of TINPUT should be a number"); 27 | const show = await showExpr?.reduce(vm) ?? 0n; 28 | assert.bigint(show, "3rd argument of TINPUT should be a number"); 29 | 30 | const input = yield* vm.printer.tinput(true, Number(timeout), show === 1n); 31 | 32 | let value: bigint; 33 | if (input == null) { 34 | if (message != null) { 35 | yield* vm.printer.print(message, new Set(["S"])); 36 | } 37 | value = def; 38 | } else { 39 | value = BigInt(input); 40 | } 41 | yield* vm.printer.print(value.toString(), new Set(["S"])); 42 | 43 | vm.getValue("RESULT").set(vm, value, [0]); 44 | 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/statement/command/tinputs.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as C from "../../parser/const"; 3 | import * as X from "../../parser/expr"; 4 | import * as U from "../../parser/util"; 5 | import Lazy from "../../lazy"; 6 | import Slice from "../../slice"; 7 | import type Expr from "../expr"; 8 | import type VM from "../../vm"; 9 | import Statement, {EraGenerator} from "../index"; 10 | 11 | const PARSER = U.arg4R2(X.expr, X.expr, X.expr, C.charSeq()); 12 | export default class TInputS extends Statement { 13 | public arg: Lazy<[Expr, Expr, Expr | undefined, string | undefined]>; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM): EraGenerator { 22 | const [timeoutExpr, defExpr, showExpr, message] = this.arg.get(); 23 | const timeout = await timeoutExpr.reduce(vm); 24 | assert.bigint(timeout, "1st argument of TINPUTS should be a number"); 25 | const def = await defExpr.reduce(vm); 26 | assert.string(def, "2nd argument of TINPUTS should be a string"); 27 | const show = await showExpr?.reduce(vm) ?? 0n; 28 | assert.bigint(show, "3rd argument of TINPUTS should be a number"); 29 | 30 | const input = yield* vm.printer.tinput(false, Number(timeout), show === 1n); 31 | 32 | let value: string; 33 | if (input == null) { 34 | if (message != null) { 35 | yield* vm.printer.print(message, new Set(["S"])); 36 | } 37 | value = def; 38 | } else { 39 | value = input; 40 | } 41 | yield* vm.printer.print(value, new Set(["S"])); 42 | 43 | vm.getValue("RESULTS").set(vm, value, [0]); 44 | 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/statement/command/toneinput.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as C from "../../parser/const"; 3 | import * as X from "../../parser/expr"; 4 | import * as U from "../../parser/util"; 5 | import Lazy from "../../lazy"; 6 | import Slice from "../../slice"; 7 | import type Expr from "../expr"; 8 | import type VM from "../../vm"; 9 | import Statement, {EraGenerator} from "../index"; 10 | 11 | const PARSER = U.arg4R2(X.expr, X.expr, X.expr, C.charSeq()); 12 | export default class TOneInput extends Statement { 13 | public arg: Lazy<[Expr, Expr, Expr | undefined, string | undefined]>; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM): EraGenerator { 22 | const [timeoutExpr, defExpr, showExpr, message] = this.arg.get(); 23 | const timeout = await timeoutExpr.reduce(vm); 24 | assert.bigint(timeout, "1st argument of TONEINPUT should be a number"); 25 | const def = await defExpr.reduce(vm); 26 | assert.bigint(def, "2nd argument of TONEINPUT should be a number"); 27 | const show = await showExpr?.reduce(vm) ?? 0n; 28 | assert.bigint(show, "3rd argument of TONEINPUT should be a number"); 29 | 30 | const input = yield* vm.printer.tinput(true, Number(timeout), show === 1n); 31 | 32 | let value: bigint; 33 | if (input == null) { 34 | if (message != null) { 35 | yield* vm.printer.print(message, new Set(["S"])); 36 | } 37 | value = def; 38 | } else { 39 | value = BigInt(input[0]); 40 | } 41 | yield* vm.printer.print(value.toString(), new Set(["S"])); 42 | 43 | vm.getValue("RESULT").set(vm, value, [0]); 44 | 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/statement/command/toneinputs.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as C from "../../parser/const"; 3 | import * as X from "../../parser/expr"; 4 | import * as U from "../../parser/util"; 5 | import Lazy from "../../lazy"; 6 | import Slice from "../../slice"; 7 | import type Expr from "../expr"; 8 | import type VM from "../../vm"; 9 | import Statement, {EraGenerator} from "../index"; 10 | 11 | const PARSER = U.arg4R2(X.expr, X.expr, X.expr, C.charSeq()); 12 | export default class TOneInputS extends Statement { 13 | public arg: Lazy<[Expr, Expr, Expr | undefined, string | undefined]>; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM): EraGenerator { 22 | const [timeoutExpr, defExpr, showExpr, message] = this.arg.get(); 23 | const timeout = await timeoutExpr.reduce(vm); 24 | assert.bigint(timeout, "1st argument of TONEINPUTS should be a number"); 25 | const def = await defExpr.reduce(vm); 26 | assert.string(def, "2nd argument of TONEINPUTS should be a string"); 27 | const show = await showExpr?.reduce(vm) ?? 0n; 28 | assert.bigint(show, "3rd argument of TONEINPUTS should be a number"); 29 | 30 | const input = yield* vm.printer.tinput(false, Number(timeout), show === 1n); 31 | 32 | let value: string; 33 | if (input == null) { 34 | if (message != null) { 35 | yield* vm.printer.print(message, new Set(["S"])); 36 | } 37 | value = def; 38 | } else { 39 | value = input; 40 | } 41 | yield* vm.printer.print(value, new Set(["S"])); 42 | 43 | vm.getValue("RESULTS").set(vm, value, [0]); 44 | 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/statement/command/trycall.ts: -------------------------------------------------------------------------------- 1 | import Lazy from "../../lazy"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | import Call from "./call"; 6 | 7 | export default class TryCall extends Statement { 8 | public arg: Call["arg"]; 9 | 10 | public constructor(raw: Slice) { 11 | super(raw); 12 | 13 | this.arg = new Lazy(raw, Call.PARSER); 14 | } 15 | 16 | public async *run(vm: VM) { 17 | const [target, argExpr] = this.arg.get(); 18 | const realTarget = target.toUpperCase(); 19 | if (vm.fnMap.has(realTarget)) { 20 | return yield* Call.exec(vm, realTarget, argExpr); 21 | } 22 | 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/statement/command/trycallform.ts: -------------------------------------------------------------------------------- 1 | import Lazy from "../../lazy"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | import Call from "./call"; 6 | import CallForm from "./callform"; 7 | 8 | export default class TryCallForm extends Statement { 9 | public arg: CallForm["arg"]; 10 | 11 | public constructor(raw: Slice) { 12 | super(raw); 13 | 14 | this.arg = new Lazy(raw, CallForm.PARSER("(,")); 15 | } 16 | 17 | public async *run(vm: VM) { 18 | const [targetExpr, argExpr] = this.arg.get(); 19 | const target = (await targetExpr.reduce(vm)).toUpperCase(); 20 | if (vm.fnMap.has(target)) { 21 | return yield* Call.exec(vm, target, argExpr); 22 | } 23 | 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/statement/command/tryccall.ts: -------------------------------------------------------------------------------- 1 | import {parseThunk} from "../../parser/erb"; 2 | import Lazy from "../../lazy"; 3 | import Slice from "../../slice"; 4 | import type Thunk from "../../thunk"; 5 | import type VM from "../../vm"; 6 | import Statement from "../index"; 7 | import Call from "./call"; 8 | 9 | const CATCH = /^CATCH$/i; 10 | const ENDCATCH = /^ENDCATCH$/i; 11 | export default class TryCCall extends Statement { 12 | public static parse(arg: Slice, lines: Slice[], from: number): [TryCCall, number] { 13 | let index = from + 1; 14 | 15 | const [thenThunk, consumedT] = parseThunk(lines, index, (l) => CATCH.test(l)); 16 | index += consumedT + 1; 17 | 18 | const [catchThunk, consumedC] = parseThunk(lines, index, (l) => ENDCATCH.test(l)); 19 | index += consumedC + 1; 20 | 21 | return [new TryCCall(arg, thenThunk, catchThunk), index - from]; 22 | } 23 | 24 | public arg: Call["arg"]; 25 | public thenThunk: Thunk; 26 | public catchThunk: Thunk; 27 | 28 | public constructor(raw: Slice, thenThunk: Thunk, catchThunk: Thunk) { 29 | super(raw); 30 | 31 | this.arg = new Lazy(raw, Call.PARSER); 32 | this.thenThunk = thenThunk; 33 | this.catchThunk = catchThunk; 34 | } 35 | 36 | public async *run(vm: VM, label?: string) { 37 | if (label != null && this.thenThunk.labelMap.has(label)) { 38 | return yield* this.thenThunk.run(vm, label); 39 | } 40 | if (label != null && this.catchThunk.labelMap.has(label)) { 41 | return yield* this.catchThunk.run(vm, label); 42 | } 43 | 44 | const [target, argExpr] = this.arg.get(); 45 | const realTarget = target.toUpperCase(); 46 | if (vm.fnMap.has(realTarget)) { 47 | yield* Call.exec(vm, realTarget, argExpr); 48 | return yield* this.thenThunk.run(vm, label); 49 | } else { 50 | return yield* this.catchThunk.run(vm, label); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/statement/command/tryccallform.ts: -------------------------------------------------------------------------------- 1 | import {parseThunk} from "../../parser/erb"; 2 | import Lazy from "../../lazy"; 3 | import Slice from "../../slice"; 4 | import type Thunk from "../../thunk"; 5 | import type VM from "../../vm"; 6 | import Statement from "../index"; 7 | import Call from "./call"; 8 | import CallForm from "./callform"; 9 | 10 | const CATCH = /^CATCH$/i; 11 | const ENDCATCH = /^ENDCATCH$/i; 12 | export default class TryCCallForm extends Statement { 13 | public static parse(arg: Slice, lines: Slice[], from: number): [TryCCallForm, number] { 14 | let index = from + 1; 15 | 16 | const [thenThunk, consumedT] = parseThunk(lines, index, (l) => CATCH.test(l)); 17 | index += consumedT + 1; 18 | 19 | const [catchThunk, consumedC] = parseThunk(lines, index, (l) => ENDCATCH.test(l)); 20 | index += consumedC + 1; 21 | 22 | return [new TryCCallForm(arg, thenThunk, catchThunk), index - from]; 23 | } 24 | 25 | public arg: CallForm["arg"]; 26 | public thenThunk: Thunk; 27 | public catchThunk: Thunk; 28 | 29 | public constructor(raw: Slice, thenThunk: Thunk, catchThunk: Thunk) { 30 | super(raw); 31 | 32 | this.arg = new Lazy(raw, CallForm.PARSER("")); 33 | this.thenThunk = thenThunk; 34 | this.catchThunk = catchThunk; 35 | } 36 | 37 | public async *run(vm: VM, label?: string) { 38 | if (label != null && this.thenThunk.labelMap.has(label)) { 39 | return yield* this.thenThunk.run(vm, label); 40 | } 41 | if (label != null && this.catchThunk.labelMap.has(label)) { 42 | return yield* this.catchThunk.run(vm, label); 43 | } 44 | 45 | const [targetExpr, argExpr] = this.arg.get(); 46 | const target = (await targetExpr.reduce(vm)).toUpperCase(); 47 | if (vm.fnMap.has(target)) { 48 | yield* Call.exec(vm, target, argExpr); 49 | return yield* this.thenThunk.run(vm, label); 50 | } else { 51 | return yield* this.catchThunk.run(vm, label); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/statement/command/trycgoto.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import {parseThunk} from "../../parser/erb"; 3 | import * as C from "../../parser/const"; 4 | import * as U from "../../parser/util"; 5 | import Lazy from "../../lazy"; 6 | import Slice from "../../slice"; 7 | import type Thunk from "../../thunk"; 8 | import type VM from "../../vm"; 9 | import Statement from "../index"; 10 | import Goto from "./goto"; 11 | 12 | const CATCH = /^CATCH$/i; 13 | const ENDCATCH = /^ENDCATCH$/i; 14 | const PARSER = U.arg1R1(C.Identifier); 15 | export default class TryCGoto extends Statement { 16 | public static parse(arg: Slice, lines: Slice[], from: number): [TryCGoto, number] { 17 | let index = from + 1; 18 | if (lines.length <= index) { 19 | throw E.parser("Unexpected end of thunk in TRYCGOTO expression"); 20 | } else if (!CATCH.test(lines[index].content)) { 21 | throw E.parser("Could not find CATCH for TRYCGOTO expression"); 22 | } 23 | index += 1; 24 | 25 | const [catchThunk, consumed] = parseThunk(lines, index, (l) => ENDCATCH.test(l)); 26 | index += consumed + 1; 27 | 28 | return [new TryCGoto(arg, catchThunk), index - from]; 29 | } 30 | 31 | public arg: Lazy; 32 | public catchThunk: Thunk; 33 | 34 | public constructor(raw: Slice, catchThunk: Thunk) { 35 | super(raw); 36 | 37 | this.arg = new Lazy(raw, PARSER); 38 | this.catchThunk = catchThunk; 39 | } 40 | 41 | public async *run(vm: VM, label?: string) { 42 | const target = this.arg.get().toUpperCase(); 43 | const context = vm.context(); 44 | if (context.fn.thunk.labelMap.has(target)) { 45 | return yield* Goto.exec(vm, target); 46 | } else { 47 | return yield* this.catchThunk.run(vm, label); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/statement/command/trycgotoform.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import {parseThunk} from "../../parser/erb"; 3 | import * as X from "../../parser/expr"; 4 | import * as U from "../../parser/util"; 5 | import Lazy from "../../lazy"; 6 | import Slice from "../../slice"; 7 | import type Thunk from "../../thunk"; 8 | import type VM from "../../vm"; 9 | import type Form from "../expr/form"; 10 | import Statement from "../index"; 11 | import Goto from "./goto"; 12 | 13 | const CATCH = /^CATCH$/i; 14 | const ENDCATCH = /^ENDCATCH$/i; 15 | const PARSER = U.arg1R1(X.form[""]); 16 | export default class TryCGotoForm extends Statement { 17 | public static parse(arg: Slice, lines: Slice[], from: number): [TryCGotoForm, number] { 18 | let index = from + 1; 19 | if (lines.length <= index) { 20 | throw E.parser("Unexpected end of thunk in TRYCGOTOFORM expression"); 21 | } else if (!CATCH.test(lines[index].content)) { 22 | throw E.parser("Could not find CATCH for TRYCGOTOFORM expression"); 23 | } 24 | index += 1; 25 | 26 | const [catchThunk, consumed] = parseThunk(lines, index, (l) => ENDCATCH.test(l)); 27 | index += consumed + 1; 28 | 29 | return [new TryCGotoForm(arg, catchThunk), index - from]; 30 | } 31 | 32 | public arg: Lazy; 33 | public catchThunk: Thunk; 34 | 35 | public constructor(raw: Slice, catchThunk: Thunk) { 36 | super(raw); 37 | 38 | this.arg = new Lazy(raw, PARSER); 39 | this.catchThunk = catchThunk; 40 | } 41 | 42 | public async *run(vm: VM, label?: string) { 43 | const target = (await this.arg.get().reduce(vm)).toUpperCase(); 44 | const context = vm.context(); 45 | if (context.fn.thunk.labelMap.has(target)) { 46 | return yield* Goto.exec(vm, target); 47 | } else { 48 | return yield* this.catchThunk.run(vm, label); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/statement/command/trycjump.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import {parseThunk} from "../../parser/erb"; 3 | import Lazy from "../../lazy"; 4 | import Slice from "../../slice"; 5 | import type Thunk from "../../thunk"; 6 | import type VM from "../../vm"; 7 | import Statement from "../index"; 8 | import Call from "./call"; 9 | import Jump from "./jump"; 10 | 11 | const CATCH = /^CATCH$/i; 12 | const ENDCATCH = /^ENDCATCH$/i; 13 | export default class TryCJump extends Statement { 14 | public static parse(arg: Slice, lines: Slice[], from: number): [TryCJump, number] { 15 | let index = from + 1; 16 | if (lines.length <= index) { 17 | throw E.parser("Unexpected end of thunk in TRYCJUMP expression"); 18 | } else if (!CATCH.test(lines[index].content)) { 19 | throw E.parser("Could not find CATCH for TRYCJUMP expression"); 20 | } 21 | index += 1; 22 | 23 | const [catchThunk, consumed] = parseThunk(lines, index, (l) => ENDCATCH.test(l)); 24 | index += consumed + 1; 25 | 26 | return [new TryCJump(arg, catchThunk), index - from]; 27 | } 28 | 29 | public arg: Jump["arg"]; 30 | public catchExpr: Thunk; 31 | 32 | public constructor(raw: Slice, catchExpr: Thunk) { 33 | super(raw); 34 | 35 | this.arg = new Lazy(raw, Call.PARSER); 36 | this.catchExpr = catchExpr; 37 | } 38 | 39 | public async *run(vm: VM, label?: string) { 40 | const [target, argExpr] = this.arg.get(); 41 | const realTarget = target.toUpperCase(); 42 | if (vm.fnMap.has(realTarget)) { 43 | return yield* Jump.exec(vm, realTarget, argExpr); 44 | } else { 45 | return yield* this.catchExpr.run(vm, label); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/statement/command/trycjumpform.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import {parseThunk} from "../../parser/erb"; 3 | import Lazy from "../../lazy"; 4 | import Slice from "../../slice"; 5 | import type Thunk from "../../thunk"; 6 | import type VM from "../../vm"; 7 | import Statement from "../index"; 8 | import CallForm from "./callform"; 9 | import Jump from "./jump"; 10 | 11 | const CATCH = /^CATCH$/i; 12 | const ENDCATCH = /^ENDCATCH$/i; 13 | export default class TryCJumpForm extends Statement { 14 | public static parse(arg: Slice, lines: Slice[], from: number): [TryCJumpForm, number] { 15 | let index = from + 1; 16 | if (lines.length <= index) { 17 | throw E.parser("Unexpected end of thunk in TRYCJUMPFORM expression"); 18 | } else if (!CATCH.test(lines[index].content)) { 19 | throw E.parser("Could not find CATCH for TRYCJUMPFORM expression"); 20 | } 21 | index += 1; 22 | 23 | const [catchThunk, consumed] = parseThunk(lines, index, (l) => ENDCATCH.test(l)); 24 | index += consumed + 1; 25 | 26 | return [new TryCJumpForm(arg, catchThunk), index - from]; 27 | } 28 | 29 | public arg: CallForm["arg"]; 30 | public catchThunk: Thunk; 31 | 32 | public constructor(raw: Slice, catchThunk: Thunk) { 33 | super(raw); 34 | 35 | this.arg = new Lazy(raw, CallForm.PARSER("")); 36 | this.catchThunk = catchThunk; 37 | } 38 | 39 | public async *run(vm: VM, label?: string) { 40 | const [targetExpr, argExpr] = this.arg.get(); 41 | const target = (await targetExpr.reduce(vm)).toUpperCase(); 42 | if (vm.fnMap.has(target)) { 43 | return yield* Jump.exec(vm, target, argExpr); 44 | } else { 45 | return yield* this.catchThunk.run(vm, label); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/statement/command/trygoto.ts: -------------------------------------------------------------------------------- 1 | import * as C from "../../parser/const"; 2 | import * as U from "../../parser/util"; 3 | import Lazy from "../../lazy"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import Statement from "../index"; 7 | import Goto from "./goto"; 8 | 9 | const PARSER = U.arg1R1(C.Identifier); 10 | export default class TryGoto extends Statement { 11 | public arg: Lazy; 12 | 13 | public constructor(raw: Slice) { 14 | super(raw); 15 | 16 | this.arg = new Lazy(raw, PARSER); 17 | } 18 | 19 | // eslint-disable-next-line @typescript-eslint/require-await 20 | public async *run(vm: VM) { 21 | const target = this.arg.get().toUpperCase(); 22 | const context = vm.context(); 23 | if (context.fn.thunk.labelMap.has(target)) { 24 | return yield* Goto.exec(vm, target); 25 | } 26 | 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/statement/command/trygotoform.ts: -------------------------------------------------------------------------------- 1 | import * as X from "../../parser/expr"; 2 | import * as U from "../../parser/util"; 3 | import Lazy from "../../lazy"; 4 | import Slice from "../../slice"; 5 | import type VM from "../../vm"; 6 | import type Form from "../expr/form"; 7 | import Statement from "../index"; 8 | import Goto from "./goto"; 9 | 10 | const PARSER = U.arg1R1(X.form[""]); 11 | export default class TryGotoForm extends Statement { 12 | public static parse(arg: Slice): TryGotoForm { 13 | return new TryGotoForm(arg); 14 | } 15 | 16 | public arg: Lazy; 17 | 18 | public constructor(raw: Slice) { 19 | super(raw); 20 | this.arg = new Lazy(raw, PARSER); 21 | } 22 | 23 | public async *run(vm: VM) { 24 | const target = (await this.arg.get().reduce(vm)).toUpperCase(); 25 | const context = vm.context(); 26 | if (context.fn.thunk.labelMap.has(target)) { 27 | return yield* Goto.exec(vm, target); 28 | } 29 | 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/statement/command/tryjump.ts: -------------------------------------------------------------------------------- 1 | import Lazy from "../../lazy"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | import Call from "./call"; 6 | import Jump from "./jump"; 7 | 8 | export default class TryJump extends Statement { 9 | public arg: Jump["arg"]; 10 | 11 | public constructor(raw: Slice) { 12 | super(raw); 13 | 14 | this.arg = new Lazy(raw, Call.PARSER); 15 | } 16 | 17 | public async *run(vm: VM) { 18 | const [target, argExpr] = this.arg.get(); 19 | const realTarget = target.toUpperCase(); 20 | if (vm.fnMap.has(realTarget)) { 21 | return yield* Jump.exec(vm, realTarget, argExpr); 22 | } 23 | 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/statement/command/tryjumpform.ts: -------------------------------------------------------------------------------- 1 | import Lazy from "../../lazy"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | import CallForm from "./callform"; 6 | import Jump from "./jump"; 7 | 8 | export default class TryJumpForm extends Statement { 9 | public arg: CallForm["arg"]; 10 | 11 | public constructor(raw: Slice) { 12 | super(raw); 13 | 14 | this.arg = new Lazy(raw, CallForm.PARSER("(")); 15 | } 16 | 17 | public async *run(vm: VM) { 18 | const [targetExpr, argExpr] = this.arg.get(); 19 | const target = (await targetExpr.reduce(vm)).toUpperCase(); 20 | if (vm.fnMap.has(target)) { 21 | return yield* Jump.exec(vm, target, argExpr); 22 | } 23 | 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/statement/command/twait.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type Expr from "../expr"; 7 | import type VM from "../../vm"; 8 | import Statement, {EraGenerator} from "../index"; 9 | 10 | const PARSER = U.arg2R2(X.expr, X.expr); 11 | export default class TWait extends Statement { 12 | public arg: Lazy<[Expr, Expr]>; 13 | 14 | public constructor(raw: Slice) { 15 | super(raw); 16 | 17 | this.arg = new Lazy(raw, PARSER); 18 | } 19 | 20 | public async *run(vm: VM): EraGenerator { 21 | const [timeoutExpr, forceExpr] = this.arg.get(); 22 | const timeout = await timeoutExpr.reduce(vm); 23 | assert.bigint(timeout, "1st argument of TWAIT should be a number"); 24 | const force = await forceExpr.reduce(vm); 25 | assert.bigint(force, "2nd argument of TWAIT should be a number"); 26 | 27 | yield* vm.printer.wait(force !== 0n); 28 | 29 | return null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/statement/command/upcheck.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import type Int1DValue from "../../value/int-1d"; 4 | import type IntChar1DValue from "../../value/int-char-1d"; 5 | import type VM from "../../vm"; 6 | import Statement from "../index"; 7 | 8 | const PARSER = U.arg0R0(); 9 | export default class UpCheck extends Statement { 10 | public constructor(raw: Slice) { 11 | super(raw); 12 | 13 | U.tryParse(PARSER, raw); 14 | } 15 | 16 | public async *run(vm: VM) { 17 | const length = Math.min( 18 | vm.getValue("PALAM").length(1), 19 | vm.getValue("UP").length(0), 20 | vm.getValue("DOWN").length(0), 21 | ); 22 | 23 | for (let i = 0; i < length; ++i) { 24 | const up = vm.getValue("UP").get(vm, [i]); 25 | const down = vm.getValue("DOWN").get(vm, [i]); 26 | const palam = vm.getValue("PALAM").get(vm, [i]); 27 | 28 | if (up <= 0 && down <= 0) { 29 | continue; 30 | } 31 | const result = palam + up - down; 32 | vm.getValue("PALAM").set(vm, result, [i]); 33 | vm.getValue("UP").set(vm, 0n, [i]); 34 | vm.getValue("DOWN").set(vm, 0n, [i]); 35 | 36 | if (!vm.printer.skipDisp) { 37 | const name = vm.code.csv.palam.get(i)!; 38 | let text = `${name} ${palam}`; 39 | if (up > 0) { 40 | text += `+${up}`; 41 | } 42 | if (down > 0) { 43 | text += `-${down}`; 44 | } 45 | text += `=${result}`; 46 | 47 | yield* vm.printer.print(text, new Set(["L"])); 48 | } 49 | } 50 | 51 | return null; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/statement/command/varset.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as X from "../../parser/expr"; 3 | import * as U from "../../parser/util"; 4 | import Lazy from "../../lazy"; 5 | import Slice from "../../slice"; 6 | import type VM from "../../vm"; 7 | import type Expr from "../expr"; 8 | import type Variable from "../expr/variable"; 9 | import Statement from "../index"; 10 | 11 | const PARSER = U.arg4R1(X.variable, X.expr, X.expr, X.expr); 12 | export default class VarSet extends Statement { 13 | public arg: Lazy<[Variable, Expr | undefined, Expr | undefined, Expr | undefined]>; 14 | 15 | public constructor(raw: Slice) { 16 | super(raw); 17 | 18 | this.arg = new Lazy(raw, PARSER); 19 | } 20 | 21 | public async *run(vm: VM) { 22 | const [destExpr, valueExpr, startExpr, endExpr] = this.arg.get(); 23 | 24 | const dest = destExpr.getCell(vm); 25 | const index = await destExpr.reduceIndex(vm); 26 | const start = await startExpr?.reduce(vm) ?? 0n; 27 | assert.bigint(start, "3rd argument of VARSET must be a number"); 28 | const end = await endExpr?.reduce(vm) ?? BigInt(dest.length(index.length)); 29 | assert.bigint(end, "4th argument of VARSET must be a number"); 30 | 31 | if (valueExpr != null) { 32 | const value = await valueExpr.reduce(vm); 33 | dest.rangeSet(vm, value, index, [Number(start), Number(end)]); 34 | } else { 35 | if (dest.type === "number") { 36 | dest.rangeSet(vm, 0n, index, [Number(start), Number(end)]); 37 | } else { 38 | dest.rangeSet(vm, "", index, [Number(start), Number(end)]); 39 | } 40 | } 41 | 42 | return null; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/statement/command/wait.ts: -------------------------------------------------------------------------------- 1 | import * as U from "../../parser/util"; 2 | import Slice from "../../slice"; 3 | import type VM from "../../vm"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class Wait extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | public async *run(vm: VM) { 15 | yield* vm.printer.wait(false); 16 | 17 | return null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/statement/command/waitanykey.ts: -------------------------------------------------------------------------------- 1 | import * as E from "../../error"; 2 | import * as U from "../../parser/util"; 3 | import Slice from "../../slice"; 4 | import Statement from "../index"; 5 | 6 | const PARSER = U.arg0R0(); 7 | export default class WaitAnyKey extends Statement { 8 | public constructor(raw: Slice) { 9 | super(raw); 10 | 11 | U.tryParse(PARSER, raw); 12 | } 13 | 14 | // eslint-disable-next-line @typescript-eslint/require-await 15 | public async *run() { 16 | throw E.notImpl("WAITANYKEY"); 17 | 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/expr/compare.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "./index"; 4 | 5 | type Operator = "==" | "!="; 6 | 7 | export default class Compare implements Expr { 8 | public left: Expr; 9 | public right: Expr; 10 | public op: Operator; 11 | 12 | public constructor(op: Operator, left: Expr, right: Expr) { 13 | this.op = op; 14 | this.left = left; 15 | this.right = right; 16 | } 17 | 18 | public async reduce(vm: VM): Promise { 19 | const left = await this.left.reduce(vm); 20 | const right = await this.right.reduce(vm); 21 | 22 | if (typeof left !== typeof right) { 23 | assert.cond(false, `Type of left and right operand of ${this.op} should be equal`); 24 | } 25 | 26 | switch (this.op) { 27 | case "==": return left === right ? 1n : 0n; 28 | case "!=": return left !== right ? 1n : 0n; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/statement/expr/const.ts: -------------------------------------------------------------------------------- 1 | import type {Leaf} from "../../value"; 2 | import type VM from "../../vm"; 3 | import type Expr from "./index"; 4 | 5 | export default class Const implements Expr { 6 | public value: Leaf; 7 | 8 | public constructor(value: Const["value"]) { 9 | this.value = value; 10 | } 11 | 12 | // eslint-disable-next-line @typescript-eslint/require-await 13 | public async reduce(_vm: VM): Promise { 14 | return this.value; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/statement/expr/form.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "./index"; 4 | 5 | export default class Form implements Expr { 6 | public expr: Array<{ 7 | value: string | Expr; 8 | display?: Expr; 9 | align?: "LEFT" | "RIGHT"; 10 | }>; 11 | 12 | public constructor(expr: Form["expr"]) { 13 | const merged: Form["expr"] = []; 14 | for (const e of expr) { 15 | const last = merged[merged.length - 1]; 16 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition 17 | if (last != null && typeof last.value === "string" && typeof e.value === "string") { 18 | last.value += e.value; 19 | } else { 20 | merged.push(e); 21 | } 22 | } 23 | 24 | this.expr = merged; 25 | } 26 | 27 | public async reduce(vm: VM): Promise { 28 | let result = ""; 29 | for (const expr of this.expr) { 30 | let value: string; 31 | if (typeof expr.value === "string") { 32 | value = expr.value; 33 | } else { 34 | const reduced = await expr.value.reduce(vm); 35 | // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check 36 | switch (typeof reduced) { 37 | case "string": value = reduced; break; 38 | case "bigint": value = reduced.toString(); break; 39 | } 40 | } 41 | 42 | if (expr.display != null) { 43 | const display = await expr.display.reduce(vm); 44 | assert.bigint(display, "Display size of form string should be an integer"); 45 | 46 | if (expr.align == null || expr.align === "LEFT") { 47 | value = value.padStart(Number(display), " "); 48 | } else { 49 | value = value.padEnd(Number(display), " "); 50 | } 51 | } 52 | 53 | result += value; 54 | } 55 | 56 | return result; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/statement/expr/index.ts: -------------------------------------------------------------------------------- 1 | import type {Leaf} from "../../value"; 2 | import type VM from "../../vm"; 3 | 4 | // eslint-disable-next-line @typescript-eslint/consistent-type-definitions 5 | export default interface Expr { 6 | reduce: (vm: VM) => Promise; 7 | } 8 | -------------------------------------------------------------------------------- /src/statement/expr/ternary.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "./index"; 4 | 5 | export default class Ternary implements Expr { 6 | public condition: Expr; 7 | public left: Expr; 8 | public right: Expr; 9 | 10 | public constructor(condition: Expr, left: Expr, right: Expr) { 11 | this.condition = condition; 12 | this.left = left; 13 | this.right = right; 14 | } 15 | 16 | public async reduce(vm: VM) { 17 | const condition = await this.condition.reduce(vm); 18 | assert.bigint(condition, "Condition of ternary operator should be an integer"); 19 | return condition !== 0n ? this.left.reduce(vm) : this.right.reduce(vm); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/statement/expr/unary-op.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "./index"; 4 | import type Variable from "./variable"; 5 | 6 | type Operator = "++" | "--"; 7 | 8 | export default class UnaryOp implements Expr { 9 | public target: Variable; 10 | public op: Operator; 11 | public postfix: boolean; 12 | 13 | public constructor(target: Variable, op: Operator, postfix: boolean) { 14 | this.target = target; 15 | this.op = op; 16 | this.postfix = postfix; 17 | } 18 | 19 | public async reduce(vm: VM): Promise { 20 | const cell = this.target.getCell(vm); 21 | const index = await this.target.reduceIndex(vm); 22 | const value = cell.get(vm, index); 23 | assert.bigint(value, `Operand of ${this.op} should be an integer`); 24 | switch (this.op) { 25 | case "++": cell.set(vm, value + 1n, index); break; 26 | case "--": cell.set(vm, value - 1n, index); break; 27 | } 28 | 29 | if (this.postfix) { 30 | return value; 31 | } else { 32 | switch (this.op) { 33 | case "++": return value + 1n; 34 | case "--": return value - 1n; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/statement/expr/unary.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "./index"; 4 | 5 | type Operator = "+" | "-" | "!" | "~"; 6 | 7 | export default class Unary implements Expr { 8 | public expr: Expr; 9 | public op: Operator; 10 | 11 | public constructor(op: Operator, expr: Expr) { 12 | this.op = op; 13 | this.expr = expr; 14 | } 15 | 16 | public async reduce(vm: VM): Promise { 17 | const value = await this.expr.reduce(vm); 18 | assert.bigint(value, `Operand of ${this.op} should be an integer`); 19 | 20 | switch (this.op) { 21 | case "+": return value; 22 | case "-": return -value; 23 | case "!": return value === 0n ? 1n : 0n; 24 | // eslint-disable-next-line no-bitwise 25 | case "~": return ~value; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/statement/expr/variable.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import Value, {Leaf} from "../../value"; 3 | import type VM from "../../vm"; 4 | import type Expr from "./index"; 5 | 6 | export default class Variable implements Expr { 7 | public name: string; 8 | public index: Expr[]; 9 | public scope?: string; 10 | 11 | public constructor(name: string, index: Expr[], scope?: string) { 12 | this.name = name.toUpperCase(); 13 | this.index = index; 14 | this.scope = scope; 15 | } 16 | 17 | public getCell(vm: VM): Value { 18 | return vm.getValue(this.name, this.scope); 19 | } 20 | 21 | public async reduce(vm: VM): Promise { 22 | if (vm.macroMap.has(this.name)) { 23 | if (this.index.length !== 0) { 24 | throw new Error("Macro cannot be indexed"); 25 | } 26 | 27 | const expr = vm.macroMap.get(this.name)?.expr; 28 | if (expr == null) { 29 | throw new Error("Empty macro cannot be referenced"); 30 | } 31 | 32 | return expr.reduce(vm); 33 | } else { 34 | return this.getCell(vm).get(vm, await this.reduceIndex(vm)); 35 | } 36 | } 37 | 38 | public async reduceIndex(vm: VM): Promise { 39 | if (this.index.length !== 0) { 40 | const result: number[] = []; 41 | for (const i of this.index) { 42 | const value = await i.reduce(vm); 43 | assert.bigint(value, "Index of variable should be an integer"); 44 | result.push(Number(value)); 45 | } 46 | 47 | return result; 48 | } else { 49 | return []; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/statement/method/abs.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function abs(vm: VM, arg: Expr[]): Promise { 6 | const value = await arg[0].reduce(vm); 7 | assert.bigint(value, "1st argument of ABS must a be number"); 8 | 9 | return value >= 0 ? value : -value; 10 | } 11 | -------------------------------------------------------------------------------- /src/statement/method/barstr.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function barStr(vm: VM, arg: Expr[]): Promise { 6 | const value = await arg[0].reduce(vm); 7 | assert.bigint(value, "1st argument of BAR must be a number"); 8 | const max = await arg[1].reduce(vm); 9 | assert.bigint(max, "2nd argument of BAR must be a number"); 10 | const length = await arg[2].reduce(vm); 11 | assert.bigint(length, "3rd argument of BAR must be a number"); 12 | 13 | const filled = length * value / max; 14 | return "[" + "*".repeat(Number(filled)) + ".".repeat(Number(length - filled)) + "]"; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/csvabl.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvAbl(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVABL must be an integer"); 8 | const index = await arg[1].reduce(vm); 9 | assert.bigint(index, "2nd argument of CSVABL must be an integer"); 10 | 11 | const character = vm.code.csv.character.get(Number(num)); 12 | assert.cond(character != null, `Character #${num} does not exist`); 13 | 14 | return character.abl.get(Number(index)) ?? 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/csvbase.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvBase(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVBASE must be an integer"); 8 | const index = await arg[1].reduce(vm); 9 | assert.bigint(index, "2nd argument of CSVBASE must be an integer"); 10 | 11 | const character = vm.code.csv.character.get(Number(num)); 12 | assert.cond(character != null, `Character #${num} does not exist`); 13 | 14 | return character.base.get(Number(index)) ?? 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/csvcallname.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvCallname(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVCALLNAME must be an integer"); 8 | 9 | const character = vm.code.csv.character.get(Number(num)); 10 | assert.cond(character != null, `Character #${num} does not exist`); 11 | 12 | return character.callname; 13 | } 14 | -------------------------------------------------------------------------------- /src/statement/method/csvcflag.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvCflag(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVCFLAG must be an integer"); 8 | const index = await arg[1].reduce(vm); 9 | assert.bigint(index, "2nd argument of CSVCFLAG must be an integer"); 10 | 11 | const character = vm.code.csv.character.get(Number(num)); 12 | assert.cond(character != null, `Character #${num} does not exist`); 13 | 14 | return character.cflag.get(Number(index)) ?? 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/csvcstr.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvCstr(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVCSTR must be an integer"); 8 | const index = await arg[1].reduce(vm); 9 | assert.bigint(index, "2nd argument of CSVCSTR must be an integer"); 10 | 11 | const character = vm.code.csv.character.get(Number(num)); 12 | assert.cond(character != null, `Character #${num} does not exist`); 13 | 14 | return character.cstr.get(Number(index)) ?? ""; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/csvequip.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvEquip(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVEQUIP must be an integer"); 8 | const index = await arg[1].reduce(vm); 9 | assert.bigint(index, "2nd argument of CSVEQUIP must be an integer"); 10 | 11 | const character = vm.code.csv.character.get(Number(num)); 12 | assert.cond(character != null, `Character #${num} does not exist`); 13 | 14 | return character.equip.get(Number(index)) ?? 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/csvexp.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvExp(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVEXP must be an integer"); 8 | const index = await arg[1].reduce(vm); 9 | assert.bigint(index, "2nd argument of CSVEXP must be an integer"); 10 | 11 | const character = vm.code.csv.character.get(Number(num)); 12 | assert.cond(character != null, `Character #${num} does not exist`); 13 | 14 | return character.exp.get(Number(index)) ?? 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/csvjuel.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvJuel(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVJUEL must be an integer"); 8 | const index = await arg[1].reduce(vm); 9 | assert.bigint(index, "2nd argument of CSVJUEL must be an integer"); 10 | 11 | const character = vm.code.csv.character.get(Number(num)); 12 | assert.cond(character != null, `Character #${num} does not exist`); 13 | 14 | return character.juel.get(Number(index)) ?? 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/csvmark.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvMark(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVMARK must be an integer"); 8 | const index = await arg[1].reduce(vm); 9 | assert.bigint(index, "2nd argument of CSVMARK must be an integer"); 10 | 11 | const character = vm.code.csv.character.get(Number(num)); 12 | assert.cond(character != null, `Character #${num} does not exist`); 13 | 14 | return character.mark.get(Number(index)) ?? 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/csvmastername.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvMastername(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVMASTERNAME must be an integer"); 8 | 9 | const character = vm.code.csv.character.get(Number(num)); 10 | assert.cond(character != null, `Character #${num} does not exist`); 11 | 12 | return character.mastername; 13 | } 14 | -------------------------------------------------------------------------------- /src/statement/method/csvname.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvName(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVNAME must be an integer"); 8 | 9 | const character = vm.code.csv.character.get(Number(num)); 10 | assert.cond(character != null, `Character #${num} does not exist`); 11 | 12 | return character.name; 13 | } 14 | -------------------------------------------------------------------------------- /src/statement/method/csvnickname.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvNickname(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVNICKNAME must be an integer"); 8 | 9 | const character = vm.code.csv.character.get(Number(num)); 10 | assert.cond(character != null, `Character #${num} does not exist`); 11 | 12 | return character.nickname; 13 | } 14 | -------------------------------------------------------------------------------- /src/statement/method/csvrelation.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvRelation(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVRELATION must be an integer"); 8 | const index = await arg[1].reduce(vm); 9 | assert.bigint(index, "2nd argument of CSVRELATION must be an integer"); 10 | 11 | const character = vm.code.csv.character.get(Number(num)); 12 | assert.cond(character != null, `Character #${num} does not exist`); 13 | 14 | return character.relation.get(Number(index)) ?? 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/csvtalent.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function csvTalent(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of CSVTALENT must be an integer"); 8 | const index = await arg[1].reduce(vm); 9 | assert.bigint(index, "2nd argument of CSVTALENT must be an integer"); 10 | 11 | const character = vm.code.csv.character.get(Number(num)); 12 | assert.cond(character != null, `Character #${num} does not exist`); 13 | 14 | return character.talent.get(Number(index)) ?? 0; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/existcsv.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function existCsv(vm: VM, arg: Expr[]): Promise { 6 | const num = await arg[0].reduce(vm); 7 | assert.bigint(num, "1st argument of EXISTCSV should be a number"); 8 | 9 | return vm.templateMap.has(Number(num)) ? 1 : 0; 10 | } 11 | -------------------------------------------------------------------------------- /src/statement/method/findchara.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | import Variable from "../expr/variable"; 5 | 6 | export default async function findChara(vm: VM, arg: Expr[]): Promise { 7 | const target = arg[0]; 8 | assert.cond(target instanceof Variable, "1st argument of FINDCHARA should be a variable"); 9 | const value = await arg[1].reduce(vm); 10 | const start = arg.length >= 3 ? await arg[2].reduce(vm) : 0n; 11 | assert.bigint(start, "3rd argument of FINDCHARA should be a nmber"); 12 | const end = arg.length >= 4 ? await arg[3].reduce(vm) : BigInt(vm.characterList.length); 13 | assert.bigint(end, "4th argument of FINDCHARA should be a number"); 14 | 15 | const index = await target.reduceIndex(vm); 16 | for (let i = start; i < end; ++i) { 17 | if (target.getCell(vm).get(vm, [Number(i), ...index]) === value) { 18 | return i; 19 | } 20 | } 21 | 22 | return -1n; 23 | } 24 | -------------------------------------------------------------------------------- /src/statement/method/findlastchara.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | import Variable from "../expr/variable"; 5 | 6 | export default async function findLastChara(vm: VM, arg: Expr[]): Promise { 7 | const target = arg[0]; 8 | assert.cond(target instanceof Variable, "1st argument of FINDLASTCHARA should be a variable"); 9 | const value = await arg[1].reduce(vm); 10 | const start = arg.length >= 3 ? await arg[2].reduce(vm) : 0n; 11 | assert.bigint(start, "3rd argument of FINDLASTCHARA should be a number"); 12 | const end = arg.length >= 4 ? await arg[3].reduce(vm) : BigInt(vm.characterList.length); 13 | assert.bigint(end, "4th argument of FINDLASTCHARA should be a number"); 14 | 15 | const index = await target.reduceIndex(vm); 16 | for (let i = end - 1n; i >= start; --i) { 17 | if (target.getCell(vm).get(vm, [Number(i), ...index]) === value) { 18 | return i; 19 | } 20 | } 21 | 22 | return -1n; 23 | } 24 | -------------------------------------------------------------------------------- /src/statement/method/getbgcolor.ts: -------------------------------------------------------------------------------- 1 | import type VM from "../../vm"; 2 | import type Expr from "../expr"; 3 | 4 | export default function getBgColor(vm: VM, _arg: Expr[]): number { 5 | return parseInt(vm.printer.background, 16); 6 | } 7 | -------------------------------------------------------------------------------- /src/statement/method/getbit.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function getBit(vm: VM, arg: Expr[]): Promise { 6 | const value = await arg[0].reduce(vm); 7 | assert.bigint(value, "1st argument of GETBIT should be a number"); 8 | const index = await arg[1].reduce(vm); 9 | assert.bigint(index, "2nd argument of GETBIT should be a number"); 10 | assert.cond(index < 64, "2nd argument of GETBIT should be less than 64"); 11 | 12 | // eslint-disable-next-line no-bitwise 13 | return (value & (1n << index)) !== 0n ? 1 : 0; 14 | } 15 | -------------------------------------------------------------------------------- /src/statement/method/getchara.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function getChara(vm: VM, arg: Expr[]): Promise { 6 | const id = await arg[0].reduce(vm); 7 | assert.bigint(id, "1st argument of GETCHARA should be an integer"); 8 | 9 | for (let i = 0; i < vm.characterList.length; ++i) { 10 | if (vm.getValue("NO").get(vm, [i]) === id) { 11 | return i; 12 | } 13 | } 14 | return -1; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/getcolor.ts: -------------------------------------------------------------------------------- 1 | import type VM from "../../vm"; 2 | import type Expr from "../expr"; 3 | 4 | export default function getColor(vm: VM, _arg: Expr[]): number { 5 | return parseInt(vm.printer.color, 16); 6 | } 7 | -------------------------------------------------------------------------------- /src/statement/method/getdefbgcolor.ts: -------------------------------------------------------------------------------- 1 | import type VM from "../../vm"; 2 | import type Expr from "../expr"; 3 | 4 | export default function getDefBgColor(vm: VM, _arg: Expr[]): number { 5 | return parseInt(vm.printer.defaultBackground, 16); 6 | } 7 | -------------------------------------------------------------------------------- /src/statement/method/getdefcolor.ts: -------------------------------------------------------------------------------- 1 | import type VM from "../../vm"; 2 | import type Expr from "../expr"; 3 | 4 | export default function getDefColor(vm: VM, _arg: Expr[]): number { 5 | return parseInt(vm.printer.defaultColor, 16); 6 | } 7 | -------------------------------------------------------------------------------- /src/statement/method/getfocuscolor.ts: -------------------------------------------------------------------------------- 1 | import type VM from "../../vm"; 2 | import type Expr from "../expr"; 3 | 4 | export default function getFocusColor(vm: VM, _arg: Expr[]): number { 5 | return parseInt(vm.printer.focus, 16); 6 | } 7 | -------------------------------------------------------------------------------- /src/statement/method/groupmatch.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type {Leaf} from "../../value"; 3 | import type VM from "../../vm"; 4 | import type Expr from "../expr"; 5 | 6 | export default async function groupMatch(vm: VM, arg: Expr[]): Promise { 7 | assert.cond(arg.length > 0, "1st argument of GROUPMATCH must exist"); 8 | const key = await arg[0].reduce(vm); 9 | const values: Array = []; 10 | for (const a of arg.slice(1)) { 11 | values.push(await a.reduce(vm)); 12 | } 13 | 14 | return values.reduce((acc: number, val) => acc + (val === key ? 1 : 0), 0); 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/inrange.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function inRange(vm: VM, arg: Expr[]): Promise { 6 | const value = await arg[0].reduce(vm); 7 | assert.bigint(value, "1st argument of INRANGE should be a number"); 8 | const min = await arg[1].reduce(vm); 9 | assert.bigint(min, "2nd argument of INRANGE should be a number"); 10 | const max = await arg[2].reduce(vm); 11 | assert.bigint(max, "3rd argument of INRANGE should be a number"); 12 | 13 | return min <= value && value <= max ? 1 : 0; 14 | } 15 | -------------------------------------------------------------------------------- /src/statement/method/limit.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function limit(vm: VM, arg: Expr[]): Promise { 6 | const value = await arg[0].reduce(vm); 7 | assert.bigint(value, "1st argument of LIMIT must a be number"); 8 | const min = await arg[1].reduce(vm); 9 | assert.bigint(min, "2nd argument of LIMIT must a be number"); 10 | const max = await arg[2].reduce(vm); 11 | assert.bigint(max, "3rd argument of LIMIT must a be number"); 12 | 13 | if (value < min) { 14 | return min; 15 | } else if (value > max) { 16 | return max; 17 | } else { 18 | return value; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/method/lineisempty.ts: -------------------------------------------------------------------------------- 1 | import type VM from "../../vm"; 2 | import type Expr from "../expr"; 3 | 4 | export default function lineIsEmpty(vm: VM, _arg: Expr[]): number { 5 | return vm.printer.chunks.length === 0 ? 1 : 0; 6 | } 7 | -------------------------------------------------------------------------------- /src/statement/method/match.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | import Variable from "../expr/variable"; 5 | 6 | const LARGE_INT = 2n ** 60n; 7 | export default async function match(vm: VM, arg: Expr[]): Promise { 8 | const target = arg[0]; 9 | assert.cond(target instanceof Variable, "1st argument of MATCH should be a variable"); 10 | const value = await arg[1].reduce(vm); 11 | const start = arg.length >= 3 ? await arg[2].reduce(vm) : 0n; 12 | assert.bigint(start, "3rd argument of MATCH should be a number"); 13 | const end = arg.length >= 4 ? await arg[3].reduce(vm) : LARGE_INT; 14 | assert.bigint(end, "4th argument of MATCH should be a number"); 15 | 16 | const varSize = target.getCell(vm).length(0); 17 | const realEnd = end > varSize ? BigInt(varSize) : end; 18 | let result = 0; 19 | for (let i = start; i < realEnd; ++i) { 20 | if (target.getCell(vm).get(vm, [Number(i)]) === value) { 21 | result += 1; 22 | } 23 | } 24 | 25 | return result; 26 | } 27 | -------------------------------------------------------------------------------- /src/statement/method/max.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function max(vm: VM, arg: Expr[]): Promise { 6 | assert.cond(arg.length > 0, "MAX must have at least 1 argument"); 7 | let result = 0n; 8 | for (let i = 0; i < arg.length; ++i) { 9 | const value = await arg[i].reduce(vm); 10 | assert.bigint(value, `${i + 1}th argument of MAX should be a number`); 11 | result = result > value ? result : value; 12 | } 13 | 14 | return result; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/maxarray.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | import Variable from "../expr/variable"; 5 | 6 | const LARGE_INT = 2n ** 60n; 7 | export default async function maxArray(vm: VM, arg: Expr[]): Promise { 8 | const target = arg[0]; 9 | assert.cond(target instanceof Variable, "1st argument of MAXARRAY should be a variable"); 10 | assert.cond( 11 | target.getCell(vm).type === "number", 12 | "1st argument of MAXARRAY should be a number variable", 13 | ); 14 | const start = arg.length >= 2 ? await arg[1].reduce(vm) : 0n; 15 | assert.bigint(start, "2nd argument of MAXARRAY should be a number"); 16 | const end = arg.length >= 3 ? await arg[2].reduce(vm) : LARGE_INT; 17 | assert.bigint(end, "3rd argument of MAXARRAY should be a number"); 18 | 19 | const varSize = target.getCell(vm).length(0); 20 | const realEnd = end > varSize ? BigInt(varSize) : end; 21 | let result = 0n; 22 | for (let i = start; i < realEnd; ++i) { 23 | const value = target.getCell(vm).get(vm, [Number(i)]) as bigint; 24 | result = result > value ? result : value; 25 | } 26 | 27 | return result; 28 | } 29 | -------------------------------------------------------------------------------- /src/statement/method/min.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | const LARGE_INT = 2n ** 60n; 6 | export default async function min(vm: VM, arg: Expr[]): Promise { 7 | assert.cond(arg.length > 0, "MIN must have at least 1 argument"); 8 | let result = LARGE_INT; 9 | for (let i = 0; i < arg.length; ++i) { 10 | const value = await arg[i].reduce(vm); 11 | assert.bigint(value, `${i + 1}th argument of MIN must be a number`); 12 | result = result > value ? value : result; 13 | } 14 | return result; 15 | } 16 | -------------------------------------------------------------------------------- /src/statement/method/minarray.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | import Variable from "../expr/variable"; 5 | 6 | const LARGE_INT = 2n ** 60n; 7 | export default async function minArray(vm: VM, arg: Expr[]): Promise { 8 | const target = arg[0]; 9 | assert.cond(target instanceof Variable, "1st argument of MINARRAY should be a variable"); 10 | assert.cond( 11 | target.getCell(vm).type === "number", 12 | "1st argument of MINARRAY should be a number variable", 13 | ); 14 | const start = arg.length >= 2 ? await arg[1].reduce(vm) : 0n; 15 | assert.bigint(start, "2nd argument of MINARRAY should be a number"); 16 | const end = arg.length >= 3 ? await arg[2].reduce(vm) : LARGE_INT; 17 | assert.bigint(end, "3rd argument of MINARRAY should be a number"); 18 | 19 | const varSize = target.getCell(vm).length(0); 20 | const realEnd = end > varSize ? BigInt(varSize) : end; 21 | let result = LARGE_INT; 22 | for (let i = start; i < realEnd; ++i) { 23 | const value = target.getCell(vm).get(vm, [Number(i)]) as bigint; 24 | result = result > value ? value : result; 25 | } 26 | 27 | return result; 28 | } 29 | -------------------------------------------------------------------------------- /src/statement/method/power.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function power(vm: VM, arg: Expr[]): Promise { 6 | const base = await arg[0].reduce(vm); 7 | assert.bigint(base, "1st argument of POWER must be a number"); 8 | const exponent = await arg[1].reduce(vm); 9 | assert.bigint(exponent, "2nd argument of POWER must be a number"); 10 | 11 | return base ** exponent; 12 | } 13 | -------------------------------------------------------------------------------- /src/statement/method/rand.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function rand(vm: VM, arg: Expr[]): Promise { 6 | if (arg.length === 0) { 7 | assert.cond(false, "RAND should have at least 1 argument"); 8 | } else if (arg.length === 1) { 9 | const max = await arg[0].reduce(vm); 10 | assert.bigint(max, "1st argument of RAND should be an integer"); 11 | return BigInt(vm.random.next()) % max; 12 | } else { 13 | const min = await arg[0].reduce(vm); 14 | assert.bigint(min, "1st argument of RAND should be an integer"); 15 | const max = await arg[1].reduce(vm); 16 | assert.bigint(max, "2nd argument of RAND should be an integer"); 17 | 18 | return (BigInt(vm.random.next()) % (max - min)) + min; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/statement/method/sign.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function sign(vm: VM, arg: Expr[]): Promise { 6 | const value = await arg[0].reduce(vm); 7 | assert.bigint(value, "1st argument of SIGN must a be number"); 8 | 9 | if (value > 0) { 10 | return 1; 11 | } else if (value < 0) { 12 | return -1; 13 | } else { 14 | return 0; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/statement/method/sqrt.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as E from "../../error"; 3 | import type VM from "../../vm"; 4 | import type Expr from "../expr"; 5 | 6 | export default async function sqrt(vm: VM, arg: Expr[]): Promise { 7 | const value = await arg[0].reduce(vm); 8 | assert.bigint(value, "1st argument of sqrt must be a number"); 9 | 10 | if (value < 0n) { 11 | throw E.misc("Argument of sqrt must be larger than 0"); 12 | } 13 | 14 | let prev = 0n; 15 | let result = value; 16 | for (let i = 0; i < 100; ++i) { 17 | if (prev === result) { 18 | break; 19 | } 20 | prev = result; 21 | result = (result + value / result) / 2n; 22 | } 23 | 24 | // This is heuristic 25 | if (result * result - 1n === value) { 26 | return result - 1n; 27 | } else { 28 | return result; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/statement/method/strlens.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function strLenS(vm: VM, arg: Expr[]): Promise { 6 | const value = await arg[0].reduce(vm); 7 | assert.string(value, "1st Argument of STRLENS should be a string"); 8 | 9 | return value.length; 10 | } 11 | -------------------------------------------------------------------------------- /src/statement/method/strlensu.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function strLenSU(vm: VM, arg: Expr[]): Promise { 6 | const value = await arg[0].reduce(vm); 7 | assert.string(value, "1st Argument of STRLENS should be a string"); 8 | 9 | return value.length; 10 | } 11 | -------------------------------------------------------------------------------- /src/statement/method/sumarray.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | import Variable from "../expr/variable"; 5 | 6 | const LARGE_INT = 2n ** 60n; 7 | export default async function sumArray(vm: VM, arg: Expr[]): Promise { 8 | const target = arg[0]; 9 | assert.cond(target instanceof Variable, "1st argument of SUMARRAY should be a variable"); 10 | assert.cond( 11 | target.getCell(vm).type === "number", 12 | "1st argument of SUMARRAY should be a number variable", 13 | ); 14 | const start = arg.length >= 2 ? await arg[1].reduce(vm) : 0n; 15 | assert.bigint(start, "2nd argument of SUMARRAY should be a number"); 16 | const end = arg.length >= 3 ? await arg[2].reduce(vm) : LARGE_INT; 17 | assert.bigint(end, "3rd argument of SUMARRAY should be a number"); 18 | 19 | const varSize = target.getCell(vm).length(0); 20 | const realEnd = end > varSize ? BigInt(varSize) : end; 21 | let result = 0n; 22 | for (let i = start; i < realEnd; ++i) { 23 | result += target.getCell(vm).get(vm, [Number(i)]) as bigint; 24 | } 25 | 26 | return result; 27 | } 28 | -------------------------------------------------------------------------------- /src/statement/method/toint.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function toInt(vm: VM, arg: Expr[]): Promise { 6 | const value = await arg[0].reduce(vm); 7 | assert.string(value, "1st Argument of TOINT should be a string"); 8 | 9 | const result = Number(value); 10 | return isNaN(result) ? 0 : result; 11 | } 12 | -------------------------------------------------------------------------------- /src/statement/method/tostr.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function toStr(vm: VM, arg: Expr[]): Promise { 6 | const value = await arg[0].reduce(vm); 7 | assert.bigint(value, "1st Argument of TOSTR should be a number"); 8 | 9 | return value.toString(); 10 | } 11 | -------------------------------------------------------------------------------- /src/statement/method/unicode.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function unicode(vm: VM, arg: Expr[]): Promise { 6 | const value = await arg[0].reduce(vm); 7 | assert.bigint(value, "1st Argument of UNICODE should be a number"); 8 | 9 | return String.fromCharCode(Number(value)); 10 | } 11 | -------------------------------------------------------------------------------- /src/statement/method/varsize.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import type VM from "../../vm"; 3 | import type Expr from "../expr"; 4 | 5 | export default async function varSize(vm: VM, arg: Expr[]): Promise { 6 | const name = await arg[0].reduce(vm); 7 | assert.string(name, "1st Argument of VARSIZE should be a string"); 8 | 9 | const depth = arg.length >= 2 ? await arg[1].reduce(vm) : 0n; 10 | assert.bigint(depth, "2nd argument of VARSIZE must be a number"); 11 | return vm.getValue(name).length(Number(depth)); 12 | } 13 | -------------------------------------------------------------------------------- /src/value/index.ts: -------------------------------------------------------------------------------- 1 | import type VM from "../vm"; 2 | 3 | export type Leaf = bigint | string; 4 | // eslint-disable-next-line @typescript-eslint/consistent-type-definitions 5 | export default interface _Value { 6 | type: "string" | "number"; 7 | name: string; 8 | value: T; 9 | reset: (value: T) => this; 10 | get: (vm: VM, index: number[]) => Leaf; 11 | set: (vm: VM, value: Leaf, index: number[]) => void; 12 | rangeSet: (vm: VM, value: Leaf, index: number[], range: [number, number]) => void; 13 | length: (depth: number) => number; 14 | } 15 | -------------------------------------------------------------------------------- /src/value/int-0d.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../assert"; 2 | import * as E from "../error"; 3 | import type VM from "../vm"; 4 | import Value, {Leaf} from "./index"; 5 | 6 | export default class Int0DValue implements Value { 7 | public type = "number"; 8 | public name: string; 9 | public value: bigint; 10 | 11 | public static normalizeIndex(name: string, index: number[]): number[] { 12 | if (index.length === 0) { 13 | return []; 14 | } else if (index.length === 1 && index[0] === 0) { 15 | return []; 16 | } else { 17 | throw E.invalidIndex("0D", name, index); 18 | } 19 | } 20 | 21 | public constructor(name: string) { 22 | this.name = name; 23 | this.value = 0n; 24 | } 25 | 26 | public reset(value: bigint | number): this { 27 | this.value = BigInt(value); 28 | return this; 29 | } 30 | 31 | public get(_vm: VM, index: number[]): bigint { 32 | Int0DValue.normalizeIndex(this.name, index); 33 | return this.value; 34 | } 35 | 36 | public set(_vm: VM, value: Leaf, index: number[]) { 37 | Int0DValue.normalizeIndex(this.name, index); 38 | assert.bigint(value, "Cannot assign a string to a numeric variable"); 39 | 40 | this.value = value; 41 | } 42 | 43 | // NOTE: index is ignored (Emuera emulation) 44 | public rangeSet(_vm: VM, value: Leaf, _index: number[], _range: [number, number]) { 45 | assert.bigint(value, "Cannot assign a string to a numeric variable"); 46 | 47 | this.value = value; 48 | } 49 | 50 | public length(depth: number): number { 51 | switch (depth) { 52 | case 0: return 1; 53 | default: throw new Error(`0D variable doesn't have a value at depth ${depth}`); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/value/special/charanum.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as E from "../../error"; 3 | import type VM from "../../vm"; 4 | import type {default as Value, Leaf} from "../index"; 5 | 6 | export default class CharaNumValue implements Value { 7 | public type = "number"; 8 | public name = "CHARANUM"; 9 | public value!: never; 10 | 11 | public constructor() { 12 | // Do nothing 13 | } 14 | 15 | public reset(): this { 16 | throw E.internal(`${this.name} cannot be reset`); 17 | } 18 | 19 | public get(vm: VM, index: number[]): bigint { 20 | assert.cond(index.length === 0, "CHARANUM cannot be indexed"); 21 | 22 | return BigInt(vm.characterList.length); 23 | } 24 | 25 | public set(_vm: VM, _value: Leaf, _index: number[]) { 26 | throw new Error(`Cannot assign a value to ${this.name}`); 27 | } 28 | 29 | public rangeSet(_vm: VM, _value: Leaf, _index: number[], _range: [number, number]) { 30 | throw new Error(`Cannot assign a value to ${this.name}`); 31 | } 32 | 33 | public length(depth: number): number { 34 | switch (depth) { 35 | case 0: return 1; 36 | default: throw new Error(`${this.name} doesn't have a value at depth ${depth}`); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/value/special/linecount.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as E from "../../error"; 3 | import type VM from "../../vm"; 4 | import type {default as Value, Leaf} from "../index"; 5 | 6 | export default class LineCountValue implements Value { 7 | public type = "number"; 8 | public name = "LINECOUNT"; 9 | public value!: never; 10 | 11 | public constructor() { 12 | // Do nothing 13 | } 14 | 15 | public reset(): this { 16 | throw E.internal(`${this.name} cannot be reset`); 17 | } 18 | 19 | public get(vm: VM, index: number[]): bigint { 20 | assert.cond(index.length === 0, "LINECOUNT cannot be indexed"); 21 | 22 | return BigInt(vm.printer.lineCount); 23 | } 24 | 25 | public set(_vm: VM, _value: Leaf, _index: number[]) { 26 | throw new Error(`Cannot assign a value to ${this.name}`); 27 | } 28 | 29 | public rangeSet(_vm: VM, _value: Leaf, _index: number[], _range: [number, number]) { 30 | throw new Error(`Cannot assign a value to ${this.name}`); 31 | } 32 | 33 | public length(depth: number): number { 34 | switch (depth) { 35 | case 0: return 1; 36 | default: throw new Error(`${this.name} doesn't have a value at depth ${depth}`); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/value/special/rand.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../../assert"; 2 | import * as E from "../../error"; 3 | import type VM from "../../vm"; 4 | import type {default as Value, Leaf} from "../index"; 5 | 6 | export default class RandValue implements Value { 7 | public type = "number"; 8 | public name = "RAND"; 9 | public value!: never; 10 | 11 | public constructor() { 12 | // Do nothing 13 | } 14 | 15 | public reset(): this { 16 | throw E.internal(`${this.name} cannot be reset`); 17 | } 18 | 19 | public get(vm: VM, index: number[]): bigint { 20 | assert.cond(index.length === 1, "RAND must be indexed by 1 value"); 21 | 22 | return BigInt(Math.floor(vm.random.next() % index[0])); 23 | } 24 | 25 | public set(_vm: VM, _value: Leaf, _index: number[]) { 26 | throw new Error("Cannot assign a value to RAND"); 27 | } 28 | 29 | public rangeSet(_vm: VM, _value: Leaf, _index: number[], _range: [number, number]) { 30 | throw new Error("Cannot assign a value to RAND"); 31 | } 32 | 33 | public length(_depth: number): number { 34 | throw new Error("Cannot get the length of RAND"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/value/str-0d.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "../assert"; 2 | import * as E from "../error"; 3 | import type VM from "../vm"; 4 | import type {default as Value, Leaf} from "./index"; 5 | 6 | export default class Str0DValue implements Value { 7 | public type = "string"; 8 | public name: string; 9 | public value: string; 10 | 11 | public static normalizeIndex(name: string, index: number[]): number[] { 12 | if (index.length === 0) { 13 | return []; 14 | } else if (index.length === 1 && index[0] === 0) { 15 | return []; 16 | } else { 17 | throw E.invalidIndex("0D", name, index); 18 | } 19 | } 20 | 21 | public constructor(name: string) { 22 | this.name = name; 23 | this.value = ""; 24 | } 25 | 26 | public reset(value: string): this { 27 | this.value = value; 28 | return this; 29 | } 30 | 31 | public get(_vm: VM, index: number[]): string { 32 | Str0DValue.normalizeIndex(this.name, index); 33 | return this.value; 34 | } 35 | 36 | public set(_vm: VM, value: Leaf, index: number[]) { 37 | Str0DValue.normalizeIndex(this.name, index); 38 | assert.string(value, "Cannot assign a number to a string variable"); 39 | 40 | this.value = value; 41 | } 42 | 43 | // NOTE: index is ignored (Emuera emulation) 44 | public rangeSet(_vm: VM, value: Leaf, _index: number[], _range: [number, number]) { 45 | assert.string(value, "Cannot assign a number to a string variable"); 46 | 47 | this.value = value; 48 | } 49 | 50 | public length(depth: number): number { 51 | switch (depth) { 52 | case 0: return 1; 53 | default: throw new Error(`0D variable doesn't have a value at depth ${depth}`); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "esModuleInterop": true, 5 | "lib": ["ES2015", "ES2017", "ES2019", "WebWorker"], 6 | "declaration": true, 7 | "outDir": "./build", 8 | "moduleResolution": "Node", 9 | "skipLibCheck": true, 10 | "strict": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "noImplicitReturns": true, 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "target": "ESNext" 16 | }, 17 | "include": ["src/**/*"] 18 | } 19 | --------------------------------------------------------------------------------