nadesiko3 3.7.21 → 3.7.22

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 (155) hide show
  1. package/batch/command.txt +370 -334
  2. package/batch/jsplugin2text.nako3 +1 -1
  3. package/batch/pickup_command.nako3 +12 -0
  4. package/core/command/snako.mts +5 -1
  5. package/core/deno/snako.ts +5 -1
  6. package/core/package-lock.json +382 -21
  7. package/core/package.json +4 -4
  8. package/core/src/nako3.mts +178 -614
  9. package/core/src/nako_basic_plugins.mts +39 -0
  10. package/core/src/nako_core_version.mts +2 -2
  11. package/core/src/nako_csv.mts +0 -1
  12. package/core/src/nako_event.mts +49 -0
  13. package/core/src/nako_gen.mts +291 -202
  14. package/core/src/nako_indent_inline.mts +2 -2
  15. package/core/src/nako_lex_rules.mts +1 -1
  16. package/core/src/nako_parser3.mts +106 -165
  17. package/core/src/nako_parser_async.mts +97 -0
  18. package/core/src/nako_parser_base.mts +4 -55
  19. package/core/src/nako_parser_message.mts +105 -0
  20. package/core/src/nako_parser_operator.mts +93 -0
  21. package/core/src/nako_plugin_manager.mts +260 -0
  22. package/core/src/nako_require.mts +292 -0
  23. package/core/src/nako_runner.mts +191 -0
  24. package/core/src/nako_tokenizer.mts +221 -0
  25. package/core/src/plugin_csv.mts +1 -1
  26. package/core/src/plugin_system.mts +34 -3259
  27. package/core/src/plugin_system_array.mts +699 -0
  28. package/core/src/plugin_system_datetime.mts +368 -0
  29. package/core/src/plugin_system_debug.mts +403 -0
  30. package/core/src/plugin_system_dict.mts +85 -0
  31. package/core/src/plugin_system_json.mts +73 -0
  32. package/core/src/plugin_system_math.mts +383 -0
  33. package/core/src/plugin_system_regexp.mts +120 -0
  34. package/core/src/plugin_system_stdio.mts +86 -0
  35. package/core/src/plugin_system_string.mts +666 -0
  36. package/core/src/plugin_system_timer.mts +152 -0
  37. package/core/src/plugin_system_types.mts +151 -0
  38. package/core/src/plugin_system_url.mts +193 -0
  39. package/core/src/plugin_toml.mts +3 -3
  40. package/core/test/fixtures/README.md +39 -0
  41. package/core/test/fixtures/make_parser_ast_golden.mjs +31 -0
  42. package/core/test/fixtures/parser_ast_golden.json +8027 -0
  43. package/core/test/fixtures/parser_corpus.mjs +120 -0
  44. package/core/test/indent_test.mjs +6 -0
  45. package/core/test/nako_basic_plugins_test.mjs +44 -0
  46. package/core/test/nako_event_test.mjs +85 -0
  47. package/core/test/nako_gen_perf_test.mjs +178 -0
  48. package/core/test/nako_parser_async_test.mjs +112 -0
  49. package/core/test/nako_parser_test.mjs +160 -0
  50. package/core/test/nako_plugin_manager_test.mjs +185 -0
  51. package/core/test/nako_require_test.mjs +153 -0
  52. package/core/test/nako_runner_test.mjs +114 -0
  53. package/core/test/nako_tokenizer_test.mjs +115 -0
  54. package/core/test/plugin_system_debug_test.mjs +141 -0
  55. package/core/test/plugin_system_split_test.mjs +179 -0
  56. package/core/test/plugin_system_test.mjs +6 -0
  57. package/core/tsconfig.json +0 -1
  58. package/package.json +17 -21
  59. package/release/_hash.txt +36 -36
  60. package/release/_script-tags.txt +16 -16
  61. package/release/command.json +1 -1
  62. package/release/command.json.js +1 -1
  63. package/release/command_cnako3.json +1 -1
  64. package/release/command_list.json +1 -1
  65. package/release/edit_main.js +6 -6
  66. package/release/edit_main.js.map +3 -3
  67. package/release/editor.js +6 -6
  68. package/release/plugin_keigo.js.map +3 -3
  69. package/release/plugin_markup.js +46 -46
  70. package/release/plugin_markup.js.map +3 -3
  71. package/release/plugin_weykturtle3d.js +1 -1
  72. package/release/plugin_weykturtle3d.js.map +3 -3
  73. package/release/version.js +2 -2
  74. package/release/version_main.js +2 -2
  75. package/release/version_main.js.map +1 -1
  76. package/release/wnako3.js +186 -213
  77. package/release/wnako3.js.map +4 -4
  78. package/release/wnako3webworker.js +172 -199
  79. package/release/wnako3webworker.js.map +4 -4
  80. package/src/cnako3mod.mjs +7 -2
  81. package/src/cnako3mod.mts +7 -2
  82. package/src/nako_version.mjs +2 -2
  83. package/src/nako_version.mts +2 -2
  84. package/src/plugin_browser_ajax.mjs +4 -4
  85. package/src/plugin_browser_ajax.mts +4 -4
  86. package/src/plugin_browser_audio.mjs +10 -10
  87. package/src/plugin_browser_audio.mts +10 -10
  88. package/src/plugin_browser_camera.mjs +4 -4
  89. package/src/plugin_browser_camera.mts +4 -4
  90. package/src/plugin_browser_canvas.mjs +2 -2
  91. package/src/plugin_browser_canvas.mts +2 -2
  92. package/src/plugin_browser_crypto.mjs +3 -3
  93. package/src/plugin_browser_crypto.mts +3 -3
  94. package/src/plugin_browser_dom_event.mjs +1 -1
  95. package/src/plugin_browser_dom_event.mts +1 -1
  96. package/src/plugin_browser_geolocation.mjs +1 -1
  97. package/src/plugin_browser_geolocation.mts +1 -1
  98. package/src/plugin_browser_hotkey.mjs +1 -1
  99. package/src/plugin_browser_hotkey.mts +1 -1
  100. package/src/plugin_browser_html.mjs +1 -1
  101. package/src/plugin_browser_html.mts +1 -1
  102. package/src/plugin_browser_location.mjs +1 -1
  103. package/src/plugin_browser_location.mts +1 -1
  104. package/src/plugin_browser_speech.mjs +1 -1
  105. package/src/plugin_browser_speech.mts +1 -1
  106. package/src/plugin_browser_storage.mjs +2 -2
  107. package/src/plugin_browser_storage.mts +2 -2
  108. package/src/plugin_httpserver.mjs +3 -3
  109. package/src/plugin_httpserver.mts +3 -3
  110. package/src/plugin_keigo.mjs +2 -2
  111. package/src/plugin_keigo.mts +2 -2
  112. package/src/plugin_node.mjs +48 -48
  113. package/src/plugin_node.mts +53 -53
  114. package/src/plugin_weykturtle3d.mjs +56 -56
  115. package/src/plugin_weykturtle3d.mts +56 -56
  116. package/src/wnako3.mjs +1 -1
  117. package/src/wnako3.mts +1 -1
  118. package/src/wnako3_editor.mjs +5 -5
  119. package/src/wnako3_editor.mts +5 -5
  120. package/src/wnako3mod.mjs +2 -2
  121. package/src/wnako3mod.mts +2 -2
  122. package/core/src/nako3.mjs +0 -1021
  123. package/core/src/nako_ast.mjs +0 -4
  124. package/core/src/nako_colors.mjs +0 -77
  125. package/core/src/nako_core_version.mjs +0 -8
  126. package/core/src/nako_csv.mjs +0 -193
  127. package/core/src/nako_errors.mjs +0 -166
  128. package/core/src/nako_from_dncl.mjs +0 -285
  129. package/core/src/nako_from_dncl2.mjs +0 -347
  130. package/core/src/nako_gen.mjs +0 -2500
  131. package/core/src/nako_global.mjs +0 -138
  132. package/core/src/nako_indent.mjs +0 -442
  133. package/core/src/nako_indent_chars.mjs +0 -29
  134. package/core/src/nako_indent_inline.mjs +0 -361
  135. package/core/src/nako_josi_list.mjs +0 -47
  136. package/core/src/nako_lex_rules.mjs +0 -319
  137. package/core/src/nako_lexer.mjs +0 -794
  138. package/core/src/nako_logger.mjs +0 -221
  139. package/core/src/nako_parser3.mjs +0 -3250
  140. package/core/src/nako_parser_base.mjs +0 -403
  141. package/core/src/nako_parser_const.mjs +0 -37
  142. package/core/src/nako_prepare.mjs +0 -329
  143. package/core/src/nako_reserved_words.mjs +0 -42
  144. package/core/src/nako_source_mapping.mjs +0 -207
  145. package/core/src/nako_test.mjs +0 -37
  146. package/core/src/nako_token.mjs +0 -1
  147. package/core/src/nako_tools.mjs +0 -53
  148. package/core/src/nako_types.mjs +0 -14
  149. package/core/src/plugin_api.mjs +0 -4
  150. package/core/src/plugin_csv.mjs +0 -97
  151. package/core/src/plugin_math.mjs +0 -352
  152. package/core/src/plugin_promise.mjs +0 -102
  153. package/core/src/plugin_system.mjs +0 -3810
  154. package/core/src/plugin_test.mjs +0 -52
  155. package/core/src/plugin_toml.mjs +0 -39
@@ -1,3250 +0,0 @@
1
- /**
2
- * nadesiko v3 parser
3
- */
4
- import { opPriority, RenbunJosi, operatorList } from './nako_parser_const.mjs';
5
- import { NakoParserBase } from './nako_parser_base.mjs';
6
- import { NakoSyntaxError } from './nako_errors.mjs';
7
- import { NakoLexer } from './nako_lexer.mjs';
8
- import { NewEmptyToken } from './nako_types.mjs';
9
- /**
10
- * 構文解析を行うクラス
11
- */
12
- export class NakoParser extends NakoParserBase {
13
- /**
14
- * 構文解析を実行する
15
- */
16
- parse(tokens, filename) {
17
- this.reset();
18
- this.tokens = tokens;
19
- this.modName = NakoLexer.filenameToModName(filename);
20
- this.modList.push(this.modName);
21
- // 解析処理 - 先頭から解析開始
22
- const result = this.startParser();
23
- // 関数毎に非同期処理が必要かどうかを判定する
24
- this.isModifiedNodes = false;
25
- this._checkAsyncFn(result);
26
- while (this.isModifiedNodes) {
27
- this.isModifiedNodes = false;
28
- this._checkAsyncFn(result);
29
- }
30
- return result;
31
- }
32
- /** パーサーの一番最初に呼び出す構文規則 */
33
- startParser() {
34
- const b = this.ySentenceList();
35
- const c = this.get();
36
- if (c && c.type !== 'eof') {
37
- this.logger.debug(`構文解析でエラー。${this.nodeToStr(c, { depth: 1 }, true)}の使い方が間違っています。`, c);
38
- throw NakoSyntaxError.fromNode(`構文解析でエラー。${this.nodeToStr(c, { depth: 1 }, false)}の使い方が間違っています。`, c);
39
- }
40
- return b;
41
- }
42
- /** 何もしない
43
- * @returns {Ast}
44
- */
45
- yNop() {
46
- return {
47
- type: 'nop',
48
- josi: '',
49
- ...this.peekSourceMap(),
50
- end: this.peekSourceMap()
51
- };
52
- }
53
- /** 複数文を返す */
54
- ySentenceList() {
55
- const blocks = [];
56
- let line = -1;
57
- const map = this.peekSourceMap();
58
- while (!this.isEOF()) {
59
- const n = this.ySentence();
60
- if (!n) {
61
- break;
62
- }
63
- blocks.push(n);
64
- if (line < 0) {
65
- line = n.line;
66
- }
67
- }
68
- if (blocks.length === 0) {
69
- const token = this.peek() || this.tokens[0];
70
- this.logger.debug('構文解析に失敗:' + this.nodeToStr(this.peek(), { depth: 1 }, true), token);
71
- throw NakoSyntaxError.fromNode('構文解析に失敗:' + this.nodeToStr(this.peek(), { depth: 1 }, false), token);
72
- }
73
- return { type: 'block', blocks, josi: '', ...map, end: this.peekSourceMap() };
74
- }
75
- /** 余剰スタックのレポートを作る */
76
- makeStackBalanceReport() {
77
- const words = [];
78
- this.stack.forEach((t) => {
79
- let w = this.nodeToStr(t, { depth: 1 }, false);
80
- if (t.josi) {
81
- w += t.josi;
82
- }
83
- words.push(w);
84
- });
85
- const desc = words.join(',');
86
- // 最近使った関数の使い方レポートを作る #1093
87
- let descFunc = '';
88
- const chA = 'A'.charCodeAt(0);
89
- for (const f of this.recentlyCalledFunc) {
90
- descFunc += ' - ';
91
- let no = 0;
92
- const josiA = (f).josi;
93
- if (josiA) {
94
- for (const arg of josiA) {
95
- const ch = String.fromCharCode(chA + no);
96
- descFunc += ch;
97
- if (arg.length === 1) {
98
- descFunc += arg[0];
99
- }
100
- else {
101
- descFunc += `(${arg.join('|')})`;
102
- }
103
- no++;
104
- }
105
- }
106
- descFunc += String(f.name) + '\n';
107
- }
108
- this.recentlyCalledFunc = [];
109
- return `未解決の単語があります: [${desc}]\n次の命令の可能性があります:\n${descFunc}`;
110
- }
111
- yEOL() {
112
- // 行末のチェック #1009
113
- const eol = this.get();
114
- if (!eol) {
115
- return null;
116
- }
117
- // 余剰スタックの確認
118
- if (this.stack.length > 0) {
119
- const report = this.makeStackBalanceReport();
120
- throw NakoSyntaxError.fromNode(report, eol);
121
- }
122
- this.recentlyCalledFunc = [];
123
- return {
124
- type: 'eol',
125
- comment: eol.value,
126
- line: eol.line,
127
- column: eol.column,
128
- file: eol.file
129
- };
130
- }
131
- /** @returns {Ast | null} */
132
- ySentence() {
133
- const map = this.peekSourceMap();
134
- // 最初の語句が決まっている構文
135
- if (this.check('eol')) {
136
- return this.yEOL();
137
- }
138
- if (this.check('もし')) {
139
- return this.yIF();
140
- }
141
- if (this.check('後判定')) {
142
- return this.yAtohantei();
143
- }
144
- if (this.check('エラー監視')) {
145
- return this.yTryExcept();
146
- }
147
- if (this.accept(['抜ける'])) {
148
- return { type: 'break', josi: '', ...map, end: this.peekSourceMap() };
149
- }
150
- if (this.accept(['続ける'])) {
151
- return { type: 'continue', josi: '', ...map, end: this.peekSourceMap() };
152
- }
153
- if (this.check('??')) {
154
- return this.yDebugPrint();
155
- }
156
- // 実行モードの指定
157
- if (this.accept(['DNCLモード'])) {
158
- return this.yDNCLMode(1);
159
- }
160
- if (this.accept(['DNCL2モード'])) {
161
- return this.yDNCLMode(2);
162
- }
163
- if (this.accept(['not', 'string', 'モード設定'])) {
164
- return this.ySetGenMode(this.y[1].value);
165
- }
166
- if (this.accept(['not', 'モジュール公開既定値', 'eq', 'string'])) {
167
- return this.yExportDefault(this.y[3].value);
168
- }
169
- if (this.accept(['not', '厳チェック'])) {
170
- return this.ySetMode('厳しくチェック');
171
- } // (#1698)
172
- // (memo) 現状「取込」はプリプロセス段階(NakoCompiler.listRequireStatements)で処理される
173
- // if (this.accept(['require', 'string', '取込'])) { return this.yRequire() }
174
- // <廃止された構文>
175
- if (this.check('逐次実行')) {
176
- return this.yTikuji();
177
- } // 廃止 #1611
178
- if (this.accept(['not', '非同期モード'])) {
179
- return this.yASyncMode();
180
- }
181
- // </廃止された構文>
182
- if (this.check2(['func', 'eq'])) {
183
- const word = this.get() || NewEmptyToken();
184
- throw NakoSyntaxError.fromNode(`関数『${word.value}』に代入できません。`, word);
185
- }
186
- // 先読みして初めて確定する構文
187
- if (this.accept([this.ySpeedMode])) {
188
- return this.y[0];
189
- }
190
- if (this.accept([this.yPerformanceMonitor])) {
191
- return this.y[0];
192
- }
193
- if (this.accept([this.yLet])) {
194
- return this.y[0];
195
- }
196
- if (this.accept([this.yDefTest])) {
197
- return this.y[0];
198
- }
199
- if (this.accept([this.yDefFunc])) {
200
- return this.y[0];
201
- }
202
- // 関数呼び出しの他、各種構文の実装
203
- if (this.accept([this.yCall])) {
204
- const c1 = this.y[0];
205
- const nextToken = this.peek();
206
- if (nextToken && nextToken.type === 'ならば') {
207
- const map = this.peekSourceMap();
208
- const cond = c1;
209
- this.get(); // skip ならば
210
- // もし文の条件として関数呼び出しがある場合
211
- return this.yIfThen(cond, map);
212
- }
213
- else if (RenbunJosi.indexOf(c1.josi || '') >= 0) { // 連文をblockとして接続する(もし構文などのため)
214
- if (this.stack.length >= 1) { // スタックの余剰をチェック
215
- const report = this.makeStackBalanceReport();
216
- throw NakoSyntaxError.fromNode(report, c1);
217
- }
218
- const c2 = this.ySentence();
219
- if (c2 !== null) {
220
- return {
221
- type: 'block',
222
- blocks: [c1, c2],
223
- josi: c2.josi,
224
- ...map,
225
- end: this.peekSourceMap()
226
- };
227
- }
228
- }
229
- return c1;
230
- }
231
- return null;
232
- }
233
- /** [廃止] 非同期モード #11 @returns {Ast} */
234
- yASyncMode() {
235
- this.logger.error('『非同期モード』構文は廃止されました(https://nadesi.com/v3/doc/go.php?1028)。', this.peek());
236
- const map = this.peekSourceMap();
237
- return { type: 'eol', ...map, end: this.peekSourceMap() };
238
- }
239
- /** set DNCL mode */
240
- yDNCLMode(ver) {
241
- const map = this.peekSourceMap();
242
- if (ver === 1) {
243
- // 配列インデックスは1から
244
- this.arrayIndexFrom = 1;
245
- // 配列アクセスをJSと逆順で指定する
246
- this.flagReverseArrayIndex = true;
247
- }
248
- else {
249
- // ver2はPythonに近いとのこと
250
- }
251
- // 配列代入時自動で初期化チェックする
252
- this.flagCheckArrayInit = true;
253
- return { type: 'eol', ...map, end: this.peekSourceMap() };
254
- }
255
- /** @returns {Ast} */
256
- ySetGenMode(mode) {
257
- const map = this.peekSourceMap();
258
- this.genMode = mode;
259
- return { type: 'eol', ...map, end: this.peekSourceMap() };
260
- }
261
- /** @returns {Ast} */
262
- yExportDefault(mode) {
263
- const map = this.peekSourceMap();
264
- this.isExportDefault = mode === '公開';
265
- this.moduleExport.set(this.modName, this.isExportDefault);
266
- return { type: 'eol', ...map, end: this.peekSourceMap() };
267
- }
268
- /** @returns {AstStrValue} */
269
- ySetMode(mode) {
270
- const map = this.peekSourceMap();
271
- return { type: 'run_mode', value: mode, ...map, end: this.peekSourceMap() };
272
- }
273
- /** @returns {AstBlocks} */
274
- yBlock() {
275
- const map = this.peekSourceMap();
276
- const blocks = [];
277
- if (this.check('ここから')) {
278
- this.get();
279
- }
280
- while (!this.isEOF()) {
281
- if (this.checkTypes(['違えば', 'ここまで', 'エラー'])) {
282
- break;
283
- }
284
- if (!this.accept([this.ySentence])) {
285
- break;
286
- }
287
- blocks.push(this.y[0]);
288
- }
289
- return { type: 'block', blocks, josi: '', ...map, end: this.peekSourceMap() };
290
- }
291
- yDefFuncReadArgs() {
292
- if (!this.check('(')) {
293
- return null;
294
- }
295
- const a = [];
296
- this.get(); // skip '('
297
- while (!this.isEOF()) {
298
- if (this.check(')')) {
299
- this.get(); // skip ''
300
- break;
301
- }
302
- const t = this.get();
303
- if (t) {
304
- a.push(t);
305
- } // Token to Ast
306
- if (this.check('comma')) {
307
- this.get();
308
- }
309
- }
310
- return a;
311
- }
312
- yDefTest() {
313
- return this.yDefFuncCommon('def_test');
314
- }
315
- yDefFunc() {
316
- return this.yDefFuncCommon('def_func');
317
- }
318
- /** ユーザー関数の定義
319
- * @returns {AstDefFunc | null}
320
- */
321
- yDefFuncCommon(type) {
322
- if (!this.check(type)) { // yDefFuncから呼ばれれば def_func なのかをチェックする
323
- return null;
324
- }
325
- const map = this.peekSourceMap();
326
- // 関数定義トークンを取得(このmetaに先読みした関数の型などが入っている)
327
- // (ref) NakoLexer.preDefineFunc
328
- const defToken = this.get(); // 'def_func' or 'def_test'
329
- if (!defToken) {
330
- return null;
331
- }
332
- const def = defToken;
333
- let isExport = this.isExportDefault;
334
- if (this.check('{')) {
335
- this.get();
336
- const funcAttribute = this.get();
337
- if (this.check('}')) {
338
- this.get();
339
- }
340
- else {
341
- throw NakoSyntaxError.fromNode('関数の属性の指定が正しくありません。『{』と『}』で囲む必要があります。', def);
342
- }
343
- if (funcAttribute !== null) {
344
- if (funcAttribute.value === '公開') {
345
- isExport = true;
346
- }
347
- if (funcAttribute.value === '非公開') {
348
- isExport = false;
349
- }
350
- if (funcAttribute.value === 'エクスポート') {
351
- isExport = true;
352
- }
353
- }
354
- }
355
- let defArgs = [];
356
- if (this.check('(')) {
357
- defArgs = this.yDefFuncReadArgs() || [];
358
- } // // lexerでも解析しているが再度詳しく
359
- const funcName = this.get();
360
- if (!funcName || funcName.type !== 'func') {
361
- this.logger.debug(this.nodeToStr(funcName, { depth: 0, typeName: '関数' }, true) + 'の宣言でエラー。', funcName);
362
- throw NakoSyntaxError.fromNode(this.nodeToStr(funcName, { depth: 0, typeName: '関数' }, false) + 'の宣言でエラー。', def);
363
- }
364
- if (this.check('(')) {
365
- // 関数引数の二重定義
366
- if (defArgs.length > 0) {
367
- this.logger.debug(this.nodeToStr(funcName, { depth: 0, typeName: '関数' }, true) + 'の宣言で、引数定義は名前の前か後に一度だけ可能です。', funcName);
368
- throw NakoSyntaxError.fromNode(this.nodeToStr(funcName, { depth: 0, typeName: '関数' }, false) + 'の宣言で、引数定義は名前の前か後に一度だけ可能です。', funcName);
369
- }
370
- defArgs = this.yDefFuncReadArgs() || [];
371
- }
372
- if (this.check('とは')) {
373
- this.get();
374
- }
375
- let block = this.yNop();
376
- let multiline = false;
377
- let asyncFn = false;
378
- if (this.check('ここから')) {
379
- multiline = true;
380
- }
381
- if (this.check('eol')) {
382
- multiline = true;
383
- }
384
- try {
385
- this.funcLevel++;
386
- this.usedAsyncFn = false;
387
- // ローカル変数を生成
388
- const backupLocalvars = this.localvars;
389
- this.localvars = new Map([['それ', { type: 'var', value: '' }]]);
390
- if (multiline) {
391
- this.saveStack();
392
- // 関数の引数をローカル変数として登録する
393
- for (const arg of defArgs) {
394
- if (!arg) {
395
- continue;
396
- }
397
- if (!arg.value) {
398
- continue;
399
- }
400
- const fnName = arg.value;
401
- this.localvars.set(fnName, { 'type': 'var', 'value': '' });
402
- }
403
- block = this.yBlock();
404
- // 「ここまで」のチェック
405
- if (this.check('ここまで')) {
406
- this.get(); // skip 'ここまで'
407
- }
408
- else {
409
- // 「ここまで」が見当たらない
410
- const nextWordO = this.peek();
411
- let nextWord = JSON.stringify(nextWordO);
412
- if (nextWordO && nextWordO.type && nextWordO.value) {
413
- nextWord = nextWordO.value;
414
- }
415
- throw NakoSyntaxError.fromNode(`『ここまで』がありません。関数定義の末尾に必要です。『${nextWord}』の前に『ここまで』を記述してください。`, def);
416
- }
417
- this.loadStack();
418
- }
419
- else {
420
- this.saveStack();
421
- block = this.ySentence() || this.yNop();
422
- this.loadStack();
423
- }
424
- this.funcLevel--;
425
- asyncFn = this.usedAsyncFn;
426
- this.localvars = backupLocalvars;
427
- }
428
- catch (err) {
429
- this.logger.debug(this.nodeToStr(funcName, { depth: 0, typeName: '関数' }, true) +
430
- 'の定義で以下のエラーがありました。\n' + String(err.message), def);
431
- throw NakoSyntaxError.fromNode(this.nodeToStr(funcName, { depth: 0, typeName: '関数' }, false) +
432
- 'の定義で以下のエラーがありました。\n' + String(err.message), def);
433
- }
434
- const func = this.funclist.get(funcName.value);
435
- if (func && !func.asyncFn && asyncFn) {
436
- func.asyncFn = asyncFn;
437
- }
438
- return {
439
- type,
440
- name: funcName.value,
441
- args: defArgs,
442
- blocks: [block],
443
- asyncFn,
444
- isExport,
445
- josi: '',
446
- meta: def.meta,
447
- ...map,
448
- end: this.peekSourceMap()
449
- };
450
- }
451
- /** 「もし」文の条件を取得 */
452
- yIFCond() {
453
- const map = this.peekSourceMap();
454
- let a = this.yGetArg();
455
- if (!a) {
456
- throw NakoSyntaxError.fromNode('「もし」文の条件式に間違いがあります。' + this.nodeToStr(this.peek(), { depth: 1 }, false), map);
457
- }
458
- // console.log('@@yIFCond=', a)
459
- // チェック : Aならば
460
- if (a.josi === 'ならば') {
461
- return a;
462
- }
463
- if (a.josi === 'でなければ') {
464
- a = { type: 'not', operator: 'not', blocks: [a], josi: '', ...map, end: this.peekSourceMap() };
465
- return a;
466
- }
467
- // チェック : AがBならば --- 「関数B(A)」のとき
468
- if ((a.josi !== '') && (this.check('func'))) {
469
- // もし文で関数呼び出しがある場合
470
- this.stack.push(a);
471
- a = this.yCall();
472
- }
473
- else
474
- // チェック : AがBならば --- 「A = B」のとき
475
- if (a.josi === 'が') {
476
- const tmpI = this.index;
477
- const b = this.yGetArg();
478
- if (!b) {
479
- throw NakoSyntaxError.fromNode('もし文の条件「AがBならば」でBがないか条件が複雑過ぎます。' +
480
- this.nodeToStr(this.peek(), { depth: 1 }, false), map);
481
- }
482
- if (this.check('ならば')) {
483
- const naraba = this.get() || { 'value': 'ならば' };
484
- b.josi = naraba.value;
485
- }
486
- if (b && (b.josi === 'ならば' || b.josi === 'でなければ')) {
487
- return {
488
- type: 'op',
489
- operator: (b.josi === 'でなければ') ? 'noteq' : 'eq',
490
- blocks: [a, b],
491
- josi: '',
492
- ...map,
493
- end: this.peekSourceMap()
494
- };
495
- }
496
- this.index = tmpI;
497
- }
498
- // もし文で追加の関数呼び出しがある場合
499
- if (!this.check('ならば')) {
500
- this.stack.push(a);
501
- a = this.yCall();
502
- }
503
- // (ならば|でなければ)を確認
504
- if (!this.check('ならば')) {
505
- const smap = a || this.yNop();
506
- this.logger.debug('もし文で『ならば』がないか、条件が複雑過ぎます。' + this.nodeToStr(this.peek(), { depth: 1 }, false) + 'の直前に『ならば』を書いてください。', smap);
507
- throw NakoSyntaxError.fromNode('もし文で『ならば』がないか、条件が複雑過ぎます。' + this.nodeToStr(this.peek(), { depth: 1 }, false) + 'の直前に『ならば』を書いてください。', smap);
508
- }
509
- const naraba = this.get();
510
- // 否定形のチェック
511
- if (naraba && naraba.value === 'でなければ') {
512
- a = {
513
- type: 'not',
514
- operator: 'not',
515
- blocks: [a],
516
- josi: '',
517
- ...map,
518
- end: this.peekSourceMap()
519
- };
520
- }
521
- if (!a) {
522
- throw NakoSyntaxError.fromNode('「もし」文の条件式に間違いがあります。' + this.nodeToStr(this.peek(), { depth: 1 }, false), map);
523
- }
524
- return a;
525
- }
526
- /** もし文
527
- * @returns {AstIf | null} */
528
- yIF() {
529
- const map = this.peekSourceMap();
530
- // 「もし」があれば「もし」文である
531
- if (!this.check('もし')) {
532
- return null;
533
- }
534
- const mosi = this.get(); // skip もし
535
- if (mosi === null) {
536
- return null;
537
- }
538
- while (this.check('comma')) {
539
- this.get();
540
- } // skip comma
541
- // 「もし」文の条件を取得
542
- let expr = null;
543
- try {
544
- expr = this.yIFCond();
545
- }
546
- catch (err) {
547
- throw NakoSyntaxError.fromNode('『もし』文の条件で次のエラーがあります。\n' + String(err.message), mosi);
548
- }
549
- return this.yIfThen(expr, map);
550
- }
551
- /** 「もし」文の「もし」以降の判定 ... 「もし」がなくても条件分岐は動くようになっている
552
- * @returns {AstIf | null}
553
- */
554
- yIfThen(expr, map) {
555
- // 「もし」文の 真偽のブロックを取得
556
- let trueBlock = this.yNop();
557
- let falseBlock = this.yNop();
558
- let tanbun = false;
559
- // True Block
560
- if (this.check('eol')) {
561
- trueBlock = this.yBlock();
562
- }
563
- else {
564
- const block = this.ySentence();
565
- if (block) {
566
- trueBlock = block;
567
- }
568
- tanbun = true;
569
- }
570
- // skip EOL
571
- while (this.check('eol')) {
572
- this.get();
573
- }
574
- // Flase Block
575
- if (this.check('違えば')) {
576
- this.get(); // skip 違えば
577
- while (this.check('comma')) {
578
- this.get();
579
- }
580
- if (this.check('eol')) {
581
- falseBlock = this.yBlock();
582
- }
583
- else {
584
- const block = this.ySentence();
585
- if (block) {
586
- falseBlock = block;
587
- }
588
- tanbun = true;
589
- }
590
- }
591
- if (tanbun === false) {
592
- if (this.check('ここまで')) {
593
- this.get();
594
- }
595
- else {
596
- throw NakoSyntaxError.fromNode('『もし』文で『ここまで』がありません。', map);
597
- }
598
- }
599
- return {
600
- type: 'if',
601
- blocks: [expr, trueBlock, falseBlock],
602
- josi: '',
603
- ...map,
604
- end: this.peekSourceMap()
605
- };
606
- }
607
- ySpeedMode() {
608
- const map = this.peekSourceMap();
609
- if (!this.check2(['string', '実行速度優先'])) {
610
- return null;
611
- }
612
- const optionNode = this.get();
613
- this.get();
614
- let val = '';
615
- if (optionNode && optionNode.value) {
616
- val = optionNode.value;
617
- }
618
- else {
619
- return null;
620
- }
621
- const options = { 行番号無し: false, 暗黙の型変換無し: false, 強制ピュア: false, それ無効: false };
622
- for (const name of val.split('/')) {
623
- // 全て有効化
624
- if (name === '全て') {
625
- for (const k of Object.keys(options)) {
626
- options[k] = true;
627
- }
628
- break;
629
- }
630
- // 個別に有効化
631
- if (Object.keys(options).includes(name)) {
632
- options[name] = true;
633
- }
634
- else {
635
- // 互換性を考えて、警告に留める。
636
- this.logger.warn(`実行速度優先文のオプション『${name}』は存在しません。`, optionNode);
637
- }
638
- }
639
- let multiline = false;
640
- if (this.check('ここから')) {
641
- this.get();
642
- multiline = true;
643
- }
644
- else if (this.check('eol')) {
645
- multiline = true;
646
- }
647
- let block = this.yNop();
648
- if (multiline) {
649
- block = this.yBlock();
650
- if (this.check('ここまで')) {
651
- this.get();
652
- }
653
- }
654
- else {
655
- block = this.ySentence() || block;
656
- }
657
- return {
658
- type: 'speed_mode',
659
- options,
660
- blocks: [block],
661
- josi: '',
662
- ...map
663
- };
664
- }
665
- yPerformanceMonitor() {
666
- const map = this.peekSourceMap();
667
- if (!this.check2(['string', 'パフォーマンスモニタ適用'])) {
668
- return null;
669
- }
670
- const optionNode = this.get();
671
- if (!optionNode) {
672
- return null;
673
- }
674
- this.get();
675
- const options = { ユーザ関数: false, システム関数本体: false, システム関数: false };
676
- for (const name of optionNode.value.split('/')) {
677
- // 全て有効化
678
- if (name === '全て') {
679
- for (const k of Object.keys(options)) {
680
- options[k] = true;
681
- }
682
- break;
683
- }
684
- // 個別に有効化
685
- if (Object.keys(options).includes(name)) {
686
- options[name] = true;
687
- }
688
- else {
689
- // 互換性を考えて、警告に留める。
690
- this.logger.warn(`パフォーマンスモニタ適用文のオプション『${name}』は存在しません。`, optionNode);
691
- }
692
- }
693
- let multiline = false;
694
- if (this.check('ここから')) {
695
- this.get();
696
- multiline = true;
697
- }
698
- else if (this.check('eol')) {
699
- multiline = true;
700
- }
701
- let block = this.yNop();
702
- if (multiline) {
703
- block = this.yBlock();
704
- if (this.check('ここまで')) {
705
- this.get();
706
- }
707
- }
708
- else {
709
- block = this.ySentence() || block;
710
- }
711
- return {
712
- type: 'performance_monitor',
713
- options,
714
- blocks: [block],
715
- josi: '',
716
- ...map
717
- };
718
- }
719
- /** [廃止] #1611 「逐次実行」構文 @returns {Ast | null} */
720
- yTikuji() {
721
- if (!this.check('逐次実行')) {
722
- return null;
723
- }
724
- const tikuji = this.getCur(); // skip
725
- this.logger.error('『逐次実行』構文は廃止されました(https://nadesi.com/v3/doc/go.php?944)。', tikuji);
726
- return { type: 'eol', ...this.peekSourceMap(), end: this.peekSourceMap() };
727
- }
728
- /**
729
- * 1つ目の値を与え、その後に続く計算式を取得し、優先規則に沿って並び替えして戻す
730
- * @param {Ast} firstValue
731
- */
732
- yGetArgOperator(firstValue) {
733
- const args = [firstValue];
734
- while (!this.isEOF()) {
735
- // 演算子がある?
736
- let op = this.peek();
737
- if (op && opPriority[op.type]) {
738
- op = this.getCur();
739
- args.push(op); // Token to Ast
740
- // 演算子後の値を取得
741
- const v = this.yValue();
742
- if (v === null) {
743
- throw NakoSyntaxError.fromNode(`計算式で演算子『${op.value}』後に値がありません`, firstValue);
744
- }
745
- args.push(v);
746
- continue;
747
- }
748
- break;
749
- }
750
- if (args.length === 0) {
751
- return null;
752
- }
753
- if (args.length === 1) {
754
- return args[0];
755
- }
756
- return this.infixToAST(args);
757
- }
758
- /**
759
- * 範囲(関数)を返す
760
- * @param kara
761
- * @returns {AstCallFunc | null}
762
- */
763
- yRange(kara) {
764
- // 範囲オブジェクト?
765
- if (!this.check('…')) {
766
- return null;
767
- }
768
- const map = this.peekSourceMap();
769
- this.get(); // skip '…'
770
- const made = this.yValue();
771
- if (!kara || !made) {
772
- throw NakoSyntaxError.fromNode('範囲オブジェクトの指定エラー。『A…B』の書式で指定してください。', map);
773
- }
774
- const meta = this.funclist.get('範囲');
775
- if (!meta) {
776
- throw new Error('関数『範囲』が見つかりません。plugin_systemをシステムに追加してください。');
777
- }
778
- return {
779
- type: 'func',
780
- name: '範囲',
781
- blocks: [kara, made],
782
- josi: made.josi,
783
- meta,
784
- asyncFn: false,
785
- ...map,
786
- end: this.peekSourceMap()
787
- };
788
- }
789
- /**
790
- * 表示(関数)を返す 「??」のエイリアスで利用 (#1745)
791
- */
792
- yDebugPrint() {
793
- const map = this.peekSourceMap();
794
- const t = this.get(); // skip '??'
795
- if (!t || t.value !== '??') {
796
- throw NakoSyntaxError.fromNode('『??』で指定してください。', map);
797
- }
798
- const arg = this.yCalc();
799
- if (!arg) {
800
- throw NakoSyntaxError.fromNode('『??(計算式)』で指定してください。', map);
801
- }
802
- const meta = this.funclist.get('ハテナ関数実行');
803
- if (!meta) {
804
- throw new Error('関数『ハテナ関数実行』が見つかりません。plugin_systemをシステムに追加してください。');
805
- }
806
- return {
807
- type: 'func',
808
- name: 'ハテナ関数実行',
809
- blocks: [arg],
810
- josi: '',
811
- meta,
812
- asyncFn: false,
813
- ...map,
814
- end: this.peekSourceMap()
815
- };
816
- }
817
- yGetArg() {
818
- // 値を一つ読む
819
- const value1 = this.yValue();
820
- if (value1 === null) {
821
- return null;
822
- }
823
- // 範囲オブジェクト?
824
- if (this.check('…')) {
825
- return this.yRange(value1);
826
- }
827
- // 計算式がある場合を考慮
828
- return this.yGetArgOperator(value1);
829
- }
830
- infixToPolish(list) {
831
- // 中間記法から逆ポーランドに変換
832
- const priority = (t) => {
833
- if (opPriority[t.type]) {
834
- return opPriority[t.type];
835
- }
836
- return 10;
837
- };
838
- const stack = [];
839
- const polish = [];
840
- while (list.length > 0) {
841
- const t = list.shift();
842
- if (!t) {
843
- break;
844
- }
845
- while (stack.length > 0) { // 優先順位を見て移動する
846
- const sTop = stack[stack.length - 1];
847
- if (priority(t) > priority(sTop)) {
848
- break;
849
- }
850
- const tpop = stack.pop();
851
- if (!tpop) {
852
- this.logger.error('計算式に間違いがあります。', t);
853
- break;
854
- }
855
- polish.push(tpop);
856
- }
857
- stack.push(t);
858
- }
859
- // 残った要素を積み替える
860
- while (stack.length > 0) {
861
- const t = stack.pop();
862
- if (t) {
863
- polish.push(t);
864
- }
865
- }
866
- return polish;
867
- }
868
- /** @returns {Ast | null} */
869
- infixToAST(list) {
870
- if (list.length === 0) {
871
- return null;
872
- }
873
- // 逆ポーランドを構文木に
874
- const josi = list[list.length - 1].josi;
875
- const node = list[list.length - 1];
876
- const polish = this.infixToPolish(list);
877
- /** @type {Ast[]} */
878
- const stack = [];
879
- for (const t of polish) {
880
- if (!opPriority[t.type]) { // 演算子ではない
881
- stack.push(t);
882
- continue;
883
- }
884
- const b = stack.pop();
885
- const a = stack.pop();
886
- if (a === undefined || b === undefined) {
887
- this.logger.debug('--- 計算式(逆ポーランド) ---\n' + JSON.stringify(polish));
888
- throw NakoSyntaxError.fromNode('計算式でエラー', node);
889
- }
890
- /** @type {AstOperator} */
891
- const op = {
892
- type: 'op',
893
- operator: t.type,
894
- blocks: [a, b],
895
- josi,
896
- startOffset: a.startOffset,
897
- endOffset: a.endOffset,
898
- line: a.line,
899
- column: a.column,
900
- file: a.file
901
- };
902
- stack.push(op);
903
- }
904
- const ans = stack.pop();
905
- if (!ans) {
906
- return null;
907
- }
908
- return ans;
909
- }
910
- yGetArgParen(y, funcName) {
911
- let isClose = false;
912
- const si = this.stack.length;
913
- while (!this.isEOF()) {
914
- if (this.check(')')) {
915
- isClose = true;
916
- break;
917
- }
918
- // カッコを用いた関数呼び出しの中で助詞を用いた関数呼び出しを有効にする #2000
919
- const v = this.yCalc();
920
- if (v) {
921
- this.pushStack(v);
922
- if (this.check('comma')) {
923
- this.get();
924
- }
925
- continue;
926
- }
927
- break;
928
- }
929
- if (!isClose) {
930
- const name = funcName || y[0].value || this.nodeToStr(y[0], { depth: 0, typeName: '関数' }, false);
931
- throw NakoSyntaxError.fromNode(`C風関数『${name}』でカッコが閉じていません`, y[0]);
932
- }
933
- const a = [];
934
- while (si < this.stack.length) {
935
- const v = this.popStack();
936
- if (v) {
937
- a.unshift(v);
938
- }
939
- }
940
- return a;
941
- }
942
- /** 関数の戻り値を続けてC風呼び出しする */
943
- yApplyCallValue(callee) {
944
- let node = callee;
945
- while (this.check('(')) {
946
- this.get(); // skip '('
947
- const args = this.yGetArgParen([node], '関数呼び出しの結果');
948
- if (!this.check(')')) {
949
- throw NakoSyntaxError.fromNode('C風関数呼び出しのエラー', node);
950
- }
951
- const close = this.get();
952
- node = {
953
- type: 'call_value',
954
- blocks: [node, ...args],
955
- josi: close?.josi || '',
956
- startOffset: node.startOffset,
957
- endOffset: close?.endOffset,
958
- line: node.line,
959
- column: node.column,
960
- file: node.file,
961
- end: this.peekSourceMap()
962
- };
963
- }
964
- return node;
965
- }
966
- /** @returns {AstRepeatTimes | null} */
967
- yRepeatTime() {
968
- const map = this.peekSourceMap();
969
- if (!this.check('回')) {
970
- return null;
971
- }
972
- this.get(); // skip '回'
973
- if (this.check('comma')) {
974
- this.get();
975
- } // skip comma
976
- if (this.check('繰返')) {
977
- this.get();
978
- } // skip 'N回、繰り返す' (#924)
979
- const num = this.popStack([]) || { type: 'word', value: 'それ', josi: '', ...map, end: this.peekSourceMap() };
980
- let multiline = false;
981
- let block = this.yNop();
982
- if (this.check('comma')) {
983
- this.get();
984
- }
985
- if (this.check('ここから')) {
986
- this.get();
987
- multiline = true;
988
- }
989
- else if (this.check('eol')) {
990
- multiline = true;
991
- }
992
- if (multiline) { // multiline
993
- block = this.yBlock();
994
- if (this.check('ここまで')) {
995
- this.get();
996
- }
997
- else {
998
- throw NakoSyntaxError.fromNode('『ここまで』がありません。『回』...『ここまで』を対応させてください。', map);
999
- }
1000
- }
1001
- else {
1002
- // singleline
1003
- const b = this.ySentence();
1004
- if (b) {
1005
- block = b;
1006
- }
1007
- }
1008
- return {
1009
- type: 'repeat_times',
1010
- blocks: [num, block],
1011
- josi: '',
1012
- ...map,
1013
- end: this.peekSourceMap()
1014
- };
1015
- }
1016
- /** @returns {AstWhile | null} */
1017
- yWhile() {
1018
- const map = this.peekSourceMap();
1019
- if (!this.check('間')) {
1020
- return null;
1021
- }
1022
- this.get(); // skip '間'
1023
- while (this.check('comma')) {
1024
- this.get();
1025
- } // skip ','
1026
- if (this.check('繰返')) {
1027
- this.get();
1028
- } // skip '繰り返す' #927
1029
- const expr = this.popStack();
1030
- if (expr === null) {
1031
- throw NakoSyntaxError.fromNode('『間』で条件がありません。', map);
1032
- }
1033
- if (this.check('comma')) {
1034
- this.get();
1035
- }
1036
- if (!this.checkTypes(['ここから', 'eol'])) {
1037
- throw NakoSyntaxError.fromNode('『間』の直後は改行が必要です', map);
1038
- }
1039
- const block = this.yBlock();
1040
- if (this.check('ここまで')) {
1041
- this.get();
1042
- }
1043
- else {
1044
- throw NakoSyntaxError.fromNode('『ここまで』がありません。『間』...『ここまで』を対応させてください。', map);
1045
- }
1046
- return {
1047
- type: 'while',
1048
- blocks: [expr, block],
1049
- josi: '',
1050
- ...map,
1051
- end: this.peekSourceMap()
1052
- };
1053
- }
1054
- /** @returns {AstAtohantei | null} */
1055
- yAtohantei() {
1056
- const map = this.peekSourceMap();
1057
- if (this.check('後判定')) {
1058
- this.get();
1059
- } // skip 後判定
1060
- if (this.check('繰返')) {
1061
- this.get();
1062
- } // skip 繰り返す
1063
- if (this.check('ここから')) {
1064
- this.get();
1065
- }
1066
- const block = this.yBlock();
1067
- if (this.check('ここまで')) {
1068
- this.get();
1069
- }
1070
- if (this.check('comma')) {
1071
- this.get();
1072
- }
1073
- let cond = this.yGetArg(); // 条件
1074
- let bUntil = false;
1075
- const t = this.peek();
1076
- if (t && t.value === 'なる' && (t.josi === 'まで' || t.josi === 'までの')) {
1077
- this.get(); // skip なるまで
1078
- bUntil = true;
1079
- }
1080
- if (this.check('間')) {
1081
- this.get();
1082
- } // skip 間
1083
- if (bUntil) { // 条件を反転する
1084
- cond = {
1085
- type: 'not',
1086
- operator: 'not',
1087
- blocks: [cond],
1088
- josi: '',
1089
- ...map,
1090
- end: this.peekSourceMap()
1091
- };
1092
- }
1093
- if (!cond) {
1094
- cond = { type: 'number', value: 1, josi: '', ...map, end: this.peekSourceMap() };
1095
- }
1096
- return {
1097
- type: 'atohantei',
1098
- blocks: [cond, block],
1099
- josi: '',
1100
- ...map,
1101
- end: this.peekSourceMap()
1102
- };
1103
- }
1104
- /** @returns {AstFor | null} */
1105
- yFor() {
1106
- const errorForArguments = '『繰り返す』文でAからBまでの指定がありません。';
1107
- let flagDown = true; // AからBまでの時、A>=Bを許容するかどうか
1108
- let flagUp = true; // AからBまでの時、A<=Bを許容するかどうか
1109
- let loopDirection = null; // ループの方向を一方向に限定する
1110
- const map = this.peekSourceMap();
1111
- if (this.check('繰返') || this.check('増繰返') || this.check('減繰返')) {
1112
- // pass
1113
- }
1114
- else {
1115
- return null;
1116
- }
1117
- const kurikaesu = this.getCur(); // skip 繰り返す
1118
- // スタックに(増や|減ら)してがある?
1119
- const incdec = this.stack.pop();
1120
- if (incdec) {
1121
- if (incdec.type === 'word' && (incdec.value === '増' || incdec.value === '減')) {
1122
- if (incdec.value === '増') {
1123
- flagDown = false;
1124
- }
1125
- else {
1126
- flagUp = false;
1127
- }
1128
- const w = String(incdec.value) + kurikaesu.type;
1129
- if (w === '増繰返') {
1130
- kurikaesu.type = '増繰返';
1131
- }
1132
- else if (w === '減繰返') {
1133
- kurikaesu.type = '減繰返';
1134
- }
1135
- else {
1136
- throw Error('[System Error] 増繰り返し | 減繰り返しのエラー。');
1137
- }
1138
- }
1139
- else {
1140
- // 普通の繰り返しの場合
1141
- this.stack.push(incdec); // 違ったので改めて追加
1142
- }
1143
- }
1144
- let vInc = this.yNop();
1145
- if (kurikaesu.type === '増繰返' || kurikaesu.type === '減繰返') {
1146
- vInc = this.popStack(['ずつ']) || this.yNop();
1147
- if (kurikaesu.type === '増繰返') {
1148
- flagDown = false;
1149
- }
1150
- else {
1151
- flagUp = false;
1152
- }
1153
- loopDirection = kurikaesu.type === '増繰返' ? 'up' : 'down';
1154
- }
1155
- const vTo = this.popStack(['まで', 'を']); // 範囲オブジェクトの場合もあり
1156
- const vFrom = this.popStack(['から']) || this.yNop();
1157
- const vWord = this.popStack(['を', 'で']);
1158
- let wordStr = '';
1159
- if (vWord !== null) { // 変数
1160
- if (vWord.type !== 'word') {
1161
- throw NakoSyntaxError.fromNode('『(変数名)をAからBまで繰り返す』で指定してください。', vWord);
1162
- }
1163
- wordStr = vWord.value;
1164
- }
1165
- if (vFrom === null || vTo === null) {
1166
- // 『AからBの範囲を繰り返す』構文のとき (#1704)
1167
- if (vFrom === null && vTo && (vTo.type === 'func' && vTo.name === '範囲')) {
1168
- // ok
1169
- }
1170
- else {
1171
- throw NakoSyntaxError.fromNode(errorForArguments, kurikaesu);
1172
- }
1173
- }
1174
- if (this.check('comma')) {
1175
- this.get();
1176
- } // skip comma
1177
- let multiline = false;
1178
- if (this.check('ここから')) {
1179
- multiline = true;
1180
- this.get();
1181
- }
1182
- else if (this.check('eol')) {
1183
- multiline = true;
1184
- this.get();
1185
- }
1186
- let block = this.yNop();
1187
- if (multiline) {
1188
- block = this.yBlock();
1189
- if (this.check('ここまで')) {
1190
- this.get();
1191
- }
1192
- else {
1193
- throw NakoSyntaxError.fromNode('『ここまで』がありません。『繰り返す』...『ここまで』を対応させてください。', map);
1194
- }
1195
- }
1196
- else {
1197
- const b = this.ySentence();
1198
- if (b) {
1199
- block = b;
1200
- }
1201
- }
1202
- if (!block) {
1203
- block = this.yNop();
1204
- }
1205
- return {
1206
- type: 'for',
1207
- blocks: [vFrom, vTo, vInc, block],
1208
- flagDown,
1209
- flagUp,
1210
- loopDirection,
1211
- word: wordStr,
1212
- josi: '',
1213
- ...map,
1214
- end: this.peekSourceMap()
1215
- };
1216
- }
1217
- /** @returns {AstBlocks | null} */
1218
- yReturn() {
1219
- const map = this.peekSourceMap();
1220
- if (!this.check('戻る')) {
1221
- return null;
1222
- }
1223
- this.get(); // skip '戻る'
1224
- const v = this.popStack(['で', 'を']) || this.yNop();
1225
- if (this.stack.length > 0) {
1226
- throw NakoSyntaxError.fromNode('『戻』文の直前に未解決の引数があります。『(式)を戻す』のように式をカッコで括ってください。', map);
1227
- }
1228
- return {
1229
- type: 'return',
1230
- blocks: [v],
1231
- josi: '',
1232
- ...map,
1233
- end: this.peekSourceMap()
1234
- };
1235
- }
1236
- /** @returns {AstForeach | null} */
1237
- yForEach() {
1238
- const map = this.peekSourceMap();
1239
- if (!this.check('反復')) {
1240
- return null;
1241
- }
1242
- this.get(); // skip '反復'
1243
- while (this.check('comma')) {
1244
- this.get();
1245
- } // skip ','
1246
- const target = this.popStack(['を']) || this.yNop();
1247
- // target == null なら「それ」の値が使われる
1248
- const name = this.popStack(['で']);
1249
- let wordStr = '';
1250
- if (name !== null) {
1251
- if (name.type !== 'word') {
1252
- throw NakoSyntaxError.fromNode('『(変数名)で(配列)を反復』で指定してください。', map);
1253
- }
1254
- wordStr = name.value;
1255
- }
1256
- let block = this.yNop();
1257
- let multiline = false;
1258
- if (this.check('ここから')) {
1259
- multiline = true;
1260
- this.get();
1261
- }
1262
- else if (this.check('eol')) {
1263
- multiline = true;
1264
- }
1265
- if (multiline) {
1266
- block = this.yBlock();
1267
- if (this.check('ここまで')) {
1268
- this.get();
1269
- }
1270
- else {
1271
- throw NakoSyntaxError.fromNode('『ここまで』がありません。『反復』...『ここまで』を対応させてください。', map);
1272
- }
1273
- }
1274
- else {
1275
- const b = this.ySentence();
1276
- if (b) {
1277
- block = b;
1278
- }
1279
- }
1280
- return {
1281
- type: 'foreach',
1282
- word: wordStr,
1283
- blocks: [target, block],
1284
- josi: '',
1285
- ...map,
1286
- end: this.peekSourceMap()
1287
- };
1288
- }
1289
- /** 条件分岐構文
1290
- * @returns {AstSwitch | null}
1291
- */
1292
- ySwitch() {
1293
- const map = this.peekSourceMap();
1294
- if (!this.check('条件分岐')) {
1295
- return null;
1296
- }
1297
- const joukenbunki = this.get(); // skip '条件分岐'
1298
- if (!joukenbunki) {
1299
- return null;
1300
- }
1301
- const eol = this.get(); // skip 'eol'
1302
- if (!eol) {
1303
- return null;
1304
- }
1305
- const expr = this.popStack(['で']);
1306
- if (!expr) {
1307
- throw NakoSyntaxError.fromNode('『(値)で条件分岐』のように記述してください。', joukenbunki);
1308
- }
1309
- if (eol.type !== 'eol') {
1310
- throw NakoSyntaxError.fromNode('『条件分岐』の直後は改行してください。', joukenbunki);
1311
- }
1312
- //
1313
- const blocks = [];
1314
- blocks[0] = expr;
1315
- blocks[1] = this.yNop(); // 後で default のAstを再設定するため
1316
- //
1317
- while (!this.isEOF()) {
1318
- if (this.check('eol')) {
1319
- this.get();
1320
- continue;
1321
- }
1322
- // ここまで?
1323
- if (this.check('ここまで')) {
1324
- this.get(); // skip ここまで
1325
- break;
1326
- }
1327
- // 違えば?
1328
- const condToken = this.peek();
1329
- if (condToken && condToken.type === '違えば') {
1330
- this.get(); // skip 違えば
1331
- if (this.check('comma')) {
1332
- this.get();
1333
- } // skip ','
1334
- const defaultBlock = this.yBlock();
1335
- if (this.check('ここまで')) {
1336
- this.get(); // skip ここまで (違えばとペア)
1337
- }
1338
- while (this.check('eol')) {
1339
- this.get();
1340
- } // skip eol
1341
- if (this.check('ここまで')) {
1342
- this.get(); // skip ここまで (条件分岐:ここまで)
1343
- }
1344
- blocks[1] = defaultBlock;
1345
- break;
1346
- }
1347
- // 通常の条件
1348
- const cond = this.yValue();
1349
- if (!cond) {
1350
- throw NakoSyntaxError.fromNode('『条件分岐』は『(条件)ならば〜ここまで』と記述してください。', joukenbunki);
1351
- }
1352
- const naraba = this.get(); // skip ならば
1353
- if (!naraba || naraba.type !== 'ならば') {
1354
- throw NakoSyntaxError.fromNode('『条件分岐』で条件は**ならばと記述してください。', joukenbunki);
1355
- }
1356
- if (this.check('comma')) {
1357
- this.get();
1358
- } // skip ','
1359
- // 条件にあったときに実行すること
1360
- const condBlock = this.yBlock();
1361
- const kokomade = this.peek();
1362
- if (kokomade && kokomade.type === 'ここまで') {
1363
- this.get(); // skip ここまで
1364
- }
1365
- blocks.push(cond);
1366
- blocks.push(condBlock);
1367
- }
1368
- const ast = {
1369
- type: 'switch',
1370
- blocks,
1371
- case_count: blocks.length / 2 - 1,
1372
- josi: '',
1373
- ...map,
1374
- end: this.peekSourceMap()
1375
- };
1376
- return ast;
1377
- }
1378
- /** 無名関数
1379
- * @returns {AstDefFunc|null}
1380
- */
1381
- yMumeiFunc() {
1382
- const map = this.peekSourceMap();
1383
- if (!this.check('def_func')) {
1384
- return null;
1385
- }
1386
- const defToken = this.get();
1387
- if (!defToken) {
1388
- return null;
1389
- }
1390
- const def = defToken;
1391
- let args = [];
1392
- // 「,」を飛ばす
1393
- if (this.check('comma')) {
1394
- this.get();
1395
- }
1396
- // 関数の引数定義は省略できる
1397
- if (this.check('(')) {
1398
- args = this.yDefFuncReadArgs() || [];
1399
- }
1400
- // 「,」を飛ばす
1401
- if (this.check('comma')) {
1402
- this.get();
1403
- }
1404
- // ブロックを読む
1405
- let block = this.yNop();
1406
- let isAsyncFn = false;
1407
- this.funcLevel++;
1408
- this.saveStack();
1409
- const backupAsyncFn = this.usedAsyncFn;
1410
- this.usedAsyncFn = false;
1411
- // ローカル変数を生成 (#1746)
1412
- const backupLocalvars = this.localvars;
1413
- this.localvars = new Map([['それ', { type: 'var', value: '' }]]);
1414
- // 関数の引数をローカル変数として登録する
1415
- for (const arg of args) {
1416
- if (!arg) {
1417
- continue;
1418
- }
1419
- if (!arg.value) {
1420
- continue;
1421
- }
1422
- const fnName = arg.value;
1423
- this.localvars.set(fnName, { 'type': 'var', 'value': '' });
1424
- }
1425
- try {
1426
- block = this.yBlock();
1427
- isAsyncFn = this.usedAsyncFn;
1428
- // 末尾の「ここまで」をチェック - もしなければエラーにする #1045
1429
- if (!this.check('ここまで')) {
1430
- throw NakoSyntaxError.fromNode('『ここまで』がありません。『には』構文か無名関数の末尾に『ここまで』が必要です。', map);
1431
- }
1432
- this.get(); // skip ここまで
1433
- }
1434
- finally {
1435
- this.loadStack();
1436
- this.usedAsyncFn = backupAsyncFn;
1437
- this.localvars = backupLocalvars;
1438
- this.funcLevel--;
1439
- }
1440
- return {
1441
- type: 'func_obj',
1442
- name: '',
1443
- args,
1444
- blocks: [block],
1445
- meta: def.meta,
1446
- josi: '',
1447
- isExport: false, // 無名関数は外部公開しない
1448
- asyncFn: isAsyncFn, // asyncFnかどうか
1449
- ...map,
1450
- end: this.peekSourceMap()
1451
- };
1452
- }
1453
- /** 代入構文 */
1454
- yDainyu() {
1455
- const map = this.peekSourceMap();
1456
- const dainyu = this.get(); // 代入
1457
- if (dainyu === null) {
1458
- return null;
1459
- }
1460
- const value = this.popStack(['を']) || { type: 'word', value: 'それ', josi: 'を', ...map };
1461
- const word = this.popStack(['へ', 'に']);
1462
- if (!word || (word.type !== 'word' && word.type !== 'func' && word.type !== 'ref_array')) {
1463
- throw NakoSyntaxError.fromNode('代入文で代入先の変数が見当たりません。『(変数名)に(値)を代入』のように使います。', dainyu);
1464
- }
1465
- if (word.type === 'func') {
1466
- throw NakoSyntaxError.fromNode('関数『' + String(word.name) + '』に代入できません。『(変数名)に(値)を代入』のように使います。', dainyu);
1467
- }
1468
- // 配列への代入
1469
- if (word.type === 'ref_array') {
1470
- const indexArray = word.index || [];
1471
- const blocks = [value, ...indexArray];
1472
- return {
1473
- type: 'let_array',
1474
- name: word.name.value,
1475
- indexes: word.index,
1476
- blocks,
1477
- josi: '',
1478
- checkInit: this.flagCheckArrayInit,
1479
- ...map,
1480
- end: this.peekSourceMap()
1481
- };
1482
- }
1483
- // 一般的な変数への代入
1484
- const word2 = this.getVarName(word);
1485
- return {
1486
- type: 'let',
1487
- name: word2.value,
1488
- blocks: [value],
1489
- josi: '',
1490
- ...map,
1491
- end: this.peekSourceMap()
1492
- };
1493
- }
1494
- /** 定める構文 */
1495
- ySadameru() {
1496
- const map = this.peekSourceMap();
1497
- const sadameru = this.get(); // 定める
1498
- if (sadameru === null) {
1499
- return null;
1500
- }
1501
- // 引数(定数名)を取得
1502
- const word = this.popStack(['を']) || { type: 'word', value: 'それ', josi: 'を', ...map, end: this.peekSourceMap() };
1503
- if (!word || (word.type !== 'word' && word.type !== 'func' && word.type !== 'ref_array')) {
1504
- throw NakoSyntaxError.fromNode('『定める』文で定数が見当たりません。『(定数名)を(値)に定める』のように使います。', sadameru);
1505
- }
1506
- // 引数(値)を取得
1507
- const value = this.popStack(['へ', 'に', 'と']) || this.yNop();
1508
- // 公開設定
1509
- let isExport = this.isExportDefault;
1510
- if (this.check2(['{', 'word', '}'])) {
1511
- this.get();
1512
- const attrNode = this.get();
1513
- if (attrNode === null) {
1514
- throw NakoSyntaxError.fromNode('定める『' + word.value + '』の定義エラー', word);
1515
- }
1516
- const attr = attrNode.value;
1517
- if (attr === '公開') {
1518
- isExport = true;
1519
- }
1520
- else if (attr === '非公開') {
1521
- isExport = false;
1522
- }
1523
- else if (attr === 'エクスポート') {
1524
- isExport = true;
1525
- }
1526
- else {
1527
- this.logger.warn(`不明な変数属性『${attr}』が指定されています。`);
1528
- }
1529
- this.get();
1530
- }
1531
- // 変数を生成する
1532
- const nameToken = this.createVar(word, true, isExport);
1533
- return {
1534
- type: 'def_local_var',
1535
- name: nameToken.value,
1536
- vartype: '定数',
1537
- isExport,
1538
- blocks: [value],
1539
- josi: '',
1540
- ...map,
1541
- end: this.peekSourceMap()
1542
- };
1543
- }
1544
- yIncDec() {
1545
- const map = this.peekSourceMap();
1546
- const action = this.get(); // (増やす|減らす)
1547
- if (action === null) {
1548
- return null;
1549
- }
1550
- // 『Nずつ増やして繰り返す』文か?
1551
- if (this.check('繰返')) {
1552
- this.pushStack({ type: 'word', value: action.value, josi: action.josi, ...map, end: this.peekSourceMap() });
1553
- return this.yFor();
1554
- }
1555
- // スタックから引数をポップ
1556
- const word = this.popStack(['を']);
1557
- if (!word || (word.type !== 'word' && word.type !== 'ref_array' && word.type !== 'ref_prop')) {
1558
- throw NakoSyntaxError.fromNode(`『${action.type}』文で定数が見当たりません。『(変数名)を(値)だけ${action.type}』のように使います。`, action);
1559
- }
1560
- let value = this.popStack(['だけ', '']);
1561
- if (!value) {
1562
- value = { type: 'number', value: 1, josi: 'だけ', ...map, end: this.peekSourceMap() };
1563
- }
1564
- // 減らすなら-1かける
1565
- if (action.value === '減') {
1566
- const minusOne = { type: 'number', value: -1, line: action.line };
1567
- value = { type: 'op', operator: '*', blocks: [value, minusOne], josi: '', ...map };
1568
- }
1569
- return {
1570
- type: 'inc',
1571
- name: word,
1572
- blocks: [value],
1573
- josi: action.josi,
1574
- ...map,
1575
- end: this.peekSourceMap()
1576
- };
1577
- }
1578
- yCall() {
1579
- if (this.isEOF()) {
1580
- return null;
1581
- }
1582
- // スタックに積んでいく
1583
- while (!this.isEOF()) {
1584
- if (this.check('ここから')) {
1585
- this.get();
1586
- }
1587
- // 代入
1588
- if (this.check('代入')) {
1589
- return this.yDainyu();
1590
- }
1591
- if (this.check('定める')) {
1592
- return this.ySadameru();
1593
- }
1594
- // 制御構文
1595
- if (this.check('回')) {
1596
- return this.yRepeatTime();
1597
- }
1598
- if (this.check('間')) {
1599
- return this.yWhile();
1600
- }
1601
- if (this.check('繰返') || this.check('増繰返') || this.check('減繰返')) {
1602
- return this.yFor();
1603
- }
1604
- if (this.check('反復')) {
1605
- return this.yForEach();
1606
- }
1607
- if (this.check('条件分岐')) {
1608
- return this.ySwitch();
1609
- }
1610
- if (this.check('戻る')) {
1611
- return this.yReturn();
1612
- }
1613
- if (this.check('増') || this.check('減')) {
1614
- return this.yIncDec();
1615
- }
1616
- // C言語風関数
1617
- if (this.check2([['func', 'word'], '('])) { // C言語風
1618
- const cur = this.peek();
1619
- if (cur && cur.josi === '') {
1620
- const t = this.yValue(); // yValueにてC言語風呼び出しをパース
1621
- if (t) {
1622
- const josi = t.josi || '';
1623
- if (t.type === 'func' && (t.josi === '' || RenbunJosi.indexOf(josi) >= 0)) {
1624
- t.josi = '';
1625
- return t; // 関数なら値とする
1626
- }
1627
- this.pushStack(t);
1628
- }
1629
- if (this.check('comma')) {
1630
- this.get();
1631
- }
1632
- continue;
1633
- }
1634
- }
1635
- // なでしこ式関数
1636
- if (this.check('func')) {
1637
- const r = this.yCallFunc();
1638
- if (r === null) {
1639
- continue;
1640
- }
1641
- // 「〜する間」の形ならスタックに積む。
1642
- if (this.check('間')) {
1643
- this.pushStack(r);
1644
- continue;
1645
- }
1646
- // 関数呼び出しの直後に、四則演算があるか?
1647
- if (!this.checkTypes(operatorList)) {
1648
- return r; // 関数呼び出しの後に演算子がないのでそのまま関数呼び出しを戻す
1649
- }
1650
- // 四則演算があった場合、計算してスタックに載せる
1651
- const s = this.yGetArgOperator(r);
1652
- this.pushStack(s);
1653
- continue;
1654
- }
1655
- // 値のとき → スタックに載せる
1656
- const t = this.yGetArg();
1657
- if (t) {
1658
- this.pushStack(t);
1659
- continue;
1660
- }
1661
- break;
1662
- } // end of while
1663
- // 助詞が余ってしまった場合
1664
- if (this.stack.length > 0) {
1665
- if (this.isReadingCalc) {
1666
- return this.popStack();
1667
- }
1668
- this.logger.debug('--- stack dump ---\n' + JSON.stringify(this.stack, null, 2) + '\npeek: ' + JSON.stringify(this.peek(), null, 2));
1669
- let msgDebug = `不完全な文です。${this.stack.map((n) => this.nodeToStr(n, { depth: 0 }, true)).join('、')}が解決していません。`;
1670
- let msg = `不完全な文です。${this.stack.map((n) => this.nodeToStr(n, { depth: 0 }, false)).join('、')}が解決していません。`;
1671
- // 各ノードについて、更に詳細な情報があるなら表示
1672
- for (const n of this.stack) {
1673
- const d0 = this.nodeToStr(n, { depth: 0 }, false);
1674
- const d1 = this.nodeToStr(n, { depth: 1 }, false);
1675
- if (d0 !== d1) {
1676
- msgDebug += `${this.nodeToStr(n, { depth: 0 }, true)}は${this.nodeToStr(n, { depth: 1 }, true)}として使われています。`;
1677
- msg += `${d0}は${d1}として使われています。`;
1678
- }
1679
- }
1680
- const first = this.stack[0];
1681
- const last = this.stack[this.stack.length - 1];
1682
- this.logger.debug(msgDebug, first);
1683
- throw NakoSyntaxError.fromNode(msg, first, last);
1684
- }
1685
- return this.popStack([]);
1686
- }
1687
- /** @returns {Ast | null} */
1688
- yCallFunc() {
1689
- const map = this.peekSourceMap();
1690
- const callToken = this.get();
1691
- if (!callToken) {
1692
- return null;
1693
- }
1694
- const t = callToken;
1695
- const f = t.meta;
1696
- const funcName = t.value;
1697
- // (関数)には ... 構文 ... https://github.com/kujirahand/nadesiko3/issues/66
1698
- let funcObj = null;
1699
- if (t.josi === 'には') {
1700
- try {
1701
- funcObj = this.yMumeiFunc();
1702
- }
1703
- catch (err) {
1704
- throw NakoSyntaxError.fromNode(`『${t.value}には...』で無名関数の定義で以下の間違いがあります。\n${err.message}`, t);
1705
- }
1706
- if (funcObj === null) {
1707
- throw NakoSyntaxError.fromNode('『Fには』構文がありましたが、関数定義が見当たりません。', t);
1708
- }
1709
- }
1710
- if (!f || typeof f.josi === 'undefined') {
1711
- throw NakoSyntaxError.fromNode('関数の定義でエラー。', t);
1712
- }
1713
- // 最近使った関数を記録
1714
- this.recentlyCalledFunc.push({ name: funcName, ...f });
1715
- // 呼び出す関数が非同期呼び出しが必要(asyncFn)ならマーク
1716
- if (f && f.asyncFn) {
1717
- this.usedAsyncFn = true;
1718
- }
1719
- // 関数の引数を取り出す処理
1720
- const args = [];
1721
- let nullCount = 0;
1722
- let valueCount = 0;
1723
- for (let i = f.josi.length - 1; i >= 0; i--) {
1724
- for (;;) {
1725
- // スタックから任意の助詞を持つ値を一つ取り出す、助詞がなければ末尾から得る
1726
- let popArg = this.popStack(f.josi[i]);
1727
- if (popArg !== null) {
1728
- valueCount++;
1729
- }
1730
- else if (i < f.josi.length - 1 || !f.isVariableJosi) {
1731
- nullCount++;
1732
- popArg = funcObj;
1733
- }
1734
- else {
1735
- break;
1736
- }
1737
- // 参照渡しの場合、引数が関数の参照渡しに該当する場合、typeを『func_pointer』に変更
1738
- if (popArg !== null && f.funcPointers !== undefined && f.funcPointers[i] !== null) {
1739
- if (popArg.type === 'func') { // 引数が関数の参照渡しに該当する場合
1740
- popArg.type = 'func_pointer';
1741
- }
1742
- else {
1743
- const varname = (f.varnames) ? f.varnames[i] : `${i + 1}番目の引数`;
1744
- throw NakoSyntaxError.fromNode(`関数『${t.value}』の引数『${varname}』には関数オブジェクトが必要です。`, t);
1745
- }
1746
- }
1747
- // 引数がnullであれば、自動的に『変数「それ」』で補完する
1748
- if (popArg === null) {
1749
- popArg = { type: 'word', value: 'それ', josi: '', ...map, end: map };
1750
- }
1751
- args.unshift(popArg); // 先頭に追加
1752
- if (i < f.josi.length - 1 || !f.isVariableJosi) {
1753
- break;
1754
- }
1755
- }
1756
- }
1757
- // 引数が不足しているとき(つまり、引数にnullがあるとき)、自動的に『変数「それ」』で補完される。
1758
- // ただし、nullが1つだけなら、変数「それ」で補完されるが、2つ以上あるときは、エラーにする
1759
- if (nullCount >= 2 && (valueCount > 0 || t.josi === '' || RenbunJosi.indexOf(t.josi) >= 0)) {
1760
- throw NakoSyntaxError.fromNode(`関数『${t.value}』の引数が不足しています。`, t);
1761
- }
1762
- this.usedFuncs.add(t.value);
1763
- // 関数呼び出しのAstを構築
1764
- const funcNode = {
1765
- type: 'func',
1766
- name: t.value,
1767
- blocks: args,
1768
- meta: f,
1769
- josi: t.josi,
1770
- asyncFn: !!f.asyncFn,
1771
- ...map,
1772
- end: this.peekSourceMap()
1773
- };
1774
- // 「プラグイン名設定」ならば、そこでスコープを変更することを意味する (#1112)
1775
- if (funcNode.name === 'プラグイン名設定') {
1776
- if (args.length > 0 && args[0]) {
1777
- let fname = String(args[0].value);
1778
- if (fname === 'メイン') {
1779
- fname = String(args[0].file);
1780
- }
1781
- this.namespaceStack.push(this.modName);
1782
- this.isExportStack.push(this.isExportDefault);
1783
- this.modName = NakoLexer.filenameToModName(fname);
1784
- this.modList.push(this.modName);
1785
- }
1786
- }
1787
- else if (funcNode.name === '名前空間ポップ') { // (#1409)
1788
- const space = this.namespaceStack.pop();
1789
- if (space) {
1790
- this.modName = space;
1791
- }
1792
- const isexport = this.isExportStack.pop();
1793
- if (isexport !== null && isexport !== undefined) {
1794
- this.isExportDefault = isexport;
1795
- }
1796
- }
1797
- // 言い切りならそこで一度切る
1798
- if (t.josi === '') {
1799
- return funcNode;
1800
- }
1801
- // 「**して、**」の場合も一度切る
1802
- if (RenbunJosi.indexOf(t.josi) >= 0) {
1803
- funcNode.josi = (this.isReadingCalc && t.josi === 'には') ? '' : 'して';
1804
- return funcNode;
1805
- }
1806
- // 続き
1807
- funcNode.meta = f;
1808
- this.pushStack(funcNode);
1809
- return null;
1810
- }
1811
- /** @returns {Ast | null} */
1812
- yLet() {
1813
- const map = this.peekSourceMap();
1814
- // 通常の変数
1815
- if (this.check2(['word', 'eq'])) {
1816
- const word = this.peek();
1817
- let expectedError = false;
1818
- try {
1819
- if (this.accept(['word', 'eq', this.yCalc]) || this.accept(['word', 'eq', this.ySentence])) {
1820
- if (this.y[2].type === 'eol') {
1821
- throw new Error('値が空です。');
1822
- }
1823
- if (this.check('comma')) {
1824
- this.get();
1825
- } // skip comma (ex) name1=val1, name2=val2
1826
- const nameToken = this.getVarName(this.y[0]);
1827
- const valueToken = this.y[2];
1828
- return {
1829
- type: 'let',
1830
- name: nameToken.value,
1831
- blocks: [valueToken],
1832
- josi: '',
1833
- ...map,
1834
- end: this.peekSourceMap()
1835
- };
1836
- }
1837
- else {
1838
- expectedError = true;
1839
- this.logger.debug(`${this.nodeToStr(word, { depth: 1 }, true)}への代入文で計算式に書き間違いがあります。`, word);
1840
- throw NakoSyntaxError.fromNode(`${this.nodeToStr(word, { depth: 1 }, false)}への代入文で計算式に書き間違いがあります。`, map);
1841
- }
1842
- }
1843
- catch (err) {
1844
- if (expectedError) {
1845
- throw err;
1846
- }
1847
- this.logger.debug(`${this.nodeToStr(word, { depth: 1 }, true)}への代入文で計算式に以下の書き間違いがあります。\n${err.message}`, word);
1848
- throw NakoSyntaxError.fromNode(`${this.nodeToStr(word, { depth: 1 }, false)}への代入文で計算式に以下の書き間違いがあります。\n${err.message}`, map);
1849
- }
1850
- }
1851
- // オブジェクトプロパティ構文:代入文::ここから (#1793)
1852
- if (this.check2(['word', '$', '*', '$', '*', '$', '*', '$', '*', 'eq']) || this.check2(['word', '$', '*', '$', '*', '$', '*', 'eq']) || this.check2(['word', '$', '*', '$', '*', 'eq']) || this.check2(['word', '$', '*', 'eq'])) {
1853
- const propList = [];
1854
- const word = this.getVarName(this.get());
1855
- for (;;) {
1856
- const flag = this.peek();
1857
- if (flag === null || flag.type !== '$') {
1858
- break;
1859
- }
1860
- this.get(); // skip $
1861
- propList.push(this.get()); // property
1862
- }
1863
- this.get(); // skip eq
1864
- const valueToken = this.yCalc(); // calc
1865
- if (valueToken === null) {
1866
- throw NakoSyntaxError.fromNode(`${this.nodeToStr(word, { depth: 1 }, false)}への代入文の計算式に書き間違いがあります。`, map);
1867
- }
1868
- return {
1869
- type: 'let_prop',
1870
- name: word.value,
1871
- index: propList,
1872
- blocks: [valueToken],
1873
- josi: '',
1874
- ...map,
1875
- end: this.peekSourceMap()
1876
- };
1877
- }
1878
- // オブジェクトプロパティ構文:代入文::ここまで
1879
- // let_array ?
1880
- if (this.check2(['word', '@'])) {
1881
- const la = this.yLetArrayAt(map);
1882
- if (this.check('comma')) {
1883
- this.get();
1884
- } // skip comma (ex) name1=val1, name2=val2
1885
- if (la) {
1886
- la.checkInit = this.flagCheckArrayInit;
1887
- return la;
1888
- }
1889
- }
1890
- if (this.check2(['word', '['])) {
1891
- const lb = this.yLetArrayBracket(map);
1892
- if (this.check('comma')) {
1893
- this.get();
1894
- } // skip comma (ex) name1=val1, name2=val2
1895
- if (lb) {
1896
- lb.checkInit = this.flagCheckArrayInit;
1897
- return lb;
1898
- }
1899
- }
1900
- // ローカル変数定義
1901
- if (this.accept(['word', 'とは'])) {
1902
- const wordToken = this.y[0];
1903
- if (!this.checkTypes(['変数', '定数'])) {
1904
- throw NakoSyntaxError.fromNode('ローカル変数『' + String(wordToken.value) + '』の定義エラー', wordToken);
1905
- }
1906
- const vtype = this.getCur(); // 変数 or 定数
1907
- let isExport = this.isExportDefault;
1908
- if (this.check2(['{', 'word', '}'])) {
1909
- this.get();
1910
- const attrNode = this.get();
1911
- if (attrNode === null) {
1912
- throw NakoSyntaxError.fromNode('ローカル変数『' + String(wordToken.value) + '』の定義エラー', wordToken);
1913
- }
1914
- const attr = attrNode.value;
1915
- if (attr === '公開') {
1916
- isExport = true;
1917
- }
1918
- else if (attr === '非公開') {
1919
- isExport = false;
1920
- }
1921
- else if (attr === 'エクスポート') {
1922
- isExport = true;
1923
- }
1924
- else {
1925
- this.logger.warn(`不明な変数属性『${attr}』が指定されています。`);
1926
- }
1927
- this.get();
1928
- }
1929
- const word = this.createVar(wordToken, vtype.type === '定数', isExport);
1930
- // 初期値がある?
1931
- let value = this.yNop();
1932
- if (this.check('eq')) {
1933
- this.get(); // skip '='
1934
- value = this.yCalc() || value;
1935
- }
1936
- if (this.check('comma')) {
1937
- this.get();
1938
- } // skip comma (ex) name1=val1, name2=val2
1939
- return {
1940
- type: 'def_local_var',
1941
- name: word.value,
1942
- vartype: vtype.type,
1943
- isExport,
1944
- blocks: [value],
1945
- ...map,
1946
- end: this.peekSourceMap()
1947
- };
1948
- }
1949
- // ローカル変数定義(その2)
1950
- if (this.accept(['変数', 'word'])) {
1951
- const wordVar = this.y[1];
1952
- this.index -= 2; // 「変数 word」の前に巻き戻す
1953
- // 変数の宣言および初期化1
1954
- if (this.accept(['変数', 'word', 'eq', this.yCalc])) {
1955
- const word = this.createVar(this.y[1], false, this.isExportDefault);
1956
- const astValue = this.y[3] || this.yNop();
1957
- return {
1958
- type: 'def_local_var',
1959
- name: word.value,
1960
- vartype: '変数',
1961
- blocks: [astValue],
1962
- ...map,
1963
- end: this.peekSourceMap()
1964
- };
1965
- }
1966
- // 変数の宣言および初期化2
1967
- if (this.accept(['変数', 'word', '{', 'word', '}', 'eq', this.yCalc])) {
1968
- let isExport = this.isExportDefault;
1969
- const attr = this.y[3].value;
1970
- if (attr === '公開') {
1971
- isExport = true;
1972
- }
1973
- else if (attr === '非公開') {
1974
- isExport = false;
1975
- }
1976
- else if (attr === 'エクスポート') {
1977
- isExport = true;
1978
- }
1979
- else {
1980
- this.logger.warn(`不明な変数属性『${attr}』が指定されています。`);
1981
- }
1982
- const word = this.createVar(this.y[1], false, isExport);
1983
- const astValue = this.y[6] || this.yNop();
1984
- return {
1985
- type: 'def_local_var',
1986
- name: word.value,
1987
- vartype: '変数',
1988
- isExport,
1989
- blocks: [astValue],
1990
- ...map,
1991
- end: this.peekSourceMap()
1992
- };
1993
- }
1994
- // 変数宣言のみの場合
1995
- {
1996
- this.index += 2; // 変数 word を読んだとする
1997
- const word = this.createVar(wordVar, false, this.isExportDefault);
1998
- return {
1999
- type: 'def_local_var',
2000
- name: word.value,
2001
- vartype: '変数',
2002
- blocks: [this.yNop()],
2003
- ...map,
2004
- end: this.peekSourceMap()
2005
- };
2006
- }
2007
- }
2008
- if (this.accept(['定数', 'word', 'eq'])) {
2009
- const constName = this.y[1];
2010
- const word = this.createVar(constName, true, this.isExportDefault);
2011
- const astValue = this.yCalc() || this.yNop();
2012
- return {
2013
- type: 'def_local_var',
2014
- name: word.value,
2015
- vartype: '定数',
2016
- blocks: [astValue],
2017
- ...map,
2018
- end: this.peekSourceMap()
2019
- };
2020
- }
2021
- if (this.accept(['定数', 'word', '{', 'word', '}', 'eq'])) {
2022
- let isExport = this.isExportDefault;
2023
- const attr = this.y[3].value;
2024
- if (attr === '公開') {
2025
- isExport = true;
2026
- }
2027
- else if (attr === '非公開') {
2028
- isExport = false;
2029
- }
2030
- else if (attr === 'エクスポート') {
2031
- isExport = true;
2032
- }
2033
- else {
2034
- this.logger.warn(`不明な定数属性『${attr}』が指定されています。`);
2035
- }
2036
- const word = this.createVar(this.y[1], true, isExport);
2037
- const astValue = this.yCalc() || this.yNop();
2038
- return {
2039
- type: 'def_local_var',
2040
- name: word.value,
2041
- vartype: '定数',
2042
- isExport,
2043
- blocks: [astValue],
2044
- ...map,
2045
- end: this.peekSourceMap()
2046
- };
2047
- }
2048
- // 複数定数への代入 #563
2049
- if (this.accept(['定数', this.yJSONArray, 'eq'])) {
2050
- const names = this.y[1];
2051
- // check array
2052
- if (names && names.blocks instanceof Array) {
2053
- for (const i in names.blocks) {
2054
- if (names.blocks[i].type !== 'word') {
2055
- throw NakoSyntaxError.fromNode(`複数定数の代入文${Number(i) + 1}番目でエラー。『定数[A,B,C]=[1,2,3]』の書式で記述してください。`, this.y[0]);
2056
- }
2057
- }
2058
- }
2059
- else {
2060
- throw NakoSyntaxError.fromNode('複数定数の代入文でエラー。『定数[A,B,C]=[1,2,3]』の書式で記述してください。', this.y[0]);
2061
- }
2062
- const namesAst = this._tokensToNodes(this.createVarList(names.blocks, true, this.isExportDefault));
2063
- const astValue = this.yCalc() || this.yNop();
2064
- return {
2065
- type: 'def_local_varlist',
2066
- names: namesAst,
2067
- vartype: '定数',
2068
- blocks: [astValue],
2069
- ...map,
2070
- end: this.peekSourceMap()
2071
- };
2072
- }
2073
- // 複数変数への代入 #563
2074
- if (this.accept(['変数', this.yJSONArray, 'eq'])) {
2075
- const names = this.y[1];
2076
- // check array
2077
- if (names && names.blocks instanceof Array) {
2078
- names.blocks.forEach((block, index) => {
2079
- if (block.type !== 'word') {
2080
- throw NakoSyntaxError.fromNode(`複数変数の代入文${String(index + 1)}番目でエラー『変数[A,B,C]=[1,2,3]』の書式で記述してください。`, this.y[0]);
2081
- }
2082
- });
2083
- }
2084
- else {
2085
- throw NakoSyntaxError.fromNode('複数変数の代入文でエラー。『変数[A,B,C]=[1,2,3]』の書式で記述してください。', this.y[0]);
2086
- }
2087
- const namesAst = this._tokensToNodes(this.createVarList(names.blocks, false, this.isExportDefault));
2088
- const astValue = this.yCalc() || this.yNop();
2089
- return {
2090
- type: 'def_local_varlist',
2091
- names: namesAst,
2092
- vartype: '変数',
2093
- blocks: [astValue],
2094
- ...map,
2095
- end: this.peekSourceMap()
2096
- };
2097
- }
2098
- // 複数変数への代入 #563
2099
- if (this.check2(['word', 'comma', 'word'])) {
2100
- // 2 word
2101
- if (this.accept(['word', 'comma', 'word', 'eq'])) {
2102
- let names = [this.y[0], this.y[2]];
2103
- names = this.createVarList(names, false, this.isExportDefault);
2104
- const astValue = this.yCalc() || this.yNop();
2105
- return {
2106
- type: 'def_local_varlist',
2107
- names,
2108
- vartype: '変数',
2109
- blocks: [astValue],
2110
- ...map,
2111
- end: this.peekSourceMap()
2112
- };
2113
- }
2114
- // 3 word
2115
- if (this.accept(['word', 'comma', 'word', 'comma', 'word', 'eq'])) {
2116
- let names = [this.y[0], this.y[2], this.y[4]];
2117
- names = this.createVarList(names, false, this.isExportDefault);
2118
- const astValue = this.yCalc() || this.yNop();
2119
- return {
2120
- type: 'def_local_varlist',
2121
- names,
2122
- vartype: '変数',
2123
- blocks: [astValue],
2124
- ...map,
2125
- end: this.peekSourceMap()
2126
- };
2127
- }
2128
- // 4 word
2129
- if (this.accept(['word', 'comma', 'word', 'comma', 'word', 'comma', 'word', 'eq'])) {
2130
- let names = [this.y[0], this.y[2], this.y[4], this.y[6]];
2131
- names = this.createVarList(names, false, this.isExportDefault);
2132
- const astValue = this.yCalc() || this.yNop();
2133
- return {
2134
- type: 'def_local_varlist',
2135
- names,
2136
- vartype: '変数',
2137
- blocks: [astValue],
2138
- ...map,
2139
- end: this.peekSourceMap()
2140
- };
2141
- }
2142
- // 5 word
2143
- if (this.accept(['word', 'comma', 'word', 'comma', 'word', 'comma', 'word', 'comma', 'word', 'eq'])) {
2144
- let names = [this.y[0], this.y[2], this.y[4], this.y[6], this.y[8]];
2145
- names = this.createVarList(names, false, this.isExportDefault);
2146
- const astValue = this.yCalc() || this.yNop();
2147
- return {
2148
- type: 'def_local_varlist',
2149
- names,
2150
- vartype: '変数',
2151
- blocks: [astValue],
2152
- ...map,
2153
- end: this.peekSourceMap()
2154
- };
2155
- }
2156
- }
2157
- return null;
2158
- }
2159
- /**
2160
- * 配列のインデックスが1から始まる場合を考慮するか
2161
- * @param {Ast} node
2162
- * @returns
2163
- */
2164
- checkArrayIndex(node) {
2165
- // 配列が0から始まるのであればそのまま返す
2166
- if (this.arrayIndexFrom === 0) {
2167
- return node;
2168
- }
2169
- // 配列が1から始まるのであれば演算を加えて返す
2170
- const minusNum = {
2171
- ...node,
2172
- 'type': 'number',
2173
- 'value': this.arrayIndexFrom
2174
- };
2175
- return {
2176
- ...node,
2177
- type: 'op',
2178
- operator: '-',
2179
- blocks: [node, minusNum]
2180
- };
2181
- }
2182
- /**
2183
- * 配列のインデックスを逆順にするのを考慮するか
2184
- * @param {Ast[]| null} ary
2185
- */
2186
- checkArrayReverse(ary) {
2187
- if (!ary) {
2188
- return [];
2189
- }
2190
- if (!this.flagReverseArrayIndex) {
2191
- return ary;
2192
- }
2193
- // 二次元以上の配列変数のアクセスを[y][x]ではなく[x][y]と順序を変更する
2194
- if (ary.length <= 1) {
2195
- return ary;
2196
- }
2197
- return ary.reverse();
2198
- }
2199
- /** @returns {AstLetArray | null} */
2200
- yLetArrayAt(map) {
2201
- // 一次元配列
2202
- if (this.accept(['word', '@', this.yValue, 'eq', this.yCalc])) {
2203
- const astValue = this.y[4];
2204
- return {
2205
- type: 'let_array',
2206
- name: this.getVarName(this.y[0]).value,
2207
- blocks: [astValue, this.checkArrayIndex(this.y[2])],
2208
- checkInit: this.flagCheckArrayInit,
2209
- ...map,
2210
- end: this.peekSourceMap()
2211
- };
2212
- }
2213
- // 二次元配列
2214
- if (this.accept(['word', '@', this.yValue, '@', this.yValue, 'eq', this.yCalc])) {
2215
- const astValue = this.y[6];
2216
- const astIndexes = this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4])]);
2217
- return {
2218
- type: 'let_array',
2219
- name: this.getVarName(this.y[0]).value,
2220
- blocks: [astValue, ...astIndexes],
2221
- checkInit: this.flagCheckArrayInit,
2222
- ...map,
2223
- end: this.peekSourceMap()
2224
- };
2225
- }
2226
- // 三次元配列
2227
- if (this.accept(['word', '@', this.yValue, '@', this.yValue, '@', this.yValue, 'eq', this.yCalc])) {
2228
- const astValue = this.y[8];
2229
- const astIndexes = this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4]), this.checkArrayIndex(this.y[6])]);
2230
- return {
2231
- type: 'let_array',
2232
- name: this.getVarName(this.y[0]).value,
2233
- blocks: [astValue, ...astIndexes],
2234
- checkInit: this.flagCheckArrayInit,
2235
- ...map,
2236
- end: this.peekSourceMap()
2237
- };
2238
- }
2239
- // 二次元配列(カンマ指定)
2240
- if (this.accept(['word', '@', this.yValue, 'comma', this.yValue, 'eq', this.yCalc])) {
2241
- const astValue = this.y[6];
2242
- const astIndexes = this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4])]);
2243
- return {
2244
- type: 'let_array',
2245
- name: this.getVarName(this.y[0]).value,
2246
- blocks: [astValue, ...astIndexes],
2247
- checkInit: this.flagCheckArrayInit,
2248
- ...map,
2249
- end: this.peekSourceMap()
2250
- };
2251
- }
2252
- // 三次元配列(カンマ指定)
2253
- if (this.accept(['word', '@', this.yValue, 'comma', this.yValue, 'comma', this.yValue, 'eq', this.yCalc])) {
2254
- const astValue = this.y[8];
2255
- const astIndexes = this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4]), this.checkArrayIndex(this.y[6])]);
2256
- return {
2257
- type: 'let_array',
2258
- name: this.getVarName(this.y[0]).value,
2259
- blocks: [astValue, ...astIndexes],
2260
- checkInit: this.flagCheckArrayInit,
2261
- ...map,
2262
- end: this.peekSourceMap()
2263
- };
2264
- }
2265
- return null;
2266
- }
2267
- /** @returns {Ast | null} */
2268
- yLetArrayBracket(map) {
2269
- // 一次元配列
2270
- if (this.accept(['word', '[', this.yCalc, ']', 'eq', this.yCalc])) {
2271
- const astValue = this.y[5];
2272
- const astIndexes = [this.checkArrayIndex(this.y[2])];
2273
- return {
2274
- type: 'let_array',
2275
- name: this.getVarName(this.y[0]).value,
2276
- blocks: [astValue, ...astIndexes],
2277
- checkInit: this.flagCheckArrayInit,
2278
- ...map,
2279
- end: this.peekSourceMap()
2280
- };
2281
- }
2282
- // 二次元配列 --- word[a][b] = c
2283
- if (this.accept(['word', '[', this.yCalc, ']', '[', this.yCalc, ']', 'eq', this.yCalc])) {
2284
- const astValue = this.y[8];
2285
- const astIndexes = this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[5])]);
2286
- return {
2287
- type: 'let_array',
2288
- name: this.getVarName(this.y[0]).value,
2289
- blocks: [astValue, ...astIndexes],
2290
- tag: '2',
2291
- checkInit: this.flagCheckArrayInit,
2292
- ...map,
2293
- end: this.peekSourceMap()
2294
- };
2295
- }
2296
- // 二次元配列 --- word[a, b] = c
2297
- if (this.accept(['word', '[', this.yCalc, 'comma', this.yCalc, ']', 'eq', this.yCalc])) {
2298
- const astValue = this.y[7];
2299
- const astIndexes = this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4])]);
2300
- return {
2301
- type: 'let_array',
2302
- name: this.getVarName(this.y[0]).value,
2303
- blocks: [astValue, ...astIndexes],
2304
- checkInit: this.flagCheckArrayInit,
2305
- tag: '2',
2306
- ...map,
2307
- end: this.peekSourceMap()
2308
- };
2309
- }
2310
- // 三次元配列 --- word[a][b][c] = d
2311
- if (this.accept(['word', '[', this.yCalc, ']', '[', this.yCalc, ']', '[', this.yCalc, ']', 'eq', this.yCalc])) {
2312
- const astValue = this.y[11];
2313
- const astIndexes = this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[5]), this.checkArrayIndex(this.y[8])]);
2314
- return {
2315
- type: 'let_array',
2316
- name: this.getVarName(this.y[0]).value,
2317
- blocks: [astValue, ...astIndexes],
2318
- checkInit: this.flagCheckArrayInit,
2319
- ...map,
2320
- end: this.peekSourceMap()
2321
- };
2322
- }
2323
- // 三次元配列 --- word[a, b, c] = d
2324
- if (this.accept(['word', '[', this.yCalc, 'comma', this.yCalc, 'comma', this.yCalc, ']', 'eq', this.yCalc])) {
2325
- const astValue = this.y[9];
2326
- const astIndexes = this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4]), this.checkArrayIndex(this.y[6])]);
2327
- return {
2328
- type: 'let_array',
2329
- name: this.getVarName(this.y[0]).value,
2330
- index: this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4]), this.checkArrayIndex(this.y[6])]),
2331
- blocks: [astValue, ...astIndexes],
2332
- checkInit: this.flagCheckArrayInit,
2333
- ...map,
2334
- end: this.peekSourceMap()
2335
- };
2336
- }
2337
- // --- --- --- --- --- --- --- --- --- --- --- --- ---
2338
- // 配列 + オブジェクトプロパティ構文 (#2139)
2339
- // --- --- --- --- --- --- --- --- --- --- --- --- ---
2340
- // 一次元配列 + オブジェクトプロパティ構文 --- word[a]$b = c
2341
- if (this.accept(['word', '[', this.yCalc, ']', '$', 'word', 'eq', this.yCalc])) {
2342
- const astValue = this.y[7];
2343
- const astIndexes = [this.checkArrayIndex(this.y[2])];
2344
- const astProp = this.y[5];
2345
- astProp.type = 'string';
2346
- return {
2347
- type: 'let_prop',
2348
- name: this.getVarName(this.y[0]).value,
2349
- blocks: [astValue, ...astIndexes],
2350
- index: [astProp],
2351
- ...map,
2352
- end: this.peekSourceMap()
2353
- };
2354
- }
2355
- // 一次元配列 + 二次元オブジェクトプロパティ構文 --- word[a]$b$c = d
2356
- if (this.accept(['word', '[', this.yCalc, ']', '$', 'word', '$', 'word', 'eq', this.yCalc])) {
2357
- const astVarName = this.y[0];
2358
- const astIndex = this.y[2];
2359
- const astProp1 = this.y[5];
2360
- const astProp2 = this.y[7];
2361
- const astValue = this.y[9];
2362
- astProp1.type = 'string';
2363
- astProp2.type = 'string';
2364
- return {
2365
- type: 'let_prop',
2366
- name: this.getVarName(astVarName).value,
2367
- blocks: [astValue, astIndex],
2368
- index: [astProp1, astProp2],
2369
- ...map,
2370
- end: this.peekSourceMap()
2371
- };
2372
- }
2373
- // 二次元配列 + オブジェクトプロパティ構文 --- word[a][b]$c = d
2374
- if (this.accept(['word', '[', this.yCalc, ']', '[', this.yCalc, ']', '$', 'word', 'eq', this.yCalc])) {
2375
- const astValue = this.y[10];
2376
- const astIndexes = this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[5])]);
2377
- const astProp = this.y[8];
2378
- astProp.type = 'string';
2379
- return {
2380
- type: 'let_prop',
2381
- name: this.getVarName(this.y[0]).value,
2382
- blocks: [astValue, ...astIndexes],
2383
- index: [astProp],
2384
- ...map,
2385
- end: this.peekSourceMap()
2386
- };
2387
- }
2388
- // 二次元配列 + オブジェクトプロパティ構文 --- word[a, b]$c = d
2389
- if (this.accept(['word', '[', this.yCalc, ',', this.yCalc, ']', '$', 'word', 'eq', this.yCalc])) {
2390
- const astValue = this.y[9];
2391
- const astIndexes = this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4])]);
2392
- const astProp = this.y[7];
2393
- astProp.type = 'string';
2394
- return {
2395
- type: 'let_prop',
2396
- name: this.getVarName(this.y[0]).value,
2397
- blocks: [astValue, ...astIndexes],
2398
- index: [astProp],
2399
- ...map,
2400
- end: this.peekSourceMap()
2401
- };
2402
- }
2403
- // 二次元配列 + 二次元オブジェクトプロパティ構文 --- word[a][b]$c$d = e
2404
- if (this.accept([
2405
- 'word', '[', this.yCalc, ']', '[', this.yCalc, ']', // 0...6
2406
- '$', 'word', '$', 'word', 'eq', this.yCalc // 7...12
2407
- ])) {
2408
- const astVarName = this.y[0];
2409
- const astIndex1 = this.y[2];
2410
- const astIndex2 = this.y[5];
2411
- const astProp1 = this.y[8];
2412
- const astProp2 = this.y[10];
2413
- const astValue = this.y[12];
2414
- astProp1.type = 'string';
2415
- astProp2.type = 'string';
2416
- return {
2417
- type: 'let_prop',
2418
- name: this.getVarName(astVarName).value,
2419
- blocks: [astValue, astIndex1, astIndex2],
2420
- index: [astProp1, astProp2],
2421
- ...map,
2422
- end: this.peekSourceMap()
2423
- };
2424
- }
2425
- return null;
2426
- }
2427
- /** @returns {Ast | null} */
2428
- yCalc() {
2429
- const map = this.peekSourceMap();
2430
- if (this.check('eol')) {
2431
- return null;
2432
- }
2433
- // 値を一つ読む
2434
- const t = this.yGetArg();
2435
- if (!t) {
2436
- return null;
2437
- }
2438
- // 助詞がある? つまり、関数呼び出しがある?
2439
- if (t.josi === '' && !this.canNextFuncTakeNoJosiArg()) {
2440
- return t;
2441
- } // 値だけの場合
2442
- // 関数の呼び出しがあるなら、スタックに載せて関数読み出しを呼ぶ
2443
- const tmpReadingCalc = this.isReadingCalc;
2444
- this.isReadingCalc = true;
2445
- this.pushStack(t);
2446
- const t1 = this.yCall();
2447
- this.isReadingCalc = tmpReadingCalc;
2448
- if (!t1) {
2449
- // 関数がなければ、先ほど積んだ値をスタックから取り出して返す
2450
- return this.popStack();
2451
- }
2452
- // 計算式をfCalcとする
2453
- let fCalc = t1;
2454
- // それが連文か助詞を読んで確認
2455
- if (RenbunJosi.indexOf(t1.josi || '') >= 0) {
2456
- // 連文なら右側を読んで左側とくっつける
2457
- const t2 = this.yCalc();
2458
- if (t2) {
2459
- fCalc = {
2460
- type: 'renbun',
2461
- operator: 'renbun',
2462
- blocks: [t1, t2],
2463
- josi: t2.josi,
2464
- ...map,
2465
- end: this.peekSourceMap()
2466
- };
2467
- }
2468
- }
2469
- // 演算子があれば続ける
2470
- const op = this.peek();
2471
- if (!op) {
2472
- return fCalc;
2473
- }
2474
- if (opPriority[op.type]) {
2475
- return this.yGetArgOperator(fCalc);
2476
- }
2477
- return fCalc;
2478
- }
2479
- /** 次の関数が空助詞の値を引数として受け取れるか */
2480
- canNextFuncTakeNoJosiArg() {
2481
- if (!this.check('func')) {
2482
- return false;
2483
- }
2484
- const func = this.peek();
2485
- const josiList = func?.meta?.josi;
2486
- if (!josiList) {
2487
- return false;
2488
- }
2489
- return josiList.some((josi) => josi.indexOf('') >= 0);
2490
- }
2491
- /** @returns {Ast | null} */
2492
- yValueKakko() {
2493
- if (!this.check('(')) {
2494
- return null;
2495
- }
2496
- const t = this.get(); // skip '('
2497
- if (!t) {
2498
- throw new Error('[System Error] check したのに get できない');
2499
- }
2500
- this.saveStack();
2501
- const v = this.yCalc() || this.ySentence();
2502
- if (v === null) {
2503
- const v2 = this.get();
2504
- this.logger.debug('(...)の解析エラー。' + this.nodeToStr(v2, { depth: 1 }, true) + 'の近く', t);
2505
- throw NakoSyntaxError.fromNode('(...)の解析エラー。' + this.nodeToStr(v2, { depth: 1 }, false) + 'の近く', t);
2506
- }
2507
- if (!this.check(')')) {
2508
- this.logger.debug('(...)の解析エラー。' + this.nodeToStr(v, { depth: 1 }, true) + 'の近く', t);
2509
- throw NakoSyntaxError.fromNode('(...)の解析エラー。' + this.nodeToStr(v, { depth: 1 }, false) + 'の近く', t);
2510
- }
2511
- const closeParent = this.get(); // skip ')'
2512
- this.loadStack();
2513
- if (closeParent) {
2514
- v.josi = closeParent.josi;
2515
- }
2516
- // (...)の後の配列アクセスに対応 #1985
2517
- return this.yRefArrayValue(v);
2518
- }
2519
- yConst(tok, map) {
2520
- // ['number', 'bigint', 'string']
2521
- const astConst = {
2522
- type: tok.type,
2523
- value: tok.value,
2524
- josi: tok.josi,
2525
- ...map
2526
- };
2527
- return astConst;
2528
- }
2529
- /** @returns {Ast | null} */
2530
- yValue() {
2531
- const map = this.peekSourceMap();
2532
- // カンマなら飛ばす #877
2533
- if (this.check('comma')) {
2534
- this.get();
2535
- }
2536
- // プリミティブな値
2537
- if (this.checkTypes(['number', 'bigint', 'string'])) {
2538
- return this.yConst(this.getCur(), map);
2539
- }
2540
- // 丸括弧
2541
- if (this.check('(')) {
2542
- return this.yValueKakko();
2543
- }
2544
- // マイナス記号
2545
- if (this.check2(['-', 'number']) || this.check2(['-', 'word']) || this.check2(['-', 'func'])) {
2546
- const m = this.get(); // skip '-'
2547
- const v = this.yValue();
2548
- const josi = (v && v.josi) ? v.josi : '';
2549
- const line = (m && m.line) ? m.line : 0;
2550
- const astLeft = { type: 'number', value: -1, line };
2551
- const astRight = v || this.yNop();
2552
- return {
2553
- type: 'op',
2554
- operator: '*',
2555
- blocks: [astLeft, astRight],
2556
- josi,
2557
- ...map,
2558
- end: this.peekSourceMap()
2559
- };
2560
- }
2561
- // NOT
2562
- if (this.check('not')) {
2563
- this.get(); // skip '!'
2564
- const v = this.yValue();
2565
- const josi = (v && v.josi) ? v.josi : '';
2566
- return {
2567
- type: 'not',
2568
- operator: 'not',
2569
- blocks: [v],
2570
- josi,
2571
- ...map,
2572
- end: this.peekSourceMap()
2573
- };
2574
- }
2575
- // JSON object
2576
- const a = this.yJSONArray();
2577
- if (a) {
2578
- return a;
2579
- }
2580
- const o = this.yJSONObject();
2581
- if (o) {
2582
- return o;
2583
- }
2584
- // 一語関数
2585
- if (this.check('func')) {
2586
- const oneWordFuncToken = this.peek();
2587
- if (!oneWordFuncToken) {
2588
- throw new Error('[System Error] 正しく値が取れませんでした。');
2589
- }
2590
- const splitType = operatorList.concat(['eol', ')', ']', 'ならば', '回', '間', '反復', '条件分岐']);
2591
- if (this.check2(['func', splitType]) || (oneWordFuncToken?.josi && oneWordFuncToken.josi !== '')) {
2592
- this.get(); // skip oneWordFuncToken
2593
- const tt = oneWordFuncToken;
2594
- const f = this.getVarNameRef(tt);
2595
- this.usedFuncs.add(f.value);
2596
- // 引数の個数をチェック
2597
- const meta = tt.meta;
2598
- const args = [];
2599
- if (!meta) {
2600
- throw NakoSyntaxError.fromNode(`一語関数『${f.value}』は存在しません。`, tt);
2601
- }
2602
- if (meta.josi && meta.josi.length === 1) {
2603
- args.push({ type: 'word', value: 'それ' });
2604
- }
2605
- else if (meta.josi && meta.josi.length >= 2) {
2606
- throw NakoSyntaxError.fromNode(`関数『${f.value}』で引数が指定されていません。${meta.josi.length}個の引数を指定してください。`, tt);
2607
- }
2608
- return {
2609
- type: 'func',
2610
- name: f.value,
2611
- blocks: args,
2612
- josi: f.josi,
2613
- meta,
2614
- asyncFn: !!meta.asyncFn,
2615
- ...map,
2616
- end: this.peekSourceMap()
2617
- };
2618
- }
2619
- }
2620
- // C風関数呼び出し FUNC(...)
2621
- if (this.check2([['func', 'word'], '(']) && this.peekDef().josi === '') {
2622
- const funcNameToken = this.peek();
2623
- if (this.accept([['func', 'word'], '(', this.yGetArgParen, ')'])) {
2624
- const funcToken = this.getVarNameRef(this.y[0]);
2625
- const meta = funcToken.meta; // undefinedかもしれない
2626
- const args = this.y[2];
2627
- const funcName = funcToken.value;
2628
- let asyncFn = false;
2629
- this.usedFuncs.add(funcName);
2630
- // 引数の個数をチェック
2631
- if (meta && meta.josi) {
2632
- // 引数の個数が異なる場合
2633
- if (meta.josi.length === args.length) {
2634
- // ok
2635
- }
2636
- else if (meta.isVariableJosi) {
2637
- // ok
2638
- }
2639
- else { // 引数の個数が違う
2640
- throw NakoSyntaxError.fromNode(`関数『${funcToken.value}』で引数${args.length}個が指定されましたが、${meta.josi.length}個の引数を指定してください。`, funcToken);
2641
- }
2642
- asyncFn = !!meta.asyncFn;
2643
- }
2644
- const funcNode = {
2645
- type: 'func',
2646
- name: funcName,
2647
- blocks: args,
2648
- josi: this.y[3].josi,
2649
- meta,
2650
- asyncFn,
2651
- ...map,
2652
- end: this.peekSourceMap()
2653
- };
2654
- return this.yApplyCallValue(funcNode);
2655
- }
2656
- throw NakoSyntaxError.fromNode('C風関数呼び出しのエラー', funcNameToken || NewEmptyToken());
2657
- }
2658
- // 無名関数(関数オブジェクト)
2659
- if (this.check('def_func')) {
2660
- return this.yMumeiFunc();
2661
- }
2662
- // 変数
2663
- const word = this.yValueWord();
2664
- if (word) {
2665
- return word;
2666
- }
2667
- // 関数への参照
2668
- const funcPtr = this.yValueFuncPointer();
2669
- if (funcPtr) {
2670
- return funcPtr;
2671
- }
2672
- // その他
2673
- return null;
2674
- }
2675
- yValueWordGetIndex(ast) {
2676
- if (!ast.index) {
2677
- ast.index = [];
2678
- }
2679
- // word @ a, b, c
2680
- if (this.check('@')) {
2681
- if (this.accept(['@', this.yValue, 'comma', this.yValue, 'comma', this.yValue])) {
2682
- ast.index.push(this.checkArrayIndex(this.y[1]));
2683
- ast.index.push(this.checkArrayIndex(this.y[3]));
2684
- ast.index.push(this.checkArrayIndex(this.y[5]));
2685
- ast.index = this.checkArrayReverse(ast.index);
2686
- ast.josi = this.y[5].josi;
2687
- return true;
2688
- }
2689
- if (this.accept(['@', this.yValue, 'comma', this.yValue])) {
2690
- ast.index.push(this.checkArrayIndex(this.y[1]));
2691
- ast.index.push(this.checkArrayIndex(this.y[3]));
2692
- ast.index = this.checkArrayReverse(ast.index);
2693
- ast.josi = this.y[3].josi;
2694
- return true;
2695
- }
2696
- if (this.accept(['@', this.yValue])) {
2697
- ast.index.push(this.checkArrayIndex(this.y[1]));
2698
- ast.josi = this.y[1].josi;
2699
- return true;
2700
- }
2701
- throw NakoSyntaxError.fromNode('変数の後ろの『@要素』の指定が不正です。', ast);
2702
- }
2703
- if (this.check('[')) {
2704
- // 1次元配列変数 + 3次元プロパティ
2705
- if (this.accept(['[', this.yCalc, ']', '$', 'word', '$', 'word', '$', 'word'])) {
2706
- ast.index.push(this.checkArrayIndex(this.y[1]));
2707
- const prop1 = this.y[4];
2708
- const prop2 = this.y[6];
2709
- const prop3 = this.y[8];
2710
- prop1.type = 'string';
2711
- prop2.type = 'string';
2712
- prop3.type = 'string';
2713
- ast.index.push(prop1);
2714
- ast.index.push(prop2);
2715
- ast.index.push(prop3);
2716
- ast.josi = prop3.josi;
2717
- return ast.josi === ''; // 助詞があればそこで終了(false)を返す (#1627)
2718
- }
2719
- // 1次元配列変数 + 2次元プロパティ
2720
- if (this.accept(['[', this.yCalc, ']', '$', 'word', '$', 'word'])) {
2721
- ast.index.push(this.checkArrayIndex(this.y[1]));
2722
- const prop1 = this.y[4];
2723
- const prop2 = this.y[6];
2724
- prop1.type = 'string';
2725
- prop2.type = 'string';
2726
- ast.index.push(prop1);
2727
- ast.index.push(prop2);
2728
- ast.josi = prop2.josi;
2729
- return ast.josi === ''; // 助詞があればそこで終了(false)を返す (#1627)
2730
- }
2731
- // 1次元配列変数 + 1次元プロパティ
2732
- if (this.accept(['[', this.yCalc, ']', '$', 'word'])) {
2733
- ast.index.push(this.checkArrayIndex(this.y[1]));
2734
- const prop = this.y[4];
2735
- prop.type = 'string';
2736
- ast.index.push(prop);
2737
- ast.josi = this.y[4].josi;
2738
- return this.y[4].josi === ''; // 助詞があればそこで終了(false)を返す (#1627)
2739
- }
2740
- if (this.accept(['[', this.yCalc, ']'])) {
2741
- ast.index.push(this.checkArrayIndex(this.y[1]));
2742
- ast.josi = this.y[2].josi;
2743
- return this.y[2].josi === ''; // 助詞があればそこで終了(false)を返す (#1627)
2744
- }
2745
- if (this.accept(['[', this.yCalc, 'comma', this.yCalc, ']'])) {
2746
- const index = [
2747
- this.checkArrayIndex(this.y[1]),
2748
- this.checkArrayIndex(this.y[3])
2749
- ];
2750
- const aa = ast.index.pop();
2751
- ast.index = this.checkArrayReverse(index);
2752
- if (aa) {
2753
- ast.index.unshift(aa);
2754
- }
2755
- ast.josi = this.y[4].josi;
2756
- return this.y[4].josi === ''; // 助詞があればそこで終了(false)を返す
2757
- }
2758
- }
2759
- if (this.check('[')) {
2760
- if (this.accept(['[', this.yCalc, 'comma', this.yCalc, 'comma', this.yCalc, ']'])) {
2761
- const index = [
2762
- this.checkArrayIndex(this.y[1]),
2763
- this.checkArrayIndex(this.y[3]),
2764
- this.checkArrayIndex(this.y[5])
2765
- ];
2766
- const aa = ast.index.pop();
2767
- ast.index = this.checkArrayReverse(index);
2768
- if (aa) {
2769
- ast.index.unshift(aa);
2770
- }
2771
- ast.josi = this.y[6].josi;
2772
- return this.y[6].josi === ''; // 助詞があればそこで終了(false)を返す
2773
- }
2774
- }
2775
- return false;
2776
- }
2777
- yValueWordGetProp(ast) {
2778
- if (!ast.index) {
2779
- ast.index = [];
2780
- }
2781
- if (this.accept(['$', 'word', '$', 'word'])) {
2782
- const prop1 = this.y[1];
2783
- const prop2 = this.y[3];
2784
- prop1.type = 'string';
2785
- prop2.type = 'string';
2786
- ast.index.push(prop1);
2787
- ast.index.push(prop2);
2788
- ast.josi = this.y[3].josi;
2789
- return ast.josi === ''; // 助詞があればそこで終了(false)を返す
2790
- }
2791
- if (this.check('$')) {
2792
- if (this.accept(['$', 'word'])) {
2793
- this.y[1].type = 'string';
2794
- ast.index.push(this.y[1]);
2795
- ast.josi = this.y[1].josi;
2796
- return this.y[1].josi === ''; // 助詞があればそこで終了(false)を返す
2797
- }
2798
- }
2799
- if (this.accept(['$', this.yValue])) {
2800
- ast.index.push(this.y[1]);
2801
- ast.josi = this.y[1].josi;
2802
- return this.y[1].josi === ''; // 助詞があればそこで終了(false)を返す
2803
- }
2804
- return false;
2805
- }
2806
- /** @returns {Ast | null} */
2807
- yValueFuncPointer() {
2808
- const map = this.peekSourceMap();
2809
- if (this.check('func_pointer')) {
2810
- const t = this.getCur();
2811
- const ast = {
2812
- type: 'func_pointer',
2813
- name: t.value,
2814
- josi: t.josi,
2815
- ...map,
2816
- end: this.peekSourceMap()
2817
- };
2818
- return ast;
2819
- }
2820
- return null;
2821
- }
2822
- /** @returns {Ast | null} */
2823
- yValueWord() {
2824
- const map = this.peekSourceMap();
2825
- if (this.check('word')) {
2826
- const t = this.getCur();
2827
- const word = this.getVarNameRef(t);
2828
- // word[n] || word@n
2829
- if ((word.josi === '' && this.checkTypes(['[', '@'])) || (word.josi !== '' && this.check('@'))) {
2830
- const ast = {
2831
- type: 'ref_array', // 配列参照
2832
- name: word,
2833
- index: [],
2834
- josi: '',
2835
- ...map,
2836
- end: this.peekSourceMap()
2837
- };
2838
- while (!this.isEOF()) {
2839
- if (!this.yValueWordGetIndex(ast)) {
2840
- break;
2841
- }
2842
- }
2843
- if (ast.index && ast.index.length === 0) {
2844
- throw NakoSyntaxError.fromNode(`配列『${word.value}』アクセスで指定ミス`, word);
2845
- }
2846
- return ast;
2847
- }
2848
- // オブジェクトプロパティ構文(参照) word$prop (#1793)
2849
- if (this.check2(['$', 'word']) || this.check2(['$', 'string'])) {
2850
- const propList = [];
2851
- let josi = '';
2852
- while (this.check('$')) {
2853
- this.get(); // skip '$'
2854
- const prop = this.get();
2855
- propList.push(prop);
2856
- josi = prop.josi;
2857
- }
2858
- return {
2859
- type: 'ref_prop', // プロパティ参照
2860
- name: word,
2861
- index: propList,
2862
- josi,
2863
- ...map,
2864
- end: this.peekSourceMap()
2865
- };
2866
- }
2867
- return word; // Token to Ast
2868
- }
2869
- return null;
2870
- }
2871
- /** 変数を生成 */
2872
- createVar(word, isConst, isExport) {
2873
- let gname = word.value;
2874
- const typeName = isConst ? 'const' : 'var';
2875
- if (this.funcLevel === 0) {
2876
- // global ?
2877
- if (gname.indexOf('__') < 0) {
2878
- gname = this.modName + '__' + gname;
2879
- }
2880
- const defValue = { type: typeName, value: '', isExport };
2881
- this.funclist.set(gname, defValue);
2882
- const wordAst = word;
2883
- wordAst.value = gname;
2884
- return word;
2885
- }
2886
- else {
2887
- // local
2888
- this.localvars.set(gname, { type: typeName, value: '' });
2889
- return word;
2890
- }
2891
- }
2892
- /** 変数名を検索して解決する
2893
- * @param {Ast|Token} word
2894
- * @return {Ast|Token}
2895
- */
2896
- getVarName(word) {
2897
- // check word name
2898
- const f = this.findVar(word.value);
2899
- if (f) {
2900
- if (f && f.scope === 'global') {
2901
- word.value = f.name;
2902
- }
2903
- return word;
2904
- }
2905
- // 変数が見つからない
2906
- this.createVar(word, false, this.isExportDefault);
2907
- return word;
2908
- }
2909
- /** 変数名を検索して解決する */
2910
- getVarNameRef(word) {
2911
- // check word name
2912
- const f = this.findVar(word.value);
2913
- if (!f) { // 変数が見つからない
2914
- if (this.funcLevel === 0 && word.value.indexOf('__') < 0) {
2915
- word.value = this.modName + '__' + String(word.value);
2916
- }
2917
- }
2918
- else if (f && f.scope === 'global') {
2919
- word.value = f.name;
2920
- }
2921
- return word;
2922
- }
2923
- /** 複数の変数名を検索して解決する */
2924
- createVarList(words, isConst, isExport) {
2925
- for (let i = 0; i < words.length; i++) {
2926
- words[i] = this.createVar(words[i], isConst, isExport);
2927
- }
2928
- return words;
2929
- }
2930
- yJSONObjectValue() {
2931
- // 戻り値の形式
2932
- // Astblocks.blocks = [key1, value1, key2, value2, key3, value3 ...]
2933
- const a = [];
2934
- const firstToken = this.peek();
2935
- if (!firstToken) {
2936
- return [];
2937
- }
2938
- while (!this.isEOF()) {
2939
- while (this.check('eol')) {
2940
- this.get();
2941
- }
2942
- if (this.check('}')) {
2943
- break;
2944
- }
2945
- // key : value
2946
- if (this.accept(['word', ':', this.yCalc])) {
2947
- this.y[0].type = 'string'; // キー名の文字列記号省略の場合
2948
- a.push(this.y[0]);
2949
- a.push(this.y[2]);
2950
- }
2951
- else if (this.accept(['string', ':', this.yCalc])) { // 'key' : value
2952
- a.push(this.y[0]);
2953
- a.push(this.y[2]);
2954
- }
2955
- else if (this.accept(['word'])) { // key
2956
- const key = this.y[0];
2957
- const val = JSON.parse(JSON.stringify(key));
2958
- key.type = 'string'; // キー名の文字列記号省略の場合
2959
- a.push(key);
2960
- a.push(val);
2961
- }
2962
- else if (this.checkTypes(['string', 'number'])) { // str or num
2963
- const w = this.getCur(); // Tokenを強制的にAstに変換している
2964
- a.push(w);
2965
- a.push(w);
2966
- }
2967
- else {
2968
- throw NakoSyntaxError.fromNode('辞書オブジェクトの宣言で末尾の『}』がありません。', firstToken);
2969
- }
2970
- if (this.check('comma')) {
2971
- this.get();
2972
- }
2973
- }
2974
- return a;
2975
- }
2976
- // 辞書型変数の取得
2977
- yJSONObject() {
2978
- const a = this.yJSONObjectRaw();
2979
- if (!a) {
2980
- return null;
2981
- }
2982
- return this.yRefArrayValue(a);
2983
- }
2984
- yJSONObjectRaw() {
2985
- const map = this.peekSourceMap();
2986
- if (this.accept(['{', '}'])) {
2987
- return {
2988
- type: 'json_obj',
2989
- blocks: [],
2990
- josi: this.y[1].josi,
2991
- ...map,
2992
- end: this.peekSourceMap()
2993
- };
2994
- }
2995
- if (this.accept(['{', this.yJSONObjectValue, '}'])) {
2996
- return {
2997
- type: 'json_obj',
2998
- blocks: this.y[1],
2999
- josi: this.y[2].josi,
3000
- ...map,
3001
- end: this.peekSourceMap()
3002
- };
3003
- }
3004
- // 辞書初期化に終わりがなかった場合 (エラーチェックのため) #958
3005
- if (this.accept(['{', this.yJSONObjectValue])) {
3006
- throw NakoSyntaxError.fromNode('辞書型変数の初期化が『}』で閉じられていません。', this.y[1]);
3007
- }
3008
- return null;
3009
- }
3010
- yJSONArrayValue() {
3011
- if (this.check('eol')) {
3012
- this.get();
3013
- }
3014
- // Arrayの最初の値
3015
- const v1 = this.yCalc();
3016
- if (v1 === null) {
3017
- return [];
3018
- }
3019
- if (this.check('comma')) {
3020
- this.get();
3021
- }
3022
- const a = [v1];
3023
- // 2つ目以降の値を取得
3024
- while (!this.isEOF()) {
3025
- if (this.check('eol')) {
3026
- this.get();
3027
- }
3028
- if (this.check(']')) {
3029
- break;
3030
- }
3031
- const v2 = this.yCalc();
3032
- if (v2 === null) {
3033
- break;
3034
- }
3035
- if (this.check('comma')) {
3036
- this.get();
3037
- }
3038
- a.push(v2);
3039
- }
3040
- return a;
3041
- }
3042
- // 配列や(値)の直後にある配列アクセスやプロパティアクセスを調べる
3043
- yRefArrayValue(value) {
3044
- let val = value;
3045
- for (;;) {
3046
- // 配列の直後に@や[]があるか?
3047
- // ただし、助詞がある場合には、別の引数の可能性があるので無視。 (例) [0,1,2]を[3,4,5]に配列***
3048
- if (val.josi === '' && this.checkTypes(['@', '['])) {
3049
- const ast = {
3050
- type: 'ref_array_value',
3051
- name: '@',
3052
- index: [val],
3053
- josi: '',
3054
- line: val.line,
3055
- end: this.peekSourceMap()
3056
- };
3057
- while (!this.isEOF()) {
3058
- if (!this.yValueWordGetIndex(ast)) {
3059
- break;
3060
- }
3061
- }
3062
- val = ast;
3063
- continue;
3064
- }
3065
- // 配列の直の後に$(プロパティ)があるか?
3066
- if (this.check('$')) {
3067
- const ast = {
3068
- type: 'ref_array_value',
3069
- name: '$',
3070
- index: [val],
3071
- josi: '',
3072
- line: val.line,
3073
- end: this.peekSourceMap()
3074
- };
3075
- while (!this.isEOF()) {
3076
- if (!this.yValueWordGetProp(ast)) {
3077
- break;
3078
- }
3079
- }
3080
- val = ast;
3081
- continue;
3082
- }
3083
- break;
3084
- }
3085
- return val;
3086
- }
3087
- yJSONArray() {
3088
- // 配列を得る
3089
- const a = this.yJSONArrayRaw();
3090
- if (!a) {
3091
- return null;
3092
- }
3093
- return this.yRefArrayValue(a);
3094
- }
3095
- /** @returns {AstBlocks | null} */
3096
- yJSONArrayRaw() {
3097
- const map = this.peekSourceMap();
3098
- if (this.accept(['[', ']'])) {
3099
- return {
3100
- type: 'json_array',
3101
- blocks: [],
3102
- josi: this.y[1].josi,
3103
- ...map,
3104
- end: this.peekSourceMap()
3105
- };
3106
- }
3107
- if (this.accept(['[', this.yJSONArrayValue, ']'])) {
3108
- return {
3109
- type: 'json_array',
3110
- blocks: this.y[1],
3111
- josi: this.y[2].josi,
3112
- ...map,
3113
- end: this.peekSourceMap()
3114
- };
3115
- }
3116
- // 配列に終わりがなかった場合 (エラーチェックのため) #958
3117
- if (this.accept(['[', this.yJSONArrayValue])) {
3118
- throw NakoSyntaxError.fromNode('配列変数の初期化が『]』で閉じられていません。', this.y[1]);
3119
- }
3120
- return null;
3121
- }
3122
- /** エラー監視構文 */
3123
- yTryExcept() {
3124
- const map = this.peekSourceMap();
3125
- if (!this.check('エラー監視')) {
3126
- return null;
3127
- }
3128
- const kansi = this.getCur(); // skip エラー監視
3129
- const block = this.yBlock();
3130
- if (!this.check2(['エラー', 'ならば'])) {
3131
- throw NakoSyntaxError.fromNode('エラー構文で『エラーならば』がありません。' +
3132
- '『エラー監視..エラーならば..ここまで』を対で記述します。', kansi);
3133
- }
3134
- this.get(); // skip エラー
3135
- this.get(); // skip ならば
3136
- const errBlock = this.yBlock();
3137
- if (this.check('ここまで')) {
3138
- this.get();
3139
- }
3140
- else {
3141
- throw NakoSyntaxError.fromNode('『ここまで』がありません。『エラー監視』...『エラーならば』...『ここまで』を対応させてください。', map);
3142
- }
3143
- return {
3144
- type: 'try_except',
3145
- blocks: [block, errBlock],
3146
- josi: '',
3147
- ...map,
3148
- end: this.peekSourceMap()
3149
- };
3150
- }
3151
- /** 関数ごとにasyncFnが必要か確認する */
3152
- _checkAsyncFn(node) {
3153
- if (!node) {
3154
- return false;
3155
- }
3156
- // 関数定義があれば関数
3157
- if (node.type === 'def_func' || node.type === 'def_test' || node.type === 'func_obj') {
3158
- // 関数定義でasyncFnが指定されているならtrueを返す
3159
- const def = node;
3160
- if (def.asyncFn) {
3161
- return true;
3162
- } // 既にasyncFnが指定されている
3163
- // 関数定義の中身を調べてasyncFnであるならtrueに変更する
3164
- let isAsyncFn = false;
3165
- for (const n of def.blocks) {
3166
- if (this._checkAsyncFn(n)) {
3167
- isAsyncFn = true;
3168
- def.asyncFn = isAsyncFn;
3169
- def.meta.asyncFn = isAsyncFn;
3170
- this.isModifiedNodes = true;
3171
- return true;
3172
- }
3173
- }
3174
- }
3175
- // 関数呼び出しを調べて非同期処理が必要ならtrueを返す
3176
- if (node.type === 'func') {
3177
- // 関数呼び出し自体が非同期処理ならtrueを返す
3178
- const callNode = node;
3179
- if (callNode.asyncFn) {
3180
- return true;
3181
- }
3182
- // 続けて、以下の関数呼び出しの引数などに非同期処理があるかどうか調べる
3183
- // 関数の引数は、node.blocksに格納されている
3184
- if (callNode.blocks) {
3185
- for (const n of callNode.blocks) {
3186
- if (this._checkAsyncFn(n)) {
3187
- callNode.asyncFn = true;
3188
- this.isModifiedNodes = true;
3189
- return true;
3190
- }
3191
- }
3192
- }
3193
- // さらに、関数のリンクを調べる
3194
- const func = this.funclist.get(callNode.name);
3195
- if (func && func.asyncFn) {
3196
- callNode.asyncFn = true;
3197
- this.isModifiedNodes = true;
3198
- return true;
3199
- }
3200
- return false;
3201
- }
3202
- // 連文 ... 現在、効率は悪いが非同期で実行することになっている
3203
- if (node.type === 'renbun') {
3204
- return true;
3205
- }
3206
- // その他
3207
- if (node.blocks) {
3208
- for (const n of node.blocks) {
3209
- if (this._checkAsyncFn(n)) {
3210
- return true;
3211
- }
3212
- }
3213
- }
3214
- return false;
3215
- }
3216
- /** TokenをそのままNodeに変換するメソッド(ただし簡単なものだけ対応)
3217
- * @returns {Ast[]}
3218
- */
3219
- _tokensToNodes(tokens) {
3220
- const nodes = [];
3221
- for (const token of tokens) {
3222
- nodes.push(this._tokenToNode(token));
3223
- }
3224
- return nodes;
3225
- }
3226
- /** TokenをそのままNodeに変換するメソッド(ただし簡単なものだけ対応)
3227
- * @returns {Ast}
3228
- */
3229
- _tokenToNode(token) {
3230
- const map = this.peekSourceMap(token);
3231
- if (token.type === 'string' || token.type === 'number' || token.type === 'bigint') {
3232
- return this.yConst(token, map);
3233
- }
3234
- if (token.type === 'word') {
3235
- return {
3236
- type: 'word',
3237
- value: token.value,
3238
- josi: token.josi,
3239
- ...map
3240
- };
3241
- }
3242
- if (token.type === 'eol' || token.type === '_eol') {
3243
- return {
3244
- type: 'eol',
3245
- ...map
3246
- };
3247
- }
3248
- throw new Error('[System Error] 未知のトークンがAstに変換されました。');
3249
- }
3250
- }