nadesiko3 3.2.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +107 -0
  3. package/bin/cnako3 +10 -0
  4. package/bin/cnako3.bat +11 -0
  5. package/bin/nako3server.bat +6 -0
  6. package/demo/ace_editor.html +90 -0
  7. package/demo/ace_editor_tabs.html +162 -0
  8. package/demo/basic.html +71 -0
  9. package/demo/browsers.html +128 -0
  10. package/demo/css/basic.css +3 -0
  11. package/demo/css/common.css +157 -0
  12. package/demo/css/editor.css +8 -0
  13. package/demo/css/flow.css +3 -0
  14. package/demo/css/index.css +3 -0
  15. package/demo/extlib/ace@1.4.12/ace.js +17 -0
  16. package/demo/extlib/ace@1.4.12/ext-code_lens.min.js +1 -0
  17. package/demo/extlib/ace@1.4.12/ext-language_tools.min.js +1 -0
  18. package/demo/extlib/ace@1.4.12/ext-options.min.js +1 -0
  19. package/demo/extlib/ace@1.4.12/ext-settings_menu.js +8 -0
  20. package/demo/extlib/ace@1.4.12/keybinding-vscode.js +8 -0
  21. package/demo/extlib/ace@1.4.12/theme-monokai.js +8 -0
  22. package/demo/extlib/ace@1.4.12/theme-xcode.js +8 -0
  23. package/demo/extlib/chart.js@3.2.1/chart.min.js +13 -0
  24. package/demo/extlib/pure-min.css +11 -0
  25. package/demo/flow.html +97 -0
  26. package/demo/graph.html +53 -0
  27. package/demo/image/nako.png +0 -0
  28. package/demo/image/nakopad-icon256.png +0 -0
  29. package/demo/image/turtle.fla +0 -0
  30. package/demo/image/turtle.png +0 -0
  31. package/demo/index.html +134 -0
  32. package/demo/js/common.js +17 -0
  33. package/demo/js/turtle3d_test.js +44 -0
  34. package/demo/js/turtle_test.js +44 -0
  35. package/demo/runscript.html +47 -0
  36. package/demo/runscript2.html +33 -0
  37. package/demo/turtle.html +58 -0
  38. package/demo/turtle2.html +141 -0
  39. package/demo/turtle3.html +278 -0
  40. package/demo/turtle3d.html +58 -0
  41. package/demo/turtle3d2.html +107 -0
  42. package/demo/version.html +24 -0
  43. package/doc/SETUP.md +172 -0
  44. package/doc/about.md +34 -0
  45. package/doc/browsers.md +60 -0
  46. package/doc/docgen.md +21 -0
  47. package/doc/editor.md +44 -0
  48. package/doc/files.md +37 -0
  49. package/doc/plugins.md +195 -0
  50. package/doc/release.md +78 -0
  51. package/doc/win32.md +57 -0
  52. package/package.json +196 -0
  53. package/release/_hash.txt +65 -0
  54. package/release/_script-tags.txt +13 -0
  55. package/release/command.json +1 -0
  56. package/release/command.json.js +1 -0
  57. package/release/command_cnako3.json +1 -0
  58. package/release/command_list.json +1 -0
  59. package/release/editor.js +2 -0
  60. package/release/editor.js.LICENSE.txt +32 -0
  61. package/release/josi.json +48 -0
  62. package/release/nako_gen_async.js +1 -0
  63. package/release/nako_gen_async.js.LICENSE.txt +595 -0
  64. package/release/plugin_caniuse.js +1 -0
  65. package/release/plugin_caniuse.js.LICENSE.txt +411 -0
  66. package/release/plugin_csv.js +1 -0
  67. package/release/plugin_csv.js.LICENSE.txt +367 -0
  68. package/release/plugin_datetime.js +1 -0
  69. package/release/plugin_datetime.js.LICENSE.txt +471 -0
  70. package/release/plugin_kansuji.js +1 -0
  71. package/release/plugin_kansuji.js.LICENSE.txt +491 -0
  72. package/release/plugin_markup.js +1 -0
  73. package/release/plugin_markup.js.LICENSE.txt +363 -0
  74. package/release/plugin_turtle.js +1 -0
  75. package/release/plugin_turtle.js.LICENSE.txt +435 -0
  76. package/release/plugin_webworker.js +1 -0
  77. package/release/plugin_webworker.js.LICENSE.txt +491 -0
  78. package/release/version.js +2 -0
  79. package/release/version.js.LICENSE.txt +32 -0
  80. package/release/wnako3.js +2 -0
  81. package/release/wnako3.js.LICENSE.txt +1 -0
  82. package/release/wnako3webworker.js +1 -0
  83. package/release/wnako3webworker.js.LICENSE.txt +847 -0
  84. package/release/yoyakugo.json +30 -0
  85. package/src/browsers.md +60 -0
  86. package/src/cnako3.js +466 -0
  87. package/src/commander_ja.js +154 -0
  88. package/src/enako3.js +69 -0
  89. package/src/era.json +22 -0
  90. package/src/index.js +5 -0
  91. package/src/nako3.js +836 -0
  92. package/src/nako3_assert.js +37 -0
  93. package/src/nako3editorfix.sfd +106 -0
  94. package/src/nako3editorfix.woff +0 -0
  95. package/src/nako3server.js +51 -0
  96. package/src/nako_colors.js +86 -0
  97. package/src/nako_errors.js +176 -0
  98. package/src/nako_gen.js +1459 -0
  99. package/src/nako_gen_async.js +1622 -0
  100. package/src/nako_global.js +113 -0
  101. package/src/nako_indent.js +480 -0
  102. package/src/nako_josi_list.js +46 -0
  103. package/src/nako_lex_rules.js +259 -0
  104. package/src/nako_lexer.js +576 -0
  105. package/src/nako_logger.js +138 -0
  106. package/src/nako_parser3.js +1768 -0
  107. package/src/nako_parser_base.js +265 -0
  108. package/src/nako_parser_const.js +37 -0
  109. package/src/nako_prepare.js +293 -0
  110. package/src/nako_reserved_words.js +35 -0
  111. package/src/nako_source_mapping.js +251 -0
  112. package/src/nako_test.js +37 -0
  113. package/src/nako_version.js +8 -0
  114. package/src/plugin_browser.js +191 -0
  115. package/src/plugin_browser_ajax.js +352 -0
  116. package/src/plugin_browser_audio.js +109 -0
  117. package/src/plugin_browser_canvas.js +462 -0
  118. package/src/plugin_browser_chart.js +296 -0
  119. package/src/plugin_browser_color.js +49 -0
  120. package/src/plugin_browser_crypto.js +26 -0
  121. package/src/plugin_browser_dialog.js +53 -0
  122. package/src/plugin_browser_dom_basic.js +322 -0
  123. package/src/plugin_browser_dom_event.js +193 -0
  124. package/src/plugin_browser_dom_parts.js +163 -0
  125. package/src/plugin_browser_geolocation.js +51 -0
  126. package/src/plugin_browser_hotkey.js +25 -0
  127. package/src/plugin_browser_html.js +59 -0
  128. package/src/plugin_browser_in_worker.js +44 -0
  129. package/src/plugin_browser_location.js +21 -0
  130. package/src/plugin_browser_speech.js +111 -0
  131. package/src/plugin_browser_storage.js +121 -0
  132. package/src/plugin_browser_system.js +12 -0
  133. package/src/plugin_browser_websocket.js +73 -0
  134. package/src/plugin_caniuse.js +24 -0
  135. package/src/plugin_csv.js +57 -0
  136. package/src/plugin_datetime.js +414 -0
  137. package/src/plugin_express.js +212 -0
  138. package/src/plugin_kansuji.js +224 -0
  139. package/src/plugin_keigo.js +55 -0
  140. package/src/plugin_markup.js +32 -0
  141. package/src/plugin_math.js +319 -0
  142. package/src/plugin_node.js +1018 -0
  143. package/src/plugin_promise.js +94 -0
  144. package/src/plugin_system.js +2109 -0
  145. package/src/plugin_test.js +38 -0
  146. package/src/plugin_turtle.js +646 -0
  147. package/src/plugin_webworker.js +334 -0
  148. package/src/plugin_weykturtle3d.js +1216 -0
  149. package/src/plugin_worker.js +92 -0
  150. package/src/repl.nako3 +63 -0
  151. package/src/turtle-elephant.png +0 -0
  152. package/src/turtle-panda.png +0 -0
  153. package/src/turtle64.png +0 -0
  154. package/src/wnako3.js +162 -0
  155. package/src/wnako3_editor.css +215 -0
  156. package/src/wnako3_editor.js +1645 -0
  157. package/src/wnako3_editor_marker_red.png +0 -0
  158. package/src/wnako3_editor_marker_red.xcf +0 -0
  159. package/src/wnako3_editor_marker_yellow.png +0 -0
  160. package/src/wnako3_editor_marker_yellow.xcf +0 -0
  161. package/src/wnako3webworker.js +70 -0
  162. package/test/ace_editor/karma.config.js +94 -0
  163. package/test/ace_editor/test/.babelrc.json +3 -0
  164. package/test/ace_editor/test/ace_editor_test.js +178 -0
  165. package/test/ace_editor/test/html/custom_context.html +140 -0
  166. package/test/async/async_basic_test.js +124 -0
  167. package/test/browser/karma.config.js +212 -0
  168. package/test/browser/test/.babelrc.json +3 -0
  169. package/test/browser/test/compare_util.js +50 -0
  170. package/test/browser/test/html/canvas_basic.html +1 -0
  171. package/test/browser/test/html/div_basic.html +2 -0
  172. package/test/browser/test/html/event_dom_form.html +4 -0
  173. package/test/browser/test/html/event_dom_scrolldiv.html +5 -0
  174. package/test/browser/test/image/canvas_test1.png +0 -0
  175. package/test/browser/test/image/canvas_test2.png +0 -0
  176. package/test/browser/test/image/canvas_test3.png +0 -0
  177. package/test/browser/test/image/canvas_test4.png +0 -0
  178. package/test/browser/test/image/canvas_test7.png +0 -0
  179. package/test/browser/test/image/canvas_test8.png +0 -0
  180. package/test/browser/test/image/canvas_test_blank.png +0 -0
  181. package/test/browser/test/image/elephant_kana.png +0 -0
  182. package/test/browser/test/image/panda_kana.png +0 -0
  183. package/test/browser/test/image/turtle_kana.png +0 -0
  184. package/test/browser/test/import_plugin_checker.js +24 -0
  185. package/test/browser/test/plugin_browser_test.js +52 -0
  186. package/test/browser/test/plugin_browser_test_ajax.js +123 -0
  187. package/test/browser/test/plugin_browser_test_color.js +18 -0
  188. package/test/browser/test/plugin_browser_test_dialog.js +72 -0
  189. package/test/browser/test/plugin_browser_test_dom_event.js +598 -0
  190. package/test/browser/test/plugin_browser_test_dom_parts.js +125 -0
  191. package/test/browser/test/plugin_browser_test_system.js +9 -0
  192. package/test/browser/test/plugin_turtle_test.js +817 -0
  193. package/test/browser/test/plugin_webworker_test.js +87 -0
  194. package/test/browser/test/require_test.js +71 -0
  195. package/test/bundled/karma.config.base.js +117 -0
  196. package/test/bundled/karma.config.js +86 -0
  197. package/test/bundled/test/.babelrc.json +3 -0
  198. package/test/bundled/test/bundled_test.js +69 -0
  199. package/test/bundled/test/html/custom_context.html +65 -0
  200. package/test/bundled/test/html/custom_debug.html +66 -0
  201. package/test/bundled/test4b.cmd +52 -0
  202. package/test/bundled/test_base/.babelrc.json +3 -0
  203. package/test/bundled/test_base/_checktool_test.js +25 -0
  204. package/test/bundled/test_base/basic_ajax_test.js +56 -0
  205. package/test/bundled/test_base/basic_async_test.js +18 -0
  206. package/test/bundled/test_base/basic_test.js +153 -0
  207. package/test/bundled/test_base/calc_test.js +132 -0
  208. package/test/bundled/test_base/css/browsers_box.css +114 -0
  209. package/test/bundled/test_base/html/custom_context.html +69 -0
  210. package/test/bundled/test_base/html/custom_debug.html +71 -0
  211. package/test/bundled/test_base/js/browsers_box.js +72 -0
  212. package/test/bundled/test_base/plugin_csv_test.js +37 -0
  213. package/test/bundled/test_base/plugin_datetime_test.js +115 -0
  214. package/test/bundled/test_base/plugin_kansuji_test.js +49 -0
  215. package/test/bundled/test_base/plugin_system_test.js +410 -0
  216. package/test/bundled/test_base/plugin_webworker_test.js +53 -0
  217. package/test/bundled/test_base/resources/ok.txt +1 -0
  218. package/test/bundled/test_base/test_utils.js +191 -0
  219. package/test/common/array_test.js +40 -0
  220. package/test/common/basic_test.js +317 -0
  221. package/test/common/calc_test.js +139 -0
  222. package/test/common/debug_test.js +16 -0
  223. package/test/common/error_test.js +16 -0
  224. package/test/common/flow_test.js +360 -0
  225. package/test/common/func_call.js +136 -0
  226. package/test/common/func_test.js +149 -0
  227. package/test/common/indent_test.js +362 -0
  228. package/test/common/lex_test.js +146 -0
  229. package/test/common/literal_test.js +72 -0
  230. package/test/common/nako_logger_test.js +26 -0
  231. package/test/common/plugin_browser_test.js +24 -0
  232. package/test/common/plugin_browser_ut_audio_test.js +88 -0
  233. package/test/common/plugin_browser_ut_color_test.js +21 -0
  234. package/test/common/plugin_browser_ut_dialog_test.js +100 -0
  235. package/test/common/plugin_browser_ut_html_test.js +13 -0
  236. package/test/common/plugin_browser_ut_system_test.js +10 -0
  237. package/test/common/plugin_csv_test.js +39 -0
  238. package/test/common/plugin_datetime_test.js +120 -0
  239. package/test/common/plugin_kansuji_test.js +59 -0
  240. package/test/common/plugin_promise_test.js +18 -0
  241. package/test/common/plugin_system_test.js +451 -0
  242. package/test/common/prepare_test.js +93 -0
  243. package/test/common/re_test.js +20 -0
  244. package/test/common/variable_scope_test.js +105 -0
  245. package/test/jsconfig.json +19 -0
  246. package/test/karma.config.js +91 -0
  247. package/test/node/add_test.nako3 +1 -0
  248. package/test/node/async_test.js +80 -0
  249. package/test/node/commander_ja_test.js +82 -0
  250. package/test/node/error_message_test.js +244 -0
  251. package/test/node/kai_test.nako3 +6 -0
  252. package/test/node/node_test.js +43 -0
  253. package/test/node/plugin_broken.js.txt +3 -0
  254. package/test/node/plugin_browser_ut_ajax_test.js +355 -0
  255. package/test/node/plugin_browser_ut_location_test.js +32 -0
  256. package/test/node/plugin_markup_test.js +44 -0
  257. package/test/node/plugin_math_test.js +42 -0
  258. package/test/node/plugin_node_test.js +93 -0
  259. package/test/node/plugin_test.js +16 -0
  260. package/test/node/relative_import_test_1.nako3 +1 -0
  261. package/test/node/relative_import_test_2.nako3 +2 -0
  262. package/test/node/require_nako3_test.js +59 -0
  263. package/test/node/requiretest.nako3 +4 -0
  264. package/test/node/requiretest_indirect.nako3 +1 -0
  265. package/test/node/requiretest_name.nako3 +5 -0
  266. package/test/node/runtime_error.nako3 +2 -0
  267. package/test/node/side_effects_test.js +106 -0
  268. package/test/node/sjis.txt +5 -0
  269. package/test/node/syntax_error.nako3 +2 -0
  270. package/test/node/wnako3_editor_test.js +360 -0
  271. package/tools/README.md +7 -0
  272. package/tools/nako3edit/html/edit.html +83 -0
  273. package/tools/nako3edit/html/edit_plugin.js +6 -0
  274. package/tools/nako3edit/html/files.html +49 -0
  275. package/tools/nako3edit/html/nako3edit.css +66 -0
  276. package/tools/nako3edit/index.nako3 +145 -0
  277. package/tools/nako3edit/run.js +12 -0
  278. package/tools/nako3server/html/edit.html +104 -0
  279. package/tools/nako3server/html/edit_plugin.js +6 -0
  280. package/tools/nako3server/html/files.html +53 -0
  281. package/tools/nako3server/html/nako3edit.css +66 -0
  282. package/tools/nako3server/index.nako3 +129 -0
  283. package/tools/nako3server/run.js +12 -0
@@ -0,0 +1,1768 @@
1
+ /**
2
+ * nadesiko v3 parser (demo version)
3
+ */
4
+ const { opPriority, keizokuJosi } = require('./nako_parser_const')
5
+ const { NakoParserBase, NakoSyntaxError } = require('./nako_parser_base')
6
+ /**
7
+ * @type {string[]}
8
+ */
9
+ const operatorList = []
10
+ for (const key in opPriority) { operatorList.push(key) }
11
+
12
+ /**
13
+ * @typedef {import('./nako3').TokenWithSourceMap} TokenWithSourceMap
14
+ * @typedef {import('./nako3').Ast} Ast
15
+ */
16
+
17
+ // @ts-ignore
18
+ class NakoParser extends NakoParserBase {
19
+ /**
20
+ * @param {TokenWithSourceMap[]} tokens 字句解析済みのトークンの配列
21
+ * @return {Ast} AST(構文木)
22
+ */
23
+ parse (tokens) {
24
+ this.reset()
25
+ /** @type {TokenWithSourceMap[]} */
26
+ this.tokens = tokens
27
+ // 解析開始
28
+ return this.startParser()
29
+ }
30
+
31
+ /** @returns {Ast} */
32
+ startParser () {
33
+ const b = this.ySentenceList()
34
+ const c = this.get()
35
+ if (c && c.type !== 'eof') {
36
+ this.logger.debug(`構文解析でエラー。${this.nodeToStr(c, { depth: 1 }, true)}の使い方が間違っています。`, c)
37
+ throw NakoSyntaxError.fromNode(`構文解析でエラー。${this.nodeToStr(c, { depth: 1 }, false)}の使い方が間違っています。`, c)
38
+ }
39
+ return b
40
+ }
41
+
42
+ /** @returns {Ast} */
43
+ ySentenceList () {
44
+ const blocks = []
45
+ let line = -1
46
+ const map = this.peekSourceMap()
47
+ while (!this.isEOF()) {
48
+ const n = this.ySentence()
49
+ if (!n) { break }
50
+ blocks.push(n)
51
+ if (line < 0) { line = n.line }
52
+ }
53
+ if (blocks.length === 0) {
54
+ const token = this.peek() || this.tokens[0]
55
+ this.logger.debug('構文解析に失敗:' + this.nodeToStr(this.peek(), { depth: 1 }, true), token)
56
+ throw NakoSyntaxError.fromNode('構文解析に失敗:' + this.nodeToStr(this.peek(), { depth: 1 }, false), token)
57
+ }
58
+
59
+ return { type: 'block', block: blocks, ...map, end: this.peekSourceMap(), genMode: this.genMode }
60
+ }
61
+
62
+ yEOL () {
63
+ // 行末のチェック #1009
64
+ const eol = this.get()
65
+ // スタックの確認
66
+ if (this.stack.length > 0) {
67
+ const words = []
68
+ this.stack.forEach((t) => {
69
+ words.push(this.nodeToStr(t, { depth: 1 }, false))
70
+ })
71
+ const desc = words.join(',')
72
+ throw NakoSyntaxError.fromNode(
73
+ `未解決の単語があります: [${desc}]`, eol)
74
+ }
75
+ return eol
76
+ }
77
+
78
+ /** @returns {Ast | null} */
79
+ ySentence () {
80
+ const map = this.peekSourceMap()
81
+ // 最初の語句が決まっている構文
82
+ if (this.check('eol')) { return this.yEOL() }
83
+ if (this.check('もし')) { return this.yIF() }
84
+ if (this.check('エラー監視')) { return this.yTryExcept() }
85
+ if (this.check('逐次実行')) { return this.yTikuji() }
86
+ if (this.accept(['抜ける'])) { return { type: 'break', josi: '', ...map, end: this.peekSourceMap() } }
87
+ if (this.accept(['続ける'])) { return { type: 'continue', josi: '', ...map, end: this.peekSourceMap() } }
88
+ if (this.accept(['require', 'string', '取込'])) {
89
+ return {
90
+ type: 'require',
91
+ value: this.y[1].value,
92
+ josi: '',
93
+ ...map,
94
+ end: this.peekSourceMap()
95
+ }
96
+ }
97
+ if (this.accept(['not', '非同期モード'])) { return this.yASyncMode() }
98
+ if (this.accept(['not', 'string', 'モード設定'])) { return this.ySetGenMode(this.y[1].value) }
99
+ // 関数呼び出し演算子
100
+ if (this.check2(['func', '←'])) { return this.yCallOp() }
101
+ if (this.check2(['func', 'eq'])) {
102
+ const word = this.get() || { value: '?' }
103
+ throw NakoSyntaxError.fromNode(`関数『${word.value}』に代入できません。『←』を使ってください。`, word)
104
+ }
105
+
106
+ // 先読みして初めて確定する構文
107
+ if (this.accept([this.ySpeedMode])) { return this.y[0] }
108
+ if (this.accept([this.yPerformanceMonitor])) { return this.y[0] }
109
+ if (this.accept([this.yLet])) { return this.y[0] }
110
+ if (this.accept([this.yDefTest])) { return this.y[0] }
111
+ if (this.accept([this.yDefFunc])) { return this.y[0] }
112
+ if (this.accept([this.yCall])) { // 関数呼び出しの他、各種構文の実装
113
+ const c1 = this.y[0]
114
+ if (c1.josi === 'して') { // 連文をblockとして接続する(もし構文、逐次実行構文などのため)
115
+ const c2 = this.ySentence()
116
+ if (c2 !== null) {
117
+ return {
118
+ type: 'block',
119
+ block: [c1, c2],
120
+ josi: c2.josi,
121
+ ...map,
122
+ end: this.peekSourceMap()
123
+ }
124
+ }
125
+ }
126
+ return c1
127
+ }
128
+ return null
129
+ }
130
+
131
+ /** @returns {Ast} */
132
+ yASyncMode () {
133
+ const map = this.peekSourceMap()
134
+ this.genMode = '非同期モード'
135
+ return { type: 'eol', ...map, end: this.peekSourceMap() }
136
+ }
137
+
138
+ /** @returns {Ast} */
139
+ ySetGenMode (mode) {
140
+ const map = this.peekSourceMap()
141
+ this.genMode = mode
142
+ return { type: 'eol', ...map, end: this.peekSourceMap() }
143
+ }
144
+
145
+ /** @returns {Ast} */
146
+ yBlock () {
147
+ const map = this.peekSourceMap()
148
+ const blocks = []
149
+ if (this.check('ここから')) { this.get() }
150
+ while (!this.isEOF()) {
151
+ if (this.checkTypes(['違えば', 'ここまで', 'エラー'])) { break }
152
+ if (!this.accept([this.ySentence])) { break }
153
+ blocks.push(this.y[0])
154
+ }
155
+ return { type: 'block', block: blocks, ...map, end: this.peekSourceMap() }
156
+ }
157
+
158
+ /** @returns {(TokenWithSourceMap | null)[] | null} */
159
+ yDefFuncReadArgs () {
160
+ if (!this.check('(')) { return null }
161
+ /** @type {(TokenWithSourceMap | null)[]} */
162
+ const a = []
163
+ this.get() // skip '('
164
+ while (!this.isEOF()) {
165
+ if (this.check(')')) {
166
+ this.get() // skip ''
167
+ break
168
+ }
169
+ a.push(this.get())
170
+ if (this.check('comma')) { this.get() }
171
+ }
172
+ return a
173
+ }
174
+
175
+ /** @returns {Ast | null} */
176
+ yDefTest () {
177
+ return this.yDef('def_test')
178
+ }
179
+
180
+ /** @returns {Ast | null} */
181
+ yDefFunc () {
182
+ return this.yDef('def_func')
183
+ }
184
+
185
+ /**
186
+ * @param {string} type
187
+ * @returns {Ast | null}
188
+ */
189
+ yDef (type) {
190
+ if (!this.check(type)) {
191
+ return null
192
+ }
193
+ const map = this.peekSourceMap()
194
+ const def = this.get() // ●
195
+ let defArgs = []
196
+ if (this.check('(')) { defArgs = this.yDefFuncReadArgs() } // // lexerでも解析しているが再度詳しく
197
+
198
+ const funcName = this.get()
199
+ if (funcName.type !== 'func') {
200
+ this.logger.debug(this.nodeToStr(funcName, { depth: 0, typeName: '関数' }, true) + 'の宣言でエラー。', funcName)
201
+ throw NakoSyntaxError.fromNode(this.nodeToStr(funcName, { depth: 0, typeName: '関数' }, false) + 'の宣言でエラー。', funcName)
202
+ }
203
+
204
+ if (this.check('(')) {
205
+ // 関数引数の二重定義
206
+ if (defArgs.length > 0) {
207
+ this.logger.debug(this.nodeToStr(funcName, { depth: 0, typeName: '関数' }, true) + 'の宣言で、引数定義は名前の前か後に一度だけ可能です。', funcName)
208
+ throw NakoSyntaxError.fromNode(this.nodeToStr(funcName, { depth: 0, typeName: '関数' }, false) + 'の宣言で、引数定義は名前の前か後に一度だけ可能です。', funcName)
209
+ }
210
+ defArgs = this.yDefFuncReadArgs()
211
+ }
212
+
213
+ if (this.check('とは')) { this.get() }
214
+ let block = null
215
+ let multiline = false
216
+ if (this.check('ここから')) { multiline = true }
217
+ if (this.check('eol')) { multiline = true }
218
+ try {
219
+ if (multiline) {
220
+ this.saveStack()
221
+ block = this.yBlock()
222
+ if (this.check('ここまで')) { this.get() } else { throw NakoSyntaxError.fromNode('『ここまで』がありません。関数定義の末尾に必要です。', def) }
223
+ this.loadStack()
224
+ } else {
225
+ this.saveStack()
226
+ block = this.ySentence()
227
+ this.loadStack()
228
+ }
229
+ } catch (err) {
230
+ this.logger.debug(this.nodeToStr(funcName, { depth: 0, typeName: '関数' }, true) +
231
+ 'の定義で以下のエラーがありました。\n' + err.message, def)
232
+ throw NakoSyntaxError.fromNode(this.nodeToStr(funcName, { depth: 0, typeName: '関数' }, false) +
233
+ 'の定義で以下のエラーがありました。\n' + err.message, def)
234
+ }
235
+
236
+ return {
237
+ type,
238
+ name: funcName,
239
+ args: defArgs,
240
+ block,
241
+ josi: '',
242
+ ...map,
243
+ end: this.peekSourceMap()
244
+ }
245
+ }
246
+
247
+ /** @returns {Ast | null} */
248
+ yIFCond () { // もしの条件の取得
249
+ const map = this.peekSourceMap()
250
+ let a = this.yGetArg()
251
+ if (!a) { return null }
252
+ // console.log('yIFCond=', a, this.peek())
253
+ // チェック : AがBならば
254
+ if (a.josi === 'が') {
255
+ const tmpI = this.index
256
+ const b = this.yGetArg()
257
+ const naraba = this.get()
258
+ if (b && b.type !== 'func' && naraba && naraba.type === 'ならば') {
259
+ return {
260
+ type: 'op',
261
+ operator: (naraba.value === 'でなければ') ? 'noteq' : 'eq',
262
+ left: a,
263
+ right: b,
264
+ josi: '',
265
+ ...map,
266
+ end: this.peekSourceMap()
267
+ }
268
+ }
269
+
270
+ this.index = tmpI
271
+ }
272
+ if (a.josi !== '') {
273
+ // もし文で関数呼び出しがある場合
274
+ this.stack.push(a)
275
+ a = this.yCall()
276
+ }
277
+ // (ならば|でなければ)を確認
278
+ if (!this.check('ならば')) {
279
+ this.logger.debug(
280
+ 'もし文で『ならば』がないか、条件が複雑過ぎます。' + this.nodeToStr(this.peek(), { depth: 1 }, false) + 'の直前に『ならば』を書いてください。', a)
281
+ throw NakoSyntaxError.fromNode(
282
+ 'もし文で『ならば』がないか、条件が複雑過ぎます。' + this.nodeToStr(this.peek(), { depth: 1 }, false) + 'の直前に『ならば』を書いてください。', a)
283
+ }
284
+
285
+ const naraba = this.get()
286
+ if (naraba.value === 'でなければ') {
287
+ a = {
288
+ type: 'not',
289
+ value: a,
290
+ josi: '',
291
+ ...map,
292
+ end: this.peekSourceMap()
293
+ }
294
+ }
295
+
296
+ return a
297
+ }
298
+
299
+ /** @returns {Ast | null} */
300
+ yIF () {
301
+ const map = this.peekSourceMap()
302
+ if (!this.check('もし')) { return null }
303
+ const mosi = this.get() // skip もし
304
+ while (this.check('comma')) { this.get() } // skip comma
305
+ let cond = null
306
+ try {
307
+ cond = this.yIFCond()
308
+ } catch (err) {
309
+ throw NakoSyntaxError.fromNode('『もし』文の条件で次のエラーがあります。\n' + err.message, mosi)
310
+ }
311
+ if (cond === null) {
312
+ throw NakoSyntaxError.fromNode('『もし』文で条件の指定が空です。', mosi)
313
+ }
314
+ let trueBlock = null
315
+ let falseBlock = null
316
+ let tanbun = false
317
+
318
+ // True Block
319
+ if (this.check('eol')) {
320
+ trueBlock = this.yBlock()
321
+ } else {
322
+ trueBlock = this.ySentence()
323
+ tanbun = true
324
+ }
325
+
326
+ // skip EOL
327
+ while (this.check('eol')) { this.get() }
328
+
329
+ // Flase Block
330
+ if (this.check('違えば')) {
331
+ this.get() // skip 違えば
332
+ while (this.check('comma')) { this.get() }
333
+ if (this.check('eol')) {
334
+ falseBlock = this.yBlock()
335
+ } else {
336
+ falseBlock = this.ySentence()
337
+ tanbun = true
338
+ }
339
+ }
340
+
341
+ if (tanbun === false) {
342
+ if (this.check('ここまで')) {
343
+ this.get()
344
+ } else {
345
+ throw NakoSyntaxError.fromNode('『もし』文で『ここまで』がありません。', mosi)
346
+ }
347
+ }
348
+ return {
349
+ type: 'if',
350
+ expr: cond,
351
+ block: trueBlock,
352
+ false_block: falseBlock,
353
+ josi: '',
354
+ ...map,
355
+ end: this.peekSourceMap()
356
+ }
357
+ }
358
+
359
+ ySpeedMode () {
360
+ const map = this.peekSourceMap()
361
+ if (!this.check2(['string', '実行速度優先'])) {
362
+ return null
363
+ }
364
+ const optionNode = this.get()
365
+ this.get()
366
+
367
+ const options = { 行番号無し: false, 暗黙の型変換無し: false, 強制ピュア: false, それ無効: false }
368
+ for (const name of optionNode.value.split('/')) {
369
+ // 全て有効化
370
+ if (name === '全て') {
371
+ for (const k of Object.keys(options)) {
372
+ options[k] = true
373
+ }
374
+ break
375
+ }
376
+
377
+ // 個別に有効化
378
+ if (Object.keys(options).includes(name)) {
379
+ options[name] = true
380
+ } else {
381
+ // 互換性を考えて、警告に留める。
382
+ this.logger.warn(`実行速度優先文のオプション『${name}』は存在しません。`, optionNode)
383
+ }
384
+ }
385
+
386
+ let multiline = false
387
+ if (this.check('ここから')) {
388
+ this.get()
389
+ multiline = true
390
+ } else if (this.check('eol')) {
391
+ multiline = true
392
+ }
393
+
394
+ let block = null
395
+ if (multiline) {
396
+ block = this.yBlock()
397
+ if (this.check('ここまで')) { this.get() }
398
+ } else {
399
+ block = this.ySentence()
400
+ }
401
+
402
+ return {
403
+ type: 'speed_mode',
404
+ options,
405
+ block,
406
+ line: optionNode.line,
407
+ josi: '',
408
+ ...map
409
+ }
410
+ }
411
+
412
+ yPerformanceMonitor () {
413
+ const map = this.peekSourceMap()
414
+ if (!this.check2(['string', 'パフォーマンスモニタ適用'])) {
415
+ return null
416
+ }
417
+ const optionNode = this.get()
418
+ this.get()
419
+
420
+ const options = { ユーザ関数: false, システム関数本体: false, システム関数: false }
421
+ for (const name of optionNode.value.split('/')) {
422
+ // 全て有効化
423
+ if (name === '全て') {
424
+ for (const k of Object.keys(options)) {
425
+ options[k] = true
426
+ }
427
+ break
428
+ }
429
+
430
+ // 個別に有効化
431
+ if (Object.keys(options).includes(name)) {
432
+ options[name] = true
433
+ } else {
434
+ // 互換性を考えて、警告に留める。
435
+ this.logger.warn(`パフォーマンスモニタ適用文のオプション『${name}』は存在しません。`, optionNode)
436
+ }
437
+ }
438
+
439
+ let multiline = false
440
+ if (this.check('ここから')) {
441
+ this.get()
442
+ multiline = true
443
+ } else if (this.check('eol')) {
444
+ multiline = true
445
+ }
446
+
447
+ let block = null
448
+ if (multiline) {
449
+ block = this.yBlock()
450
+ if (this.check('ここまで')) { this.get() }
451
+ } else {
452
+ block = this.ySentence()
453
+ }
454
+
455
+ return {
456
+ type: 'performance_monitor',
457
+ options,
458
+ block,
459
+ line: optionNode.line,
460
+ josi: '',
461
+ ...map
462
+ }
463
+ }
464
+
465
+ /** @returns {Ast | null} */
466
+ yTikuji () {
467
+ const map = this.peekSourceMap()
468
+ if (!this.check('逐次実行')) { return null }
469
+ const tikuji = this.get() // skip 逐次実行
470
+ const blocks = []
471
+ let errorBlock = null
472
+ if (!this.check('eol')) {
473
+ throw NakoSyntaxError.fromNode('『逐次実行』の直後は改行が必要です', tikuji)
474
+ }
475
+ // ブロックを読む
476
+ for (;;) {
477
+ if (this.check('ここまで')) { break }
478
+ if (this.check('eol')) {
479
+ this.get() // skip EOL
480
+ continue
481
+ }
482
+ if (this.check2(['エラー', 'ならば'])) {
483
+ this.get() // skip エラー
484
+ this.get() // skip ならば
485
+ errorBlock = this.yBlock()
486
+ break
487
+ }
488
+ let block = null
489
+ // 「先に」「次に」句はブロック宣言 #717 (ただしブロック以外も可能)
490
+ if (this.check('先に') || this.check('次に')) {
491
+ const tugini = this.get() // skip 先に | 次に
492
+ if (this.check('comma')) { this.get() }
493
+ if (this.check('eol')) { // block
494
+ block = this.yBlock()
495
+ if (!this.check('ここまで')) {
496
+ let tuginiType = '次に'
497
+ if (tugini != null) { tuginiType = tugini.type }
498
+ throw NakoSyntaxError.fromNode(`『${tuginiType}』...『ここまで』を対応させてください。`, tugini)
499
+ }
500
+ this.get() // skip 'ここまで'
501
+ } else { // line
502
+ block = this.ySentence()
503
+ }
504
+ } else {
505
+ block = this.ySentence()
506
+ }
507
+ // add block
508
+ if (block != null) { blocks.push(block) }
509
+ }
510
+ if (!this.check('ここまで')) {
511
+ console.log(blocks, this.peek())
512
+ throw NakoSyntaxError.fromNode('『逐次実行』...『ここまで』を対応させてください。', tikuji)
513
+ }
514
+ this.get() // skip 'ここまで'
515
+ return {
516
+ type: 'tikuji',
517
+ blocks: blocks,
518
+ errorBlock: errorBlock,
519
+ josi: '',
520
+ ...map,
521
+ end: this.peekSourceMap()
522
+ }
523
+ }
524
+
525
+ /** @returns {Ast | null} */
526
+ yGetArg () {
527
+ // 値を一つ読む
528
+ const value1 = this.yValue()
529
+ if (value1 === null) { return null }
530
+ // 計算式がある場合を考慮
531
+ const args = [value1]
532
+ while (!this.isEOF()) {
533
+ // 演算子がある?
534
+ const op = this.peek()
535
+ if (op && opPriority[op.type]) {
536
+ args.push(this.get())
537
+ // 演算子後の値を取得
538
+ const v = this.yValue()
539
+ if (v === null) {
540
+ throw NakoSyntaxError.fromNode(
541
+ `計算式で演算子『${op.value}』後に値がありません`,
542
+ value1)
543
+ }
544
+ args.push(v)
545
+ continue
546
+ }
547
+ break
548
+ }
549
+ if (args.length === 0) { return null }
550
+ if (args.length === 1) { return args[0] }
551
+ return this.infixToAST(args)
552
+ }
553
+
554
+ infixToPolish (list) {
555
+ // 中間記法から逆ポーランドに変換
556
+ const priority = (t) => {
557
+ if (opPriority[t.type]) { return opPriority[t.type] }
558
+ return 10
559
+ }
560
+ const stack = []
561
+ const polish = []
562
+ while (list.length > 0) {
563
+ const t = list.shift()
564
+ while (stack.length > 0) { // 優先順位を見て移動する
565
+ const sTop = stack[stack.length - 1]
566
+ if (priority(t) > priority(sTop)) { break }
567
+ polish.push(stack.pop())
568
+ }
569
+ stack.push(t)
570
+ }
571
+ // 残った要素を積み替える
572
+ while (stack.length > 0) { polish.push(stack.pop()) }
573
+ return polish
574
+ }
575
+
576
+ /** @returns {Ast | null} */
577
+ infixToAST (list) {
578
+ if (list.length === 0) { return null }
579
+ // 逆ポーランドを構文木に
580
+ const josi = list[list.length - 1].josi
581
+ const node = list[list.length - 1]
582
+ const polish = this.infixToPolish(list)
583
+ /** @type {Ast[]} */
584
+ const stack = []
585
+ for (const t of polish) {
586
+ if (!opPriority[t.type]) { // 演算子ではない
587
+ stack.push(t)
588
+ continue
589
+ }
590
+ const b = stack.pop()
591
+ const a = stack.pop()
592
+ if (a === undefined || b === undefined) {
593
+ this.logger.debug('--- 計算式(逆ポーランド) ---\n' + JSON.stringify(polish))
594
+ throw NakoSyntaxError.fromNode('計算式でエラー', node)
595
+ }
596
+ /** @type {Ast} */
597
+ const op = {
598
+ type: 'op',
599
+ operator: t.type,
600
+ left: a,
601
+ right: b,
602
+ josi: josi,
603
+ startOffset: a.startOffset,
604
+ endOffset: a.endOffset,
605
+ line: a.line,
606
+ column: a.column,
607
+ file: a.file
608
+ }
609
+ stack.push(op)
610
+ }
611
+ return stack.pop()
612
+ }
613
+
614
+ yGetArgParen (y) { // C言語風呼び出しでカッコの中を取得
615
+ let isClose = false
616
+ const si = this.stack.length
617
+ while (!this.isEOF()) {
618
+ if (this.check(')')) {
619
+ isClose = true
620
+ break
621
+ }
622
+ const v = this.yGetArg()
623
+ if (v) {
624
+ this.pushStack(v)
625
+ if (this.check('comma')) { this.get() }
626
+ continue
627
+ }
628
+ break
629
+ }
630
+ if (!isClose) {
631
+ throw NakoSyntaxError.fromNode(`C風関数『${y[0].value}』でカッコが閉じていません`, y[0])
632
+ }
633
+ const a = []
634
+ while (si < this.stack.length) {
635
+ const v = this.popStack()
636
+ a.unshift(v)
637
+ }
638
+ return a
639
+ }
640
+
641
+ /** @returns {Ast | null} */
642
+ yRepeatTime () {
643
+ const map = this.peekSourceMap()
644
+ if (!this.check('回')) { return null }
645
+ this.get() // skip '回'
646
+ if (this.check('comma')) { this.get() } // skip comma
647
+ if (this.check('繰り返す')) { this.get() } // skip 'N回、繰り返す' (#924)
648
+ let num = this.popStack([])
649
+ let multiline = false
650
+ let block = null
651
+ if (num === null) { num = { type: 'word', value: 'それ', josi: '', ...map, end: this.peekSourceMap() } }
652
+ if (this.check('comma')) { this.get() }
653
+ if (this.check('ここから')) {
654
+ this.get()
655
+ multiline = true
656
+ } else if (this.check('eol')) {
657
+ multiline = true
658
+ }
659
+ if (multiline) { // multiline
660
+ block = this.yBlock()
661
+ if (this.check('ここまで')) { this.get() } else { throw NakoSyntaxError.fromNode('『ここまで』がありません。『回』...『ここまで』を対応させてください。', map) }
662
+ } else {
663
+ // singleline
664
+ block = this.ySentence()
665
+ }
666
+
667
+ return {
668
+ type: 'repeat_times',
669
+ value: num,
670
+ block: block,
671
+ josi: '',
672
+ ...map,
673
+ end: this.peekSourceMap()
674
+ }
675
+ }
676
+
677
+ /** @returns {Ast | null} */
678
+ yWhile () {
679
+ const map = this.peekSourceMap()
680
+ if (!this.check('間')) { return null }
681
+ this.get() // skip '間'
682
+ while (this.check('comma')) { this.get() } // skip ','
683
+ if (this.check('繰り返す')) { this.get() } // skip '繰り返す' #927
684
+ const cond = this.popStack([])
685
+ if (cond === null) {
686
+ throw NakoSyntaxError.fromNode('『間』で条件がありません。', cond)
687
+ }
688
+ if (this.check('comma')) { this.get() }
689
+ if (!this.checkTypes(['ここから', 'eol'])) {
690
+ throw NakoSyntaxError.fromNode('『間』の直後は改行が必要です', cond)
691
+ }
692
+
693
+ const block = this.yBlock()
694
+ if (this.check('ここまで')) { this.get() }
695
+ return {
696
+ type: 'while',
697
+ cond,
698
+ block,
699
+ josi: '',
700
+ ...map,
701
+ end: this.peekSourceMap()
702
+ }
703
+ }
704
+
705
+ /** @returns {Ast | null} */
706
+ yFor () {
707
+ const map = this.peekSourceMap()
708
+ if (!this.check('繰り返す')) { return null }
709
+ const kurikaesu = this.get()
710
+ const vTo = this.popStack(['まで'])
711
+ const vFrom = this.popStack(['から'])
712
+ const word = this.popStack(['を', 'で'])
713
+ if (vFrom === null || vTo === null) {
714
+ throw NakoSyntaxError.fromNode('『繰り返す』文でAからBまでの指定がありません。', kurikaesu)
715
+ }
716
+
717
+ let multiline = false
718
+ if (this.check('ここから')) {
719
+ multiline = true
720
+ this.get()
721
+ } else if (this.check('eol')) {
722
+ multiline = true
723
+ this.get()
724
+ }
725
+ let block = null
726
+ if (multiline) {
727
+ block = this.yBlock()
728
+ if (this.check('ここまで')) {
729
+ this.get()
730
+ } else {
731
+ throw NakoSyntaxError.fromNode('『ここまで』がありません。『繰り返す』...『ここまで』を対応させてください。', map)
732
+ }
733
+ } else { block = this.ySentence() }
734
+
735
+ return {
736
+ type: 'for',
737
+ from: vFrom,
738
+ to: vTo,
739
+ word,
740
+ block,
741
+ josi: '',
742
+ ...map,
743
+ end: this.peekSourceMap()
744
+ }
745
+ }
746
+
747
+ /** @returns {Ast | null} */
748
+ yReturn () {
749
+ const map = this.peekSourceMap()
750
+ if (!this.check('戻る')) { return null }
751
+ this.get() // skip '戻る'
752
+ const v = this.popStack(['で', 'を'])
753
+ return {
754
+ type: 'return',
755
+ value: v,
756
+ josi: '',
757
+ ...map,
758
+ end: this.peekSourceMap()
759
+ }
760
+ }
761
+
762
+ /** @returns {Ast | null} */
763
+ yForEach () {
764
+ const map = this.peekSourceMap()
765
+ if (!this.check('反復')) { return null }
766
+ this.get() // skip '反復'
767
+ while (this.check('comma')) { this.get() } // skip ','
768
+ const target = this.popStack(['を'])
769
+ const name = this.popStack(['で'])
770
+ let block = null
771
+ let multiline = false
772
+ if (this.check('ここから')) {
773
+ multiline = true
774
+ this.get()
775
+ } else if (this.check('eol')) { multiline = true }
776
+
777
+ if (multiline) {
778
+ block = this.yBlock()
779
+ if (this.check('ここまで')) { this.get() }
780
+ } else { block = this.ySentence() }
781
+
782
+ return {
783
+ type: 'foreach',
784
+ name,
785
+ target,
786
+ block,
787
+ josi: '',
788
+ ...map,
789
+ end: this.peekSourceMap()
790
+ }
791
+ }
792
+
793
+ /** @returns {Ast | null} */
794
+ ySwitch () {
795
+ const map = this.peekSourceMap()
796
+ if (!this.check('条件分岐')) { return null }
797
+ const joukenbunki = this.get() // skip '条件分岐'
798
+ const eol = this.get() // skip 'eol'
799
+ const value = this.popStack(['で'])
800
+ if (!value) {
801
+ throw NakoSyntaxError.fromNode('『(値)で条件分岐』のように記述してください。', joukenbunki)
802
+ }
803
+ if (eol.type !== 'eol') {
804
+ throw NakoSyntaxError.fromNode('『条件分岐』の直後は改行してください。', joukenbunki)
805
+ }
806
+ let isDefaultClause = false // 「違えば」内かどうか
807
+ let skippedKokomade = false
808
+ const cases = []
809
+ while (!this.isEOF()) {
810
+ if (this.check('ここまで')) {
811
+ if (skippedKokomade) {
812
+ throw NakoSyntaxError.fromNode('『条件分岐』は『(条件)ならば〜ここまで』と記述してください。', joukenbunki)
813
+ }
814
+ this.get() // skip ここまで
815
+ break
816
+ }
817
+ if (this.check('eol')) {
818
+ this.get()
819
+ continue
820
+ }
821
+ if (isDefaultClause) {
822
+ throw NakoSyntaxError.fromNode('『条件分岐』で『違えば〜ここまで』の後に処理を続けることは出来ません。', joukenbunki)
823
+ }
824
+ // 違えば?
825
+ let cond = this.peek()
826
+ if (cond.type === '違えば') {
827
+ skippedKokomade = false
828
+ isDefaultClause = true
829
+ this.get() // skip 違えば
830
+ if (this.check('comma')) { this.get() } // skip ','
831
+ } else {
832
+ if (skippedKokomade) {
833
+ throw NakoSyntaxError.fromNode('『条件分岐』は『(条件)ならば〜ここまで』と記述してください。', joukenbunki)
834
+ }
835
+ // 「**ならば」を得る
836
+ cond = this.yValue()
837
+ const naraba = this.get() // skip ならば
838
+ if (naraba.type !== 'ならば') {
839
+ throw NakoSyntaxError.fromNode('『条件分岐』で条件は**ならばと記述してください。', joukenbunki)
840
+ }
841
+ if (this.check('comma')) { this.get() } // skip ','
842
+ }
843
+ // 条件にあったときに実行すること
844
+ const condBlock = this.yBlock()
845
+ if (this.peek().type === 'ここまで') {
846
+ this.get() // skip ここまで
847
+ } else {
848
+ if (isDefaultClause) {
849
+ throw NakoSyntaxError.fromNode('『条件分岐』は『違えば〜ここまで』と記述してください。', joukenbunki)
850
+ }
851
+ // 次が「違えば」の場合に限り、「もし〜ここまで」の「ここまで」を省略できる
852
+ skippedKokomade = true
853
+ }
854
+ cases.push([cond, condBlock])
855
+ }
856
+
857
+ return {
858
+ type: 'switch',
859
+ value,
860
+ cases,
861
+ josi: '',
862
+ ...map,
863
+ end: this.peekSourceMap()
864
+ }
865
+ }
866
+
867
+ /** @returns {Ast | null} */
868
+ yMumeiFunc () { // 無名関数の定義
869
+ const map = this.peekSourceMap()
870
+ if (!this.check('def_func')) { return null }
871
+ const def = this.get()
872
+ let args = []
873
+ // 「,」を飛ばす
874
+ if (this.check('comma')) { this.get() }
875
+ // 関数の引数定義は省略できる
876
+ if (this.check('(')) { args = this.yDefFuncReadArgs() }
877
+ // 「,」を飛ばす
878
+ if (this.check('comma')) { this.get() }
879
+ // ブロックを読む
880
+ this.saveStack()
881
+ const block = this.yBlock()
882
+ // 末尾の「ここまで」をチェック - もしなければエラーにする #1045
883
+ if (!this.check('ここまで')) {
884
+ throw NakoSyntaxError.fromNode('『ここまで』がありません。『には』構文か無名関数の末尾に『ここまで』が必要です。', map)
885
+ }
886
+ this.get() // skip ここまで
887
+ this.loadStack()
888
+ return {
889
+ type: 'func_obj',
890
+ args,
891
+ block,
892
+ meta: def.meta,
893
+ josi: '',
894
+ ...map,
895
+ end: this.peekSourceMap()
896
+ }
897
+ }
898
+
899
+ /** @returns {import('./nako3').Ast | null | undefined} */
900
+ yCall () {
901
+ if (this.isEOF()) { return null }
902
+ while (!this.isEOF()) {
903
+ const map = this.peekSourceMap()
904
+ // 代入
905
+ if (this.check('代入')) {
906
+ const dainyu = this.get()
907
+ const value = this.popStack(['を'])
908
+ const word = this.popStack(['へ', 'に'])
909
+ if (!word || (word.type !== 'word' && word.type !== 'func' && word.type !== '配列参照')) {
910
+ throw NakoSyntaxError.fromNode('代入文で代入先の変数が見当たりません。', dainyu)
911
+ }
912
+
913
+ switch (word.type) {
914
+ case '配列参照': // 配列への代入
915
+ return { type: 'let_array', name: word.name, index: word.index, value: value, josi: '', ...map, end: this.peekSourceMap() }
916
+ default:
917
+ return { type: 'let', name: word, value: value, josi: '', ...map, end: this.peekSourceMap() }
918
+ }
919
+ }
920
+ if (this.check('定める')) {
921
+ const dainyu = this.get()
922
+ const word = this.popStack(['を'])
923
+ const value = this.popStack(['に'])
924
+ if (!word || word.type !== 'word') {
925
+ throw NakoSyntaxError.fromNode('代入文で代入先の変数が見当たりません。', dainyu)
926
+ }
927
+ return {
928
+ type: 'def_local_var',
929
+ name: word,
930
+ vartype: '定数',
931
+ value: value,
932
+ ...map,
933
+ end: this.peekSourceMap()
934
+ }
935
+ }
936
+ // 制御構文
937
+ if (this.check('ここから')) { this.get() }
938
+ if (this.check('回')) { return this.yRepeatTime() }
939
+ if (this.check('間')) { return this.yWhile() }
940
+ if (this.check('繰り返す')) { return this.yFor() }
941
+ if (this.check('反復')) { return this.yForEach() }
942
+ if (this.check('条件分岐')) { return this.ySwitch() }
943
+ // 戻す
944
+ if (this.check('戻る')) { return this.yReturn() }
945
+ // C言語風関数
946
+ if (this.check2([['func', 'word'], '(']) && this.peek().josi === '') { // C言語風
947
+ const t = this.yValue()
948
+ if (t.type === 'func' && (t.josi === '' || keizokuJosi.indexOf(t.josi) >= 0)) {
949
+ t.josi = ''
950
+ return t // 関数なら値とする
951
+ }
952
+ this.pushStack(t)
953
+ if (this.check('comma')) { this.get() }
954
+ continue
955
+ }
956
+ // なでしこ式関数
957
+ if (this.check('func')) {
958
+ const r = this.yCallFunc()
959
+ if (r === null) { continue }
960
+ // 「〜する間」の形ならスタックに積む。
961
+ if (this.check('間')) {
962
+ this.pushStack(r)
963
+ continue
964
+ }
965
+ return r
966
+ }
967
+ // 値のとき → スタックに載せる
968
+ const t = this.yGetArg()
969
+ if (t) {
970
+ this.pushStack(t)
971
+ continue
972
+ }
973
+ break
974
+ } // end of while
975
+ // 助詞が余ってしまった場合
976
+ if (this.stack.length > 0) {
977
+ this.logger.debug('--- stack dump ---\n' + JSON.stringify(this.stack, null, 2) + '\npeek: ' + JSON.stringify(this.peek(), null, 2))
978
+ let msgDebug = `不完全な文です。${this.stack.map((n) => this.nodeToStr(n, { depth: 0 }, true)).join('、')}が解決していません。`
979
+ let msg = `不完全な文です。${this.stack.map((n) => this.nodeToStr(n, { depth: 0 }, false)).join('、')}が解決していません。`
980
+
981
+ // 各ノードについて、更に詳細な情報があるなら表示
982
+ for (const n of this.stack) {
983
+ const d0 = this.nodeToStr(n, { depth: 0 }, false)
984
+ const d1 = this.nodeToStr(n, { depth: 1 }, false)
985
+ if (d0 !== d1) {
986
+ msgDebug += `${this.nodeToStr(n, { depth: 0 }, true)}は${this.nodeToStr(n, { depth: 1 }, true)}として使われています。`
987
+ msg += `${d0}は${d1}として使われています。`
988
+ }
989
+ }
990
+
991
+ const first = this.stack[0]
992
+ const last = this.stack[this.stack.length - 1]
993
+ this.logger.debug(msgDebug, first)
994
+ throw NakoSyntaxError.fromNode(msg, first, last)
995
+ }
996
+ return this.popStack([])
997
+ }
998
+
999
+ /** @returns {Ast | null} */
1000
+ yCallFunc () {
1001
+ const map = this.peekSourceMap()
1002
+ const t = this.get()
1003
+ const f = t.meta
1004
+ // (関数)には ... 構文 ... https://github.com/kujirahand/nadesiko3/issues/66
1005
+ let funcObj = null
1006
+ if (t.josi === 'には') {
1007
+ try {
1008
+ funcObj = this.yMumeiFunc()
1009
+ } catch (err) {
1010
+ throw NakoSyntaxError.fromNode(`『${t.value}には...』で無名関数の定義で以下の間違いがあります。\n${err.message}`, t)
1011
+ }
1012
+ if (funcObj === null) { throw NakoSyntaxError.fromNode('『Fには』構文がありましたが、関数定義が見当たりません。', t) }
1013
+ }
1014
+ if (!f || typeof f.josi === 'undefined') { throw NakoSyntaxError.fromNode('関数の定義でエラー。', t) }
1015
+
1016
+ const args = []
1017
+ let nullCount = 0
1018
+ let valueCount = 0
1019
+ for (let i = 0; i < f.josi.length; i++) {
1020
+ while (true) {
1021
+ let popArg = this.popStack(f.josi[i])
1022
+ if (popArg !== null) {
1023
+ valueCount++
1024
+ } else if (i < f.josi.length - 1 || !f.isVariableJosi) {
1025
+ nullCount++
1026
+ popArg = funcObj
1027
+ } else {
1028
+ break
1029
+ }
1030
+
1031
+ if (popArg !== null && f.funcPointers !== undefined && f.funcPointers[i] !== null) {
1032
+ if (popArg.type === 'func') { // 引数が関数の参照渡しに該当する場合、typeを『func_pointer』に変更
1033
+ popArg.type = 'func_pointer'
1034
+ } else {
1035
+ throw NakoSyntaxError.fromNode(
1036
+ `関数『${t.value}』の引数『${f.varnames[i]}』には関数オブジェクトが必要です。`, t)
1037
+ }
1038
+ }
1039
+
1040
+ args.push(popArg)
1041
+
1042
+ if (i < f.josi.length - 1 || !f.isVariableJosi) {
1043
+ break
1044
+ }
1045
+ }
1046
+ }
1047
+ // 1つだけなら、変数「それ」で補完される
1048
+ if (nullCount >= 2 && (valueCount > 0 || t.josi === '' || keizokuJosi.indexOf(t.josi) >= 0)) { throw NakoSyntaxError.fromNode(`関数『${t.value}』の引数が不足しています。`, t) }
1049
+
1050
+ const funcNode = { type: 'func', name: t.value, args: args, josi: t.josi, ...map, end: this.peekSourceMap() }
1051
+ // 言い切りならそこで一度切る
1052
+ if (t.josi === '') { return funcNode }
1053
+
1054
+ // **して、** の場合も一度切る
1055
+ if (keizokuJosi.indexOf(t.josi) >= 0) {
1056
+ funcNode.josi = 'して'
1057
+ return funcNode
1058
+ }
1059
+ // 続き
1060
+ funcNode.meta = f
1061
+ this.pushStack(funcNode)
1062
+ return null
1063
+ }
1064
+
1065
+ /** 関数呼び出し演算子 #891
1066
+ * @returns {Ast | null} */
1067
+ yCallOp () {
1068
+ if (!this.check2(['func', '←'])) { return null }
1069
+ const map = this.peekSourceMap()
1070
+ // 関数名を得る
1071
+ const word = this.get()
1072
+ if (word == null) { throw new Error('関数が取得できません。') }
1073
+ try {
1074
+ const op = this.get()
1075
+ if (op == null) { throw new Error('関数呼び出し演算子が取得できません。') }
1076
+ const funcName = word.value
1077
+ // 関数の引数0をチェック
1078
+ const argCount = word.meta.josi.length
1079
+ if (argCount === 0) {
1080
+ throw NakoSyntaxError.fromNode(`引数がない関数『${funcName}』を関数呼び出し演算子で呼び出すことはできません。`, word)
1081
+ }
1082
+ // 引数を順に取得
1083
+ const curStackPos = this.stack.length
1084
+ while (!this.isEOF()) {
1085
+ const t = this.yGetArg()
1086
+ if (t) {
1087
+ this.pushStack(t)
1088
+ if ((this.stack.length - curStackPos) === argCount) { break }
1089
+ continue
1090
+ }
1091
+ break
1092
+ }
1093
+ // この場合第一引数の省略は認めない
1094
+ const realArgCount = this.stack.length - curStackPos
1095
+ if (realArgCount !== argCount) {
1096
+ throw NakoSyntaxError.fromNode(`関数『${funcName}』呼び出しで引数の数(${realArgCount})が定義(${argCount})と違います。`, word)
1097
+ }
1098
+ // 引数を取り出す
1099
+ const tmpList = this.stack.splice(curStackPos, argCount)
1100
+ // 引数が1つなら助詞は省略が可能。ただし、引数が2つ以上の時、正しく助詞の順序を入れ替える
1101
+ let argList = tmpList
1102
+ if (argCount >= 2) {
1103
+ argList = []
1104
+ const defList = word.meta.josi
1105
+ defList.forEach((josiList, i) => {
1106
+ for (let j = 0; j < tmpList.length; j++) {
1107
+ const t = tmpList[j]
1108
+ if (josiList.indexOf(t.josi) >= 0) {
1109
+ argList[i] = t
1110
+ return
1111
+ }
1112
+ }
1113
+ const josiStr = josiList.join(',')
1114
+ throw new Error(`助詞『${josiStr}』が見当たりません。`)
1115
+ })
1116
+ }
1117
+ // funcノードを返す
1118
+ return {
1119
+ type: 'func',
1120
+ name: funcName,
1121
+ args: argList,
1122
+ setter: true, // 重要
1123
+ josi: '',
1124
+ ...map,
1125
+ end: this.peekSourceMap()
1126
+ }
1127
+ } catch (err) {
1128
+ this.logger.debug(`${this.nodeToStr(word, { depth: 0 }, true)}の関数呼び出しで引数(『←』以降)が読み取れません。`, word)
1129
+ throw NakoSyntaxError.fromNode(
1130
+ `${this.nodeToStr(word, { depth: 0 }, false)}の関数呼び出しでエラーがあります。\n${err.message}`, word)
1131
+ }
1132
+ }
1133
+
1134
+ /** @returns {Ast | null} */
1135
+ yLet () {
1136
+ const map = this.peekSourceMap()
1137
+ // 通常の変数
1138
+ if (this.check2(['word', 'eq'])) {
1139
+ const word = this.peek()
1140
+ let threw = false
1141
+ try {
1142
+ if (this.accept(['word', 'eq', this.yCalc]) || this.accept(['word', 'eq', this.ySentence])) {
1143
+ if (this.y[2].type === 'eol') {
1144
+ throw new Error('値が空です。')
1145
+ }
1146
+ return {
1147
+ type: 'let',
1148
+ name: this.y[0],
1149
+ value: this.y[2],
1150
+ ...map,
1151
+ end: this.peekSourceMap()
1152
+ }
1153
+ } else {
1154
+ threw = true
1155
+ this.logger.debug(`${this.nodeToStr(word, { depth: 1 }, true)}への代入文で計算式に書き間違いがあります。`, word)
1156
+ throw NakoSyntaxError.fromNode(`${this.nodeToStr(word, { depth: 1 }, false)}への代入文で計算式に書き間違いがあります。`, word)
1157
+ }
1158
+ } catch (err) {
1159
+ if (threw) {
1160
+ throw err
1161
+ }
1162
+ this.logger.debug(`${this.nodeToStr(word, { depth: 1 }, true)}への代入文で計算式に以下の書き間違いがあります。\n${err.message}`, word)
1163
+ throw NakoSyntaxError.fromNode(`${this.nodeToStr(word, { depth: 1 }, false)}への代入文で計算式に以下の書き間違いがあります。\n${err.message}`, word)
1164
+ }
1165
+ }
1166
+
1167
+ // let_array ?
1168
+ if (this.check2(['word', '@'])) {
1169
+ const la = this.yLetArrayAt(map)
1170
+ if (la) { return la }
1171
+ }
1172
+ if (this.check2(['word', '['])) {
1173
+ const lb = this.yLetArrayBracket(map)
1174
+ if (lb) { return lb }
1175
+ }
1176
+
1177
+ // ローカル変数定義
1178
+ if (this.accept(['word', 'とは'])) {
1179
+ const word = this.y[0]
1180
+ if (!this.checkTypes(['変数', '定数'])) {
1181
+ throw NakoSyntaxError.fromNode('ローカル変数『' + word.value + '』の定義エラー', word)
1182
+ }
1183
+
1184
+ const vtype = this.get() // 変数
1185
+ // 初期値がある?
1186
+ let value = null
1187
+ if (this.check('eq')) {
1188
+ this.get()
1189
+ value = this.yCalc()
1190
+ }
1191
+ return {
1192
+ type: 'def_local_var',
1193
+ name: word,
1194
+ vartype: vtype.type,
1195
+ value,
1196
+ ...map,
1197
+ end: this.peekSourceMap()
1198
+ }
1199
+ }
1200
+ // ローカル変数定義(その2)
1201
+ if (this.accept(['変数', 'word', 'eq', this.yCalc])) {
1202
+ return {
1203
+ type: 'def_local_var',
1204
+ name: this.y[1],
1205
+ vartype: '変数',
1206
+ value: this.y[3],
1207
+ ...map,
1208
+ end: this.peekSourceMap()
1209
+ }
1210
+ }
1211
+
1212
+ if (this.accept(['定数', 'word', 'eq', this.yCalc])) {
1213
+ return {
1214
+ type: 'def_local_var',
1215
+ name: this.y[1],
1216
+ vartype: '定数',
1217
+ value: this.y[3],
1218
+ ...map,
1219
+ end: this.peekSourceMap()
1220
+ }
1221
+ }
1222
+
1223
+ // 複数定数への代入 #563
1224
+ if (this.accept(['定数', this.yJSONArray, 'eq', this.yCalc])) {
1225
+ const names = this.y[1]
1226
+ // check array
1227
+ if (names && names.value instanceof Array) {
1228
+ for (const i in names.value) {
1229
+ if (names.value[i].type !== 'word') {
1230
+ throw NakoSyntaxError.fromNode(`複数定数の代入文${i + 1}番目でエラー。『定数[A,B,C]=[1,2,3]』の書式で記述してください。`, this.y[0])
1231
+ }
1232
+ }
1233
+ } else {
1234
+ throw NakoSyntaxError.fromNode('複数定数の代入文でエラー。『定数[A,B,C]=[1,2,3]』の書式で記述してください。', this.y[0])
1235
+ }
1236
+ return {
1237
+ type: 'def_local_varlist',
1238
+ names: names.value,
1239
+ vartype: '定数',
1240
+ value: this.y[3],
1241
+ ...map,
1242
+ end: this.peekSourceMap()
1243
+ }
1244
+ }
1245
+ // 複数変数への代入 #563
1246
+ if (this.accept(['変数', this.yJSONArray, 'eq', this.yCalc])) {
1247
+ const names = this.y[1]
1248
+ // check array
1249
+ if (names && names.value instanceof Array) {
1250
+ for (const i in names.value) {
1251
+ if (names.value[i].type !== 'word') {
1252
+ throw NakoSyntaxError.fromNode(`複数変数の代入文${i + 1}番目でエラー。『変数[A,B,C]=[1,2,3]』の書式で記述してください。`, this.y[0])
1253
+ }
1254
+ }
1255
+ } else {
1256
+ throw NakoSyntaxError.fromNode('複数変数の代入文でエラー。『変数[A,B,C]=[1,2,3]』の書式で記述してください。', this.y[0])
1257
+ }
1258
+ return {
1259
+ type: 'def_local_varlist',
1260
+ names: names.value,
1261
+ vartype: '変数',
1262
+ value: this.y[3],
1263
+ ...map,
1264
+ end: this.peekSourceMap()
1265
+ }
1266
+ }
1267
+
1268
+ // 複数変数への代入 #563
1269
+ if (this.check2(['word', 'comma', 'word'])) {
1270
+ // 2 word
1271
+ if (this.accept(['word', 'comma', 'word', 'eq', this.yCalc])) {
1272
+ return {
1273
+ type: 'def_local_varlist',
1274
+ names: [this.y[0], this.y[2]],
1275
+ vartype: '変数',
1276
+ value: this.y[4],
1277
+ ...map,
1278
+ end: this.peekSourceMap()
1279
+ }
1280
+ }
1281
+ // 3 word
1282
+ if (this.accept(['word', 'comma', 'word', 'comma', 'word', 'eq', this.yCalc])) {
1283
+ return {
1284
+ type: 'def_local_varlist',
1285
+ names: [this.y[0], this.y[2], this.y[4]],
1286
+ vartype: '変数',
1287
+ value: this.y[6],
1288
+ ...map,
1289
+ end: this.peekSourceMap()
1290
+ }
1291
+ }
1292
+ // 4 word
1293
+ if (this.accept(['word', 'comma', 'word', 'comma', 'word', 'comma', 'word', 'eq', this.yCalc])) {
1294
+ return {
1295
+ type: 'def_local_varlist',
1296
+ names: [this.y[0], this.y[2], this.y[4], this.y[6]],
1297
+ vartype: '変数',
1298
+ value: this.y[8],
1299
+ ...map,
1300
+ end: this.peekSourceMap()
1301
+ }
1302
+ }
1303
+ // 5 word
1304
+ if (this.accept(['word', 'comma', 'word', 'comma', 'word', 'comma', 'word', 'comma', 'word', 'eq', this.yCalc])) {
1305
+ return {
1306
+ type: 'def_local_varlist',
1307
+ names: [this.y[0], this.y[2], this.y[4], this.y[6], this.y[8]],
1308
+ vartype: '変数',
1309
+ value: this.y[10],
1310
+ ...map,
1311
+ end: this.peekSourceMap()
1312
+ }
1313
+ }
1314
+ }
1315
+ return null
1316
+ }
1317
+
1318
+ /** @returns {Ast | null} */
1319
+ yLetArrayAt (map) {
1320
+ // 一次元配列
1321
+ if (this.accept(['word', '@', this.yValue, 'eq', this.yCalc])) {
1322
+ return {
1323
+ type: 'let_array',
1324
+ name: this.y[0],
1325
+ index: [this.y[2]],
1326
+ value: this.y[4],
1327
+ ...map,
1328
+ end: this.peekSourceMap()
1329
+ }
1330
+ }
1331
+
1332
+ // 二次元配列
1333
+ if (this.accept(['word', '@', this.yValue, '@', this.yValue, 'eq', this.yCalc])) {
1334
+ return {
1335
+ type: 'let_array',
1336
+ name: this.y[0],
1337
+ index: [this.y[2], this.y[4]],
1338
+ value: this.y[6],
1339
+ ...map,
1340
+ end: this.peekSourceMap()
1341
+ }
1342
+ }
1343
+
1344
+ // 三次元配列
1345
+ if (this.accept(['word', '@', this.yValue, '@', this.yValue, '@', this.yValue, 'eq', this.yCalc])) {
1346
+ return {
1347
+ type: 'let_array',
1348
+ name: this.y[0],
1349
+ index: [this.y[2], this.y[4], this.y[6]],
1350
+ value: this.y[8],
1351
+ ...map,
1352
+ end: this.peekSourceMap()
1353
+ }
1354
+ }
1355
+
1356
+ // 二次元配列(カンマ指定)
1357
+ if (this.accept(['word', '@', this.yValue, 'comma', this.yValue, 'eq', this.yCalc])) {
1358
+ return {
1359
+ type: 'let_array',
1360
+ name: this.y[0],
1361
+ index: [this.y[2], this.y[4]],
1362
+ value: this.y[6],
1363
+ ...map,
1364
+ end: this.peekSourceMap()
1365
+ }
1366
+ }
1367
+
1368
+ // 三次元配列(カンマ指定)
1369
+ if (this.accept(['word', '@', this.yValue, 'comma', this.yValue, 'comma', this.yValue, 'eq', this.yCalc])) {
1370
+ return {
1371
+ type: 'let_array',
1372
+ name: this.y[0],
1373
+ index: [this.y[2], this.y[4], this.y[6]],
1374
+ value: this.y[8],
1375
+ ...map,
1376
+ end: this.peekSourceMap()
1377
+ }
1378
+ }
1379
+ return null
1380
+ }
1381
+
1382
+ /** @returns {Ast | null} */
1383
+ yLetArrayBracket (map) {
1384
+ // 一次元配列
1385
+ if (this.accept(['word', '[', this.yCalc, ']', 'eq', this.yCalc])) {
1386
+ return {
1387
+ type: 'let_array',
1388
+ name: this.y[0],
1389
+ index: [this.y[2]],
1390
+ value: this.y[5],
1391
+ ...map,
1392
+ end: this.peekSourceMap()
1393
+ }
1394
+ }
1395
+
1396
+ // 二次元配列
1397
+ if (this.accept(['word', '[', this.yCalc, ']', '[', this.yCalc, ']', 'eq', this.yCalc])) {
1398
+ return {
1399
+ type: 'let_array',
1400
+ name: this.y[0],
1401
+ index: [this.y[2], this.y[5]],
1402
+ value: this.y[8],
1403
+ tag: '2',
1404
+ ...map,
1405
+ end: this.peekSourceMap()
1406
+ }
1407
+ }
1408
+
1409
+ // 三次元配列
1410
+ if (this.accept(['word', '[', this.yCalc, ']', '[', this.yCalc, ']', '[', this.yCalc, ']', 'eq', this.yCalc])) {
1411
+ return {
1412
+ type: 'let_array',
1413
+ name: this.y[0],
1414
+ index: [this.y[2], this.y[5], this.y[8]],
1415
+ value: this.y[11],
1416
+ ...map,
1417
+ end: this.peekSourceMap()
1418
+ }
1419
+ }
1420
+ return null
1421
+ }
1422
+
1423
+ /** @returns {Ast | null} */
1424
+ yCalc () {
1425
+ const map = this.peekSourceMap()
1426
+ if (this.check('eol')) { return null }
1427
+ // 値を一つ読む
1428
+ const t = this.yGetArg()
1429
+ if (!t) { return null }
1430
+ // 助詞がある? つまり、関数呼び出しがある?
1431
+ if (t.josi === '') { return t } // 値だけの場合
1432
+ // 関数の呼び出しがあるなら、スタックに載せて関数読み出しを呼ぶ
1433
+ this.pushStack(t)
1434
+ const t1 = this.yCall()
1435
+ // それが連文か確認
1436
+ if (t1.josi !== 'して') { return t1 } // 連文ではない
1437
+ // 連文なら右側を読んで左側とくっつける
1438
+ const t2 = this.yCalc()
1439
+ if (!t2) { return t1 }
1440
+ return {
1441
+ type: 'renbun',
1442
+ left: t1,
1443
+ right: t2,
1444
+ josi: t2.josi,
1445
+ ...map,
1446
+ end: this.peekSourceMap()
1447
+ }
1448
+ }
1449
+
1450
+ /** @returns {Ast | null} */
1451
+ yValueKakko () {
1452
+ if (!this.check('(')) { return null }
1453
+ const t = this.get() // skip '('
1454
+ this.saveStack()
1455
+ const v = this.yCalc() || this.ySentence()
1456
+ if (v === null) {
1457
+ const v2 = this.get()
1458
+ this.logger.debug('(...)の解析エラー。' + this.nodeToStr(v2, { depth: 1 }, true) + 'の近く', t)
1459
+ throw NakoSyntaxError.fromNode('(...)の解析エラー。' + this.nodeToStr(v2, { depth: 1 }, false) + 'の近く', t)
1460
+ }
1461
+ if (!this.check(')')) {
1462
+ this.logger.debug('(...)の解析エラー。' + this.nodeToStr(v, { depth: 1 }, true) + 'の近く', t)
1463
+ throw NakoSyntaxError.fromNode('(...)の解析エラー。' + this.nodeToStr(v, { depth: 1 }, false) + 'の近く', t)
1464
+ }
1465
+
1466
+ const closeParent = this.get() // skip ')'
1467
+ this.loadStack()
1468
+ v.josi = closeParent.josi
1469
+ return v
1470
+ }
1471
+
1472
+ /** @returns {Ast | null} */
1473
+ yValue () {
1474
+ const map = this.peekSourceMap()
1475
+
1476
+ // カンマなら飛ばす #877
1477
+ if (this.check('comma')) { this.get() }
1478
+
1479
+ // プリミティブな値
1480
+ if (this.checkTypes(['number', 'string'])) { return this.get() }
1481
+
1482
+ // 丸括弧
1483
+ if (this.check('(')) { return this.yValueKakko() }
1484
+
1485
+ // マイナス記号
1486
+ if (this.check2(['-', 'number']) || this.check2(['-', 'word']) || this.check2(['-', 'func'])) {
1487
+ const m = this.get() // skip '-'
1488
+ const v = this.yValue()
1489
+ const josi = (v && v.josi) ? v.josi : ''
1490
+ return {
1491
+ type: 'op',
1492
+ operator: '*',
1493
+ left: { type: 'number', value: -1, line: m.line },
1494
+ right: v,
1495
+ josi: josi,
1496
+ ...map,
1497
+ end: this.peekSourceMap()
1498
+ }
1499
+ }
1500
+ // NOT
1501
+ if (this.check('not')) {
1502
+ this.get() // skip '!'
1503
+ const v = this.yValue()
1504
+ const josi = (v && v.josi) ? v.josi : ''
1505
+ return {
1506
+ type: 'not',
1507
+ value: v,
1508
+ josi: josi,
1509
+ ...map,
1510
+ end: this.peekSourceMap()
1511
+ }
1512
+ }
1513
+ // JSON object
1514
+ const a = this.yJSONArray()
1515
+ if (a) { return a }
1516
+ const o = this.yJSONObject()
1517
+ if (o) { return o }
1518
+ // 一語関数
1519
+ const splitType = operatorList.concat(['eol', ')', ']', 'ならば', '回', '間', '反復', '条件分岐'])
1520
+ if (this.check2(['func', splitType])) {
1521
+ const f = this.get()
1522
+ return {
1523
+ type: 'func',
1524
+ name: f.value,
1525
+ args: [],
1526
+ josi: f.josi,
1527
+ ...map,
1528
+ end: this.peekSourceMap()
1529
+ }
1530
+ }
1531
+ // C風関数呼び出し FUNC(...)
1532
+ if (this.check2([['func', 'word'], '(']) && this.peek().josi === '') {
1533
+ const f = this.peek()
1534
+ if (this.accept([['func', 'word'], '(', this.yGetArgParen, ')'])) {
1535
+ return {
1536
+ type: 'func',
1537
+ name: this.y[0].value,
1538
+ args: this.y[2],
1539
+ josi: this.y[3].josi,
1540
+ ...map,
1541
+ end: this.peekSourceMap()
1542
+ }
1543
+ } else { throw NakoSyntaxError.fromNode('C風関数呼び出しのエラー', f) }
1544
+ }
1545
+ // 関数呼び出し演算子
1546
+ if (this.check2(['func', '←'])) { return this.yCallOp() }
1547
+ // 無名関数(関数オブジェクト)
1548
+ if (this.check('def_func')) { return this.yMumeiFunc() }
1549
+ // 変数
1550
+ const word = this.yValueWord()
1551
+ if (word) { return word }
1552
+ // その他
1553
+ return null
1554
+ }
1555
+
1556
+ yValueWordGetIndex (ast) {
1557
+ // word @ a, b, c
1558
+ if (this.check('@')) {
1559
+ if (this.accept(['@', this.yValue, 'comma', this.yValue, 'comma', this.yValue])) {
1560
+ ast.index.push(this.y[1])
1561
+ ast.index.push(this.y[3])
1562
+ ast.index.push(this.y[5])
1563
+ ast.josi = this.y[5].josi
1564
+ return true
1565
+ }
1566
+ if (this.accept(['@', this.yValue, 'comma', this.yValue])) {
1567
+ ast.index.push(this.y[1])
1568
+ ast.index.push(this.y[3])
1569
+ ast.josi = this.y[3].josi
1570
+ return true
1571
+ }
1572
+ if (this.accept(['@', this.yValue])) {
1573
+ ast.index.push(this.y[1])
1574
+ ast.josi = this.y[1].josi
1575
+ return true
1576
+ }
1577
+ throw NakoSyntaxError.fromNode('変数の後ろの『@要素』の指定が不正です。', ast.name)
1578
+ }
1579
+ if (this.check('[')) {
1580
+ if (this.accept(['[', this.yCalc, ']'])) {
1581
+ ast.index.push(this.y[1])
1582
+ ast.josi = this.y[2].josi
1583
+ return true
1584
+ }
1585
+ }
1586
+ return false
1587
+ }
1588
+
1589
+ /** @returns {Ast | null} */
1590
+ yValueWord () {
1591
+ const map = this.peekSourceMap()
1592
+ if (this.check('word')) {
1593
+ const word = this.get()
1594
+ if (this.skipRefArray) { return word }
1595
+
1596
+ // word[n] || word@n
1597
+ if (word.josi === '' && this.checkTypes(['[', '@'])) {
1598
+ const ast = {
1599
+ type: '配列参照',
1600
+ name: word,
1601
+ index: [],
1602
+ josi: '',
1603
+ ...map,
1604
+ end: this.peekSourceMap()
1605
+ }
1606
+ while (!this.isEOF()) {
1607
+ if (!this.yValueWordGetIndex(ast)) { break }
1608
+ }
1609
+ if (ast.index.length === 0) { throw NakoSyntaxError.fromNode(`配列『${word.value}』アクセスで指定ミス`, word) }
1610
+ return ast
1611
+ }
1612
+ return word
1613
+ }
1614
+ return null
1615
+ }
1616
+
1617
+ yJSONObjectValue () {
1618
+ const a = []
1619
+ const firstToken = this.peek()
1620
+ while (!this.isEOF()) {
1621
+ while (this.check('eol')) { this.get() }
1622
+ if (this.check('}')) { break }
1623
+ if (this.accept(['word', ':', this.yCalc])) {
1624
+ this.y[0].type = 'string' // キー名の文字列記号省略の場合
1625
+ a.push({
1626
+ key: this.y[0],
1627
+ value: this.y[2]
1628
+ })
1629
+ } else if (this.accept(['string', ':', this.yCalc])) {
1630
+ a.push({
1631
+ key: this.y[0],
1632
+ value: this.y[2]
1633
+ })
1634
+ } else if (this.check('word')) {
1635
+ const w = this.get()
1636
+ w.type = 'string'
1637
+ a.push({
1638
+ key: w,
1639
+ value: w
1640
+ })
1641
+ } else if (this.checkTypes(['string', 'number'])) {
1642
+ const w = this.get()
1643
+ a.push({
1644
+ key: w,
1645
+ value: w
1646
+ })
1647
+ } else { throw NakoSyntaxError.fromNode('辞書オブジェクトの宣言で末尾の『}』がありません。', firstToken) }
1648
+
1649
+ if (this.check('comma')) { this.get() }
1650
+ }
1651
+ return a
1652
+ }
1653
+
1654
+ /** @returns {Ast | null} */
1655
+ yJSONObject () {
1656
+ const map = this.peekSourceMap()
1657
+ if (this.accept(['{', '}'])) {
1658
+ return {
1659
+ type: 'json_obj',
1660
+ value: [],
1661
+ josi: this.y[1].josi,
1662
+ ...map,
1663
+ end: this.peekSourceMap()
1664
+ }
1665
+ }
1666
+
1667
+ if (this.accept(['{', this.yJSONObjectValue, '}'])) {
1668
+ return {
1669
+ type: 'json_obj',
1670
+ value: this.y[1],
1671
+ josi: this.y[2].josi,
1672
+ ...map,
1673
+ end: this.peekSourceMap()
1674
+ }
1675
+ }
1676
+
1677
+ // 辞書初期化に終わりがなかった場合 (エラーチェックのため) #958
1678
+ if (this.accept(['{', this.yJSONObjectValue])) {
1679
+ throw NakoSyntaxError.fromNode(
1680
+ '辞書型変数の初期化が『}』で閉じられていません。',
1681
+ this.y[1])
1682
+ }
1683
+
1684
+ return null
1685
+ }
1686
+
1687
+ yJSONArrayValue () {
1688
+ if (this.check('eol')) { this.get() }
1689
+ const v1 = this.yCalc()
1690
+ if (v1 === null) { return null }
1691
+ if (this.check('comma')) { this.get() }
1692
+ const a = [v1]
1693
+ while (!this.isEOF()) {
1694
+ if (this.check('eol')) { this.get() }
1695
+ if (this.check(']')) { break }
1696
+ const v2 = this.yCalc()
1697
+ if (v2 === null) { break }
1698
+ if (this.check('comma')) { this.get() }
1699
+ a.push(v2)
1700
+ }
1701
+ return a
1702
+ }
1703
+
1704
+ /** @returns {Ast | null} */
1705
+ yJSONArray () {
1706
+ const map = this.peekSourceMap()
1707
+ if (this.accept(['[', ']'])) {
1708
+ return {
1709
+ type: 'json_array',
1710
+ value: [],
1711
+ josi: this.y[1].josi,
1712
+ ...map,
1713
+ end: this.peekSourceMap()
1714
+ }
1715
+ }
1716
+
1717
+ if (this.accept(['[', this.yJSONArrayValue, ']'])) {
1718
+ return {
1719
+ type: 'json_array',
1720
+ value: this.y[1],
1721
+ josi: this.y[2].josi,
1722
+ ...map,
1723
+ end: this.peekSourceMap()
1724
+ }
1725
+ }
1726
+ // 配列に終わりがなかった場合 (エラーチェックのため) #958
1727
+ if (this.accept(['[', this.yJSONArrayValue])) {
1728
+ throw NakoSyntaxError.fromNode(
1729
+ '配列変数の初期化が『]』で閉じられていません。',
1730
+ this.y[1])
1731
+ }
1732
+
1733
+ return null
1734
+ }
1735
+
1736
+ /** @returns {Ast | null} */
1737
+ yTryExcept () {
1738
+ const map = this.peekSourceMap()
1739
+ if (!this.check('エラー監視')) { return null }
1740
+ const kansi = this.get() // skip エラー監視
1741
+ const block = this.yBlock()
1742
+ if (!this.check2(['エラー', 'ならば'])) {
1743
+ throw NakoSyntaxError.fromNode(
1744
+ 'エラー構文で『エラーならば』がありません。' +
1745
+ '『エラー監視..エラーならば..ここまで』を対で記述します。',
1746
+ kansi)
1747
+ }
1748
+
1749
+ this.get() // skip エラー
1750
+ this.get() // skip ならば
1751
+ const errBlock = this.yBlock()
1752
+ if (this.check('ここまで')) {
1753
+ this.get()
1754
+ } else {
1755
+ throw NakoSyntaxError.fromNode('『ここまで』がありません。『エラー監視』...『エラーならば』...『ここまで』を対応させてください。', map)
1756
+ }
1757
+ return {
1758
+ type: 'try_except',
1759
+ block,
1760
+ errBlock,
1761
+ josi: '',
1762
+ ...map,
1763
+ end: this.peekSourceMap()
1764
+ }
1765
+ }
1766
+ }
1767
+
1768
+ module.exports = NakoParser