nadesiko3 3.3.48 → 3.3.49

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 (105) hide show
  1. package/core/.editorconfig +6 -0
  2. package/core/.eslintrc.cjs +33 -0
  3. package/core/.github/dependabot.yml +7 -0
  4. package/core/.github/workflows/nodejs.yml +37 -0
  5. package/core/.github/workflows/super-linter.yml +61 -0
  6. package/core/.github/workflows/textlint.yml +199 -0
  7. package/core/LICENSE +21 -0
  8. package/core/README.md +66 -0
  9. package/core/batch/build_nako_version.nako3 +42 -0
  10. package/core/command/snako.mjs +105 -0
  11. package/core/command/snako.mts +116 -0
  12. package/core/index.mjs +21 -0
  13. package/core/index.mts +21 -0
  14. package/core/package.json +47 -0
  15. package/core/sample/hello.nako3 +7 -0
  16. package/core/sample/hoge.mjs +4 -0
  17. package/core/sample/hoge.mts +6 -0
  18. package/core/src/nako3.mjs +858 -0
  19. package/core/src/nako3.mts +967 -0
  20. package/core/src/nako_colors.mjs +78 -0
  21. package/core/src/nako_colors.mts +86 -0
  22. package/core/src/nako_core_version.mjs +8 -0
  23. package/core/src/nako_core_version.mts +19 -0
  24. package/core/src/nako_csv.mjs +185 -0
  25. package/core/src/nako_csv.mts +188 -0
  26. package/core/src/nako_errors.mjs +173 -0
  27. package/core/src/nako_errors.mts +197 -0
  28. package/core/src/nako_from_dncl.mjs +255 -0
  29. package/core/src/nako_from_dncl.mts +250 -0
  30. package/core/src/nako_gen.mjs +1648 -0
  31. package/core/src/nako_gen.mts +1719 -0
  32. package/core/src/nako_gen_async.mjs +1659 -0
  33. package/core/src/nako_gen_async.mts +1732 -0
  34. package/core/src/nako_global.mjs +107 -0
  35. package/core/src/nako_global.mts +138 -0
  36. package/core/src/nako_indent.mjs +445 -0
  37. package/core/src/nako_indent.mts +492 -0
  38. package/core/src/nako_josi_list.mjs +38 -0
  39. package/core/src/nako_josi_list.mts +45 -0
  40. package/core/src/nako_lex_rules.mjs +253 -0
  41. package/core/src/nako_lex_rules.mts +260 -0
  42. package/core/src/nako_lexer.mjs +609 -0
  43. package/core/src/nako_lexer.mts +612 -0
  44. package/core/src/nako_logger.mjs +199 -0
  45. package/core/src/nako_logger.mts +232 -0
  46. package/core/src/nako_parser3.mjs +2439 -0
  47. package/core/src/nako_parser3.mts +2195 -0
  48. package/core/src/nako_parser_base.mjs +370 -0
  49. package/core/src/nako_parser_base.mts +370 -0
  50. package/core/src/nako_parser_const.mjs +37 -0
  51. package/core/src/nako_parser_const.mts +37 -0
  52. package/core/src/nako_prepare.mjs +304 -0
  53. package/core/src/nako_prepare.mts +315 -0
  54. package/core/src/nako_reserved_words.mjs +38 -0
  55. package/core/src/nako_reserved_words.mts +38 -0
  56. package/core/src/nako_source_mapping.mjs +207 -0
  57. package/core/src/nako_source_mapping.mts +262 -0
  58. package/core/src/nako_test.mjs +37 -0
  59. package/core/src/nako_types.mjs +25 -0
  60. package/core/src/nako_types.mts +151 -0
  61. package/core/src/plugin_csv.mjs +49 -0
  62. package/core/src/plugin_csv.mts +50 -0
  63. package/core/src/plugin_math.mjs +328 -0
  64. package/core/src/plugin_math.mts +326 -0
  65. package/core/src/plugin_promise.mjs +91 -0
  66. package/core/src/plugin_promise.mts +91 -0
  67. package/core/src/plugin_system.mjs +2832 -0
  68. package/core/src/plugin_system.mts +2690 -0
  69. package/core/src/plugin_test.mjs +34 -0
  70. package/core/src/plugin_test.mts +34 -0
  71. package/core/test/array_test.mjs +34 -0
  72. package/core/test/basic_test.mjs +344 -0
  73. package/core/test/calc_test.mjs +140 -0
  74. package/core/test/core_module_test.mjs +23 -0
  75. package/core/test/debug_test.mjs +16 -0
  76. package/core/test/dncl_test.mjs +94 -0
  77. package/core/test/error_message_test.mjs +210 -0
  78. package/core/test/error_test.mjs +16 -0
  79. package/core/test/flow_test.mjs +373 -0
  80. package/core/test/func_call.mjs +160 -0
  81. package/core/test/func_test.mjs +149 -0
  82. package/core/test/indent_test.mjs +364 -0
  83. package/core/test/lex_test.mjs +168 -0
  84. package/core/test/literal_test.mjs +73 -0
  85. package/core/test/nako_lexer_test.mjs +35 -0
  86. package/core/test/nako_logger_test.mjs +76 -0
  87. package/core/test/nako_logger_test.mts +78 -0
  88. package/core/test/plugin_csv_test.mjs +38 -0
  89. package/core/test/plugin_promise_test.mjs +18 -0
  90. package/core/test/plugin_system_test.mjs +630 -0
  91. package/core/test/prepare_test.mjs +96 -0
  92. package/core/test/re_test.mjs +22 -0
  93. package/core/test/side_effects_test.mjs +92 -0
  94. package/core/test/variable_scope_test.mjs +149 -0
  95. package/core/tsconfig.json +101 -0
  96. package/package.json +4 -2
  97. package/release/_hash.txt +12 -12
  98. package/release/_script-tags.txt +14 -14
  99. package/release/editor.js +1 -1
  100. package/release/stats.json +1 -1
  101. package/release/version.js +1 -1
  102. package/release/wnako3.js +1 -1
  103. package/src/nako_version.mjs +2 -2
  104. package/src/nako_version.mts +2 -2
  105. package/test/async/async_basic_test.mjs +3 -3
@@ -0,0 +1,1659 @@
1
+ /**
2
+ * file: nako_gen_async
3
+ * パーサーが生成した中間オブジェクトを実際のJavaScriptのコードに変換する。
4
+ * なお、扱いやすさ優先で、なでしこの一文を一つの関数として生成し、非同期実行する。
5
+ */
6
+ import { NakoSyntaxError, NakoError, NakoRuntimeError } from './nako_errors.mjs';
7
+ import nakoVersion from './nako_core_version.mjs';
8
+ import { NakoGen } from './nako_gen.mjs';
9
+ /**
10
+ * なでしこのインタプリタコード
11
+ */
12
+ const NakoCodeNop = 'NOP';
13
+ const NakoCodeLabel = 'LBL';
14
+ const NakoCodeEOL = 'EOL';
15
+ const NakoCodeJump = 'JMP'; // JUMP addr
16
+ const NakoCodeJumpIfTrue = 'JMP_T'; // pop and jump addr
17
+ const NakoCodeJumpIfFalse = 'JMP_F'; // pop and jump addr
18
+ const NakoCodeCall = 'CALL'; // call addr
19
+ const NakoCodeCallObj = 'CALL_OBJ'; // call addr
20
+ const NakoCodeReturn = 'RET';
21
+ const NakoCodeTry = 'TRY';
22
+ const NakoCodeCode = 'CODE';
23
+ const NakoCodeTagIsFuncpoint = 0x0F;
24
+ /**
25
+ * なでしこのインタプリタが用いる簡易コードを表現するクラス
26
+ */
27
+ class NakoCode {
28
+ /**
29
+ * @param type
30
+ * @param value
31
+ */
32
+ constructor(type, value) {
33
+ /** Codeのタイプ
34
+ * @type {string}
35
+ */
36
+ this.type = type;
37
+ /** Codeの値 / ラベルならラベル名
38
+ * @type {string}
39
+ */
40
+ this.value = value;
41
+ /** ラベルならジャンプ先
42
+ * @type {number}
43
+ */
44
+ this.no = -1;
45
+ /** タグ
46
+ * @type {number}
47
+ */
48
+ this.tag = 0;
49
+ }
50
+ }
51
+ /**
52
+ * 構文木からJSのコードを生成するクラス
53
+ */
54
+ export class NakoGenASync {
55
+ /**
56
+ * @param com コンパイラのインスタンス
57
+ */
58
+ constructor(com) {
59
+ this.com = com;
60
+ /**
61
+ * 出力するJavaScriptコードのヘッダー部分で定義する必要のある関数。fnはjsのコード。
62
+ * プラグイン関数は含まれない。
63
+ */
64
+ this.nakoFuncList = { ...com.getNakoFuncList() };
65
+ /**
66
+ * なでしこで定義したテストの一覧
67
+ * @type {Record<string, { josi: string[][], fn: string, type: 'test_func' }>}
68
+ */
69
+ this.nakoTestList = {};
70
+ /**
71
+ * プログラム内で参照された関数のリスト。プラグインの命令を含む。
72
+ * JavaScript単体で実行するとき、このリストにある関数の定義をJavaScriptコードの先頭に付け足す。
73
+ * @type {Set<string>}
74
+ */
75
+ this.usedFuncSet = new Set();
76
+ /**
77
+ * ループ時の一時変数が被らないようにIDで管理
78
+ * @type {number}
79
+ */
80
+ this.loopId = 1;
81
+ /**
82
+ * 変換中の処理が、ループの中かどうかを判定する
83
+ * @type {boolean}
84
+ */
85
+ this.flagLoop = false;
86
+ /**
87
+ * 変換後のコード管理番号
88
+ * @type {number}
89
+ */
90
+ this.codeId = 0;
91
+ /**
92
+ * 変換後のコードを保持する配列
93
+ * @type {Array<NakoCode>}
94
+ */
95
+ this.codeArray = [];
96
+ /** @type {NakoCode | null} */
97
+ this.labelContinue = null;
98
+ /** @type {NakoCode | null} */
99
+ this.labelBreak = null;
100
+ /**
101
+ * ジャンプ先を表現するラベル
102
+ * @type {Object<string, number>}
103
+ */
104
+ this.labels = {};
105
+ // コンパイラのインスタンス
106
+ this.__self = com;
107
+ /**
108
+ * コードジェネレータの種類
109
+ * @type {string}
110
+ */
111
+ this.genMode = '非同期モード';
112
+ /**
113
+ * 行番号とファイル名が分かるときは `l123:main.nako3`、行番号だけ分かるときは `l123`、そうでなければ任意の文字列。
114
+ * @type {string | null}
115
+ */
116
+ this.lastLineNo = null;
117
+ /**
118
+ * スタック
119
+ * @type {{ isFunction: boolean, names: Set<string>, readonly: Set<string> }[]}
120
+ */
121
+ this.varslistSet = com.__varslist.map((v) => ({ isFunction: false, names: new Set(Object.keys(v)), readonly: new Set() }));
122
+ /**
123
+ * スタックトップ
124
+ * @type {{ isFunction: boolean, names: Set<string>, readonly: Set<string> }}
125
+ */
126
+ this.varsSet = { isFunction: false, names: new Set(), readonly: new Set() };
127
+ this.varslistSet[2] = this.varsSet;
128
+ // 1以上のとき高速化する。
129
+ // 実行速度優先ブロック内で1増える。
130
+ this.speedMode = {
131
+ lineNumbers: 0,
132
+ implicitTypeCasting: 0,
133
+ invalidSore: 0,
134
+ forcePure: 0 // 全てのシステム命令をpureとして扱う。命令からローカル変数への参照が出来なくなる。
135
+ };
136
+ // 1以上のとき測定をinjectする。
137
+ // パフォーマンスモニタのブロック内で1増える。
138
+ this.performanceMonitor = {
139
+ userFunction: 0,
140
+ systemFunction: 0,
141
+ systemFunctionBody: 0 // システム関数(呼び出しコードを除く)
142
+ };
143
+ }
144
+ /**
145
+ * @param com
146
+ * @param {Ast} ast
147
+ * @param {boolean | string} isTest 文字列なら1つのテストだけを実行する
148
+ */
149
+ static generate(com, ast, isTest) {
150
+ const gen = new NakoGenASync(com);
151
+ // ユーザー定義関数をシステムに登録する
152
+ gen.registerFunction(ast);
153
+ // JSコードを生成する
154
+ let js = gen.convGen(ast, !!isTest);
155
+ // JSコードを実行するための事前ヘッダ部分の生成
156
+ js = gen.getDefFuncCode(isTest) + js;
157
+ com.getLogger().trace('--- generate(非同期モード) ---\n' + js);
158
+ // テストの実行
159
+ if (js && isTest) {
160
+ js += '\n__self._runTests(__tests);\n';
161
+ }
162
+ return {
163
+ // なでしこの実行環境ありの場合
164
+ runtimeEnv: js,
165
+ // JavaScript単体で動かす場合
166
+ standalone: `\
167
+ const nakoVersion = ${JSON.stringify(nakoVersion)};
168
+ ${NakoError.toString()}
169
+ ${NakoRuntimeError.toString()}
170
+ this.logger = {
171
+ error(message) { console.error(message) },
172
+ send(level, message) { console.log(message) },
173
+ };
174
+ this.__varslist = [{}, {}, {}];
175
+ this.__vars = this.__varslist[2];
176
+ this.__module = {};
177
+ this.__locals = {};
178
+ this.__labels = {};
179
+ this.__code = [];
180
+ this.__callstack = [];
181
+ this.__stack = [];
182
+ this.__genMode = '非同期モード';
183
+ try {
184
+ ${gen.getVarsCode()}
185
+ ${js}
186
+ } catch (err) {
187
+ if (!(err instanceof NakoRuntimeError)) {
188
+ err = new NakoRuntimeError(err, this.__varslist[0].line);
189
+ }
190
+ this.logger.error(err);
191
+ throw err;
192
+ }`,
193
+ gen // コード生成に使ったNakoGenのインスタンス
194
+ };
195
+ }
196
+ /**
197
+ * @param {import("./nako3").Ast} node
198
+ * @param {boolean} forceUpdate
199
+ */
200
+ convLineno(node, forceUpdate) {
201
+ if (this.speedMode.lineNumbers > 0) {
202
+ return '';
203
+ }
204
+ /** @type {string} */
205
+ let lineNo;
206
+ if (typeof node.line !== 'number') {
207
+ lineNo = 'unknown';
208
+ }
209
+ else if (typeof node.file !== 'string') {
210
+ lineNo = `l${node.line}`;
211
+ }
212
+ else {
213
+ lineNo = `l${node.line}:${node.file}`;
214
+ }
215
+ // 強制的に行番号をアップデートするか
216
+ if (!forceUpdate) {
217
+ if (lineNo === this.lastLineNo) {
218
+ return '';
219
+ }
220
+ this.lastLineNo = lineNo;
221
+ }
222
+ // 例: __v0.line='l1:main.nako3'
223
+ return `__v0.line=${JSON.stringify(lineNo)};`;
224
+ }
225
+ /**
226
+ * ローカル変数のJavaScriptコードを生成する。
227
+ * 基本的に取得のために利用
228
+ * @param {string} name
229
+ */
230
+ varname(name) {
231
+ const keys = JSON.stringify(name);
232
+ return `sys.__vars[${keys}]`;
233
+ }
234
+ /**
235
+ * プログラムの実行に必要な関数を書き出す(システム領域)
236
+ * @returns {string}
237
+ */
238
+ getVarsCode() {
239
+ let code = '';
240
+ // プログラム中で使った関数を列挙して書き出す
241
+ for (const key of Array.from(this.usedFuncSet.values())) {
242
+ const f = this.__self.__varslist[0][key];
243
+ const name = `this.__varslist[0]["${key}"]`;
244
+ if (typeof (f) === 'function') {
245
+ code += name + '=' + f.toString() + ';\n';
246
+ }
247
+ else {
248
+ code += name + '=' + JSON.stringify(f) + ';\n';
249
+ }
250
+ }
251
+ return code;
252
+ }
253
+ /**
254
+ * プログラムの実行に必要な関数定義を書き出す(グローバル領域)
255
+ * convGenの結果を利用するため、convGenの後に呼び出すこと。
256
+ * @param {boolean | string} isTest テストかどうか。stringの場合は1つのテストのみ。
257
+ * @returns {string}
258
+ */
259
+ getDefFuncCode(isTest) {
260
+ let code = '';
261
+ // よく使う変数のショートカット
262
+ code += 'const __self = this.__self = this;\n';
263
+ code += 'const __varslist = this.__varslist;\n';
264
+ code += 'const __module = this.__module;\n';
265
+ code += 'const __v0 = this.__v0 = this.__varslist[0];\n';
266
+ code += 'const __v1 = this.__v1 = this.__varslist[1];\n';
267
+ code += 'const __vars = this.__vars = this.__varslist[2];\n';
268
+ code += 'const __code = this.__code;\n';
269
+ // なでしこの関数定義を行う
270
+ let nakoFuncCode = '';
271
+ for (const key in this.nakoFuncList) {
272
+ const f = this.nakoFuncList[key].fn;
273
+ nakoFuncCode += '' +
274
+ `//[DEF_FUNC name='${key}']\n` +
275
+ `__v1["${key}"]=${f};\n;` +
276
+ `//[/DEF_FUNC name='${key}']\n`;
277
+ }
278
+ if (nakoFuncCode !== '') {
279
+ code += '__v0.line=\'関数の定義\';\n' + nakoFuncCode;
280
+ }
281
+ // プラグインの初期化関数を実行する
282
+ let pluginCode = '';
283
+ for (const name in this.__self.__module) {
284
+ const initkey = `!${name}:初期化`;
285
+ if (this.varslistSet[0].names.has(initkey)) {
286
+ this.usedFuncSet.add(`!${name}:初期化`);
287
+ pluginCode += `__v0["!${name}:初期化"](__self);\n`;
288
+ }
289
+ }
290
+ if (pluginCode !== '') {
291
+ code += '__v0.line=\'プラグインの初期化\';\n' + pluginCode;
292
+ }
293
+ // テストの定義を行う
294
+ if (isTest) {
295
+ let testCode = 'const __tests = [];\n';
296
+ for (const key in this.nakoTestList) {
297
+ if (isTest === true || (typeof isTest === 'string' && isTest === key)) {
298
+ const f = this.nakoTestList[key].fn;
299
+ testCode += `${f};\n;`;
300
+ }
301
+ }
302
+ if (testCode !== '') {
303
+ code += '__v0.line=\'テストの定義\';\n';
304
+ code += testCode + '\n';
305
+ }
306
+ }
307
+ return code;
308
+ }
309
+ /**
310
+ * プラグイン・オブジェクトを追加
311
+ * @param po プラグイン・オブジェクト
312
+ */
313
+ addPlugin(po) {
314
+ return this.__self.addPlugin(po);
315
+ }
316
+ /**
317
+ * プラグイン・オブジェクトを追加(ブラウザ向け)
318
+ * @param name オブジェクト名
319
+ * @param po 関数リスト
320
+ */
321
+ addPluginObject(name, po) {
322
+ this.__self.addPluginObject(name, po);
323
+ }
324
+ /**
325
+ * プラグイン・ファイルを追加(Node.js向け)
326
+ * @param objName オブジェクト名
327
+ * @param path ファイルパス
328
+ * @param po 登録するオブジェクト
329
+ */
330
+ addPluginFile(objName, path, po) {
331
+ this.__self.addPluginFile(objName, path, po);
332
+ }
333
+ /**
334
+ * 関数を追加する
335
+ * @param key 関数名
336
+ * @param josi 助詞
337
+ * @param fn 関数
338
+ */
339
+ addFunc(key, josi, fn) {
340
+ this.__self.addFunc(key, josi, fn);
341
+ }
342
+ /**
343
+ * プラグイン関数を参照する
344
+ * @param key プラグイン関数の関数名
345
+ * @returns プラグイン・オブジェクト
346
+ */
347
+ getFunc(key) {
348
+ return this.__self.getFunc(key);
349
+ }
350
+ /**
351
+ * 関数を先に登録してしまう
352
+ */
353
+ registerFunction(ast) {
354
+ if (ast.type !== 'block') {
355
+ throw NakoSyntaxError.fromNode('構文解析に失敗しています。構文は必ずblockが先頭になります', ast);
356
+ }
357
+ const registFunc = (node) => {
358
+ for (let i = 0; i < node.block.length; i++) {
359
+ const t = node.block[i];
360
+ if (t.type === 'def_func') {
361
+ const name = t.name.value;
362
+ this.usedFuncSet.add(name);
363
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
364
+ this.__self.__varslist[1][name] = function () { }; // 事前に適当な値を設定
365
+ this.nakoFuncList[name] = {
366
+ josi: t.name.meta.josi,
367
+ fn: '',
368
+ type: 'func'
369
+ };
370
+ }
371
+ else if (t.type === 'speed_mode') {
372
+ if (t.block.type === 'block') {
373
+ registFunc(t.block);
374
+ }
375
+ else {
376
+ registFunc(t);
377
+ }
378
+ }
379
+ else if (t.type === 'performance_monitor') {
380
+ if (t.block.type === 'block') {
381
+ registFunc(t.block);
382
+ }
383
+ else {
384
+ registFunc(t);
385
+ }
386
+ }
387
+ }
388
+ };
389
+ registFunc(ast);
390
+ // __self.__varslistの変更を反映
391
+ const initialNames = new Set();
392
+ if (this.speedMode.invalidSore === 0) {
393
+ initialNames.add('それ');
394
+ }
395
+ this.varsSet = { isFunction: false, names: initialNames, readonly: new Set() };
396
+ this.varslistSet = this.__self.__varslist.map(v => ({ isFunction: false, names: new Set(Object.keys(v)), readonly: new Set() }));
397
+ this.varslistSet[2] = this.varsSet;
398
+ }
399
+ /**
400
+ * @param {Ast} node
401
+ * @param {boolean} isTest
402
+ */
403
+ convGen(node, isTest) {
404
+ // convert
405
+ this._convGen(node, true);
406
+ // ラベルアドレスの解決が必要なコード一覧
407
+ const needToFixAddr = new Set([
408
+ NakoCodeJump, NakoCodeJumpIfTrue, NakoCodeJumpIfFalse, NakoCodeCall, NakoCodeTry
409
+ ]);
410
+ // コードの最適化をするか?
411
+ const optimization = true;
412
+ let codes = this.codeArray;
413
+ //
414
+ if (optimization) {
415
+ // NOPを削除
416
+ codes = codes.filter((code) => {
417
+ return code.type !== NakoCodeNop;
418
+ });
419
+ // 未参照のラベルを探す - ただし関数呼び出しは削除しない
420
+ const usedLabels = new Set();
421
+ codes.forEach((code) => {
422
+ if (needToFixAddr.has(code.type)) {
423
+ usedLabels.add(code.value);
424
+ }
425
+ });
426
+ // 未参照のラベルを削除
427
+ codes = codes.filter((code) => {
428
+ if (code.type !== NakoCodeLabel) {
429
+ return true;
430
+ }
431
+ if (code.tag === NakoCodeTagIsFuncpoint) {
432
+ return true;
433
+ }
434
+ return usedLabels.has(code.value);
435
+ });
436
+ // EOLが連続していたら削除する
437
+ let i = 0;
438
+ while (i < codes.length - 1) {
439
+ if (codes[i].type === NakoCodeEOL && codes[i + 1].type === NakoCodeEOL) {
440
+ codes.splice(i + 1, 1);
441
+ continue;
442
+ }
443
+ i++;
444
+ }
445
+ this.codeArray = codes;
446
+ }
447
+ // ラベルアドレスの解決
448
+ codes.forEach((code, index) => {
449
+ if (code.type === NakoCodeLabel) {
450
+ this.labels[code.value] = index;
451
+ }
452
+ });
453
+ codes.forEach((code) => {
454
+ if (needToFixAddr.has(code.type)) {
455
+ if (code.no < 0) {
456
+ code.no = this.labels[code.value];
457
+ }
458
+ }
459
+ });
460
+ let result = '';
461
+ // コードの生成
462
+ codes.forEach((code, index) => {
463
+ switch (code.type) {
464
+ case NakoCodeNop:
465
+ result += `case ${index}: break; // [NOP] ${code.value}\n`;
466
+ break;
467
+ case NakoCodeLabel:
468
+ result += `case ${index}: break; // [LABEL] ${code.value}\n`;
469
+ break;
470
+ case NakoCodeEOL:
471
+ result += `case ${index}: ${code.value}; break; // [EOL]\n`;
472
+ break;
473
+ case NakoCodeJump:
474
+ result += `case ${index}: sys.nextIndex = ${code.no}; break; // ${code.value}\n`;
475
+ break;
476
+ case NakoCodeJumpIfTrue:
477
+ result += `case ${index}: if (sys.__stack.pop()) { sys.nextIndex = ${code.no};} break; // ${code.value}\n`;
478
+ break;
479
+ case NakoCodeJumpIfFalse:
480
+ result += `case ${index}: if (!sys.__stack.pop()) { sys.nextIndex = ${code.no}} break; // ${code.value}\n`;
481
+ break;
482
+ case NakoCodeReturn:
483
+ result += `case ${index}: sys.__return(sys); break;\n`;
484
+ break;
485
+ case NakoCodeCall:
486
+ result += `case ${index}: sys.__call(${code.no}, sys); break; // ${code.value}\n`;
487
+ break;
488
+ case NakoCodeCallObj:
489
+ result += `case ${index}: sys.__callObj('${code.value}', ${index}, sys); break; // ${code.value}\n`;
490
+ break;
491
+ case NakoCodeTry:
492
+ result += `case ${index}: sys.tryIndex = ${code.no}; break; // TRY \n`;
493
+ break;
494
+ case NakoCodeCode:
495
+ {
496
+ // trim last
497
+ const s = code.value.replace(/\s+$/, '');
498
+ result += `case ${index}: {\n${s}\n};break;\n`;
499
+ break;
500
+ }
501
+ default:
502
+ throw new Error('invalid code type');
503
+ }
504
+ });
505
+ result = `
506
+ //-------------------------
507
+ // main_code
508
+ this.__labels = ${JSON.stringify(this.labels)};
509
+ this.nextAsync = (sys) => {
510
+ if (sys.index >= sys.codeSize || sys.index < 0) {return}
511
+ const __v0 = sys.__v0
512
+ try {
513
+ sys.inLoop = true
514
+ while (sys.index < sys.codeSize && sys.index >= 0) {
515
+ // console.log('@@[run]', sys.index)
516
+ switch (sys.index) {
517
+ // --- CODE.BEGIN ---
518
+ ${result}
519
+ // --- CODE.END ---
520
+ default:
521
+ sys.inLoop = false
522
+ console.log(sys.index, sys.__stack)
523
+ throw new Error('Invalid sys.index:' + sys.index)
524
+ break
525
+ }
526
+ // check next
527
+ if (sys.nextIndex >= 0) {
528
+ sys.index = sys.nextIndex
529
+ sys.nextIndex = -1
530
+ } else {
531
+ sys.index++
532
+ }
533
+ if (sys.async) {
534
+ sys.__saveSysenv(sys)
535
+ sys.async = false
536
+ break
537
+ }
538
+ } // end of while
539
+ sys.inLoop = false
540
+ } catch (e) {
541
+ sys.__errorAsync(e, sys)
542
+ }
543
+ }
544
+ this.__errorAsync = (e, sys) => { // エラーが起きた時呼び出す
545
+ sys.__v0["エラーメッセージ"] = e.message;
546
+ if (e.message == '__終わる__') {
547
+ sys.__stopAsync(sys)
548
+ return
549
+ }
550
+ if (sys.tryIndex >= 0) {
551
+ sys.index = sys.tryIndex;
552
+ setTimeout(() => {sys.nextAsync(sys)}, 1)
553
+ } else {
554
+ throw e
555
+ }
556
+ }
557
+ this.__call = (no, sys) => {
558
+ const info = {lastVars:sys.__vars, backNo: this.index + 1}
559
+ sys.__callstack.push(info);
560
+ sys.__vars = {"それ":""}
561
+ sys.__varslist.push(sys.__vars)
562
+ sys.nextIndex = no;
563
+ }
564
+ this.__return = sys => {
565
+ if (sys.__callstack.length === 0) {
566
+ sys.__destroySysenv(sys, sys.curSysenv.envid)
567
+ sys.index = -2
568
+ sys.nextIndex = -1
569
+ return
570
+ }
571
+ const sore = sys.__vars['それ'];
572
+ sys.__varslist.pop();
573
+ const info = sys.__callstack.pop();
574
+ sys.nextIndex = info.backNo;
575
+ sys.__vars = info.lastVars;
576
+ sys.__vars['それ'] = sore
577
+ sys.__stack.push(sore);
578
+ }
579
+ this.__resetAsync = sys => {
580
+ sys.index = 0
581
+ sys.codeSize = ${codes.length};
582
+ sys.async = false
583
+ sys.nextIndex = -1
584
+ sys.tryIndex = -1
585
+ }
586
+ this.__stopAsync = sys => {
587
+ sys.__resetAsync(sys)
588
+ sys.index = -1 // force stop!!
589
+ }
590
+ this.__callNakoCode = (no, backNo, sys) => {
591
+ this.__call(backNo, sys)
592
+ sys.nextIndex = no
593
+ const sysenv = sys.setAsync(sys)
594
+ setTimeout(() => {
595
+ // console.log('//__callNakoCode, back=', backNo, 'no=', no)
596
+ sys.compAsync(sys, sysenv)
597
+ } ,1)
598
+ }
599
+ this.__callNakoCodeEntry = (no, sys) => {
600
+ sys.__saveSysenv(sys)
601
+ sys.curSysenv = sys.__generateSysenv(sys)
602
+ sys.__restoreSysenv(sys)
603
+ sys.__vars = {"それ":""}
604
+ sys.__varslist.push(sys.__vars)
605
+ sys.index = no;
606
+ sys.nextAsync(sys)
607
+ }
608
+ this.__callObj = (vname, curNo, sys) => {
609
+ if (sys.__vars[vname]) {
610
+ const fname = sys.__vars[vname]
611
+ // console.log(sys.__labels)
612
+ if (fname && sys.__labels[fname]) {
613
+ const no = sys.__labels[fname]
614
+ sys.__call(no, sys)
615
+ return
616
+ } else {
617
+ console.log('vname=', vname, 'label=', fname)
618
+ }
619
+ }
620
+ throw new Error('async error in __callObj::', vname)
621
+ }
622
+ this.__generateSysenv = sys => {
623
+ sys.envid = ( sys.envid == null ? 0 : sys.envid ) + 1
624
+ const sysenv = {
625
+ callstack: [],
626
+ varstack: [],
627
+ varslist: [sys.__varslist[0], sys.__varslist[1], sys.__varslist[2]],
628
+ index: -1,
629
+ nextIndex: -1,
630
+ tryIndex: -1,
631
+ envid: sys.envid
632
+ }
633
+ sysenv.vars = sysenv.varslist[2]
634
+ if (sys.sysenvs == null) { sys.sysenvs={} }
635
+ sys.sysenvs[sys.envid] = sysenv
636
+ // console.log('generete envid '+sys.envid)
637
+ return sysenv
638
+ }
639
+ this.__destroySysenv = (sys, envid) => {
640
+ delete sys.sysenvs[envid]
641
+ // console.log('destroy envid '+envid)
642
+ }
643
+ this.__saveSysenv = sys => {
644
+ const sysenv = sys.curSysenv
645
+ sysenv.callstack = sys.__callstack
646
+ sysenv.varstack = sys.__stack
647
+ sysenv.varslist = sys.__varslist
648
+ sysenv.vars = sys.__vars
649
+ sysenv.index = sys.index
650
+ sysenv.nextIndex = sys.nextIndex
651
+ sysenv.tryIndex = sys.tryIndex
652
+ }
653
+ this.__restoreSysenv = sys => {
654
+ const sysenv = sys.curSysenv
655
+ sys.__callstack = sysenv.callstack
656
+ sys.__stack = sysenv.varstack
657
+ sys.__varslist = sysenv.varslist
658
+ sys.__vars = sysenv.vars
659
+ ___vars = sys.__vars
660
+ sys.index = sysenv.index
661
+ sys.nextIndex = sysenv.nextIndex
662
+ sys.tryIndex = sysenv.tryIndex
663
+ }
664
+ this.setAsync = sys => {
665
+ sys.async = true
666
+ return sys.curSysenv
667
+ }
668
+ this.compAsync = (sys,sysenv) => {
669
+ if (sys.async && sys.curSysenv != null && sysenv != null && sys.curSysenv.envid === sysenv.envid) {
670
+ sys.async = false
671
+ } else {
672
+ if (sys.curSysenv == null || sysenv == null || sys.curSysenv.envid !== sysenv.envid) {
673
+ sys.__saveSysenv(sys)
674
+ const envid = sys.curSysenv.envid
675
+ sys.curSysenv = sysenv
676
+ sys.__restoreSysenv(sys)
677
+ // console.log('switch envid '+envid+' to '+sys.curSysenv.envid)
678
+ }
679
+ sys.nextAsync(sys)
680
+ }
681
+ }
682
+
683
+ this.__resetAsync(this)
684
+ this.curSysenv = this.__generateSysenv(this)
685
+ this.nextAsync(this)
686
+ //-------------------------
687
+ `;
688
+ if (isTest) {
689
+ return '';
690
+ }
691
+ else {
692
+ return result;
693
+ }
694
+ }
695
+ /**
696
+ * @param {Ast} node
697
+ * @param {boolean} isExpression
698
+ */
699
+ _convGen(node, isExpression) {
700
+ let code = '';
701
+ if (node instanceof Array) {
702
+ for (let i = 0; i < node.length; i++) {
703
+ const n = node[i];
704
+ code += this._convGen(n, isExpression);
705
+ }
706
+ return code;
707
+ }
708
+ if (node === null) {
709
+ return 'null';
710
+ }
711
+ if (node === undefined) {
712
+ return 'undefined';
713
+ }
714
+ if (typeof (node) !== 'object') {
715
+ return '' + node;
716
+ }
717
+ // switch
718
+ switch (node.type) {
719
+ // === NOP ===
720
+ case 'nop':
721
+ break;
722
+ case 'comment':
723
+ if (!node.value) {
724
+ node.value = '';
725
+ }
726
+ this.addCode(new NakoCode(NakoCodeNop, node.value));
727
+ break;
728
+ case 'eol':
729
+ this.addCode(new NakoCode(NakoCodeEOL, this.convLineno(node, true)));
730
+ break;
731
+ // === 単純なコード変換 ===
732
+ case 'number':
733
+ this.addCodeStr(`sys.__stack.push(${node.value});//number`);
734
+ break;
735
+ case 'string':
736
+ this.convString(node);
737
+ break;
738
+ case 'word':
739
+ case 'variable':
740
+ this.convGetVar(node);
741
+ break;
742
+ case 'op':
743
+ case 'calc':
744
+ this.convOp(node);
745
+ break;
746
+ case 'renbun':
747
+ this.convRenbun(node);
748
+ break;
749
+ case 'not':
750
+ this._convGen(node.value, true);
751
+ this.addCodeStr('if (sys.__stack.length==0) throw new Error(\'NOTでスタックに値がありません\');' +
752
+ 'sys.__stack[sys.__stack.length-1] = (sys.__stack[sys.__stack.length-1]) ? 0:1');
753
+ break;
754
+ case '配列参照':
755
+ this.convRefArray(node);
756
+ break;
757
+ case 'json_array':
758
+ this.convJsonArray(node);
759
+ break;
760
+ case 'json_obj':
761
+ this.convJsonObj(node);
762
+ break;
763
+ case 'bool':
764
+ {
765
+ const b = (node.value) ? 'true' : 'false';
766
+ this.addCodeStr(`sys.__stack.push(${b})`);
767
+ break;
768
+ }
769
+ case 'null':
770
+ this.addCodeStr('sys.__stack.push(null)');
771
+ break;
772
+ case 'func':
773
+ case 'func_pointer':
774
+ case 'calc_func':
775
+ this.convFunc(node, isExpression); // 関数の呼び出し
776
+ break;
777
+ // === 文の変換 ===
778
+ case 'let':
779
+ this.convLet(node);
780
+ break;
781
+ case 'let_array':
782
+ this.convLetArray(node);
783
+ break;
784
+ case 'block':
785
+ for (let i = 0; i < node.block.length; i++) {
786
+ const b = node.block[i];
787
+ this._convGen(b, false);
788
+ }
789
+ break;
790
+ case 'if':
791
+ this.convIf(node);
792
+ break;
793
+ case 'repeat_times':
794
+ this.convRepeatTimes(node);
795
+ break;
796
+ case 'break':
797
+ this.addCodeStr(this.convCheckLoop(node, 'break'));
798
+ break;
799
+ case 'continue':
800
+ this.addCodeStr(this.convCheckLoop(node, 'continue'));
801
+ break;
802
+ case 'for':
803
+ this.convFor(node);
804
+ break;
805
+ case 'foreach':
806
+ this.convForeach(node);
807
+ break;
808
+ case 'while':
809
+ this.convWhile(node);
810
+ break;
811
+ case 'switch':
812
+ this.convSwitch(node);
813
+ break;
814
+ case 'return':
815
+ this.convReturn(node);
816
+ break;
817
+ case 'end':
818
+ code += this.addCodeStr('__varslist[0][\'終\']();');
819
+ break;
820
+ case 'def_local_var':
821
+ this.convDefLocalVar(node);
822
+ break;
823
+ case 'def_local_varlist':
824
+ code += this.addCodeStr(this.convDefLocalVarlist(node));
825
+ break;
826
+ case 'tikuji':
827
+ throw NakoSyntaxError.fromNode('「逐次実行」構文は「!非同期モード」では使えません。', node);
828
+ case 'speed_mode':
829
+ throw NakoSyntaxError.fromNode('「速度有線」構文は「!非同期モード」では使えません。', node);
830
+ case 'performance_monitor':
831
+ this.convPerformanceMonitor(node, isExpression);
832
+ break;
833
+ case 'func_obj':
834
+ this.convFuncObj(node);
835
+ break;
836
+ case 'def_test':
837
+ this.convDefTest(node);
838
+ break;
839
+ case 'def_func':
840
+ code += this.addCodeStr(this.convDefFunc(node));
841
+ break;
842
+ // TODO
843
+ case 'try_except':
844
+ code += this.convTryExcept(node);
845
+ break;
846
+ case 'require':
847
+ code += this.convRequire(node);
848
+ break;
849
+ default:
850
+ throw new Error('System Error: unknown_type=' + node.type);
851
+ }
852
+ return code;
853
+ }
854
+ convRequire(node) {
855
+ const gen = new NakoGen(this.com);
856
+ this.addCodeStr(gen.convRequire(node));
857
+ return '';
858
+ }
859
+ /**
860
+ * add code to array
861
+ * @param {string} codeStr
862
+ * @returns {string}
863
+ */
864
+ addCodeStr(codeStr) {
865
+ if (codeStr === '') {
866
+ return '';
867
+ }
868
+ const a = codeStr.split('\n');
869
+ const a2 = a.map((row) => ' ' + row.replace(/\s+$/, ''));
870
+ const c = new NakoCode(NakoCodeCode, a2.join('\n'));
871
+ return this.addCode(c);
872
+ }
873
+ /**
874
+ * add code to array
875
+ * @param {NakoCode} code
876
+ * @returns {string}
877
+ */
878
+ addCode(code) {
879
+ this.codeArray[this.codeId] = code;
880
+ this.codeId++;
881
+ return '';
882
+ }
883
+ /**
884
+ * make label for jump
885
+ * @param {string} name
886
+ * @returns {NakoCode}
887
+ */
888
+ makeLabel(name) {
889
+ const uniqLabel = name + '_' + (this.loopId++);
890
+ return this.makeLabelDirectly(uniqLabel);
891
+ }
892
+ /**
893
+ * make label for function
894
+ * @param {string} labelName
895
+ * @returns {NakoCode}
896
+ */
897
+ makeLabelDirectly(labelName) {
898
+ const c = new NakoCode(NakoCodeLabel, labelName);
899
+ this.labels[labelName] = -1;
900
+ return c;
901
+ }
902
+ /**
903
+ * make Jump
904
+ * @param {NakoCode} label
905
+ * @returns {NakoCode}
906
+ */
907
+ makeJump(label) {
908
+ return new NakoCode(NakoCodeJump, label.value);
909
+ }
910
+ /**
911
+ * make Jump if true
912
+ * @param {NakoCode} label
913
+ * @returns {NakoCode}
914
+ */
915
+ makeJumpIfTrue(label) {
916
+ return new NakoCode(NakoCodeJumpIfTrue, label.value);
917
+ }
918
+ /**
919
+ * make Jump if false
920
+ * @param {NakoCode} label
921
+ * @returns {NakoCode}
922
+ */
923
+ makeJumpIfFalse(label) {
924
+ return new NakoCode(NakoCodeJumpIfFalse, label.value);
925
+ }
926
+ /**
927
+ * @param {Ast} node
928
+ */
929
+ convIf(node) {
930
+ const labelBegin = this.makeLabel('もし:ここから');
931
+ const labelEnd = this.makeLabel('もし:ここまで');
932
+ const labelIfFalse = this.makeLabel('もし:違えば');
933
+ //
934
+ this.addCode(labelBegin);
935
+ this._convGen(node.expr, true);
936
+ this.addCode(this.makeJumpIfFalse(labelIfFalse));
937
+ this._convGen(node.block, false);
938
+ this.addCode(this.makeJump(labelEnd));
939
+ this.addCode(labelIfFalse);
940
+ if (node.falseBlock) {
941
+ this._convGen(node.falseBlock, false);
942
+ }
943
+ this.addCode(labelEnd);
944
+ return '';
945
+ }
946
+ convRepeatTimes(node) {
947
+ this.flagLoop = true;
948
+ this.varsSet.names.add('回数');
949
+ this.varsSet.readonly.add('回数');
950
+ // ループ管理変数を作成
951
+ const loopVar = `sys.__tmp_i${this.loopId}`;
952
+ this.loopId++;
953
+ // ループ回数を取得
954
+ const loopCount = `sys.__tmp_count${this.loopId}`;
955
+ this.loopId++;
956
+ this._convGen(node.value, true);
957
+ this.addCodeStr(`${loopCount} = sys.__stack.pop(); ${loopVar} = 0;`);
958
+ const labelCheck = this.makeLabel('回:条件チェック');
959
+ this.addCode(labelCheck);
960
+ const labelEnd = this.makeLabel('回:ここまで');
961
+ this.labelBreak = labelEnd;
962
+ this.labelContinue = labelCheck;
963
+ // 繰り返し判定
964
+ const kaisu = 'sys.__vars["回数"]';
965
+ const cond = `${kaisu} = ++${loopVar}\n` +
966
+ `sys.__stack.push(${loopVar} > ${loopCount})\n`;
967
+ this.addCodeStr(cond);
968
+ this.addCode(this.makeJumpIfTrue(labelEnd));
969
+ this.convGenLoop(node.block); // read block
970
+ this.addCode(this.makeJump(labelCheck));
971
+ this.addCode(labelEnd);
972
+ this.flagLoop = false;
973
+ return '';
974
+ }
975
+ /**
976
+ * @param {string} name
977
+ * @returns {{i: number, name: string, isTop: boolean, js: string} | null}
978
+ */
979
+ findVar(name) {
980
+ // __vars ? (ローカル変数)
981
+ if (this.varsSet.names.has(name)) {
982
+ return { i: this.varslistSet.length - 1, name, isTop: true, js: `sys.__vars[${JSON.stringify(name)}]` };
983
+ }
984
+ // __varslist ?
985
+ for (let i = 2; i >= 0; i--) {
986
+ if (this.varslistSet[i].names.has(name)) {
987
+ return { i, name, isTop: false, js: `sys.__varslist[${i}][${JSON.stringify(name)}]` };
988
+ }
989
+ }
990
+ return null;
991
+ }
992
+ /**
993
+ * 定義済みの変数の参照
994
+ * @param {string} name
995
+ * @param {Ast} position
996
+ */
997
+ genVar(name, position) {
998
+ const res = this.findVar(name);
999
+ const lno = position.line;
1000
+ if (res === null) {
1001
+ // 定義されていない名前の参照は変数の定義とみなす。
1002
+ // 多くの場合はundefined値を持つ変数であり分かりづらいバグを引き起こすが、
1003
+ // 「ナデシコする」などの命令の中で定義された変数の参照の場合があるため警告に留める。
1004
+ // ただし、自動的に定義される変数『引数』『それ』などは例外 #952
1005
+ if (name === '引数' || name === 'それ' || name === '対象' || name === '対象キー' || name === '回数') {
1006
+ // デフォルト定義されている変数名
1007
+ }
1008
+ else {
1009
+ this.__self.getLogger().warn(`変数『${name}』は定義されていません。`, position);
1010
+ }
1011
+ this.varsSet.names.add(name);
1012
+ return this.varname(name);
1013
+ }
1014
+ const i = res.i;
1015
+ // システム関数・変数の場合
1016
+ if (i === 0) {
1017
+ const pv = this.__self.getFunc(name);
1018
+ if (!pv) {
1019
+ return `${res.js}/*err:${lno}*/`;
1020
+ }
1021
+ if (pv.type === 'const' || pv.type === 'var') {
1022
+ return res.js;
1023
+ }
1024
+ if (pv.type === 'func') {
1025
+ if (!pv.josi) {
1026
+ throw new Error('[System Error]');
1027
+ }
1028
+ if (pv.josi.length === 0) {
1029
+ return `(${res.js}())`;
1030
+ }
1031
+ throw NakoSyntaxError.fromNode(`『${name}』が複文で使われました。単文で記述してください。(v1非互換)`, position);
1032
+ }
1033
+ throw NakoSyntaxError.fromNode(`『${name}』は関数であり参照できません。`, position);
1034
+ }
1035
+ return res.js;
1036
+ }
1037
+ convGetVar(node) {
1038
+ const name = node.value;
1039
+ let varName = `sys.__vars[${JSON.stringify(name)}]`;
1040
+ const o = this.findVar(name);
1041
+ if (o != null) {
1042
+ varName = o.js;
1043
+ }
1044
+ this.addCodeStr(`sys.__stack.push(${varName});`);
1045
+ }
1046
+ convComment(node) {
1047
+ let commentSrc = String(node.value);
1048
+ commentSrc = commentSrc.replace(/\n/g, '¶');
1049
+ const lineNo = this.convLineno(node, false);
1050
+ if (commentSrc === '' && lineNo === '') {
1051
+ return ';';
1052
+ }
1053
+ if (commentSrc === '') {
1054
+ return ';' + lineNo + '\n';
1055
+ }
1056
+ return ';' + lineNo + '//' + commentSrc + '\n';
1057
+ }
1058
+ convReturn(node) {
1059
+ // 関数の中であれば利用可能
1060
+ if (this.varsSet.names.has('!関数')) {
1061
+ throw NakoSyntaxError.fromNode('『戻る』がありますが、関数定義内のみで使用可能です。', node);
1062
+ }
1063
+ if (node.value) {
1064
+ this._convGen(node.value, true);
1065
+ this.addCodeStr('sys.__vars["それ"] = sys.__stack.pop()');
1066
+ }
1067
+ this.addCode(new NakoCode(NakoCodeReturn, ''));
1068
+ return '';
1069
+ }
1070
+ convCheckLoop(node, cmd) {
1071
+ // ループの中であれば利用可能
1072
+ if (!this.flagLoop) {
1073
+ const cmdj = (cmd === 'continue') ? '続ける' : '抜ける';
1074
+ throw NakoSyntaxError.fromNode(`『${cmdj}』文がありますが、それは繰り返しの中で利用してください。`, node);
1075
+ }
1076
+ if (cmd === 'continue') {
1077
+ if (this.labelContinue) {
1078
+ this.addCode(this.makeJump(this.labelContinue));
1079
+ }
1080
+ }
1081
+ else {
1082
+ if (this.labelBreak) {
1083
+ this.addCode(this.makeJump(this.labelBreak));
1084
+ }
1085
+ }
1086
+ return '';
1087
+ }
1088
+ convDefFuncCommon(node, name) {
1089
+ // deffunc_code
1090
+ const isMumeiFunc = (name === '');
1091
+ let funcName = name;
1092
+ if (isMumeiFunc) {
1093
+ funcName = `無名関数:${this.loopId++}`;
1094
+ }
1095
+ const labelEnd = this.makeLabel(`関数「${funcName}」:ここまで`);
1096
+ this.addCode(this.makeJump(labelEnd));
1097
+ const labelBegin = this.makeLabelDirectly(funcName);
1098
+ labelBegin.tag = NakoCodeTagIsFuncpoint; // 削除対象からはずすため
1099
+ this.addCode(labelBegin);
1100
+ //
1101
+ const initialNames = new Set();
1102
+ this.varsSet = { isFunction: true, names: initialNames, readonly: new Set() };
1103
+ this.varsSet.names.add('それ');
1104
+ // ローカル変数をPUSHする
1105
+ this.varslistSet.push(this.varsSet);
1106
+ // JSの引数と引数をバインド
1107
+ const meta = isMumeiFunc ? node.meta : node.name.meta;
1108
+ let code = '';
1109
+ let codeCall = '';
1110
+ code += `//関数『${funcName}』の初期化処理\n`;
1111
+ // 宣言済みの名前を保存
1112
+ // const varsDeclared = Array.from(this.varsSet.names.values())
1113
+ // 引数をローカル変数に設定 (スタックの末尾から取得する必要があるので、逆順に値を得る)
1114
+ code += '// 引数をローカル変数として登録\n';
1115
+ for (let i = meta.varnames.length - 1; i >= 0; i--) {
1116
+ const word = meta.varnames[i];
1117
+ code += ` ${this.varname(word)} = sys.__stack.pop();\n`;
1118
+ this.varsSet.names.add(word);
1119
+ codeCall += ''; // sys.__stack.push(arguments[${i}]);\n
1120
+ }
1121
+ code += '// ここまで:引数をローカル変数として登録\n';
1122
+ this.addCodeStr(code);
1123
+ // 関数定義は、グローバル領域で。
1124
+ this.usedFuncSet.add(funcName);
1125
+ this.varslistSet[1].names.add(funcName);
1126
+ this.nakoFuncList[funcName] = {
1127
+ josi: meta.josi,
1128
+ fn: '(function(){\n' +
1129
+ ' const sys = (arguments.length > 0) ? arguments[arguments.length-1] : {}; \n' +
1130
+ ' if (sys.newenv) { \n' +
1131
+ ' sys.newenv = false\n' +
1132
+ ` sys.__callNakoCodeEntry(sys.__labels['${funcName}'], sys);` + '\n' +
1133
+ ' } else {\n' +
1134
+ ' ' + codeCall + '\n' +
1135
+ ` sys.__callNakoCode(sys.__labels['${funcName}'], sys.nextIndex, sys);` + '\n' +
1136
+ ' if (!sys.inLoop) { sys.nextAsync(sys) }\n' +
1137
+ ' } })',
1138
+ type: 'func'
1139
+ };
1140
+ // ブロックを解析
1141
+ this._convGen(node.block, false);
1142
+ this.varslistSet.pop();
1143
+ this.varsSet = this.varslistSet[this.varslistSet.length - 1];
1144
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
1145
+ this.__self.__varslist[1][funcName] = function () { };
1146
+ this.addCode(new NakoCode(NakoCodeReturn, ''));
1147
+ this.addCode(labelEnd);
1148
+ // 無名関数の定義であれば無名関数をPUSH
1149
+ if (!name) {
1150
+ this.addCodeStr(`sys.__stack.push('${funcName}')`);
1151
+ }
1152
+ return '';
1153
+ }
1154
+ convDefTest(node) {
1155
+ throw NakoSyntaxError.fromNode('テスト構文は!非同期モードでは使えません。', node);
1156
+ }
1157
+ convDefFunc(node) {
1158
+ const name = NakoGen.getFuncName(node.name.value);
1159
+ this.convDefFuncCommon(node, name);
1160
+ // ★この時点では関数のコードを生成しない★
1161
+ // プログラム冒頭でコード生成時に関数定義を行う
1162
+ return '';
1163
+ }
1164
+ convFuncObj(node) {
1165
+ return this.convDefFuncCommon(node, '');
1166
+ }
1167
+ convJsonObj(node) {
1168
+ const list = node.value;
1169
+ const objName = `sys.__tmp_obj${this.loopId++}`;
1170
+ this.addCodeStr(objName + '={}; // convJsonObj::ここから');
1171
+ list.forEach((e) => {
1172
+ this._convGen(e.value, true);
1173
+ this._convGen(e.key, true);
1174
+ this.addCodeStr(`${objName}[sys.__stack.pop()]=sys.__stack.pop()`);
1175
+ });
1176
+ this.addCodeStr(`this.__stack.push(${objName}); delete $objName; // convJsonObj::ここまで`);
1177
+ return '';
1178
+ }
1179
+ convJsonArray(node) {
1180
+ const list = node.value;
1181
+ this.addCode(this.makeLabel('convJsonArray::ここから'));
1182
+ list.forEach((e) => this._convGen(e, true));
1183
+ const size = list.length;
1184
+ this.addCodeStr(`sys.__stack.push(sys.__stack.splice(sys.__stack.length-${size},${size}))`);
1185
+ return '';
1186
+ }
1187
+ convRefArray(node) {
1188
+ // 名前をPUSH
1189
+ this._convGen(node.name, true);
1190
+ const list = node.index;
1191
+ for (let i = 0; i < list.length; i++) {
1192
+ // push index
1193
+ this._convGen(list[i], true);
1194
+ // pop index & push value
1195
+ this.addCodeStr('const idx = sys.__stack.pop();\n' +
1196
+ 'const obj = sys.__stack.pop();\n' +
1197
+ 'sys.__stack.push(obj[idx]);');
1198
+ }
1199
+ return '';
1200
+ }
1201
+ convLetArray(node) {
1202
+ // 代入する値をPUSH
1203
+ this._convGen(node.value, true);
1204
+ // 変数を取得
1205
+ this._convGen(node.name, true);
1206
+ const list = node.index;
1207
+ for (let i = 0; i < list.length; i++) {
1208
+ this._convGen(list[i], true);
1209
+ if (i === list.length - 1) { // 代入
1210
+ this.addCodeStr('const idx = this.__stack.pop();' +
1211
+ 'const obj = this.__stack.pop();' +
1212
+ 'const val = this.__stack.pop();' +
1213
+ 'obj[idx]=val;');
1214
+ break;
1215
+ }
1216
+ // index アクセス
1217
+ this.addCodeStr('const idx = sys.__stack.pop();\n' +
1218
+ 'const obj = sys.__stack.pop();\n' +
1219
+ 'sys.__stack.push(obj[idx]);');
1220
+ }
1221
+ return '';
1222
+ }
1223
+ convGenLoop(node) {
1224
+ const tmpflag = this.flagLoop;
1225
+ this.flagLoop = true;
1226
+ try {
1227
+ return this._convGen(node, false);
1228
+ }
1229
+ finally {
1230
+ this.flagLoop = tmpflag;
1231
+ }
1232
+ }
1233
+ convFor(node) {
1234
+ this.flagLoop = true;
1235
+ // ループ変数について
1236
+ let word;
1237
+ if (node.word !== null) { // ループ変数を使う時
1238
+ const varName = node.word.value;
1239
+ this.varsSet.names.add(varName);
1240
+ word = this.varname(varName);
1241
+ }
1242
+ else {
1243
+ this.varsSet.names.add('dummy');
1244
+ word = this.varname('dummy');
1245
+ }
1246
+ const sore = this.varname('それ');
1247
+ const idLoop = this.loopId++;
1248
+ const varI = `sys.__tmp__i${idLoop}`;
1249
+ // ループ条件を変数に入れる用
1250
+ const varTo = `sys.__tmp__to${idLoop}`;
1251
+ // ループ条件を確認
1252
+ this._convGen(node.from, true);
1253
+ this._convGen(node.to, true);
1254
+ this.addCodeStr(`${varTo}=sys.__stack.pop();${varI}=sys.__stack.pop();`);
1255
+ // ループ変数を初期化
1256
+ this.addCodeStr(`${sore} = ${word} = ${varI}`);
1257
+ // 繰り返し判定
1258
+ const labelCheck = this.makeLabel('繰返:条件確認');
1259
+ const labelInc = this.makeLabel('繰返:加算');
1260
+ this.addCode(labelCheck);
1261
+ const labelEnd = this.makeLabel('繰返:ここまで');
1262
+ this.addCodeStr(`sys.__stack.push(${word} <= ${varTo})`);
1263
+ this.addCode(this.makeJumpIfFalse(labelEnd));
1264
+ this.labelContinue = labelInc;
1265
+ this.labelBreak = labelEnd;
1266
+ // ループ内のブロック内容を得る
1267
+ this.convGenLoop(node.block); // block
1268
+ this.addCode(labelInc);
1269
+ this.addCodeStr(`${sore} = ++${word};`);
1270
+ this.addCode(this.makeJump(labelCheck));
1271
+ this.addCode(labelEnd);
1272
+ this.addCodeStr(`delete ${varI};delete ${varTo};//繰返:掃除`);
1273
+ this.flagLoop = false;
1274
+ return '';
1275
+ }
1276
+ convForeach(node) {
1277
+ this.flagLoop = true;
1278
+ // 対象を用意する
1279
+ let taisyo = '__v0["対象"]';
1280
+ const taisyoKey = '__v0["対象キー"]';
1281
+ if (node.name) {
1282
+ taisyo = this.varname(node.name.value);
1283
+ this.varsSet.names.add(node.name.value);
1284
+ }
1285
+ // 反復対象を調べる
1286
+ const target = node.target;
1287
+ if (target === null) {
1288
+ throw NakoSyntaxError.fromNode('『反復』の対象がありません。', node);
1289
+ }
1290
+ const sore = this.varname('それ');
1291
+ const targetArray = `sys.__tmp__target${this.loopId++}`;
1292
+ const targetKeys = `sys.__tmp__keys${this.loopId++}`;
1293
+ const loopVar = `sys.__tmp__i${this.loopId++}`;
1294
+ const loopCount = `sys.__tmp__count${this.loopId++}`;
1295
+ // 反復対象を評価
1296
+ this._convGen(node.target, true);
1297
+ // どのように反復するか判定
1298
+ const initCode = '// 反復: 初期化\n' +
1299
+ `${targetArray} = sys.__stack.pop();\n` +
1300
+ `${loopVar} = 0;\n` +
1301
+ // 文字列や数値なら反復できるように配列に入れる
1302
+ `if (typeof(${targetArray}) == 'string' || typeof(${targetArray}) == 'number') { ${targetArray} = [${targetArray}]; }\n` +
1303
+ // Objectならキー一覧を得る
1304
+ `if (${targetArray} instanceof Array) { ${loopCount} = ${targetArray}.length; }\n` +
1305
+ 'else { // キーの一覧を得る\n' +
1306
+ ` ${targetKeys} = Object.keys(${targetArray}); \n` +
1307
+ ' // hasOwnPropertyがfalseならばkeyを消す処理\n' +
1308
+ ` ${targetKeys} = ${targetKeys}.filter((key)=>{ return ${targetArray}.hasOwnProperty(key) })\n` +
1309
+ ` ${loopCount} = ${targetKeys}.length;\n` +
1310
+ '}\n';
1311
+ this.addCodeStr(initCode);
1312
+ const labelCheck = this.makeLabel('反復:条件確認');
1313
+ const labelInc = this.makeLabel('反復:加算');
1314
+ const labelEnd = this.makeLabel('反復:ここまで');
1315
+ this.labelBreak = labelEnd;
1316
+ this.labelContinue = labelInc;
1317
+ this.addCode(labelCheck);
1318
+ const setTarget = `if (${targetArray} instanceof Array) {\n` +
1319
+ // eslint-disable-next-line no-irregular-whitespace
1320
+ ` ${taisyo} = ${sore} = ${targetArray}[${loopVar}]; ${taisyoKey} = ${loopVar};\n` +
1321
+ '} else {\n' +
1322
+ ` ${taisyoKey} = ${targetKeys}[${loopVar}]; ${taisyo} = ${sore} = ${targetArray}[${taisyoKey}];\n` +
1323
+ '}\n';
1324
+ this.addCodeStr(`${setTarget}\nsys.__stack.push(${loopVar} < ${loopCount});`);
1325
+ this.addCode(this.makeJumpIfFalse(labelEnd));
1326
+ // 反復ブロックを定義
1327
+ this.convGenLoop(node.block); // block
1328
+ // 加算
1329
+ this.addCode(labelInc);
1330
+ this.addCodeStr(`${loopVar}++`);
1331
+ this.addCode(this.makeJump(labelCheck));
1332
+ this.addCode(labelEnd);
1333
+ this.flagLoop = false;
1334
+ return '';
1335
+ }
1336
+ convWhile(node) {
1337
+ this.flagLoop = true;
1338
+ const labelBegin = this.makeLabel('間:ここから');
1339
+ const labelEnd = this.makeLabel('間:ここまで');
1340
+ this.labelContinue = labelBegin;
1341
+ this.labelBreak = labelEnd;
1342
+ this.addCode(labelBegin);
1343
+ // 条件をスタックに
1344
+ this._convGen(node.cond, true);
1345
+ this.addCode(this.makeJumpIfFalse(labelEnd));
1346
+ // ブロックを追加
1347
+ this.convGenLoop(node.block);
1348
+ this.addCode(this.makeJump(labelBegin));
1349
+ this.addCode(labelEnd);
1350
+ this.flagLoop = false;
1351
+ return '';
1352
+ }
1353
+ /**
1354
+ * @param {Ast} _node
1355
+ * @param {boolean} _isExpression
1356
+ */
1357
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1358
+ convSpeedMode(_node, _isExpression) {
1359
+ return '';
1360
+ }
1361
+ /**
1362
+ * @param {Ast} node
1363
+ * @param {boolean} isExpression
1364
+ */
1365
+ convPerformanceMonitor(node, isExpression) {
1366
+ const prev = { ...this.performanceMonitor };
1367
+ if (node.options['ユーザ関数']) {
1368
+ this.performanceMonitor.userFunction++;
1369
+ }
1370
+ if (node.options['システム関数本体']) {
1371
+ this.performanceMonitor.systemFunctionBody++;
1372
+ }
1373
+ if (node.options['システム関数']) {
1374
+ this.performanceMonitor.systemFunction++;
1375
+ }
1376
+ this._convGen(node.block, isExpression);
1377
+ this.performanceMonitor = prev;
1378
+ }
1379
+ convSwitch(node) {
1380
+ // 値をPUSH
1381
+ this._convGen(node.value, true);
1382
+ const varValue = `sys.__tmp__i${this.loopId++}`;
1383
+ this.addCodeStr(`${varValue} = sys.__stack.pop()`);
1384
+ const labelEnd = this.makeLabel('条件分岐:ここまで');
1385
+ const cases = node.cases;
1386
+ for (let i = 0; i < cases.length; i++) {
1387
+ const cvalue = cases[i][0];
1388
+ if (cvalue.type === '違えば') {
1389
+ this.convGenLoop(cases[i][1]);
1390
+ }
1391
+ else {
1392
+ const nextLabel = this.makeLabel('条件分岐:次');
1393
+ this._convGen(cvalue, true);
1394
+ this.addCodeStr(`sys.__stack.push(sys.__stack.pop() == ${varValue})`);
1395
+ this.addCode(this.makeJumpIfFalse(nextLabel));
1396
+ this.convGenLoop(cases[i][1]);
1397
+ this.addCode(this.makeJump(labelEnd));
1398
+ this.addCode(nextLabel);
1399
+ }
1400
+ }
1401
+ this.addCode(labelEnd);
1402
+ this.addCodeStr(`delete ${varValue}//条件分岐:掃除`);
1403
+ return '';
1404
+ }
1405
+ convFuncGetArgsCalcType(funcName, func, node) {
1406
+ const opts = {};
1407
+ for (let i = 0; i < node.args.length; i++) {
1408
+ const arg = node.args[i];
1409
+ if (i === 0 && arg === null) {
1410
+ this.addCodeStr('sys.__stack.push(sys.__vars[\'それ\'])');
1411
+ opts.sore = true;
1412
+ }
1413
+ else {
1414
+ // 関数の引数を評価
1415
+ this._convGen(arg, true);
1416
+ }
1417
+ }
1418
+ return opts;
1419
+ }
1420
+ getPluginList() {
1421
+ const r = [];
1422
+ for (const name in this.__self.__module) {
1423
+ r.push(name);
1424
+ }
1425
+ return r;
1426
+ }
1427
+ /**
1428
+ * 関数の呼び出し
1429
+ * @param {Ast} node
1430
+ * @param {boolean} isExpression
1431
+ * @returns string コード
1432
+ */
1433
+ convFunc(node, isExpression) {
1434
+ let isJSFunc = false;
1435
+ let isMumeiFunc = false;
1436
+ const funcName = NakoGen.getFuncName(node.name);
1437
+ const res = this.findVar(funcName);
1438
+ if (res === null) {
1439
+ throw NakoSyntaxError.fromNode(`関数『${funcName}』が見当たりません。有効プラグイン=[` + this.getPluginList().join(', ') + ']', node);
1440
+ }
1441
+ let func;
1442
+ if (res.i === 0) { // plugin function
1443
+ func = this.__self.getFunc(funcName);
1444
+ if (func.type !== 'func') {
1445
+ throw NakoSyntaxError.fromNode(`『${funcName}』は関数ではありません。`, node);
1446
+ }
1447
+ isJSFunc = true;
1448
+ }
1449
+ else {
1450
+ func = this.nakoFuncList[funcName];
1451
+ // 無名関数の可能性
1452
+ if (func === undefined) {
1453
+ isMumeiFunc = true;
1454
+ func = { return_none: false };
1455
+ }
1456
+ }
1457
+ // 関数の参照渡しか?
1458
+ if (node.type === 'func_pointer') {
1459
+ return res.js;
1460
+ }
1461
+ // 関数の参照渡しでない場合
1462
+ // 関数定義より助詞を一つずつ調べる
1463
+ const argsOpts = this.convFuncGetArgsCalcType(funcName, func, node);
1464
+ // function
1465
+ this.usedFuncSet.add(funcName);
1466
+ let funcBegin = '';
1467
+ let funcEnd = '';
1468
+ // setter?
1469
+ if (node.setter) {
1470
+ funcBegin += ';__self.isSetter = true;\n';
1471
+ funcEnd += ';__self.isSetter = false;\n';
1472
+ }
1473
+ // 変数「それ」が補完されていることをヒントとして出力
1474
+ if (argsOpts.sore) {
1475
+ funcBegin += '/*[sore]*/';
1476
+ }
1477
+ // 引数をスタックに積む
1478
+ const arcCount = node.args.length;
1479
+ // 必要な引数分だけスタックから下ろして呼び出す
1480
+ let code = '';
1481
+ if (isJSFunc) {
1482
+ code += funcBegin;
1483
+ code += `const args = sys.__stack.splice(sys.__stack.length - ${arcCount}, ${arcCount});\n`;
1484
+ // code += `console.log("call:${funcName}", args, 'sys');\n`
1485
+ code += 'args.push(sys);\n';
1486
+ code += `const ret = ${res.js}.apply(sys, args);\n`;
1487
+ if (!func.return_none) {
1488
+ code += 'sys.__vars[\'それ\'] = ret;\n';
1489
+ if (isExpression) {
1490
+ code += 'sys.__stack.push(ret);\n';
1491
+ }
1492
+ }
1493
+ code += funcEnd;
1494
+ this.addCodeStr(code);
1495
+ }
1496
+ else {
1497
+ if (isMumeiFunc) {
1498
+ this.addCode(new NakoCode(NakoCodeCallObj, funcName));
1499
+ }
1500
+ else {
1501
+ this.addCode(new NakoCode(NakoCodeCall, funcName));
1502
+ }
1503
+ if (!isExpression) {
1504
+ this.addCodeStr('sys.__stack.pop();// 戻り値を利用しない関数呼出');
1505
+ }
1506
+ }
1507
+ }
1508
+ convRenbun(node) {
1509
+ this._convGen(node.left, false);
1510
+ this._convGen(node.right, true);
1511
+ }
1512
+ convOp(node) {
1513
+ const OP_TBL = {
1514
+ '&': '+""+',
1515
+ eq: '==',
1516
+ noteq: '!=',
1517
+ '===': '===',
1518
+ '!==': '!==',
1519
+ gt: '>',
1520
+ lt: '<',
1521
+ gteq: '>=',
1522
+ lteq: '<=',
1523
+ and: '&&',
1524
+ or: '||',
1525
+ shift_l: '<<',
1526
+ shift_r: '>>',
1527
+ shift_r0: '>>>',
1528
+ '÷': '/'
1529
+ };
1530
+ const op = node.operator; // 演算子
1531
+ // 値はスタックに載せられる
1532
+ // left
1533
+ this._convGen(node.left, true);
1534
+ // right
1535
+ this._convGen(node.right, true);
1536
+ // calc
1537
+ let code = 'const rv = sys.__stack.pop();\n' +
1538
+ 'const lv = sys.__stack.pop();\n';
1539
+ if (op === '^') {
1540
+ code += 'const v = (Math.pow(lv, rv))\n';
1541
+ }
1542
+ else {
1543
+ const op2 = OP_TBL[op] || op;
1544
+ code += `const v = ((lv) ${op2} (rv));\n`;
1545
+ }
1546
+ // code += `if (isNaN(v) && '${op}' != '&') { console.log('ERROR:${op}', lv, rv) }\n`
1547
+ code += `sys.__stack.push(v); //op:${op}\n`;
1548
+ this.addCodeStr(code);
1549
+ }
1550
+ convLet(node) {
1551
+ let code = '';
1552
+ // 値をスタックに載せる
1553
+ if (node.value === null) {
1554
+ // 値が省略されたら「それ」を載せる
1555
+ this.addCodeStr('sys.__stack.push(sys.__vars[\'それ\'])');
1556
+ }
1557
+ else {
1558
+ // 値がある場合
1559
+ this._convGen(node.value, true);
1560
+ }
1561
+ // 変数名
1562
+ const name = node.name.value;
1563
+ const res = this.findVar(name);
1564
+ if (res === null) {
1565
+ this.varsSet.names.add(name);
1566
+ code = `${this.varname(name)}=sys.__stack.pop();`;
1567
+ }
1568
+ else {
1569
+ // 定数ならエラーを出す
1570
+ if (this.varslistSet[res.i].readonly.has(name)) {
1571
+ throw NakoSyntaxError.fromNode(`定数『${name}』は既に定義済みなので、値を代入することはできません。`, node);
1572
+ }
1573
+ code = `${res.js}=sys.__stack.pop();`;
1574
+ }
1575
+ this.addCodeStr(code + '//let');
1576
+ }
1577
+ convDefLocalVar(node) {
1578
+ if (node.value === null) {
1579
+ this.addCodeStr('sys.__stack.push(null)');
1580
+ }
1581
+ else {
1582
+ this._convGen(node.value, true);
1583
+ }
1584
+ const name = node.name.value;
1585
+ const vtype = node.vartype; // 変数 or 定数
1586
+ // 二重定義?
1587
+ if (this.varsSet.names.has(name)) {
1588
+ throw NakoSyntaxError.fromNode(`${vtype}『${name}』の二重定義はできません。`, node);
1589
+ }
1590
+ this.varsSet.names.add(name);
1591
+ // 定数?
1592
+ if (vtype === '定数') {
1593
+ this.varsSet.readonly.add(name);
1594
+ }
1595
+ this.addCodeStr(`${this.varname(name)}=sys.__stack.pop()`);
1596
+ return '';
1597
+ }
1598
+ // #563 複数変数への代入
1599
+ convDefLocalVarlist(node) {
1600
+ const vtype = node.vartype; // 変数 or 定数
1601
+ if (node.value === null) {
1602
+ this.addCodeStr('sys.__stack.push(null)');
1603
+ }
1604
+ else {
1605
+ this._convGen(node.value, true);
1606
+ }
1607
+ const varI = `sys.__tmp_i${this.loopId}`;
1608
+ this.loopId++;
1609
+ this.addCodeStr(`${varI}=sys.__stack.pop();if (!(${varI} instanceof Array)) { ${varI}=[${varI}] }`);
1610
+ for (const nameObj of node.names) {
1611
+ const name = nameObj.value;
1612
+ // 二重定義?
1613
+ if (this.varsSet.names.has(name)) {
1614
+ throw NakoSyntaxError.fromNode(`${vtype}『${name}』の二重定義はできません。`, node);
1615
+ }
1616
+ //
1617
+ this.varsSet.names.add(name);
1618
+ if (vtype === '定数') {
1619
+ this.varsSet.readonly.add(name);
1620
+ }
1621
+ const vname = this.varname(name);
1622
+ this.addCodeStr(`${vname}=${varI}.pop()`);
1623
+ }
1624
+ this.addCodeStr(`delete ${varI}//複数代入:掃除`);
1625
+ return '';
1626
+ }
1627
+ convString(node) {
1628
+ let value = '' + node.value;
1629
+ const mode = node.mode;
1630
+ value = value.replace(/\\/g, '\\\\');
1631
+ value = value.replace(/"/g, '\\"');
1632
+ value = value.replace(/\r/g, '\\r');
1633
+ value = value.replace(/\n/g, '\\n');
1634
+ if (mode === 'ex') {
1635
+ throw new Error('[システムエラー] ジェネレーターでの文字列の展開はサポートしていません');
1636
+ }
1637
+ this.addCodeStr(`sys.__stack.push("${value}")//string`);
1638
+ return '"' + value + '"';
1639
+ }
1640
+ convTryExcept(node) {
1641
+ const labelExcept = this.makeLabel('エラー監視:ならば');
1642
+ const labelEnd = this.makeLabel('エラー監視:ここまで');
1643
+ // エラーをひっかけるように設定
1644
+ this.addCode(new NakoCode(NakoCodeTry, labelExcept.value));
1645
+ this._convGen(node.block, false);
1646
+ this.addCode(this.makeJump(labelEnd));
1647
+ this.addCode(labelExcept);
1648
+ this._convGen(node.errBlock, false);
1649
+ this.addCode(labelEnd);
1650
+ }
1651
+ }
1652
+ // ブラウザに登録する
1653
+ if (typeof (navigator) === 'object' && typeof (navigator.nako3) === 'object') {
1654
+ // Webブラウザの場合
1655
+ const nako3 = navigator.nako3;
1656
+ if (nako3.addCodeGenerator) {
1657
+ nako3.addCodeGenerator('非同期モード', NakoGenASync);
1658
+ }
1659
+ }