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.
Files changed (103) hide show
  1. package/dist/async/index.js +162 -0
  2. package/dist/async/zeppos_timer.js +58 -0
  3. package/dist/audio/index.js +260 -0
  4. package/dist/config/index.js +57 -0
  5. package/dist/debug/index.js +8 -0
  6. package/dist/index.js +103 -0
  7. package/dist/platform/index.js +1 -0
  8. package/dist/plugins/basic_command/$.js +8 -0
  9. package/dist/plugins/basic_command/audio.js +40 -0
  10. package/dist/plugins/basic_command/basic.js +124 -0
  11. package/dist/plugins/basic_command/character.js +112 -0
  12. package/dist/plugins/basic_command/conditional.js +260 -0
  13. package/dist/plugins/basic_command/config.js +22 -0
  14. package/dist/plugins/basic_command/decorator.js +24 -0
  15. package/dist/plugins/basic_command/eval.js +67 -0
  16. package/dist/plugins/basic_command/img.js +249 -0
  17. package/dist/plugins/basic_command/index.js +22 -0
  18. package/dist/plugins/basic_command/menu.js +140 -0
  19. package/dist/plugins/global_gesture/index.js +25 -0
  20. package/dist/plugins/transform/animation.js +440 -0
  21. package/dist/plugins/transform/commands.js +38 -0
  22. package/dist/plugins/transform/example_profiles.js +32 -0
  23. package/dist/plugins/transform/hz_anime.js +211 -0
  24. package/dist/plugins/transform/index.js +93 -0
  25. package/dist/script/index.js +537 -0
  26. package/dist/script/readscript.js +15 -0
  27. package/dist/script/strtools.js +157 -0
  28. package/dist/storage/decorator.js +260 -0
  29. package/dist/storage/fs.js +96 -0
  30. package/dist/storage/index.js +442 -0
  31. package/dist/system/index.js +144 -0
  32. package/dist/ui/index.js +535 -0
  33. package/dist/utils/path.js +289 -0
  34. package/license.txt +202 -0
  35. package/package.json +26 -0
  36. package/src/async/index.ts +124 -0
  37. package/src/async/zeppos_timer.js +65 -0
  38. package/src/audio/index.ts +224 -0
  39. package/src/config/index.ts +80 -0
  40. package/src/debug/index.ts +11 -0
  41. package/src/index.ts +122 -0
  42. package/src/platform/index.ts +158 -0
  43. package/src/plugins/basic_command/$.ts +11 -0
  44. package/src/plugins/basic_command/audio.ts +53 -0
  45. package/src/plugins/basic_command/basic.ts +145 -0
  46. package/src/plugins/basic_command/character.ts +144 -0
  47. package/src/plugins/basic_command/conditional.ts +349 -0
  48. package/src/plugins/basic_command/config.ts +29 -0
  49. package/src/plugins/basic_command/decorator.ts +29 -0
  50. package/src/plugins/basic_command/eval.ts +88 -0
  51. package/src/plugins/basic_command/img.ts +317 -0
  52. package/src/plugins/basic_command/index.ts +24 -0
  53. package/src/plugins/basic_command/menu.ts +178 -0
  54. package/src/plugins/global_gesture/index.ts +29 -0
  55. package/src/plugins/transform/animation.ts +542 -0
  56. package/src/plugins/transform/commands.ts +53 -0
  57. package/src/plugins/transform/example_profiles.ts +36 -0
  58. package/src/plugins/transform/hz_anime.ts +214 -0
  59. package/src/plugins/transform/index.ts +141 -0
  60. package/src/plugins/transform/readme.md +1 -0
  61. package/src/script/index.ts +623 -0
  62. package/src/script/readscript.ts +17 -0
  63. package/src/script/strtools.ts +159 -0
  64. package/src/storage/decorator.ts +473 -0
  65. package/src/storage/fs.ts +104 -0
  66. package/src/storage/index.ts +541 -0
  67. package/src/system/index.ts +95 -0
  68. package/src/ui/index.ts +699 -0
  69. package/src/utils/path.js +338 -0
  70. package/tsconfig.json +111 -0
  71. package/types/async/index.d.ts +24 -0
  72. package/types/async/zeppos_timer.d.ts +14 -0
  73. package/types/audio/index.d.ts +64 -0
  74. package/types/config/index.d.ts +9 -0
  75. package/types/debug/index.d.ts +6 -0
  76. package/types/index.d.ts +41 -0
  77. package/types/platform/index.d.ts +134 -0
  78. package/types/plugins/basic_command/$.d.ts +2 -0
  79. package/types/plugins/basic_command/audio.d.ts +2 -0
  80. package/types/plugins/basic_command/basic.d.ts +3 -0
  81. package/types/plugins/basic_command/character.d.ts +2 -0
  82. package/types/plugins/basic_command/conditional.d.ts +2 -0
  83. package/types/plugins/basic_command/config.d.ts +2 -0
  84. package/types/plugins/basic_command/decorator.d.ts +2 -0
  85. package/types/plugins/basic_command/eval.d.ts +2 -0
  86. package/types/plugins/basic_command/img.d.ts +2 -0
  87. package/types/plugins/basic_command/index.d.ts +2 -0
  88. package/types/plugins/basic_command/menu.d.ts +2 -0
  89. package/types/plugins/global_gesture/index.d.ts +2 -0
  90. package/types/plugins/transform/animation.d.ts +131 -0
  91. package/types/plugins/transform/commands.d.ts +7 -0
  92. package/types/plugins/transform/example_profiles.d.ts +2 -0
  93. package/types/plugins/transform/hz_anime.d.ts +51 -0
  94. package/types/plugins/transform/index.d.ts +13 -0
  95. package/types/script/index.d.ts +123 -0
  96. package/types/script/readscript.d.ts +2 -0
  97. package/types/script/strtools.d.ts +31 -0
  98. package/types/storage/decorator.d.ts +41 -0
  99. package/types/storage/fs.d.ts +1 -0
  100. package/types/storage/index.d.ts +86 -0
  101. package/types/system/index.d.ts +35 -0
  102. package/types/ui/index.d.ts +167 -0
  103. package/types/utils/path.d.ts +84 -0
@@ -0,0 +1,623 @@
1
+ import { HZEngineCore } from "../index.js";
2
+
3
+ import { HzsInfo, Storage } from "../storage/index.js";
4
+ import {
5
+ mergeObjs2Str as joinObjs2Str,
6
+ parseInterpolatedStr,
7
+ removeComment,
8
+ splitStr2Objs,
9
+ } from "./strtools.js";
10
+ import { readline } from "./readscript.js";
11
+ import { Save } from "../storage/decorator.js";
12
+ export class Script {
13
+ constructor(public _core: HZEngineCore) {}
14
+ /**
15
+ * 调用栈
16
+ * 在call时保存当前执行位置和语句栈,在return时恢复执行位置和语句栈
17
+ */
18
+ @Save("script.callStack")
19
+ private accessor _callStack: {
20
+ position: [path: string, index: number] | null;
21
+ statementStack: Script.StatementStack;
22
+ }[] = [];
23
+
24
+ /**
25
+ * 语句栈
26
+ * 比如while, if,会在语句开始时入栈,语句结束时出栈
27
+ */
28
+ @Save("script.statementStack")
29
+ private accessor _statementStack: Script.StatementStack = [];
30
+
31
+ /**
32
+ * 下一次执行的脚本位置
33
+ * 注意:存储该值的时候应总是拷贝赋值而非直接引用赋值
34
+ */
35
+ @Save("script.nextRunPosition")
36
+ private accessor _nextRunPosition: [path: string, index: number] | null =
37
+ null;
38
+
39
+ // Script Run
40
+
41
+ /**
42
+ * 执行_nextRunPosition,并返回下一行_nextRunPosition是否不为null
43
+ */
44
+ runSingleLine(): boolean {
45
+ if (!this._nextRunPosition) {
46
+ // 文件尾隐式执行 return
47
+ this.return();
48
+ return false;
49
+ }
50
+ let nowRunPosition: [path: string, index: number] = [
51
+ ...this._nextRunPosition,
52
+ ];
53
+ this.incrementNextPosition();
54
+
55
+ let rawCommand = readline(
56
+ this._core,
57
+ nowRunPosition[0],
58
+ nowRunPosition[1]
59
+ )!;
60
+
61
+ // remove comment
62
+
63
+ rawCommand = removeComment(rawCommand);
64
+
65
+ if (rawCommand.trim().length && !rawCommand.trim().startsWith("#")) {
66
+ if (rawCommand.trim().startsWith("*")) {
67
+ if (this._statementStack.length) {
68
+ throw `label between statement is not allowed, at file [${nowRunPosition[0]}] line [${nowRunPosition[1]}]`;
69
+ }
70
+ } else {
71
+ this._core.debug.log("Run cmd: " + rawCommand);
72
+
73
+ // Process Command
74
+ this._processCmd(rawCommand, [...nowRunPosition]);
75
+ }
76
+ }
77
+
78
+ return !!this._nextRunPosition;
79
+ }
80
+
81
+ // Label Point Control
82
+
83
+ /**
84
+ * 跳转到目标标签
85
+ * @param targetLabel
86
+ */
87
+ jumpLabel(targetLabel: string) {
88
+ let labelPosition = this._locateLabel(targetLabel);
89
+ this._nextRunPosition = labelPosition;
90
+ this._statementStack = [];
91
+ }
92
+
93
+ jump(path: string, index: number, clearStatementStack: boolean = false) {
94
+ this._nextRunPosition = [path, index];
95
+
96
+ if (clearStatementStack) {
97
+ this._statementStack = [];
98
+ }
99
+ }
100
+
101
+ /**
102
+ * 调用目标标签
103
+ * 保存当前执行位置至调用栈,跳转到目标位置,直到return返回
104
+ * @param targetLabel
105
+ */
106
+ callLabel(targetLabel: string) {
107
+ // console.log(
108
+ // `pending to call label ${targetLabel}, stack=${JSON.stringify(
109
+ // this._routeStack
110
+ // )}`
111
+ // );
112
+ let labelPosition = this._locateLabel(targetLabel);
113
+ this._callStack.push({
114
+ position: this._nextRunPosition ? [...this._nextRunPosition] : null,
115
+ statementStack: this._statementStack,
116
+ });
117
+ this._nextRunPosition = labelPosition;
118
+ this._statementStack = [];
119
+
120
+ // console.log(
121
+ // `finished call label ${targetLabel}, stack=${JSON.stringify(
122
+ // this._routeStack
123
+ // )}`
124
+ // );
125
+ }
126
+
127
+ hasLabel(targetLabel: string) {
128
+ return (
129
+ this._core.storage.preloadedData?.script?.labelMap?.[targetLabel] != null
130
+ );
131
+ }
132
+
133
+ return() {
134
+ // console.log(`pending to return, stack=${JSON.stringify(this._routeStack)}`);
135
+ let stackItem = this._callStack.pop();
136
+ if (stackItem) {
137
+ this._statementStack = stackItem.statementStack;
138
+ this._nextRunPosition = stackItem.position;
139
+ } else {
140
+ this._nextRunPosition = null;
141
+ this._statementStack = [];
142
+
143
+ // Game End
144
+ this._core.end();
145
+ }
146
+
147
+ // console.log(`finished return, stack=${JSON.stringify(this._routeStack)}`);
148
+ }
149
+
150
+ clear() {
151
+ this._nextRunPosition = null;
152
+ this._callStack = [];
153
+ }
154
+
155
+ private _locateLabel(labelName: string) {
156
+ if (this._core.storage.preloadedData == null)
157
+ throw "Preloaded Data is Null";
158
+ if (!this._core.storage.preloadedData.script.labelMap[labelName])
159
+ throw `Error: Label [${labelName}] not found`;
160
+ let labelData: [name: string, index: number] = [
161
+ ...this._core.storage.preloadedData.script.labelMap[labelName],
162
+ ] as any;
163
+ return labelData;
164
+ }
165
+
166
+ public incrementNextPosition() {
167
+ if (this._core.storage.preloadedData == null)
168
+ throw "Preloaded Data is Null";
169
+ if (!this._nextRunPosition) throw "_nextRunPosition is null";
170
+ // console.log(`hzsInfoMap=${JSON.stringify(this._core.storage.preloadedData.script.hzsInfoMap)}`);
171
+
172
+ let hzsInfo: HzsInfo | undefined =
173
+ this._core.storage.preloadedData.script.hzsInfoMap[
174
+ this._nextRunPosition[0]
175
+ ];
176
+ if (!hzsInfo)
177
+ throw `Preloaded hzsInfo of path(${this._nextRunPosition[0]}) not found`;
178
+ this._nextRunPosition[1]++;
179
+ if (this._nextRunPosition[1] >= hzsInfo.totalLines)
180
+ this._nextRunPosition = null;
181
+ }
182
+
183
+ // Middleware
184
+ private _middlewares: Script.Middleware[] = [];
185
+ use(middleware: Script.Middleware, add_front: boolean = false) {
186
+ if (add_front) {
187
+ this._middlewares.unshift(middleware);
188
+ } else {
189
+ this._middlewares.push(middleware);
190
+ }
191
+ }
192
+
193
+ private _processCmd(
194
+ cmd: string,
195
+ nowRunPosition: [path: string, index: number]
196
+ ) {
197
+ let ctx = this._buildContext(cmd, nowRunPosition);
198
+ if (this._middlewares.length === 0) {
199
+ this._processUnsolvedCmd(cmd);
200
+ return;
201
+ }
202
+ let i = 0,
203
+ len = this._middlewares.length;
204
+ let nextFunc = () => {
205
+ i++;
206
+ if (i >= len) {
207
+ this._processUnsolvedCmd(cmd);
208
+ return;
209
+ }
210
+ this._middlewares[i](ctx, nextFunc);
211
+ };
212
+ this._middlewares[0](ctx, nextFunc);
213
+ }
214
+
215
+ private _processUnsolvedCmd(cmd: string) {
216
+ if (cmd.trim().length === 0)
217
+ return; // Empty Line
218
+ else if (cmd.trim().startsWith("*"))
219
+ return; // Label Command
220
+ else {
221
+ throw `Can not parse command: ${cmd}`;
222
+ }
223
+ }
224
+
225
+ private _buildContext(
226
+ cmd: string,
227
+ nowRunPosition: [path: string, index: number]
228
+ ): Script.Context {
229
+ return new Script.Context(
230
+ this._core,
231
+ cmd,
232
+ nowRunPosition[0],
233
+ nowRunPosition[1],
234
+ this._statementStack
235
+ );
236
+ }
237
+
238
+ // Statement Analyse
239
+ private _statementAnalyseStack: Script.StatementStack = [];
240
+ private _analyseStatementMiddlewares: Script.Middleware[] = [];
241
+ private _buildAnalyseStatementContext(
242
+ cmd: string,
243
+ nowRunPosition: [path: string, index: number]
244
+ ) {
245
+ return new Script.ContextForAnalyseStatement(
246
+ this._core,
247
+ cmd,
248
+ nowRunPosition[0],
249
+ nowRunPosition[1],
250
+ this._statementAnalyseStack
251
+ );
252
+ }
253
+ useAnalyseStatement(
254
+ middleware: Script.MiddlewareForAnalyseStatement,
255
+ add_front?: boolean
256
+ ) {
257
+ if (add_front) {
258
+ this._analyseStatementMiddlewares.unshift(middleware);
259
+ } else {
260
+ this._analyseStatementMiddlewares.push(middleware);
261
+ }
262
+ }
263
+ /**
264
+ * 分析statement
265
+ *
266
+ * Analyze the statement syntax and record the script point location and related information in advance.
267
+ * When the script executes a statement, the regular middleware corresponding to that statement will load
268
+ * the information saved by analyseStatement before the statement, and if there is no analysis, it will call
269
+ * analyseStatement to analyze, and the corresponding analysis middleware will process and save the information.
270
+ * After analysis is complete, reset _nextRunPosition to the location before the call, switch back to the normal mode,
271
+ * and continue executing.
272
+ */
273
+ analyseStatement(ctx: Script.Context) {
274
+ // this._core.debug.log("[HZEngine] Start statement analyse mode");
275
+
276
+ // Backup _nextRunPosition
277
+ let _nextRunPositionBackup: [path: string, index: number] | null = this
278
+ ._nextRunPosition
279
+ ? [...this._nextRunPosition]
280
+ : null;
281
+ let covered: boolean = false;
282
+ // Set _nextRunPosition to the current position of the statement
283
+ this._nextRunPosition = [ctx.currentPath, ctx.currentLineIndex];
284
+ while (this._nextRunPosition) {
285
+ let rawCommand = readline(
286
+ this._core,
287
+ this._nextRunPosition[0],
288
+ this._nextRunPosition[1]
289
+ );
290
+ if (rawCommand == null)
291
+ throw `Readline Error(got ${rawCommand}), at file [${
292
+ this._nextRunPosition[0]
293
+ }] line [${this._nextRunPosition[1] + 1}]`;
294
+
295
+ // If the command is not empty and not a comment
296
+ if (rawCommand.trim().length && !rawCommand.trim().startsWith("#")) {
297
+ // If it is a label command, check if the statement stack is empty
298
+ if (rawCommand.trim().startsWith("*")) {
299
+ if (this._statementStack.length) {
300
+ throw `label between statement is not allowed, at file [${
301
+ this._nextRunPosition[0]
302
+ }] line [${this._nextRunPosition[1] + 1}]`;
303
+ }
304
+ } else {
305
+ // Build context for analyzing statement
306
+ let sub_ctx = this._buildAnalyseStatementContext(rawCommand, [
307
+ ...this._nextRunPosition,
308
+ ]);
309
+
310
+ // console.log(`[HZEngine] analyse statement command [${rawCommand}]`);
311
+
312
+ // Process command
313
+ if (this._analyseStatementMiddlewares.length === 0) {
314
+ // TODO do nothing
315
+ } else {
316
+ let i = 0,
317
+ len = this._analyseStatementMiddlewares.length;
318
+ let nextFunc = () => {
319
+ i++;
320
+ if (i >= len) {
321
+ // TODO do nothing
322
+ } else {
323
+ this._analyseStatementMiddlewares[i](sub_ctx, nextFunc);
324
+ }
325
+ };
326
+ this._analyseStatementMiddlewares[0](sub_ctx, nextFunc);
327
+ }
328
+ }
329
+ }
330
+
331
+ // Move to the next line
332
+ this.incrementNextPosition();
333
+
334
+ if (covered) {
335
+ if (this._statementAnalyseStack.length === 0) {
336
+ break;
337
+ }
338
+ } else {
339
+ if (this._statementAnalyseStack.length > 0) {
340
+ covered = true;
341
+ }
342
+ }
343
+ }
344
+
345
+ this._nextRunPosition = _nextRunPositionBackup;
346
+
347
+ // Check if the statement stack is empty
348
+ if (this._statementAnalyseStack.length > 0) {
349
+ throw `statement not closed, at file [${
350
+ this._statementAnalyseStack[
351
+ this._statementAnalyseStack.length - 1
352
+ ][1][0]
353
+ }] line [${
354
+ this._statementAnalyseStack[
355
+ this._statementAnalyseStack.length - 1
356
+ ][1][1] + 1
357
+ }]`;
358
+ }
359
+
360
+ // Reset _nextRunPosition to the backup value, and switch back to normal mode, and continue executing
361
+ // this._core.debug.logconsole.log("[HZEngine] Finished analyse statement mode ");
362
+ }
363
+
364
+ // eval
365
+ evalScope(code: string) {
366
+ try {
367
+ return new Function("sd", "gd", "hz", `${code}`)(
368
+ this._core.storage.sd,
369
+ this._core.storage.gd,
370
+ this._core
371
+ );
372
+ } catch (e) {
373
+ this._core.debug.log(`Error in evalScope: ${e}`);
374
+ }
375
+ }
376
+ evalExpression(code: string) {
377
+ this._core.debug.log(`evalExpression: ${code}`);
378
+
379
+ try {
380
+ return new Function("sd", "gd", "hz", `return (${code})`)(
381
+ this._core.storage.sd,
382
+ this._core.storage.gd,
383
+ this._core
384
+ );
385
+ } catch (e) {
386
+ this._core.debug.log(`Error in evalExpression: ${e}`);
387
+ }
388
+ }
389
+
390
+ // parse string
391
+ parseString(str: string) {
392
+ let parsedInterpolated = parseInterpolatedStr(str);
393
+ let res: string = "";
394
+ for (let item of parsedInterpolated) {
395
+ if (item.isExpression) {
396
+ res += this.evalExpression(item.str);
397
+ } else {
398
+ res += item.str;
399
+ }
400
+ }
401
+
402
+ return res;
403
+ }
404
+ }
405
+
406
+ export namespace Script {
407
+ export type Middleware = (ctx: Context, next: () => void) => void;
408
+ export class Context {
409
+ constructor(
410
+ protected _core: HZEngineCore,
411
+ private _rawtext: string,
412
+ public readonly currentPath: string,
413
+ public readonly currentLineIndex: number,
414
+ private _statementStack: StatementStack
415
+ ) {}
416
+ public get rawtext(): string {
417
+ return this._rawtext;
418
+ }
419
+ public set rawtext(rawtext: string) {
420
+ this._rawtext = rawtext;
421
+ this._rawtextChanged = true;
422
+ }
423
+ private _rawtextChanged = false;
424
+ private _slicedArgs: Context.SlicedArg[] | null = null;
425
+ get slicedArgs(): Context.SlicedArg[] {
426
+ if (!this._slicedArgs || this._rawtextChanged)
427
+ this._slicedArgs = splitStr2Objs(this.rawtext);
428
+ this._rawtextChanged = false;
429
+ return this._slicedArgs;
430
+ }
431
+ // 注意只有在修改在触发slicedArgs的时候才会更新rawtext
432
+ set slicedArgs(slicedArgs: Context.SlicedArg[]) {
433
+ this._slicedArgs = JSON.parse(JSON.stringify(slicedArgs)); // TODO 深拷贝 性能
434
+ this._rawtext = joinObjs2Str(slicedArgs);
435
+ // TODO this._rawtextChanged = true ???
436
+ }
437
+ /**
438
+ * 開始一個新的Statement,返回該Statement的數據
439
+ * Start a new statement and return the data of the new statement
440
+ * @param identifier the identifier of the statement
441
+ * @returns the data of the new statement
442
+ */
443
+ startStatement(
444
+ identifier: string,
445
+ data?: Storage.Saveable<unknown>
446
+ ): StatementData {
447
+ let statement_data = data ?? this.getStatementData();
448
+ let statementStackItem: StatementStackItem = [
449
+ identifier,
450
+ [this.currentPath, this.currentLineIndex],
451
+ statement_data,
452
+ ];
453
+ this._statementStack.push(statementStackItem);
454
+ return statement_data;
455
+ }
456
+ endStatement(identifier: string) {
457
+ if (this._statementStack.length === 0)
458
+ throw `statement not open, at file [${this.currentPath}] line [${
459
+ this.currentLineIndex + 1
460
+ }]`;
461
+ if (
462
+ this._statementStack[this._statementStack.length - 1][0] !== identifier
463
+ )
464
+ throw `the last statement in the stack is not ${identifier}, at file [${
465
+ this.currentPath
466
+ }] line [${this.currentLineIndex + 1}]`;
467
+ return this._statementStack.pop()![2] as NonNullable<
468
+ Storage.Saveable<unknown>
469
+ >;
470
+ }
471
+ get statementStack() {
472
+ return this._statementStack;
473
+ }
474
+
475
+ // statement data will stored in core.storage.globalData.script.statement_data
476
+ // the key of the statement data is the line index of the start statement,
477
+ // for example: the key of the statement data of "menu ... end menu"
478
+ // is stored in the key of the line index of the "menu" statement
479
+ getStatementData(): NonNullable<Storage.Saveable<unknown>> {
480
+ let statement_data_in_file = this._core.storage.getSaveableData(
481
+ this._core.storage.globalData,
482
+ true,
483
+ "script",
484
+ "statement_data",
485
+ this.currentPath
486
+ ) as Record<string, NonNullable<Storage.Saveable<unknown>>>;
487
+ if (!statement_data_in_file["" + this.currentLineIndex]) {
488
+ this._core.script.analyseStatement(this);
489
+ statement_data_in_file = this._core.storage.getSaveableData(
490
+ this._core.storage.globalData,
491
+ true,
492
+ "script",
493
+ "statement_data",
494
+ this.currentPath
495
+ ) as Record<string, NonNullable<Storage.Saveable<unknown>>>;
496
+ if (!statement_data_in_file[this.currentLineIndex])
497
+ throw `analyse statement failed as statement data not found, at file [${
498
+ this.currentPath
499
+ }] line [${this.currentLineIndex + 1}]`;
500
+ }
501
+
502
+ return statement_data_in_file[this.currentLineIndex]!;
503
+ }
504
+
505
+ setStatementData(
506
+ statement_data: NonNullable<Storage.Saveable<unknown>>,
507
+ start_position: [path: string, index: number]
508
+ ) {
509
+ let statement_data_in_file = this._core.storage.getSaveableData(
510
+ this._core.storage.globalData,
511
+ true,
512
+ "script",
513
+ "statement_data",
514
+ start_position[0]
515
+ ) as Record<string, NonNullable<Storage.Saveable<unknown>>>;
516
+ statement_data_in_file["" + start_position[1]] = statement_data;
517
+ this._core.storage.saveGlobalData();
518
+ }
519
+ }
520
+ export namespace Utils {
521
+ export const joinSlicedArgs: typeof joinObjs2Str = joinObjs2Str;
522
+ export const splitRawtext: typeof splitStr2Objs = splitStr2Objs;
523
+ export function splitCommas(rawtext: string): string[] {
524
+ let slicedArgs = splitStr2Objs(rawtext);
525
+ // console.log(`splitCommas rawtext: ${rawtext}, slicedArgs: ${JSON.stringify(slicedArgs)}`);
526
+
527
+ let res: string[] = [];
528
+ for (let i = 0; i < slicedArgs.length; i++) {
529
+ if (slicedArgs[i].isQuoted) res.push(`"${slicedArgs[i].str}"`);
530
+ else if (slicedArgs[i].isSquared) res.push(`[${slicedArgs[i].str}]`);
531
+ else if (slicedArgs[i].isRounded) res.push(`(${slicedArgs[i].str})`);
532
+ else {
533
+ slicedArgs[i].str.split(",").forEach((str) => {
534
+ str = str.trim();
535
+ if (str) res.push(str);
536
+ });
537
+ }
538
+ }
539
+ // console.log(`splitCommas res: ${JSON.stringify(res)}`);
540
+
541
+ return res;
542
+ }
543
+ export function parseTuple(rawtext: string) {
544
+ if (
545
+ rawtext.length < 2 ||
546
+ rawtext[0] !== "(" ||
547
+ rawtext[rawtext.length - 1] !== ")"
548
+ ) {
549
+ throw `invalid tuple: ${rawtext}`;
550
+ }
551
+ rawtext = rawtext.slice(1, rawtext.length - 1);
552
+ // console.log(`parseTuple rawtext: ${rawtext}`);
553
+
554
+ return parseHzsArgs(rawtext);
555
+ }
556
+ export function parseArray(rawtext: string) {
557
+ if (
558
+ rawtext.length < 2 ||
559
+ rawtext[0] !== "[" ||
560
+ rawtext[rawtext.length - 1] !== "]"
561
+ ) {
562
+ throw `invalid array: ${rawtext}`;
563
+ }
564
+ rawtext = rawtext.slice(1, rawtext.length - 1);
565
+ return parseHzsArgs(rawtext);
566
+ }
567
+ export function parseHzsArgs(rawtext: string): TupleOrArr {
568
+ rawtext = rawtext.trim();
569
+ let arr = splitCommas(rawtext);
570
+ let res = arr.map((str) => {
571
+ if (str.startsWith("(")) return parseTuple(str);
572
+ else if (str.startsWith("[")) return parseArray(str);
573
+ else return str;
574
+ });
575
+ // console.log(`parseHzsArgs from: "${rawtext}" ; res: ${JSON.stringify(res)}`);
576
+
577
+ return res;
578
+ }
579
+
580
+ type TupleOrArr = (string | TupleOrArr)[];
581
+ }
582
+ export type MiddlewareForAnalyseStatement = (
583
+ ctx: ContextForAnalyseStatement,
584
+ next: () => void
585
+ ) => void;
586
+ export class ContextForAnalyseStatement extends Context {
587
+ startStatement(
588
+ identifier: string,
589
+ data: Storage.Saveable<unknown> = {}
590
+ ): StatementData {
591
+ return super.startStatement(identifier, data ?? {});
592
+ }
593
+ endStatement(identifier: string) {
594
+ let statement_data = super.endStatement(identifier);
595
+ // save analysed statement data
596
+ let statement_data_in_file = this._core.storage.getSaveableData(
597
+ this._core.storage.globalData,
598
+ true,
599
+ "script",
600
+ "statement_data",
601
+ this.currentPath
602
+ ) as Record<string, NonNullable<Storage.Saveable<unknown>>>;
603
+ statement_data_in_file["" + this.currentLineIndex] = statement_data;
604
+ this._core.storage.saveGlobalData();
605
+ return statement_data;
606
+ }
607
+ }
608
+ export namespace Context {
609
+ export interface SlicedArg {
610
+ str: string;
611
+ isQuoted?: boolean; // 是否被雙引號包圍
612
+ isSquared?: boolean; // 是否被方括號包圍
613
+ }
614
+ }
615
+
616
+ export type StatementStackItem = [
617
+ identifier: string,
618
+ start_position: [path: string, index: number],
619
+ statement_data: Storage.JSONValue,
620
+ ];
621
+ export type StatementStack = StatementStackItem[];
622
+ export type StatementData = NonNullable<Storage.JSONValue>;
623
+ }
@@ -0,0 +1,17 @@
1
+ import { HZEngineCore } from "../index.js";
2
+
3
+ // import * as hmFS from '@zos/fs'
4
+ let cache = new Map();
5
+ export function readline(_core: HZEngineCore, path: string, line_index: number): string | undefined {
6
+ if (cache.has(path)) {
7
+ return cache.get(path)[line_index];
8
+ }
9
+ // Get Command
10
+ let hzsContent = _core.platform.readFileSync({
11
+ path,
12
+ options: { encoding: "utf8" },
13
+ }) as string;
14
+ let file = hzsContent.split(/\r?\n|(?<!\n)\r/)
15
+ cache.set(path, file);
16
+ return file[line_index];
17
+ }