summaryrefslogtreecommitdiff
path: root/src/client/scripts
diff options
context:
space:
mode:
authormarihachi <marihachi0620@gmail.com>2021-01-02 23:03:15 +0900
committerGitHub <noreply@github.com>2021-01-02 23:03:15 +0900
commit9e3610d513b769080c3e79d981423c0d1317e5a1 (patch)
treec564bcd5270f1d83fb98ce8d065c03ea11398429 /src/client/scripts
parentnoteコンポーネントを二つに分けたり (diff)
downloadsharkey-9e3610d513b769080c3e79d981423c0d1317e5a1.tar.gz
sharkey-9e3610d513b769080c3e79d981423c0d1317e5a1.tar.bz2
sharkey-9e3610d513b769080c3e79d981423c0d1317e5a1.zip
hpml refactoring (#7047)
Diffstat (limited to 'src/client/scripts')
-rw-r--r--src/client/scripts/hpml/evaluator.ts150
-rw-r--r--src/client/scripts/hpml/index.ts58
-rw-r--r--src/client/scripts/hpml/lib.ts80
3 files changed, 151 insertions, 137 deletions
diff --git a/src/client/scripts/hpml/evaluator.ts b/src/client/scripts/hpml/evaluator.ts
index dc6a6d5a4b..4fa95e89c9 100644
--- a/src/client/scripts/hpml/evaluator.ts
+++ b/src/client/scripts/hpml/evaluator.ts
@@ -1,19 +1,13 @@
import autobind from 'autobind-decorator';
-import * as seedrandom from 'seedrandom';
-import { Variable, PageVar, envVarsDef, funcDefs, Block, isFnBlock } from '.';
+import { Variable, PageVar, envVarsDef, Block, isFnBlock, Fn, HpmlScope, HpmlError } from '.';
import { version } from '@/config';
import { AiScript, utils, values } from '@syuilo/aiscript';
import { createAiScriptEnv } from '../aiscript/api';
import { collectPageVars } from '../collect-page-vars';
-import { initLib } from './lib';
+import { initHpmlLib, initAiLib } from './lib';
import * as os from '@/os';
import { markRaw, ref, Ref } from 'vue';
-type Fn = {
- slots: string[];
- exec: (args: Record<string, any>) => ReturnType<Hpml['evaluate']>;
-};
-
/**
* Hpml evaluator
*/
@@ -41,7 +35,7 @@ export class Hpml {
if (this.opts.enableAiScript) {
this.aiscript = markRaw(new AiScript({ ...createAiScriptEnv({
storageKey: 'pages:' + this.page.id
- }), ...initLib(this)}, {
+ }), ...initAiLib(this)}, {
in: (q) => {
return new Promise(ok => {
os.dialog({
@@ -137,7 +131,7 @@ export class Hpml {
}
@autobind
- private _interpolate(str: string, scope: Scope) {
+ private _interpolateScope(str: string, scope: HpmlScope) {
return str.replace(/{(.+?)}/g, match => {
const v = scope.getState(match.slice(1, -1).trim());
return v == null ? 'NULL' : v.toString();
@@ -157,14 +151,14 @@ export class Hpml {
}
for (const v of this.variables) {
- values[v.name] = this.evaluate(v, new Scope([values]));
+ values[v.name] = this.evaluate(v, new HpmlScope([values]));
}
return values;
}
@autobind
- private evaluate(block: Block, scope: Scope): any {
+ private evaluate(block: Block, scope: HpmlScope): any {
if (block.type === null) {
return null;
}
@@ -174,11 +168,11 @@ export class Hpml {
}
if (block.type === 'text' || block.type === 'multiLineText') {
- return this._interpolate(block.value || '', scope);
+ return this._interpolateScope(block.value || '', scope);
}
if (block.type === 'textList') {
- return this._interpolate(block.value || '', scope).trim().split('\n');
+ return this._interpolateScope(block.value || '', scope).trim().split('\n');
}
if (block.type === 'ref') {
@@ -197,7 +191,8 @@ export class Hpml {
}
}
- if (isFnBlock(block)) { // ユーザー関数定義
+ // Define user function
+ if (isFnBlock(block)) {
return {
slots: block.value.slots.map(x => x.name),
exec: (slotArg: Record<string, any>) => {
@@ -206,7 +201,8 @@ export class Hpml {
} as Fn;
}
- if (block.type.startsWith('fn:')) { // ユーザー関数呼び出し
+ // Call user function
+ if (block.type.startsWith('fn:')) {
const fnName = block.type.split(':')[1];
const fn = scope.getState(fnName);
const args = {} as Record<string, any>;
@@ -219,77 +215,9 @@ export class Hpml {
if (block.args === undefined) return null;
- const date = new Date();
- const day = `${this.opts.visitor ? this.opts.visitor.id : ''} ${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
-
- const funcs: { [p in keyof typeof funcDefs]: Function } = {
- not: (a: boolean) => !a,
- or: (a: boolean, b: boolean) => a || b,
- and: (a: boolean, b: boolean) => a && b,
- eq: (a: any, b: any) => a === b,
- notEq: (a: any, b: any) => a !== b,
- gt: (a: number, b: number) => a > b,
- lt: (a: number, b: number) => a < b,
- gtEq: (a: number, b: number) => a >= b,
- ltEq: (a: number, b: number) => a <= b,
- if: (bool: boolean, a: any, b: any) => bool ? a : b,
- for: (times: number, fn: Fn) => {
- const result = [];
- for (let i = 0; i < times; i++) {
- result.push(fn.exec({
- [fn.slots[0]]: i + 1
- }));
- }
- return result;
- },
- add: (a: number, b: number) => a + b,
- subtract: (a: number, b: number) => a - b,
- multiply: (a: number, b: number) => a * b,
- divide: (a: number, b: number) => a / b,
- mod: (a: number, b: number) => a % b,
- round: (a: number) => Math.round(a),
- strLen: (a: string) => a.length,
- strPick: (a: string, b: number) => a[b - 1],
- strReplace: (a: string, b: string, c: string) => a.split(b).join(c),
- strReverse: (a: string) => a.split('').reverse().join(''),
- join: (texts: string[], separator: string) => texts.join(separator || ''),
- stringToNumber: (a: string) => parseInt(a),
- numberToString: (a: number) => a.toString(),
- splitStrByLine: (a: string) => a.split('\n'),
- pick: (list: any[], i: number) => list[i - 1],
- listLen: (list: any[]) => list.length,
- random: (probability: number) => Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * 100) < probability,
- rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * (max - min + 1)),
- randomPick: (list: any[]) => list[Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * list.length)],
- dailyRandom: (probability: number) => Math.floor(seedrandom(`${day}:${block.id}`)() * 100) < probability,
- dailyRannum: (min: number, max: number) => min + Math.floor(seedrandom(`${day}:${block.id}`)() * (max - min + 1)),
- dailyRandomPick: (list: any[]) => list[Math.floor(seedrandom(`${day}:${block.id}`)() * list.length)],
- seedRandom: (seed: any, probability: number) => Math.floor(seedrandom(seed)() * 100) < probability,
- seedRannum: (seed: any, min: number, max: number) => min + Math.floor(seedrandom(seed)() * (max - min + 1)),
- seedRandomPick: (seed: any, list: any[]) => list[Math.floor(seedrandom(seed)() * list.length)],
- DRPWPM: (list: string[]) => {
- const xs = [];
- let totalFactor = 0;
- for (const x of list) {
- const parts = x.split(' ');
- const factor = parseInt(parts.pop()!, 10);
- const text = parts.join(' ');
- totalFactor += factor;
- xs.push({ factor, text });
- }
- const r = seedrandom(`${day}:${block.id}`)() * totalFactor;
- let stackedFactor = 0;
- for (const x of xs) {
- if (r >= stackedFactor && r <= stackedFactor + x.factor) {
- return x.text;
- } else {
- stackedFactor += x.factor;
- }
- }
- return xs[0].text;
- },
- };
+ const funcs = initHpmlLib(block, scope, this.opts.randomSeed, this.opts.visitor);
+ // Call function
const fnName = block.type;
const fn = (funcs as any)[fnName];
if (fn == null) {
@@ -299,53 +227,3 @@ export class Hpml {
}
}
}
-
-class HpmlError extends Error {
- public info?: any;
-
- constructor(message: string, info?: any) {
- super(message);
-
- this.info = info;
-
- // Maintains proper stack trace for where our error was thrown (only available on V8)
- if (Error.captureStackTrace) {
- Error.captureStackTrace(this, HpmlError);
- }
- }
-}
-
-class Scope {
- private layerdStates: Record<string, any>[];
- public name: string;
-
- constructor(layerdStates: Scope['layerdStates'], name?: Scope['name']) {
- this.layerdStates = layerdStates;
- this.name = name || 'anonymous';
- }
-
- @autobind
- public createChildScope(states: Record<string, any>, name?: Scope['name']): Scope {
- const layer = [states, ...this.layerdStates];
- return new Scope(layer, name);
- }
-
- /**
- * 指定した名前の変数の値を取得します
- * @param name 変数名
- */
- @autobind
- public getState(name: string): any {
- for (const later of this.layerdStates) {
- const state = later[name];
- if (state !== undefined) {
- return state;
- }
- }
-
- throw new HpmlError(
- `No such variable '${name}' in scope '${this.name}'`, {
- scope: this.layerdStates
- });
- }
-}
diff --git a/src/client/scripts/hpml/index.ts b/src/client/scripts/hpml/index.ts
index c87d5b9985..fa34b25d8d 100644
--- a/src/client/scripts/hpml/index.ts
+++ b/src/client/scripts/hpml/index.ts
@@ -2,6 +2,8 @@
* Hpml
*/
+import autobind from 'autobind-decorator';
+
import {
faMagic,
faSquareRootAlt,
@@ -27,6 +29,7 @@ import {
faCalculator,
} from '@fortawesome/free-solid-svg-icons';
import { faFlag } from '@fortawesome/free-regular-svg-icons';
+import { Hpml } from './evaluator';
export type Block<V = any> = {
id: string;
@@ -47,6 +50,11 @@ export type Variable = Block & {
name: string;
};
+export type Fn = {
+ slots: string[];
+ exec: (args: Record<string, any>) => ReturnType<Hpml['evaluate']>;
+};
+
export type Type = 'string' | 'number' | 'boolean' | 'stringArray' | null;
export const funcDefs: Record<string, { in: any[]; out: any; category: string; icon: any; }> = {
@@ -137,3 +145,53 @@ export function isLiteralBlock(v: Block) {
if (literalDefs[v.type]) return true;
return false;
}
+
+export class HpmlScope {
+ private layerdStates: Record<string, any>[];
+ public name: string;
+
+ constructor(layerdStates: HpmlScope['layerdStates'], name?: HpmlScope['name']) {
+ this.layerdStates = layerdStates;
+ this.name = name || 'anonymous';
+ }
+
+ @autobind
+ public createChildScope(states: Record<string, any>, name?: HpmlScope['name']): HpmlScope {
+ const layer = [states, ...this.layerdStates];
+ return new HpmlScope(layer, name);
+ }
+
+ /**
+ * 指定した名前の変数の値を取得します
+ * @param name 変数名
+ */
+ @autobind
+ public getState(name: string): any {
+ for (const later of this.layerdStates) {
+ const state = later[name];
+ if (state !== undefined) {
+ return state;
+ }
+ }
+
+ throw new HpmlError(
+ `No such variable '${name}' in scope '${this.name}'`, {
+ scope: this.layerdStates
+ });
+ }
+}
+
+export class HpmlError extends Error {
+ public info?: any;
+
+ constructor(message: string, info?: any) {
+ super(message);
+
+ this.info = info;
+
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, HpmlError);
+ }
+ }
+}
diff --git a/src/client/scripts/hpml/lib.ts b/src/client/scripts/hpml/lib.ts
index 5ec5078415..11e4f2fc39 100644
--- a/src/client/scripts/hpml/lib.ts
+++ b/src/client/scripts/hpml/lib.ts
@@ -2,6 +2,8 @@ import * as tinycolor from 'tinycolor2';
import Chart from 'chart.js';
import { Hpml } from './evaluator';
import { values, utils } from '@syuilo/aiscript';
+import { Block, Fn, HpmlScope } from '.';
+import * as seedrandom from 'seedrandom';
// https://stackoverflow.com/questions/38493564/chart-area-background-color-chartjs
Chart.pluginService.register({
@@ -16,7 +18,7 @@ Chart.pluginService.register({
}
});
-export function initLib(hpml: Hpml) {
+export function initAiLib(hpml: Hpml) {
return {
'MkPages:updated': values.FN_NATIVE(([callback]) => {
hpml.pageVarUpdatedCallback = (callback as values.VFn);
@@ -122,3 +124,79 @@ export function initLib(hpml: Hpml) {
})
};
}
+
+export function initHpmlLib(block: Block, scope: HpmlScope, randomSeed: string, visitor?: any) {
+
+ const date = new Date();
+ const day = `${visitor ? visitor.id : ''} ${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
+
+ const funcs: Record<string, Function> = {
+ not: (a: boolean) => !a,
+ or: (a: boolean, b: boolean) => a || b,
+ and: (a: boolean, b: boolean) => a && b,
+ eq: (a: any, b: any) => a === b,
+ notEq: (a: any, b: any) => a !== b,
+ gt: (a: number, b: number) => a > b,
+ lt: (a: number, b: number) => a < b,
+ gtEq: (a: number, b: number) => a >= b,
+ ltEq: (a: number, b: number) => a <= b,
+ if: (bool: boolean, a: any, b: any) => bool ? a : b,
+ for: (times: number, fn: Fn) => {
+ const result: any[] = [];
+ for (let i = 0; i < times; i++) {
+ result.push(fn.exec({
+ [fn.slots[0]]: i + 1
+ }));
+ }
+ return result;
+ },
+ add: (a: number, b: number) => a + b,
+ subtract: (a: number, b: number) => a - b,
+ multiply: (a: number, b: number) => a * b,
+ divide: (a: number, b: number) => a / b,
+ mod: (a: number, b: number) => a % b,
+ round: (a: number) => Math.round(a),
+ strLen: (a: string) => a.length,
+ strPick: (a: string, b: number) => a[b - 1],
+ strReplace: (a: string, b: string, c: string) => a.split(b).join(c),
+ strReverse: (a: string) => a.split('').reverse().join(''),
+ join: (texts: string[], separator: string) => texts.join(separator || ''),
+ stringToNumber: (a: string) => parseInt(a),
+ numberToString: (a: number) => a.toString(),
+ splitStrByLine: (a: string) => a.split('\n'),
+ pick: (list: any[], i: number) => list[i - 1],
+ listLen: (list: any[]) => list.length,
+ random: (probability: number) => Math.floor(seedrandom(`${randomSeed}:${block.id}`)() * 100) < probability,
+ rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${randomSeed}:${block.id}`)() * (max - min + 1)),
+ randomPick: (list: any[]) => list[Math.floor(seedrandom(`${randomSeed}:${block.id}`)() * list.length)],
+ dailyRandom: (probability: number) => Math.floor(seedrandom(`${day}:${block.id}`)() * 100) < probability,
+ dailyRannum: (min: number, max: number) => min + Math.floor(seedrandom(`${day}:${block.id}`)() * (max - min + 1)),
+ dailyRandomPick: (list: any[]) => list[Math.floor(seedrandom(`${day}:${block.id}`)() * list.length)],
+ seedRandom: (seed: any, probability: number) => Math.floor(seedrandom(seed)() * 100) < probability,
+ seedRannum: (seed: any, min: number, max: number) => min + Math.floor(seedrandom(seed)() * (max - min + 1)),
+ seedRandomPick: (seed: any, list: any[]) => list[Math.floor(seedrandom(seed)() * list.length)],
+ DRPWPM: (list: string[]) => {
+ const xs: any[] = [];
+ let totalFactor = 0;
+ for (const x of list) {
+ const parts = x.split(' ');
+ const factor = parseInt(parts.pop()!, 10);
+ const text = parts.join(' ');
+ totalFactor += factor;
+ xs.push({ factor, text });
+ }
+ const r = seedrandom(`${day}:${block.id}`)() * totalFactor;
+ let stackedFactor = 0;
+ for (const x of xs) {
+ if (r >= stackedFactor && r <= stackedFactor + x.factor) {
+ return x.text;
+ } else {
+ stackedFactor += x.factor;
+ }
+ }
+ return xs[0].text;
+ },
+ };
+
+ return funcs;
+}