This repository has been archived on 2026-04-20. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
AsaJS/src/compilers/bindings/Function.ts
2026-02-23 11:00:34 +07:00

322 lines
8 KiB
TypeScript

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<string, Callback>()
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<string, Callback>
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,
}
})
})