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,612 +1,612 @@
1
- // なでしこの字句解析を行う
2
- // 既に全角半角を揃えたコードに対して字句解析を行う
3
- import { opPriority } from './nako_parser_const.mjs'
4
-
5
- // 予約語句
6
- // (memo)「回」「間」「繰返」「反復」「抜」「続」「戻」「代入」などは _replaceWord で word から変換
7
- /** @types {Record<string, string>} */
8
- import reservedWords from './nako_reserved_words.mjs'
9
- import { NakoLogger } from './nako_logger.mjs'
10
-
11
- // 助詞の一覧
12
- import { josiRE, removeJosiMap, tararebaMap } from './nako_josi_list.mjs'
13
-
14
- // 字句解析ルールの一覧
15
- import { rules, unitRE } from './nako_lex_rules.mjs'
16
- import { NakoLexerError, InternalLexerError } from './nako_errors.mjs'
17
-
18
- import { Token, FuncList, FuncArgs } from './nako_types.mjs'
19
-
20
- export class NakoLexer {
21
- public logger: NakoLogger;
22
- public funclist: FuncList;
23
- public modList: string[];
24
- public result: Token[];
25
- public modName: string;
26
- /**
27
- * @param logger
28
- */
29
- constructor (logger: NakoLogger) {
30
- this.logger = logger // 字句解析した際,確認された関数の一覧
31
- this.funclist = {}
32
- this.modList = [] // 字句解析した際,取り込むモジュール一覧 --- nako3::lex で更新される
33
- this.result = []
34
- this.modName = 'main.nako3' // モジュール名
35
- }
36
-
37
- /** 関数一覧をセット */
38
- setFuncList (listObj: FuncList) {
39
- this.funclist = listObj
40
- }
41
-
42
- /**
43
- * @param tokens
44
- * @param {boolean} isFirst
45
- * @param {string} filename
46
- */
47
- replaceTokens (tokens: Token[], isFirst: boolean, filename: string) {
48
- this.result = tokens
49
- this.modName = NakoLexer.filenameToModName(filename)
50
- // 関数の定義があれば funclist を更新
51
- NakoLexer.preDefineFunc(tokens, this.logger, this.funclist)
52
- this._replaceWord(this.result)
53
-
54
- if (isFirst) {
55
- if (this.result.length > 0) {
56
- const eof = this.result[this.result.length - 1]
57
- this.result.push({
58
- type: 'eol',
59
- line: eof.line,
60
- column: 0,
61
- file: eof.file,
62
- josi: '',
63
- value: '---',
64
- startOffset: eof.startOffset,
65
- endOffset: eof.endOffset,
66
- rawJosi: ''
67
- }) // 改行
68
- this.result.push({
69
- type: 'eof',
70
- line: eof.line,
71
- column: 0,
72
- file: eof.file,
73
- josi: '',
74
- value: '',
75
- startOffset: eof.startOffset,
76
- endOffset: eof.endOffset,
77
- rawJosi: ''
78
- }) // ファイル末尾
79
- } else {
80
- this.result.push({
81
- type: 'eol',
82
- line: 0,
83
- column: 0,
84
- file: '',
85
- josi: '',
86
- value: '---',
87
- startOffset: 0,
88
- endOffset: 0,
89
- rawJosi: ''
90
- }) // 改行
91
- this.result.push({
92
- type: 'eof',
93
- line: 0,
94
- column: 0,
95
- file: '',
96
- josi: '',
97
- value: '',
98
- startOffset: 0,
99
- endOffset: 0,
100
- rawJosi: ''
101
- }) // ファイル末尾
102
- }
103
- }
104
- return this.result
105
- }
106
-
107
- /**
108
- * ファイル内で定義されている関数名を列挙する。結果はfunclistに書き込む。その他のトークンの置換処理も行う。
109
- * シンタックスハイライトの処理から呼び出すためにstaticメソッドにしている。
110
- * @param {Token[]} tokens
111
- * @param {import('./nako_logger.mjs').NakoLogger} logger
112
- * @param {FuncList} funclist
113
- */
114
- static preDefineFunc (tokens: Token[], logger: NakoLogger, funclist: FuncList) {
115
- // 関数を先読みして定義
116
- let i = 0
117
- let isFuncPointer = false
118
- const readArgs = () => {
119
- const args: Token[] = []
120
- const keys:{[key:string]: string[]} = {}
121
- if (tokens[i].type !== '(') { return [] }
122
- i++
123
- while (tokens[i]) {
124
- const t = tokens[i]
125
- i++
126
- if (t.type === ')') { break }
127
- if (t.type === 'func') { isFuncPointer = true } else if (t.type !== '|' && t.type !== 'comma') {
128
- if (isFuncPointer) {
129
- t.funcPointer = true
130
- isFuncPointer = false
131
- }
132
- args.push(t)
133
- if (!keys[t.value]) { keys[t.value] = [] }
134
-
135
- keys[t.value].push(t.josi)
136
- }
137
- }
138
- const varnames: string[] = []
139
- const funcPointers: any[] = []
140
- const result: FuncArgs = []
141
- const already: {[key: string]: boolean} = {}
142
- for (const arg of args) {
143
- if (!already[arg.value]) {
144
- const josi = keys[arg.value]
145
- result.push(josi)
146
- varnames.push(arg.value)
147
- if (arg.funcPointer) { funcPointers.push(arg.value) } else { funcPointers.push(null) }
148
-
149
- already[arg.value] = true
150
- }
151
- }
152
- return [result, varnames, funcPointers]
153
- }
154
- // トークンを一つずつ確認
155
- while (i < tokens.length) {
156
- // タイプの置換
157
- const t = tokens[i]
158
- // 無名関数の定義:「xxには**」があった場合 ... 暗黙的な関数定義とする
159
- if ((t.type === 'word' && t.josi === 'には') || (t.type === 'word' && t.josi === 'は~')) {
160
- t.josi = 'には'
161
- tokens.splice(i + 1, 0, { type: 'def_func', value: '関数', line: t.line, column: t.column, file: t.file, josi: '', startOffset: t.endOffset, endOffset: t.endOffset, rawJosi: '', tag: '無名関数' })
162
- i++
163
- continue
164
- }
165
- // N回をN|回に置換
166
- if (t.type === 'word' && t.josi === '' && t.value.length >= 2) {
167
- if (t.value.match(/回$/)) {
168
- t.value = t.value.substring(0, t.value.length - 1)
169
- // N回を挿入
170
- if (!t.endOffset) { t.endOffset = 1 }
171
- const kai = { type: '回', value: '回', line: t.line, column: t.column, file: t.file, josi: '', startOffset: t.endOffset - 1, endOffset: t.endOffset, rawJosi: '' }
172
- tokens.splice(i + 1, 0, kai)
173
- t.endOffset--
174
- i++
175
- }
176
- }
177
- // 予約語の置換
178
- if (t.type === 'word' && reservedWords[t.value]) {
179
- t.type = reservedWords[t.value]
180
- if (t.value === 'そう') { t.value = 'それ' }
181
- }
182
- // 関数定義の確認
183
- if (t.type !== 'def_test' && t.type !== 'def_func') {
184
- i++
185
- continue
186
- }
187
- // 無名関数か普通関数定義かを判定する (1つ前が改行かどうかで判定)
188
- let isMumei = true
189
- let prevToken = { type: 'eol' }
190
- if (i >= 1) { prevToken = tokens[i - 1] }
191
- if (prevToken.type === 'eol') { isMumei = false }
192
- // 関数名や引数を得る
193
- const defToken = t
194
- i++ // skip "●" or "関数"
195
- let josi = []
196
- let varnames = []
197
- let funcPointers = []
198
- let funcName = ''
199
- let funcNameToken: Token | null = null
200
- // 関数名の前に引数定義
201
- if (tokens[i] && tokens[i].type === '(') { [josi, varnames, funcPointers] = readArgs() }
202
-
203
- // 関数名を得る
204
- if (!isMumei && tokens[i] && tokens[i].type === 'word') {
205
- funcNameToken = tokens[i++]
206
- funcName = funcNameToken.value
207
- }
208
-
209
- // 関数名の後で引数定義
210
- if (josi.length === 0 && tokens[i] && tokens[i].type === '(') { [josi, varnames, funcPointers] = readArgs() }
211
-
212
- // 名前のある関数定義ならば関数テーブルに関数名を登録
213
- // 無名関数は登録しないように気をつける
214
- if (funcName !== '' && funcNameToken) {
215
- const modName = NakoLexer.filenameToModName(t.file)
216
- funcName = modName + '__' + funcName
217
- if (funcName in funclist) { // 関数の二重定義を警告
218
- // main__は省略 #1223
219
- const dispName = funcName.replace(/^main__/, '')
220
- logger.warn(`関数『${dispName}』は既に定義されています。`, defToken)
221
- }
222
- funcNameToken.value = funcName
223
- funclist[funcName] = {
224
- type: 'func',
225
- josi,
226
- fn: null,
227
- asyncFn: false,
228
- varnames,
229
- funcPointers
230
- }
231
- }
232
- // 無名関数のために
233
- defToken.meta = {
234
- type: 'func',
235
- josi,
236
- varnames,
237
- funcPointers
238
- }
239
- }
240
- }
241
-
242
- /** 文字列を{と}の部分で分割する。中括弧が対応していない場合nullを返す。 */
243
- splitStringEx (code: string): string[] | null {
244
- /** @type {string[]} */
245
- const list = []
246
-
247
- // "A{B}C{D}E" -> ["A", "B}C", "D}E"] -> ["A", "B", "C", "D", "E"]
248
- // "A{B}C}D{E}F" -> ["A", "B}C}D", "E}F"] -> ["A", "B", "C}D", "E", "F"]
249
- const arr = code.split(/[{{]/)
250
- list.push(arr[0])
251
- for (const s of arr.slice(1)) {
252
- const end = s.replace('}', '}').indexOf('}')
253
- if (end === -1) {
254
- return null
255
- }
256
- list.push(s.slice(0, end), s.slice(end + 1))
257
- }
258
-
259
- return list
260
- }
261
-
262
- _replaceWord (tokens: Token[]): void {
263
- let comment = []
264
- let i = 0
265
- const getLastType = () => {
266
- if (i <= 0) { return 'eol' }
267
- return tokens[i - 1].type
268
- }
269
- const modSelf = (tokens.length > 0) ? NakoLexer.filenameToModName(tokens[0].file) : 'main.nako3'
270
- while (i < tokens.length) {
271
- const t = tokens[i]
272
- // 関数を強制的に置換( word => func )
273
- if (t.type === 'word' && t.value !== 'それ') {
274
- // 関数を変換
275
- const funcName = t.value
276
- if (funcName.indexOf('__') < 0) {
277
- // 自身のモジュール名を検索
278
- const gname1 = `${modSelf}__${funcName}`
279
- const gfo1 = this.funclist[gname1]
280
- if (gfo1 && gfo1.type === 'func') {
281
- t.type = 'func'
282
- t.meta = gfo1
283
- t.value = gname1
284
- continue
285
- }
286
- // モジュール関数を置換
287
- for (const mod of this.modList) {
288
- const gname = `${mod}__${funcName}`
289
- const gfo = this.funclist[gname]
290
- if (gfo && gfo.type === 'func') {
291
- t.type = 'func'
292
- t.meta = gfo
293
- t.value = gname
294
- break
295
- }
296
- }
297
- if (t.type === 'func') { continue }
298
- }
299
- const fo = this.funclist[funcName]
300
- if (fo && fo.type === 'func') {
301
- t.type = 'func'
302
- t.meta = fo
303
- }
304
- }
305
- // 数字につくマイナス記号を判定
306
- // (ng) 5 - 3 || word - 3
307
- // (ok) (行頭)-3 || 1 * -3 || Aに -3を 足す
308
- if (t.type === '-' && tokens[i + 1] && tokens[i + 1].type === 'number') {
309
- // 一つ前の語句が、(行頭|演算子|助詞付きの語句)なら 負数である
310
- const ltype = getLastType()
311
- if (ltype === 'eol' || opPriority[ltype] || tokens[i - 1].josi !== '') {
312
- tokens.splice(i, 1) // remove '-'
313
- tokens[i].value *= -1
314
- }
315
- }
316
- // 助詞の「は」を = に展開
317
- if (t.josi === undefined) { t.josi = '' }
318
- if (t.josi === 'は') {
319
- if (!t.rawJosi) { t.rawJosi = t.josi }
320
- const startOffset = (t.endOffset === undefined) ? undefined : t.endOffset - t.rawJosi.length
321
- tokens.splice(i + 1, 0, {
322
- type: 'eq',
323
- line: t.line,
324
- column: t.column,
325
- file: t.file,
326
- startOffset,
327
- endOffset: t.endOffset,
328
- josi: '',
329
- rawJosi: '',
330
- value: undefined
331
- })
332
- i += 2
333
- t.josi = t.rawJosi = ''
334
- t.endOffset = startOffset
335
- continue
336
- }
337
- // 「とは」を一つの単語にする
338
- if (t.josi === 'とは') {
339
- if (!t.rawJosi) { t.rawJosi = t.josi }
340
- const startOffset = t.endOffset === undefined ? undefined : t.endOffset - t.rawJosi.length
341
- tokens.splice(i + 1, 0, {
342
- type: t.josi,
343
- line: t.line,
344
- column: t.column,
345
- file: t.file,
346
- startOffset,
347
- endOffset: t.endOffset,
348
- josi: '',
349
- rawJosi: '',
350
- value: undefined
351
- })
352
- t.josi = t.rawJosi = ''
353
- t.endOffset = startOffset
354
- i += 2
355
- continue
356
- }
357
- // 助詞のならばをトークンとする
358
- if (tararebaMap[t.josi]) {
359
- const josi = (t.josi === 'でなければ' || t.josi === 'なければ') ? 'でなければ' : 'ならば'
360
- if (!t.rawJosi) { t.rawJosi = josi }
361
- const startOffset = t.endOffset === undefined ? undefined : t.endOffset - t.rawJosi.length
362
- tokens.splice(i + 1, 0, {
363
- type: 'ならば',
364
- value: josi,
365
- line: t.line,
366
- column: t.column,
367
- file: t.file,
368
- startOffset,
369
- endOffset: t.endOffset,
370
- josi: '',
371
- rawJosi: ''
372
- })
373
- t.josi = t.rawJosi = ''
374
- t.endOffset = startOffset
375
- i += 2
376
- continue
377
- }
378
- // '_' + 改行 を飛ばす (演算子直後に改行を入れたい場合に使う)
379
- if (t.type === '_eol') {
380
- tokens.splice(i, 1)
381
- continue
382
- }
383
- // コメントを飛ばす
384
- if (t.type === 'line_comment' || t.type === 'range_comment') {
385
- comment.push(t.value)
386
- tokens.splice(i, 1)
387
- continue
388
- }
389
- // 改行にコメントを埋め込む
390
- if (t.type === 'eol') {
391
- t.value = comment.join('/')
392
- comment = []
393
- }
394
- i++
395
- }
396
- }
397
-
398
- /**
399
- * @param {string} src
400
- * @param {number} line
401
- * @param {string} filename
402
- * @returns {Token[]}
403
- */
404
- tokenize (src: string, line: number, filename: string): Token[] {
405
- const srcLength: number = src.length
406
- const result: Token[] = []
407
- let columnCurrent
408
- let lineCurrent
409
- let column = 1
410
- let isDefTest = false
411
- while (src !== '') {
412
- let ok = false
413
- // 各ルールについて
414
- for (const rule of rules) {
415
- // 正規表現でマッチ
416
- const m = rule.pattern.exec(src)
417
- if (!m) { continue }
418
- ok = true
419
- // 空白ならスキップ
420
- if (rule.name === 'space') {
421
- column += m[0].length
422
- src = src.substring(m[0].length)
423
- continue
424
- }
425
- // マッチしたルールがコールバックを持つなら
426
- if (rule.cbParser) {
427
- // コールバックを呼ぶ
428
- /** @type {{ src: string, res: string, josi: string, numEOL: number }} */
429
- let rp
430
-
431
- if (isDefTest && rule.name === 'word') {
432
- rp = rule.cbParser(src, false)
433
- } else {
434
- try {
435
- rp = rule.cbParser(src)
436
- } catch (e: any) {
437
- throw new NakoLexerError(
438
- e.message,
439
- srcLength - src.length,
440
- srcLength - src.length + 1,
441
- line,
442
- filename
443
- )
444
- }
445
- }
446
-
447
- if (rule.name === 'string_ex') {
448
- // 展開あり文字列 → aaa{x}bbb{x}cccc
449
- const list = this.splitStringEx(rp.res)
450
- if (list === null) {
451
- throw new InternalLexerError(
452
- '展開あり文字列で値の埋め込み{...}が対応していません。',
453
- srcLength - src.length,
454
- srcLength - rp.src.length,
455
- line,
456
- filename
457
- )
458
- }
459
- let offset = 0
460
- for (let i = 0; i < list.length; i++) {
461
- const josi = (i === list.length - 1) ? rp.josi : ''
462
- if (i % 2 === 0) {
463
- result.push({
464
- type: 'string',
465
- value: list[i],
466
- file: filename,
467
- josi,
468
- line,
469
- column,
470
- preprocessedCodeOffset: srcLength - src.length + offset,
471
- preprocessedCodeLength: list[i].length + 2 + josi.length
472
- })
473
- // 先頭なら'"...{'、それ以外なら'}...{'、最後は何でも良い
474
- offset += list[i].length + 2
475
- } else {
476
- result.push({ type: '&', value: '&', josi: '', file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset, preprocessedCodeLength: 0 })
477
- result.push({ type: '(', value: '(', josi: '', file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset, preprocessedCodeLength: 0 })
478
- result.push({ type: 'code', value: list[i], josi: '', file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset, preprocessedCodeLength: list[i].length })
479
- result.push({ type: ')', value: ')', josi: '', file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset + list[i].length, preprocessedCodeLength: 0 })
480
- result.push({ type: '&', value: '&', josi: '', file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset + list[i].length, preprocessedCodeLength: 0 })
481
- offset += list[i].length
482
- }
483
- }
484
- line += rp.numEOL
485
- column += src.length - rp.src.length
486
- src = rp.src
487
- if (rp.numEOL > 0) {
488
- column = 1
489
- }
490
- break
491
- }
492
- columnCurrent = column
493
- column += src.length - rp.src.length
494
- result.push({ type: rule.name, value: rp.res, josi: rp.josi, line, column: columnCurrent, file: filename, preprocessedCodeOffset: srcLength - src.length, preprocessedCodeLength: src.length - rp.src.length })
495
- src = rp.src
496
- line += rp.numEOL
497
- if (rp.numEOL > 0) {
498
- column = 1
499
- }
500
- break
501
- }
502
-
503
- // ソースを進める前に位置を計算
504
- const srcOffset = srcLength - src.length
505
-
506
- // 値を変換する必要があるか?
507
- let value: any = m[0]
508
- if (rule.cb) { value = rule.cb(value) }
509
- // ソースを進める
510
- columnCurrent = column
511
- lineCurrent = line
512
- column += m[0].length
513
- src = src.substring(m[0].length)
514
- if ((rule.name === 'eol' && value === '\n') || rule.name === '_eol') {
515
- value = line++
516
- column = 1
517
- }
518
-
519
- // 数値なら単位を持つか? --- #994
520
- if (rule.name === 'number') {
521
- // 単位があれば読み飛ばす
522
- const um = unitRE.exec(src)
523
- if (um) {
524
- src = src.substring(um[0].length)
525
- column += m[0].length
526
- }
527
- }
528
-
529
- let josi = ''
530
- if (rule.readJosi) {
531
- const j = josiRE.exec(src)
532
- if (j) {
533
- josi = j[0].replace(/^\s+/, '')
534
- column += j[0].length
535
- src = src.substring(j[0].length)
536
- // 助詞の直後にあるカンマを無視 #877
537
- if (src.charAt(0) === ',') {
538
- src = src.substring(1)
539
- }
540
- // 「**である」なら削除 #939 #974
541
- if (removeJosiMap[josi]) { josi = '' }
542
- }
543
- }
544
-
545
- switch (rule.name) {
546
- case 'def_test': {
547
- isDefTest = true
548
- break
549
- }
550
- case 'eol': {
551
- isDefTest = false
552
- break
553
- }
554
- default: {
555
- break
556
- }
557
- }
558
- // ここまで‰(#682) を処理
559
- if (rule.name === 'dec_lineno') {
560
- line--
561
- continue
562
- }
563
-
564
- result.push({
565
- type: rule.name,
566
- value,
567
- line: lineCurrent,
568
- column: columnCurrent,
569
- file: filename,
570
- josi,
571
- preprocessedCodeOffset: srcOffset,
572
- preprocessedCodeLength: (srcLength - src.length) - srcOffset
573
- })
574
- break
575
- }
576
- if (!ok) {
577
- throw new InternalLexerError('未知の語句: ' + src.substring(0, 3) + '...',
578
- srcLength - src.length,
579
- srcLength - srcLength + 3,
580
- line,
581
- filename
582
- )
583
- }
584
- }
585
- return result
586
- }
587
-
588
- // トークン配列をtype文字列に変換
589
- static tokensToTypeStr (tokens: Token[], sep: string) {
590
- const a = tokens.map((v) => {
591
- return v.type
592
- })
593
- return a.join(sep)
594
- }
595
-
596
- /**
597
- * ファイル名からモジュール名へ変換
598
- * @param {string} filename
599
- * @returns {string}
600
- */
601
- static filenameToModName (filename: string) {
602
- if (!filename) { return 'main' }
603
- // パスがあればパスを削除
604
- filename = filename.replace(/[\\:]/g, '/') // Windowsのpath記号を/に置換
605
- if (filename.indexOf('/') >= 0) {
606
- const a = filename.split('/')
607
- filename = a[a.length - 1]
608
- }
609
- filename = filename.replace(/\.nako3?$/, '')
610
- return filename
611
- }
612
- }
1
+ // なでしこの字句解析を行う
2
+ // 既に全角半角を揃えたコードに対して字句解析を行う
3
+ import { opPriority } from './nako_parser_const.mjs'
4
+
5
+ // 予約語句
6
+ // (memo)「回」「間」「繰返」「反復」「抜」「続」「戻」「代入」などは _replaceWord で word から変換
7
+ /** @types {Record<string, string>} */
8
+ import reservedWords from './nako_reserved_words.mjs'
9
+ import { NakoLogger } from './nako_logger.mjs'
10
+
11
+ // 助詞の一覧
12
+ import { josiRE, removeJosiMap, tararebaMap } from './nako_josi_list.mjs'
13
+
14
+ // 字句解析ルールの一覧
15
+ import { rules, unitRE } from './nako_lex_rules.mjs'
16
+ import { NakoLexerError, InternalLexerError } from './nako_errors.mjs'
17
+
18
+ import { Token, FuncList, FuncArgs } from './nako_types.mjs'
19
+
20
+ export class NakoLexer {
21
+ public logger: NakoLogger;
22
+ public funclist: FuncList;
23
+ public modList: string[];
24
+ public result: Token[];
25
+ public modName: string;
26
+ /**
27
+ * @param logger
28
+ */
29
+ constructor (logger: NakoLogger) {
30
+ this.logger = logger // 字句解析した際,確認された関数の一覧
31
+ this.funclist = {}
32
+ this.modList = [] // 字句解析した際,取り込むモジュール一覧 --- nako3::lex で更新される
33
+ this.result = []
34
+ this.modName = 'main.nako3' // モジュール名
35
+ }
36
+
37
+ /** 関数一覧をセット */
38
+ setFuncList (listObj: FuncList) {
39
+ this.funclist = listObj
40
+ }
41
+
42
+ /**
43
+ * @param tokens
44
+ * @param {boolean} isFirst
45
+ * @param {string} filename
46
+ */
47
+ replaceTokens (tokens: Token[], isFirst: boolean, filename: string) {
48
+ this.result = tokens
49
+ this.modName = NakoLexer.filenameToModName(filename)
50
+ // 関数の定義があれば funclist を更新
51
+ NakoLexer.preDefineFunc(tokens, this.logger, this.funclist)
52
+ this._replaceWord(this.result)
53
+
54
+ if (isFirst) {
55
+ if (this.result.length > 0) {
56
+ const eof = this.result[this.result.length - 1]
57
+ this.result.push({
58
+ type: 'eol',
59
+ line: eof.line,
60
+ column: 0,
61
+ file: eof.file,
62
+ josi: '',
63
+ value: '---',
64
+ startOffset: eof.startOffset,
65
+ endOffset: eof.endOffset,
66
+ rawJosi: ''
67
+ }) // 改行
68
+ this.result.push({
69
+ type: 'eof',
70
+ line: eof.line,
71
+ column: 0,
72
+ file: eof.file,
73
+ josi: '',
74
+ value: '',
75
+ startOffset: eof.startOffset,
76
+ endOffset: eof.endOffset,
77
+ rawJosi: ''
78
+ }) // ファイル末尾
79
+ } else {
80
+ this.result.push({
81
+ type: 'eol',
82
+ line: 0,
83
+ column: 0,
84
+ file: '',
85
+ josi: '',
86
+ value: '---',
87
+ startOffset: 0,
88
+ endOffset: 0,
89
+ rawJosi: ''
90
+ }) // 改行
91
+ this.result.push({
92
+ type: 'eof',
93
+ line: 0,
94
+ column: 0,
95
+ file: '',
96
+ josi: '',
97
+ value: '',
98
+ startOffset: 0,
99
+ endOffset: 0,
100
+ rawJosi: ''
101
+ }) // ファイル末尾
102
+ }
103
+ }
104
+ return this.result
105
+ }
106
+
107
+ /**
108
+ * ファイル内で定義されている関数名を列挙する。結果はfunclistに書き込む。その他のトークンの置換処理も行う。
109
+ * シンタックスハイライトの処理から呼び出すためにstaticメソッドにしている。
110
+ * @param {Token[]} tokens
111
+ * @param {import('./nako_logger.mjs').NakoLogger} logger
112
+ * @param {FuncList} funclist
113
+ */
114
+ static preDefineFunc (tokens: Token[], logger: NakoLogger, funclist: FuncList) {
115
+ // 関数を先読みして定義
116
+ let i = 0
117
+ let isFuncPointer = false
118
+ const readArgs = () => {
119
+ const args: Token[] = []
120
+ const keys:{[key:string]: string[]} = {}
121
+ if (tokens[i].type !== '(') { return [] }
122
+ i++
123
+ while (tokens[i]) {
124
+ const t = tokens[i]
125
+ i++
126
+ if (t.type === ')') { break }
127
+ if (t.type === 'func') { isFuncPointer = true } else if (t.type !== '|' && t.type !== 'comma') {
128
+ if (isFuncPointer) {
129
+ t.funcPointer = true
130
+ isFuncPointer = false
131
+ }
132
+ args.push(t)
133
+ if (!keys[t.value]) { keys[t.value] = [] }
134
+
135
+ keys[t.value].push(t.josi)
136
+ }
137
+ }
138
+ const varnames: string[] = []
139
+ const funcPointers: any[] = []
140
+ const result: FuncArgs = []
141
+ const already: {[key: string]: boolean} = {}
142
+ for (const arg of args) {
143
+ if (!already[arg.value]) {
144
+ const josi = keys[arg.value]
145
+ result.push(josi)
146
+ varnames.push(arg.value)
147
+ if (arg.funcPointer) { funcPointers.push(arg.value) } else { funcPointers.push(null) }
148
+
149
+ already[arg.value] = true
150
+ }
151
+ }
152
+ return [result, varnames, funcPointers]
153
+ }
154
+ // トークンを一つずつ確認
155
+ while (i < tokens.length) {
156
+ // タイプの置換
157
+ const t = tokens[i]
158
+ // 無名関数の定義:「xxには**」があった場合 ... 暗黙的な関数定義とする
159
+ if ((t.type === 'word' && t.josi === 'には') || (t.type === 'word' && t.josi === 'は~')) {
160
+ t.josi = 'には'
161
+ tokens.splice(i + 1, 0, { type: 'def_func', value: '関数', line: t.line, column: t.column, file: t.file, josi: '', startOffset: t.endOffset, endOffset: t.endOffset, rawJosi: '', tag: '無名関数' })
162
+ i++
163
+ continue
164
+ }
165
+ // N回をN|回に置換
166
+ if (t.type === 'word' && t.josi === '' && t.value.length >= 2) {
167
+ if (t.value.match(/回$/)) {
168
+ t.value = t.value.substring(0, t.value.length - 1)
169
+ // N回を挿入
170
+ if (!t.endOffset) { t.endOffset = 1 }
171
+ const kai = { type: '回', value: '回', line: t.line, column: t.column, file: t.file, josi: '', startOffset: t.endOffset - 1, endOffset: t.endOffset, rawJosi: '' }
172
+ tokens.splice(i + 1, 0, kai)
173
+ t.endOffset--
174
+ i++
175
+ }
176
+ }
177
+ // 予約語の置換
178
+ if (t.type === 'word' && reservedWords[t.value]) {
179
+ t.type = reservedWords[t.value]
180
+ if (t.value === 'そう') { t.value = 'それ' }
181
+ }
182
+ // 関数定義の確認
183
+ if (t.type !== 'def_test' && t.type !== 'def_func') {
184
+ i++
185
+ continue
186
+ }
187
+ // 無名関数か普通関数定義かを判定する (1つ前が改行かどうかで判定)
188
+ let isMumei = true
189
+ let prevToken = { type: 'eol' }
190
+ if (i >= 1) { prevToken = tokens[i - 1] }
191
+ if (prevToken.type === 'eol') { isMumei = false }
192
+ // 関数名や引数を得る
193
+ const defToken = t
194
+ i++ // skip "●" or "関数"
195
+ let josi = []
196
+ let varnames = []
197
+ let funcPointers = []
198
+ let funcName = ''
199
+ let funcNameToken: Token | null = null
200
+ // 関数名の前に引数定義
201
+ if (tokens[i] && tokens[i].type === '(') { [josi, varnames, funcPointers] = readArgs() }
202
+
203
+ // 関数名を得る
204
+ if (!isMumei && tokens[i] && tokens[i].type === 'word') {
205
+ funcNameToken = tokens[i++]
206
+ funcName = funcNameToken.value
207
+ }
208
+
209
+ // 関数名の後で引数定義
210
+ if (josi.length === 0 && tokens[i] && tokens[i].type === '(') { [josi, varnames, funcPointers] = readArgs() }
211
+
212
+ // 名前のある関数定義ならば関数テーブルに関数名を登録
213
+ // 無名関数は登録しないように気をつける
214
+ if (funcName !== '' && funcNameToken) {
215
+ const modName = NakoLexer.filenameToModName(t.file)
216
+ funcName = modName + '__' + funcName
217
+ if (funcName in funclist) { // 関数の二重定義を警告
218
+ // main__は省略 #1223
219
+ const dispName = funcName.replace(/^main__/, '')
220
+ logger.warn(`関数『${dispName}』は既に定義されています。`, defToken)
221
+ }
222
+ funcNameToken.value = funcName
223
+ funclist[funcName] = {
224
+ type: 'func',
225
+ josi,
226
+ fn: null,
227
+ asyncFn: false,
228
+ varnames,
229
+ funcPointers
230
+ }
231
+ }
232
+ // 無名関数のために
233
+ defToken.meta = {
234
+ type: 'func',
235
+ josi,
236
+ varnames,
237
+ funcPointers
238
+ }
239
+ }
240
+ }
241
+
242
+ /** 文字列を{と}の部分で分割する。中括弧が対応していない場合nullを返す。 */
243
+ splitStringEx (code: string): string[] | null {
244
+ /** @type {string[]} */
245
+ const list = []
246
+
247
+ // "A{B}C{D}E" -> ["A", "B}C", "D}E"] -> ["A", "B", "C", "D", "E"]
248
+ // "A{B}C}D{E}F" -> ["A", "B}C}D", "E}F"] -> ["A", "B", "C}D", "E", "F"]
249
+ const arr = code.split(/[{{]/)
250
+ list.push(arr[0])
251
+ for (const s of arr.slice(1)) {
252
+ const end = s.replace('}', '}').indexOf('}')
253
+ if (end === -1) {
254
+ return null
255
+ }
256
+ list.push(s.slice(0, end), s.slice(end + 1))
257
+ }
258
+
259
+ return list
260
+ }
261
+
262
+ _replaceWord (tokens: Token[]): void {
263
+ let comment = []
264
+ let i = 0
265
+ const getLastType = () => {
266
+ if (i <= 0) { return 'eol' }
267
+ return tokens[i - 1].type
268
+ }
269
+ const modSelf = (tokens.length > 0) ? NakoLexer.filenameToModName(tokens[0].file) : 'main.nako3'
270
+ while (i < tokens.length) {
271
+ const t = tokens[i]
272
+ // 関数を強制的に置換( word => func )
273
+ if (t.type === 'word' && t.value !== 'それ') {
274
+ // 関数を変換
275
+ const funcName = t.value
276
+ if (funcName.indexOf('__') < 0) {
277
+ // 自身のモジュール名を検索
278
+ const gname1 = `${modSelf}__${funcName}`
279
+ const gfo1 = this.funclist[gname1]
280
+ if (gfo1 && gfo1.type === 'func') {
281
+ t.type = 'func'
282
+ t.meta = gfo1
283
+ t.value = gname1
284
+ continue
285
+ }
286
+ // モジュール関数を置換
287
+ for (const mod of this.modList) {
288
+ const gname = `${mod}__${funcName}`
289
+ const gfo = this.funclist[gname]
290
+ if (gfo && gfo.type === 'func') {
291
+ t.type = 'func'
292
+ t.meta = gfo
293
+ t.value = gname
294
+ break
295
+ }
296
+ }
297
+ if (t.type === 'func') { continue }
298
+ }
299
+ const fo = this.funclist[funcName]
300
+ if (fo && fo.type === 'func') {
301
+ t.type = 'func'
302
+ t.meta = fo
303
+ }
304
+ }
305
+ // 数字につくマイナス記号を判定
306
+ // (ng) 5 - 3 || word - 3
307
+ // (ok) (行頭)-3 || 1 * -3 || Aに -3を 足す
308
+ if (t.type === '-' && tokens[i + 1] && tokens[i + 1].type === 'number') {
309
+ // 一つ前の語句が、(行頭|演算子|助詞付きの語句)なら 負数である
310
+ const ltype = getLastType()
311
+ if (ltype === 'eol' || opPriority[ltype] || tokens[i - 1].josi !== '') {
312
+ tokens.splice(i, 1) // remove '-'
313
+ tokens[i].value *= -1
314
+ }
315
+ }
316
+ // 助詞の「は」を = に展開
317
+ if (t.josi === undefined) { t.josi = '' }
318
+ if (t.josi === 'は') {
319
+ if (!t.rawJosi) { t.rawJosi = t.josi }
320
+ const startOffset = (t.endOffset === undefined) ? undefined : t.endOffset - t.rawJosi.length
321
+ tokens.splice(i + 1, 0, {
322
+ type: 'eq',
323
+ line: t.line,
324
+ column: t.column,
325
+ file: t.file,
326
+ startOffset,
327
+ endOffset: t.endOffset,
328
+ josi: '',
329
+ rawJosi: '',
330
+ value: undefined
331
+ })
332
+ i += 2
333
+ t.josi = t.rawJosi = ''
334
+ t.endOffset = startOffset
335
+ continue
336
+ }
337
+ // 「とは」を一つの単語にする
338
+ if (t.josi === 'とは') {
339
+ if (!t.rawJosi) { t.rawJosi = t.josi }
340
+ const startOffset = t.endOffset === undefined ? undefined : t.endOffset - t.rawJosi.length
341
+ tokens.splice(i + 1, 0, {
342
+ type: t.josi,
343
+ line: t.line,
344
+ column: t.column,
345
+ file: t.file,
346
+ startOffset,
347
+ endOffset: t.endOffset,
348
+ josi: '',
349
+ rawJosi: '',
350
+ value: undefined
351
+ })
352
+ t.josi = t.rawJosi = ''
353
+ t.endOffset = startOffset
354
+ i += 2
355
+ continue
356
+ }
357
+ // 助詞のならばをトークンとする
358
+ if (tararebaMap[t.josi]) {
359
+ const josi = (t.josi === 'でなければ' || t.josi === 'なければ') ? 'でなければ' : 'ならば'
360
+ if (!t.rawJosi) { t.rawJosi = josi }
361
+ const startOffset = t.endOffset === undefined ? undefined : t.endOffset - t.rawJosi.length
362
+ tokens.splice(i + 1, 0, {
363
+ type: 'ならば',
364
+ value: josi,
365
+ line: t.line,
366
+ column: t.column,
367
+ file: t.file,
368
+ startOffset,
369
+ endOffset: t.endOffset,
370
+ josi: '',
371
+ rawJosi: ''
372
+ })
373
+ t.josi = t.rawJosi = ''
374
+ t.endOffset = startOffset
375
+ i += 2
376
+ continue
377
+ }
378
+ // '_' + 改行 を飛ばす (演算子直後に改行を入れたい場合に使う)
379
+ if (t.type === '_eol') {
380
+ tokens.splice(i, 1)
381
+ continue
382
+ }
383
+ // コメントを飛ばす
384
+ if (t.type === 'line_comment' || t.type === 'range_comment') {
385
+ comment.push(t.value)
386
+ tokens.splice(i, 1)
387
+ continue
388
+ }
389
+ // 改行にコメントを埋め込む
390
+ if (t.type === 'eol') {
391
+ t.value = comment.join('/')
392
+ comment = []
393
+ }
394
+ i++
395
+ }
396
+ }
397
+
398
+ /**
399
+ * @param {string} src
400
+ * @param {number} line
401
+ * @param {string} filename
402
+ * @returns {Token[]}
403
+ */
404
+ tokenize (src: string, line: number, filename: string): Token[] {
405
+ const srcLength: number = src.length
406
+ const result: Token[] = []
407
+ let columnCurrent
408
+ let lineCurrent
409
+ let column = 1
410
+ let isDefTest = false
411
+ while (src !== '') {
412
+ let ok = false
413
+ // 各ルールについて
414
+ for (const rule of rules) {
415
+ // 正規表現でマッチ
416
+ const m = rule.pattern.exec(src)
417
+ if (!m) { continue }
418
+ ok = true
419
+ // 空白ならスキップ
420
+ if (rule.name === 'space') {
421
+ column += m[0].length
422
+ src = src.substring(m[0].length)
423
+ continue
424
+ }
425
+ // マッチしたルールがコールバックを持つなら
426
+ if (rule.cbParser) {
427
+ // コールバックを呼ぶ
428
+ /** @type {{ src: string, res: string, josi: string, numEOL: number }} */
429
+ let rp
430
+
431
+ if (isDefTest && rule.name === 'word') {
432
+ rp = rule.cbParser(src, false)
433
+ } else {
434
+ try {
435
+ rp = rule.cbParser(src)
436
+ } catch (e: any) {
437
+ throw new NakoLexerError(
438
+ e.message,
439
+ srcLength - src.length,
440
+ srcLength - src.length + 1,
441
+ line,
442
+ filename
443
+ )
444
+ }
445
+ }
446
+
447
+ if (rule.name === 'string_ex') {
448
+ // 展開あり文字列 → aaa{x}bbb{x}cccc
449
+ const list = this.splitStringEx(rp.res)
450
+ if (list === null) {
451
+ throw new InternalLexerError(
452
+ '展開あり文字列で値の埋め込み{...}が対応していません。',
453
+ srcLength - src.length,
454
+ srcLength - rp.src.length,
455
+ line,
456
+ filename
457
+ )
458
+ }
459
+ let offset = 0
460
+ for (let i = 0; i < list.length; i++) {
461
+ const josi = (i === list.length - 1) ? rp.josi : ''
462
+ if (i % 2 === 0) {
463
+ result.push({
464
+ type: 'string',
465
+ value: list[i],
466
+ file: filename,
467
+ josi,
468
+ line,
469
+ column,
470
+ preprocessedCodeOffset: srcLength - src.length + offset,
471
+ preprocessedCodeLength: list[i].length + 2 + josi.length
472
+ })
473
+ // 先頭なら'"...{'、それ以外なら'}...{'、最後は何でも良い
474
+ offset += list[i].length + 2
475
+ } else {
476
+ result.push({ type: '&', value: '&', josi: '', file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset, preprocessedCodeLength: 0 })
477
+ result.push({ type: '(', value: '(', josi: '', file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset, preprocessedCodeLength: 0 })
478
+ result.push({ type: 'code', value: list[i], josi: '', file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset, preprocessedCodeLength: list[i].length })
479
+ result.push({ type: ')', value: ')', josi: '', file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset + list[i].length, preprocessedCodeLength: 0 })
480
+ result.push({ type: '&', value: '&', josi: '', file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset + list[i].length, preprocessedCodeLength: 0 })
481
+ offset += list[i].length
482
+ }
483
+ }
484
+ line += rp.numEOL
485
+ column += src.length - rp.src.length
486
+ src = rp.src
487
+ if (rp.numEOL > 0) {
488
+ column = 1
489
+ }
490
+ break
491
+ }
492
+ columnCurrent = column
493
+ column += src.length - rp.src.length
494
+ result.push({ type: rule.name, value: rp.res, josi: rp.josi, line, column: columnCurrent, file: filename, preprocessedCodeOffset: srcLength - src.length, preprocessedCodeLength: src.length - rp.src.length })
495
+ src = rp.src
496
+ line += rp.numEOL
497
+ if (rp.numEOL > 0) {
498
+ column = 1
499
+ }
500
+ break
501
+ }
502
+
503
+ // ソースを進める前に位置を計算
504
+ const srcOffset = srcLength - src.length
505
+
506
+ // 値を変換する必要があるか?
507
+ let value: any = m[0]
508
+ if (rule.cb) { value = rule.cb(value) }
509
+ // ソースを進める
510
+ columnCurrent = column
511
+ lineCurrent = line
512
+ column += m[0].length
513
+ src = src.substring(m[0].length)
514
+ if ((rule.name === 'eol' && value === '\n') || rule.name === '_eol') {
515
+ value = line++
516
+ column = 1
517
+ }
518
+
519
+ // 数値なら単位を持つか? --- #994
520
+ if (rule.name === 'number') {
521
+ // 単位があれば読み飛ばす
522
+ const um = unitRE.exec(src)
523
+ if (um) {
524
+ src = src.substring(um[0].length)
525
+ column += m[0].length
526
+ }
527
+ }
528
+
529
+ let josi = ''
530
+ if (rule.readJosi) {
531
+ const j = josiRE.exec(src)
532
+ if (j) {
533
+ josi = j[0].replace(/^\s+/, '')
534
+ column += j[0].length
535
+ src = src.substring(j[0].length)
536
+ // 助詞の直後にあるカンマを無視 #877
537
+ if (src.charAt(0) === ',') {
538
+ src = src.substring(1)
539
+ }
540
+ // 「**である」なら削除 #939 #974
541
+ if (removeJosiMap[josi]) { josi = '' }
542
+ }
543
+ }
544
+
545
+ switch (rule.name) {
546
+ case 'def_test': {
547
+ isDefTest = true
548
+ break
549
+ }
550
+ case 'eol': {
551
+ isDefTest = false
552
+ break
553
+ }
554
+ default: {
555
+ break
556
+ }
557
+ }
558
+ // ここまで‰(#682) を処理
559
+ if (rule.name === 'dec_lineno') {
560
+ line--
561
+ continue
562
+ }
563
+
564
+ result.push({
565
+ type: rule.name,
566
+ value,
567
+ line: lineCurrent,
568
+ column: columnCurrent,
569
+ file: filename,
570
+ josi,
571
+ preprocessedCodeOffset: srcOffset,
572
+ preprocessedCodeLength: (srcLength - src.length) - srcOffset
573
+ })
574
+ break
575
+ }
576
+ if (!ok) {
577
+ throw new InternalLexerError('未知の語句: ' + src.substring(0, 3) + '...',
578
+ srcLength - src.length,
579
+ srcLength - srcLength + 3,
580
+ line,
581
+ filename
582
+ )
583
+ }
584
+ }
585
+ return result
586
+ }
587
+
588
+ // トークン配列をtype文字列に変換
589
+ static tokensToTypeStr (tokens: Token[], sep: string) {
590
+ const a = tokens.map((v) => {
591
+ return v.type
592
+ })
593
+ return a.join(sep)
594
+ }
595
+
596
+ /**
597
+ * ファイル名からモジュール名へ変換
598
+ * @param {string} filename
599
+ * @returns {string}
600
+ */
601
+ static filenameToModName (filename: string) {
602
+ if (!filename) { return 'main' }
603
+ // パスがあればパスを削除
604
+ filename = filename.replace(/[\\:]/g, '/') // Windowsのpath記号を/に置換
605
+ if (filename.indexOf('/') >= 0) {
606
+ const a = filename.split('/')
607
+ filename = a[a.length - 1]
608
+ }
609
+ filename = filename.replace(/\.nako3?$/, '')
610
+ return filename
611
+ }
612
+ }