asajs 4.0.0-indev-2 → 4.0.0-indev-4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -2
- package/config.d.ts +29 -0
- package/dist/js/compilers/Configuration.js +20 -1
- package/dist/js/compilers/Memory.js +1 -1
- package/dist/js/compilers/PreCompile.js +12 -1
- package/dist/js/compilers/bindings/Checker.js +9 -0
- package/dist/js/compilers/bindings/{Funtion.js → Function.js} +30 -6
- package/dist/js/compilers/bindings/Lexer.js +49 -6
- package/dist/js/compilers/bindings/Parser.js +152 -12
- package/dist/js/compilers/ui/builder.js +44 -14
- package/dist/js/compilers/ui/installer.js +139 -4
- package/dist/js/compilers/ui/linker.js +36 -9
- package/dist/js/compilers/ui/manifest.js +9 -5
- package/dist/js/components/AnimationKeyframe.js +1 -1
- package/dist/js/components/UI.js +4 -3
- package/dist/js/components/Utils.js +83 -20
- package/dist/js/index.js +3 -0
- package/dist/types/compilers/Configuration.d.ts +5 -1
- package/dist/types/compilers/PreCompile.d.ts +2 -0
- package/dist/types/compilers/bindings/Checker.d.ts +3 -0
- package/dist/types/compilers/bindings/{Funtion.d.ts → Function.d.ts} +1 -1
- package/dist/types/compilers/bindings/Parser.d.ts +12 -3
- package/dist/types/compilers/ui/installer.d.ts +11 -1
- package/dist/types/compilers/ui/linker.d.ts +2 -0
- package/dist/types/compilers/ui/manifest.d.ts +1 -0
- package/dist/types/components/AnimationKeyframe.d.ts +4 -4
- package/dist/types/components/UI.d.ts +1 -0
- package/dist/types/components/Utils.d.ts +16 -16
- package/dist/types/index.d.ts +3 -0
- package/dist/types/types/enums/index.d.ts +0 -1
- package/dist/types/types/vanilla/intellisense.d.ts +11194 -11194
- package/package.json +1 -1
- package/resources/asajs.config.cjs +17 -0
- package/dist/js/compilers/RunEnd.js +0 -7
- package/dist/js/compilers/ui/builddata.js +0 -4
- package/dist/js/compilers/ui/config.js +0 -1
- package/dist/js/compilers/ui/prevdata.js +0 -8
- package/dist/js/types/enums/SmartAnimationType.js +0 -3
- package/dist/types/compilers/RunEnd.d.ts +0 -1
- package/dist/types/compilers/ui/builddata.d.ts +0 -1
- package/dist/types/compilers/ui/config.d.ts +0 -1
- package/dist/types/compilers/ui/prevdata.d.ts +0 -3
- package/dist/types/types/enums/SmartAnimationType.d.ts +0 -2
package/README.md
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="./resources/logo.png" />
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
<h2 align="center">
|
|
5
|
+
<a href="https://asajs.asakiyuki.com/">AsaJS</a> - The <a href="https://www.minecraft.net/en-us">Minecraft: Bedrock Edition</a> JSON-UI Framework
|
|
6
|
+
</h2>
|
|
7
|
+
|
|
8
|
+
[](https://www.npmjs.com/package/asajs)
|
|
9
|
+
[](https://github.com/AsakiYuki/AsaJS/blob/main/README.md)
|
|
10
|
+
[](https://www.npmjs.com/package/asajs)
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
**AsaJS** is a NodeJS library designed to empower developers to create **JSON-UI** packages for Minecraft: Bedrock Edition using **JavaScript** or **TypeScript**.
|
|
14
|
+
|
|
15
|
+
Say goodbye to the tedious and repetitive nature of manual JSON editing. AsaJS streamlines your workflow by bringing the programmatic flexibility to Minecraft UI development.
|
|
16
|
+
|
|
17
|
+
## 🛠 Installation
|
|
18
|
+
|
|
19
|
+
To use AsaJS, ensure you have **[Node.js](https://nodejs.org/en)** installed on your system.
|
|
20
|
+
|
|
21
|
+
**Latest stable version:**
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install asajs
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**For our nightly builds:**
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
npm install asajs@indev
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 📖 "Hello World!" Example
|
|
34
|
+
|
|
35
|
+
Creating a custom UI with AsaJS is straightforward. Here is how you can insert a custom label onto the Minecraft Start Screen:
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
import { Anchor, Label, Modify } from "asajs"
|
|
39
|
+
|
|
40
|
+
const label = Label({
|
|
41
|
+
text: "Hello World from my Custom UI!",
|
|
42
|
+
shadow: true,
|
|
43
|
+
anchor: Anchor.TOP_MIDDLE,
|
|
44
|
+
offset: [0, 10],
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// This code injects the label into the top of the start screen
|
|
48
|
+
Modify("start", "start_screen_content").insertChild(label)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 📚 Documentation
|
|
52
|
+
|
|
53
|
+
Detailed guides and API references are available at: 👉 https://asajs.asakiyuki.com/
|
|
54
|
+
|
|
55
|
+
## 🤝 Contributing
|
|
56
|
+
|
|
57
|
+
Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
|
package/config.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Variable } from "./src/types/properties/value.ts"
|
|
2
|
+
|
|
3
|
+
export interface Config {
|
|
4
|
+
compiler?: {
|
|
5
|
+
enabled?: boolean
|
|
6
|
+
autoImport?: boolean
|
|
7
|
+
importToPreview?: boolean
|
|
8
|
+
autoEnable?: boolean
|
|
9
|
+
gdkUserId?: string
|
|
10
|
+
}
|
|
11
|
+
packinfo?: {
|
|
12
|
+
name?: string
|
|
13
|
+
description?: string
|
|
14
|
+
version?: [number, number, number]
|
|
15
|
+
|
|
16
|
+
metadata?: {
|
|
17
|
+
authors?: string[]
|
|
18
|
+
license?: string
|
|
19
|
+
url?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
subpacks?: {
|
|
23
|
+
folder_name?: string
|
|
24
|
+
name?: string
|
|
25
|
+
memory_performance_tier?: number
|
|
26
|
+
}[]
|
|
27
|
+
}
|
|
28
|
+
global_variables?: Record<Variable, string>
|
|
29
|
+
}
|
|
@@ -1 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
if (!fs.existsSync("asajs.config.cjs")) {
|
|
4
|
+
fs.copyFileSync("node_modules/asajs/resources/asajs.config.cjs", "asajs.config.cjs");
|
|
5
|
+
}
|
|
6
|
+
if (!fs.existsSync(".gitignore")) {
|
|
7
|
+
fs.writeFileSync(".gitignore", `node_modules`, "utf-8");
|
|
8
|
+
}
|
|
9
|
+
export const config = require(path.resolve(process.cwd(), "asajs.config.cjs")).config;
|
|
10
|
+
export let isBuildMode = config.compiler?.enabled ?? false;
|
|
11
|
+
export let isLinkMode = config.compiler?.autoImport ?? false;
|
|
12
|
+
export let unLinked = !(config.compiler?.autoImport ?? true);
|
|
13
|
+
for (const arg of process.argv) {
|
|
14
|
+
if (arg === "--build")
|
|
15
|
+
isBuildMode = true;
|
|
16
|
+
if (arg === "--link")
|
|
17
|
+
isLinkMode = true;
|
|
18
|
+
else if (arg === "--unlink")
|
|
19
|
+
unLinked = true;
|
|
20
|
+
}
|
|
@@ -20,7 +20,7 @@ export class Memory extends Class {
|
|
|
20
20
|
}
|
|
21
21
|
static build() {
|
|
22
22
|
const data = new Map();
|
|
23
|
-
Memory.files.entries().forEach(([path, { elements, namespace }]) => {
|
|
23
|
+
Array.from(Memory.files.entries()).forEach(([path, { elements, namespace }]) => {
|
|
24
24
|
const record = {};
|
|
25
25
|
elements.forEach(element => (record[element.name] = element));
|
|
26
26
|
data.set(path, {
|
|
@@ -6,4 +6,15 @@ Map.prototype.toJSON = function () {
|
|
|
6
6
|
Array.prototype.lastItem = function () {
|
|
7
7
|
return this[this.length - 1];
|
|
8
8
|
};
|
|
9
|
-
|
|
9
|
+
const now = performance.now();
|
|
10
|
+
function TypeHighlight(type) {
|
|
11
|
+
switch (type) {
|
|
12
|
+
case "INFO":
|
|
13
|
+
return `\x1b[32mINFO\x1b[0m`;
|
|
14
|
+
default:
|
|
15
|
+
return type;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function Log(type, message) {
|
|
19
|
+
console.log(`\x1b[90m[${(performance.now() - now).toFixed(2)}ms]\x1b[0m`, `[${TypeHighlight(type)}]`, message, "\x1b[0m");
|
|
20
|
+
}
|
|
@@ -7,6 +7,15 @@ export function isWordChar(char) {
|
|
|
7
7
|
export function isNumberChar(char) {
|
|
8
8
|
return /\d/.test(char);
|
|
9
9
|
}
|
|
10
|
+
export function isHexChar(char) {
|
|
11
|
+
return /[0-9a-fA-F]/.test(char);
|
|
12
|
+
}
|
|
13
|
+
export function isBinaryChar(char) {
|
|
14
|
+
return /[01]/.test(char);
|
|
15
|
+
}
|
|
16
|
+
export function isOctalChar(char) {
|
|
17
|
+
return /[0-7]/.test(char);
|
|
18
|
+
}
|
|
10
19
|
export function isCompileBinding(input) {
|
|
11
20
|
return input.startsWith("[") && input.endsWith("]");
|
|
12
21
|
}
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
import { RandomBindingString } from "../../components/Utils.js";
|
|
2
|
-
|
|
2
|
+
import { Parser } from "./Parser.js";
|
|
3
|
+
export const FunctionMap = new Map();
|
|
3
4
|
function callFn(name, ...args) {
|
|
4
|
-
return
|
|
5
|
+
return FunctionMap.get(name)(...args);
|
|
5
6
|
}
|
|
6
7
|
// Default Functions
|
|
7
|
-
|
|
8
|
+
FunctionMap.set("abs", number => {
|
|
8
9
|
const randomBinding = RandomBindingString(16);
|
|
9
10
|
return {
|
|
10
11
|
genBindings: [{ source: `((-1 + (${number} > 0) * 2) * ${number})`, target: randomBinding }],
|
|
11
12
|
value: randomBinding,
|
|
12
13
|
};
|
|
13
14
|
});
|
|
14
|
-
|
|
15
|
+
FunctionMap.set("new", expression => {
|
|
15
16
|
const randomBinding = RandomBindingString(16);
|
|
16
17
|
return {
|
|
17
18
|
genBindings: [{ source: expression, target: randomBinding }],
|
|
18
19
|
value: randomBinding,
|
|
19
20
|
};
|
|
20
21
|
});
|
|
21
|
-
|
|
22
|
+
FunctionMap.set("sqrt", number => {
|
|
22
23
|
const rtn = RandomBindingString(16), $1 = RandomBindingString(16), $2 = RandomBindingString(16);
|
|
23
24
|
const { genBindings: absValue, value: absRtn } = callFn("abs", number);
|
|
24
25
|
return {
|
|
@@ -40,8 +41,31 @@ FuntionMap.set("sqrt", number => {
|
|
|
40
41
|
value: rtn,
|
|
41
42
|
};
|
|
42
43
|
});
|
|
43
|
-
|
|
44
|
+
FunctionMap.set("translatable", key => {
|
|
44
45
|
return {
|
|
45
46
|
value: `'%' + ${key}`,
|
|
46
47
|
};
|
|
47
48
|
});
|
|
49
|
+
FunctionMap.set("bin", input => {
|
|
50
|
+
const { ret, bindings } = Parser.intToBin(input);
|
|
51
|
+
bindings.push({
|
|
52
|
+
source: `'§z' + ${Array.from({ length: 30 }, (_, i) => `${ret}${30 - i - 1}`).join(" + ")}`,
|
|
53
|
+
target: ret,
|
|
54
|
+
});
|
|
55
|
+
return { genBindings: bindings, value: ret };
|
|
56
|
+
});
|
|
57
|
+
FunctionMap.set("bind", (value, bait) => {
|
|
58
|
+
const ret = RandomBindingString(16);
|
|
59
|
+
if (!bait) {
|
|
60
|
+
throw new Error("Bait is required");
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
genBindings: [{ source: `((${bait} - ${bait}) + ${value})`, target: ret }],
|
|
64
|
+
value: ret,
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
FunctionMap.set("int", input => {
|
|
68
|
+
return {
|
|
69
|
+
value: input,
|
|
70
|
+
};
|
|
71
|
+
});
|
|
@@ -39,6 +39,7 @@ export function Lexer(input, start = 0, end) {
|
|
|
39
39
|
case "*":
|
|
40
40
|
case "/":
|
|
41
41
|
case "%":
|
|
42
|
+
case "^":
|
|
42
43
|
tokens.push(makeToken(input, TokenKind.OPERATOR, index));
|
|
43
44
|
break;
|
|
44
45
|
case "(":
|
|
@@ -53,18 +54,24 @@ export function Lexer(input, start = 0, end) {
|
|
|
53
54
|
case "=":
|
|
54
55
|
if (input[index + 1] === input[index])
|
|
55
56
|
tokens.push(makeToken(input, TokenKind.OPERATOR, index++, 2));
|
|
56
|
-
else
|
|
57
|
-
|
|
58
|
-
throw new Error();
|
|
59
|
-
}
|
|
57
|
+
else
|
|
58
|
+
tokens.push(makeToken(input, TokenKind.OPERATOR, index));
|
|
60
59
|
break;
|
|
61
60
|
case "!":
|
|
62
61
|
case ">":
|
|
63
62
|
case "<":
|
|
64
63
|
if (input[index + 1] === "=")
|
|
65
64
|
tokens.push(makeToken(input, TokenKind.OPERATOR, index++, 2));
|
|
66
|
-
else
|
|
67
|
-
|
|
65
|
+
else {
|
|
66
|
+
if (input[index] === input[index + 1]) {
|
|
67
|
+
if (input[index] !== "!")
|
|
68
|
+
tokens.push(makeToken(input, TokenKind.OPERATOR, index++, 2));
|
|
69
|
+
else
|
|
70
|
+
tokens.push(makeToken(input, TokenKind.OPERATOR, index));
|
|
71
|
+
}
|
|
72
|
+
else
|
|
73
|
+
tokens.push(makeToken(input, TokenKind.OPERATOR, index));
|
|
74
|
+
}
|
|
68
75
|
break;
|
|
69
76
|
// string
|
|
70
77
|
case "'": {
|
|
@@ -168,6 +175,42 @@ export function Lexer(input, start = 0, end) {
|
|
|
168
175
|
default: {
|
|
169
176
|
let start = index;
|
|
170
177
|
if (Checker.isNumberChar(token)) {
|
|
178
|
+
if (token === "0") {
|
|
179
|
+
const numType = input[index + 1];
|
|
180
|
+
if (numType === "x") {
|
|
181
|
+
index += 2;
|
|
182
|
+
while (Checker.isHexChar(input[index + 1]))
|
|
183
|
+
index++;
|
|
184
|
+
if (start + 2 === index) {
|
|
185
|
+
console.error(`\x1b[31merror: ${input + "\n" + " ".repeat(index + 6) + "^"}\nInvalid character.\x1b[0m`);
|
|
186
|
+
throw new Error();
|
|
187
|
+
}
|
|
188
|
+
tokens.push(makeToken(input, TokenKind.NUMBER, start, index - start + 1));
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
else if (numType === "b") {
|
|
192
|
+
index += 2;
|
|
193
|
+
while (Checker.isBinaryChar(input[index + 1]))
|
|
194
|
+
index++;
|
|
195
|
+
tokens.push(makeToken(input, TokenKind.NUMBER, start, index - start + 1));
|
|
196
|
+
if (start + 2 === index) {
|
|
197
|
+
console.error(`\x1b[31merror: ${input + "\n" + " ".repeat(index + 6) + "^"}\nInvalid character.\x1b[0m`);
|
|
198
|
+
throw new Error();
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
else if (numType === "o") {
|
|
203
|
+
index += 2;
|
|
204
|
+
while (Checker.isOctalChar(input[index + 1]))
|
|
205
|
+
index++;
|
|
206
|
+
tokens.push(makeToken(input, TokenKind.NUMBER, start, index - start + 1));
|
|
207
|
+
if (start + 2 === index) {
|
|
208
|
+
console.error(`\x1b[31merror: ${input + "\n" + " ".repeat(index + 6) + "^"}\nInvalid character.\x1b[0m`);
|
|
209
|
+
throw new Error();
|
|
210
|
+
}
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
171
214
|
while (Checker.isNumberChar(input[index + 1]))
|
|
172
215
|
index++;
|
|
173
216
|
if (input[index + 1] === "e") {
|
|
@@ -1,21 +1,55 @@
|
|
|
1
1
|
import { TokenKind, TSTokenKind } from "./types.js";
|
|
2
2
|
import { BindingType } from "../../types/enums/BindingType.js";
|
|
3
|
-
import {
|
|
3
|
+
import { FunctionMap } from "./Function.js";
|
|
4
4
|
import { Lexer } from "./Lexer.js";
|
|
5
|
+
import { RandomBindingString } from "../../components/Utils.js";
|
|
5
6
|
export class Parser {
|
|
6
7
|
input;
|
|
8
|
+
cache;
|
|
7
9
|
position = 0;
|
|
8
|
-
tokens;
|
|
9
10
|
genBindings = [];
|
|
10
11
|
output;
|
|
11
|
-
|
|
12
|
+
tokens;
|
|
13
|
+
constructor(input, cache = new Map(), tokens) {
|
|
12
14
|
this.input = input;
|
|
13
|
-
this.
|
|
15
|
+
this.cache = cache;
|
|
16
|
+
if (tokens) {
|
|
17
|
+
this.tokens = tokens;
|
|
18
|
+
tokens = undefined;
|
|
19
|
+
}
|
|
20
|
+
else
|
|
21
|
+
this.tokens = Lexer(input);
|
|
14
22
|
this.output = this.parseExpression();
|
|
15
23
|
if (this.at()) {
|
|
16
24
|
this.expect(TokenKind.EOF, "Unexpected token!");
|
|
17
25
|
}
|
|
18
26
|
}
|
|
27
|
+
static intToBin(input) {
|
|
28
|
+
const bindings = [];
|
|
29
|
+
const rtn = RandomBindingString(16);
|
|
30
|
+
for (let i = 0; i < 30; i++) {
|
|
31
|
+
bindings.push({
|
|
32
|
+
source: `(${input} / ${2 ** i} - (${input} / ${2 ** (i + 1)}) * 2)`,
|
|
33
|
+
target: `${rtn}${i}`,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
ret: rtn,
|
|
38
|
+
bindings,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
intToBin(input) {
|
|
42
|
+
const inStr = `expr:${input}`;
|
|
43
|
+
if (this.cache.has(inStr)) {
|
|
44
|
+
return this.cache.get(inStr);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const { ret, bindings } = Parser.intToBin(input);
|
|
48
|
+
this.cache.set(inStr, ret);
|
|
49
|
+
this.genBindings.push(...bindings);
|
|
50
|
+
return ret;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
19
53
|
at() {
|
|
20
54
|
return this.tokens[this.position];
|
|
21
55
|
}
|
|
@@ -81,19 +115,106 @@ export class Parser {
|
|
|
81
115
|
return left;
|
|
82
116
|
}
|
|
83
117
|
parseMultiplicativeExpression() {
|
|
84
|
-
let left = this.
|
|
118
|
+
let left = this.parseBitwiseLogicExpression(), current;
|
|
85
119
|
while ((current = this.at()) &&
|
|
86
120
|
this.at()?.kind === TokenKind.OPERATOR &&
|
|
87
121
|
["*", "/", "%"].includes(current.value)) {
|
|
88
122
|
const operator = this.eat();
|
|
89
123
|
const right = this.parsePrimaryExpression();
|
|
90
|
-
if (current.value === "%")
|
|
91
|
-
|
|
124
|
+
if (current.value === "%") {
|
|
125
|
+
const cacheStr = `expr:${left}${operator.value}${right}`;
|
|
126
|
+
if (this.cache.has(cacheStr)) {
|
|
127
|
+
return (left = this.cache.get(cacheStr));
|
|
128
|
+
}
|
|
129
|
+
const ret = RandomBindingString(16);
|
|
130
|
+
this.genBindings.push({
|
|
131
|
+
source: `(${left} - (${left} / ${right} * ${right}))`,
|
|
132
|
+
target: ret,
|
|
133
|
+
});
|
|
134
|
+
this.cache.set(cacheStr, ret);
|
|
135
|
+
left = ret;
|
|
136
|
+
}
|
|
92
137
|
else
|
|
93
138
|
left = `(${left} ${operator.value} ${right})`;
|
|
94
139
|
}
|
|
95
140
|
return left;
|
|
96
141
|
}
|
|
142
|
+
parseBitwiseLogicExpression() {
|
|
143
|
+
let left = this.parseBitwiseShiftExpression(), current;
|
|
144
|
+
while ((current = this.at()) &&
|
|
145
|
+
this.at()?.kind === TokenKind.OPERATOR &&
|
|
146
|
+
["&", "|", "^"].includes(current.value)) {
|
|
147
|
+
const operator = this.eat();
|
|
148
|
+
const right = this.parsePrimaryExpression();
|
|
149
|
+
const cacheStr = `expr:${left}${operator.value}${right}`;
|
|
150
|
+
if (this.cache.has(cacheStr)) {
|
|
151
|
+
return (left = this.cache.get(cacheStr));
|
|
152
|
+
}
|
|
153
|
+
const ret = RandomBindingString(16);
|
|
154
|
+
this.cache.set(cacheStr, ret);
|
|
155
|
+
const leftBin = this.intToBin(left);
|
|
156
|
+
const rightBin = this.intToBin(right);
|
|
157
|
+
if (operator.value === "&") {
|
|
158
|
+
this.genBindings.push(...Array.from({ length: 30 }, (_, i) => {
|
|
159
|
+
return {
|
|
160
|
+
source: `(${leftBin}${i} * ${rightBin}${i})`,
|
|
161
|
+
target: `${ret}${i}`,
|
|
162
|
+
};
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
else if (operator.value === "|") {
|
|
166
|
+
this.genBindings.push(...Array.from({ length: 30 }, (_, i) => {
|
|
167
|
+
return {
|
|
168
|
+
source: `(${leftBin}${i} + ${rightBin}${i} - (${leftBin}${i} * ${rightBin}${i}))`,
|
|
169
|
+
target: `${ret}${i}`,
|
|
170
|
+
};
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
this.genBindings.push(...Array.from({ length: 30 }, (_, i) => {
|
|
175
|
+
return {
|
|
176
|
+
source: `(${leftBin}${i} + ${rightBin}${i} - 2 * (${leftBin}${i} * ${rightBin}${i}))`,
|
|
177
|
+
target: `${ret}${i}`,
|
|
178
|
+
};
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
this.genBindings.push({
|
|
182
|
+
source: `(${Array.from({ length: 30 }, (_, i) => `(${ret}${i} * ${2 ** i})`).join(" + ")})`,
|
|
183
|
+
target: ret,
|
|
184
|
+
});
|
|
185
|
+
left = ret;
|
|
186
|
+
}
|
|
187
|
+
return left;
|
|
188
|
+
}
|
|
189
|
+
parseBitwiseShiftExpression() {
|
|
190
|
+
let left = this.parsePrimaryExpression(), current;
|
|
191
|
+
while ((current = this.at()) &&
|
|
192
|
+
this.at()?.kind === TokenKind.OPERATOR &&
|
|
193
|
+
[">>", "<<"].includes(current.value)) {
|
|
194
|
+
const operator = this.eat();
|
|
195
|
+
const right = this.parsePrimaryExpression();
|
|
196
|
+
const cacheStr = `expr:${left}${operator.value}${right}`;
|
|
197
|
+
if (this.cache.has(cacheStr)) {
|
|
198
|
+
return (left = this.cache.get(cacheStr));
|
|
199
|
+
}
|
|
200
|
+
const ret = RandomBindingString(16);
|
|
201
|
+
this.cache.set(cacheStr, ret);
|
|
202
|
+
const leftBind = this.intToBin(left);
|
|
203
|
+
const op = operator.value === "<<" ? "-" : "+";
|
|
204
|
+
this.genBindings.push(...Array.from({ length: 30 }, (_, i) => {
|
|
205
|
+
return {
|
|
206
|
+
source: `((0 * ${left}) + ('${leftBind}' + (${i} ${op} ${right})))`,
|
|
207
|
+
target: `${ret}${i}`,
|
|
208
|
+
};
|
|
209
|
+
}));
|
|
210
|
+
this.genBindings.push({
|
|
211
|
+
source: `(${Array.from({ length: 30 }, (_, i) => `(${ret}${i} * ${2 ** i})`).join(" + ")})`,
|
|
212
|
+
target: ret,
|
|
213
|
+
});
|
|
214
|
+
left = ret;
|
|
215
|
+
}
|
|
216
|
+
return left;
|
|
217
|
+
}
|
|
97
218
|
parsePrimaryExpression() {
|
|
98
219
|
const left = this.at();
|
|
99
220
|
switch (left?.kind) {
|
|
@@ -107,8 +228,18 @@ export class Parser {
|
|
|
107
228
|
return `(${value})`;
|
|
108
229
|
}
|
|
109
230
|
case TokenKind.NUMBER: {
|
|
110
|
-
const
|
|
111
|
-
|
|
231
|
+
const numberToken = this.eat();
|
|
232
|
+
switch (numberToken.value[1]) {
|
|
233
|
+
case "x":
|
|
234
|
+
return "" + parseInt(numberToken.value.slice(2), 16);
|
|
235
|
+
case "o":
|
|
236
|
+
return "" + parseInt(numberToken.value.slice(2), 8);
|
|
237
|
+
case "b":
|
|
238
|
+
return "" + parseInt(numberToken.value.slice(2), 2);
|
|
239
|
+
default:
|
|
240
|
+
const [num, exp] = numberToken.value.split("e");
|
|
241
|
+
return "" + (exp ? +num * 10 ** +exp : num);
|
|
242
|
+
}
|
|
112
243
|
}
|
|
113
244
|
case TokenKind.VARIABLE:
|
|
114
245
|
case TokenKind.STRING:
|
|
@@ -184,7 +315,15 @@ export class Parser {
|
|
|
184
315
|
}
|
|
185
316
|
}
|
|
186
317
|
this.eat();
|
|
187
|
-
|
|
318
|
+
const inputStr = `func:${callerToken.value}(${args.join(", ")})`;
|
|
319
|
+
if (this.cache.has(inputStr)) {
|
|
320
|
+
return this.cache.get(inputStr);
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
const ret = this.functionCall(callerToken.value, ...args);
|
|
324
|
+
this.cache.set(inputStr, ret);
|
|
325
|
+
return ret;
|
|
326
|
+
}
|
|
188
327
|
}
|
|
189
328
|
else if (left?.kind === TokenKind.OPERATOR) {
|
|
190
329
|
this.warn(`Implicit string literal '${callerToken.value}'. Use quoted string ('${callerToken.value}') for clarity!`, callerToken);
|
|
@@ -198,8 +337,8 @@ export class Parser {
|
|
|
198
337
|
return callerToken.value;
|
|
199
338
|
}
|
|
200
339
|
}
|
|
201
|
-
|
|
202
|
-
const func =
|
|
340
|
+
functionCall(name, ...params) {
|
|
341
|
+
const func = FunctionMap.get(name.toLowerCase());
|
|
203
342
|
if (!func) {
|
|
204
343
|
return this.expect(TokenKind.WORD, "Function not found!");
|
|
205
344
|
}
|
|
@@ -226,6 +365,7 @@ export class Parser {
|
|
|
226
365
|
out() {
|
|
227
366
|
return {
|
|
228
367
|
out: `(${this.output})`,
|
|
368
|
+
cache: this.cache,
|
|
229
369
|
gen: this.genBindings.map(({ source, target }) => ({
|
|
230
370
|
binding_type: BindingType.VIEW,
|
|
231
371
|
source_property_name: `(${source})`,
|
|
@@ -1,34 +1,46 @@
|
|
|
1
|
-
import { isBuildMode } from "../Configuration.js";
|
|
1
|
+
import { config, isBuildMode, isLinkMode, unLinked } from "../Configuration.js";
|
|
2
2
|
import { Memory } from "../Memory.js";
|
|
3
|
-
import { createBuildFolder, linkToGame } from "./linker.js";
|
|
4
|
-
import { genManifest } from "./manifest.js";
|
|
3
|
+
import { createBuildFolder, gamePath, getBuildFolderName, linkToGame, unlink } from "./linker.js";
|
|
4
|
+
import { genManifest, version } from "./manifest.js";
|
|
5
5
|
import fs from "fs/promises";
|
|
6
|
+
import { BuildCache } from "./buildcache.js";
|
|
7
|
+
import { disableRSP, enableRSP } from "./installer.js";
|
|
8
|
+
import { Log } from "../PreCompile.js";
|
|
9
|
+
import path from "path";
|
|
6
10
|
async function buildUI() {
|
|
7
11
|
const build = Memory.build();
|
|
8
12
|
build.set("ui/_ui_defs.json", {
|
|
9
13
|
ui_defs: Array.from(build.keys()),
|
|
10
14
|
});
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
});
|
|
15
|
+
if (config.global_variables)
|
|
16
|
+
build.set("ui/_global_variables.json", config.global_variables);
|
|
14
17
|
const out = await Promise.all(build.entries().map(async ([file, value]) => {
|
|
15
|
-
const outFile = `build
|
|
18
|
+
const outFile = `build/${file}`;
|
|
16
19
|
await fs
|
|
17
20
|
.stat(outFile.split(/\\|\//g).slice(0, -1).join("/"))
|
|
18
21
|
.catch(async () => await fs.mkdir(outFile.split(/\\|\//g).slice(0, -1).join("/"), { recursive: true }));
|
|
19
|
-
await fs
|
|
22
|
+
await fs
|
|
23
|
+
.writeFile(outFile, JSON.stringify(Object.fromEntries(Object.entries(value).map(([key, value]) => {
|
|
20
24
|
const extend = value.extend;
|
|
21
25
|
return [extend ? key + String(extend) : key, value];
|
|
22
|
-
}))), "utf-8")
|
|
26
|
+
}))), "utf-8")
|
|
27
|
+
.then(() => Log("INFO", `${outFile} with ${Object.keys(value).length} elements created!`));
|
|
23
28
|
build.delete(file);
|
|
24
29
|
return file;
|
|
25
30
|
}));
|
|
26
31
|
await Promise.all([
|
|
27
|
-
fs.writeFile("build/build/manifest.json", await genManifest(), "utf-8"),
|
|
28
|
-
fs.writeFile("build/build/.gitignore", [...out, "manifest.json"].join("\n"), "utf-8"),
|
|
29
32
|
fs
|
|
30
|
-
.
|
|
31
|
-
.
|
|
33
|
+
.writeFile("build/manifest.json", await genManifest(), "utf-8")
|
|
34
|
+
.then(() => Log("INFO", "build/manifest.json created!")),
|
|
35
|
+
fs
|
|
36
|
+
.writeFile("build/.gitignore", [...out, "manifest.json"].join("\n"), "utf-8")
|
|
37
|
+
.then(() => Log("INFO", "build/.gitignore created!")),
|
|
38
|
+
BuildCache.set("build-files", [...out, "manifest.json"]).then(() => Log("INFO", "build-files set!")),
|
|
39
|
+
BuildCache.set("version", version).then(() => Log("INFO", "version set!")),
|
|
40
|
+
fs
|
|
41
|
+
.stat("build/pack_icon.png")
|
|
42
|
+
.catch(() => fs.copyFile("node_modules/asajs/resources/pack_icon.png", "build/pack_icon.png"))
|
|
43
|
+
.then(() => Log("INFO", "build/pack_icon.png copied!")),
|
|
32
44
|
]);
|
|
33
45
|
return out.length;
|
|
34
46
|
}
|
|
@@ -38,8 +50,26 @@ if (isBuildMode) {
|
|
|
38
50
|
if (first) {
|
|
39
51
|
await createBuildFolder();
|
|
40
52
|
await buildUI();
|
|
41
|
-
|
|
53
|
+
if (isLinkMode)
|
|
54
|
+
await linkToGame();
|
|
55
|
+
if (config.compiler?.autoEnable)
|
|
56
|
+
await enableRSP();
|
|
57
|
+
else
|
|
58
|
+
await disableRSP();
|
|
59
|
+
Log("INFO", "Build completed!");
|
|
60
|
+
console.log("========================= PACK INFO =========================");
|
|
61
|
+
console.log("Name:", `\x1b[32m${config.packinfo?.name || "MyPack"}\x1b[0m`);
|
|
62
|
+
console.log("Description:", `\x1b[32m${config.packinfo?.description || "Create your Minecraft JSON-UI resource packs using JavaScript."}\x1b[0m`);
|
|
63
|
+
console.log("Version:", config.packinfo?.version || [4, 0, 0]);
|
|
64
|
+
console.log("UUID:", await BuildCache.get("uuid"));
|
|
65
|
+
if (gamePath)
|
|
66
|
+
console.log("Install Path:", `\x1b[32m"${path.join(gamePath, await getBuildFolderName())}"\x1b[0m`);
|
|
67
|
+
console.log("=============================================================");
|
|
42
68
|
}
|
|
43
69
|
first = false;
|
|
44
70
|
});
|
|
45
71
|
}
|
|
72
|
+
else if (isLinkMode)
|
|
73
|
+
linkToGame();
|
|
74
|
+
else if (unLinked)
|
|
75
|
+
unlink();
|