import { RandomBindingString, RandomString, ResolveBinding } from "../../components/Utils.js" import { BindingItem } from "../../types/properties/value.js" import { bindingFuntions } from "../Configuration.js" import { isBinding, isNumber, isString } from "./Checker.js" import { Expression, GenBinding } from "./types.js" type CallbackRet = { genBindings?: GenBinding[] value: Expression } type Callback = (...args: Expression[]) => CallbackRet export const FunctionMap = new Map() export const defaultFunctions = { /** * Returns the absolute value of a number (the value without regard to whether it is positive or negative). For example, the absolute value of -5 is the same as the absolute value of 5. * @param number * @returns */ abs: number => { const randomBinding = RandomBindingString() return { genBindings: [{ source: `((-1 + (${number} > 0) * 2) * ${number})`, target: randomBinding }], value: randomBinding, } }, /** * Returns the negative absolute value of a number (the value without regard to whether it is positive or negative). For example, the absolute value of 5 is the same as the negative absolute value of -5. * @param number * @returns */ negabs: number => { const randomBinding = RandomBindingString() return { genBindings: [{ source: `((-1 + (${number} < 0) * 2) * ${number})`, target: randomBinding }], value: randomBinding, } }, /** * Generate a new binding for expression * @param expression * @returns */ new: expression => { const randomBinding = RandomBindingString() return { genBindings: [{ source: expression, target: randomBinding }], value: randomBinding, } }, sqrt: input => { const ret = RandomBindingString() const isNegative = RandomBindingString() const isLowerThanTwo = RandomBindingString() const next = RandomBindingString() const nextEqualOrGreaterThan = RandomBindingString() const isNextEqualOrGreaterThanRet = `(${nextEqualOrGreaterThan} * ${ret})` const isNotNextEqualOrGreaterThanRet = `((not ${nextEqualOrGreaterThan}) * ${next})` const lowerThanTwoPart = `(${isLowerThanTwo} * ${input})` const notLowerThanTwoPart = `((not ${isLowerThanTwo}) * (${isNextEqualOrGreaterThanRet} + ${isNotNextEqualOrGreaterThanRet}))` const negativePart = `(${isNegative} * -1)` const notNegativePart = `((not ${isNegative}) * (${lowerThanTwoPart} + ${notLowerThanTwoPart}))` return { genBindings: [ { source: `(${input} < 0)`, target: isNegative, }, { source: `(${input} < 2)`, target: isLowerThanTwo, }, { source: input, target: ret, }, { source: `(${ret} + ${input} / ${ret}) / 2`, target: next, }, { source: `(${next} = ${ret}) or (${next} > ${ret})`, target: nextEqualOrGreaterThan, }, { source: `${negativePart} + ${notNegativePart}`, target: ret, }, ], value: ret, } }, cache_value: (cache_binding, override_binding, is_read) => { return { value: `((${is_read} * ${cache_binding}) + ((not ${is_read}) * ${override_binding}))`, } }, vector_length: (x, y, z) => { const newBind = defaultFunctions.new(`${y} * ${y} + ${x} * ${x} + ${z} * ${z}`) as CallbackRet const sqrtBind = defaultFunctions.sqrt(newBind.value) as CallbackRet return { genBindings: [newBind.genBindings![0], ...sqrtBind.genBindings!], value: sqrtBind.value, } }, strlen: str => { if (!/\#\w+/.test(str)) throw new Error("Invalid string") const count = RandomBindingString() const inputStr = RandomBindingString() return { genBindings: [ { source: `0 * (${str} = 'a')`, target: count, }, { source: `'a' + ${str}`, target: inputStr, }, { source: `${count} + (not ((('%.' + (${count} + 1) + 's') * ${inputStr}) = ${inputStr}))`, target: count, }, ], value: count, } }, not_contains: (source_str, contains_str) => { return { value: `(${source_str} - ${contains_str}) = ${source_str}`, } }, contains: (source_str, contains_str) => { return { value: `not ((${source_str} - ${contains_str}) = ${source_str})`, } }, starts_with: (source_str, start_str) => { const prefix = `'asajs:${RandomString(5)}:'` if (isString(source_str)) { if (isString(start_str)) { return { value: `${source_str.slice(1, -1).startsWith(start_str.slice(1, -1))}`, } } else { source_str = prefix.slice(0, -1) + source_str.slice(1) const start_str_bind = RandomBindingString() return { genBindings: [ { source: `${prefix} + ${start_str}`, target: start_str_bind, }, ], value: `not ((${source_str} - ${start_str_bind}) = ${source_str})`, } } } else { if (isString(start_str)) { const strLength = start_str.length - 2 return { value: `('%.${strLength}s' * ${source_str} = ${start_str})`, } } else { const source_str_bind = RandomBindingString() const start_str_bind = RandomBindingString() return { genBindings: [ { source: `${prefix} + ${source_str}`, target: source_str_bind, }, { source: `${prefix} + ${start_str}`, target: start_str_bind, }, ], value: `not ((${source_str_bind} - ${start_str_bind}) = ${source_str_bind})`, } } } }, str_slice: (str, start, end) => { const prefix = `'asajs:${RandomString(5)}:'` const genStrBinds: GenBinding = { source: ``, target: RandomBindingString(), } if (isBinding(str)) genStrBinds.source = `(${prefix} + ${str})` else if (isString(str)) genStrBinds.source = `${prefix.slice(0, -1)}${str.slice(1)}` else throw new Error("Invalid str") if (isBinding(start)) start = `('%.' + (${prefix.length - 2} + ${start}) + 's')` else if (isNumber(start)) start = `'%.${+start + prefix.length - 2}s'` else throw new Error("Invalid start") if (end) { if (isBinding(end)) end = `('%.' + (${prefix.length - 2} + ${end}) + 's')` else if (isNumber(end)) end = `'%.${+end + prefix.length - 2}s'` else throw new Error("Invalid end") const sliceEnd: GenBinding = { source: `(${end} * ${genStrBinds.target})`, target: RandomBindingString(), } return { genBindings: [genStrBinds, sliceEnd], value: `${sliceEnd.target} - (${start} * ${sliceEnd.target})`, } } else { return { genBindings: [genStrBinds], value: `${genStrBinds.target} - (${start} * ${genStrBinds.target})`, } } }, /** * Return a translatable string * @param key * @returns */ translatable: key => { return { genBindings: [], value: `'%' + ${key}`, } }, /** * Return a binary of int32 number in string * @param value * @param bait * @returns */ // bin: value => { // const {} = intToBin(value) // return { // value, // } // }, /** * Generate value bindings * @param value * @param bait * @returns */ bind: (value, bait) => { const ret = RandomBindingString() if (!bait) { throw new Error("Bait is required") } return { genBindings: [{ source: `((${bait} - ${bait}) + ${value})`, target: ret }], value: ret, } }, /** * Return a int of float number, because string in JSON-UI cannot read it. * @param input * @returns */ int: input => { const ret = RandomBindingString() return { genBindings: [{ source: `${input}`, target: ret }], value: ret, } }, } satisfies Record Object.entries(defaultFunctions).forEach(([key, value]) => FunctionMap.set(key, value)) if (bindingFuntions) Object.entries(bindingFuntions).forEach(([key, value]) => { // @ts-ignore FunctionMap.set(key, (...args) => { const { generate_bindings, return_value } = value(...args) if (generate_bindings) { const resolve = ResolveBinding(new Map(), ...(generate_bindings as BindingItem[])) return { genBindings: resolve.map(({ source_property_name, target_property_name }) => ({ source: source_property_name!, target: target_property_name!, })), value: return_value, } } return { value: return_value, } }) })