-
+
+
diff --git a/src/client/components/page/page.note.vue b/src/client/components/page/page.note.vue
index 27c1f1ed6..925844c1b 100644
--- a/src/client/components/page/page.note.vue
+++ b/src/client/components/page/page.note.vue
@@ -1,15 +1,16 @@
-
-
+
+
diff --git a/src/client/components/page/page.number-input.vue b/src/client/components/page/page.number-input.vue
index cf4000010..7b7d79933 100644
--- a/src/client/components/page/page.number-input.vue
+++ b/src/client/components/page/page.number-input.vue
@@ -1,36 +1,44 @@
- {{ hpml.interpolate(value.text) }}
+ {{ hpml.interpolate(block.text) }}
diff --git a/src/client/components/page/page.post.vue b/src/client/components/page/page.post.vue
index 46870ca0d..33c6e6b14 100644
--- a/src/client/components/page/page.post.vue
+++ b/src/client/components/page/page.post.vue
@@ -6,12 +6,14 @@
diff --git a/src/client/components/page/page.section.vue b/src/client/components/page/page.section.vue
index 9f05f3a0c..d32f5dc73 100644
--- a/src/client/components/page/page.section.vue
+++ b/src/client/components/page/page.section.vue
@@ -1,29 +1,30 @@
- {{ value.title }}
+ {{ block.title }}
-
+
diff --git a/src/client/components/page/page.text-input.vue b/src/client/components/page/page.text-input.vue
index f0fe70e33..e67814af1 100644
--- a/src/client/components/page/page.text-input.vue
+++ b/src/client/components/page/page.text-input.vue
@@ -1,36 +1,44 @@
- {{ hpml.interpolate(value.text) }}
+ {{ hpml.interpolate(block.text) }}
diff --git a/src/client/components/page/page.text.vue b/src/client/components/page/page.text.vue
index f109c9f04..1896d00f4 100644
--- a/src/client/components/page/page.text.vue
+++ b/src/client/components/page/page.text.vue
@@ -6,7 +6,9 @@
diff --git a/src/client/components/page/page.textarea.vue b/src/client/components/page/page.textarea.vue
index 205448977..97d688368 100644
--- a/src/client/components/page/page.textarea.vue
+++ b/src/client/components/page/page.textarea.vue
@@ -3,7 +3,9 @@
diff --git a/src/client/pages/page-editor/page-editor.script-block.vue b/src/client/pages/page-editor/page-editor.script-block.vue
index 3fbcd1b19..68bc1b50c 100644
--- a/src/client/pages/page-editor/page-editor.script-block.vue
+++ b/src/client/pages/page-editor/page-editor.script-block.vue
@@ -61,8 +61,10 @@ import { faPencilAlt, faPlug } from '@fortawesome/free-solid-svg-icons';
import { v4 as uuid } from 'uuid';
import XContainer from './page-editor.container.vue';
import MkTextarea from '@/components/ui/textarea.vue';
-import { isLiteralBlock, funcDefs, blockDefs } from '@/scripts/hpml/index';
+import { blockDefs } from '@/scripts/hpml/index';
import * as os from '@/os';
+import { isLiteralValue } from '@/scripts/hpml/expr';
+import { funcDefs } from '@/scripts/hpml/lib';
export default defineComponent({
components: {
@@ -166,7 +168,7 @@ export default defineComponent({
return;
}
- if (isLiteralBlock(this.value)) return;
+ if (isLiteralValue(this.value)) return;
const empties = [];
for (let i = 0; i < funcDefs[this.value.type].in.length; i++) {
diff --git a/src/client/scripts/hpml/block.ts b/src/client/scripts/hpml/block.ts
new file mode 100644
index 000000000..804c5c112
--- /dev/null
+++ b/src/client/scripts/hpml/block.ts
@@ -0,0 +1,109 @@
+// blocks
+
+export type BlockBase = {
+ id: string;
+ type: string;
+};
+
+export type TextBlock = BlockBase & {
+ type: 'text';
+ text: string;
+};
+
+export type SectionBlock = BlockBase & {
+ type: 'section';
+ title: string;
+ children: (Block | VarBlock)[];
+};
+
+export type ImageBlock = BlockBase & {
+ type: 'image';
+ fileId: string | null;
+};
+
+export type ButtonBlock = BlockBase & {
+ type: 'button';
+ text: any;
+ primary: boolean;
+ action: string;
+ content: string;
+ event: string;
+ message: string;
+ var: string;
+ fn: string;
+};
+
+export type IfBlock = BlockBase & {
+ type: 'if';
+ var: string;
+ children: Block[];
+};
+
+export type TextareaBlock = BlockBase & {
+ type: 'textarea';
+ text: string;
+};
+
+export type PostBlock = BlockBase & {
+ type: 'post';
+ text: string;
+ attachCanvasImage: boolean;
+ canvasId: string;
+};
+
+export type CanvasBlock = BlockBase & {
+ type: 'canvas';
+ name: string; // canvas id
+ width: number;
+ height: number;
+};
+
+export type NoteBlock = BlockBase & {
+ type: 'note';
+ detailed: boolean;
+ note: string | null;
+};
+
+export type Block =
+ TextBlock | SectionBlock | ImageBlock | ButtonBlock | IfBlock | TextareaBlock | PostBlock | CanvasBlock | NoteBlock | VarBlock;
+
+// variable blocks
+
+export type VarBlockBase = BlockBase & {
+ name: string;
+};
+
+export type NumberInputVarBlock = VarBlockBase & {
+ type: 'numberInput';
+ text: string;
+};
+
+export type TextInputVarBlock = VarBlockBase & {
+ type: 'textInput';
+ text: string;
+};
+
+export type SwitchVarBlock = VarBlockBase & {
+ type: 'switch';
+ text: string;
+};
+
+export type RadioButtonVarBlock = VarBlockBase & {
+ type: 'radioButton';
+ title: string;
+ values: string[];
+};
+
+export type CounterVarBlock = VarBlockBase & {
+ type: 'counter';
+ text: string;
+ inc: number;
+};
+
+export type VarBlock =
+ NumberInputVarBlock | TextInputVarBlock | SwitchVarBlock | RadioButtonVarBlock | CounterVarBlock;
+
+const varBlock = ['numberInput', 'textInput', 'switch', 'radioButton', 'counter'];
+export function isVarBlock(block: Block): block is VarBlock {
+ return varBlock.includes(block.type);
+}
diff --git a/src/client/scripts/hpml/evaluator.ts b/src/client/scripts/hpml/evaluator.ts
index 4fa95e89c..20261d333 100644
--- a/src/client/scripts/hpml/evaluator.ts
+++ b/src/client/scripts/hpml/evaluator.ts
@@ -1,12 +1,13 @@
import autobind from 'autobind-decorator';
-import { Variable, PageVar, envVarsDef, Block, isFnBlock, Fn, HpmlScope, HpmlError } from '.';
+import { PageVar, envVarsDef, 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 { initHpmlLib, initAiLib } from './lib';
import * as os from '@/os';
-import { markRaw, ref, Ref } from 'vue';
+import { markRaw, ref, Ref, unref } from 'vue';
+import { Expr, isLiteralValue, Variable } from './expr';
/**
* Hpml evaluator
@@ -94,7 +95,7 @@ export class Hpml {
public interpolate(str: string) {
if (str == null) return null;
return str.replace(/{(.+?)}/g, match => {
- const v = this.vars[match.slice(1, -1).trim()];
+ const v = unref(this.vars)[match.slice(1, -1).trim()];
return v == null ? 'NULL' : v.toString();
});
}
@@ -158,72 +159,76 @@ export class Hpml {
}
@autobind
- private evaluate(block: Block, scope: HpmlScope): any {
- if (block.type === null) {
- return null;
- }
+ private evaluate(expr: Expr, scope: HpmlScope): any {
- if (block.type === 'number') {
- return parseInt(block.value, 10);
- }
-
- if (block.type === 'text' || block.type === 'multiLineText') {
- return this._interpolateScope(block.value || '', scope);
- }
-
- if (block.type === 'textList') {
- return this._interpolateScope(block.value || '', scope).trim().split('\n');
- }
-
- if (block.type === 'ref') {
- return scope.getState(block.value);
- }
-
- if (block.type === 'aiScriptVar') {
- if (this.aiscript) {
- try {
- return utils.valToJs(this.aiscript.scope.get(block.value));
- } catch (e) {
- return null;
- }
- } else {
+ if (isLiteralValue(expr)) {
+ if (expr.type === null) {
return null;
}
- }
- // Define user function
- if (isFnBlock(block)) {
- return {
- slots: block.value.slots.map(x => x.name),
- exec: (slotArg: Record
) => {
- return this.evaluate(block.value.expression, scope.createChildScope(slotArg, block.id));
+ if (expr.type === 'number') {
+ return parseInt((expr.value as any), 10);
+ }
+
+ if (expr.type === 'text' || expr.type === 'multiLineText') {
+ return this._interpolateScope(expr.value || '', scope);
+ }
+
+ if (expr.type === 'textList') {
+ return this._interpolateScope(expr.value || '', scope).trim().split('\n');
+ }
+
+ if (expr.type === 'ref') {
+ return scope.getState(expr.value);
+ }
+
+ if (expr.type === 'aiScriptVar') {
+ if (this.aiscript) {
+ try {
+ return utils.valToJs(this.aiscript.scope.get(expr.value));
+ } catch (e) {
+ return null;
+ }
+ } else {
+ return null;
}
- } as Fn;
+ }
+
+ // Define user function
+ if (expr.type == 'fn') {
+ return {
+ slots: expr.value.slots.map(x => x.name),
+ exec: (slotArg: Record) => {
+ return this.evaluate(expr.value.expression, scope.createChildScope(slotArg, expr.id));
+ }
+ } as Fn;
+ }
+ return;
}
// Call user function
- if (block.type.startsWith('fn:')) {
- const fnName = block.type.split(':')[1];
+ if (expr.type.startsWith('fn:')) {
+ const fnName = expr.type.split(':')[1];
const fn = scope.getState(fnName);
const args = {} as Record;
for (let i = 0; i < fn.slots.length; i++) {
const name = fn.slots[i];
- args[name] = this.evaluate(block.args[i], scope);
+ args[name] = this.evaluate(expr.args[i], scope);
}
return fn.exec(args);
}
- if (block.args === undefined) return null;
+ if (expr.args === undefined) return null;
- const funcs = initHpmlLib(block, scope, this.opts.randomSeed, this.opts.visitor);
+ const funcs = initHpmlLib(expr, scope, this.opts.randomSeed, this.opts.visitor);
// Call function
- const fnName = block.type;
+ const fnName = expr.type;
const fn = (funcs as any)[fnName];
if (fn == null) {
throw new HpmlError(`No such function '${fnName}'`);
} else {
- return fn(...block.args.map(x => this.evaluate(x, scope)));
+ return fn(...expr.args.map(x => this.evaluate(x, scope)));
}
}
}
diff --git a/src/client/scripts/hpml/expr.ts b/src/client/scripts/hpml/expr.ts
new file mode 100644
index 000000000..00e3ed118
--- /dev/null
+++ b/src/client/scripts/hpml/expr.ts
@@ -0,0 +1,79 @@
+import { literalDefs, Type } from '.';
+
+export type ExprBase = {
+ id: string;
+};
+
+// value
+
+export type EmptyValue = ExprBase & {
+ type: null;
+ value: null;
+};
+
+export type TextValue = ExprBase & {
+ type: 'text';
+ value: string;
+};
+
+export type MultiLineTextValue = ExprBase & {
+ type: 'multiLineText';
+ value: string;
+};
+
+export type TextListValue = ExprBase & {
+ type: 'textList';
+ value: string;
+};
+
+export type NumberValue = ExprBase & {
+ type: 'number';
+ value: number;
+};
+
+export type RefValue = ExprBase & {
+ type: 'ref';
+ value: string; // value is variable name
+};
+
+export type AiScriptRefValue = ExprBase & {
+ type: 'aiScriptVar';
+ value: string; // value is variable name
+};
+
+export type UserFnValue = ExprBase & {
+ type: 'fn';
+ value: UserFnInnerValue;
+};
+type UserFnInnerValue = {
+ slots: {
+ name: string;
+ type: Type;
+ }[];
+ expression: Expr;
+};
+
+export type Value =
+ EmptyValue | TextValue | MultiLineTextValue | TextListValue | NumberValue | RefValue | AiScriptRefValue | UserFnValue;
+
+export function isLiteralValue(expr: Expr): expr is Value {
+ if (expr.type == null) return true;
+ if (literalDefs[expr.type]) return true;
+ return false;
+}
+
+// call function
+
+export type CallFn = ExprBase & { // "fn:hoge" or string
+ type: string;
+ args: Expr[];
+ value: null;
+};
+
+// variable
+export type Variable = (Value | CallFn) & {
+ name: string;
+};
+
+// expression
+export type Expr = Variable | Value | CallFn;
diff --git a/src/client/scripts/hpml/index.ts b/src/client/scripts/hpml/index.ts
index fa34b25d8..924cd32eb 100644
--- a/src/client/scripts/hpml/index.ts
+++ b/src/client/scripts/hpml/index.ts
@@ -3,52 +3,16 @@
*/
import autobind from 'autobind-decorator';
-
import {
faMagic,
faSquareRootAlt,
faAlignLeft,
- faShareAlt,
- faPlus,
- faMinus,
- faTimes,
- faDivide,
faList,
faQuoteRight,
- faEquals,
- faGreaterThan,
- faLessThan,
- faGreaterThanEqual,
- faLessThanEqual,
- faNotEqual,
- faDice,
faSortNumericUp,
- faExchangeAlt,
- faRecycle,
- faIndent,
- faCalculator,
} from '@fortawesome/free-solid-svg-icons';
-import { faFlag } from '@fortawesome/free-regular-svg-icons';
import { Hpml } from './evaluator';
-
-export type Block = {
- id: string;
- type: string;
- args: Block[];
- value: V;
-};
-
-export type FnBlock = Block<{
- slots: {
- name: string;
- type: Type;
- }[];
- expression: Block;
-}>;
-
-export type Variable = Block & {
- name: string;
-};
+import { funcDefs } from './lib';
export type Fn = {
slots: string[];
@@ -57,46 +21,6 @@ export type Fn = {
export type Type = 'string' | 'number' | 'boolean' | 'stringArray' | null;
-export const funcDefs: Record = {
- if: { in: ['boolean', 0, 0], out: 0, category: 'flow', icon: faShareAlt, },
- for: { in: ['number', 'function'], out: null, category: 'flow', icon: faRecycle, },
- not: { in: ['boolean'], out: 'boolean', category: 'logical', icon: faFlag, },
- or: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: faFlag, },
- and: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: faFlag, },
- add: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faPlus, },
- subtract: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faMinus, },
- multiply: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faTimes, },
- divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, },
- mod: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, },
- round: { in: ['number'], out: 'number', category: 'operation', icon: faCalculator, },
- eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faEquals, },
- notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faNotEqual, },
- gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThan, },
- lt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faLessThan, },
- gtEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThanEqual, },
- ltEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faLessThanEqual, },
- strLen: { in: ['string'], out: 'number', category: 'text', icon: faQuoteRight, },
- strPick: { in: ['string', 'number'], out: 'string', category: 'text', icon: faQuoteRight, },
- strReplace: { in: ['string', 'string', 'string'], out: 'string', category: 'text', icon: faQuoteRight, },
- strReverse: { in: ['string'], out: 'string', category: 'text', icon: faQuoteRight, },
- join: { in: ['stringArray', 'string'], out: 'string', category: 'text', icon: faQuoteRight, },
- stringToNumber: { in: ['string'], out: 'number', category: 'convert', icon: faExchangeAlt, },
- numberToString: { in: ['number'], out: 'string', category: 'convert', icon: faExchangeAlt, },
- splitStrByLine: { in: ['string'], out: 'stringArray', category: 'convert', icon: faExchangeAlt, },
- pick: { in: [null, 'number'], out: null, category: 'list', icon: faIndent, },
- listLen: { in: [null], out: 'number', category: 'list', icon: faIndent, },
- rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, },
- dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, },
- seedRannum: { in: [null, 'number', 'number'], out: 'number', category: 'random', icon: faDice, },
- random: { in: ['number'], out: 'boolean', category: 'random', icon: faDice, },
- dailyRandom: { in: ['number'], out: 'boolean', category: 'random', icon: faDice, },
- seedRandom: { in: [null, 'number'], out: 'boolean', category: 'random', icon: faDice, },
- randomPick: { in: [0], out: 0, category: 'random', icon: faDice, },
- dailyRandomPick: { in: [0], out: 0, category: 'random', icon: faDice, },
- seedRandomPick: { in: [null, 0], out: 0, category: 'random', icon: faDice, },
- DRPWPM: { in: ['stringArray'], out: 'string', category: 'random', icon: faDice, }, // dailyRandomPickWithProbabilityMapping
-};
-
export const literalDefs: Record = {
text: { out: 'string', category: 'value', icon: faQuoteRight, },
multiLineText: { out: 'string', category: 'value', icon: faAlignLeft, },
@@ -116,10 +40,6 @@ export const blockDefs = [
}))
];
-export function isFnBlock(block: Block): block is FnBlock {
- return block.type === 'fn';
-}
-
export type PageVar = { name: string; value: any; type: Type; };
export const envVarsDef: Record = {
@@ -140,12 +60,6 @@ export const envVarsDef: Record = {
NULL: null,
};
-export function isLiteralBlock(v: Block) {
- if (v.type === null) return true;
- if (literalDefs[v.type]) return true;
- return false;
-}
-
export class HpmlScope {
private layerdStates: Record[];
public name: string;
diff --git a/src/client/scripts/hpml/lib.ts b/src/client/scripts/hpml/lib.ts
index 11e4f2fc3..745456218 100644
--- a/src/client/scripts/hpml/lib.ts
+++ b/src/client/scripts/hpml/lib.ts
@@ -2,9 +2,31 @@ 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 { Fn, HpmlScope } from '.';
+import { Expr } from './expr';
import * as seedrandom from 'seedrandom';
+import {
+ faShareAlt,
+ faPlus,
+ faMinus,
+ faTimes,
+ faDivide,
+ faQuoteRight,
+ faEquals,
+ faGreaterThan,
+ faLessThan,
+ faGreaterThanEqual,
+ faLessThanEqual,
+ faNotEqual,
+ faDice,
+ faExchangeAlt,
+ faRecycle,
+ faIndent,
+ faCalculator,
+} from '@fortawesome/free-solid-svg-icons';
+import { faFlag } from '@fortawesome/free-regular-svg-icons';
+
// https://stackoverflow.com/questions/38493564/chart-area-background-color-chartjs
Chart.pluginService.register({
beforeDraw: (chart, easing) => {
@@ -125,7 +147,47 @@ export function initAiLib(hpml: Hpml) {
};
}
-export function initHpmlLib(block: Block, scope: HpmlScope, randomSeed: string, visitor?: any) {
+export const funcDefs: Record = {
+ if: { in: ['boolean', 0, 0], out: 0, category: 'flow', icon: faShareAlt, },
+ for: { in: ['number', 'function'], out: null, category: 'flow', icon: faRecycle, },
+ not: { in: ['boolean'], out: 'boolean', category: 'logical', icon: faFlag, },
+ or: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: faFlag, },
+ and: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: faFlag, },
+ add: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faPlus, },
+ subtract: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faMinus, },
+ multiply: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faTimes, },
+ divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, },
+ mod: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, },
+ round: { in: ['number'], out: 'number', category: 'operation', icon: faCalculator, },
+ eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faEquals, },
+ notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faNotEqual, },
+ gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThan, },
+ lt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faLessThan, },
+ gtEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThanEqual, },
+ ltEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faLessThanEqual, },
+ strLen: { in: ['string'], out: 'number', category: 'text', icon: faQuoteRight, },
+ strPick: { in: ['string', 'number'], out: 'string', category: 'text', icon: faQuoteRight, },
+ strReplace: { in: ['string', 'string', 'string'], out: 'string', category: 'text', icon: faQuoteRight, },
+ strReverse: { in: ['string'], out: 'string', category: 'text', icon: faQuoteRight, },
+ join: { in: ['stringArray', 'string'], out: 'string', category: 'text', icon: faQuoteRight, },
+ stringToNumber: { in: ['string'], out: 'number', category: 'convert', icon: faExchangeAlt, },
+ numberToString: { in: ['number'], out: 'string', category: 'convert', icon: faExchangeAlt, },
+ splitStrByLine: { in: ['string'], out: 'stringArray', category: 'convert', icon: faExchangeAlt, },
+ pick: { in: [null, 'number'], out: null, category: 'list', icon: faIndent, },
+ listLen: { in: [null], out: 'number', category: 'list', icon: faIndent, },
+ rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, },
+ dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, },
+ seedRannum: { in: [null, 'number', 'number'], out: 'number', category: 'random', icon: faDice, },
+ random: { in: ['number'], out: 'boolean', category: 'random', icon: faDice, },
+ dailyRandom: { in: ['number'], out: 'boolean', category: 'random', icon: faDice, },
+ seedRandom: { in: [null, 'number'], out: 'boolean', category: 'random', icon: faDice, },
+ randomPick: { in: [0], out: 0, category: 'random', icon: faDice, },
+ dailyRandomPick: { in: [0], out: 0, category: 'random', icon: faDice, },
+ seedRandomPick: { in: [null, 0], out: 0, category: 'random', icon: faDice, },
+ DRPWPM: { in: ['stringArray'], out: 'string', category: 'random', icon: faDice, }, // dailyRandomPickWithProbabilityMapping
+};
+
+export function initHpmlLib(expr: Expr, scope: HpmlScope, randomSeed: string, visitor?: any) {
const date = new Date();
const day = `${visitor ? visitor.id : ''} ${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
@@ -166,12 +228,12 @@ export function initHpmlLib(block: Block, scope: HpmlScope, randomSeed: string,
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)],
+ random: (probability: number) => Math.floor(seedrandom(`${randomSeed}:${expr.id}`)() * 100) < probability,
+ rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${randomSeed}:${expr.id}`)() * (max - min + 1)),
+ randomPick: (list: any[]) => list[Math.floor(seedrandom(`${randomSeed}:${expr.id}`)() * list.length)],
+ dailyRandom: (probability: number) => Math.floor(seedrandom(`${day}:${expr.id}`)() * 100) < probability,
+ dailyRannum: (min: number, max: number) => min + Math.floor(seedrandom(`${day}:${expr.id}`)() * (max - min + 1)),
+ dailyRandomPick: (list: any[]) => list[Math.floor(seedrandom(`${day}:${expr.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)],
@@ -185,7 +247,7 @@ export function initHpmlLib(block: Block, scope: HpmlScope, randomSeed: string,
totalFactor += factor;
xs.push({ factor, text });
}
- const r = seedrandom(`${day}:${block.id}`)() * totalFactor;
+ const r = seedrandom(`${day}:${expr.id}`)() * totalFactor;
let stackedFactor = 0;
for (const x of xs) {
if (r >= stackedFactor && r <= stackedFactor + x.factor) {
diff --git a/src/client/scripts/hpml/type-checker.ts b/src/client/scripts/hpml/type-checker.ts
index 14950e019..9633b3cd0 100644
--- a/src/client/scripts/hpml/type-checker.ts
+++ b/src/client/scripts/hpml/type-checker.ts
@@ -1,5 +1,7 @@
import autobind from 'autobind-decorator';
-import { Type, Block, funcDefs, envVarsDef, Variable, PageVar, isLiteralBlock } from '.';
+import { Type, envVarsDef, PageVar } from '.';
+import { Expr, isLiteralValue, Variable } from './expr';
+import { funcDefs } from './lib';
type TypeError = {
arg: number;
@@ -20,10 +22,10 @@ export class HpmlTypeChecker {
}
@autobind
- public typeCheck(v: Block): TypeError | null {
- if (isLiteralBlock(v)) return null;
+ public typeCheck(v: Expr): TypeError | null {
+ if (isLiteralValue(v)) return null;
- const def = funcDefs[v.type];
+ const def = funcDefs[v.type || ''];
if (def == null) {
throw new Error('Unknown type: ' + v.type);
}
@@ -58,8 +60,8 @@ export class HpmlTypeChecker {
}
@autobind
- public getExpectedType(v: Block, slot: number): Type {
- const def = funcDefs[v.type];
+ public getExpectedType(v: Expr, slot: number): Type {
+ const def = funcDefs[v.type || ''];
if (def == null) {
throw new Error('Unknown type: ' + v.type);
}
@@ -86,7 +88,7 @@ export class HpmlTypeChecker {
}
@autobind
- public infer(v: Block): Type {
+ public infer(v: Expr): Type {
if (v.type === null) return null;
if (v.type === 'text') return 'string';
if (v.type === 'multiLineText') return 'string';
@@ -103,7 +105,7 @@ export class HpmlTypeChecker {
return pageVar.type;
}
- const envVar = envVarsDef[v.value];
+ const envVar = envVarsDef[v.value || ''];
if (envVar !== undefined) {
return envVar;
}