nadesiko3 3.3.49 → 3.3.50

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 (342) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +143 -143
  3. package/batch/.DS_Store +0 -0
  4. package/batch/browsers.template.md +3 -3
  5. package/batch/build_browsers.nako3 +72 -72
  6. package/batch/build_command.nako3 +44 -44
  7. package/batch/build_nako_version.nako3 +42 -42
  8. package/batch/calc_hash.nako3 +29 -29
  9. package/batch/cmd_txt2json.nako3 +74 -74
  10. package/batch/command.txt +79 -0
  11. package/batch/command_nakopad.txt +69 -0
  12. package/batch/download-extlib.nako3 +43 -43
  13. package/batch/gen_command_nakopad.nako3 +57 -57
  14. package/batch/jsplugin2text.nako3 +285 -285
  15. package/batch/pickup_command.nako3 +110 -110
  16. package/batch/pickup_reserved_words.nako3 +11 -11
  17. package/batch/publish_version.nako3 +46 -46
  18. package/batch/show_agents.js +14 -14
  19. package/batch/turtle2js.nako3 +21 -21
  20. package/bin/cnako3 +10 -10
  21. package/core/LICENSE +21 -21
  22. package/core/README.md +66 -66
  23. package/core/batch/build_nako_version.nako3 +42 -42
  24. package/core/command/snako.mjs +105 -105
  25. package/core/command/snako.mts +116 -116
  26. package/core/index.mjs +21 -21
  27. package/core/package.json +47 -47
  28. package/core/sample/hello.nako3 +7 -7
  29. package/core/sample/hoge.mjs +4 -4
  30. package/core/sample/hoge.mts +6 -6
  31. package/core/src/nako3.mjs +864 -858
  32. package/core/src/nako3.mts +976 -967
  33. package/core/src/nako_colors.mjs +78 -78
  34. package/core/src/nako_colors.mts +86 -86
  35. package/core/src/nako_core_version.mjs +8 -8
  36. package/core/src/nako_core_version.mts +19 -19
  37. package/core/src/nako_csv.mjs +185 -185
  38. package/core/src/nako_csv.mts +188 -188
  39. package/core/src/nako_errors.mjs +173 -173
  40. package/core/src/nako_errors.mts +197 -197
  41. package/core/src/nako_from_dncl.mjs +255 -255
  42. package/core/src/nako_from_dncl.mts +250 -250
  43. package/core/src/nako_gen.mjs +1647 -1648
  44. package/core/src/nako_gen.mts +1718 -1719
  45. package/core/src/nako_gen_async.mjs +1659 -1659
  46. package/core/src/nako_gen_async.mts +1732 -1732
  47. package/core/src/nako_global.mjs +107 -107
  48. package/core/src/nako_global.mts +138 -138
  49. package/core/src/nako_indent.mjs +445 -445
  50. package/core/src/nako_indent.mts +492 -492
  51. package/core/src/nako_josi_list.mjs +38 -38
  52. package/core/src/nako_josi_list.mts +45 -45
  53. package/core/src/nako_lex_rules.mjs +253 -253
  54. package/core/src/nako_lex_rules.mts +260 -260
  55. package/core/src/nako_lexer.mjs +609 -609
  56. package/core/src/nako_lexer.mts +612 -612
  57. package/core/src/nako_logger.mjs +199 -199
  58. package/core/src/nako_logger.mts +232 -232
  59. package/core/src/nako_parser3.mjs +2439 -2439
  60. package/core/src/nako_parser3.mts +2195 -2195
  61. package/core/src/nako_parser_base.mjs +370 -370
  62. package/core/src/nako_parser_base.mts +370 -370
  63. package/core/src/nako_parser_const.mjs +37 -37
  64. package/core/src/nako_parser_const.mts +37 -37
  65. package/core/src/nako_prepare.mjs +304 -304
  66. package/core/src/nako_prepare.mts +315 -315
  67. package/core/src/nako_reserved_words.mjs +38 -38
  68. package/core/src/nako_reserved_words.mts +38 -38
  69. package/core/src/nako_source_mapping.mjs +207 -207
  70. package/core/src/nako_source_mapping.mts +262 -262
  71. package/core/src/nako_test.mjs +37 -37
  72. package/core/src/nako_types.mjs +25 -25
  73. package/core/src/nako_types.mts +151 -151
  74. package/core/src/plugin_csv.mjs +49 -49
  75. package/core/src/plugin_csv.mts +50 -50
  76. package/core/src/plugin_math.mjs +328 -328
  77. package/core/src/plugin_math.mts +326 -326
  78. package/core/src/plugin_promise.mjs +91 -91
  79. package/core/src/plugin_promise.mts +91 -91
  80. package/core/src/plugin_system.mjs +2832 -2832
  81. package/core/src/plugin_system.mts +2690 -2690
  82. package/core/src/plugin_test.mjs +34 -34
  83. package/core/src/plugin_test.mts +34 -34
  84. package/demo/.DS_Store +0 -0
  85. package/demo/ace_editor.html +89 -89
  86. package/demo/ace_editor_tabs.html +161 -161
  87. package/demo/basic.html +71 -71
  88. package/demo/browsers.html +10 -9
  89. package/demo/css/basic.css +3 -3
  90. package/demo/css/common.css +157 -157
  91. package/demo/css/editor.css +8 -8
  92. package/demo/css/flow.css +3 -3
  93. package/demo/css/index.css +3 -3
  94. package/demo/extlib/.DS_Store +0 -0
  95. package/demo/flow.html +98 -98
  96. package/demo/graph.html +53 -53
  97. package/demo/image/nakopad-icon256.png +0 -0
  98. package/demo/index.html +133 -133
  99. package/demo/js/common.js +17 -17
  100. package/demo/js/turtle3d_test.js +44 -44
  101. package/demo/js/turtle_test.js +45 -45
  102. package/demo/nako3/calc.nako3 +4 -4
  103. package/demo/runscript.html +47 -47
  104. package/demo/runscript2.html +33 -33
  105. package/demo/runscript3.html +35 -35
  106. package/demo/runscript4.html +33 -33
  107. package/demo/turtle.html +58 -58
  108. package/demo/turtle2.html +141 -141
  109. package/demo/turtle3.html +279 -279
  110. package/demo/turtle3d.html +58 -58
  111. package/demo/turtle3d2.html +107 -107
  112. package/demo/version.html +24 -24
  113. package/doc/SETUP.md +157 -157
  114. package/doc/about.md +17 -17
  115. package/doc/browsers.md +26 -26
  116. package/doc/docgen.md +21 -21
  117. package/doc/editor.md +44 -44
  118. package/doc/files.md +39 -39
  119. package/doc/plugins.md +234 -234
  120. package/doc/release.md +79 -79
  121. package/doc/textlint.md +43 -43
  122. package/doc/win32.md +57 -57
  123. package/package.json +195 -195
  124. package/release/_hash.txt +28 -28
  125. package/release/_script-tags.txt +14 -14
  126. package/release/command.json +1 -1
  127. package/release/command.json.js +1 -1
  128. package/release/command_cnako3.json +1 -1
  129. package/release/command_list.json +1 -1
  130. package/release/editor.js +1 -1
  131. package/release/nako_gen_async.js +1 -1
  132. package/release/nako_gen_async.js.LICENSE.txt +35 -0
  133. package/release/plugin_caniuse.js.LICENSE.txt +11 -0
  134. package/release/plugin_csv.js +1 -1
  135. package/release/plugin_csv.js.LICENSE.txt +15 -0
  136. package/release/plugin_datetime.js.LICENSE.txt +15 -0
  137. package/release/plugin_kansuji.js.LICENSE.txt +3 -0
  138. package/release/plugin_markup.js.LICENSE.txt +11 -0
  139. package/release/plugin_turtle.js.LICENSE.txt +15 -0
  140. package/release/plugin_webworker.js.LICENSE.txt +3 -0
  141. package/release/plugin_weykturtle3d.js.LICENSE.txt +3 -0
  142. package/release/stats.json +1 -1
  143. package/release/version.js +1 -1
  144. package/release/wnako3.js +1 -1
  145. package/release/wnako3webworker.js +1 -1
  146. package/release/wnako3webworker.js.LICENSE.txt +131 -0
  147. package/src/.DS_Store +0 -0
  148. package/src/browsers.txt +12 -11
  149. package/src/browsers_agents.json +2 -2
  150. package/src/browsers_agents.mjs +1 -1
  151. package/src/cnako3.mjs +17 -17
  152. package/src/cnako3.mts +18 -18
  153. package/src/cnako3mod.mjs +707 -707
  154. package/src/cnako3mod.mts +712 -712
  155. package/src/commander_ja.mjs +164 -164
  156. package/src/commander_ja.mts +161 -161
  157. package/src/enako3.mjs +68 -68
  158. package/src/era.mjs +22 -22
  159. package/src/image_turtle-elephant.mjs +2 -2
  160. package/src/image_turtle-panda.mjs +2 -2
  161. package/src/image_turtle64.mjs +2 -2
  162. package/src/index.mjs +9 -9
  163. package/src/index.mts +10 -10
  164. package/src/nako3editorfix.sfd +106 -106
  165. package/src/nako_version.mjs +8 -8
  166. package/src/nako_version.mts +2 -2
  167. package/src/plugin_browser.mjs +213 -213
  168. package/src/plugin_browser.mts +206 -206
  169. package/src/plugin_browser_ajax.mjs +399 -399
  170. package/src/plugin_browser_audio.mjs +109 -109
  171. package/src/plugin_browser_canvas.mjs +449 -449
  172. package/src/plugin_browser_chart.mjs +294 -294
  173. package/src/plugin_browser_color.mjs +49 -49
  174. package/src/plugin_browser_crypto.mjs +26 -26
  175. package/src/plugin_browser_dialog.mjs +53 -53
  176. package/src/plugin_browser_dom_basic.mjs +336 -336
  177. package/src/plugin_browser_dom_event.mjs +193 -193
  178. package/src/plugin_browser_dom_parts.mjs +396 -396
  179. package/src/plugin_browser_geolocation.mjs +51 -51
  180. package/src/plugin_browser_hotkey.mjs +25 -25
  181. package/src/plugin_browser_html.mjs +59 -59
  182. package/src/plugin_browser_in_worker.mjs +45 -45
  183. package/src/plugin_browser_location.mjs +21 -21
  184. package/src/plugin_browser_speech.mjs +111 -111
  185. package/src/plugin_browser_storage.mjs +121 -121
  186. package/src/plugin_browser_system.mjs +31 -31
  187. package/src/plugin_browser_websocket.mjs +73 -73
  188. package/src/plugin_caniuse.mjs +29 -29
  189. package/src/plugin_datetime.mjs +394 -394
  190. package/src/plugin_httpserver.mjs +277 -277
  191. package/src/plugin_httpserver.mts +286 -286
  192. package/src/plugin_kansuji.mjs +224 -224
  193. package/src/plugin_keigo.mjs +55 -55
  194. package/src/plugin_markup.mjs +32 -32
  195. package/src/plugin_node.mjs +1047 -1047
  196. package/src/plugin_node.mts +980 -980
  197. package/src/plugin_turtle.mjs +647 -647
  198. package/src/plugin_webworker.mjs +334 -334
  199. package/src/plugin_weykturtle3d.mjs +1214 -1214
  200. package/src/plugin_worker.mjs +95 -95
  201. package/src/repl.nako3 +63 -63
  202. package/src/wnako3.mjs +12 -12
  203. package/src/wnako3.mts +11 -11
  204. package/src/wnako3_editor.css +215 -215
  205. package/src/wnako3_editor.mjs +1542 -1542
  206. package/src/wnako3_editor.mts +1658 -1657
  207. package/src/wnako3mod.mjs +213 -213
  208. package/src/wnako3mod.mts +214 -214
  209. package/src/wnako3webworker.mjs +69 -69
  210. package/test/.DS_Store +0 -0
  211. package/test/ace_editor/karma.config.js +94 -94
  212. package/test/ace_editor/test/.babelrc.json +3 -3
  213. package/test/ace_editor/test/ace_editor_test.js +178 -178
  214. package/test/ace_editor/test/html/custom_context.html +139 -139
  215. package/test/async/async_basic_test.mjs +122 -122
  216. package/test/browser/.DS_Store +0 -0
  217. package/test/browser/karma.config.js +221 -221
  218. package/test/browser/test/.babelrc.json +3 -3
  219. package/test/browser/test/compare_util.js +50 -50
  220. package/test/browser/test/html/div_basic.html +2 -2
  221. package/test/browser/test/html/event_dom_form.html +4 -4
  222. package/test/browser/test/html/event_dom_scrolldiv.html +5 -5
  223. package/test/browser/test/import_plugin_checker.js +24 -24
  224. package/test/browser/test/plugin_browser_test.js +51 -51
  225. package/test/browser/test/plugin_browser_test_ajax.js +123 -123
  226. package/test/browser/test/plugin_browser_test_color.js +18 -18
  227. package/test/browser/test/plugin_browser_test_dialog.js +72 -72
  228. package/test/browser/test/plugin_browser_test_dom_event.js +598 -598
  229. package/test/browser/test/plugin_browser_test_dom_parts.js +125 -125
  230. package/test/browser/test/plugin_browser_test_system.js +9 -9
  231. package/test/browser/test/plugin_turtle_test.js +817 -817
  232. package/test/browser/test/plugin_webworker_test.js +86 -86
  233. package/test/browser/test/require_test.js +68 -68
  234. package/test/bundled/.DS_Store +0 -0
  235. package/test/bundled/karma.config.base.js +117 -117
  236. package/test/bundled/karma.config.js +86 -86
  237. package/test/bundled/test/.babelrc.json +3 -3
  238. package/test/bundled/test/bundled_test.js +69 -69
  239. package/test/bundled/test/html/custom_context.html +65 -65
  240. package/test/bundled/test/html/custom_debug.html +66 -66
  241. package/test/bundled/test4b.cmd +52 -52
  242. package/test/bundled/test_base/.DS_Store +0 -0
  243. package/test/bundled/test_base/.babelrc.json +3 -3
  244. package/test/bundled/test_base/_checktool_test.js +25 -25
  245. package/test/bundled/test_base/basic_ajax_test.js +56 -56
  246. package/test/bundled/test_base/basic_async_test.js +18 -18
  247. package/test/bundled/test_base/basic_test.js +153 -153
  248. package/test/bundled/test_base/calc_test.js +132 -132
  249. package/test/bundled/test_base/css/browsers_box.css +114 -114
  250. package/test/bundled/test_base/html/custom_context.html +69 -69
  251. package/test/bundled/test_base/html/custom_debug.html +71 -71
  252. package/test/bundled/test_base/js/browsers_box.js +72 -72
  253. package/test/bundled/test_base/plugin_csv_test.js +37 -37
  254. package/test/bundled/test_base/plugin_datetime_test.js +115 -115
  255. package/test/bundled/test_base/plugin_kansuji_test.js +49 -49
  256. package/test/bundled/test_base/plugin_system_test.js +410 -410
  257. package/test/bundled/test_base/plugin_webworker_test.js +53 -53
  258. package/test/bundled/test_base/test_utils.js +191 -191
  259. package/test/common/.DS_Store +0 -0
  260. package/test/common/plugin_browser_test.mjs +22 -22
  261. package/test/common/plugin_browser_ut_audio_test.mjs +108 -108
  262. package/test/common/plugin_browser_ut_color_test.mjs +21 -21
  263. package/test/common/plugin_browser_ut_dialog_test.mjs +100 -100
  264. package/test/common/plugin_browser_ut_html_test.mjs +13 -13
  265. package/test/common/plugin_browser_ut_system_test.mjs +10 -10
  266. package/test/common/plugin_markup_test.mjs +23 -23
  267. package/test/jsconfig.json +19 -19
  268. package/test/karma.config.js +91 -91
  269. package/test/node/.DS_Store +0 -0
  270. package/test/node/async_test.mjs +96 -96
  271. package/test/node/commander_ja_test.mjs +89 -89
  272. package/test/node/error_message_test.mjs +243 -243
  273. package/test/node/kai_test.nako3 +6 -6
  274. package/test/node/node_test.mjs +60 -60
  275. package/test/node/plugin_broken.js.txt +3 -3
  276. package/test/node/plugin_browser_ut_ajax_test.mjs.todo +357 -357
  277. package/test/node/plugin_browser_ut_location_test.mjs +42 -42
  278. package/test/node/plugin_markup_test.mjs +47 -47
  279. package/test/node/plugin_math_test.mjs +45 -45
  280. package/test/node/plugin_node_test.mjs +98 -98
  281. package/test/node/plugin_test.mjs +44 -44
  282. package/test/node/relative_import_test_1.nako3 +1 -1
  283. package/test/node/relative_import_test_2.nako3 +2 -2
  284. package/test/node/require_nako3_test.mjs +67 -67
  285. package/test/node/requiretest.nako3 +4 -4
  286. package/test/node/requiretest_indirect.nako3 +1 -1
  287. package/test/node/requiretest_name.nako3 +5 -5
  288. package/test/node/runtime_error.nako3 +2 -2
  289. package/test/node/scope1.nako3 +10 -10
  290. package/test/node/scope2.nako3 +12 -12
  291. package/test/node/side_effects_test.mjs +39 -39
  292. package/test/node/sjis.txt +5 -5
  293. package/test/node/syntax_error.nako3 +1 -1
  294. package/test/node/wnako3_editor_test.mjs +384 -384
  295. package/tools/.DS_Store +0 -0
  296. package/tools/README.md +12 -12
  297. package/tools/check_new_version.nako3 +25 -25
  298. package/tools/nako3edit/.DS_Store +0 -0
  299. package/tools/nako3edit/a.sqlite3 +0 -0
  300. package/tools/nako3edit/html/.DS_Store +0 -0
  301. package/tools/nako3edit/html/daisyui/LICENSE +22 -22
  302. package/tools/nako3edit/html/daisyui/version_2.14.1 +1 -1
  303. package/tools/nako3edit/html/edit.html +170 -170
  304. package/tools/nako3edit/html/edit_plugin.js +6 -6
  305. package/tools/nako3edit/html/files.html +125 -125
  306. package/tools/nako3edit/html/nako3edit.css +65 -65
  307. package/tools/nako3edit/index.mjs +248 -248
  308. package/tools/nako3server/index.html +10 -10
  309. package/tools/nako3server/index.mjs +116 -116
  310. package/tools/nako3server/index.nako3 +34 -34
  311. package/core/.editorconfig +0 -6
  312. package/core/.eslintrc.cjs +0 -33
  313. package/core/.github/dependabot.yml +0 -7
  314. package/core/.github/workflows/nodejs.yml +0 -37
  315. package/core/.github/workflows/super-linter.yml +0 -61
  316. package/core/.github/workflows/textlint.yml +0 -199
  317. package/core/index.mts +0 -21
  318. package/core/test/array_test.mjs +0 -34
  319. package/core/test/basic_test.mjs +0 -344
  320. package/core/test/calc_test.mjs +0 -140
  321. package/core/test/core_module_test.mjs +0 -23
  322. package/core/test/debug_test.mjs +0 -16
  323. package/core/test/dncl_test.mjs +0 -94
  324. package/core/test/error_message_test.mjs +0 -210
  325. package/core/test/error_test.mjs +0 -16
  326. package/core/test/flow_test.mjs +0 -373
  327. package/core/test/func_call.mjs +0 -160
  328. package/core/test/func_test.mjs +0 -149
  329. package/core/test/indent_test.mjs +0 -364
  330. package/core/test/lex_test.mjs +0 -168
  331. package/core/test/literal_test.mjs +0 -73
  332. package/core/test/nako_lexer_test.mjs +0 -35
  333. package/core/test/nako_logger_test.mjs +0 -76
  334. package/core/test/nako_logger_test.mts +0 -78
  335. package/core/test/plugin_csv_test.mjs +0 -38
  336. package/core/test/plugin_promise_test.mjs +0 -18
  337. package/core/test/plugin_system_test.mjs +0 -630
  338. package/core/test/prepare_test.mjs +0 -96
  339. package/core/test/re_test.mjs +0 -22
  340. package/core/test/side_effects_test.mjs +0 -92
  341. package/core/test/variable_scope_test.mjs +0 -149
  342. package/core/tsconfig.json +0 -101
@@ -1,967 +1,976 @@
1
- /**
2
- * nadesiko v3
3
- */
4
- // types
5
- import { Token, Ast, FuncList, FuncListItem, FuncArgs, NakoEvent, CompilerOptions, NakoComEventName } from './nako_types.mjs'
6
- // parser / lexer
7
- import { NakoParser } from './nako_parser3.mjs'
8
- import { NakoLexer } from './nako_lexer.mjs'
9
- import { NakoPrepare } from './nako_prepare.mjs'
10
- import { NakoGen, generateJS, NakoGenOptions, NakoGenResult } from './nako_gen.mjs'
11
- import { NakoGenASync } from './nako_gen_async.mjs'
12
- import NakoIndent from './nako_indent.mjs'
13
- import { convertDNCL } from './nako_from_dncl.mjs'
14
- import { SourceMappingOfTokenization, SourceMappingOfIndentSyntax, OffsetToLineColumn, subtractSourceMapByPreCodeLength } from './nako_source_mapping.mjs'
15
- import { NakoLexerError, NakoImportError, NakoSyntaxError, InternalLexerError } from './nako_errors.mjs'
16
- import { NakoLogger } from './nako_logger.mjs'
17
- import { NakoGlobal } from './nako_global.mjs'
18
- // version info
19
- import coreVersion from './nako_core_version.mjs'
20
- // basic plugins
21
- import PluginSystem from './plugin_system.mjs'
22
- import PluginMath from './plugin_math.mjs'
23
- import PluginCSV from './plugin_csv.mjs'
24
- import PluginPromise from './plugin_promise.mjs'
25
- import PluginTest from './plugin_test.mjs'
26
-
27
- const cloneAsJSON = (x: any): any => JSON.parse(JSON.stringify(x))
28
-
29
- export interface NakoCompilerOption {
30
- useBasicPlugin: boolean;
31
- }
32
-
33
- /** インタプリタに「取り込み」文を追加するために用意するオブジェクト */
34
- export interface LoaderToolTask<T> {
35
- task: Promise<T>;
36
- }
37
- export interface LoaderTool {
38
- // type: 'nako3' | 'js' | 'invalid' | 'mjs'
39
- resolvePath: (name: string, token: Token, fromFile: string) => { type: string, filePath: string };
40
- readNako3: (filePath: string, token: Token) => LoaderToolTask<string>;
41
- readJs: (filePath: string, token: Token) => LoaderToolTask<any>;
42
- }
43
-
44
- interface DependenciesItem {
45
- tokens: Token[];
46
- alias: Set<string>;
47
- addPluginFile: () => void;
48
- funclist: any;
49
- }
50
- type Dependencies = { [key:string]:DependenciesItem }
51
-
52
- interface LexResult {
53
- commentTokens: Token[];
54
- tokens: Token[];
55
- requireTokens: Token[];
56
- }
57
-
58
- type NakoVars = {[key: string]: any}
59
-
60
- /** なでしこコンパイラ */
61
- export class NakoCompiler {
62
- private nakoFuncList: FuncList;
63
- private funclist: FuncList;
64
- private pluginFunclist: Record<string, FuncListItem>;
65
- private pluginfiles: Record<string, any>;
66
- private commandlist: Set<string>;
67
- private prepare: NakoPrepare;
68
- private parser: NakoParser;
69
- private lexer: NakoLexer;
70
- private dependencies: Dependencies;
71
- private usedFuncs: Set<string>;
72
- protected logger: NakoLogger;
73
- protected eventList: NakoEvent[];
74
- // global objects
75
- __varslist: NakoVars[];
76
- __locals: NakoVars;
77
- __self: NakoCompiler;
78
- __vars: NakoVars;
79
- __v0: NakoVars;
80
- __v1: NakoVars;
81
- __globals: NakoGlobal[];
82
- __module: Record<string, Record<string, FuncListItem>>;
83
- numFailures: number; // エラーレポートの数を記録
84
- public version: string;
85
- public coreVersion: string;
86
- /**
87
- * @param {undefined | {'useBasicPlugin':true|false}} options
88
- */
89
- constructor (options: NakoCompilerOption | undefined = undefined) {
90
- if (options === undefined) {
91
- options = { useBasicPlugin: true }
92
- }
93
- // 環境のリセット
94
- /** @type {Record<string, any>[]} */
95
- this.__varslist = [{}, {}, {}] // このオブジェクトは変更しないこと (this.gen.__varslist と共有する)
96
- this.__locals = {} // ローカル変数
97
- this.__self = this
98
- this.__vars = this.__varslist[2]
99
- this.__v0 = this.__varslist[0]
100
- this.__v1 = this.__varslist[1]
101
- // バージョンを設定
102
- this.version = coreVersion.version
103
- this.coreVersion = coreVersion.version
104
- /**
105
- * @type {NakoGlobal[]}
106
- */
107
- this.__globals = [] // 生成した NakoGlobal のインスタンスを保持
108
- /** @type {Record<string, Record<string, NakoFunction>>} */
109
- this.__module = {} // requireなどで取り込んだモジュールの一覧
110
- this.pluginFunclist = {} // プラグインで定義された関数
111
- this.funclist = {} // プラグインで定義された関数 + ユーザーが定義した関数
112
- this.pluginfiles = {} // 取り込んだファイル一覧
113
- this.commandlist = new Set() // プラグインで定義された定数・変数・関数の名前
114
- this.nakoFuncList = {} // __v1に配置するJavaScriptのコードで定義された関数
115
- this.eventList = [] // 実行前に環境を変更するためのイベント
116
-
117
- this.logger = new NakoLogger()
118
-
119
- // 必要なオブジェクトを覚えておく
120
- this.prepare = NakoPrepare.getInstance()
121
- this.parser = new NakoParser(this.logger)
122
- this.lexer = new NakoLexer(this.logger)
123
-
124
- /**
125
- * 取り込み文を置換するためのオブジェクト。
126
- * 正規化されたファイル名がキーになり、取り込み文の引数に指定された正規化されていないファイル名はaliasに入れられる。
127
- * JavaScriptファイルによるプラグインの場合、contentは空文字列。
128
- * funclistはシンタックスハイライトの高速化のために事前に取り出した、ファイルが定義する関数名のリスト。
129
- */
130
- this.dependencies = {}
131
- this.usedFuncs = new Set()
132
- this.numFailures = 0
133
-
134
- if (options.useBasicPlugin) { this.addBasicPlugins() }
135
- }
136
-
137
- getLogger (): NakoLogger {
138
- return this.logger
139
- }
140
-
141
- getNakoFuncList (): FuncList {
142
- return this.nakoFuncList
143
- }
144
-
145
- getNakoFunc (name: string): FuncListItem|undefined {
146
- return this.nakoFuncList[name]
147
- }
148
-
149
- getPluginfiles (): Record<string, any> {
150
- return this.pluginfiles
151
- }
152
-
153
- /**
154
- * 基本的なプラグインを追加する
155
- */
156
- addBasicPlugins () {
157
- this.addPluginObject('PluginSystem', PluginSystem)
158
- this.addPluginObject('PluginMath', PluginMath)
159
- this.addPluginObject('PluginPromise', PluginPromise)
160
- this.addPluginObject('PluginAssert', PluginTest)
161
- this.addPluginObject('PluginCSV', PluginCSV)
162
- }
163
-
164
- /**
165
- * loggerを新しいインスタンスで置き換える。
166
- */
167
- replaceLogger () {
168
- const logger = this.lexer.logger = this.parser.logger = this.logger = new NakoLogger()
169
- return logger
170
- }
171
-
172
- /**
173
- * ファイル内のrequire文の位置を列挙する。出力の配列はstartでソートされている。
174
- * @param {Token[]} tokens rawtokenizeの出力
175
- */
176
- static listRequireStatements (tokens: Token[]): Token[] {
177
- const requireStatements: Token[] = []
178
- for (let i = 0; i + 2 < tokens.length; i++) {
179
- // not (string|string_ex) '取り込み'
180
- if (!(tokens[i].type === 'not' &&
181
- (tokens[i + 1].type === 'string' || tokens[i + 1].type === 'string_ex') &&
182
- tokens[i + 2].value === '取込')) {
183
- continue
184
- }
185
- requireStatements.push({
186
- ...tokens[i],
187
- start: i,
188
- end: i + 3,
189
- value: tokens[i + 1].value + '',
190
- firstToken: tokens[i],
191
- lastToken: tokens[i + 2]
192
- })
193
- i += 2
194
- }
195
- return requireStatements
196
- }
197
-
198
- /**
199
- * プログラムが依存するファイルを再帰的に取得する。
200
- * - 依存するファイルがJavaScriptファイルの場合、そのファイルを実行して評価結果をthis.addPluginFileに渡す。
201
- * - 依存するファイルがなでしこ言語の場合、ファイルの中身を取得して変数に保存し、再帰する。
202
- *
203
- * @param {string} code
204
- * @param {string} filename
205
- * @param {string} preCode
206
- * @param {LoaderTool} tools 実行環境 (ブラウザ or Node.js) によって外部ファイルの取得・実行方法は異なるため、引数でそれらを行う関数を受け取る。
207
- * - resolvePath は指定した名前をもつファイルを検索し、正規化されたファイル名を返す関数。返されたファイル名はreadNako3かreadJsの引数になる。
208
- * - readNako3は指定されたファイルの中身を返す関数。
209
- * - readJsは指定したファイルをJavaScriptのプログラムとして実行し、`export default` でエクスポートされた値を返す関数。
210
- * @returns {Promise<unknown> | void}
211
- * @protected
212
- */
213
- _loadDependencies (code: string, filename: string, preCode: string, tools: LoaderTool) {
214
- const dependencies: Dependencies = {}
215
- const compiler = new NakoCompiler({ useBasicPlugin: true })
216
-
217
- /**
218
- * @param {any} item
219
- * @param {any} tasks
220
- */
221
- const loadJS = (item: any, tasks: any) => {
222
- // jsならプラグインとして読み込む。(ESMでは必ず動的に読む)
223
- const obj = tools.readJs(item.filePath, item.firstToken)
224
- tasks.push(obj.task.then((res: any) => {
225
- const pluginFuncs = res()
226
- this.addPluginFile(item.value, item.filePath, pluginFuncs, false)
227
- dependencies[item.filePath].funclist = pluginFuncs
228
- dependencies[item.filePath].addPluginFile = () => { this.addPluginFile(item.value, item.filePath, pluginFuncs, false) }
229
- }))
230
- }
231
- const loadNako3 = (item: any, tasks: any) => {
232
- // nako3ならファイルを読んでdependenciesに保存する。
233
- const content = tools.readNako3(item.filePath, item.firstToken)
234
- const registerFile = (code: string) => {
235
- // シンタックスハイライトの高速化のために、事前にファイルが定義する関数名のリストを取り出しておく。
236
- // preDefineFuncはトークン列に変更を加えるため、事前にクローンしておく。
237
- // 「プラグイン名設定」を行う (#956)
238
- const modName = NakoLexer.filenameToModName(item.filePath)
239
- code = `『${modName}』にプラグイン名設定;` + code + ';『メイン』にプラグイン名設定;'
240
- const tokens = this.rawtokenize(code, 0, item.filePath)
241
- dependencies[item.filePath].tokens = tokens
242
- const funclist = {}
243
- NakoLexer.preDefineFunc(cloneAsJSON(tokens), this.logger, funclist)
244
- dependencies[item.filePath].funclist = funclist
245
- // 再帰
246
- return loadRec(code, item.filePath, '')
247
- }
248
- // 取り込み構文における問題を減らすため、必ず非同期でプログラムを読み込む仕様とした #1219
249
- tasks.push(content.task.then((res) => registerFile(res)))
250
- }
251
- const loadRec = (code: string, filename: string, preCode: string): Promise<unknown>|void => {
252
- const tasks: Promise<unknown>[] = []
253
- // 取り込みが必要な情報一覧を調べる(トークン分割して、取り込みタグを得る)
254
- const tags = NakoCompiler.listRequireStatements(compiler.rawtokenize(code, 0, filename, preCode))
255
- // パスを解決する
256
- const tagsResolvePath = tags.map((v) => ({ ...v, ...tools.resolvePath(v.value, v.firstToken as Token, filename) }))
257
- // 取り込み開始
258
- for (const item of tagsResolvePath) {
259
- // 2回目以降の読み込み
260
- // eslint-disable-next-line no-prototype-builtins
261
- if (dependencies.hasOwnProperty(item.filePath)) {
262
- dependencies[item.filePath].alias.add(item.value)
263
- continue
264
- }
265
-
266
- // 初回の読み込み
267
- // eslint-disable-next-line @typescript-eslint/no-empty-function
268
- dependencies[item.filePath] = { tokens: [], alias: new Set([item.value]), addPluginFile: ():void => {}, funclist: {} }
269
- if (item.type === 'js' || item.type === 'mjs') {
270
- loadJS(item, tasks)
271
- } else if (item.type === 'nako3') {
272
- loadNako3(item, tasks)
273
- } else {
274
- throw new NakoImportError(`ファイル『${item.value}』を読み込めません。ファイルが存在しないか未対応の拡張子です。`,
275
- (item.firstToken as Token).file, (item.firstToken as Token).line)
276
- }
277
- }
278
- if (tasks.length > 0) {
279
- return Promise.all(tasks)
280
- }
281
- return undefined
282
- }
283
-
284
- try {
285
- const result = loadRec(code, filename, preCode)
286
-
287
- // 非同期な場合のエラーハンドリング
288
- if (result !== undefined) {
289
- result.catch((err) => {
290
- // 読み込みに失敗しても処理は続ける方針なので、失敗しても例外は投げない
291
- // たぶん、その後の構文解析でエラーになるため
292
- this.logger.warn(err.msg)
293
- })
294
- }
295
-
296
- // すべてが終わってからthis.dependenciesに代入する。そうしないと、「実行」ボタンを連打した場合など、
297
- // loadDependencies() が並列実行されるときに正しく動作しない。
298
- this.dependencies = dependencies
299
- return result
300
- } catch (err) {
301
- // 同期処理では素直に例外を投げる
302
- this.logger.warn('' + err)
303
- throw err
304
- }
305
- }
306
-
307
- /**
308
- * コードを単語に分割する
309
- * @param {string} code なでしこのプログラム
310
- * @param {number} line なでしこのプログラムの行番号
311
- * @param {string} filename
312
- * @param {string} [preCode]
313
- * @returns {TokenWithSourceMap[]} トークンのリスト
314
- */
315
- rawtokenize (code: string, line: number, filename: string, preCode = ''): Token[] {
316
- if (!code.startsWith(preCode)) {
317
- throw new Error('codeの先頭にはpreCodeを含める必要があります。')
318
- }
319
- // インデント構文 (#596)
320
- const { code: code2, insertedLines, deletedLines } = NakoIndent.convert(code, filename)
321
- // DNCL構文 (#1140)
322
- const code3 = convertDNCL(code2, filename)
323
- // 全角半角の統一処理
324
- const preprocessed = this.prepare.convert(code3)
325
-
326
- const tokenizationSourceMapping = new SourceMappingOfTokenization(code2.length, preprocessed)
327
- const indentationSyntaxSourceMapping = new SourceMappingOfIndentSyntax(code2, insertedLines, deletedLines)
328
- const offsetToLineColumn = new OffsetToLineColumn(code)
329
-
330
- // トークン分割
331
- /** @type {import('./nako_lexer.mjs').Token[]} */
332
- let tokens
333
- try {
334
- tokens = this.lexer.tokenize(preprocessed.map((v) => v.text).join(''), line, filename)
335
- } catch (err) {
336
- if (!(err instanceof InternalLexerError)) {
337
- throw err
338
- }
339
- // エラー位置をソースコード上の位置に変換して返す
340
- const dest = indentationSyntaxSourceMapping.map(tokenizationSourceMapping.map(err.preprocessedCodeStartOffset), tokenizationSourceMapping.map(err.preprocessedCodeEndOffset))
341
- /** @type {number | undefined} */
342
- const line = dest.startOffset === null ? err.line : offsetToLineColumn.map(dest.startOffset, false).line
343
- const map = subtractSourceMapByPreCodeLength({ ...dest, line }, preCode)
344
- throw new NakoLexerError(err.msg, map.startOffset, map.endOffset, map.line, filename)
345
- }
346
-
347
- // ソースコード上の位置に変換
348
- return tokens.map((token) => {
349
- const dest = indentationSyntaxSourceMapping.map(
350
- tokenizationSourceMapping.map(token.preprocessedCodeOffset || 0),
351
- tokenizationSourceMapping.map((token.preprocessedCodeOffset || 0) + (token.preprocessedCodeLength || 0))
352
- )
353
- let line = token.line
354
- let column = 0
355
- if (token.type === 'eol' && dest.endOffset !== null) {
356
- // eolはparserで `line = ${eolToken.line};` に変換されるため、
357
- // 行末のeolのlineは次の行の行数を表す必要がある。
358
- const out = offsetToLineColumn.map(dest.endOffset, false)
359
- line = out.line
360
- column = out.column
361
- } else if (dest.startOffset !== null) {
362
- const out = offsetToLineColumn.map(dest.startOffset, false)
363
- line = out.line
364
- column = out.column
365
- }
366
- return {
367
- ...token,
368
- ...subtractSourceMapByPreCodeLength({ line, column, startOffset: dest.startOffset, endOffset: dest.endOffset }, preCode),
369
- rawJosi: token.josi
370
- }
371
- })
372
- }
373
-
374
- /**
375
- * 単語の属性を構文解析に先立ち補正する
376
- * @param {Token[]} tokens トークンのリスト
377
- * @param {boolean} isFirst 最初の呼び出しかどうか
378
- * @param {string} filename
379
- * @returns コード (なでしこ)
380
- */
381
- converttoken (tokens: Token[], isFirst: boolean, filename: string): Token[] {
382
- const tok = this.lexer.replaceTokens(tokens, isFirst, filename)
383
- return tok
384
- }
385
-
386
- /**
387
- * 環境のリセット
388
- */
389
- reset () {
390
- /**
391
- * なでしこのローカル変数をスタックで管理
392
- * __varslist[0] プラグイン領域
393
- * __varslist[1] なでしこグローバル領域
394
- * __varslist[2] 最初のローカル変数 ( == __vars }
395
- * @type {Record<string, any>[]}
396
- */
397
- this.__varslist = [this.__varslist[0], {}, {}]
398
- this.__v0 = this.__varslist[0]
399
- this.__v1 = this.__varslist[1]
400
- this.__vars = this.__varslist[2]
401
- this.__locals = {}
402
-
403
- // プラグイン命令以外を削除する。
404
- this.funclist = {}
405
- for (const name of Object.keys(this.__v0)) {
406
- const original = this.pluginFunclist[name]
407
- if (!original) {
408
- continue
409
- }
410
- this.funclist[name] = JSON.parse(JSON.stringify(original))
411
- }
412
-
413
- this.lexer.setFuncList(this.funclist)
414
- this.clearPlugins()
415
- this.logger.clear()
416
- }
417
-
418
- /**
419
- * typeがcodeのトークンを単語に分割するための処理
420
- * @param {string} code
421
- * @param {number} line
422
- * @param {string} filename
423
- * @param {number | null} startOffset
424
- * @returns
425
- * @private
426
- */
427
- lexCodeToken (code: string, line: number, filename: string, startOffset: number|null): {commentTokens: Token[], tokens: Token[]} {
428
- // 単語に分割
429
- let tokens = this.rawtokenize(code, line, filename, '')
430
-
431
- // 文字列内位置からファイル内位置へ変換
432
- if (startOffset === null) {
433
- for (const token of tokens) {
434
- token.startOffset = undefined
435
- token.endOffset = undefined
436
- }
437
- } else {
438
- for (const token of tokens) {
439
- if (token.startOffset !== undefined) {
440
- token.startOffset += startOffset
441
- }
442
- if (token.endOffset !== undefined) {
443
- token.endOffset += startOffset
444
- }
445
- }
446
- }
447
-
448
- // convertTokenで消されるコメントのトークンを残す
449
- const commentTokens = tokens.filter((t) => t.type === 'line_comment' || t.type === 'range_comment')
450
- .map((v) => ({ ...v })) // clone
451
-
452
- tokens = this.converttoken(tokens, false, filename)
453
-
454
- return { tokens, commentTokens }
455
- }
456
-
457
- /**
458
- * 再帰的にrequire文を置換する。
459
- * .jsであれば削除し、.nako3であればそのファイルのトークン列で置換する。
460
- * @param {TokenWithSourceMap[]} tokens
461
- * @param {Set<string>} [includeGuard]
462
- * @returns {Token[]} 削除された取り込み文のトークン
463
- */
464
- replaceRequireStatements (tokens: Token[], includeGuard:Set<string> = new Set()): Token[] {
465
- /** @type {TokenWithSourceMap[]} */
466
- const deletedTokens = []
467
- for (const r of NakoCompiler.listRequireStatements(tokens).reverse()) {
468
- // C言語のinclude guardと同じ仕組みで無限ループを防ぐ。
469
- if (includeGuard.has(r.value)) {
470
- deletedTokens.push(...tokens.splice((r.start || 0), (r.end || 0) - (r.start || 0)))
471
- continue
472
- }
473
- const filePath = Object.keys(this.dependencies).find((key) => this.dependencies[key].alias.has(r.value))
474
- if (filePath === undefined) {
475
- if (!r.firstToken) { throw new Error(`ファイル『${r.value}』が読み込まれていません。`) }
476
- throw new NakoLexerError(`ファイル『${r.value}』が読み込まれていません。`,
477
- (r.firstToken as Token).startOffset || 0,
478
- (r.firstToken as Token).endOffset || 0,
479
- (r.firstToken as Token).line, (r.firstToken as Token).file)
480
- }
481
- this.dependencies[filePath].addPluginFile()
482
- const children = cloneAsJSON(this.dependencies[filePath].tokens)
483
- includeGuard.add(r.value)
484
- deletedTokens.push(...this.replaceRequireStatements(children, includeGuard))
485
- deletedTokens.push(...tokens.splice(r.start || 0, (r.end || 0) - (r.start || 0), ...children))
486
- }
487
- return deletedTokens
488
- }
489
-
490
- /**
491
- * replaceRequireStatementsのシンタックスハイライト用の実装。
492
- * @param {TokenWithSourceMap[]} tokens
493
- * @returns {TokenWithSourceMap[]} 削除された取り込み文のトークン
494
- */
495
- removeRequireStatements (tokens: Token[]): Token[] {
496
- /** @type {TokenWithSourceMap[]} */
497
- const deletedTokens = []
498
- for (const r of NakoCompiler.listRequireStatements(tokens).reverse()) {
499
- // プラグイン命令のシンタックスハイライトのために、addPluginFileを呼んで関数のリストをthis.dependencies[filePath].funclistに保存させる。
500
- const filePath = Object.keys(this.dependencies).find((key) => this.dependencies[key].alias.has(r.value))
501
- if (filePath !== undefined) {
502
- this.dependencies[filePath].addPluginFile()
503
- }
504
-
505
- // 全ての取り込み文を削除する。そうしないとトークン化に時間がかかりすぎる。
506
- deletedTokens.push(...tokens.splice(r.start || 0, (r.end || 0) - (r.start || 0)))
507
- }
508
- return deletedTokens
509
- }
510
-
511
- /**
512
- * @param {string} code
513
- * @param {string} filename
514
- * @param {string} [preCode]
515
- */
516
- lex (code: string, filename = 'main.nako3', preCode = '', syntaxHighlighting = false): LexResult {
517
- // 単語に分割
518
- let tokens = this.rawtokenize(code, 0, filename, preCode)
519
-
520
- // require文を再帰的に置換する
521
- const requireStatementTokens = syntaxHighlighting ? this.removeRequireStatements(tokens) : this.replaceRequireStatements(tokens, undefined)
522
- for (const t of requireStatementTokens) {
523
- if (t.type === 'word' || t.type === 'not') {
524
- t.type = 'require'
525
- }
526
- }
527
- if (requireStatementTokens.length >= 3) {
528
- // modList を更新
529
- for (let i = 0; i < requireStatementTokens.length; i += 3) {
530
- let modName = requireStatementTokens[i + 1].value
531
- modName = NakoLexer.filenameToModName(modName)
532
- if (this.lexer.modList.indexOf(modName) < 0) {
533
- this.lexer.modList.push(modName)
534
- }
535
- }
536
- }
537
-
538
- // convertTokenで消されるコメントのトークンを残す
539
- /** @type {TokenWithSourceMap[]} */
540
- const commentTokens = tokens.filter((t) => t.type === 'line_comment' || t.type === 'range_comment')
541
- .map((v) => ({ ...v })) // clone
542
-
543
- tokens = this.converttoken(tokens, true, filename)
544
-
545
- for (let i = 0; i < tokens.length; i++) {
546
- if (tokens[i] && tokens[i].type === 'code') {
547
- const children = this.lexCodeToken(tokens[i].value, tokens[i].line, filename, tokens[i].startOffset || 0)
548
- commentTokens.push(...children.commentTokens)
549
- tokens.splice(i, 1, ...children.tokens)
550
- i--
551
- }
552
- }
553
-
554
- this.logger.trace('--- lex ---\n' + JSON.stringify(tokens, null, 2))
555
-
556
- return { commentTokens, tokens, requireTokens: requireStatementTokens }
557
- }
558
-
559
- /**
560
- * コードをパースしてASTにする
561
- * @param code なでしこのプログラム
562
- * @param filename
563
- * @param [preCode]
564
- * @return Ast
565
- */
566
- parse (code: string, filename: string, preCode = '') {
567
- // 関数を字句解析と構文解析に登録
568
- this.lexer.setFuncList(this.funclist)
569
- this.parser.setFuncList(this.funclist)
570
-
571
- const lexerOutput = this.lex(code, filename, preCode)
572
-
573
- // 構文木を作成
574
- /** @type {Ast} */
575
- let ast
576
- try {
577
- this.parser.genMode = 'sync' // set default
578
- ast = this.parser.parse(lexerOutput.tokens, filename)
579
- } catch (err: any) {
580
- if (typeof err.startOffset !== 'number') {
581
- throw NakoSyntaxError.fromNode(err.message, lexerOutput.tokens[this.parser.getIndex()])
582
- }
583
- throw err
584
- }
585
- this.usedFuncs = this.getUsedFuncs(ast)
586
- this.logger.trace('--- ast ---\n' + JSON.stringify(ast, null, 2))
587
- return ast
588
- }
589
-
590
- /**
591
- * @param {Ast} ast
592
- */
593
- getUsedFuncs (ast: Ast): Set<string> {
594
- const queue = [ast]
595
- this.usedFuncs = new Set()
596
-
597
- while (queue.length > 0) {
598
- const ast_ = queue.pop()
599
-
600
- if (ast_ !== null && ast_ !== undefined && ast_.block !== null && ast_.block !== undefined) {
601
- this.getUsedAndDefFuncs(queue, JSON.parse(JSON.stringify(ast_.block)))
602
- }
603
- }
604
-
605
- return this.deleteUnNakoFuncs()
606
- }
607
-
608
- getUsedAndDefFuncs (astQueue: Ast[], blockQueue: Ast[]): void {
609
- while (blockQueue.length > 0) {
610
- const block = blockQueue.pop()
611
-
612
- if (block !== null && block !== undefined) {
613
- this.getUsedAndDefFunc(block, astQueue, blockQueue)
614
- }
615
- }
616
- }
617
-
618
- getUsedAndDefFunc (block: Ast, astQueue: any[], blockQueue: Ast[]): void {
619
- if (['func', 'func_pointer'].includes(block.type) && block.name !== null && block.name !== undefined) {
620
- this.usedFuncs.add(block.name as string)
621
- }
622
- astQueue.push([block, block.block as Ast])
623
- blockQueue.push.apply(blockQueue, [block.value].concat(block.args))
624
- }
625
-
626
- deleteUnNakoFuncs (): Set<string> {
627
- for (const func of this.usedFuncs) {
628
- if (!this.commandlist.has(func)) {
629
- this.usedFuncs.delete(func)
630
- }
631
- }
632
- return this.usedFuncs
633
- }
634
-
635
- /**
636
- * プログラムをコンパイルしてランタイム用のJavaScriptのコードを返す
637
- * @param code コード (なでしこ)
638
- * @param filename
639
- * @param isTest テストかどうか
640
- * @param preCode
641
- */
642
- compile (code: string, filename: string, isTest = false, preCode = ''): string {
643
- const opt = new CompilerOptions()
644
- opt.testOnly = isTest
645
- opt.preCode = preCode
646
- const res = this.compileFromCode(code, filename, opt)
647
- return res.runtimeEnv
648
- }
649
-
650
- /** parse & generate */
651
- compileFromCode (code: string, filename: string, options: CompilerOptions): NakoGenResult {
652
- if (filename === '') { filename = 'main.nako3' }
653
- try {
654
- if (options.resetEnv) { this.reset() }
655
- if (options.resetAll) { this.clearPlugins() }
656
- // onBeforeParse
657
- this.eventList.filter(o => o.eventName === 'beforeParse').map(e => e.callback(code))
658
- const ast = this.parse(code, filename, options.preCode)
659
- // onBeforeGenerate
660
- this.eventList.filter(o => o.eventName === 'beforeGenerate').map(e => e.callback(ast))
661
- // generate
662
- const outCode = this.generateCode(ast, new NakoGenOptions(options.testOnly))
663
- // onAfterGenerate
664
- this.eventList.filter(o => o.eventName === 'afterGenerate').map(e => e.callback(outCode))
665
- return outCode
666
- } catch (e: any) {
667
- this.logger.error(e)
668
- throw e
669
- }
670
- }
671
-
672
- /**
673
- * プログラムをコンパイルしてJavaScriptのコードオブジェクトを返す
674
- * @param {AST} ast
675
- * @param opt テストかどうか
676
- * @return {Object}
677
- */
678
- generateCode (ast: Ast, opt: NakoGenOptions): NakoGenResult {
679
- // Select Code Generator #637
680
- switch (ast.genMode) {
681
- // ノーマルモード
682
- case 'sync':
683
- return generateJS(this, ast, opt)
684
- // 『!非同期モード』は非推奨
685
- case '非同期モード':
686
- this.logger.warn('『!非同期モード』の利用は非推奨です。[詳細](https://github.com/kujirahand/nadesiko3/issues/1164)')
687
- return NakoGenASync.generate(this, ast, opt.isTest)
688
- default:
689
- throw new Error(`コードジェネレータの「${ast.genMode}」はサポートされていません。`)
690
- }
691
- }
692
-
693
- /** (非推奨)
694
- * @param code
695
- * @param fname
696
- * @param isReset
697
- * @param isTest テストかどうか。stringの場合は1つのテストのみ。
698
- * @param [preCode]
699
- */
700
- async _run (code: string, fname: string, isReset: boolean, isTest: boolean, preCode = ''): Promise<NakoGlobal> {
701
- const opts: CompilerOptions = new CompilerOptions({
702
- resetEnv: isReset,
703
- resetAll: isReset,
704
- testOnly: isTest,
705
- preCode
706
- })
707
- return this._runEx(code, fname, opts)
708
- }
709
-
710
- /** 各プラグインをリセットする */
711
- clearPlugins () {
712
- // 他に実行している「なでしこ」があればクリアする
713
- this.__globals.forEach((sys: NakoGlobal) => {
714
- sys.reset()
715
- })
716
- this.__globals = [] // clear
717
- }
718
-
719
- /**
720
- * 環境を指定してJavaScriptのコードを実行する
721
- * @param code JavaScriptのコード
722
- * @param nakoGlobal 実行環境
723
- */
724
- private evalJS (code: string, nakoGlobal: NakoGlobal): void {
725
- // 実行前に環境を初期化するイベントを実行(beforeRun)
726
- this.eventList.filter(o => o.eventName === 'beforeRun').map(e => e.callback(nakoGlobal))
727
- // eslint-disable-next-line no-new-func
728
- const f = new Function(code)
729
- f.apply(nakoGlobal)
730
- // 実行後に終了イベントを実行(finish)
731
- this.eventList.filter(o => o.eventName === 'finish').map(e => e.callback(nakoGlobal))
732
- }
733
-
734
- /**
735
- * 同期的になでしこのプログラムcodeを実行する
736
- * @param code なでしこのプログラム
737
- * @param filename ファイル名
738
- * @param options オプション
739
- * @returns 実行に利用したグローバルオブジェクト
740
- */
741
- public runSync (code: string, filename: string, options: CompilerOptions = new CompilerOptions()): NakoGlobal {
742
- // コンパイル
743
- const out = this.compileFromCode(code, filename, options)
744
- // 実行前に環境を生成
745
- const nakoGlobal = this.getNakoGlobal(options, out.gen)
746
- // 実行
747
- this.evalJS(out.runtimeEnv, nakoGlobal)
748
- return nakoGlobal
749
- }
750
-
751
- /**
752
- * 非同期になでしこのプログラムcodeを実行する
753
- * @param code なでしこのプログラム
754
- * @param filename ファイル名
755
- * @param options オプション
756
- * @returns 実行に利用したグローバルオブジェクト
757
- */
758
- public async runAsync (code: string, filename: string, options: CompilerOptions = new CompilerOptions()): Promise<NakoGlobal> {
759
- // コンパイル
760
- const out = this.compileFromCode(code, filename, options)
761
- // 実行前に環境を生成
762
- const nakoGlobal = this.getNakoGlobal(options, out.gen)
763
- // 実行
764
- this.evalJS(out.runtimeEnv, nakoGlobal)
765
- return nakoGlobal
766
- }
767
-
768
- private getNakoGlobal (options: CompilerOptions, gen: NakoGen): NakoGlobal {
769
- // オプションを参照
770
- let g: NakoGlobal|null = options.nakoGlobal
771
- if (!g) {
772
- // 空ならば前回の値を参照(リセットするなら新規で作成する)
773
- if (this.__globals.length > 0 && options.resetAll === false && options.resetEnv === false) {
774
- g = this.__globals[this.__globals.length - 1]
775
- } else {
776
- g = new NakoGlobal(this, gen)
777
- }
778
- }
779
- if (this.__globals.indexOf(g) < 0) { this.__globals.push(g) }
780
- return g
781
- }
782
-
783
- /**
784
- * イベントを登録する
785
- * @param eventName イベント名
786
- * @param callback コールバック関数
787
- */
788
- addListener (eventName: NakoComEventName, callback: (event:any) => void) {
789
- this.eventList.push({ eventName, callback })
790
- }
791
-
792
- /**
793
- * テストを実行する
794
- * @param code
795
- * @param fname
796
- * @param preCode
797
- * @param testOnly
798
- */
799
- test (code: string, fname: string, preCode = '', testOnly = false) {
800
- const options = new CompilerOptions()
801
- options.preCode = preCode
802
- options.testOnly = testOnly
803
- return this.runSync(code, fname, options)
804
- }
805
-
806
- /**
807
- * なでしこのプログラムを実行(他に実行しているインスタンスはそのまま)
808
- * @param code
809
- * @param fname
810
- * @param [preCode]
811
- */
812
- run (code: string, fname = 'main.nako3', preCode = ''): NakoGlobal {
813
- const options = new CompilerOptions()
814
- options.preCode = preCode
815
- return this.runSync(code, fname, options)
816
- }
817
-
818
- /**
819
- * JavaScriptのみで動くコードを取得する場合
820
- * @param code
821
- * @param filename
822
- * @param opt
823
- */
824
- compileStandalone (code: string, filename: string, opt: NakoGenOptions|null = null): string {
825
- if (opt === null) { opt = new NakoGenOptions() }
826
- const ast = this.parse(code, filename)
827
- return this.generateCode(ast, opt).standalone
828
- }
829
-
830
- /**
831
- * プラグイン・オブジェクトを追加
832
- * @param po プラグイン・オブジェクト
833
- * @param persistent falseのとき、次以降の実行では使えない
834
- */
835
- addPlugin (po: {[key: string]: any}, persistent = true): void {
836
- // 変数のメタ情報を確認
837
- const __v0 = this.__varslist[0]
838
- if (__v0.meta === undefined) { __v0.meta = {} }
839
-
840
- // プラグインの値をオブジェクトにコピー
841
- for (const key in po) {
842
- const v = po[key]
843
- this.funclist[key] = v
844
- if (persistent) {
845
- this.pluginFunclist[key] = JSON.parse(JSON.stringify(v))
846
- }
847
- if (v.type === 'func') {
848
- __v0[key] = v.fn
849
- } else if (v.type === 'const' || v.type === 'var') {
850
- __v0[key] = v.value
851
- __v0.meta[key] = {
852
- readonly: (v.type === 'const')
853
- }
854
- } else {
855
- console.error('[プラグイン追加エラー]', v)
856
- throw new Error('プラグインの追加でエラー。')
857
- }
858
- // コマンドを登録するか?
859
- if (key === '初期化' || key.substring(0, 1) === '!') { // 登録しない関数名
860
- continue
861
- }
862
- this.commandlist.add(key)
863
- }
864
- }
865
-
866
- /**
867
- * プラグイン・オブジェクトを追加(ブラウザ向け)
868
- * @param objName オブジェクト名
869
- * @param po 関数リスト
870
- * @param persistent falseのとき、次以降の実行では使えない
871
- */
872
- addPluginObject (objName: string, po: {[key: string]: any}, persistent = true): void {
873
- this.__module[objName] = po
874
- this.pluginfiles[objName] = '*'
875
- // 初期化をチェック
876
- if (typeof (po['初期化']) === 'object') {
877
- const def = po['初期化']
878
- delete po['初期化']
879
- const initKey = `!${objName}:初期化`
880
- po[initKey] = def
881
- }
882
- // メタ情報をチェック (#1034)
883
- if (po.meta && po.meta.value && typeof (po.meta) === 'object') {
884
- const meta = po.meta
885
- delete po.meta
886
- const pluginName = meta.value.pluginName || objName
887
- const metaKey = `__${pluginName}`.replace('-', '__')
888
- po[metaKey] = meta
889
- }
890
- this.addPlugin(po, persistent)
891
- }
892
-
893
- /**
894
- * プラグイン・ファイルを追加(Node.js向け)
895
- * @param objName オブジェクト名
896
- * @param fpath ファイルパス
897
- * @param po 登録するオブジェクト
898
- * @param persistent falseのとき、次以降の実行では使えない
899
- */
900
- addPluginFile (objName: string, fpath: string, po: {[key: string]: any}, persistent = true): void {
901
- // Windowsのパスがあると、JSファイル書き出しでエラーになるので、置換する
902
- if (objName.indexOf('\\') >= 0) {
903
- objName = objName.replace(/\\/g, '/')
904
- }
905
- this.addPluginObject(objName, po, persistent)
906
- if (this.pluginfiles[objName] === undefined) {
907
- this.pluginfiles[objName] = fpath
908
- }
909
- }
910
-
911
- /**
912
- * 関数を追加する
913
- * @param {string} key 関数名
914
- * @param {string[][]} josi 助詞
915
- * @param {Function} fn 関数
916
- * @param {boolean} returnNone 値を返す関数の場合はfalseを指定
917
- * @param {boolean} asyncFn Promiseを返す関数かを指定
918
- */
919
- addFunc (key: string, josi: FuncArgs, fn: any, returnNone = true, asyncFn = false): void {
920
- this.funclist[key] = { josi, fn, type: 'func', return_none: returnNone, asyncFn }
921
- this.pluginFunclist[key] = cloneAsJSON(this.funclist[key])
922
- this.__varslist[0][key] = fn
923
- }
924
-
925
- // (非推奨) 互換性のため ... 関数を追加する
926
- public setFunc (key: string, josi: FuncArgs, fn: any, returnNone = true, asyncFn = false): void {
927
- this.addFunc(key, josi, fn, returnNone, asyncFn)
928
- }
929
-
930
- /**
931
- * プラグイン関数を参照する
932
- * @param key プラグイン関数の関数名
933
- * @returns プラグイン・オブジェクト
934
- */
935
- getFunc (key: string): FuncListItem {
936
- return this.funclist[key]
937
- }
938
-
939
- /** (非推奨) 同期的になでしこのプログラムcodeを実行する */
940
- private _runEx (code: string, filename: string, opts: Partial<CompilerOptions>, preCode = '', nakoGlobal: NakoGlobal|undefined = undefined): NakoGlobal {
941
- // コンパイル
942
- const options: CompilerOptions = new CompilerOptions(opts)
943
- options.preCode = preCode
944
- if (nakoGlobal) { options.nakoGlobal = nakoGlobal }
945
- return this.runSync(code, filename, options)
946
- }
947
-
948
- /** (非推奨) 同期的に実行
949
- * @param code
950
- * @param fname
951
- * @param opts
952
- * @param [preCode]
953
- */
954
- public runEx (code: string, fname: string, opts: Partial<CompilerOptions>, preCode = '') {
955
- return this._runEx(code, fname, opts, preCode)
956
- }
957
-
958
- /**
959
- * (非推奨) なでしこのプログラムを実行(他に実行しているインスタンスもリセットする)
960
- * @param code
961
- * @param fname
962
- * @param [preCode]
963
- */
964
- async runReset (code: string, fname = 'main.nako3', preCode = ''): Promise<NakoGlobal> {
965
- return this._runEx(code, fname, { resetAll: true, resetEnv: true }, preCode)
966
- }
967
- }
1
+ /**
2
+ * nadesiko v3
3
+ */
4
+ // types
5
+ import { Token, Ast, FuncList, FuncListItem, FuncArgs, NakoEvent, CompilerOptions, NakoComEventName } from './nako_types.mjs'
6
+ // parser / lexer
7
+ import { NakoParser } from './nako_parser3.mjs'
8
+ import { NakoLexer } from './nako_lexer.mjs'
9
+ import { NakoPrepare } from './nako_prepare.mjs'
10
+ import { NakoGen, generateJS, NakoGenOptions, NakoGenResult } from './nako_gen.mjs'
11
+ import { NakoGenASync } from './nako_gen_async.mjs'
12
+ import NakoIndent from './nako_indent.mjs'
13
+ import { convertDNCL } from './nako_from_dncl.mjs'
14
+ import { SourceMappingOfTokenization, SourceMappingOfIndentSyntax, OffsetToLineColumn, subtractSourceMapByPreCodeLength } from './nako_source_mapping.mjs'
15
+ import { NakoLexerError, NakoImportError, NakoSyntaxError, InternalLexerError } from './nako_errors.mjs'
16
+ import { NakoLogger } from './nako_logger.mjs'
17
+ import { NakoGlobal } from './nako_global.mjs'
18
+ // version info
19
+ import coreVersion from './nako_core_version.mjs'
20
+ // basic plugins
21
+ import PluginSystem from './plugin_system.mjs'
22
+ import PluginMath from './plugin_math.mjs'
23
+ import PluginCSV from './plugin_csv.mjs'
24
+ import PluginPromise from './plugin_promise.mjs'
25
+ import PluginTest from './plugin_test.mjs'
26
+
27
+ const cloneAsJSON = (x: any): any => JSON.parse(JSON.stringify(x))
28
+
29
+ export interface NakoCompilerOption {
30
+ useBasicPlugin: boolean;
31
+ }
32
+
33
+ /** インタプリタに「取り込み」文を追加するために用意するオブジェクト */
34
+ export interface LoaderToolTask<T> {
35
+ task: Promise<T>;
36
+ }
37
+ export interface LoaderTool {
38
+ // type: 'nako3' | 'js' | 'invalid' | 'mjs'
39
+ resolvePath: (name: string, token: Token, fromFile: string) => { type: string, filePath: string };
40
+ readNako3: (filePath: string, token: Token) => LoaderToolTask<string>;
41
+ readJs: (filePath: string, token: Token) => LoaderToolTask<any>;
42
+ }
43
+
44
+ interface DependenciesItem {
45
+ tokens: Token[];
46
+ alias: Set<string>;
47
+ addPluginFile: () => void;
48
+ funclist: any;
49
+ }
50
+ type Dependencies = { [key:string]:DependenciesItem }
51
+
52
+ interface LexResult {
53
+ commentTokens: Token[];
54
+ tokens: Token[];
55
+ requireTokens: Token[];
56
+ }
57
+
58
+ type NakoVars = {[key: string]: any}
59
+
60
+ export interface NakoResetOption {
61
+ needToClearPlugin: boolean
62
+ }
63
+
64
+ /** なでしこコンパイラ */
65
+ export class NakoCompiler {
66
+ private nakoFuncList: FuncList;
67
+ private funclist: FuncList;
68
+ private pluginFunclist: Record<string, FuncListItem>;
69
+ private pluginfiles: Record<string, any>;
70
+ private commandlist: Set<string>;
71
+ private prepare: NakoPrepare;
72
+ private parser: NakoParser;
73
+ private lexer: NakoLexer;
74
+ private dependencies: Dependencies;
75
+ private usedFuncs: Set<string>;
76
+ protected logger: NakoLogger;
77
+ protected eventList: NakoEvent[];
78
+ // global objects
79
+ __varslist: NakoVars[];
80
+ __locals: NakoVars;
81
+ __self: NakoCompiler;
82
+ __vars: NakoVars;
83
+ __v0: NakoVars;
84
+ __v1: NakoVars;
85
+ __globals: NakoGlobal[];
86
+ __module: Record<string, Record<string, FuncListItem>>;
87
+ numFailures: number; // エラーレポートの数を記録
88
+ public version: string;
89
+ public coreVersion: string;
90
+ /**
91
+ * @param {undefined | {'useBasicPlugin':true|false}} options
92
+ */
93
+ constructor (options: NakoCompilerOption | undefined = undefined) {
94
+ if (options === undefined) {
95
+ options = { useBasicPlugin: true }
96
+ }
97
+ // 環境のリセット
98
+ this.__varslist = [{}, {}, {}] // このオブジェクトは変更しないこと (this.gen.__varslist と共有する)
99
+ this.__locals = {} // ローカル変数
100
+ this.__self = this
101
+ this.__vars = this.__varslist[2]
102
+ this.__v0 = this.__varslist[0]
103
+ this.__v1 = this.__varslist[1]
104
+ // バージョンを設定
105
+ this.version = coreVersion.version
106
+ this.coreVersion = coreVersion.version
107
+ /**
108
+ * @type {NakoGlobal[]}
109
+ */
110
+ this.__globals = [] // 生成した NakoGlobal のインスタンスを保持
111
+ /** @type {Record<string, Record<string, NakoFunction>>} */
112
+ this.__module = {} // requireなどで取り込んだモジュールの一覧
113
+ this.pluginFunclist = {} // プラグインで定義された関数
114
+ this.funclist = {} // プラグインで定義された関数 + ユーザーが定義した関数
115
+ this.pluginfiles = {} // 取り込んだファイル一覧
116
+ this.commandlist = new Set() // プラグインで定義された定数・変数・関数の名前
117
+ this.nakoFuncList = {} // __v1に配置するJavaScriptのコードで定義された関数
118
+ this.eventList = [] // 実行前に環境を変更するためのイベント
119
+
120
+ this.logger = new NakoLogger()
121
+
122
+ /**
123
+ * 取り込み文を置換するためのオブジェクト。
124
+ * 正規化されたファイル名がキーになり、取り込み文の引数に指定された正規化されていないファイル名はaliasに入れられる。
125
+ * JavaScriptファイルによるプラグインの場合、contentは空文字列。
126
+ * funclistはシンタックスハイライトの高速化のために事前に取り出した、ファイルが定義する関数名のリスト。
127
+ */
128
+ this.dependencies = {}
129
+ this.usedFuncs = new Set()
130
+ this.numFailures = 0
131
+
132
+ if (options.useBasicPlugin) { this.addBasicPlugins() }
133
+ // 必要なオブジェクトを覚えておく
134
+ this.prepare = NakoPrepare.getInstance()
135
+ this.parser = new NakoParser(this.logger)
136
+ this.lexer = new NakoLexer(this.logger)
137
+ // 関数一覧を設定
138
+ this.lexer.setFuncList(this.funclist)
139
+ }
140
+
141
+ getLogger (): NakoLogger {
142
+ return this.logger
143
+ }
144
+
145
+ getNakoFuncList (): FuncList {
146
+ return this.nakoFuncList
147
+ }
148
+
149
+ getNakoFunc (name: string): FuncListItem|undefined {
150
+ return this.nakoFuncList[name]
151
+ }
152
+
153
+ getPluginfiles (): Record<string, any> {
154
+ return this.pluginfiles
155
+ }
156
+
157
+ /**
158
+ * 基本的なプラグインを追加する
159
+ */
160
+ addBasicPlugins () {
161
+ this.addPluginObject('PluginSystem', PluginSystem)
162
+ this.addPluginObject('PluginMath', PluginMath)
163
+ this.addPluginObject('PluginPromise', PluginPromise)
164
+ this.addPluginObject('PluginAssert', PluginTest)
165
+ this.addPluginObject('PluginCSV', PluginCSV)
166
+ }
167
+
168
+ /**
169
+ * loggerを新しいインスタンスで置き換える。
170
+ */
171
+ replaceLogger () {
172
+ const logger = this.lexer.logger = this.parser.logger = this.logger = new NakoLogger()
173
+ return logger
174
+ }
175
+
176
+ /**
177
+ * ファイル内のrequire文の位置を列挙する。出力の配列はstartでソートされている。
178
+ * @param {Token[]} tokens rawtokenizeの出力
179
+ */
180
+ static listRequireStatements (tokens: Token[]): Token[] {
181
+ const requireStatements: Token[] = []
182
+ for (let i = 0; i + 2 < tokens.length; i++) {
183
+ // not (string|string_ex) '取り込み'
184
+ if (!(tokens[i].type === 'not' &&
185
+ (tokens[i + 1].type === 'string' || tokens[i + 1].type === 'string_ex') &&
186
+ tokens[i + 2].value === '取込')) {
187
+ continue
188
+ }
189
+ requireStatements.push({
190
+ ...tokens[i],
191
+ start: i,
192
+ end: i + 3,
193
+ value: tokens[i + 1].value + '',
194
+ firstToken: tokens[i],
195
+ lastToken: tokens[i + 2]
196
+ })
197
+ i += 2
198
+ }
199
+ return requireStatements
200
+ }
201
+
202
+ /**
203
+ * プログラムが依存するファイルを再帰的に取得する。
204
+ * - 依存するファイルがJavaScriptファイルの場合、そのファイルを実行して評価結果をthis.addPluginFileに渡す。
205
+ * - 依存するファイルがなでしこ言語の場合、ファイルの中身を取得して変数に保存し、再帰する。
206
+ *
207
+ * @param {string} code
208
+ * @param {string} filename
209
+ * @param {string} preCode
210
+ * @param {LoaderTool} tools 実行環境 (ブラウザ or Node.js) によって外部ファイルの取得・実行方法は異なるため、引数でそれらを行う関数を受け取る。
211
+ * - resolvePath は指定した名前をもつファイルを検索し、正規化されたファイル名を返す関数。返されたファイル名はreadNako3かreadJsの引数になる。
212
+ * - readNako3は指定されたファイルの中身を返す関数。
213
+ * - readJsは指定したファイルをJavaScriptのプログラムとして実行し、`export default` でエクスポートされた値を返す関数。
214
+ * @returns {Promise<unknown> | void}
215
+ * @protected
216
+ */
217
+ _loadDependencies (code: string, filename: string, preCode: string, tools: LoaderTool) {
218
+ const dependencies: Dependencies = {}
219
+ const compiler = new NakoCompiler({ useBasicPlugin: true })
220
+
221
+ /**
222
+ * @param {any} item
223
+ * @param {any} tasks
224
+ */
225
+ const loadJS = (item: any, tasks: any) => {
226
+ // jsならプラグインとして読み込む。(ESMでは必ず動的に読む)
227
+ const obj = tools.readJs(item.filePath, item.firstToken)
228
+ tasks.push(obj.task.then((res: any) => {
229
+ const pluginFuncs = res()
230
+ this.addPluginFile(item.value, item.filePath, pluginFuncs, false)
231
+ dependencies[item.filePath].funclist = pluginFuncs
232
+ dependencies[item.filePath].addPluginFile = () => { this.addPluginFile(item.value, item.filePath, pluginFuncs, false) }
233
+ }))
234
+ }
235
+ const loadNako3 = (item: any, tasks: any) => {
236
+ // nako3ならファイルを読んでdependenciesに保存する。
237
+ const content = tools.readNako3(item.filePath, item.firstToken)
238
+ const registerFile = (code: string) => {
239
+ // シンタックスハイライトの高速化のために、事前にファイルが定義する関数名のリストを取り出しておく。
240
+ // preDefineFuncはトークン列に変更を加えるため、事前にクローンしておく。
241
+ // 「プラグイン名設定」を行う (#956)
242
+ const modName = NakoLexer.filenameToModName(item.filePath)
243
+ code = `『${modName}』にプラグイン名設定;` + code + ';『メイン』にプラグイン名設定;'
244
+ const tokens = this.rawtokenize(code, 0, item.filePath)
245
+ dependencies[item.filePath].tokens = tokens
246
+ const funclist = {}
247
+ NakoLexer.preDefineFunc(cloneAsJSON(tokens), this.logger, funclist)
248
+ dependencies[item.filePath].funclist = funclist
249
+ // 再帰
250
+ return loadRec(code, item.filePath, '')
251
+ }
252
+ // 取り込み構文における問題を減らすため、必ず非同期でプログラムを読み込む仕様とした #1219
253
+ tasks.push(content.task.then((res) => registerFile(res)))
254
+ }
255
+ const loadRec = (code: string, filename: string, preCode: string): Promise<unknown>|void => {
256
+ const tasks: Promise<unknown>[] = []
257
+ // 取り込みが必要な情報一覧を調べる(トークン分割して、取り込みタグを得る)
258
+ const tags = NakoCompiler.listRequireStatements(compiler.rawtokenize(code, 0, filename, preCode))
259
+ // パスを解決する
260
+ const tagsResolvePath = tags.map((v) => ({ ...v, ...tools.resolvePath(v.value, v.firstToken as Token, filename) }))
261
+ // 取り込み開始
262
+ for (const item of tagsResolvePath) {
263
+ // 2回目以降の読み込み
264
+ // eslint-disable-next-line no-prototype-builtins
265
+ if (dependencies.hasOwnProperty(item.filePath)) {
266
+ dependencies[item.filePath].alias.add(item.value)
267
+ continue
268
+ }
269
+
270
+ // 初回の読み込み
271
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
272
+ dependencies[item.filePath] = { tokens: [], alias: new Set([item.value]), addPluginFile: ():void => {}, funclist: {} }
273
+ if (item.type === 'js' || item.type === 'mjs') {
274
+ loadJS(item, tasks)
275
+ } else if (item.type === 'nako3') {
276
+ loadNako3(item, tasks)
277
+ } else {
278
+ throw new NakoImportError(`ファイル『${item.value}』を読み込めません。ファイルが存在しないか未対応の拡張子です。`,
279
+ (item.firstToken as Token).file, (item.firstToken as Token).line)
280
+ }
281
+ }
282
+ if (tasks.length > 0) {
283
+ return Promise.all(tasks)
284
+ }
285
+ return undefined
286
+ }
287
+
288
+ try {
289
+ const result = loadRec(code, filename, preCode)
290
+
291
+ // 非同期な場合のエラーハンドリング
292
+ if (result !== undefined) {
293
+ result.catch((err) => {
294
+ // 読み込みに失敗しても処理は続ける方針なので、失敗しても例外は投げない
295
+ // たぶん、その後の構文解析でエラーになるため
296
+ this.logger.warn(err.msg)
297
+ })
298
+ }
299
+
300
+ // すべてが終わってからthis.dependenciesに代入する。そうしないと、「実行」ボタンを連打した場合など、
301
+ // loadDependencies() が並列実行されるときに正しく動作しない。
302
+ this.dependencies = dependencies
303
+ return result
304
+ } catch (err) {
305
+ // 同期処理では素直に例外を投げる
306
+ this.logger.warn('' + err)
307
+ throw err
308
+ }
309
+ }
310
+
311
+ /**
312
+ * コードを単語に分割する
313
+ * @param {string} code なでしこのプログラム
314
+ * @param {number} line なでしこのプログラムの行番号
315
+ * @param {string} filename
316
+ * @param {string} [preCode]
317
+ * @returns {TokenWithSourceMap[]} トークンのリスト
318
+ */
319
+ rawtokenize (code: string, line: number, filename: string, preCode = ''): Token[] {
320
+ if (!code.startsWith(preCode)) {
321
+ throw new Error('codeの先頭にはpreCodeを含める必要があります。')
322
+ }
323
+ // インデント構文 (#596)
324
+ const { code: code2, insertedLines, deletedLines } = NakoIndent.convert(code, filename)
325
+ // DNCL構文 (#1140)
326
+ const code3 = convertDNCL(code2, filename)
327
+ // 全角半角の統一処理
328
+ const preprocessed = this.prepare.convert(code3)
329
+
330
+ const tokenizationSourceMapping = new SourceMappingOfTokenization(code2.length, preprocessed)
331
+ const indentationSyntaxSourceMapping = new SourceMappingOfIndentSyntax(code2, insertedLines, deletedLines)
332
+ const offsetToLineColumn = new OffsetToLineColumn(code)
333
+
334
+ // トークン分割
335
+ let tokens: Token[]
336
+ try {
337
+ tokens = this.lexer.tokenize(preprocessed.map((v) => v.text).join(''), line, filename)
338
+ } catch (err) {
339
+ if (!(err instanceof InternalLexerError)) {
340
+ throw err
341
+ }
342
+ // エラー位置をソースコード上の位置に変換して返す
343
+ const dest = indentationSyntaxSourceMapping.map(tokenizationSourceMapping.map(err.preprocessedCodeStartOffset), tokenizationSourceMapping.map(err.preprocessedCodeEndOffset))
344
+ /** @type {number | undefined} */
345
+ const line = dest.startOffset === null ? err.line : offsetToLineColumn.map(dest.startOffset, false).line
346
+ const map = subtractSourceMapByPreCodeLength({ ...dest, line }, preCode)
347
+ throw new NakoLexerError(err.msg, map.startOffset, map.endOffset, map.line, filename)
348
+ }
349
+
350
+ // ソースコード上の位置に変換
351
+ return tokens.map((token) => {
352
+ const dest = indentationSyntaxSourceMapping.map(
353
+ tokenizationSourceMapping.map(token.preprocessedCodeOffset || 0),
354
+ tokenizationSourceMapping.map((token.preprocessedCodeOffset || 0) + (token.preprocessedCodeLength || 0))
355
+ )
356
+ let line = token.line
357
+ let column = 0
358
+ if (token.type === 'eol' && dest.endOffset !== null) {
359
+ // eolはparserで `line = ${eolToken.line};` に変換されるため、
360
+ // 行末のeolのlineは次の行の行数を表す必要がある。
361
+ const out = offsetToLineColumn.map(dest.endOffset, false)
362
+ line = out.line
363
+ column = out.column
364
+ } else if (dest.startOffset !== null) {
365
+ const out = offsetToLineColumn.map(dest.startOffset, false)
366
+ line = out.line
367
+ column = out.column
368
+ }
369
+ return {
370
+ ...token,
371
+ ...subtractSourceMapByPreCodeLength({ line, column, startOffset: dest.startOffset, endOffset: dest.endOffset }, preCode),
372
+ rawJosi: token.josi
373
+ }
374
+ })
375
+ }
376
+
377
+ /**
378
+ * 単語の属性を構文解析に先立ち補正する
379
+ * @param {Token[]} tokens トークンのリスト
380
+ * @param {boolean} isFirst 最初の呼び出しかどうか
381
+ * @param {string} filename
382
+ * @returns コード (なでしこ)
383
+ */
384
+ converttoken (tokens: Token[], isFirst: boolean, filename: string): Token[] {
385
+ const tok = this.lexer.replaceTokens(tokens, isFirst, filename)
386
+ return tok
387
+ }
388
+
389
+ /**
390
+ * 環境のリセット
391
+ * {NakoResetOption|undefined}
392
+ */
393
+ reset (options: NakoResetOption|undefined = undefined) {
394
+ if (!options || options.needToClearPlugin) {
395
+ // (メモ) #1245
396
+ // 通常、リセット処理では、プラグインの!クリアを呼ぶ。
397
+ // しかし、エディタではクリアイベントを呼ぶと、時計などのコンテンツが止まってしまう
398
+ // そのため、例外的にオプションを指定すると、プラグインのクリアイベントを呼ばない
399
+ this.clearPlugins()
400
+ }
401
+ /**
402
+ * なでしこのローカル変数をスタックで管理
403
+ * __varslist[0] プラグイン領域
404
+ * __varslist[1] なでしこグローバル領域
405
+ * __varslist[2] 最初のローカル変数 ( == __vars }
406
+ */
407
+ this.__varslist = [this.__varslist[0], {}, {}]
408
+ this.__v0 = this.__varslist[0]
409
+ this.__v1 = this.__varslist[1]
410
+ this.__vars = this.__varslist[2]
411
+ this.__locals = {}
412
+
413
+ // プラグイン命令以外を削除する。
414
+ this.funclist = {}
415
+ for (const name of Object.keys(this.__v0)) {
416
+ const original = this.pluginFunclist[name]
417
+ if (!original) {
418
+ continue
419
+ }
420
+ this.funclist[name] = JSON.parse(JSON.stringify(original))
421
+ }
422
+
423
+ this.lexer.setFuncList(this.funclist)
424
+ this.logger.clear()
425
+ }
426
+
427
+ /**
428
+ * typeがcodeのトークンを単語に分割するための処理
429
+ * @param {string} code
430
+ * @param {number} line
431
+ * @param {string} filename
432
+ * @param {number | null} startOffset
433
+ * @returns
434
+ * @private
435
+ */
436
+ lexCodeToken (code: string, line: number, filename: string, startOffset: number|null): {commentTokens: Token[], tokens: Token[]} {
437
+ // 単語に分割
438
+ let tokens = this.rawtokenize(code, line, filename, '')
439
+
440
+ // 文字列内位置からファイル内位置へ変換
441
+ if (startOffset === null) {
442
+ for (const token of tokens) {
443
+ token.startOffset = undefined
444
+ token.endOffset = undefined
445
+ }
446
+ } else {
447
+ for (const token of tokens) {
448
+ if (token.startOffset !== undefined) {
449
+ token.startOffset += startOffset
450
+ }
451
+ if (token.endOffset !== undefined) {
452
+ token.endOffset += startOffset
453
+ }
454
+ }
455
+ }
456
+
457
+ // convertTokenで消されるコメントのトークンを残す
458
+ const commentTokens = tokens.filter((t) => t.type === 'line_comment' || t.type === 'range_comment')
459
+ .map((v) => ({ ...v })) // clone
460
+
461
+ tokens = this.converttoken(tokens, false, filename)
462
+
463
+ return { tokens, commentTokens }
464
+ }
465
+
466
+ /**
467
+ * 再帰的にrequire文を置換する。
468
+ * .jsであれば削除し、.nako3であればそのファイルのトークン列で置換する。
469
+ * @param {TokenWithSourceMap[]} tokens
470
+ * @param {Set<string>} [includeGuard]
471
+ * @returns {Token[]} 削除された取り込み文のトークン
472
+ */
473
+ replaceRequireStatements (tokens: Token[], includeGuard:Set<string> = new Set()): Token[] {
474
+ /** @type {TokenWithSourceMap[]} */
475
+ const deletedTokens = []
476
+ for (const r of NakoCompiler.listRequireStatements(tokens).reverse()) {
477
+ // C言語のinclude guardと同じ仕組みで無限ループを防ぐ。
478
+ if (includeGuard.has(r.value)) {
479
+ deletedTokens.push(...tokens.splice((r.start || 0), (r.end || 0) - (r.start || 0)))
480
+ continue
481
+ }
482
+ const filePath = Object.keys(this.dependencies).find((key) => this.dependencies[key].alias.has(r.value))
483
+ if (filePath === undefined) {
484
+ if (!r.firstToken) { throw new Error(`ファイル『${r.value}』が読み込まれていません。`) }
485
+ throw new NakoLexerError(`ファイル『${r.value}』が読み込まれていません。`,
486
+ (r.firstToken as Token).startOffset || 0,
487
+ (r.firstToken as Token).endOffset || 0,
488
+ (r.firstToken as Token).line, (r.firstToken as Token).file)
489
+ }
490
+ this.dependencies[filePath].addPluginFile()
491
+ const children = cloneAsJSON(this.dependencies[filePath].tokens)
492
+ includeGuard.add(r.value)
493
+ deletedTokens.push(...this.replaceRequireStatements(children, includeGuard))
494
+ deletedTokens.push(...tokens.splice(r.start || 0, (r.end || 0) - (r.start || 0), ...children))
495
+ }
496
+ return deletedTokens
497
+ }
498
+
499
+ /**
500
+ * replaceRequireStatementsのシンタックスハイライト用の実装。
501
+ * @param {TokenWithSourceMap[]} tokens
502
+ * @returns {TokenWithSourceMap[]} 削除された取り込み文のトークン
503
+ */
504
+ removeRequireStatements (tokens: Token[]): Token[] {
505
+ /** @type {TokenWithSourceMap[]} */
506
+ const deletedTokens = []
507
+ for (const r of NakoCompiler.listRequireStatements(tokens).reverse()) {
508
+ // プラグイン命令のシンタックスハイライトのために、addPluginFileを呼んで関数のリストをthis.dependencies[filePath].funclistに保存させる。
509
+ const filePath = Object.keys(this.dependencies).find((key) => this.dependencies[key].alias.has(r.value))
510
+ if (filePath !== undefined) {
511
+ this.dependencies[filePath].addPluginFile()
512
+ }
513
+
514
+ // 全ての取り込み文を削除する。そうしないとトークン化に時間がかかりすぎる。
515
+ deletedTokens.push(...tokens.splice(r.start || 0, (r.end || 0) - (r.start || 0)))
516
+ }
517
+ return deletedTokens
518
+ }
519
+
520
+ /**
521
+ * @param {string} code
522
+ * @param {string} filename
523
+ * @param {string} [preCode]
524
+ */
525
+ lex (code: string, filename = 'main.nako3', preCode = '', syntaxHighlighting = false): LexResult {
526
+ // 単語に分割
527
+ let tokens = this.rawtokenize(code, 0, filename, preCode)
528
+
529
+ // require文を再帰的に置換する
530
+ const requireStatementTokens = syntaxHighlighting ? this.removeRequireStatements(tokens) : this.replaceRequireStatements(tokens, undefined)
531
+ for (const t of requireStatementTokens) {
532
+ if (t.type === 'word' || t.type === 'not') {
533
+ t.type = 'require'
534
+ }
535
+ }
536
+ if (requireStatementTokens.length >= 3) {
537
+ // modList を更新
538
+ for (let i = 0; i < requireStatementTokens.length; i += 3) {
539
+ let modName = requireStatementTokens[i + 1].value
540
+ modName = NakoLexer.filenameToModName(modName)
541
+ if (this.lexer.modList.indexOf(modName) < 0) {
542
+ this.lexer.modList.push(modName)
543
+ }
544
+ }
545
+ }
546
+
547
+ // convertTokenで消されるコメントのトークンを残す
548
+ /** @type {TokenWithSourceMap[]} */
549
+ const commentTokens = tokens.filter((t) => t.type === 'line_comment' || t.type === 'range_comment')
550
+ .map((v) => ({ ...v })) // clone
551
+
552
+ tokens = this.converttoken(tokens, true, filename)
553
+
554
+ for (let i = 0; i < tokens.length; i++) {
555
+ if (tokens[i] && tokens[i].type === 'code') {
556
+ const children = this.lexCodeToken(tokens[i].value, tokens[i].line, filename, tokens[i].startOffset || 0)
557
+ commentTokens.push(...children.commentTokens)
558
+ tokens.splice(i, 1, ...children.tokens)
559
+ i--
560
+ }
561
+ }
562
+
563
+ this.logger.trace('--- lex ---\n' + JSON.stringify(tokens, null, 2))
564
+
565
+ return { commentTokens, tokens, requireTokens: requireStatementTokens }
566
+ }
567
+
568
+ /**
569
+ * コードをパースしてASTにする
570
+ * @param code なでしこのプログラム
571
+ * @param filename
572
+ * @param [preCode]
573
+ * @return Ast
574
+ */
575
+ parse (code: string, filename: string, preCode = '') {
576
+ // 関数を字句解析と構文解析に登録
577
+ this.lexer.setFuncList(this.funclist)
578
+ this.parser.setFuncList(this.funclist)
579
+
580
+ const lexerOutput = this.lex(code, filename, preCode)
581
+
582
+ // 構文木を作成
583
+ /** @type {Ast} */
584
+ let ast
585
+ try {
586
+ this.parser.genMode = 'sync' // set default
587
+ ast = this.parser.parse(lexerOutput.tokens, filename)
588
+ } catch (err: any) {
589
+ if (typeof err.startOffset !== 'number') {
590
+ throw NakoSyntaxError.fromNode(err.message, lexerOutput.tokens[this.parser.getIndex()])
591
+ }
592
+ throw err
593
+ }
594
+ this.usedFuncs = this.getUsedFuncs(ast)
595
+ this.logger.trace('--- ast ---\n' + JSON.stringify(ast, null, 2))
596
+ return ast
597
+ }
598
+
599
+ /**
600
+ * @param {Ast} ast
601
+ */
602
+ getUsedFuncs (ast: Ast): Set<string> {
603
+ const queue = [ast]
604
+ this.usedFuncs = new Set()
605
+
606
+ while (queue.length > 0) {
607
+ const ast_ = queue.pop()
608
+
609
+ if (ast_ !== null && ast_ !== undefined && ast_.block !== null && ast_.block !== undefined) {
610
+ this.getUsedAndDefFuncs(queue, JSON.parse(JSON.stringify(ast_.block)))
611
+ }
612
+ }
613
+
614
+ return this.deleteUnNakoFuncs()
615
+ }
616
+
617
+ getUsedAndDefFuncs (astQueue: Ast[], blockQueue: Ast[]): void {
618
+ while (blockQueue.length > 0) {
619
+ const block = blockQueue.pop()
620
+
621
+ if (block !== null && block !== undefined) {
622
+ this.getUsedAndDefFunc(block, astQueue, blockQueue)
623
+ }
624
+ }
625
+ }
626
+
627
+ getUsedAndDefFunc (block: Ast, astQueue: any[], blockQueue: Ast[]): void {
628
+ if (['func', 'func_pointer'].includes(block.type) && block.name !== null && block.name !== undefined) {
629
+ this.usedFuncs.add(block.name as string)
630
+ }
631
+ astQueue.push([block, block.block as Ast])
632
+ blockQueue.push.apply(blockQueue, [block.value].concat(block.args))
633
+ }
634
+
635
+ deleteUnNakoFuncs (): Set<string> {
636
+ for (const func of this.usedFuncs) {
637
+ if (!this.commandlist.has(func)) {
638
+ this.usedFuncs.delete(func)
639
+ }
640
+ }
641
+ return this.usedFuncs
642
+ }
643
+
644
+ /**
645
+ * プログラムをコンパイルしてランタイム用のJavaScriptのコードを返す
646
+ * @param code コード (なでしこ)
647
+ * @param filename
648
+ * @param isTest テストかどうか
649
+ * @param preCode
650
+ */
651
+ compile (code: string, filename: string, isTest = false, preCode = ''): string {
652
+ const opt = new CompilerOptions()
653
+ opt.testOnly = isTest
654
+ opt.preCode = preCode
655
+ const res = this.compileFromCode(code, filename, opt)
656
+ return res.runtimeEnv
657
+ }
658
+
659
+ /** parse & generate */
660
+ compileFromCode (code: string, filename: string, options: CompilerOptions): NakoGenResult {
661
+ if (filename === '') { filename = 'main.nako3' }
662
+ try {
663
+ if (options.resetEnv) { this.reset() }
664
+ if (options.resetAll) { this.clearPlugins() }
665
+ // onBeforeParse
666
+ this.eventList.filter(o => o.eventName === 'beforeParse').map(e => e.callback(code))
667
+ const ast = this.parse(code, filename, options.preCode)
668
+ // onBeforeGenerate
669
+ this.eventList.filter(o => o.eventName === 'beforeGenerate').map(e => e.callback(ast))
670
+ // generate
671
+ const outCode = this.generateCode(ast, new NakoGenOptions(options.testOnly))
672
+ // onAfterGenerate
673
+ this.eventList.filter(o => o.eventName === 'afterGenerate').map(e => e.callback(outCode))
674
+ return outCode
675
+ } catch (e: any) {
676
+ this.logger.error(e)
677
+ throw e
678
+ }
679
+ }
680
+
681
+ /**
682
+ * プログラムをコンパイルしてJavaScriptのコードオブジェクトを返す
683
+ * @param {AST} ast
684
+ * @param opt テストかどうか
685
+ * @return {Object}
686
+ */
687
+ generateCode (ast: Ast, opt: NakoGenOptions): NakoGenResult {
688
+ // Select Code Generator #637
689
+ switch (ast.genMode) {
690
+ // ノーマルモード
691
+ case 'sync':
692
+ return generateJS(this, ast, opt)
693
+ // 『!非同期モード』は非推奨
694
+ case '非同期モード':
695
+ this.logger.warn('『!非同期モード』の利用は非推奨です。[詳細](https://github.com/kujirahand/nadesiko3/issues/1164)')
696
+ return NakoGenASync.generate(this, ast, opt.isTest)
697
+ default:
698
+ throw new Error(`コードジェネレータの「${ast.genMode}」はサポートされていません。`)
699
+ }
700
+ }
701
+
702
+ /** (非推奨)
703
+ * @param code
704
+ * @param fname
705
+ * @param isReset
706
+ * @param isTest テストかどうか。stringの場合は1つのテストのみ。
707
+ * @param [preCode]
708
+ */
709
+ async _run (code: string, fname: string, isReset: boolean, isTest: boolean, preCode = ''): Promise<NakoGlobal> {
710
+ const opts: CompilerOptions = new CompilerOptions({
711
+ resetEnv: isReset,
712
+ resetAll: isReset,
713
+ testOnly: isTest,
714
+ preCode
715
+ })
716
+ return this._runEx(code, fname, opts)
717
+ }
718
+
719
+ /** 各プラグインをリセットする */
720
+ clearPlugins () {
721
+ // 他に実行している「なでしこ」があればクリアする
722
+ this.__globals.forEach((sys: NakoGlobal) => {
723
+ sys.reset()
724
+ })
725
+ this.__globals = [] // clear
726
+ }
727
+
728
+ /**
729
+ * 環境を指定してJavaScriptのコードを実行する
730
+ * @param code JavaScriptのコード
731
+ * @param nakoGlobal 実行環境
732
+ */
733
+ private evalJS (code: string, nakoGlobal: NakoGlobal): void {
734
+ // 実行前に環境を初期化するイベントを実行(beforeRun)
735
+ this.eventList.filter(o => o.eventName === 'beforeRun').map(e => e.callback(nakoGlobal))
736
+ // eslint-disable-next-line no-new-func
737
+ const f = new Function(code)
738
+ f.apply(nakoGlobal)
739
+ // 実行後に終了イベントを実行(finish)
740
+ this.eventList.filter(o => o.eventName === 'finish').map(e => e.callback(nakoGlobal))
741
+ }
742
+
743
+ /**
744
+ * 同期的になでしこのプログラムcodeを実行する
745
+ * @param code なでしこのプログラム
746
+ * @param filename ファイル名
747
+ * @param options オプション
748
+ * @returns 実行に利用したグローバルオブジェクト
749
+ */
750
+ public runSync (code: string, filename: string, options: CompilerOptions = new CompilerOptions()): NakoGlobal {
751
+ // コンパイル
752
+ const out = this.compileFromCode(code, filename, options)
753
+ // 実行前に環境を生成
754
+ const nakoGlobal = this.getNakoGlobal(options, out.gen)
755
+ // 実行
756
+ this.evalJS(out.runtimeEnv, nakoGlobal)
757
+ return nakoGlobal
758
+ }
759
+
760
+ /**
761
+ * 非同期になでしこのプログラムcodeを実行する
762
+ * @param code なでしこのプログラム
763
+ * @param filename ファイル名
764
+ * @param options オプション
765
+ * @returns 実行に利用したグローバルオブジェクト
766
+ */
767
+ public async runAsync (code: string, filename: string, options: CompilerOptions = new CompilerOptions()): Promise<NakoGlobal> {
768
+ // コンパイル
769
+ const out = this.compileFromCode(code, filename, options)
770
+ // 実行前に環境を生成
771
+ const nakoGlobal = this.getNakoGlobal(options, out.gen)
772
+ // 実行
773
+ this.evalJS(out.runtimeEnv, nakoGlobal)
774
+ return nakoGlobal
775
+ }
776
+
777
+ private getNakoGlobal (options: CompilerOptions, gen: NakoGen): NakoGlobal {
778
+ // オプションを参照
779
+ let g: NakoGlobal|null = options.nakoGlobal
780
+ if (!g) {
781
+ // 空ならば前回の値を参照(リセットするなら新規で作成する)
782
+ if (this.__globals.length > 0 && options.resetAll === false && options.resetEnv === false) {
783
+ g = this.__globals[this.__globals.length - 1]
784
+ } else {
785
+ g = new NakoGlobal(this, gen)
786
+ }
787
+ }
788
+ if (this.__globals.indexOf(g) < 0) { this.__globals.push(g) }
789
+ return g
790
+ }
791
+
792
+ /**
793
+ * イベントを登録する
794
+ * @param eventName イベント名
795
+ * @param callback コールバック関数
796
+ */
797
+ addListener (eventName: NakoComEventName, callback: (event:any) => void) {
798
+ this.eventList.push({ eventName, callback })
799
+ }
800
+
801
+ /**
802
+ * テストを実行する
803
+ * @param code
804
+ * @param fname
805
+ * @param preCode
806
+ * @param testOnly
807
+ */
808
+ test (code: string, fname: string, preCode = '', testOnly = false) {
809
+ const options = new CompilerOptions()
810
+ options.preCode = preCode
811
+ options.testOnly = testOnly
812
+ return this.runSync(code, fname, options)
813
+ }
814
+
815
+ /**
816
+ * なでしこのプログラムを実行(他に実行しているインスタンスはそのまま)
817
+ * @param code
818
+ * @param fname
819
+ * @param [preCode]
820
+ */
821
+ run (code: string, fname = 'main.nako3', preCode = ''): NakoGlobal {
822
+ const options = new CompilerOptions()
823
+ options.preCode = preCode
824
+ return this.runSync(code, fname, options)
825
+ }
826
+
827
+ /**
828
+ * JavaScriptのみで動くコードを取得する場合
829
+ * @param code
830
+ * @param filename
831
+ * @param opt
832
+ */
833
+ compileStandalone (code: string, filename: string, opt: NakoGenOptions|null = null): string {
834
+ if (opt === null) { opt = new NakoGenOptions() }
835
+ const ast = this.parse(code, filename)
836
+ return this.generateCode(ast, opt).standalone
837
+ }
838
+
839
+ /**
840
+ * プラグイン・オブジェクトを追加
841
+ * @param po プラグイン・オブジェクト
842
+ * @param persistent falseのとき、次以降の実行では使えない
843
+ */
844
+ addPlugin (po: {[key: string]: any}, persistent = true): void {
845
+ // 変数のメタ情報を確認
846
+ const __v0 = this.__varslist[0]
847
+ if (__v0.meta === undefined) { __v0.meta = {} }
848
+
849
+ // プラグインの値をオブジェクトにコピー
850
+ for (const key in po) {
851
+ const v = po[key]
852
+ this.funclist[key] = v
853
+ if (persistent) {
854
+ this.pluginFunclist[key] = JSON.parse(JSON.stringify(v))
855
+ }
856
+ if (v.type === 'func') {
857
+ __v0[key] = v.fn
858
+ } else if (v.type === 'const' || v.type === 'var') {
859
+ __v0[key] = v.value
860
+ __v0.meta[key] = {
861
+ readonly: (v.type === 'const')
862
+ }
863
+ } else {
864
+ console.error('[プラグイン追加エラー]', v)
865
+ throw new Error('プラグインの追加でエラー。')
866
+ }
867
+ // コマンドを登録するか?
868
+ if (key === '初期化' || key.substring(0, 1) === '!') { // 登録しない関数名
869
+ continue
870
+ }
871
+ this.commandlist.add(key)
872
+ }
873
+ }
874
+
875
+ /**
876
+ * プラグイン・オブジェクトを追加(ブラウザ向け)
877
+ * @param objName オブジェクト名
878
+ * @param po 関数リスト
879
+ * @param persistent falseのとき、次以降の実行では使えない
880
+ */
881
+ addPluginObject (objName: string, po: {[key: string]: any}, persistent = true): void {
882
+ this.__module[objName] = po
883
+ this.pluginfiles[objName] = '*'
884
+ // 初期化をチェック
885
+ if (typeof (po['初期化']) === 'object') {
886
+ const def = po['初期化']
887
+ delete po['初期化']
888
+ const initKey = `!${objName}:初期化`
889
+ po[initKey] = def
890
+ }
891
+ // メタ情報をチェック (#1034)
892
+ if (po.meta && po.meta.value && typeof (po.meta) === 'object') {
893
+ const meta = po.meta
894
+ delete po.meta
895
+ const pluginName = meta.value.pluginName || objName
896
+ const metaKey = `__${pluginName}`.replace('-', '__')
897
+ po[metaKey] = meta
898
+ }
899
+ this.addPlugin(po, persistent)
900
+ }
901
+
902
+ /**
903
+ * プラグイン・ファイルを追加(Node.js向け)
904
+ * @param objName オブジェクト名
905
+ * @param fpath ファイルパス
906
+ * @param po 登録するオブジェクト
907
+ * @param persistent falseのとき、次以降の実行では使えない
908
+ */
909
+ addPluginFile (objName: string, fpath: string, po: {[key: string]: any}, persistent = true): void {
910
+ // Windowsのパスがあると、JSファイル書き出しでエラーになるので、置換する
911
+ if (objName.indexOf('\\') >= 0) {
912
+ objName = objName.replace(/\\/g, '/')
913
+ }
914
+ this.addPluginObject(objName, po, persistent)
915
+ if (this.pluginfiles[objName] === undefined) {
916
+ this.pluginfiles[objName] = fpath
917
+ }
918
+ }
919
+
920
+ /**
921
+ * 関数を追加する
922
+ * @param {string} key 関数名
923
+ * @param {string[][]} josi 助詞
924
+ * @param {Function} fn 関数
925
+ * @param {boolean} returnNone 値を返す関数の場合はfalseを指定
926
+ * @param {boolean} asyncFn Promiseを返す関数かを指定
927
+ */
928
+ addFunc (key: string, josi: FuncArgs, fn: any, returnNone = true, asyncFn = false): void {
929
+ this.funclist[key] = { josi, fn, type: 'func', return_none: returnNone, asyncFn }
930
+ this.pluginFunclist[key] = cloneAsJSON(this.funclist[key])
931
+ this.__varslist[0][key] = fn
932
+ }
933
+
934
+ // (非推奨) 互換性のため ... 関数を追加する
935
+ public setFunc (key: string, josi: FuncArgs, fn: any, returnNone = true, asyncFn = false): void {
936
+ this.addFunc(key, josi, fn, returnNone, asyncFn)
937
+ }
938
+
939
+ /**
940
+ * プラグイン関数を参照する
941
+ * @param key プラグイン関数の関数名
942
+ * @returns プラグイン・オブジェクト
943
+ */
944
+ getFunc (key: string): FuncListItem {
945
+ return this.funclist[key]
946
+ }
947
+
948
+ /** (非推奨) 同期的になでしこのプログラムcodeを実行する */
949
+ private _runEx (code: string, filename: string, opts: Partial<CompilerOptions>, preCode = '', nakoGlobal: NakoGlobal|undefined = undefined): NakoGlobal {
950
+ // コンパイル
951
+ const options: CompilerOptions = new CompilerOptions(opts)
952
+ options.preCode = preCode
953
+ if (nakoGlobal) { options.nakoGlobal = nakoGlobal }
954
+ return this.runSync(code, filename, options)
955
+ }
956
+
957
+ /** (非推奨) 同期的に実行
958
+ * @param code
959
+ * @param fname
960
+ * @param opts
961
+ * @param [preCode]
962
+ */
963
+ public runEx (code: string, fname: string, opts: Partial<CompilerOptions>, preCode = '') {
964
+ return this._runEx(code, fname, opts, preCode)
965
+ }
966
+
967
+ /**
968
+ * (非推奨) なでしこのプログラムを実行(他に実行しているインスタンスもリセットする)
969
+ * @param code
970
+ * @param fname
971
+ * @param [preCode]
972
+ */
973
+ async runReset (code: string, fname = 'main.nako3', preCode = ''): Promise<NakoGlobal> {
974
+ return this._runEx(code, fname, { resetAll: true, resetEnv: true }, preCode)
975
+ }
976
+ }