pages refactoring, fix bug (#7066)
* pages refactoring * pages: fix if block * fix code format * remove passing of the page parameter * remove comment * fix indent * replace with unref * fix conditions of isVarBlock() * Update src/client/scripts/hpml/block.ts use includes() instead of find() Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
This commit is contained in:
109
src/client/scripts/hpml/block.ts
Normal file
109
src/client/scripts/hpml/block.ts
Normal file
@ -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);
|
||||
}
|
@ -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<string, any>) => {
|
||||
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<string, any>) => {
|
||||
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<string, any>;
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
79
src/client/scripts/hpml/expr.ts
Normal file
79
src/client/scripts/hpml/expr.ts
Normal file
@ -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;
|
@ -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<V = any> = {
|
||||
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<string, { in: any[]; out: any; category: string; icon: any; }> = {
|
||||
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<string, { out: any; category: string; icon: any; }> = {
|
||||
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<string, Type> = {
|
||||
@ -140,12 +60,6 @@ export const envVarsDef: Record<string, Type> = {
|
||||
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<string, any>[];
|
||||
public name: string;
|
||||
|
@ -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<string, { in: any[]; out: any; category: string; icon: any; }> = {
|
||||
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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user