feat: bitwise operator to binding
This commit is contained in:
parent
a2ad1bf977
commit
3cee867044
6 changed files with 179 additions and 27 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import { RandomBindingString } from "../../components/Utils.js"
|
||||
import { Parser } from "./Parser.js"
|
||||
import { Expression, GenBinding } from "./types.js"
|
||||
|
||||
type Callback = (...args: Expression[]) => {
|
||||
|
|
@ -6,14 +7,14 @@ type Callback = (...args: Expression[]) => {
|
|||
value: Expression
|
||||
}
|
||||
|
||||
export const FuntionMap = new Map<string, Callback>()
|
||||
export const FunctionMap = new Map<string, Callback>()
|
||||
|
||||
function callFn(name: string, ...args: Expression[]) {
|
||||
return FuntionMap.get(name)!(...args)
|
||||
return FunctionMap.get(name)!(...args)
|
||||
}
|
||||
|
||||
// Default Functions
|
||||
FuntionMap.set("abs", number => {
|
||||
FunctionMap.set("abs", number => {
|
||||
const randomBinding = RandomBindingString(16)
|
||||
return {
|
||||
genBindings: [{ source: `((-1 + (${number} > 0) * 2) * ${number})`, target: randomBinding }],
|
||||
|
|
@ -21,7 +22,7 @@ FuntionMap.set("abs", number => {
|
|||
}
|
||||
})
|
||||
|
||||
FuntionMap.set("new", expression => {
|
||||
FunctionMap.set("new", expression => {
|
||||
const randomBinding = RandomBindingString(16)
|
||||
return {
|
||||
genBindings: [{ source: expression, target: randomBinding }],
|
||||
|
|
@ -29,7 +30,7 @@ FuntionMap.set("new", expression => {
|
|||
}
|
||||
})
|
||||
|
||||
FuntionMap.set("sqrt", number => {
|
||||
FunctionMap.set("sqrt", number => {
|
||||
const rtn = RandomBindingString(16),
|
||||
$1 = RandomBindingString(16),
|
||||
$2 = RandomBindingString(16)
|
||||
|
|
@ -56,8 +57,19 @@ FuntionMap.set("sqrt", number => {
|
|||
}
|
||||
})
|
||||
|
||||
FuntionMap.set("translatable", key => {
|
||||
FunctionMap.set("translatable", key => {
|
||||
return {
|
||||
value: `'%' + ${key}`,
|
||||
}
|
||||
})
|
||||
|
||||
FunctionMap.set("bin", input => {
|
||||
const { ret, bindings } = Parser.intToBin(input)
|
||||
|
||||
bindings.push({
|
||||
source: `'§z' + ${Array.from({ length: 30 }, (_, i) => `${ret}${30 - i - 1}`).join(" + ")}`,
|
||||
target: ret,
|
||||
})
|
||||
|
||||
return { genBindings: bindings, value: ret }
|
||||
})
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export function Lexer(input: string, start: number = 0, end?: number) {
|
|||
case "*":
|
||||
case "/":
|
||||
case "%":
|
||||
case "^":
|
||||
tokens.push(makeToken(input, TokenKind.OPERATOR, index))
|
||||
break
|
||||
|
||||
|
|
@ -63,19 +64,19 @@ export function Lexer(input: string, start: number = 0, end?: number) {
|
|||
case "|":
|
||||
case "=":
|
||||
if (input[index + 1] === input[index]) tokens.push(makeToken(input, TokenKind.OPERATOR, index++, 2))
|
||||
else {
|
||||
console.error(
|
||||
`\x1b[31merror: ${input + "\n" + " ".repeat(index + 7) + "^"}\nInvalid character.\x1b[0m`,
|
||||
)
|
||||
throw new Error()
|
||||
}
|
||||
else tokens.push(makeToken(input, TokenKind.OPERATOR, index))
|
||||
break
|
||||
|
||||
case "!":
|
||||
case ">":
|
||||
case "<":
|
||||
if (input[index + 1] === "=") tokens.push(makeToken(input, TokenKind.OPERATOR, index++, 2))
|
||||
else {
|
||||
if (input[index] === input[index + 1]) {
|
||||
if (input[index] !== "!") tokens.push(makeToken(input, TokenKind.OPERATOR, index++, 2))
|
||||
else tokens.push(makeToken(input, TokenKind.OPERATOR, index))
|
||||
} else tokens.push(makeToken(input, TokenKind.OPERATOR, index))
|
||||
}
|
||||
break
|
||||
|
||||
// string
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { Expression, GenBinding, Token, TokenKind, TSToken, TSTokenKind } from "./types.js"
|
||||
import { BindingType } from "../../types/enums/BindingType.js"
|
||||
import { BindingItem } from "../../types/properties/value.js"
|
||||
import { FuntionMap } from "./Funtion.js"
|
||||
import { Binding, BindingItem } from "../../types/properties/value.js"
|
||||
import { FunctionMap } from "./Funtion.js"
|
||||
import { Lexer } from "./Lexer.js"
|
||||
import { RandomBindingString } from "../../components/Utils.js"
|
||||
|
||||
export class Parser {
|
||||
position: number = 0
|
||||
|
|
@ -11,7 +12,10 @@ export class Parser {
|
|||
genBindings: GenBinding[] = []
|
||||
output: Expression
|
||||
|
||||
constructor(private input: string) {
|
||||
constructor(
|
||||
private input: string,
|
||||
private cache = new Map<string, unknown>(),
|
||||
) {
|
||||
this.tokens = Lexer(this.input)
|
||||
this.output = this.parseExpression()
|
||||
|
||||
|
|
@ -20,6 +24,36 @@ export class Parser {
|
|||
}
|
||||
}
|
||||
|
||||
static intToBin(input: string) {
|
||||
const bindings: GenBinding[] = []
|
||||
const rtn = RandomBindingString(16)
|
||||
|
||||
for (let i = 0; i < 30; i++) {
|
||||
bindings.push({
|
||||
source: `(${input} / ${2 ** i} - (${input} / ${2 ** (i + 1)}) * 2)`,
|
||||
target: `${rtn}${i}`,
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
ret: rtn,
|
||||
bindings,
|
||||
}
|
||||
}
|
||||
|
||||
intToBin(input: string) {
|
||||
const inStr = `expr:${input}`
|
||||
|
||||
if (this.cache.has(inStr)) {
|
||||
return this.cache.get(inStr) as string
|
||||
} else {
|
||||
const { ret, bindings } = Parser.intToBin(input)
|
||||
this.cache.set(inStr, ret)
|
||||
this.genBindings.push(...bindings)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
at() {
|
||||
return this.tokens[this.position]
|
||||
}
|
||||
|
|
@ -114,7 +148,7 @@ export class Parser {
|
|||
}
|
||||
|
||||
private parseMultiplicativeExpression(): Expression {
|
||||
let left = this.parsePrimaryExpression(),
|
||||
let left = this.parseBitwiseLogicExpression(),
|
||||
current
|
||||
|
||||
while (
|
||||
|
|
@ -132,6 +166,99 @@ export class Parser {
|
|||
return left
|
||||
}
|
||||
|
||||
private parseBitwiseLogicExpression(): Expression {
|
||||
let left = this.parseBitwiseShiftExpression(),
|
||||
current
|
||||
|
||||
while (
|
||||
(current = this.at()) &&
|
||||
this.at()?.kind === TokenKind.OPERATOR &&
|
||||
["&", "|", "^"].includes(<string>current.value)
|
||||
) {
|
||||
const operator = this.eat()
|
||||
const right = this.parsePrimaryExpression()
|
||||
|
||||
const ret = RandomBindingString(16)
|
||||
|
||||
const leftBin = this.intToBin(left)
|
||||
const rightBin = this.intToBin(right)
|
||||
|
||||
if (operator.value === "&") {
|
||||
this.genBindings.push(
|
||||
...Array.from({ length: 30 }, (_, i) => {
|
||||
return {
|
||||
source: `(${leftBin}${i} * ${rightBin}${i})`,
|
||||
target: `${ret}${i}`,
|
||||
}
|
||||
}),
|
||||
)
|
||||
} else if (operator.value === "|") {
|
||||
this.genBindings.push(
|
||||
...Array.from({ length: 30 }, (_, i) => {
|
||||
return {
|
||||
source: `(${leftBin}${i} + ${rightBin}${i} - (${leftBin}${i} * ${rightBin}${i}))`,
|
||||
target: `${ret}${i}`,
|
||||
}
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
this.genBindings.push(
|
||||
...Array.from({ length: 30 }, (_, i) => {
|
||||
return {
|
||||
source: `(${leftBin}${i} + ${rightBin}${i} - 2 * (${leftBin}${i} * ${rightBin}${i}))`,
|
||||
target: `${ret}${i}`,
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
this.genBindings.push({
|
||||
source: `(${Array.from({ length: 30 }, (_, i) => `(${ret}${i} * ${2 ** i})`).join(" + ")})`,
|
||||
target: ret,
|
||||
})
|
||||
|
||||
left = ret
|
||||
}
|
||||
|
||||
return left
|
||||
}
|
||||
|
||||
private parseBitwiseShiftExpression(): Expression {
|
||||
let left = this.parsePrimaryExpression(),
|
||||
current
|
||||
|
||||
while (
|
||||
(current = this.at()) &&
|
||||
this.at()?.kind === TokenKind.OPERATOR &&
|
||||
[">>", "<<"].includes(<string>current.value)
|
||||
) {
|
||||
const operator = this.eat()
|
||||
const right = this.parsePrimaryExpression()
|
||||
const ret = RandomBindingString(16)
|
||||
const leftBind = this.intToBin(left)
|
||||
|
||||
const op = operator.value === "<<" ? "-" : "+"
|
||||
|
||||
this.genBindings.push(
|
||||
...Array.from({ length: 30 }, (_, i) => {
|
||||
return {
|
||||
source: `((0 * ${left}) + ('${leftBind}' + (${i} ${op} ${right})))`,
|
||||
target: `${ret}${i}`,
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
this.genBindings.push({
|
||||
source: `(${Array.from({ length: 30 }, (_, i) => `(${ret}${i} * ${2 ** i})`).join(" + ")})`,
|
||||
target: ret,
|
||||
})
|
||||
|
||||
left = ret
|
||||
}
|
||||
|
||||
return left
|
||||
}
|
||||
|
||||
private parsePrimaryExpression(): Expression {
|
||||
const left = this.at()
|
||||
|
||||
|
|
@ -247,7 +374,15 @@ export class Parser {
|
|||
|
||||
this.eat()
|
||||
|
||||
return this.funtionCall(<string>callerToken.value, ...args)
|
||||
const inputStr = `func:${callerToken.value}(${args.join(", ")})`
|
||||
|
||||
if (this.cache.has(inputStr)) {
|
||||
return this.cache.get(inputStr) as Expression
|
||||
} else {
|
||||
const ret = this.functionCall(<string>callerToken.value, ...args)
|
||||
this.cache.set(inputStr, ret)
|
||||
return ret
|
||||
}
|
||||
} else if (left?.kind === TokenKind.OPERATOR) {
|
||||
this.warn(
|
||||
`Implicit string literal '${callerToken.value}'. Use quoted string ('${callerToken.value}') for clarity!`,
|
||||
|
|
@ -265,8 +400,8 @@ export class Parser {
|
|||
}
|
||||
}
|
||||
|
||||
private funtionCall(name: string, ...params: Expression[]): Expression {
|
||||
const func = FuntionMap.get(name.toLowerCase())
|
||||
private functionCall(name: string, ...params: Expression[]): Expression {
|
||||
const func = FunctionMap.get(name.toLowerCase())
|
||||
if (!func) {
|
||||
return this.expect(TokenKind.WORD, "Function not found!")!
|
||||
} else {
|
||||
|
|
@ -294,9 +429,10 @@ export class Parser {
|
|||
return this.input + "\n" + " ".repeat(token.start + (isWarn ? 5 : 7)) + "^".repeat(token.length)
|
||||
}
|
||||
|
||||
out(): { gen?: BindingItem[]; out: Expression } {
|
||||
out(): { gen?: BindingItem[]; out: Expression; cache: Map<string, unknown> } {
|
||||
return {
|
||||
out: `(${this.output})`,
|
||||
cache: this.cache,
|
||||
gen: this.genBindings.map(
|
||||
({ source, target }) =>
|
||||
<BindingItem>{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { BindingItem, ButtonMapping, ModificationItem, VariableItem, Variables }
|
|||
import { Animation } from "./Animation.js"
|
||||
import { AnimationKeyframe } from "./AnimationKeyframe.js"
|
||||
import { Class } from "./Class.js"
|
||||
import { ExtendsOf, RandomString, ResolveBinding } from "./Utils.js"
|
||||
import { RandomString, ResolveBinding } from "./Utils.js"
|
||||
import { RandomNamespace } from "../compilers/Random.js"
|
||||
|
||||
import util from "node:util"
|
||||
|
|
@ -37,6 +37,7 @@ export class UI<T extends Type, K extends Renderer | null = null> extends Class
|
|||
protected readonly extendType?: Type
|
||||
|
||||
protected properties: Properties<T, K> = <any>{}
|
||||
protected bindCache = new Map<string, unknown>()
|
||||
|
||||
constructor(
|
||||
public type?: T,
|
||||
|
|
@ -83,7 +84,7 @@ export class UI<T extends Type, K extends Renderer | null = null> extends Class
|
|||
* @returns
|
||||
*/
|
||||
addBindings(...bindings: BindingItem[]) {
|
||||
this.bindings.push(...ResolveBinding(...bindings))
|
||||
this.bindings.push(...ResolveBinding(this.bindCache, ...bindings))
|
||||
return this
|
||||
}
|
||||
|
||||
|
|
@ -336,7 +337,7 @@ export class ModifyUI<T extends Type = Type.PANEL, S extends string = string> ex
|
|||
return this.addModifications({
|
||||
array_name: ArrayName.BINDINGS,
|
||||
operation: Operation.INSERT_BACK,
|
||||
value: ResolveBinding(...bindings),
|
||||
value: ResolveBinding(this.bindCache, ...bindings),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -344,7 +345,7 @@ export class ModifyUI<T extends Type = Type.PANEL, S extends string = string> ex
|
|||
return this.addModifications({
|
||||
array_name: ArrayName.BINDINGS,
|
||||
operation: Operation.INSERT_FRONT,
|
||||
value: ResolveBinding(...bindings),
|
||||
value: ResolveBinding(this.bindCache, ...bindings),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,13 +68,13 @@ export function Color(hex: string | number): Array3<number> {
|
|||
}
|
||||
}
|
||||
|
||||
export function ResolveBinding(...bindings: BindingItem[]) {
|
||||
export function ResolveBinding(cache: Map<string, unknown>, ...bindings: BindingItem[]) {
|
||||
const result: BindingItem[] = []
|
||||
|
||||
for (const binding of bindings) {
|
||||
if (binding.source_property_name) {
|
||||
if (isCompileBinding(binding.source_property_name)) {
|
||||
const { gen, out } = new Parser(binding.source_property_name.slice(1, -1)).out()
|
||||
const { gen, out } = new Parser(binding.source_property_name.slice(1, -1), cache).out()
|
||||
if (gen) result.push(...gen)
|
||||
binding.source_property_name = out
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,3 +14,5 @@ export * as Properties from "./types/properties/index.js"
|
|||
export { ItemAuxID } from "./types/enums/Items.js"
|
||||
|
||||
export { ArrayName, Operation } from "./types/properties/index.js"
|
||||
|
||||
export { Lexer } from "./compilers/bindings/Lexer.js"
|
||||
|
|
|
|||
Reference in a new issue