diff --git a/src/compilers/Memory.ts b/src/compilers/Memory.ts index 1fff7f7..72354dc 100644 --- a/src/compilers/Memory.ts +++ b/src/compilers/Memory.ts @@ -17,7 +17,7 @@ export const Memory = { return namespace }, - gen_ui_file_content(namespace: string, elements: Map>) { + gen_ui_file_contents(namespace: string, elements: Map>) { return JSON.stringify( { namespace, diff --git a/src/compilers/PreCompile.ts b/src/compilers/PreCompile.ts index bd03047..ed18081 100644 --- a/src/compilers/PreCompile.ts +++ b/src/compilers/PreCompile.ts @@ -2,6 +2,10 @@ declare global { interface Map { toJSON(): Record } + + interface Array { + lastItem(): T + } } Map.prototype.toJSON = function () { @@ -9,3 +13,7 @@ Map.prototype.toJSON = function () { this.forEach((value, key) => (obj[key] = value)) return obj } + +Array.prototype.lastItem = function () { + return this[this.length - 1] +} diff --git a/src/compilers/RunEnd.ts b/src/compilers/RunEnd.ts index 53f16fa..4acb27f 100644 --- a/src/compilers/RunEnd.ts +++ b/src/compilers/RunEnd.ts @@ -2,6 +2,6 @@ import { Memory } from "./Memory.js" process.on("beforeExit", () => { Memory.cache.forEach(({ elements, namespace }) => { - console.log(Memory.gen_ui_file_content(namespace, elements)) + const contents = Memory.gen_ui_file_contents(namespace, elements) }) }) diff --git a/src/compilers/bindings/Bindings.ts b/src/compilers/bindings/Bindings.ts new file mode 100644 index 0000000..9aad1fd --- /dev/null +++ b/src/compilers/bindings/Bindings.ts @@ -0,0 +1,149 @@ +import { Token, TokenKind } from "./types.js" + +export const BindingTranspiler = { + isBlankChar(char: string) { + return /\s/.test(char) + }, + + isWordChar(char: string) { + return char && /\w/.test(char) + }, + + isNumberChar(char: string) { + return /\d/.test(char) + }, + + token(input: string, kind: TokenKind, start: number, length: number = 1): Token { + return { + value: input.slice(start, start + length), + kind, + start, + length, + } + }, + + lexer(input: string) { + const tokens: Token[] = [] + + let index = 0 + do { + const token = input[index] + + if (BindingTranspiler.isBlankChar(token)) continue + + switch (token) { + // Literals + case "#": + case "$": { + const start = index++ + + do { + const token = input[index] + if (BindingTranspiler.isWordChar(token)) continue + else break + } while (++index < input.length) + + tokens.push(BindingTranspiler.token(input, TokenKind.VARIABLE, start, index - start)) + + break + } + + case "'": { + const start = index++ + + do { + const token = input[index] + if (token === "'") break + } while (++index < input.length) + + tokens.push(BindingTranspiler.token(input, TokenKind.STRING, start, index - start + 1)) + + break + } + + case "`": { + const start = index++, + struct: boolean[] = [] + + do { + const token = input[index] + let lastStruct = struct.lastItem() + + if (token === "`") { + if (struct.length) { + if (lastStruct === false) struct.pop() + else struct.push(false) + } else break + } + + if (token === "$") { + if (input[index + 1] === "{" && !lastStruct) { + struct.push(true) + index++ + } + } + + if (token === "}" && lastStruct === true) struct.pop() + } while (++index < input.length) + + tokens.push(BindingTranspiler.token(input, TokenKind.TEMPLATE_STRING, start, index - start + 1)) + + break + } + + case ",": + tokens.push(BindingTranspiler.token(input, TokenKind.COMMA, index)) + break + + // Single operators + case "+": + case "-": + case "*": + case "/": + tokens.push(BindingTranspiler.token(input, TokenKind.OPERATOR, index)) + break + + case "(": + tokens.push(BindingTranspiler.token(input, TokenKind.OPEN_PARENTHESIS, index)) + break + + case ")": + tokens.push(BindingTranspiler.token(input, TokenKind.CLOSE_PARENTHESIS, index)) + break + + // Double operators + case "&": + case "|": + case "=": + if (input[index + 1] === input[index]) + tokens.push(BindingTranspiler.token(input, TokenKind.OPERATOR, ++index, 2)) + else tokens.push(BindingTranspiler.token(input, TokenKind.OPERATOR, index)) + break + + case "!": + case ">": + case "<": + if (input[index + 1] === "=") + tokens.push(BindingTranspiler.token(input, TokenKind.OPERATOR, ++index, 2)) + else tokens.push(BindingTranspiler.token(input, TokenKind.OPERATOR, index)) + break + + default: { + let start = index + + if (BindingTranspiler.isNumberChar(token)) { + while (BindingTranspiler.isNumberChar(input[index + 1])) index++ + tokens.push(BindingTranspiler.token(input, TokenKind.NUMBER, start, index - start + 1)) + } else if (BindingTranspiler.isWordChar(token)) { + while (BindingTranspiler.isWordChar(input[index + 1])) index++ + tokens.push(BindingTranspiler.token(input, TokenKind.WORD, start, index - start + 1)) + } + } + } + } while (++index < input.length) + + return tokens + }, + + parser(input: string, tokens: Token[]) {}, +} diff --git a/src/compilers/bindings/index.ts b/src/compilers/bindings/index.ts new file mode 100644 index 0000000..6082dd4 --- /dev/null +++ b/src/compilers/bindings/index.ts @@ -0,0 +1 @@ +export * from "./Bindings.js" diff --git a/src/compilers/bindings/types.ts b/src/compilers/bindings/types.ts new file mode 100644 index 0000000..71e8209 --- /dev/null +++ b/src/compilers/bindings/types.ts @@ -0,0 +1,20 @@ +export enum TokenKind { + VARIABLE, + NUMBER, + STRING, + TEMPLATE_STRING, + WORD, + + OPEN_PARENTHESIS, + CLOSE_PARENTHESIS, + + OPERATOR, + COMMA, +} + +export interface Token { + kind: TokenKind + value: string + start: number + length: number +} diff --git a/src/index.ts b/src/index.ts index ff6add8..d92a565 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,10 @@ import "./compilers/PreCompile.js" import "./compilers/RunEnd.js" +export * from "./components/Animation.js" export * from "./components/UI.js" -export { Animation } from "./components/Animation.js" export * from "./components/Utils.js" export * from "./types/enums/index.js" +export * from "./compilers/bindings/index.js" export * as Properties from "./types/properties/index.js" diff --git a/test/app.ts b/test/app.ts index 2799a38..7ed14ef 100644 --- a/test/app.ts +++ b/test/app.ts @@ -1,13 +1,7 @@ -import { Binding, Color, Label, Panel } from ".." +import { BindingTranspiler } from ".." -Panel({ - "#test": 123, - [Binding.IS_CREATIVE_LAYOUT]: true, - [Binding.INVENTORY_SELECTED_ITEM_COLOR]: Color(0x00ff00), -}).addChild( - Label({ - text: "Hello, World!", - shadow: true, - color: Color("#3b0202"), - }) -) +const input = "abcdef + 123456" + +const lexer = BindingTranspiler.lexer(input) + +console.log(lexer)