nadesiko3 3.2.27

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