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,1719 @@
1
+ /**
2
+ * パーサーが生成した中間オブジェクトを実際のJavaScriptのコードに変換する。
3
+ * なお速度優先で忠実にJavaScriptのコードを生成する。
4
+ */
5
+
6
+ import { NakoRuntimeError, NakoSyntaxError, NakoError } from './nako_errors.mjs'
7
+ import { NakoLexer } from './nako_lexer.mjs'
8
+ import { Ast, FuncList, FuncArgs, Token } from './nako_types.mjs'
9
+ import { NakoCompiler } from './nako3.mjs'
10
+
11
+ // なでしこで定義した関数の開始コードと終了コード
12
+ const topOfFunction = '(function(){\n'
13
+ const endOfFunction = '})'
14
+ const topOfFunctionAsync = '(async function(){\n'
15
+
16
+ interface VarsSet {
17
+ isFunction: boolean;
18
+ names: Set<string>;
19
+ readonly: Set<string>;
20
+ }
21
+ interface SpeedMode {
22
+ lineNumbers: number; // 行番号を出力しない
23
+ implicitTypeCasting: number; // 数値加算でparseFloatを出力しない
24
+ invalidSore: number; // 「それ」を用いない
25
+ forcePure: number; // 全てのシステム命令をpureとして扱う。命令からローカル変数への参照が出来なくなる。
26
+ }
27
+ interface PerformanceMonitor {
28
+ userFunction: number; // 呼び出されたユーザ関数
29
+ systemFunction: number; // システム関数(呼び出しコードを含む)
30
+ systemFunctionBody: number; // システム関数(呼び出しコードを除く)
31
+ mumeiId: number;
32
+ }
33
+ interface FindVarResult {
34
+ i: number;
35
+ name: string;
36
+ isTop: boolean;
37
+ js: string
38
+ }
39
+ /** コード生成オプション */
40
+ export class NakoGenOptions {
41
+ isTest: boolean
42
+ importFiles: string[]
43
+ codeStandalone: string
44
+ codeEnv: string
45
+ constructor (isTest = false, importFiles: string[] = [], codeStandalone = '', convEnv = '') {
46
+ this.isTest = isTest
47
+ this.codeStandalone = codeStandalone
48
+ this.codeEnv = convEnv
49
+ this.importFiles = ['plugin_system.mjs', 'plugin_math.mjs', 'plugin_csv.mjs', 'plugin_promise.mjs', 'plugin_test.mjs']
50
+ for (const fname of importFiles) {
51
+ this.importFiles.push(fname)
52
+ }
53
+ }
54
+ }
55
+
56
+ /**
57
+ * 構文木からJSのコードを生成するクラス
58
+ */
59
+ export class NakoGen {
60
+ private nakoFuncList: FuncList; // なでしこ自身で定義した関数の一覧
61
+ private nakoTestFuncs: FuncList; // テストのための関数
62
+ private usedFuncSet: Set<string>; // 利用があった関数をメモする
63
+ private usedAsyncFn: boolean; // 非同期関数が使われているか判定
64
+ private loopId: number; // ループを生成する際にダミーのループ変数を管理するため
65
+ private flagLoop: boolean; // 変換中のソースがループの中かどうかを判定する
66
+ // コード生成オプション
67
+ private speedMode: SpeedMode;
68
+ private performanceMonitor: PerformanceMonitor;
69
+ private warnUndefinedVar: boolean;
70
+ private warnUndefinedReturnUserFunc: number;
71
+ private warnUndefinedCallingUserFunc: number;
72
+ private warnUndefinedCallingSystemFunc: number;
73
+ private warnUndefinedCalledUserFuncArgs: number;
74
+ // 変数管理
75
+ private varslistSet: VarsSet[]; // [システム変数一覧, グローバル変数一覧, ローカル変数一覧]で変数セットを記録
76
+ private varsSet: VarsSet; // ローカルな変数を記録
77
+ // public
78
+ numAsyncFn: number;
79
+ __self: NakoCompiler;
80
+ genMode: string;
81
+ lastLineNo: string | null; // `l123:main.nako3`形式
82
+
83
+ /** constructor
84
+ * @param com コンパイラのインスタンス
85
+ */
86
+ constructor (com: NakoCompiler) {
87
+ /**
88
+ * 出力するJavaScriptコードのヘッダー部分で定義する必要のある関数。fnはjsのコード。
89
+ * プラグイン関数は含まれない。
90
+ */
91
+ this.nakoFuncList = { ...com.getNakoFuncList() }
92
+
93
+ /**
94
+ * なでしこで定義したテストの一覧
95
+ */
96
+ this.nakoTestFuncs = {}
97
+
98
+ /**
99
+ * プログラム内で参照された関数のリスト。プラグインの命令を含む。
100
+ * JavaScript単体で実行するとき、このリストにある関数の定義をJavaScriptコードの先頭に付け足す。
101
+ */
102
+ this.usedFuncSet = new Set()
103
+
104
+ /**
105
+ * ループ時の一時変数が被らないようにIDで管理
106
+ */
107
+ this.loopId = 1
108
+
109
+ /**
110
+ * 非同関数を何回使ったか
111
+ */
112
+ this.numAsyncFn = 0
113
+
114
+ /**
115
+ * 関数定義の際、関数の中でasyncFn=trueの関数を呼び出したかどうかを調べる @see convDefFuncCommon
116
+ */
117
+ this.usedAsyncFn = false
118
+
119
+ /** 変換中の処理が、ループの中かどうかを判定する */
120
+ this.flagLoop = false
121
+
122
+ this.__self = com
123
+
124
+ /** コードジェネレータの種類 */
125
+ this.genMode = 'sync'
126
+
127
+ /** 行番号とファイル名が分かるときは `l123:main.nako3`、行番号だけ分かるときは `l123`、そうでなければ任意の文字列。 */
128
+ this.lastLineNo = null
129
+
130
+ /** スタック */
131
+ this.varslistSet = com.__varslist.map((v) => ({ isFunction: false, names: new Set(Object.keys(v)), readonly: new Set() }))
132
+
133
+ /** スタックトップ */
134
+ this.varsSet = { isFunction: false, names: new Set(), readonly: new Set() }
135
+ this.varslistSet[2] = this.varsSet
136
+
137
+ // 1以上のとき高速化する。
138
+ // 実行速度優先ブロック内で1増える。
139
+ this.speedMode = {
140
+ lineNumbers: 0, // 行番号を出力しない
141
+ implicitTypeCasting: 0, // 数値加算でparseFloatを出力しない
142
+ invalidSore: 0, // 「それ」を用いない
143
+ forcePure: 0 // 全てのシステム命令をpureとして扱う。命令からローカル変数への参照が出来なくなる。
144
+ }
145
+
146
+ // 1以上のとき測定をinjectする。
147
+ // パフォーマンスモニタのブロック内で1増える。
148
+ this.performanceMonitor = {
149
+ userFunction: 0, // 呼び出されたユーザ関数
150
+ systemFunction: 0, // システム関数(呼び出しコードを含む)
151
+ systemFunctionBody: 0, // システム関数(呼び出しコードを除く)
152
+ mumeiId: 0
153
+ }
154
+
155
+ /**
156
+ * 未定義の変数の警告を行う
157
+ */
158
+ this.warnUndefinedVar = true
159
+
160
+ // 暫定変数
161
+ this.warnUndefinedReturnUserFunc = 1
162
+ this.warnUndefinedCallingUserFunc = 1
163
+ this.warnUndefinedCallingSystemFunc = 1
164
+ this.warnUndefinedCalledUserFuncArgs = 1
165
+ }
166
+
167
+ static isValidIdentifier (name: string) {
168
+ // TODO: いらなそうな部分は削る
169
+ // https://stackoverflow.com/a/9337047
170
+ // eslint-disable-next-line no-misleading-character-class
171
+ return /^(?!(?:do|if|in|for|let|new|try|var|case|else|enum|eval|false|null|this|true|void|with|break|catch|class|const|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$)[$A-Z_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc][$A-Z_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc0-9\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19b0-\u19c0\u19c8\u19c9\u19d0-\u19d9\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1dc0-\u1de6\u1dfc-\u1dff\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f]*$/.test(name)
172
+ }
173
+
174
+ /**
175
+ * @param {Ast} node
176
+ * @param {boolean} forceUpdate
177
+ */
178
+ convLineno (node: Ast, forceUpdate = false): string {
179
+ if (this.speedMode.lineNumbers > 0) { return '' }
180
+
181
+ let lineNo: string
182
+ if (typeof node.line !== 'number') {
183
+ lineNo = 'unknown'
184
+ } else if (typeof node.file !== 'string') {
185
+ lineNo = `l${node.line}`
186
+ } else {
187
+ lineNo = `l${node.line}:${node.file}`
188
+ }
189
+
190
+ // 強制的に行番号をアップデートするか
191
+ if (!forceUpdate) {
192
+ if (lineNo === this.lastLineNo) { return '' }
193
+ this.lastLineNo = lineNo
194
+ }
195
+ // 例: __v0.line='l1:main.nako3'
196
+ return `__v0.line=${JSON.stringify(lineNo)};`
197
+ }
198
+
199
+ /**
200
+ * ローカル変数のJavaScriptコードを生成する。
201
+ * @param {string} name
202
+ */
203
+ varname (name: string): string {
204
+ if (this.varslistSet.length === 3) {
205
+ // グローバル
206
+ return `__varslist[${2}][${JSON.stringify(name)}]`
207
+ } else {
208
+ // 関数内
209
+ if (NakoGen.isValidIdentifier(name)) {
210
+ return name
211
+ } else {
212
+ return `__vars[${JSON.stringify(name)}]`
213
+ }
214
+ }
215
+ }
216
+
217
+ /**
218
+ * @param {string} name
219
+ * @returns {string}
220
+ */
221
+ static getFuncName (name: string): string {
222
+ if (name.indexOf('__') >= 0) { // スコープがある場合
223
+ const a = name.split('__')
224
+ const scope = a[0]
225
+ const name3 = NakoGen.getFuncName(a[1])
226
+ return `${scope}__${name3}`
227
+ }
228
+ let name2 = name.replace(/[ぁ-ん]+$/, '')
229
+ if (name2 === '') { name2 = name }
230
+ return name2
231
+ }
232
+
233
+ /** @param {Ast} node */
234
+ static convPrint (node: Ast): string {
235
+ return `__print(${node});`
236
+ }
237
+
238
+ /** @param {Ast} node */
239
+ convRequire (node: Ast): string {
240
+ const moduleName = node.value
241
+ return this.convLineno(node, false) +
242
+ `__module['${moduleName}'] = require('${moduleName}');\n`
243
+ }
244
+
245
+ /**
246
+ * プログラムの実行に必要な関数定義を書き出す(グローバル領域)
247
+ * convGenの結果を利用するため、convGenの後に呼び出すこと。
248
+ * @param com
249
+ * @param opt
250
+ */
251
+ getDefFuncCode (com: NakoCompiler, opt: NakoGenOptions): string {
252
+ let code = ''
253
+ // よく使う変数のショートカット
254
+ code += `const nakoVersion = { version: ${JSON.stringify(com.version)} }\n`
255
+ code += 'const __self = self;\n'
256
+ code += 'self.__self = self;\n'
257
+ code += 'const __varslist = self.__varslist;\n'
258
+ code += 'const __module = self.__module;\n'
259
+ code += 'const __v0 = self.__v0 = self.__varslist[0];\n'
260
+ code += 'const __v1 = self.__v1 = self.__varslist[1];\n'
261
+ code += 'const __vars = self.__vars = self.__varslist[2];\n'
262
+ // なでしこの関数定義を行う
263
+ let nakoFuncCode = ''
264
+ for (const key in this.nakoFuncList) {
265
+ const f = this.nakoFuncList[key].fn
266
+ const isAsync = this.nakoFuncList[key].asyncFn ? 'true' : 'false'
267
+ nakoFuncCode += '' +
268
+ `//[DEF_FUNC name='${key}' asyncFn=${isAsync}]\n` +
269
+ `self.__varslist[1]["${key}"]=${f};\n;` +
270
+ `//[/DEF_FUNC name='${key}']\n`
271
+ }
272
+ if (nakoFuncCode !== '') { code += '__v0.line=\'関数の定義\';\n' + nakoFuncCode }
273
+
274
+ // テストの定義を行う
275
+ if (opt.isTest) {
276
+ let testCode = 'const __tests = [];\n'
277
+
278
+ for (const key in this.nakoTestFuncs) {
279
+ const f = this.nakoTestFuncs[key].fn
280
+ testCode += `${f};\n;`
281
+ }
282
+
283
+ if (testCode !== '') {
284
+ code += '__v0.line=\'テストの定義\';\n'
285
+ code += testCode + '\n'
286
+ }
287
+ }
288
+
289
+ return code
290
+ }
291
+
292
+ /**
293
+ * プラグイン・オブジェクトを追加
294
+ * @param po プラグイン・オブジェクト
295
+ */
296
+ addPlugin (po: FuncList): void {
297
+ return this.__self.addPlugin(po)
298
+ }
299
+
300
+ /**
301
+ * プラグイン・オブジェクトを追加(ブラウザ向け)
302
+ * @param name オブジェクト名
303
+ * @param po 関数リスト
304
+ */
305
+ addPluginObject (name: string, po: FuncList): void {
306
+ this.__self.addPluginObject(name, po)
307
+ }
308
+
309
+ /**
310
+ * プラグイン・ファイルを追加(Node.js向け)
311
+ * @param objName オブジェクト名
312
+ * @param path ファイルパス
313
+ * @param po 登録するオブジェクト
314
+ */
315
+ addPluginFile (objName: string, path: string, po: FuncList): void {
316
+ this.__self.addPluginFile(objName, path, po)
317
+ }
318
+
319
+ /**
320
+ * 関数を追加する
321
+ * @param key 関数名
322
+ * @param josi 助詞
323
+ * @param fn 関数
324
+ */
325
+ addFunc (key: string, josi: FuncArgs, fn: any) {
326
+ this.__self.addFunc(key, josi, fn)
327
+ }
328
+
329
+ /**
330
+ * プラグイン関数を参照する
331
+ * @param key プラグイン関数の関数名
332
+ * @returns プラグイン・オブジェクト
333
+ */
334
+ getFunc (key: string) {
335
+ return this.__self.getFunc(key)
336
+ }
337
+
338
+ /**
339
+ * 関数を先に登録してしまう
340
+ */
341
+ registerFunction (ast: Ast) {
342
+ if (ast.type !== 'block') { throw NakoSyntaxError.fromNode('構文解析に失敗しています。構文は必ずblockが先頭になります', ast) }
343
+
344
+ /** 関数一覧 */
345
+ const funcList: {name: string; node:Ast}[] = []
346
+ // なでしこ関数を定義して this.nako_func[name] に定義する
347
+ const registFunc = (node: Ast) => {
348
+ if (!node.block) { return }
349
+ const blockList: Ast[] = (node.block instanceof Array) ? node.block : [node.block]
350
+ for (let i = 0; i < blockList.length; i++) {
351
+ const t = blockList[i]
352
+ if (t.type === 'def_func') {
353
+ if (!t.name) { throw new Error('[System Error] 関数の定義で関数名が指定されていない') }
354
+ const name: string = (t.name as Token).value
355
+ this.usedFuncSet.add(name)
356
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
357
+ this.__self.__varslist[1][name] = function () { } // 事前に適当な値を設定
358
+ this.varslistSet[1].names.add(name) // global
359
+ const meta = ((t.name) as Ast).meta // todo: 強制変換したが正しいかチェック
360
+ this.nakoFuncList[name] = {
361
+ josi: meta.josi,
362
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
363
+ fn: () => {},
364
+ type: 'func',
365
+ asyncFn: t.asyncFn
366
+ }
367
+ funcList.push({ name, node: t })
368
+ // eslint-disable-next-line brace-style
369
+ }
370
+ // 実行速度優先 などのオプションが付いている場合の処理
371
+ else if (t.type === 'speed_mode') {
372
+ if (!t.block) { continue }
373
+ if ((t.block as Ast).type === 'block') {
374
+ registFunc(t.block as Ast)
375
+ } else {
376
+ registFunc(t)
377
+ }
378
+ } else if (t.type === 'performance_monitor') {
379
+ if (!t.block) { continue }
380
+ if ((t.block as Ast).type === 'block') {
381
+ registFunc(t.block as Ast)
382
+ } else {
383
+ registFunc(t)
384
+ }
385
+ }
386
+ }
387
+ }
388
+ // 関数の登録
389
+ registFunc(ast)
390
+
391
+ // __self.__varslistの変更を反映
392
+ const initialNames: Set<string> = new Set()
393
+ if (this.speedMode.invalidSore === 0) {
394
+ initialNames.add('それ')
395
+ }
396
+ this.varsSet = { isFunction: false, names: initialNames, readonly: new Set() }
397
+ this.varslistSet = this.__self.__varslist.map((v) => ({ isFunction: false, names: new Set(Object.keys(v)), readonly: new Set() }))
398
+ this.varslistSet[2] = this.varsSet
399
+ }
400
+
401
+ /**
402
+ * @param node
403
+ * @param opt
404
+ */
405
+ convGen (node: Ast, opt: NakoGenOptions): string {
406
+ const result = this.convLineno(node, false) + this._convGen(node, true)
407
+ if (opt.isTest) {
408
+ return ''
409
+ } else {
410
+ return result
411
+ }
412
+ }
413
+
414
+ /**
415
+ * @param {Ast} node
416
+ * @param {boolean} isExpression
417
+ */
418
+ _convGen (node: Ast|null, isExpression: boolean): string {
419
+ if (!node) { return '' }
420
+ let code = ''
421
+ if (node instanceof Array) {
422
+ for (let i = 0; i < node.length; i++) {
423
+ const n = node[i]
424
+ code += this._convGen(n, isExpression)
425
+ }
426
+ return code
427
+ }
428
+ if (node === null) { return 'null' }
429
+ if (node === undefined) { return 'undefined' }
430
+ if (typeof (node) !== 'object') { return '' + node }
431
+ // switch
432
+ switch (node.type) {
433
+ case 'nop':
434
+ break
435
+ case 'block':
436
+ // eslint-disable-next-line no-case-declarations
437
+ const modName: string = NakoLexer.filenameToModName(node.file || '')
438
+ code += `;__self.__modName='${modName}';\n`
439
+ if (!node.block) { return code }
440
+ // eslint-disable-next-line no-case-declarations
441
+ const blocks: any = (node.block instanceof Array) ? node.block : [node.block]
442
+ for (let i = 0; i < blocks.length; i++) {
443
+ const b = blocks[i]
444
+ code += this._convGen(b, false)
445
+ }
446
+ break
447
+ case 'comment':
448
+ case 'eol':
449
+ code += this.convComment(node)
450
+ break
451
+ case 'break':
452
+ code += this.convCheckLoop(node, 'break')
453
+ break
454
+ case 'continue':
455
+ code += this.convCheckLoop(node, 'continue')
456
+ break
457
+ case 'end':
458
+ code += '__varslist[0][\'終\']();'
459
+ break
460
+ case 'number':
461
+ code += node.value
462
+ break
463
+ case 'string':
464
+ code += this.convString(node)
465
+ break
466
+ case 'def_local_var':
467
+ code += this.convDefLocalVar(node)
468
+ break
469
+ case 'def_local_varlist':
470
+ code += this.convDefLocalVarlist(node)
471
+ break
472
+ case 'let':
473
+ code += this.convLet(node)
474
+ break
475
+ case 'inc':
476
+ code += this.convInc(node)
477
+ break
478
+ case 'word':
479
+ case 'variable':
480
+ code += this.convGetVar(node)
481
+ break
482
+ case 'op':
483
+ case 'calc':
484
+ code += this.convOp(node)
485
+ break
486
+ case 'renbun':
487
+ code += this.convRenbun(node)
488
+ break
489
+ case 'not':
490
+ code += '((' + this._convGen(node.value as Ast, true) + ')?0:1)'
491
+ break
492
+ case 'func':
493
+ case 'func_pointer':
494
+ case 'calc_func':
495
+ code += this.convCallFunc(node, isExpression)
496
+ break
497
+ case 'if':
498
+ code += this.convIf(node)
499
+ break
500
+ case 'tikuji':
501
+ code += this.convTikuji(node)
502
+ break
503
+ case 'for':
504
+ code += this.convFor(node)
505
+ break
506
+ case 'foreach':
507
+ code += this.convForeach(node)
508
+ break
509
+ case 'repeat_times':
510
+ code += this.convRepeatTimes(node)
511
+ break
512
+ case 'speed_mode':
513
+ code += this.convSpeedMode(node, isExpression)
514
+ break
515
+ case 'performance_monitor':
516
+ code += this.convPerformanceMonitor(node, isExpression)
517
+ break
518
+ case 'while':
519
+ code += this.convWhile(node)
520
+ break
521
+ case 'atohantei':
522
+ code += this.convAtohantei(node)
523
+ break
524
+ case 'switch':
525
+ code += this.convSwitch(node)
526
+ break
527
+ case 'let_array':
528
+ code += this.convLetArray(node)
529
+ break
530
+ case '配列参照':
531
+ code += this.convRefArray(node)
532
+ break
533
+ case 'json_array':
534
+ code += this.convJsonArray(node)
535
+ break
536
+ case 'json_obj':
537
+ code += this.convJsonObj(node)
538
+ break
539
+ case 'func_obj':
540
+ code += this.convFuncObj(node)
541
+ break
542
+ case 'bool':
543
+ code += (node.value) ? 'true' : 'false'
544
+ break
545
+ case 'null':
546
+ code += 'null'
547
+ break
548
+ case 'def_test':
549
+ code += this.convDefTest(node)
550
+ break
551
+ case 'def_func':
552
+ code += this.convDefFunc(node)
553
+ break
554
+ case 'return':
555
+ code += this.convReturn(node)
556
+ break
557
+ case 'try_except':
558
+ code += this.convTryExcept(node)
559
+ break
560
+ case 'require':
561
+ code += this.convRequire(node)
562
+ break
563
+ default:
564
+ throw new Error('System Error: unknown_type=' + node.type)
565
+ }
566
+ return code
567
+ }
568
+
569
+ /** 変数を検索 */
570
+ findVar (name: string): FindVarResult|null {
571
+ // __vars ? (ローカル変数)
572
+ if (this.varslistSet.length > 3 && this.varsSet.names.has(name)) {
573
+ return { i: this.varslistSet.length - 1, name, isTop: true, js: this.varname(name) }
574
+ }
575
+ // __varslist ?
576
+ for (let i = 2; i >= 0; i--) {
577
+ if (this.varslistSet[i].names.has(name)) {
578
+ // ユーザーの定義したグローバル変数 (__varslist[2]) は、変数展開されている(そのままの名前で定義されている)可能性がある。
579
+ // それ以外の変数は、必ず__varslistに入っている。
580
+ return { i, name, isTop: false, js: `__varslist[${i}][${JSON.stringify(name)}]` }
581
+ }
582
+ }
583
+
584
+ return null
585
+ }
586
+
587
+ /**
588
+ * 定義済みの変数の参照
589
+ * @param {string} name
590
+ * @param {Ast} position
591
+ */
592
+ genVar (name: string, position: Ast): string {
593
+ const res = this.findVar(name)
594
+ const lno = position.line
595
+ if (res === null) {
596
+ // 定義されていない名前の参照は変数の定義とみなす。
597
+ // 多くの場合はundefined値を持つ変数であり分かりづらいバグを引き起こすが、
598
+ // 「ナデシコする」などの命令の中で定義された変数の参照の場合があるため警告に留める。
599
+ // ただし、自動的に定義される変数『引数』『それ』などは例外 #952
600
+ if (name === '引数' || name === 'それ' || name === '対象' || name === '対象キー') {
601
+ // デフォルト定義されている変数名
602
+ } else {
603
+ if (this.warnUndefinedVar) {
604
+ // main__は省略して表示するように。 #1223
605
+ const dispName = name.replace(/^main__(.+)$/, '$1')
606
+ this.__self.getLogger().warn(`変数『${dispName}』は定義されていません。`, position)
607
+ }
608
+ }
609
+ this.varsSet.names.add(name)
610
+ return this.varname(name)
611
+ }
612
+
613
+ const i = res.i
614
+ // システム関数・変数の場合
615
+ if (i === 0) {
616
+ const pv = this.__self.getNakoFunc(name)
617
+ if (!pv) { return `${res.js}/*err:${lno}*/` }
618
+ if (pv.type === 'const' || pv.type === 'var') { return res.js }
619
+ if (pv.type === 'func') {
620
+ if (!pv.josi || pv.josi.length === 0) { return `(${res.js}())` }
621
+
622
+ throw NakoSyntaxError.fromNode(`『${name}』が複文で使われました。単文で記述してください。(v1非互換)`, position)
623
+ }
624
+ throw NakoSyntaxError.fromNode(`『${name}』は関数であり参照できません。`, position)
625
+ }
626
+ return res.js
627
+ }
628
+
629
+ convGetVar (node: Ast): string {
630
+ const name = node.value
631
+ return this.genVar(name, node)
632
+ }
633
+
634
+ convComment (node:Ast): string {
635
+ let commentSrc = String(node.value)
636
+ commentSrc = commentSrc.replace(/\n/g, '¶')
637
+ const lineNo = this.convLineno(node, false)
638
+ if (commentSrc === '' && lineNo === '') { return ';' }
639
+ if (commentSrc === '') {
640
+ return ';' + lineNo + '\n'
641
+ }
642
+ return ';' + lineNo + '//' + commentSrc + '\n'
643
+ }
644
+
645
+ convReturn (node: Ast): string {
646
+ // 関数の中であれば利用可能
647
+ if (this.varsSet.names.has('!関数')) { throw NakoSyntaxError.fromNode('『戻る』がありますが、関数定義内のみで使用可能です。', node) }
648
+
649
+ const lno = this.convLineno(node, false)
650
+ let value
651
+ if (node.value) {
652
+ value = this._convGen(node.value, true)
653
+ } else
654
+ if (this.speedMode.invalidSore === 0) {
655
+ value = this.varname('それ')
656
+ } else {
657
+ return lno + 'return;'
658
+ }
659
+ if (this.warnUndefinedReturnUserFunc === 0) {
660
+ return lno + `return ${value};`
661
+ } else {
662
+ return lno + `return (function(a){if(a===undefined){__self.logger.warn('ユーザ関数からundefinedが返されています',{file:'${node.file}',line:${node.line}});};return a;})(${value});`
663
+ }
664
+ }
665
+
666
+ convCheckLoop (node: Ast, cmd: string): string {
667
+ // ループの中であれば利用可能
668
+ if (!this.flagLoop) {
669
+ const cmdj = (cmd === 'continue') ? '続ける' : '抜ける'
670
+ throw NakoSyntaxError.fromNode(`『${cmdj}』文がありますが、それは繰り返しの中で利用してください。`, node)
671
+ }
672
+ return this.convLineno(node) + cmd + ';'
673
+ }
674
+
675
+ convDefFuncCommon (node: Ast, name: string): string {
676
+ // パフォーマンスモニタ:ユーザ関数のinjectの定義
677
+ let performanceMonitorInjectAtStart = ''
678
+ let performanceMonitorInjectAtEnd = ''
679
+ if (this.performanceMonitor.userFunction !== 0) {
680
+ let key = name
681
+ if (!key) {
682
+ if (typeof this.performanceMonitor.mumeiId === 'undefined') {
683
+ this.performanceMonitor.mumeiId = 0
684
+ }
685
+ this.performanceMonitor.mumeiId++
686
+ key = `anous_${this.performanceMonitor.mumeiId}`
687
+ }
688
+ performanceMonitorInjectAtStart = 'const performanceMonitorEnd = (function (key, type) {\n' +
689
+ 'const uf_start = performance.now() * 1000;\n' +
690
+ 'return function () {\n' +
691
+ 'const el_time = performance.now() * 1000 - uf_start;\n' +
692
+ 'if (!__self.__performance_monitor) {\n' +
693
+ '__self.__performance_monitor={};\n' +
694
+ '__self.__performance_monitor[key] = { called:1, totel_usec: el_time, min_usec: el_time, max_usec: el_time, type: type };\n' +
695
+ '} else if (!__self.__performance_monitor[key]) {\n' +
696
+ '__self.__performance_monitor[key] = { called:1, totel_usec: el_time, min_usec: el_time, max_usec: el_time, type: type };\n' +
697
+ '} else {\n' +
698
+ '__self.__performance_monitor[key].called++;\n' +
699
+ '__self.__performance_monitor[key].totel_usec+=el_time;\n' +
700
+ 'if(__self.__performance_monitor[key].min_usec>el_time){__self.__performance_monitor[key].min_usec=el_time;}\n' +
701
+ 'if(__self.__performance_monitor[key].max_usec<el_time){__self.__performance_monitor[key].max_usec=el_time;}\n' +
702
+ `}};})('${key}', 'user');` +
703
+ 'try {\n'
704
+ performanceMonitorInjectAtEnd = '} finally { performanceMonitorEnd(); }\n'
705
+ }
706
+ let variableDeclarations = ''
707
+ const popStack = ''
708
+ const initialNames: Set<string> = new Set()
709
+ if (this.speedMode.invalidSore === 0) {
710
+ initialNames.add('それ')
711
+ }
712
+ this.varsSet = { isFunction: true, names: initialNames, readonly: new Set() }
713
+ // ローカル変数をPUSHする
714
+ this.varslistSet.push(this.varsSet)
715
+ // JSの引数と引数をバインド
716
+ variableDeclarations += ' var 引数 = arguments;\n'
717
+ // ローカル変数を生成
718
+ variableDeclarations += ' var __vars = {};\n'
719
+ // 宣言済みの名前を保存
720
+ const varsDeclared = Array.from(this.varsSet.names.values())
721
+ let code = ''
722
+ // 引数をローカル変数に設定
723
+ const meta = (!name) ? node.meta : (node.name as Ast).meta
724
+ for (let i = 0; i < meta.varnames.length; i++) {
725
+ const word = meta.varnames[i]
726
+ if (this.warnUndefinedCalledUserFuncArgs === 0) {
727
+ code += ` ${this.varname(word)} = arguments[${i}];\n`
728
+ } else
729
+ if (name) {
730
+ code += ` ${this.varname(word)} = (function(a){if(a===undefined){__self.logger.warn('ユーザ関数(${name})の引数(${this.varname(word)})にundefinedが渡されました',{file:'${node.file}',line:${node.line}});};return a;})(arguments[${i}]);\n`
731
+ } else {
732
+ code += ` ${this.varname(word)} = (function(a){if(a===undefined){__self.logger.warn('匿名関数の引数(${this.varname(word)})にundefinedが渡されました',{file:'${node.file}',line:${node.line}});};return a;})(arguments[${i}]);\n`
733
+ }
734
+ this.varsSet.names.add(word)
735
+ }
736
+ // 関数定義は、グローバル領域で。
737
+ if (name) {
738
+ this.usedFuncSet.add(name)
739
+ this.varslistSet[1].names.add(name)
740
+ if (this.nakoFuncList[name] === undefined) {
741
+ // 既に generate で作成済みのはず(念のため)
742
+ this.nakoFuncList[name] = {
743
+ josi: (node.name as Ast).meta.josi,
744
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
745
+ fn: () => {},
746
+ type: 'func',
747
+ asyncFn: false
748
+ }
749
+ }
750
+ }
751
+ // ブロックを解析
752
+ const oldUsedAsyncFn = this.usedAsyncFn
753
+ this.usedAsyncFn = false
754
+ const block = this._convGen(node.block as Ast, false)
755
+ code += block.split('\n').map((line) => ' ' + line).join('\n') + '\n'
756
+ // 関数の最後に、変数「それ」をreturnするようにする
757
+ if (this.speedMode.invalidSore === 0) {
758
+ code += ` return (${this.varname('それ')});\n`
759
+ }
760
+ // パフォーマンスモニタ:ユーザ関数のinject
761
+ code += performanceMonitorInjectAtEnd
762
+ // ブロックでasyncFnを使ったか
763
+ if (name && this.usedAsyncFn) {
764
+ this.nakoFuncList[name].asyncFn = true
765
+ }
766
+ // 関数の末尾に、ローカル変数をPOP
767
+
768
+ // 関数内で定義されたローカル変数の宣言
769
+ for (const name of Array.from(this.varsSet.names.values())) {
770
+ if (!varsDeclared.includes(name)) {
771
+ if (NakoGen.isValidIdentifier(name)) {
772
+ variableDeclarations += ` var ${name};\n`
773
+ }
774
+ }
775
+ }
776
+ if (this.speedMode.invalidSore === 0) {
777
+ if (NakoGen.isValidIdentifier('それ')) {
778
+ variableDeclarations += ' var それ = \'\';\n'
779
+ } else {
780
+ variableDeclarations += ` ${this.varname('それ')} = '';`
781
+ }
782
+ }
783
+ // usedAsyncFnの値に応じて関数定義の方法を変更
784
+ const tof = (this.usedAsyncFn) ? topOfFunctionAsync : topOfFunction
785
+ // 関数コード全体を構築
786
+ code = tof + performanceMonitorInjectAtStart + variableDeclarations + code + popStack
787
+ code += endOfFunction
788
+
789
+ // 名前があれば、関数を登録する
790
+ if (name) {
791
+ this.nakoFuncList[name].fn = code
792
+ this.nakoFuncList[name].asyncFn = this.usedAsyncFn
793
+ meta.asyncFn = this.usedAsyncFn
794
+ }
795
+ this.usedAsyncFn = oldUsedAsyncFn // 以前の値を戻す
796
+
797
+ this.varslistSet.pop()
798
+ this.varsSet = this.varslistSet[this.varslistSet.length - 1]
799
+ if (name) { this.__self.__varslist[1][name] = code }
800
+ return code
801
+ }
802
+
803
+ convDefTest (node: Ast): string {
804
+ const name = (node.name as Ast).value
805
+ let code = `__tests.push({ name: '${name}', f: () => {\n`
806
+
807
+ // ブロックを解析
808
+ const block = this._convGen(node.block as Ast, false)
809
+
810
+ code += ` ${block}\n` +
811
+ '}});'
812
+
813
+ this.nakoTestFuncs[name] = {
814
+ josi: (node.name as Ast).meta.josi,
815
+ fn: code,
816
+ type: 'test_func'
817
+ }
818
+
819
+ // ★この時点ではテストコードを生成しない★
820
+ // プログラム冒頭でコード生成時にテストの定義を行う
821
+ return ''
822
+ }
823
+
824
+ convDefFunc (node: Ast): string {
825
+ // ※ [関数定義のメモ]
826
+ // ※ 関数の定義はプログラムの冒頭に移される。
827
+ // ※ そのため、生成されたコードはここでは返さない
828
+ // ※ registerFunction を参照
829
+ if (!node.name) { return '' }
830
+ const name = NakoGen.getFuncName((node.name as Ast).value as string)
831
+ this.convDefFuncCommon(node, name)
832
+ return ''
833
+ }
834
+
835
+ convFuncObj (node: Ast): string {
836
+ return this.convDefFuncCommon(node, '')
837
+ }
838
+
839
+ convJsonObj (node: Ast): string {
840
+ const list = node.value
841
+ const codelist = list.map((e: {key: Ast, value: Ast}) => {
842
+ const key = this._convGen(e.key, true)
843
+ const val = this._convGen(e.value, true)
844
+ return `${key}:${val}`
845
+ })
846
+ return '{' + codelist.join(',') + '}'
847
+ }
848
+
849
+ convJsonArray (node: Ast): string {
850
+ const list = node.value
851
+ const codelist = list.map((e: Ast) => {
852
+ return this._convGen(e, true)
853
+ })
854
+ return '[' + codelist.join(',') + ']'
855
+ }
856
+
857
+ convRefArray (node: Ast): string {
858
+ const name = this._convGen(node.name as Ast, true)
859
+ const list: Ast[] | undefined = node.index
860
+ let code = name
861
+ if (!list) { return code }
862
+ for (let i = 0; i < list.length; i++) {
863
+ const idx = this._convGen(list[i] as Ast, true)
864
+ code += '[' + idx + ']'
865
+ }
866
+ return code
867
+ }
868
+
869
+ convLetArray (node: Ast): string {
870
+ const name = this._convGen(node.name as Ast, true)
871
+ const list: Ast[] = node.index || []
872
+ let codeInit = ''
873
+ let code = name
874
+ let codeArray = ''
875
+ // codeInit?
876
+ if (node.checkInit) {
877
+ const arrayDefCode = '[0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0]'
878
+ codeInit += `\n/*配列初期化*/if (!(${name} instanceof Array)) { ${name} = ${arrayDefCode}; console.log('初期化:${name}') };`
879
+ for (let i = 0; i < list.length - 1; i++) {
880
+ const idx = this._convGen(list[i], true)
881
+ codeArray += `[${idx}]`
882
+ codeInit += `\n/*配列初期化${i}*/if (!(${name}${codeArray} instanceof Array)) { ${name}${codeArray} = ${arrayDefCode}; };`
883
+ // codeInit += `\n/*配列初期化${i}*/if (!(${name}${codeArray} instanceof Array)) { ${name}${codeArray} = ${arrayDefCode}; console.log('初期化:${i}:${name}${codeArray}',JSON.stringify(${name})) }; `
884
+ }
885
+ codeInit += '\n'
886
+ }
887
+ // array
888
+ for (let i = 0; i < list.length; i++) {
889
+ const idx = this._convGen(list[i], true)
890
+ code += '[' + idx + ']'
891
+ }
892
+ const value = this._convGen(node.value, true)
893
+ code += ' = ' + value + ';\n'
894
+ // generate code
895
+ const src = this.convLineno(node, false) + codeInit + code
896
+ return src
897
+ }
898
+
899
+ convGenLoop (node: Ast): string {
900
+ const tmpflag = this.flagLoop
901
+ this.flagLoop = true
902
+ try {
903
+ return this._convGen(node, false)
904
+ } finally {
905
+ this.flagLoop = tmpflag
906
+ }
907
+ }
908
+
909
+ convFor (node: Ast): string {
910
+ // ループ変数について
911
+ let word
912
+ if (node.word !== null) { // ループ変数を使う時
913
+ const varName = (node.word as Token).value // todo: Forの最初のパラメータが Token か Astか確認
914
+ this.varsSet.names.add(varName)
915
+ word = this.varname(varName)
916
+ } else {
917
+ this.varsSet.names.add('dummy')
918
+ word = this.varname('dummy')
919
+ }
920
+ const idLoop = this.loopId++
921
+ const varI = `$nako_i${idLoop}`
922
+ // ループ条件を確認
923
+ const kara = this._convGen(node.from as Ast, true)
924
+ const made = this._convGen(node.to as Ast, true)
925
+ let inc = '1'
926
+ if (node.inc !== null || node.inc === undefined || node.inc === 'null') {
927
+ inc = this._convGen(node.inc as Ast, true)
928
+ }
929
+ // ループ内のブロック内容を得る
930
+ const block = this.convGenLoop(node.block as Ast)
931
+ // ループ条件を変数に入れる用
932
+ const varFrom = `$nako_from${idLoop}`
933
+ const varTo = `$nako_to${idLoop}`
934
+ let sorePrefex = ''
935
+ if (this.speedMode.invalidSore === 0) {
936
+ sorePrefex = `${this.varname('それ')} = `
937
+ }
938
+ const code =
939
+ `\n//[FOR id=${idLoop}]\n` +
940
+ `const ${varFrom} = ${kara};\n` +
941
+ `const ${varTo} = ${made};\n` +
942
+ `if (${varFrom} <= ${varTo}) { // up\n` +
943
+ ` for (let ${varI} = ${varFrom}; ${varI} <= ${varTo}; ${varI}+= ${inc}) {\n` +
944
+ ` ${sorePrefex}${word} = ${varI};\n` +
945
+ ` ${block}\n` +
946
+ ' };\n' +
947
+ '} else { // down\n' +
948
+ ` for (let ${varI} = ${varFrom}; ${varI} >= ${varTo}; ${varI}-= ${inc}) {\n` +
949
+ ` ${sorePrefex}${word} = ${varI};` + '\n' +
950
+ ` ${block}\n` +
951
+ ' };\n' +
952
+ `};\n//[/FOR id=${idLoop}]\n`
953
+ return this.convLineno(node, false) + code
954
+ }
955
+
956
+ convForeach (node: Ast): string {
957
+ let target
958
+ if (node.target === null) {
959
+ if (this.speedMode.invalidSore === 0) {
960
+ target = this.varname('それ')
961
+ } else {
962
+ throw NakoSyntaxError.fromNode('『反復』の対象がありません。', node)
963
+ }
964
+ } else { target = this._convGen(node.target as Ast, true) }
965
+
966
+ // blockより早く変数を定義する必要がある
967
+ let nameS = '__v0["対象"]'
968
+ if (node.name) {
969
+ nameS = this.varname((node.name as Ast).value)
970
+ this.varsSet.names.add((node.name as Ast).value)
971
+ }
972
+
973
+ const block = this.convGenLoop(node.block as Ast)
974
+ const id = this.loopId++
975
+ const key = '__v0["対象キー"]'
976
+ let sorePrefex = ''
977
+ if (this.speedMode.invalidSore === 0) {
978
+ sorePrefex = `${this.varname('それ')} = `
979
+ }
980
+ const code =
981
+ `let $nako_foreach_v${id}=${target};\n` +
982
+ `for (let $nako_i${id} in $nako_foreach_v${id})` + '{\n' +
983
+ ` if ($nako_foreach_v${id}.hasOwnProperty($nako_i${id})) {\n` +
984
+ ` ${nameS} = ${sorePrefex}$nako_foreach_v${id}[$nako_i${id}];` + '\n' +
985
+ ` ${key} = $nako_i${id};\n` +
986
+ ` ${block}\n` +
987
+ ' }\n' +
988
+ '};\n'
989
+ return this.convLineno(node, false) + code
990
+ }
991
+
992
+ convRepeatTimes (node: Ast): string {
993
+ const id = this.loopId++
994
+ const value = this._convGen(node.value, true)
995
+ const block = this.convGenLoop(node.block as Ast)
996
+ const kaisu = '__v0["回数"]'
997
+ let sorePrefex = ''
998
+ if (this.speedMode.invalidSore === 0) {
999
+ sorePrefex = `${this.varname('それ')} = `
1000
+ }
1001
+ const code =
1002
+ `let $nako_times_v${id} = ${value};\n` +
1003
+ `for(var $nako_i${id} = 1; $nako_i${id} <= $nako_times_v${id}; $nako_i${id}++)` + '{\n' +
1004
+ ` ${sorePrefex}${kaisu} = $nako_i${id};` + '\n' +
1005
+ ' ' + block + '\n}\n'
1006
+ return this.convLineno(node, false) + code
1007
+ }
1008
+
1009
+ /**
1010
+ * @param {Ast} node
1011
+ * @param {boolean} isExpression
1012
+ */
1013
+ convSpeedMode (node: Ast, isExpression: boolean): string {
1014
+ if (!node.options) { return '' }
1015
+ const prev = { ...this.speedMode }
1016
+ if (node.options['行番号無し']) {
1017
+ this.speedMode.lineNumbers++
1018
+ }
1019
+ if (node.options['暗黙の型変換無し']) {
1020
+ this.speedMode.implicitTypeCasting++
1021
+ }
1022
+ if (node.options['強制ピュア']) {
1023
+ this.speedMode.forcePure++
1024
+ }
1025
+ if (node.options['それ無効']) {
1026
+ this.speedMode.invalidSore++
1027
+ }
1028
+ try {
1029
+ return this._convGen(node.block as Ast, isExpression)
1030
+ } finally {
1031
+ this.speedMode = prev
1032
+ }
1033
+ }
1034
+
1035
+ /**
1036
+ * @param {Ast} node
1037
+ * @param {boolean} isExpression
1038
+ */
1039
+ convPerformanceMonitor (node: Ast, isExpression: boolean): string {
1040
+ const prev = { ...this.performanceMonitor }
1041
+ if (!node.options) { return '' }
1042
+ if (node.options['ユーザ関数']) {
1043
+ this.performanceMonitor.userFunction++
1044
+ }
1045
+ if (node.options['システム関数本体']) {
1046
+ this.performanceMonitor.systemFunctionBody++
1047
+ }
1048
+ if (node.options['システム関数']) {
1049
+ this.performanceMonitor.systemFunction++
1050
+ }
1051
+ try {
1052
+ return this._convGen(node.block as Ast, isExpression)
1053
+ } finally {
1054
+ this.performanceMonitor = prev
1055
+ }
1056
+ }
1057
+
1058
+ convWhile (node: Ast): string {
1059
+ const cond = this._convGen(node.cond as Ast, true)
1060
+ const block = this.convGenLoop(node.block as Ast)
1061
+ const code =
1062
+ `while (${cond})` + '{\n' +
1063
+ ` ${block}` + '\n' +
1064
+ '}\n'
1065
+ return this.convLineno(node, false) + code
1066
+ }
1067
+
1068
+ convAtohantei (node: Ast): string {
1069
+ const id = this.loopId++
1070
+ const varId = `$nako_i${id}`
1071
+ const cond = this._convGen(node.cond as Ast, true)
1072
+ const block = this.convGenLoop(node.block as Ast)
1073
+ const code =
1074
+ 'for(;;) {\n' +
1075
+ ` ${block}\n` +
1076
+ ` let ${varId} = ${cond};\n` +
1077
+ ` if (${varId}) { continue } else { break }\n` +
1078
+ '}\n\n'
1079
+ return this.convLineno(node, false) + code
1080
+ }
1081
+
1082
+ convSwitch (node: Ast): string {
1083
+ const value: string = this._convGen(node.value, true)
1084
+ const cases: any[] = node.cases || []
1085
+ let body = ''
1086
+ for (let i = 0; i < cases.length; i++) {
1087
+ const cvalue = cases[i][0]
1088
+ const cblock = this.convGenLoop(cases[i][1])
1089
+ if (cvalue.type === '違えば') {
1090
+ body += ' default:\n'
1091
+ } else {
1092
+ const cvalueCode = this._convGen(cvalue, true)
1093
+ body += ` case ${cvalueCode}:\n`
1094
+ }
1095
+ body += ` ${cblock}\n` +
1096
+ ' break\n'
1097
+ }
1098
+ const code =
1099
+ `switch (${value})` + '{\n' +
1100
+ `${body}` + '\n' +
1101
+ '}\n'
1102
+ return this.convLineno(node, false) + code
1103
+ }
1104
+
1105
+ convIf (node: Ast): string {
1106
+ const expr = this._convGen(node.expr as Ast, true)
1107
+ const block = this._convGen(node.block as Ast, false)
1108
+ const falseBlock = (node.false_block === null)
1109
+ ? ''
1110
+ : 'else {' + this._convGen(node.false_block as Ast, false) + '};\n'
1111
+ return this.convLineno(node, false) +
1112
+ `if (${expr}) {\n ${block}\n}` + falseBlock + ';\n'
1113
+ }
1114
+
1115
+ convTikuji (node: Ast): string {
1116
+ const pid = this.loopId++
1117
+ // gen tikuji blocks
1118
+ const curName = `__tikuji${pid}`
1119
+ let code = `const ${curName} = []\n`
1120
+ const blocks: Ast[] = (node.blocks) ? node.blocks : []
1121
+ for (let i = 0; i < blocks.length; i++) {
1122
+ const block = this._convGen(blocks[i], false).replace(/\s+$/, '') + '\n'
1123
+ const blockLineNo = this.convLineno(blocks[i], true)
1124
+ const blockCode =
1125
+ `${curName}.push(function(resolve, reject) {\n` +
1126
+ ' __self.resolve = resolve;\n' +
1127
+ ' __self.reject = reject;\n' +
1128
+ ' __self.resolveCount = 0;\n' +
1129
+ ` ${blockLineNo}\n` +
1130
+ ` ${block}` +
1131
+ ' if (__self.resolveCount === 0) resolve();\n' +
1132
+ // eslint-disable-next-line no-template-curly-in-string
1133
+ '}); // end of tikuji__${pid}[{$i}]\n'
1134
+ code += blockCode
1135
+ }
1136
+ code += `// end of ${curName} \n`
1137
+ // gen error block
1138
+ let errorCode =
1139
+ ` ${curName}.splice(0);\n` + // clear
1140
+ ' __v0["エラーメッセージ"]=errMsg;\n'
1141
+ if (node.errorBlock != null) {
1142
+ const errBlock = this._convGen(node.errorBlock as Ast, false).replace(/\s+$/, '') + '\n'
1143
+ errorCode += errBlock
1144
+ }
1145
+ code += `const ${curName}__reject = function(errMsg){\n${errorCode}};\n`
1146
+ // gen run block
1147
+ code += '__self.resolve = undefined;\n'
1148
+ code += `const ${curName}__resolve = function(){\n`
1149
+ code += ' setTimeout(function(){\n'
1150
+ code += ` if (${curName}.length == 0) {return}\n`
1151
+ code += ` const f = ${curName}.shift()\n`
1152
+ code += ` f(${curName}__resolve, ${curName}__reject);\n`
1153
+ code += ' }, 0);\n'
1154
+ code += '};\n'
1155
+ code += `${curName}__resolve()\n`
1156
+ return this.convLineno(node, false) + code
1157
+ }
1158
+
1159
+ convFuncGetArgsCalcType (_funcName: string, _func: any, node: Ast): [any, any] {
1160
+ const args = []
1161
+ const opts: {[key: string]: boolean} = {}
1162
+ const nodeArgs = (node.args) ? node.args : []
1163
+ for (let i = 0; i < nodeArgs.length; i++) {
1164
+ const arg = nodeArgs[i]
1165
+ if (i === 0 && arg === null && this.speedMode.invalidSore === 0) {
1166
+ args.push(this.varname('それ'))
1167
+ opts.sore = true
1168
+ } else {
1169
+ args.push(this._convGen(arg, true))
1170
+ }
1171
+ }
1172
+ return [args, opts]
1173
+ }
1174
+
1175
+ getPluginList () {
1176
+ const r = []
1177
+ for (const name in this.__self.__module) { r.push(name) }
1178
+ return r
1179
+ }
1180
+
1181
+ /**
1182
+ * 関数の呼び出し
1183
+ * @param {Ast} node
1184
+ * @param {boolean} isExpression
1185
+ * @returns string コード
1186
+ */
1187
+ convCallFunc (node: Ast, isExpression: boolean): string {
1188
+ const funcName = NakoGen.getFuncName(node.name as string)
1189
+ const res = this.findVar(funcName)
1190
+ if (res === null) {
1191
+ throw NakoSyntaxError.fromNode(`関数『${funcName}』が見当たりません。有効プラグイン=[` + this.getPluginList().join(', ') + ']', node)
1192
+ }
1193
+ // どの関数を呼び出すのか関数を特定する
1194
+ let func
1195
+ if (res.i === 0) { // plugin function
1196
+ func = this.__self.getFunc(funcName)
1197
+ if (!func) { throw new Error(`[System Error] 関数「${funcName}」NakoCompiler.nakoFuncList の不整合があります。`) }
1198
+ if (func.type !== 'func') {
1199
+ throw NakoSyntaxError.fromNode(`『${funcName}』は関数ではありません。`, node)
1200
+ }
1201
+ } else {
1202
+ func = this.nakoFuncList[funcName]
1203
+ // 無名関数の可能性
1204
+ if (func === undefined) { func = { return_none: false } }
1205
+ }
1206
+ // 関数の参照渡しか?
1207
+ if (node.type === 'func_pointer') {
1208
+ return res.js
1209
+ }
1210
+
1211
+ // 関数の参照渡しでない場合
1212
+ // 関数定義より助詞を一つずつ調べる
1213
+ const argsInfo = this.convFuncGetArgsCalcType(funcName, func, node)
1214
+ const args = argsInfo[0]
1215
+ const argsOpts = argsInfo[1]
1216
+ // function
1217
+ this.usedFuncSet.add(funcName)
1218
+
1219
+ // 関数呼び出しで、引数の末尾にthisを追加する-システム情報を参照するため
1220
+ args.push('__self')
1221
+ let funcDef = 'function'
1222
+ let funcBegin = ''
1223
+ let funcEnd = ''
1224
+ // setter?
1225
+ if (node.setter) {
1226
+ funcBegin += ';__self.isSetter = true;\n'
1227
+ funcEnd += ';__self.isSetter = false;\n'
1228
+ }
1229
+ // 関数内 (__varslist.length > 3) からプラグイン関数 (res.i === 0) を呼び出すとき、 そのプラグイン関数がpureでなければ
1230
+ // 呼び出しの直前に全てのローカル変数をthis.__localsに入れる。
1231
+ if (res.i === 0 && this.varslistSet.length > 3 && func.pure !== true && this.speedMode.forcePure === 0) { // undefinedはfalseとみなす
1232
+ // 展開されたローカル変数の列挙
1233
+ const localVars = []
1234
+ for (const name of Array.from(this.varsSet.names.values())) {
1235
+ if (NakoGen.isValidIdentifier(name)) {
1236
+ localVars.push({ str: JSON.stringify(name), js: this.varname(name) })
1237
+ }
1238
+ }
1239
+
1240
+ // --- 実行前 ---
1241
+
1242
+ // 全ての展開されていないローカル変数を __self.__locals にコピーする
1243
+ funcBegin += '__self.__locals = __vars;\n'
1244
+
1245
+ // 全ての展開されたローカル変数を __self.__locals に保存する
1246
+ for (const v of localVars) {
1247
+ funcBegin += `__self.__locals[${v.str}] = ${v.js};\n`
1248
+ }
1249
+
1250
+ // --- 実行後 ---
1251
+
1252
+ // 全ての展開されたローカル変数を __self.__locals から受け取る
1253
+ // 「それ」は関数の実行結果を受け取るために使うためスキップ。
1254
+ for (const v of localVars) {
1255
+ if (v.js !== 'それ') {
1256
+ funcEnd += `${v.js} = __self.__locals[${v.str}];\n`
1257
+ }
1258
+ }
1259
+ }
1260
+ // 変数「それ」が補完されていることをヒントとして出力
1261
+ if (argsOpts.sore) { funcBegin += '/*[sore]*/' }
1262
+
1263
+ const indent = (text: string, n: number) => {
1264
+ let result = ''
1265
+ for (const line of text.split('\n')) {
1266
+ if (line !== '') {
1267
+ result += ' '.repeat(n) + line + '\n'
1268
+ }
1269
+ }
1270
+ return result
1271
+ }
1272
+
1273
+ // 関数呼び出しコードの構築
1274
+ let argsCode: string
1275
+ if ((this.warnUndefinedCallingUserFunc === 0 && res.i !== 0) || (this.warnUndefinedCallingSystemFunc === 0 && res.i === 0)) {
1276
+ argsCode = args.join(',')
1277
+ } else {
1278
+ argsCode = ''
1279
+ args.forEach((arg: string) => {
1280
+ if (arg === '__self') {
1281
+ argsCode += `,${arg}`
1282
+ } else {
1283
+ if (res.i === 0) {
1284
+ argsCode += `,(function(a){if(a===undefined){__self.logger.warn('命令『${funcName}』の引数にundefinedを渡しています。',{file:'${node.file}',line:${node.line}});};return a;})(${arg})`
1285
+ } else {
1286
+ argsCode += `,(function(a){if(a===undefined){__self.logger.warn('ユーザ関数『${funcName}』の引数にundefinedを渡しています。',{file:'${node.file}',line:${node.line}});};return a;})(${arg})`
1287
+ }
1288
+ }
1289
+ })
1290
+ argsCode = argsCode.substring(1)
1291
+ }
1292
+
1293
+ let funcCall = `${res.js}(${argsCode})`
1294
+ if (func.asyncFn) {
1295
+ funcDef = `async ${funcDef}`
1296
+ funcCall = `await ${funcCall}`
1297
+ this.numAsyncFn++
1298
+ this.usedAsyncFn = true
1299
+ }
1300
+ if (res.i === 0 && this.performanceMonitor.systemFunctionBody !== 0) {
1301
+ let key = funcName
1302
+ if (!key) {
1303
+ if (typeof this.performanceMonitor.mumeiId === 'undefined') {
1304
+ this.performanceMonitor.mumeiId = 0
1305
+ }
1306
+ this.performanceMonitor.mumeiId++
1307
+ key = `anous_${this.performanceMonitor.mumeiId}`
1308
+ }
1309
+ funcCall = `(${funcDef} (key, type) {\n` +
1310
+ 'const sbf_start = performance.now() * 1000;\n' +
1311
+ 'try {\n' +
1312
+ 'return ' + funcCall + ';\n' +
1313
+ '} finally {\n' +
1314
+ 'const sbl_time = performance.now() * 1000 - sbf_start;\n' +
1315
+ 'if (!__self.__performance_monitor) {\n' +
1316
+ '__self.__performance_monitor={};\n' +
1317
+ '__self.__performance_monitor[key] = { called:1, totel_usec: sbl_time, min_usec: sbl_time, max_usec: sbl_time, type: type };\n' +
1318
+ '} else if (!__self.__performance_monitor[key]) {\n' +
1319
+ '__self.__performance_monitor[key] = { called:1, totel_usec: sbl_time, min_usec: sbl_time, max_usec: sbl_time, type: type };\n' +
1320
+ '} else {\n' +
1321
+ '__self.__performance_monitor[key].called++;\n' +
1322
+ '__self.__performance_monitor[key].totel_usec+=sbl_time;\n' +
1323
+ 'if(__self.__performance_monitor[key].min_usec>sbl_time){__self.__performance_monitor[key].min_usec=sbl_time;}\n' +
1324
+ 'if(__self.__performance_monitor[key].max_usec<sbl_time){__self.__performance_monitor[key].max_usec=sbl_time;}\n' +
1325
+ `}}})('${funcName}_body', 'sysbody')\n`
1326
+ }
1327
+
1328
+ let code = ''
1329
+ if (func.return_none) {
1330
+ // 戻り値のない関数の場合
1331
+ if (funcEnd === '') {
1332
+ if (funcBegin === '') {
1333
+ code = `${funcCall};\n`
1334
+ } else {
1335
+ code = `${funcBegin} ${funcCall};\n`
1336
+ }
1337
+ } else {
1338
+ code = `${funcBegin}try {\n${indent(funcCall, 1)};\n} finally {\n${indent(funcEnd, 1)}}\n`
1339
+ }
1340
+ } else {
1341
+ // 戻り値のある関数の場合
1342
+ let sorePrefex = ''
1343
+ if (this.speedMode.invalidSore === 0) {
1344
+ sorePrefex = `${this.varname('それ')} = `
1345
+ }
1346
+ if (funcBegin === '' && funcEnd === '') {
1347
+ code = `(${sorePrefex}${funcCall})`
1348
+ } else {
1349
+ if (funcEnd === '') {
1350
+ code = `(${funcDef}(){\n${indent(`${funcBegin};\nreturn ${sorePrefex} ${funcCall}`, 1)}}).call(this)`
1351
+ } else {
1352
+ code = `(${funcDef}(){\n${indent(`${funcBegin}try {\n${indent(`return ${sorePrefex}${funcCall};`, 1)}\n} finally {\n${indent(funcEnd, 1)}}`, 1)}}).call(this)`
1353
+ }
1354
+ }
1355
+ // ...して
1356
+ if (node.josi === 'して' || (node.josi === '' && !isExpression)) { code += ';\n' }
1357
+ }
1358
+
1359
+ if (res.i === 0 && this.performanceMonitor.systemFunction !== 0) {
1360
+ code = '(function (key, type) {\n' +
1361
+ 'const sf_start = performance.now() * 1000;\n' +
1362
+ 'try {\n' +
1363
+ 'return ' + code + ';\n' +
1364
+ '} finally {\n' +
1365
+ 'const sl_time = performance.now() * 1000 - sf_start;\n' +
1366
+ 'if (!__self.__performance_monitor) {\n' +
1367
+ '__self.__performance_monitor={};\n' +
1368
+ '__self.__performance_monitor[key] = { called:1, totel_usec: sl_time, min_usec: sl_time, max_usec: sl_time, type: type };\n' +
1369
+ '} else if (!__self.__performance_monitor[key]) {\n' +
1370
+ '__self.__performance_monitor[key] = { called:1, totel_usec: sl_time, min_usec: sl_time, max_usec: sl_time, type: type };\n' +
1371
+ '} else {\n' +
1372
+ '__self.__performance_monitor[key].called++;\n' +
1373
+ '__self.__performance_monitor[key].totel_usec+=sl_time;\n' +
1374
+ 'if(__self.__performance_monitor[key].min_usec>sl_time){__self.__performance_monitor[key].min_usec=sl_time;}\n' +
1375
+ 'if(__self.__performance_monitor[key].max_usec<sl_time){__self.__performance_monitor[key].max_usec=sl_time;}\n' +
1376
+ `}}})('${funcName}_sys', 'system')\n`
1377
+ }
1378
+
1379
+ return code
1380
+ }
1381
+
1382
+ convRenbun (node: Ast): string {
1383
+ const right = this._convGen(node.right as Ast, true)
1384
+ const left = this._convGen(node.left as Ast, false)
1385
+ return `(function(){${left}; return ${right}}).call(this)`
1386
+ }
1387
+
1388
+ convOp (node: Ast): string {
1389
+ const OP_TBL: {[key: string]: string} = { // トークン名からJS演算子
1390
+ '&': '+""+',
1391
+ eq: '==',
1392
+ noteq: '!=',
1393
+ '===': '===',
1394
+ '!==': '!==',
1395
+ gt: '>',
1396
+ lt: '<',
1397
+ gteq: '>=',
1398
+ lteq: '<=',
1399
+ and: '&&',
1400
+ or: '||',
1401
+ shift_l: '<<',
1402
+ shift_r: '>>',
1403
+ shift_r0: '>>>',
1404
+ '÷': '/'
1405
+ }
1406
+ let op: string = node.operator || '' // 演算子
1407
+ let right = this._convGen(node.right as Ast, true)
1408
+ let left = this._convGen(node.left as Ast, true)
1409
+ if (op === '+' && this.speedMode.implicitTypeCasting === 0) {
1410
+ if (node.left && (node.left as Ast).type !== 'number') {
1411
+ left = `parseFloat(${left})`
1412
+ }
1413
+ if (node.right && (node.right as Ast).type !== 'number') {
1414
+ right = `parseFloat(${right})`
1415
+ }
1416
+ }
1417
+ // 階乗
1418
+ if (op === '^') { return `(Math.pow(${left}, ${right}))` }
1419
+ // 整数の割り算 #1152
1420
+ if (op === '÷÷') { return `(Math.floor(${left} / ${right}))` }
1421
+
1422
+ // 一般的なオペレータに変換
1423
+ if (OP_TBL[op]) { op = OP_TBL[op] }
1424
+ //
1425
+ return `(${left} ${op} ${right})`
1426
+ }
1427
+
1428
+ convInc (node: Ast): string {
1429
+ // もし値が省略されていたら、変数「それ」に代入する
1430
+ let value = null
1431
+ if (this.speedMode.invalidSore === 0) { value = this.varname('それ') }
1432
+ if (node.value) { value = this._convGen(node.value, true) }
1433
+ if (value == null) {
1434
+ throw NakoSyntaxError.fromNode('加算する先の変数名がありません。', node)
1435
+ }
1436
+ // 変数名
1437
+ const name: string = (node.name as Ast).value
1438
+ let res = this.findVar(name)
1439
+ let code = ''
1440
+ if (res === null) {
1441
+ this.varsSet.names.add(name)
1442
+ res = this.findVar(name)
1443
+ if (!res) { throw new Error('『増』または『減』で変数が見当たりません。') }
1444
+ }
1445
+ const jsName = res.js
1446
+ // 自動初期化するか
1447
+ code += `if (typeof(${jsName}) === 'undefined') { ${jsName} = 0; }`
1448
+ code += `${jsName} += ${value}`
1449
+ return ';' + this.convLineno(node, false) + code + '\n'
1450
+ }
1451
+
1452
+ convLet (node: Ast): string {
1453
+ // もし値が省略されていたら、変数「それ」に代入する
1454
+ let value = null
1455
+ if (this.speedMode.invalidSore === 0) { value = this.varname('それ') }
1456
+ if (node.value) { value = this._convGen(node.value, true) }
1457
+ if (value == null) {
1458
+ throw NakoSyntaxError.fromNode('代入する先の変数名がありません。', node)
1459
+ }
1460
+ // 変数名
1461
+ const name: string = (node.name as Ast).value
1462
+ const res = this.findVar(name)
1463
+ let code = ''
1464
+ if (res === null) {
1465
+ this.varsSet.names.add(name)
1466
+ code = `${this.varname(name)} = ${value};`
1467
+ } else {
1468
+ // 定数ならエラーを出す
1469
+ if (this.varslistSet[res.i].readonly.has(name)) {
1470
+ throw NakoSyntaxError.fromNode(
1471
+ `定数『${name}』は既に定義済みなので、値を代入することはできません。`, node)
1472
+ }
1473
+ code = `${res.js} = ${value};`
1474
+ }
1475
+
1476
+ return ';' + this.convLineno(node, false) + code + '\n'
1477
+ }
1478
+
1479
+ convDefLocalVar (node: Ast): string {
1480
+ const value = (node.value === null) ? 'null' : this._convGen(node.value, true)
1481
+ const name = (node.name as Ast).value
1482
+ const vtype = node.vartype // 変数 or 定数
1483
+ // 二重定義?
1484
+ if (this.varsSet.names.has(name)) { throw NakoSyntaxError.fromNode(`${vtype}『${name}』の二重定義はできません。`, node) }
1485
+
1486
+ //
1487
+ this.varsSet.names.add(name)
1488
+ if (vtype === '定数') {
1489
+ this.varsSet.readonly.add(name)
1490
+ }
1491
+ const code = `${this.varname(name)}=${value};\n`
1492
+ return this.convLineno(node, false) + code
1493
+ }
1494
+
1495
+ // #563 複数変数への代入
1496
+ convDefLocalVarlist (node: Ast): string {
1497
+ let code = ''
1498
+ const vtype = node.vartype // 変数 or 定数
1499
+ const value = (node.value === null) ? 'null' : this._convGen(node.value, true)
1500
+ this.loopId++
1501
+ const varI = `$nako_i${this.loopId}`
1502
+ code += `${varI}=${value}\n`
1503
+ code += `if (!(${varI} instanceof Array)) { ${varI}=[${varI}] }\n`
1504
+ const names: Ast[] = (node.names) ? node.names : []
1505
+ for (let i = 0; i < names.length; i++) {
1506
+ const nameObj = names[i]
1507
+ const name = nameObj.value
1508
+ // 二重定義?
1509
+ if (this.varsSet.names.has(name)) {
1510
+ // 複数変数文では、二重定義も許容する #1027
1511
+ // throw NakoSyntaxError.fromNode(`${vtype}『${name}』の二重定義はできません。`, node)
1512
+ }
1513
+ this.varsSet.names.add(name)
1514
+ if (vtype === '定数') {
1515
+ this.varsSet.readonly.add(name)
1516
+ }
1517
+ const vname = this.varname(name)
1518
+ code += `${vname}=${varI}[${i}];\n`
1519
+ }
1520
+ return this.convLineno(node, false) + code
1521
+ }
1522
+
1523
+ convString (node: Ast): string {
1524
+ let value = '' + node.value
1525
+ const mode = node.mode
1526
+ value = value.replace(/\\/g, '\\\\')
1527
+ value = value.replace(/"/g, '\\"')
1528
+ value = value.replace(/\r/g, '\\r')
1529
+ value = value.replace(/\n/g, '\\n')
1530
+ if (mode === 'ex') {
1531
+ const rf = (a: string, name: string) => {
1532
+ return '"+' + this.genVar(name, node) + '+"'
1533
+ }
1534
+ value = value.replace(/\{(.+?)\}/g, rf)
1535
+ value = value.replace(/{(.+?)}/g, rf)
1536
+ }
1537
+ return '"' + value + '"'
1538
+ }
1539
+
1540
+ convTryExcept (node: Ast): string {
1541
+ const block = this._convGen(node.block as Ast, false)
1542
+ const errBlock = this._convGen(node.errBlock as Ast, false)
1543
+ return this.convLineno(node, false) +
1544
+ `try {\n${block}\n} catch (e) {\n` +
1545
+ ' __v0["エラーメッセージ"] = e.message;\n' +
1546
+ ';\n' +
1547
+ `${errBlock}}\n`
1548
+ }
1549
+
1550
+ getUsedFuncSet (): Set<string> {
1551
+ return this.usedFuncSet
1552
+ }
1553
+
1554
+ getPluginInitCode (): string {
1555
+ // プラグインの初期化関数を実行する
1556
+ let code = ''
1557
+ let pluginCode = ''
1558
+ for (const name in this.__self.__module) {
1559
+ const initkey = `!${name}:初期化`
1560
+ if (this.varslistSet[0].names.has(initkey)) {
1561
+ this.usedFuncSet.add(`!${name}:初期化`)
1562
+ pluginCode += `__v0["!${name}:初期化"](__self);\n`
1563
+ }
1564
+ }
1565
+ if (pluginCode !== '') { code += '__v0.line=\'l0:プラグインの初期化\';\n' + pluginCode }
1566
+ return code
1567
+ }
1568
+ }
1569
+
1570
+ export interface NakoGenResult {
1571
+ // なでしこの実行環境ありの場合
1572
+ runtimeEnv: string;
1573
+ // JavaScript単体で動かす場合
1574
+ standalone: string;
1575
+ // コード生成に使ったNakoGenのインスタンス
1576
+ gen: any
1577
+ }
1578
+
1579
+ /**
1580
+ * @param com
1581
+ * @param ast
1582
+ * @param opt
1583
+ */
1584
+ export function generateJS (com: NakoCompiler, ast: Ast, opt: NakoGenOptions): NakoGenResult {
1585
+ const gen = new NakoGen(com)
1586
+
1587
+ // ※ [関数定義に関するコード生成のヒント]
1588
+ // ※ 関数の名前だけを(1)で登録して、(2)で実際に関数のコードを生成する。
1589
+ // ※ ただし(2)では生成するだけなので、(3)でプログラム冒頭に関数定義のコードを記述する。
1590
+ // この順番を変えることはできない (グローバル変数が認識できなくなったり、関数定義のタイミングがずれる)
1591
+
1592
+ // (1) ユーザー定義関数をシステムに登録する
1593
+ gen.registerFunction(ast)
1594
+
1595
+ // (2) JSコードを生成する
1596
+ let js = gen.convGen(ast, opt)
1597
+
1598
+ // (3) JSコードを実行するための事前ヘッダ部分の生成
1599
+ const jsInit = gen.getDefFuncCode(com, opt)
1600
+
1601
+ // テストの実行
1602
+ if (js && opt.isTest) {
1603
+ js += '\n__self._runTests(__tests);\n'
1604
+ }
1605
+ // async method
1606
+ if (gen.numAsyncFn > 0) {
1607
+ const asyncMain = '__nako3async' + (new Date()).getTime() + '_' + ('' + Math.random()).replace('.', '_') + '__'
1608
+ js = `
1609
+ // --------------------------------------------------
1610
+ // <nadesiko3::gen::async times="${gen.numAsyncFn}">
1611
+ async function ${asyncMain}(self) {
1612
+ ${jsInit}
1613
+ ${js}
1614
+ } // end of ${asyncMain}
1615
+ ${asyncMain}.call(self, self).catch(err => {
1616
+ self.numFailures++
1617
+ throw new NakoRuntimeError(err, self.__v0.line) // エラー位置を認識
1618
+ })
1619
+ // <nadesiko3::gen::async>
1620
+ // --------------------------------------------------
1621
+ `
1622
+ } else {
1623
+ js = `
1624
+ // --------------------------------------------------
1625
+ try {
1626
+ ${jsInit}
1627
+ ${js}
1628
+ } catch (err) {
1629
+ self.numFailures++
1630
+ throw new NakoRuntimeError(err, self.__v0.line) // エラー位置を認識
1631
+ }
1632
+ // --------------------------------------------------
1633
+ `
1634
+ }
1635
+ // デバッグメッセージ
1636
+ com.getLogger().trace('--- generate ---\n' + js)
1637
+ let codeImportFiles = ''
1638
+ const importNames = []
1639
+ for (const f of opt.importFiles) {
1640
+ if (f === 'nako_errors.mjs') { continue }
1641
+ const ff = 'nako3runtime_' + f.replace(/\.(js|mjs)$/, '').replace(/[^a-zA-Z0-9_]/g, '_')
1642
+ importNames.push(ff)
1643
+ codeImportFiles += `import ${ff} from './nako3runtime/${f}'\n`
1644
+ }
1645
+ const standaloneJSCode = `\
1646
+ // <standaloneCode>
1647
+ // 将来的に ESModule に対応する #1217
1648
+ import path from 'path'
1649
+ import { NakoRuntimeError } from './nako3runtime/nako_errors.mjs'
1650
+ ${codeImportFiles}
1651
+ const self = {}
1652
+ self.coreVersion = '${com.coreVersion}'
1653
+ self.version = '${com.version}'
1654
+ self.logger = {
1655
+ error: (message) => { console.error(message) },
1656
+ warn: (message) => { console.warn(message) },
1657
+ send: (level, message) => { console.log(message) },
1658
+ };
1659
+ self.__varslist = [{}, {}, {}]
1660
+ self.__v0 = self.__varslist[0]
1661
+ self.initFuncList = []
1662
+ self.clearFuncList = []
1663
+ // Copy module functions
1664
+ for (const mod of [${importNames.join(', ')}]) {
1665
+ for (const funcName in mod) {
1666
+ if (funcName === '初期化') {
1667
+ self.initFuncList.push(mod[funcName].fn)
1668
+ continue
1669
+ }
1670
+ if (funcName === '!クリア') {
1671
+ self.clearFuncList.push(mod[funcName].fn)
1672
+ continue
1673
+ }
1674
+ self.__varslist[0][funcName] = mod[funcName].fn
1675
+ }
1676
+ }
1677
+ self.__vars = self.__varslist[2];
1678
+ self.__module = {};
1679
+ self.__locals = {};
1680
+ self.__genMode = 'sync';
1681
+
1682
+ // プラグインの初期化コードを実行
1683
+ self.initFuncList.map(f => f(self))
1684
+
1685
+ try {
1686
+ ${opt.codeStandalone}
1687
+ // <JS>
1688
+ ${js}
1689
+ // </JS>
1690
+ // プラグインのクリアコードを実行
1691
+ self.clearFuncList.map(f => f(self))
1692
+ } catch (err) {
1693
+ self.logger.error(err);
1694
+ throw err;
1695
+ }
1696
+ // </standaloneCode>
1697
+ `
1698
+ // ---
1699
+ const initCode = gen.getPluginInitCode()
1700
+ const runtimeEnvCode = `
1701
+ // runtimeEnv
1702
+ ${NakoError.toString()}
1703
+ ${NakoRuntimeError.toString()}
1704
+ const self = this
1705
+ ${opt.codeEnv}
1706
+ ${jsInit}
1707
+ ${initCode}
1708
+ ${js}
1709
+ // end of runtimeEnv
1710
+ `
1711
+ return {
1712
+ // なでしこの実行環境ありの場合(thisが有効)
1713
+ runtimeEnv: runtimeEnvCode,
1714
+ // JavaScript単体で動かす場合
1715
+ standalone: standaloneJSCode,
1716
+ // コード生成に使ったNakoGenのインスタンス
1717
+ gen
1718
+ }
1719
+ }