hzengine-core 0.1.2-dev
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/dist/async/index.js +162 -0
- package/dist/async/zeppos_timer.js +58 -0
- package/dist/audio/index.js +260 -0
- package/dist/config/index.js +57 -0
- package/dist/debug/index.js +8 -0
- package/dist/index.js +103 -0
- package/dist/platform/index.js +1 -0
- package/dist/plugins/basic_command/$.js +8 -0
- package/dist/plugins/basic_command/audio.js +40 -0
- package/dist/plugins/basic_command/basic.js +124 -0
- package/dist/plugins/basic_command/character.js +112 -0
- package/dist/plugins/basic_command/conditional.js +260 -0
- package/dist/plugins/basic_command/config.js +22 -0
- package/dist/plugins/basic_command/decorator.js +24 -0
- package/dist/plugins/basic_command/eval.js +67 -0
- package/dist/plugins/basic_command/img.js +249 -0
- package/dist/plugins/basic_command/index.js +22 -0
- package/dist/plugins/basic_command/menu.js +140 -0
- package/dist/plugins/global_gesture/index.js +25 -0
- package/dist/plugins/transform/animation.js +440 -0
- package/dist/plugins/transform/commands.js +38 -0
- package/dist/plugins/transform/example_profiles.js +32 -0
- package/dist/plugins/transform/hz_anime.js +211 -0
- package/dist/plugins/transform/index.js +93 -0
- package/dist/script/index.js +537 -0
- package/dist/script/readscript.js +15 -0
- package/dist/script/strtools.js +157 -0
- package/dist/storage/decorator.js +260 -0
- package/dist/storage/fs.js +96 -0
- package/dist/storage/index.js +442 -0
- package/dist/system/index.js +144 -0
- package/dist/ui/index.js +535 -0
- package/dist/utils/path.js +289 -0
- package/license.txt +202 -0
- package/package.json +26 -0
- package/src/async/index.ts +124 -0
- package/src/async/zeppos_timer.js +65 -0
- package/src/audio/index.ts +224 -0
- package/src/config/index.ts +80 -0
- package/src/debug/index.ts +11 -0
- package/src/index.ts +122 -0
- package/src/platform/index.ts +158 -0
- package/src/plugins/basic_command/$.ts +11 -0
- package/src/plugins/basic_command/audio.ts +53 -0
- package/src/plugins/basic_command/basic.ts +145 -0
- package/src/plugins/basic_command/character.ts +144 -0
- package/src/plugins/basic_command/conditional.ts +349 -0
- package/src/plugins/basic_command/config.ts +29 -0
- package/src/plugins/basic_command/decorator.ts +29 -0
- package/src/plugins/basic_command/eval.ts +88 -0
- package/src/plugins/basic_command/img.ts +317 -0
- package/src/plugins/basic_command/index.ts +24 -0
- package/src/plugins/basic_command/menu.ts +178 -0
- package/src/plugins/global_gesture/index.ts +29 -0
- package/src/plugins/transform/animation.ts +542 -0
- package/src/plugins/transform/commands.ts +53 -0
- package/src/plugins/transform/example_profiles.ts +36 -0
- package/src/plugins/transform/hz_anime.ts +214 -0
- package/src/plugins/transform/index.ts +141 -0
- package/src/plugins/transform/readme.md +1 -0
- package/src/script/index.ts +623 -0
- package/src/script/readscript.ts +17 -0
- package/src/script/strtools.ts +159 -0
- package/src/storage/decorator.ts +473 -0
- package/src/storage/fs.ts +104 -0
- package/src/storage/index.ts +541 -0
- package/src/system/index.ts +95 -0
- package/src/ui/index.ts +699 -0
- package/src/utils/path.js +338 -0
- package/tsconfig.json +111 -0
- package/types/async/index.d.ts +24 -0
- package/types/async/zeppos_timer.d.ts +14 -0
- package/types/audio/index.d.ts +64 -0
- package/types/config/index.d.ts +9 -0
- package/types/debug/index.d.ts +6 -0
- package/types/index.d.ts +41 -0
- package/types/platform/index.d.ts +134 -0
- package/types/plugins/basic_command/$.d.ts +2 -0
- package/types/plugins/basic_command/audio.d.ts +2 -0
- package/types/plugins/basic_command/basic.d.ts +3 -0
- package/types/plugins/basic_command/character.d.ts +2 -0
- package/types/plugins/basic_command/conditional.d.ts +2 -0
- package/types/plugins/basic_command/config.d.ts +2 -0
- package/types/plugins/basic_command/decorator.d.ts +2 -0
- package/types/plugins/basic_command/eval.d.ts +2 -0
- package/types/plugins/basic_command/img.d.ts +2 -0
- package/types/plugins/basic_command/index.d.ts +2 -0
- package/types/plugins/basic_command/menu.d.ts +2 -0
- package/types/plugins/global_gesture/index.d.ts +2 -0
- package/types/plugins/transform/animation.d.ts +131 -0
- package/types/plugins/transform/commands.d.ts +7 -0
- package/types/plugins/transform/example_profiles.d.ts +2 -0
- package/types/plugins/transform/hz_anime.d.ts +51 -0
- package/types/plugins/transform/index.d.ts +13 -0
- package/types/script/index.d.ts +123 -0
- package/types/script/readscript.d.ts +2 -0
- package/types/script/strtools.d.ts +31 -0
- package/types/storage/decorator.d.ts +41 -0
- package/types/storage/fs.d.ts +1 -0
- package/types/storage/index.d.ts +86 -0
- package/types/system/index.d.ts +35 -0
- package/types/ui/index.d.ts +167 -0
- package/types/utils/path.d.ts +84 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import Path from "../../utils/path.js";
|
|
2
|
+
export function audio_command(core) {
|
|
3
|
+
// play command: play <channel> "<path>"
|
|
4
|
+
core.script.use((ctx, next) => {
|
|
5
|
+
if (ctx.rawtext.trim().split(" ")[0].toLowerCase() !== "play" &&
|
|
6
|
+
ctx.rawtext.trim().split(" ")[0].toLowerCase() !== "queue")
|
|
7
|
+
return next();
|
|
8
|
+
if (ctx.slicedArgs.length !== 3)
|
|
9
|
+
throw `${ctx.slicedArgs[0].str.toUpperCase()} Command: incorrect amount of args`;
|
|
10
|
+
if (!ctx.slicedArgs[2].isQuoted)
|
|
11
|
+
throw `the third arg of ${ctx.slicedArgs[0].str} command should be quoted as string path`;
|
|
12
|
+
let channel_name = ctx.slicedArgs[1].str;
|
|
13
|
+
let path = ctx.slicedArgs[2].str;
|
|
14
|
+
core.debug.log(`Play Command: play ${channel_name} ${path}`);
|
|
15
|
+
let channel = core.audio.channels[channel_name];
|
|
16
|
+
if (!channel) {
|
|
17
|
+
throw `audio channel ${channel_name} not found`;
|
|
18
|
+
}
|
|
19
|
+
if (ctx.slicedArgs[0].str !== "queue") {
|
|
20
|
+
channel.stop();
|
|
21
|
+
}
|
|
22
|
+
channel.push({ path: Path.join(core.storage.projectRoot, "audio", path) });
|
|
23
|
+
channel.play();
|
|
24
|
+
});
|
|
25
|
+
// stop command: stop <channel>
|
|
26
|
+
core.script.use((ctx, next) => {
|
|
27
|
+
if (ctx.rawtext.trim().split(" ")[0].toLowerCase() !== "stop") {
|
|
28
|
+
return next();
|
|
29
|
+
}
|
|
30
|
+
if (ctx.slicedArgs.length !== 2) {
|
|
31
|
+
throw `Stop Command: incorrect amount of args`;
|
|
32
|
+
}
|
|
33
|
+
let channel_name = ctx.slicedArgs[1].str;
|
|
34
|
+
let channel = core.audio.channels[channel_name];
|
|
35
|
+
if (!channel) {
|
|
36
|
+
throw `audio channel ${channel_name} not found`;
|
|
37
|
+
}
|
|
38
|
+
channel.stop();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export function basic_commands(core) {
|
|
2
|
+
// jump command
|
|
3
|
+
core.script.use((ctx, next) => {
|
|
4
|
+
let strArr = ctx.rawtext.trim().split(/ +/);
|
|
5
|
+
if (strArr.length === 0 || strArr[0].toLowerCase() !== "jump")
|
|
6
|
+
return next();
|
|
7
|
+
if (strArr.length !== 2)
|
|
8
|
+
throw "Jump Command: incorrect amount of args";
|
|
9
|
+
core.debug.log(`Jump Command: jump to label [${strArr[1]}]`);
|
|
10
|
+
core.script.jumpLabel(strArr[1]);
|
|
11
|
+
});
|
|
12
|
+
// call command
|
|
13
|
+
core.script.use((ctx, next) => {
|
|
14
|
+
let strArr = ctx.rawtext.trim().split(/ +/);
|
|
15
|
+
if (strArr.length === 0 || strArr[0].toLowerCase() !== "call")
|
|
16
|
+
return next();
|
|
17
|
+
if (strArr.length !== 2)
|
|
18
|
+
throw "Call Command: incorrect amount of args";
|
|
19
|
+
core.debug.log(`Call Command: call label [${strArr[1]}]`);
|
|
20
|
+
core.script.callLabel(strArr[1]);
|
|
21
|
+
});
|
|
22
|
+
// return command
|
|
23
|
+
core.script.use((ctx, next) => {
|
|
24
|
+
let strArr = ctx.rawtext.trim().split(/ +/);
|
|
25
|
+
if (strArr.length === 0 || strArr[0].toLowerCase() !== "return")
|
|
26
|
+
return next();
|
|
27
|
+
if (strArr.length !== 1)
|
|
28
|
+
throw "Return Command: this command can not have args";
|
|
29
|
+
core.debug.log(`Return Command: return`);
|
|
30
|
+
core.script.return();
|
|
31
|
+
});
|
|
32
|
+
// (debug) echo command
|
|
33
|
+
core.script.use((ctx, next) => {
|
|
34
|
+
let str = ctx.rawtext.trim();
|
|
35
|
+
if (!str.startsWith("echo"))
|
|
36
|
+
return next();
|
|
37
|
+
core.debug.log(`[ECHO] ${core.script.parseString(str.slice(4).trim())}`);
|
|
38
|
+
});
|
|
39
|
+
// say command
|
|
40
|
+
core.script.use((ctx, next) => {
|
|
41
|
+
if (!ctx.rawtext.trim().startsWith('"'))
|
|
42
|
+
return next();
|
|
43
|
+
function parseSayCommandArgs() {
|
|
44
|
+
if (ctx.slicedArgs.length > 0 &&
|
|
45
|
+
ctx.slicedArgs[ctx.slicedArgs.length - 1].str === "nowait") {
|
|
46
|
+
ctx.slicedArgs = ctx.slicedArgs.slice(0, ctx.slicedArgs.length - 1);
|
|
47
|
+
return {
|
|
48
|
+
wait: false,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
return {
|
|
53
|
+
wait: true,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
let parsed = parseSayCommandArgs();
|
|
58
|
+
if (ctx.slicedArgs.length > 2 || ctx.slicedArgs.length < 1) {
|
|
59
|
+
throw `Say Command: incorrect amount of args`;
|
|
60
|
+
}
|
|
61
|
+
if (ctx.slicedArgs.length === 1) {
|
|
62
|
+
core.debug.log(`[SAY] ${ctx.slicedArgs[0].str}`);
|
|
63
|
+
sayAction(core, "", ctx.slicedArgs[0].str, parsed.wait);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
if (!ctx.slicedArgs[1].isQuoted)
|
|
67
|
+
throw `Say Command: second arg should be quoted`;
|
|
68
|
+
core.debug.log(`[SAY] ${ctx.slicedArgs[0].str}: ${ctx.slicedArgs[1].str}`);
|
|
69
|
+
sayAction(core, ctx.slicedArgs[0].str, ctx.slicedArgs[1].str, parsed.wait);
|
|
70
|
+
}
|
|
71
|
+
// if (parsed?.wait) core.system.pause();
|
|
72
|
+
});
|
|
73
|
+
// pause command
|
|
74
|
+
core.script.use((ctx, next) => {
|
|
75
|
+
if (ctx.rawtext.trim().split(" ")[0].toLowerCase() !== "pause")
|
|
76
|
+
return next();
|
|
77
|
+
if (ctx.slicedArgs.length > 2)
|
|
78
|
+
throw `Pause Command: incorrect amount of args`;
|
|
79
|
+
if (ctx.slicedArgs.length === 2) {
|
|
80
|
+
if (ctx.slicedArgs[1].isQuoted ||
|
|
81
|
+
!isFinite(Number(ctx.slicedArgs[1].str)))
|
|
82
|
+
throw `Pause Command: the second arg must be a number`;
|
|
83
|
+
core.system.pause(Number(ctx.slicedArgs[1].str) * 1000);
|
|
84
|
+
}
|
|
85
|
+
else
|
|
86
|
+
core.system.pause();
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
export function sayAction(core, who, what, wait) {
|
|
90
|
+
const say_view_tag = "hzengine.say";
|
|
91
|
+
const say_view_name = "say";
|
|
92
|
+
what = core.script.parseString(what);
|
|
93
|
+
let message = {
|
|
94
|
+
who,
|
|
95
|
+
what,
|
|
96
|
+
};
|
|
97
|
+
let router = core.ui.getRouter(say_view_tag);
|
|
98
|
+
if (!router) {
|
|
99
|
+
core.ui.addRouter(say_view_tag, "ct");
|
|
100
|
+
router = core.ui.getRouter(say_view_tag);
|
|
101
|
+
}
|
|
102
|
+
if (!router.length) {
|
|
103
|
+
// @ts-ignore
|
|
104
|
+
router.push(say_view_name, message);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// @ts-ignore
|
|
108
|
+
router.update(message);
|
|
109
|
+
}
|
|
110
|
+
if (wait) {
|
|
111
|
+
if (core.config.getConfig("game.autoplay.enable")) {
|
|
112
|
+
let delay = core.config.getConfig("game.autoplay.extra_delay") +
|
|
113
|
+
core.config.getConfig("game.autoplay.ms_per_char") *
|
|
114
|
+
what.length;
|
|
115
|
+
if (isNaN(delay) || !isFinite(delay) || delay < 0) {
|
|
116
|
+
throw `Say Action: AutoPlay Delay Error: ${delay} ms`;
|
|
117
|
+
}
|
|
118
|
+
core.system.pause(delay);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
core.system.pause();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { sayAction } from "./basic.js";
|
|
2
|
+
export function character_command(core) {
|
|
3
|
+
// character command
|
|
4
|
+
// Used to create a character, and save it to script field in archive storage,
|
|
5
|
+
// so that it can be used when the archive is loaded next time the game starts
|
|
6
|
+
// by loading the archive.
|
|
7
|
+
// syntax: character <short_name> <display_name>
|
|
8
|
+
// if the short name is conflict with an existing character, the script will throw an error
|
|
9
|
+
core.script.use((ctx, next) => {
|
|
10
|
+
if (ctx.rawtext.trim().split(" ")[0].toLowerCase() !== "character")
|
|
11
|
+
return next();
|
|
12
|
+
if (ctx.slicedArgs.length > 3)
|
|
13
|
+
throw `Character Command: incorrect amount of args`;
|
|
14
|
+
if (ctx.slicedArgs.length === 1)
|
|
15
|
+
throw `Character Command: incorrect amount of args`;
|
|
16
|
+
if (ctx.slicedArgs.length === 2)
|
|
17
|
+
throw `Character Command: display name not specified`;
|
|
18
|
+
if (ctx.slicedArgs[1].isQuoted)
|
|
19
|
+
throw `Character Command: short name should not be quoted`;
|
|
20
|
+
// Check if the short name is conflict with an existing character
|
|
21
|
+
let short_name = ctx.slicedArgs[1].str;
|
|
22
|
+
let characterMap = getCharacterMap();
|
|
23
|
+
if (characterMap[short_name] != null) {
|
|
24
|
+
if (characterMap[short_name].declare_info.path === ctx.currentPath &&
|
|
25
|
+
characterMap[short_name].declare_info.index === ctx.currentLineIndex) {
|
|
26
|
+
}
|
|
27
|
+
else
|
|
28
|
+
throw `Character Command: short name [${short_name}] conflict, at file [${characterMap[short_name].declare_info.path}] line [${characterMap[short_name].declare_info.index + 1}] and `;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
let display_name = ctx.slicedArgs[2].isQuoted
|
|
32
|
+
? core.script.parseString(ctx.slicedArgs[2].str)
|
|
33
|
+
: ctx.slicedArgs[2].str;
|
|
34
|
+
characterMap[short_name] = buildCharacterInfo(short_name, display_name);
|
|
35
|
+
}
|
|
36
|
+
function buildCharacterInfo(short_name, display_name) {
|
|
37
|
+
return {
|
|
38
|
+
short_name,
|
|
39
|
+
display_name,
|
|
40
|
+
declare_info: {
|
|
41
|
+
path: ctx.currentPath,
|
|
42
|
+
index: ctx.currentLineIndex,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
function getCharacterMap() {
|
|
48
|
+
// // Check core.storage.archiveData
|
|
49
|
+
// if (typeof core.storage.archiveData == null)
|
|
50
|
+
// throw `[HZEngine] ArchiveData is null`;
|
|
51
|
+
// if (typeof core.storage.archiveData != "object")
|
|
52
|
+
// throw `[HZEngine] ArchiveData is not a object(Record or Array)`;
|
|
53
|
+
// if (Array.isArray(core.storage.archiveData))
|
|
54
|
+
// throw `[HZEngine] ArchiveData is an Array`;
|
|
55
|
+
// // Check core.storage.archiveData.script
|
|
56
|
+
// if (core.storage.archiveData.script == null) {
|
|
57
|
+
// console.log(`[HZEngine] ArchiveData.script is null, create it`);
|
|
58
|
+
// core.storage.archiveData.script = {};
|
|
59
|
+
// }
|
|
60
|
+
// if (typeof core.storage.archiveData.script != "object")
|
|
61
|
+
// throw `[HZEngine] ArchiveData.script is not a object(Record or Array)`;
|
|
62
|
+
// if (Array.isArray(core.storage.archiveData.script))
|
|
63
|
+
// throw `[HZEngine] ArchiveData.script is an Array`;
|
|
64
|
+
// // Check core.storage.archiveData.script.characterMap
|
|
65
|
+
// if (core.storage.archiveData.script.characterMap == null) {
|
|
66
|
+
// console.log(
|
|
67
|
+
// `[HZEngine] ArchiveData.script.characterMap is null, create it`
|
|
68
|
+
// );
|
|
69
|
+
// core.storage.archiveData.script.characterMap = {};
|
|
70
|
+
// }
|
|
71
|
+
// if (typeof core.storage.archiveData.script.characterMap != "object")
|
|
72
|
+
// throw `[HZEngine] ArchiveData.script.characterMap is not a object(Record or Array)`;
|
|
73
|
+
// if (Array.isArray(core.storage.archiveData.script.characterMap))
|
|
74
|
+
// throw `[HZEngine] ArchiveData.script.characterMap is an Array`;
|
|
75
|
+
// return core.storage.archiveData.script.characterMap as any; // TODO
|
|
76
|
+
return core.storage.getSaveableData(core.storage.archiveData, true, "script", "characterMap");
|
|
77
|
+
}
|
|
78
|
+
// Gramma: %short_name% "{message}"
|
|
79
|
+
core.script.use((ctx, next) => {
|
|
80
|
+
if (ctx.slicedArgs[0].isQuoted)
|
|
81
|
+
return next();
|
|
82
|
+
let characterMap = getCharacterMap();
|
|
83
|
+
let short_name = ctx.slicedArgs[0].str;
|
|
84
|
+
for (let key in characterMap) {
|
|
85
|
+
// console.log(`Character Say Command: key=${key} sn=${short_name}`);
|
|
86
|
+
function parseSayCommandArgs() {
|
|
87
|
+
if (ctx.slicedArgs.length > 0 &&
|
|
88
|
+
ctx.slicedArgs[ctx.slicedArgs.length - 1].str === "nowait") {
|
|
89
|
+
ctx.slicedArgs = ctx.slicedArgs.slice(0, ctx.slicedArgs.length - 1);
|
|
90
|
+
return {
|
|
91
|
+
wait: false,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
return {
|
|
96
|
+
wait: true,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
let parsed = parseSayCommandArgs();
|
|
101
|
+
if (key === short_name) {
|
|
102
|
+
if (ctx.slicedArgs.length != 2)
|
|
103
|
+
throw `Character Say Command: incorrect amount of args`;
|
|
104
|
+
console.log("Character Say Command:", short_name, ctx.slicedArgs[1].str);
|
|
105
|
+
sayAction(core, characterMap[key].display_name, ctx.slicedArgs[1].str, parsed.wait);
|
|
106
|
+
// if(parsed.wait) core.system.pause();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return next();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
export function conditional(core) {
|
|
2
|
+
// if ... [elif] ... [else] ... end if
|
|
3
|
+
// syntax:
|
|
4
|
+
// if <expression>
|
|
5
|
+
// <code>
|
|
6
|
+
// [elif <expression>]
|
|
7
|
+
// <code>
|
|
8
|
+
// [else]
|
|
9
|
+
// <code>
|
|
10
|
+
// end if
|
|
11
|
+
// if statement start
|
|
12
|
+
core.script.use((ctx, next) => {
|
|
13
|
+
if (ctx.slicedArgs[0].isQuoted ||
|
|
14
|
+
ctx.slicedArgs[0].str.toLowerCase() !== "if") {
|
|
15
|
+
return next();
|
|
16
|
+
}
|
|
17
|
+
let data = ctx.startStatement("if");
|
|
18
|
+
// console.log(`if data=${JSON.stringify(data)}`);
|
|
19
|
+
let if_expression_res = core.script.evalExpression(data.if_expression);
|
|
20
|
+
if (typeof if_expression_res !== "boolean") {
|
|
21
|
+
throw `If statement: if expression must return boolean, at file [${data.start_position[0]}] line [${data.start_position[1]}]`;
|
|
22
|
+
}
|
|
23
|
+
if (if_expression_res) {
|
|
24
|
+
// continue to execute next line
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// analyse elif
|
|
28
|
+
for (let i = 0; i < data.elif_list.length; i++) {
|
|
29
|
+
let item = data.elif_list[i];
|
|
30
|
+
let elif_expression_res = core.script.evalExpression(item.expression);
|
|
31
|
+
if (typeof elif_expression_res !== "boolean") {
|
|
32
|
+
throw `If statement: elif expression must return boolean, at file [${item.position[0]}] line [${item.position[1]}]`;
|
|
33
|
+
}
|
|
34
|
+
if (elif_expression_res) {
|
|
35
|
+
// jump to next line of elif statement
|
|
36
|
+
core.script.jump(item.position[0], item.position[1] + 1);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// analyse else
|
|
41
|
+
if (data.else_position) {
|
|
42
|
+
core.script.jump(data.else_position[0], data.else_position[1] + 1);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// the expressions are all false
|
|
46
|
+
// jump to the end of if statement
|
|
47
|
+
core.script.jump(data.end_position[0], data.end_position[1]);
|
|
48
|
+
});
|
|
49
|
+
// elif statement
|
|
50
|
+
// when come across next elif statement, jump to "end if" statement to stop execute.
|
|
51
|
+
core.script.use((ctx, next) => {
|
|
52
|
+
if (ctx.slicedArgs[0].isQuoted ||
|
|
53
|
+
ctx.slicedArgs[0].str.toLowerCase() !== "elif") {
|
|
54
|
+
return next();
|
|
55
|
+
}
|
|
56
|
+
if (ctx.statementStack.length === 0 ||
|
|
57
|
+
ctx.statementStack[ctx.statementStack.length - 1][0] !== "if") {
|
|
58
|
+
throw `If statement: elif statement must come after if statement, at file [${ctx.currentPath}] line [${ctx.currentLineIndex}]`;
|
|
59
|
+
}
|
|
60
|
+
let data = ctx.statementStack[ctx.statementStack.length - 1][2];
|
|
61
|
+
// check if this elif statement belong to the closest if statement
|
|
62
|
+
for (let i = 0; i < data.elif_list.length; i++) {
|
|
63
|
+
let item = data.elif_list[i];
|
|
64
|
+
if (item.position[0] === ctx.currentPath &&
|
|
65
|
+
item.position[1] === ctx.currentLineIndex) {
|
|
66
|
+
// jump to the "end if" statement
|
|
67
|
+
core.script.jump(data.end_position[0], data.end_position[1]);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// the elif statement is not belong to the closest if statement, so throw error
|
|
72
|
+
throw `If statement: elif statement not belong to the closest if statement, at file [${ctx.currentPath}] line [${ctx.currentLineIndex}]`;
|
|
73
|
+
});
|
|
74
|
+
// else statement
|
|
75
|
+
core.script.use((ctx, next) => {
|
|
76
|
+
if (ctx.slicedArgs[0].isQuoted ||
|
|
77
|
+
ctx.slicedArgs[0].str.toLowerCase() !== "else") {
|
|
78
|
+
return next();
|
|
79
|
+
}
|
|
80
|
+
if (ctx.statementStack.length === 0 ||
|
|
81
|
+
ctx.statementStack[ctx.statementStack.length - 1][0] !== "if") {
|
|
82
|
+
throw `If statement: else statement must come after if statement, at file [${ctx.currentPath}] line [${ctx.currentLineIndex}]`;
|
|
83
|
+
}
|
|
84
|
+
let data = ctx.statementStack[ctx.statementStack.length - 1][2];
|
|
85
|
+
// check if this else statement belong to the closest if statement
|
|
86
|
+
if (data.else_position) {
|
|
87
|
+
if (data.else_position[0] === ctx.currentPath &&
|
|
88
|
+
data.else_position[1] === ctx.currentLineIndex) {
|
|
89
|
+
// jump to the "end if" statement
|
|
90
|
+
core.script.jump(data.end_position[0], data.end_position[1]);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// the else statement is not belong to the closest if statement, so throw error
|
|
95
|
+
throw `If statement: else statement not belong to the closest if statement, at file [${ctx.currentPath}] line [${ctx.currentLineIndex}]`;
|
|
96
|
+
});
|
|
97
|
+
// end if statement
|
|
98
|
+
core.script.use((ctx, next) => {
|
|
99
|
+
if (ctx.rawtext.trim().split(/ +/).join(" ").toLowerCase() !== "end if") {
|
|
100
|
+
return next();
|
|
101
|
+
}
|
|
102
|
+
if (ctx.statementStack.length === 0 ||
|
|
103
|
+
ctx.statementStack[ctx.statementStack.length - 1][0] !== "if") {
|
|
104
|
+
throw `If statement: end if statement must come after if statement, at file [${ctx.currentPath}] line [${ctx.currentLineIndex}]`;
|
|
105
|
+
}
|
|
106
|
+
ctx.endStatement("if");
|
|
107
|
+
});
|
|
108
|
+
// analyse statement
|
|
109
|
+
// if statement
|
|
110
|
+
core.script.useAnalyseStatement((ctx, next) => {
|
|
111
|
+
if (ctx.slicedArgs[0].isQuoted ||
|
|
112
|
+
ctx.slicedArgs[0].str.toLowerCase() !== "if") {
|
|
113
|
+
return next();
|
|
114
|
+
}
|
|
115
|
+
if (ctx.slicedArgs.length < 2) {
|
|
116
|
+
throw `If statement: no expression specified, at file [${ctx.currentPath}] line [${ctx.currentLineIndex}]`;
|
|
117
|
+
}
|
|
118
|
+
ctx.startStatement("if", {
|
|
119
|
+
start_position: [ctx.currentPath, ctx.currentLineIndex],
|
|
120
|
+
if_expression: ctx.rawtext.trim().slice(2).trim(),
|
|
121
|
+
elif_list: [],
|
|
122
|
+
else_position: null,
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
// elif statement
|
|
126
|
+
core.script.useAnalyseStatement((ctx, next) => {
|
|
127
|
+
if (ctx.slicedArgs[0].isQuoted ||
|
|
128
|
+
ctx.slicedArgs[0].str.toLowerCase() !== "elif") {
|
|
129
|
+
return next();
|
|
130
|
+
}
|
|
131
|
+
if (ctx.slicedArgs.length < 2) {
|
|
132
|
+
throw `If statement: no expression specified, at file [${ctx.currentPath}] line [${ctx.currentLineIndex}]`;
|
|
133
|
+
}
|
|
134
|
+
// check if the closest statement is if statement
|
|
135
|
+
if (ctx.statementStack.length === 0 ||
|
|
136
|
+
ctx.statementStack[ctx.statementStack.length - 1][0] !== "if") {
|
|
137
|
+
throw `If statement: elif statement must come after if statement, at file [${ctx.currentPath}] line [${ctx.currentLineIndex}]`;
|
|
138
|
+
}
|
|
139
|
+
let data = ctx.statementStack[ctx.statementStack.length - 1][2];
|
|
140
|
+
data.elif_list.push({
|
|
141
|
+
position: [ctx.currentPath, ctx.currentLineIndex],
|
|
142
|
+
expression: ctx.rawtext.trim().slice(4).trim(),
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
// else statement
|
|
146
|
+
core.script.useAnalyseStatement((ctx, next) => {
|
|
147
|
+
if (ctx.slicedArgs[0].isQuoted ||
|
|
148
|
+
ctx.slicedArgs[0].str.toLowerCase() !== "else") {
|
|
149
|
+
return next();
|
|
150
|
+
}
|
|
151
|
+
// check if the closest statement is if statement
|
|
152
|
+
if (ctx.statementStack.length === 0 ||
|
|
153
|
+
ctx.statementStack[ctx.statementStack.length - 1][0] !== "if") {
|
|
154
|
+
throw `If statement: else statement must come after if statement, at file [${ctx.currentPath}] line [${ctx.currentLineIndex}]`;
|
|
155
|
+
}
|
|
156
|
+
let data = ctx.statementStack[ctx.statementStack.length - 1][2];
|
|
157
|
+
data.else_position = [ctx.currentPath, ctx.currentLineIndex];
|
|
158
|
+
});
|
|
159
|
+
// end if statement
|
|
160
|
+
core.script.useAnalyseStatement((ctx, next) => {
|
|
161
|
+
if (ctx.rawtext.trim().split(/ +/).join(" ").toLowerCase() !== "end if") {
|
|
162
|
+
return next();
|
|
163
|
+
}
|
|
164
|
+
let data = ctx.endStatement("if");
|
|
165
|
+
data.end_position = [ctx.currentPath, ctx.currentLineIndex];
|
|
166
|
+
ctx.setStatementData(data, [...data.start_position]);
|
|
167
|
+
});
|
|
168
|
+
// while ... end while
|
|
169
|
+
// while statement start
|
|
170
|
+
core.script.use((ctx, next) => {
|
|
171
|
+
if (ctx.slicedArgs[0].isQuoted ||
|
|
172
|
+
ctx.slicedArgs[0].str.toLowerCase() !== "while") {
|
|
173
|
+
return next();
|
|
174
|
+
}
|
|
175
|
+
let data = ctx.startStatement("while");
|
|
176
|
+
// test if expression return boolean
|
|
177
|
+
let expression = data.while_expression;
|
|
178
|
+
let expression_res = core.script.evalExpression(expression);
|
|
179
|
+
if (typeof expression_res !== "boolean") {
|
|
180
|
+
throw `While statement: while expression must return boolean, at file [${data.start_position[0]}] line [${data.start_position[1]}]`;
|
|
181
|
+
}
|
|
182
|
+
if (!expression_res) {
|
|
183
|
+
ctx.endStatement("while");
|
|
184
|
+
core.script.jump(...data.end_position, false);
|
|
185
|
+
core.script.incrementNextPosition();
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
// while statement end
|
|
192
|
+
core.script.use((ctx, next) => {
|
|
193
|
+
if (ctx.rawtext.trim().split(/ +/).join(" ").toLowerCase() !== "end while") {
|
|
194
|
+
return next();
|
|
195
|
+
}
|
|
196
|
+
// check if the closest statement is while statement
|
|
197
|
+
if (ctx.statementStack.length === 0 ||
|
|
198
|
+
ctx.statementStack[ctx.statementStack.length - 1][0] !== "while") {
|
|
199
|
+
throw `While statement: end while statement must come after while statement, at file [${ctx.currentPath}] line [${ctx.currentLineIndex}]`;
|
|
200
|
+
}
|
|
201
|
+
// jump to while statement start line (loop)
|
|
202
|
+
let data = ctx.statementStack[ctx.statementStack.length - 1][2];
|
|
203
|
+
ctx.endStatement("while");
|
|
204
|
+
core.script.jump(...data.start_position, false);
|
|
205
|
+
});
|
|
206
|
+
// analyse while statement start
|
|
207
|
+
core.script.useAnalyseStatement((ctx, next) => {
|
|
208
|
+
if (ctx.slicedArgs[0].isQuoted ||
|
|
209
|
+
ctx.slicedArgs[0].str.toLowerCase() !== "while") {
|
|
210
|
+
return next();
|
|
211
|
+
}
|
|
212
|
+
let while_expression = ctx.rawtext.trim().slice(5).trim();
|
|
213
|
+
if (while_expression === "") {
|
|
214
|
+
throw `While statement: no expression specified, at file [${ctx.currentPath}] line [${ctx.currentLineIndex}]`;
|
|
215
|
+
}
|
|
216
|
+
ctx.startStatement("while", {
|
|
217
|
+
start_position: [ctx.currentPath, ctx.currentLineIndex],
|
|
218
|
+
while_expression,
|
|
219
|
+
end_position: [ctx.currentPath, ctx.currentLineIndex],
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
// analyse while statement end
|
|
223
|
+
core.script.useAnalyseStatement((ctx, next) => {
|
|
224
|
+
if (ctx.rawtext.trim().split(/ +/).join(" ").toLowerCase() !== "end while") {
|
|
225
|
+
return next();
|
|
226
|
+
}
|
|
227
|
+
let data = ctx.endStatement("while");
|
|
228
|
+
data.end_position = [ctx.currentPath, ctx.currentLineIndex];
|
|
229
|
+
ctx.setStatementData(data, [...data.start_position]);
|
|
230
|
+
});
|
|
231
|
+
// do ... end do until
|
|
232
|
+
// break command
|
|
233
|
+
core.script.use((ctx, next) => {
|
|
234
|
+
if (ctx.slicedArgs[0].isQuoted ||
|
|
235
|
+
ctx.slicedArgs.length !== 1 ||
|
|
236
|
+
ctx.slicedArgs[0].str.toLowerCase() !== "break") {
|
|
237
|
+
return next();
|
|
238
|
+
}
|
|
239
|
+
// find the closest breakable statement
|
|
240
|
+
// (currently only while statement is breakable)
|
|
241
|
+
//(debug) output stack
|
|
242
|
+
// console.log("Stack="+JSON.stringify(ctx.statementStack));
|
|
243
|
+
let resIndex = -1;
|
|
244
|
+
for (let i = ctx.statementStack.length - 1; i >= 0; i--) {
|
|
245
|
+
if (ctx.statementStack[i][0] === "while") {
|
|
246
|
+
resIndex = i;
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (resIndex === -1) {
|
|
251
|
+
throw `Break command: break statement must come after while statement, at file [${ctx.currentPath}] line [${ctx.currentLineIndex}]`;
|
|
252
|
+
}
|
|
253
|
+
let data = ctx.statementStack[resIndex][2];
|
|
254
|
+
core.debug.log(`Break command: break from ${ctx.statementStack[resIndex][0]} statement at file [${data.start_position[0]}] line [${data.start_position[1] + 1}]`);
|
|
255
|
+
core.script.jump(...data.end_position, false);
|
|
256
|
+
core.script.incrementNextPosition();
|
|
257
|
+
// clear statement stack from breakable statement
|
|
258
|
+
ctx.statementStack.splice(resIndex + 1, ctx.statementStack.length - resIndex - 1);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function config_command(core) {
|
|
2
|
+
// set command: set <key> <value>
|
|
3
|
+
core.script.use((ctx, next) => {
|
|
4
|
+
if (ctx.rawtext.trim().split(" ")[0].toLowerCase() !== "set")
|
|
5
|
+
return next();
|
|
6
|
+
if (ctx.slicedArgs.length !== 3)
|
|
7
|
+
throw `Set Command: incorrect amount of args`;
|
|
8
|
+
let key = ctx.slicedArgs[1].str;
|
|
9
|
+
let val = ctx.slicedArgs[2].str;
|
|
10
|
+
if (!ctx.slicedArgs[2].isQuoted) {
|
|
11
|
+
if (val === "true" || val === "false") {
|
|
12
|
+
val = val === "true";
|
|
13
|
+
}
|
|
14
|
+
else if (!isNaN(val)) {
|
|
15
|
+
val = Number(val);
|
|
16
|
+
}
|
|
17
|
+
else
|
|
18
|
+
throw `Set Command: value type not supported`;
|
|
19
|
+
}
|
|
20
|
+
core.config.setConfig(key, val);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { splitStr2Strs } from "../../script/strtools.js";
|
|
2
|
+
export function decorator_module(core) {
|
|
3
|
+
core.script.use((ctx, next) => {
|
|
4
|
+
if (!ctx.slicedArgs[0].isSquared)
|
|
5
|
+
return next();
|
|
6
|
+
let decorator = ctx.slicedArgs[0].str.toLowerCase();
|
|
7
|
+
switch (splitStr2Strs(decorator).join("").toLowerCase()) {
|
|
8
|
+
case "real=true":
|
|
9
|
+
core.storage.globalData.realEnv = true;
|
|
10
|
+
break;
|
|
11
|
+
case "real":
|
|
12
|
+
if (!core.storage.globalData.realEnv) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
break;
|
|
16
|
+
default:
|
|
17
|
+
core.debug.log(`Decorator: Unknown decorator [${decorator}]`);
|
|
18
|
+
}
|
|
19
|
+
ctx.rawtext = ctx.rawtext.trim().slice(ctx.slicedArgs[0].str.length + 2).trim();
|
|
20
|
+
if (!ctx.rawtext.length)
|
|
21
|
+
return;
|
|
22
|
+
return next();
|
|
23
|
+
}, true);
|
|
24
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export function eval_module(core) {
|
|
2
|
+
// single line eval command
|
|
3
|
+
// syntax: eval <code>
|
|
4
|
+
core.script.use((ctx, next) => {
|
|
5
|
+
if (ctx.slicedArgs[0].isQuoted ||
|
|
6
|
+
ctx.slicedArgs[0].str.toLowerCase() !== "eval") {
|
|
7
|
+
return next();
|
|
8
|
+
}
|
|
9
|
+
if (ctx.slicedArgs.length === 1) {
|
|
10
|
+
throw `Eval Command: no code specified`;
|
|
11
|
+
}
|
|
12
|
+
if (ctx.slicedArgs.length === 2) {
|
|
13
|
+
core.script.evalScope(ctx.slicedArgs[1].str);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
// delete "eval" prefix
|
|
17
|
+
core.script.evalScope(ctx.rawtext.trim().slice(4).trim());
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
// multi line eval statement (script ... end script)
|
|
21
|
+
// syntax:
|
|
22
|
+
// script
|
|
23
|
+
// <code>
|
|
24
|
+
// end script
|
|
25
|
+
// script statement start
|
|
26
|
+
core.script.use((ctx, next) => {
|
|
27
|
+
if (ctx.rawtext.trim().split(" ")[0] !== "script") {
|
|
28
|
+
return next();
|
|
29
|
+
}
|
|
30
|
+
let data = ctx.startStatement("script");
|
|
31
|
+
core.script.evalScope(data.js_code_scope);
|
|
32
|
+
core.script.jump(data.end_position[0], data.end_position[1]);
|
|
33
|
+
});
|
|
34
|
+
// script statement end
|
|
35
|
+
core.script.use((ctx, next) => {
|
|
36
|
+
if (ctx.rawtext.trim().split(/ +/).join(" ") !== "end script")
|
|
37
|
+
return next();
|
|
38
|
+
ctx.endStatement("script");
|
|
39
|
+
});
|
|
40
|
+
// analyze script statement start
|
|
41
|
+
core.script.useAnalyseStatement((ctx, next) => {
|
|
42
|
+
if (ctx.rawtext.trim().split(" ")[0] !== "script") {
|
|
43
|
+
return next();
|
|
44
|
+
}
|
|
45
|
+
let data = ctx.startStatement("script");
|
|
46
|
+
data.start_position = [ctx.currentPath, ctx.currentLineIndex];
|
|
47
|
+
data.js_code_scope = "";
|
|
48
|
+
});
|
|
49
|
+
// analyze script code scope/statement end
|
|
50
|
+
core.script.useAnalyseStatement((ctx, next) => {
|
|
51
|
+
if (ctx.statementStack.length &&
|
|
52
|
+
ctx.statementStack[ctx.statementStack.length - 1][0] === "script") {
|
|
53
|
+
if (ctx.rawtext.trim().split(/ +/).join(" ") === "end script") {
|
|
54
|
+
// end script
|
|
55
|
+
let data = ctx.endStatement("script");
|
|
56
|
+
data.end_position = [ctx.currentPath, ctx.currentLineIndex];
|
|
57
|
+
ctx.setStatementData(data, [...data.start_position]);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// add code line to code scope
|
|
61
|
+
ctx.statementStack[ctx.statementStack.length - 1][2].js_code_scope += ctx.rawtext.trim() + '\n';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else
|
|
65
|
+
return next();
|
|
66
|
+
}, true);
|
|
67
|
+
}
|