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 { RandomBindingString } from "../../components/Utils.js"
|
||||||
|
import { Parser } from "./Parser.js"
|
||||||
import { Expression, GenBinding } from "./types.js"
|
import { Expression, GenBinding } from "./types.js"
|
||||||
|
|
||||||
type Callback = (...args: Expression[]) => {
|
type Callback = (...args: Expression[]) => {
|
||||||
|
|
@ -6,14 +7,14 @@ type Callback = (...args: Expression[]) => {
|
||||||
value: Expression
|
value: Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FuntionMap = new Map<string, Callback>()
|
export const FunctionMap = new Map<string, Callback>()
|
||||||
|
|
||||||
function callFn(name: string, ...args: Expression[]) {
|
function callFn(name: string, ...args: Expression[]) {
|
||||||
return FuntionMap.get(name)!(...args)
|
return FunctionMap.get(name)!(...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default Functions
|
// Default Functions
|
||||||
FuntionMap.set("abs", number => {
|
FunctionMap.set("abs", number => {
|
||||||
const randomBinding = RandomBindingString(16)
|
const randomBinding = RandomBindingString(16)
|
||||||
return {
|
return {
|
||||||
genBindings: [{ source: `((-1 + (${number} > 0) * 2) * ${number})`, target: randomBinding }],
|
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)
|
const randomBinding = RandomBindingString(16)
|
||||||
return {
|
return {
|
||||||
genBindings: [{ source: expression, target: randomBinding }],
|
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),
|
const rtn = RandomBindingString(16),
|
||||||
$1 = RandomBindingString(16),
|
$1 = RandomBindingString(16),
|
||||||
$2 = RandomBindingString(16)
|
$2 = RandomBindingString(16)
|
||||||
|
|
@ -56,8 +57,19 @@ FuntionMap.set("sqrt", number => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
FuntionMap.set("translatable", key => {
|
FunctionMap.set("translatable", key => {
|
||||||
return {
|
return {
|
||||||
value: `'%' + ${key}`,
|
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 "/":
|
||||||
case "%":
|
case "%":
|
||||||
|
case "^":
|
||||||
tokens.push(makeToken(input, TokenKind.OPERATOR, index))
|
tokens.push(makeToken(input, TokenKind.OPERATOR, index))
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
@ -63,19 +64,19 @@ export function Lexer(input: string, start: number = 0, end?: number) {
|
||||||
case "|":
|
case "|":
|
||||||
case "=":
|
case "=":
|
||||||
if (input[index + 1] === input[index]) tokens.push(makeToken(input, TokenKind.OPERATOR, index++, 2))
|
if (input[index + 1] === input[index]) tokens.push(makeToken(input, TokenKind.OPERATOR, index++, 2))
|
||||||
else {
|
else tokens.push(makeToken(input, TokenKind.OPERATOR, index))
|
||||||
console.error(
|
|
||||||
`\x1b[31merror: ${input + "\n" + " ".repeat(index + 7) + "^"}\nInvalid character.\x1b[0m`,
|
|
||||||
)
|
|
||||||
throw new Error()
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case "!":
|
case "!":
|
||||||
case ">":
|
case ">":
|
||||||
case "<":
|
case "<":
|
||||||
if (input[index + 1] === "=") tokens.push(makeToken(input, TokenKind.OPERATOR, index++, 2))
|
if (input[index + 1] === "=") tokens.push(makeToken(input, TokenKind.OPERATOR, index++, 2))
|
||||||
else tokens.push(makeToken(input, TokenKind.OPERATOR, index))
|
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
|
break
|
||||||
|
|
||||||
// string
|
// string
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { Expression, GenBinding, Token, TokenKind, TSToken, TSTokenKind } from "./types.js"
|
import { Expression, GenBinding, Token, TokenKind, TSToken, TSTokenKind } from "./types.js"
|
||||||
import { BindingType } from "../../types/enums/BindingType.js"
|
import { BindingType } from "../../types/enums/BindingType.js"
|
||||||
import { BindingItem } from "../../types/properties/value.js"
|
import { Binding, BindingItem } from "../../types/properties/value.js"
|
||||||
import { FuntionMap } from "./Funtion.js"
|
import { FunctionMap } from "./Funtion.js"
|
||||||
import { Lexer } from "./Lexer.js"
|
import { Lexer } from "./Lexer.js"
|
||||||
|
import { RandomBindingString } from "../../components/Utils.js"
|
||||||
|
|
||||||
export class Parser {
|
export class Parser {
|
||||||
position: number = 0
|
position: number = 0
|
||||||
|
|
@ -11,7 +12,10 @@ export class Parser {
|
||||||
genBindings: GenBinding[] = []
|
genBindings: GenBinding[] = []
|
||||||
output: Expression
|
output: Expression
|
||||||
|
|
||||||
constructor(private input: string) {
|
constructor(
|
||||||
|
private input: string,
|
||||||
|
private cache = new Map<string, unknown>(),
|
||||||
|
) {
|
||||||
this.tokens = Lexer(this.input)
|
this.tokens = Lexer(this.input)
|
||||||
this.output = this.parseExpression()
|
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() {
|
at() {
|
||||||
return this.tokens[this.position]
|
return this.tokens[this.position]
|
||||||
}
|
}
|
||||||
|
|
@ -114,7 +148,7 @@ export class Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseMultiplicativeExpression(): Expression {
|
private parseMultiplicativeExpression(): Expression {
|
||||||
let left = this.parsePrimaryExpression(),
|
let left = this.parseBitwiseLogicExpression(),
|
||||||
current
|
current
|
||||||
|
|
||||||
while (
|
while (
|
||||||
|
|
@ -132,6 +166,99 @@ export class Parser {
|
||||||
return left
|
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 {
|
private parsePrimaryExpression(): Expression {
|
||||||
const left = this.at()
|
const left = this.at()
|
||||||
|
|
||||||
|
|
@ -247,7 +374,15 @@ export class Parser {
|
||||||
|
|
||||||
this.eat()
|
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) {
|
} else if (left?.kind === TokenKind.OPERATOR) {
|
||||||
this.warn(
|
this.warn(
|
||||||
`Implicit string literal '${callerToken.value}'. Use quoted string ('${callerToken.value}') for clarity!`,
|
`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 {
|
private functionCall(name: string, ...params: Expression[]): Expression {
|
||||||
const func = FuntionMap.get(name.toLowerCase())
|
const func = FunctionMap.get(name.toLowerCase())
|
||||||
if (!func) {
|
if (!func) {
|
||||||
return this.expect(TokenKind.WORD, "Function not found!")!
|
return this.expect(TokenKind.WORD, "Function not found!")!
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -294,9 +429,10 @@ export class Parser {
|
||||||
return this.input + "\n" + " ".repeat(token.start + (isWarn ? 5 : 7)) + "^".repeat(token.length)
|
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 {
|
return {
|
||||||
out: `(${this.output})`,
|
out: `(${this.output})`,
|
||||||
|
cache: this.cache,
|
||||||
gen: this.genBindings.map(
|
gen: this.genBindings.map(
|
||||||
({ source, target }) =>
|
({ source, target }) =>
|
||||||
<BindingItem>{
|
<BindingItem>{
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { BindingItem, ButtonMapping, ModificationItem, VariableItem, Variables }
|
||||||
import { Animation } from "./Animation.js"
|
import { Animation } from "./Animation.js"
|
||||||
import { AnimationKeyframe } from "./AnimationKeyframe.js"
|
import { AnimationKeyframe } from "./AnimationKeyframe.js"
|
||||||
import { Class } from "./Class.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 { RandomNamespace } from "../compilers/Random.js"
|
||||||
|
|
||||||
import util from "node:util"
|
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 readonly extendType?: Type
|
||||||
|
|
||||||
protected properties: Properties<T, K> = <any>{}
|
protected properties: Properties<T, K> = <any>{}
|
||||||
|
protected bindCache = new Map<string, unknown>()
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public type?: T,
|
public type?: T,
|
||||||
|
|
@ -83,7 +84,7 @@ export class UI<T extends Type, K extends Renderer | null = null> extends Class
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
addBindings(...bindings: BindingItem[]) {
|
addBindings(...bindings: BindingItem[]) {
|
||||||
this.bindings.push(...ResolveBinding(...bindings))
|
this.bindings.push(...ResolveBinding(this.bindCache, ...bindings))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -336,7 +337,7 @@ export class ModifyUI<T extends Type = Type.PANEL, S extends string = string> ex
|
||||||
return this.addModifications({
|
return this.addModifications({
|
||||||
array_name: ArrayName.BINDINGS,
|
array_name: ArrayName.BINDINGS,
|
||||||
operation: Operation.INSERT_BACK,
|
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({
|
return this.addModifications({
|
||||||
array_name: ArrayName.BINDINGS,
|
array_name: ArrayName.BINDINGS,
|
||||||
operation: Operation.INSERT_FRONT,
|
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[] = []
|
const result: BindingItem[] = []
|
||||||
|
|
||||||
for (const binding of bindings) {
|
for (const binding of bindings) {
|
||||||
if (binding.source_property_name) {
|
if (binding.source_property_name) {
|
||||||
if (isCompileBinding(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)
|
if (gen) result.push(...gen)
|
||||||
binding.source_property_name = out
|
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 { ItemAuxID } from "./types/enums/Items.js"
|
||||||
|
|
||||||
export { ArrayName, Operation } from "./types/properties/index.js"
|
export { ArrayName, Operation } from "./types/properties/index.js"
|
||||||
|
|
||||||
|
export { Lexer } from "./compilers/bindings/Lexer.js"
|
||||||
|
|
|
||||||
Reference in a new issue