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
package/src/nako3.js ADDED
@@ -0,0 +1,836 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * nadesiko v3
4
+ */
5
+ const Parser = require('./nako_parser3')
6
+ const NakoLexer = require('./nako_lexer')
7
+ const Prepare = require('./nako_prepare')
8
+ const NakoGenSync = require('./nako_gen')
9
+ const NakoIndent = require('./nako_indent')
10
+ const PluginSystem = require('./plugin_system')
11
+ const PluginMath = require('./plugin_math')
12
+ const PluginPromise = require('./plugin_promise')
13
+ const PluginTest = require('./plugin_test')
14
+ const { SourceMappingOfTokenization, SourceMappingOfIndentSyntax, OffsetToLineColumn, subtractSourceMapByPreCodeLength } = require('./nako_source_mapping')
15
+ const { NakoRuntimeError, NakoLexerError, NakoImportError, NakoSyntaxError, InternalLexerError } = require('./nako_errors')
16
+ const NakoLogger = require('./nako_logger')
17
+ const NakoGlobal = require('./nako_global')
18
+
19
+ /** @type {<T>(x: T) => T} */
20
+ const cloneAsJSON = (x) => JSON.parse(JSON.stringify(x))
21
+
22
+ // Select Code Generator #637
23
+ /**
24
+ * @type {Object}
25
+ */
26
+ const codeGenerators = {
27
+ // [key: String]
28
+ sync: NakoGenSync
29
+ }
30
+
31
+ /**
32
+ * @param {String | undefined} mode
33
+ */
34
+ function NakoGen (mode) {
35
+ if (codeGenerators[mode]) { return codeGenerators[mode] }
36
+ throw new Error(`コードジェネレータの「${mode}」はサポートされていません。`)
37
+ }
38
+
39
+ /**
40
+ * @typedef {{
41
+ * type: string
42
+ * value: any
43
+ * line: number
44
+ * column: number
45
+ * file: string
46
+ * josi: string
47
+ * meta?: any
48
+ * rawJosi: string
49
+ * startOffset: number | null
50
+ * endOffset: number | null
51
+ * isDefinition?: boolean
52
+ * }} TokenWithSourceMap
53
+ *
54
+ * @typedef {{
55
+ * resetEnv: boolean
56
+ * testOnly: boolean | string
57
+ * resetAll: boolean
58
+ * }} CompilerOptions
59
+ */
60
+
61
+ /**
62
+ * 一部のプロパティのみ。
63
+ * @typedef {{
64
+ * type: string
65
+ * cond?: TokenWithSourceMap | Ast
66
+ * block?: (TokenWithSourceMap | Ast)[] | TokenWithSourceMap | Ast
67
+ * false_block?: TokenWithSourceMap | Ast
68
+ * name?: TokenWithSourceMap | Ast
69
+ * josi?: string
70
+ * value?: unknown
71
+ * line?: number
72
+ * column?: number
73
+ * file?: string
74
+ * startOffset: number | null
75
+ * endOffset: number | null
76
+ * rawJosi?: string
77
+ * vartype?: string
78
+ * index?: Ast[]
79
+ * end?: {
80
+ * startOffset: number | null
81
+ * endOffset: number | null
82
+ * line?: number
83
+ * column?: number
84
+ * }
85
+ * genMode?: string
86
+ * }} Ast
87
+ *
88
+ * @typedef {(
89
+ * | { type: 'func', josi: string[][], pure?: boolean, fn?: Function, return_none: boolean }
90
+ * | { type: 'var' | 'const', value: any}
91
+ * )} NakoFunction
92
+ */
93
+
94
+ class NakoCompiler {
95
+ /**
96
+ * @param {undefined | {'useBasicPlugin':true|false}} options
97
+ */
98
+ constructor (options) {
99
+ if (options === undefined) {
100
+ options = { useBasicPlugin: true }
101
+ }
102
+ this.silent = true
103
+ this.filename = 'inline'
104
+ this.options = options
105
+ // 環境のリセット
106
+ /** @type {Record<string, any>[]} */
107
+ this.__varslist = [{}, {}, {}] // このオブジェクトは変更しないこと (this.gen.__varslist と共有する)
108
+ this.__locals = {} // ローカル変数
109
+ this.__self = this
110
+ this.__vars = this.__varslist[2]
111
+ /**
112
+ * @type {NakoGlobal[]}
113
+ */
114
+ this.__globals = [] // 生成した NakoGlobalのインスタンスを保持
115
+ /** @type {Record<string, Record<string, NakoFunction>>} */
116
+ this.__module = {} // requireなどで取り込んだモジュールの一覧
117
+ /** @type {Record<string, NakoFunction>} */
118
+ this.pluginFunclist = {} // プラグインで定義された関数
119
+ /** @type {Record<string, NakoFunction>} */
120
+ this.funclist = {} // プラグインで定義された関数 + ユーザーが定義した関数
121
+ this.pluginfiles = {} // 取り込んだファイル一覧
122
+ this.isSetter = false // 代入的関数呼び出しを管理(#290)
123
+ this.commandlist = new Set() // プラグインで定義された定数・変数・関数の名前
124
+ /** @type {Record<string, { josi: string[][], fn: string, type: 'func' }>} */
125
+ this.nako_func = {} // __v1に配置するJavaScriptのコードで定義された関数
126
+
127
+ this.logger = new NakoLogger()
128
+
129
+ // 必要なオブジェクトを覚えておく
130
+ this.prepare = new Prepare(this.logger)
131
+ this.parser = new Parser(this.logger)
132
+ this.lexer = new NakoLexer(this.logger)
133
+
134
+ /**
135
+ * 取り込み文を置換するためのオブジェクト。
136
+ * 正規化されたファイル名がキーになり、取り込み文の引数に指定された正規化されていないファイル名はaliasに入れられる。
137
+ * JavaScriptファイルによるプラグインの場合、contentは空文字列。
138
+ * funclistはシンタックスハイライトの高速化のために事前に取り出した、ファイルが定義する関数名のリスト。
139
+ * @type {Record<string, { tokens: TokenWithSourceMap[], alias: Set<string>, addPluginFile: () => void, funclist: Record<string, object> }>}
140
+ */
141
+ this.dependencies = {}
142
+
143
+ /** @type {Set<string>} */
144
+ this.usedFuncs = new Set()
145
+
146
+ this.setFunc = this.addFunc // エイリアス
147
+
148
+ this.numFailures = 0
149
+
150
+ if (options.useBasicPlugin) { this.addBasicPlugins() }
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
+ }
162
+
163
+ /**
164
+ * loggerを新しいインスタンスで置き換える。
165
+ */
166
+ replaceLogger () {
167
+ const logger = this.prepare.logger = this.lexer.logger = this.parser.logger = this.logger = new NakoLogger()
168
+ return logger
169
+ }
170
+
171
+ /**
172
+ * ファイル内のrequire文の位置を列挙する。出力の配列はstartでソートされている。
173
+ * @param {TokenWithSourceMap[]} tokens rawtokenizeの出力
174
+ */
175
+ static listRequireStatements (tokens) {
176
+ /** @type {{ start: number, end: number, value: string, firstToken: TokenWithSourceMap, lastToken: TokenWithSourceMap }[]} */
177
+ const requireStatements = []
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({ start: i, end: i + 3, value: tokens[i + 1].value + '', firstToken: tokens[i], lastToken: tokens[i + 2] })
186
+ i += 2
187
+ }
188
+ return requireStatements
189
+ }
190
+
191
+ /**
192
+ * プログラムが依存するファイルを再帰的に取得する。
193
+ * - .jsであれば評価してthis.addPluginFileを呼び出し、.nako3であればファイルをfetchしてdependenciesに保存し再帰する。
194
+ * - resolvePathはファイルを検索して正規化する必要がある。
195
+ * - needNako3やneedJsがPromiseを返すなら並列処理し、処理の終了を知らせるためのPromiseを返す。そうでなければ同期的に処理する。
196
+ * (`instanceof Promise` はpolyfillで動作しない場合があるため、Promiseかどうかを明示する必要がある。)
197
+ * - readNako3はソースコードを返す。readJsはrequireあるいはevalする関数を返す。
198
+ * @param {string} code
199
+ * @param {string} filename
200
+ * @param {string} preCode
201
+ * @param {{
202
+ * resolvePath: (name: string, token: TokenWithSourceMap) => { type: 'nako3' | 'js' | 'invalid', filePath: string }
203
+ * readNako3: (filePath: string, token: TokenWithSourceMap) => { sync: true, value: string } | { sync: false, value: Promise<string> }
204
+ * readJs: (filePath: string, token: TokenWithSourceMap) => { sync: true, value: () => object } | { sync: false, value: Promise<() => object> }
205
+ * }} tools
206
+ * @returns {Promise<unknown> | void}
207
+ * @protected
208
+ */
209
+ _loadDependencies (code, filename, preCode, tools) {
210
+ /** @type {NakoCompiler['dependencies']} */
211
+ const dependencies = {}
212
+ const compiler = new NakoCompiler({ useBasicPlugin: true })
213
+
214
+ /** @param {string} code @param {string} filename @param {string} preCode @returns {Promise<unknown> | void} */
215
+ const inner = (code, filename, preCode) => {
216
+ /** @type {Promise<unknown>[]} */
217
+ const tasks = []
218
+ for (const item of NakoCompiler.listRequireStatements(compiler.rawtokenize(code, 0, filename, preCode)).map((v) => ({ ...v, ...tools.resolvePath(v.value, v.firstToken) }))) {
219
+ // 2回目以降の読み込み
220
+ // eslint-disable-next-line no-prototype-builtins
221
+ if (dependencies.hasOwnProperty(item.filePath)) {
222
+ dependencies[item.filePath].alias.add(item.value)
223
+ continue
224
+ }
225
+
226
+ // 初回の読み込み
227
+ dependencies[item.filePath] = { tokens: [], alias: new Set([item.value]), addPluginFile: () => {}, funclist: {} }
228
+ if (item.type === 'js') {
229
+ // jsならプラグインとして読み込む。
230
+ const obj = tools.readJs(item.filePath, item.firstToken)
231
+ if (obj.sync) {
232
+ dependencies[item.filePath].addPluginFile = () => { this.addPluginFile(item.value, item.filePath, dependencies[item.filePath].funclist = obj.value(), false) }
233
+ } else {
234
+ tasks.push(obj.value.then((res) => {
235
+ dependencies[item.filePath].addPluginFile = () => { this.addPluginFile(item.value, item.filePath, dependencies[item.filePath].funclist = res(), false) }
236
+ }))
237
+ }
238
+ } else if (item.type === 'nako3') {
239
+ // nako3ならファイルを読んでdependenciesに保存する。
240
+ const content = tools.readNako3(item.filePath, item.firstToken)
241
+ /** @param {string} code */
242
+ const registerFile = (code) => {
243
+ // シンタックスハイライトの高速化のために、事前にファイルが定義する関数名のリストを取り出しておく。
244
+ // preDefineFuncはトークン列に変更を加えるため、事前にクローンしておく。
245
+ // 「プラグイン名設定」を行う (#956)
246
+ code = `「${item.filePath}」にプラグイン名設定;` + code + ';『メイン』にプラグイン名設定;'
247
+ const tokens = this.rawtokenize(code, 0, item.filePath)
248
+ dependencies[item.filePath].tokens = tokens
249
+ /** @type {import('./nako_lexer').FuncList} */
250
+ const funclist = {}
251
+ NakoLexer.preDefineFunc(cloneAsJSON(tokens), this.logger, funclist)
252
+ dependencies[item.filePath].funclist = funclist
253
+
254
+ // 再帰
255
+ return inner(code, item.filePath, '')
256
+ }
257
+ if (content.sync) {
258
+ registerFile(content.value)
259
+ } else {
260
+ tasks.push(content.value.then((res) => registerFile(res)))
261
+ }
262
+ } else {
263
+ throw new NakoImportError(`ファイル ${item.value} を読み込めません。未対応の拡張子です。`, item.firstToken.line, item.firstToken.file)
264
+ }
265
+ }
266
+
267
+ if (tasks.length > 0) {
268
+ return Promise.all(tasks)
269
+ }
270
+ }
271
+
272
+ try {
273
+ const result = inner(code, filename, preCode)
274
+
275
+ // 非同期な場合のエラーハンドリング
276
+ if (result !== undefined) {
277
+ result.catch((err) => { this.logger.error(err); throw err })
278
+ }
279
+
280
+ // すべてが終わってからthis.dependenciesに代入する。そうしないと、「実行」ボタンを連打した場合など、
281
+ // loadDependencies() が並列実行されるときに正しく動作しない。
282
+ this.dependencies = dependencies
283
+ return result
284
+ } catch (err) { // 同期的な場合のエラーハンドリング
285
+ this.logger.error(err)
286
+ throw err
287
+ }
288
+ }
289
+
290
+ /**
291
+ * コードを単語に分割する
292
+ * @param {string} code なでしこのプログラム
293
+ * @param {number} line なでしこのプログラムの行番号
294
+ * @param {string} filename
295
+ * @param {string} [preCode]
296
+ * @returns {TokenWithSourceMap[]} トークンのリスト
297
+ */
298
+ rawtokenize (code, line, filename, preCode = '') {
299
+ if (!code.startsWith(preCode)) {
300
+ throw new Error('codeの先頭にはpreCodeを含める必要があります。')
301
+ }
302
+ // インデント構文 (#596)
303
+ const { code: code2, insertedLines, deletedLines } = NakoIndent.convert(code, filename)
304
+
305
+ // 全角半角の統一処理
306
+ const preprocessed = this.prepare.convert(code2)
307
+
308
+ const tokenizationSourceMapping = new SourceMappingOfTokenization(code2.length, preprocessed)
309
+ const indentationSyntaxSourceMapping = new SourceMappingOfIndentSyntax(code2, insertedLines, deletedLines)
310
+ const offsetToLineColumn = new OffsetToLineColumn(code)
311
+
312
+ // トークン分割
313
+ /** @type {import('./nako_lexer').Token[]} */
314
+ let tokens
315
+ try {
316
+ tokens = this.lexer.setInput(preprocessed.map((v) => v.text).join(''), line, filename)
317
+ } catch (err) {
318
+ if (!(err instanceof InternalLexerError)) {
319
+ throw err
320
+ }
321
+
322
+ // エラー位置をソースコード上の位置に変換して返す
323
+ const dest = indentationSyntaxSourceMapping.map(tokenizationSourceMapping.map(err.preprocessedCodeStartOffset), tokenizationSourceMapping.map(err.preprocessedCodeEndOffset))
324
+ /** @type {number | undefined} */
325
+ const line = dest.startOffset === null ? err.line : offsetToLineColumn.map(dest.startOffset, false).line
326
+ const map = subtractSourceMapByPreCodeLength({ ...dest, line }, preCode)
327
+ throw new NakoLexerError(err.msg, map.startOffset, map.endOffset, map.line, filename)
328
+ }
329
+
330
+ // ソースコード上の位置に変換
331
+ return tokens.map((token, i) => {
332
+ const dest = indentationSyntaxSourceMapping.map(
333
+ tokenizationSourceMapping.map(token.preprocessedCodeOffset),
334
+ tokenizationSourceMapping.map(token.preprocessedCodeOffset + token.preprocessedCodeLength)
335
+ )
336
+ let line = token.line
337
+ let column = 0
338
+ if (token.type === 'eol' && dest.endOffset !== null) {
339
+ // eolはparserで `line = ${eolToken.line};` に変換されるため、
340
+ // 行末のeolのlineは次の行の行数を表す必要がある。
341
+ const out = offsetToLineColumn.map(dest.endOffset, false)
342
+ line = out.line
343
+ column = out.column
344
+ } else if (dest.startOffset !== null) {
345
+ const out = offsetToLineColumn.map(dest.startOffset, false)
346
+ line = out.line
347
+ column = out.column
348
+ }
349
+ return {
350
+ ...token,
351
+ ...subtractSourceMapByPreCodeLength({ line, column, startOffset: dest.startOffset, endOffset: dest.endOffset }, preCode),
352
+ rawJosi: token.josi
353
+ }
354
+ })
355
+ }
356
+
357
+ /**
358
+ * 単語の属性を構文解析に先立ち補正する
359
+ * @param {TokenWithSourceMap[]} tokens トークンのリスト
360
+ * @param {boolean} isFirst 最初の呼び出しかどうか
361
+ * @returns コード (なでしこ)
362
+ */
363
+ converttoken (tokens, isFirst) {
364
+ return this.lexer.setInput2(tokens, isFirst)
365
+ }
366
+
367
+ /**
368
+ * 環境のリセット
369
+ */
370
+ reset () {
371
+ /**
372
+ * なでしこのローカル変数をスタックで管理
373
+ * __varslist[0] プラグイン領域
374
+ * __varslist[1] なでしこグローバル領域
375
+ * __varslist[2] 最初のローカル変数 ( == __vars }
376
+ * @type {Record<string, any>[]}
377
+ */
378
+ this.__varslist = [this.__varslist[0], {}, {}]
379
+ this.__v0 = this.__varslist[0]
380
+ this.__v1 = this.__varslist[1]
381
+ this.__vars = this.__varslist[2]
382
+ this.__locals = {}
383
+
384
+ // プラグイン命令以外を削除する。
385
+ this.funclist = {}
386
+ for (const name of Object.keys(this.__v0)) {
387
+ const original = this.pluginFunclist[name]
388
+ if (!original) {
389
+ continue
390
+ }
391
+ this.funclist[name] = JSON.parse(JSON.stringify(original))
392
+ }
393
+
394
+ this.lexer.setFuncList(this.funclist)
395
+ }
396
+
397
+ /**
398
+ * typeがcodeのトークンを単語に分割するための処理
399
+ * @param {string} code
400
+ * @param {number} line
401
+ * @param {string} filename
402
+ * @param {number | null} startOffset
403
+ * @returns {{ commentTokens: TokenWithSourceMap[], tokens: TokenWithSourceMap[] }}
404
+ * @private
405
+ */
406
+ lexCodeToken (code, line, filename, startOffset) {
407
+ // 単語に分割
408
+ let tokens = this.rawtokenize(code, line, filename, '')
409
+
410
+ // 文字列内位置からファイル内位置へ変換
411
+ if (startOffset === null) {
412
+ for (const token of tokens) {
413
+ token.startOffset = null
414
+ token.endOffset = null
415
+ }
416
+ } else {
417
+ for (const token of tokens) {
418
+ if (token.startOffset !== null) {
419
+ token.startOffset += startOffset
420
+ }
421
+ if (token.endOffset !== null) {
422
+ token.endOffset += startOffset
423
+ }
424
+ }
425
+ }
426
+
427
+ // convertTokenで消されるコメントのトークンを残す
428
+ const commentTokens = tokens.filter((t) => t.type === 'line_comment' || t.type === 'range_comment')
429
+ .map((v) => ({ ...v })) // clone
430
+
431
+ tokens = this.converttoken(tokens, false)
432
+
433
+ return { tokens, commentTokens }
434
+ }
435
+
436
+ /**
437
+ * 再帰的にrequire文を置換する。
438
+ * .jsであれば削除し、.nako3であればそのファイルのトークン列で置換する。
439
+ * @param {TokenWithSourceMap[]} tokens
440
+ * @param {Set<string>} [includeGuard]
441
+ * @returns {TokenWithSourceMap[]} 削除された取り込み文のトークン
442
+ */
443
+ replaceRequireStatements (tokens, includeGuard = new Set()) {
444
+ /** @type {TokenWithSourceMap[]} */
445
+ const deletedTokens = []
446
+ for (const r of NakoCompiler.listRequireStatements(tokens).reverse()) {
447
+ // C言語のinclude guardと同じ仕組みで無限ループを防ぐ。
448
+ if (includeGuard.has(r.value)) {
449
+ deletedTokens.push(...tokens.splice(r.start, r.end - r.start))
450
+ continue
451
+ }
452
+ const filePath = Object.keys(this.dependencies).find((key) => this.dependencies[key].alias.has(r.value))
453
+ if (filePath === undefined) {
454
+ throw new NakoLexerError(`ファイル ${r.value} が読み込まれていません。`, r.firstToken.startOffset, r.firstToken.endOffset, r.firstToken.line, r.firstToken.file)
455
+ }
456
+ this.dependencies[filePath].addPluginFile()
457
+ const children = cloneAsJSON(this.dependencies[filePath].tokens)
458
+ includeGuard.add(r.value)
459
+ deletedTokens.push(...this.replaceRequireStatements(children, includeGuard))
460
+ deletedTokens.push(...tokens.splice(r.start, r.end - r.start, ...children))
461
+ }
462
+ return deletedTokens
463
+ }
464
+
465
+ /**
466
+ * replaceRequireStatementsのシンタックスハイライト用の実装。
467
+ * @param {TokenWithSourceMap[]} tokens
468
+ * @returns {TokenWithSourceMap[]} 削除された取り込み文のトークン
469
+ */
470
+ removeRequireStatements (tokens) {
471
+ /** @type {TokenWithSourceMap[]} */
472
+ const deletedTokens = []
473
+ for (const r of NakoCompiler.listRequireStatements(tokens).reverse()) {
474
+ // プラグイン命令のシンタックスハイライトのために、addPluginFileを呼んで関数のリストをthis.dependencies[filePath].funclistに保存させる。
475
+ const filePath = Object.keys(this.dependencies).find((key) => this.dependencies[key].alias.has(r.value))
476
+ if (filePath !== undefined) {
477
+ this.dependencies[filePath].addPluginFile()
478
+ }
479
+
480
+ // 全ての取り込み文を削除する。そうしないとトークン化に時間がかかりすぎる。
481
+ deletedTokens.push(...tokens.splice(r.start, r.end - r.start))
482
+ }
483
+ return deletedTokens
484
+ }
485
+
486
+ /**
487
+ * @param {string} code
488
+ * @param {string} filename
489
+ * @param {string} [preCode]
490
+ * @returns {{ commentTokens: TokenWithSourceMap[], tokens: TokenWithSourceMap[], requireTokens: TokenWithSourceMap[] }}
491
+ */
492
+ lex (code, filename, preCode = '', syntaxHighlighting = false) {
493
+ // 単語に分割
494
+ let tokens = this.rawtokenize(code, 0, filename, preCode)
495
+
496
+ // require文を再帰的に置換する
497
+ const requireStatementTokens = syntaxHighlighting ? this.removeRequireStatements(tokens) : this.replaceRequireStatements(tokens, undefined)
498
+ for (const t of requireStatementTokens) {
499
+ if (t.type === 'word' || t.type === 'not') {
500
+ t.type = 'require'
501
+ }
502
+ }
503
+
504
+ // convertTokenで消されるコメントのトークンを残す
505
+ /** @type {TokenWithSourceMap[]} */
506
+ const commentTokens = tokens.filter((t) => t.type === 'line_comment' || t.type === 'range_comment')
507
+ .map((v) => ({ ...v })) // clone
508
+
509
+ tokens = this.converttoken(tokens, true)
510
+
511
+ for (let i = 0; i < tokens.length; i++) {
512
+ if (tokens[i].type === 'code') {
513
+ const children = this.lexCodeToken(tokens[i].value, tokens[i].line, filename, tokens[i].startOffset)
514
+ commentTokens.push(...children.commentTokens)
515
+ tokens.splice(i, 1, ...children.tokens)
516
+ i--
517
+ }
518
+ }
519
+
520
+ this.logger.trace('--- lex ---\n' + JSON.stringify(tokens, null, 2))
521
+
522
+ return { commentTokens, tokens, requireTokens: requireStatementTokens }
523
+ }
524
+
525
+ /**
526
+ * コードをパースしてASTにする
527
+ * @param {string} code なでしこのプログラム
528
+ * @param {string} filename
529
+ * @param {string} [preCode]
530
+ * @return {Ast}
531
+ */
532
+ parse (code, filename, preCode = '') {
533
+ // 関数を字句解析と構文解析に登録
534
+ this.lexer.setFuncList(this.funclist)
535
+ this.parser.setFuncList(this.funclist)
536
+
537
+ const lexerOutput = this.lex(code, filename, preCode)
538
+
539
+ // 構文木を作成
540
+ /** @type {Ast} */
541
+ let ast
542
+ try {
543
+ ast = this.parser.parse(lexerOutput.tokens)
544
+ } catch (err) {
545
+ if (typeof err.startOffset !== 'number') {
546
+ throw NakoSyntaxError.fromNode(err.message, lexerOutput.tokens[this.parser.index])
547
+ }
548
+ throw err
549
+ }
550
+ this.usedFuncs = this.getUsedFuncs(ast)
551
+ this.logger.trace('--- ast ---\n' + JSON.stringify(ast, null, 2))
552
+ return ast
553
+ }
554
+
555
+ /**
556
+ * @param {Ast} ast
557
+ */
558
+ getUsedFuncs (ast) {
559
+ const queue = [ast]
560
+ this.usedFuncs = new Set()
561
+
562
+ while (queue.length > 0) {
563
+ const ast_ = queue.pop()
564
+
565
+ if (ast_ !== null && ast_ !== undefined && ast_.block !== null && ast_.block !== undefined) {
566
+ this.getUsedAndDefFuncs(queue, JSON.parse(JSON.stringify(ast_.block)))
567
+ }
568
+ }
569
+
570
+ return this.deleteUnNakoFuncs()
571
+ }
572
+
573
+ getUsedAndDefFuncs (astQueue, blockQueue) {
574
+ while (blockQueue.length > 0) {
575
+ const block = blockQueue.pop()
576
+
577
+ if (block !== null && block !== undefined) {
578
+ this.getUsedAndDefFunc(block, astQueue, blockQueue)
579
+ }
580
+ }
581
+ }
582
+
583
+ getUsedAndDefFunc (block, astQueue, blockQueue) {
584
+ if (['func', 'func_pointer'].includes(block.type) && block.name !== null && block.name !== undefined) {
585
+ this.usedFuncs.add(block.name)
586
+ }
587
+
588
+ // eslint-disable-next-line no-useless-call
589
+ astQueue.push.apply(astQueue, [block, block.block])
590
+ blockQueue.push.apply(blockQueue, [block.value].concat(block.args))
591
+ }
592
+
593
+ deleteUnNakoFuncs () {
594
+ for (const func of this.usedFuncs) {
595
+ if (!this.commandlist.has(func)) {
596
+ this.usedFuncs.delete(func)
597
+ }
598
+ }
599
+
600
+ return this.usedFuncs
601
+ }
602
+
603
+ /**
604
+ * プログラムをコンパイルしてJavaScriptのコードを返す
605
+ * @param {string} code コード (なでしこ)
606
+ * @param {string} filename
607
+ * @param {boolean | string} isTest テストかどうか。stringの場合は1つのテストのみ。
608
+ * @param {string} [preCode]
609
+ */
610
+ compile (code, filename, isTest, preCode = '') {
611
+ const ast = this.parse(code, filename, preCode)
612
+ return NakoGen(ast.genMode).generate(this, ast, isTest).runtimeEnv
613
+ }
614
+
615
+ /**
616
+ * @param {string} code
617
+ * @param {string} fname
618
+ * @param {boolean} isReset
619
+ * @param {boolean | string} isTest テストかどうか。stringの場合は1つのテストのみ。
620
+ * @param {string} [preCode]
621
+ */
622
+ _run (code, fname, isReset, isTest, preCode = '') {
623
+ const opts = {
624
+ resetLog: isReset,
625
+ testOnly: isTest
626
+ }
627
+ return this._runEx(code, fname, opts, preCode)
628
+ }
629
+
630
+ clearPlugins () {
631
+ // 他に実行している「なでしこ」があればクリアする
632
+ this.__globals.forEach((sys) => {
633
+ sys.reset()
634
+ })
635
+ }
636
+
637
+ /**
638
+ * @param {string} code
639
+ * @param {string} fname
640
+ * @param {Partial<CompilerOptions>} opts
641
+ * @param {string} [preCode]
642
+ * @param {NakoGlobal} [nakoGlobal] ナデシコ命令でスコープを共有するため
643
+ */
644
+ _runEx (code, fname, opts, preCode = '', nakoGlobal) {
645
+ // コンパイル
646
+ let out
647
+ try {
648
+ const optsAll = Object.assign({ resetEnv: true, testOnly: false, resetAll: true }, opts)
649
+ if (optsAll.resetEnv) { this.reset() }
650
+ if (optsAll.resetAll) { this.clearPlugins() }
651
+ const ast = this.parse(code, fname, preCode)
652
+ out = NakoGen(ast.genMode).generate(this, ast, optsAll.testOnly)
653
+ } catch (e) {
654
+ this.logger.error(e)
655
+ throw e
656
+ }
657
+ // 実行
658
+ nakoGlobal = nakoGlobal || new NakoGlobal(this, out.gen)
659
+ if (this.__globals.indexOf(nakoGlobal) < 0) {
660
+ this.__globals.push(nakoGlobal)
661
+ }
662
+ try {
663
+ // eslint-disable-next-line no-new-func
664
+ new Function(out.runtimeEnv).apply(nakoGlobal)
665
+ return nakoGlobal
666
+ } catch (e) {
667
+ let err = e
668
+ if (!(e instanceof NakoRuntimeError)) {
669
+ err = new NakoRuntimeError(e, nakoGlobal.__varslist[0].line)
670
+ }
671
+ this.logger.error(err)
672
+ throw err
673
+ }
674
+ }
675
+
676
+ /**
677
+ * @param {string} code
678
+ * @param {string} fname
679
+ * @param {Partial<CompilerOptions>} opts
680
+ * @param {string} [preCode]
681
+ */
682
+ runEx (code, fname, opts, preCode = '') {
683
+ return this._runEx(code, fname, opts, preCode)
684
+ }
685
+
686
+ /**
687
+ * @param {string} code
688
+ * @param {string} fname
689
+ * @param {string} [preCode]
690
+ * @param {string | undefined} [testName]
691
+ */
692
+ test (code, fname, preCode = '', testName = undefined) {
693
+ return this._runEx(code, fname, { testOnly: testName || true }, preCode)
694
+ }
695
+
696
+ /**
697
+ * なでしこのプログラムを実行(他に実行しているインスタンスはそのまま)
698
+ * @param {string} code
699
+ * @param {string} fname
700
+ * @param {string} [preCode]
701
+ */
702
+ run (code, fname, preCode = '') {
703
+ return this._runEx(code, fname, { resetAll: false }, preCode)
704
+ }
705
+
706
+ /**
707
+ * なでしこのプログラムを実行(他に実行しているインスタンスもリセットする)
708
+ * @param {string} code
709
+ * @param {string} fname
710
+ * @param {string} [preCode]
711
+ */
712
+ runReset (code, fname, preCode = '') {
713
+ return this._runEx(code, fname, { resetAll: true }, preCode)
714
+ }
715
+
716
+ /**
717
+ * JavaScriptのみで動くコードを取得する場合
718
+ * @param {string} code
719
+ * @param {string} filename
720
+ * @param {boolean | string} isTest
721
+ * @param {string} [preCode]
722
+ */
723
+ compileStandalone (code, filename, isTest, preCode = '') {
724
+ const ast = this.parse(code, filename, preCode)
725
+ return NakoGen(ast.genMode).generate(this, ast, isTest).standalone
726
+ }
727
+
728
+ /**
729
+ * プラグイン・オブジェクトを追加
730
+ * @param po プラグイン・オブジェクト
731
+ * @param {boolean} [persistent] falseのとき、次以降の実行では使えない
732
+ */
733
+ addPlugin (po, persistent = true) {
734
+ // 変数のメタ情報を確認
735
+ const __v0 = this.__varslist[0]
736
+ if (__v0.meta === undefined) { __v0.meta = {} }
737
+
738
+ // プラグインの値をオブジェクトにコピー
739
+ for (const key in po) {
740
+ const v = po[key]
741
+ this.funclist[key] = v
742
+ if (persistent) {
743
+ this.pluginFunclist[key] = JSON.parse(JSON.stringify(v))
744
+ }
745
+ if (v.type === 'func') {
746
+ __v0[key] = v.fn
747
+ } else if (v.type === 'const' || v.type === 'var') {
748
+ __v0[key] = v.value
749
+ __v0.meta[key] = {
750
+ readonly: (v.type === 'const')
751
+ }
752
+ } else {
753
+ throw new Error('プラグインの追加でエラー。')
754
+ }
755
+ // コマンドを登録するか?
756
+ if (key === '初期化' || key.substr(0, 1) === '!') { // 登録しない関数名
757
+ continue
758
+ }
759
+ this.commandlist.add(key)
760
+ }
761
+ }
762
+
763
+ /**
764
+ * プラグイン・オブジェクトを追加(ブラウザ向け)
765
+ * @param objName オブジェクト名
766
+ * @param po 関数リスト
767
+ * @param {boolean} [persistent] falseのとき、次以降の実行では使えない
768
+ */
769
+ addPluginObject (objName, po, persistent = true) {
770
+ this.__module[objName] = po
771
+ this.pluginfiles[objName] = '*'
772
+ // 初期化をチェック
773
+ if (typeof (po['初期化']) === 'object') {
774
+ const def = po['初期化']
775
+ delete po['初期化']
776
+ const initKey = `!${objName}:初期化`
777
+ po[initKey] = def
778
+ }
779
+ // メタ情報をチェック (#1034)
780
+ if (po.meta && po.meta.value && typeof (po.meta) === 'object') {
781
+ const meta = po.meta
782
+ delete po.meta
783
+ const pluginName = meta.value.pluginName || objName
784
+ const metaKey = `__${pluginName}`.replace('-', '__')
785
+ po[metaKey] = meta
786
+ }
787
+ this.addPlugin(po, persistent)
788
+ }
789
+
790
+ /**
791
+ * プラグイン・ファイルを追加(Node.js向け)
792
+ * @param {string} objName オブジェクト名
793
+ * @param {string} fpath ファイルパス
794
+ * @param po 登録するオブジェクト
795
+ * @param {boolean} [persistent] falseのとき、次以降の実行では使えない
796
+ */
797
+ addPluginFile (objName, fpath, po, persistent = true) {
798
+ this.addPluginObject(objName, po, persistent)
799
+ if (this.pluginfiles[objName] === undefined) {
800
+ this.pluginfiles[objName] = fpath
801
+ }
802
+ }
803
+
804
+ /**
805
+ * 関数を追加する
806
+ * @param {string} key 関数名
807
+ * @param {string[][]} josi 助詞
808
+ * @param {Function} fn 関数
809
+ * @param {boolean} returnNone 値を返す関数の場合はfalseを設定する。
810
+ */
811
+ addFunc (key, josi, fn, returnNone = true) {
812
+ this.funclist[key] = { josi, fn, type: 'func', return_none: returnNone }
813
+ this.pluginFunclist[key] = cloneAsJSON(this.funclist[key])
814
+ this.__varslist[0][key] = fn
815
+ }
816
+
817
+ /**
818
+ * プラグイン関数を参照する
819
+ * @param {string} key プラグイン関数の関数名
820
+ * @returns {NakoFunction} プラグイン・オブジェクト
821
+ */
822
+ getFunc (key) {
823
+ return this.funclist[key]
824
+ }
825
+
826
+ /**
827
+ * コードジェネレータを追加する
828
+ * @param {string} mode
829
+ * @param {any} obj
830
+ */
831
+ addCodeGenerator (mode, obj) {
832
+ codeGenerators[mode] = obj
833
+ }
834
+ }
835
+
836
+ module.exports = NakoCompiler