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,794 +0,0 @@
1
- // なでしこの字句解析を行う
2
- // 既に全角半角を揃えたコードに対して字句解析を行う
3
- import { opPriority } from './nako_parser_const.mjs';
4
- // 予約語句
5
- // (memo)「回」「間」「繰返」「反復」「抜」「続」「戻」「代入」などは _replaceWord で word から変換
6
- /** @types {Record<string, string>} */
7
- import reservedWords from './nako_reserved_words.mjs';
8
- import { isIndentChars } from './nako_indent_chars.mjs';
9
- // 助詞の一覧
10
- import { josiRE, removeJosiMap, tararebaMap, josiListExport } from './nako_josi_list.mjs';
11
- // 字句解析ルールの一覧
12
- import { rules, unitRE, cssUnitRE } from './nako_lex_rules.mjs';
13
- import { NakoLexerError, InternalLexerError } from './nako_errors.mjs';
14
- export class NakoLexer {
15
- /**
16
- * @param logger
17
- */
18
- constructor(logger) {
19
- this.logger = logger; // 字句解析した際,確認された関数の一覧
20
- this.funclist = new Map();
21
- this.modList = []; // 字句解析した際,取り込むモジュール一覧 --- nako3::lex で更新される
22
- this.result = [];
23
- this.modName = 'main.nako3'; // モジュール名
24
- this.moduleExport = new Map();
25
- this.reservedWords = Array.from(reservedWords.keys()); // for plugin_system::予約語一覧取得
26
- this.josiList = josiListExport; // for plugin_system::助詞一覧取得
27
- }
28
- /** 関数一覧をセット */
29
- setFuncList(listMap) {
30
- this.funclist = listMap;
31
- }
32
- /** モジュール公開既定値一覧をセット */
33
- setModuleExport(exportObj) {
34
- this.moduleExport = exportObj;
35
- }
36
- /**
37
- * @param tokens
38
- * @param {boolean} isFirst
39
- * @param {string} filename
40
- */
41
- replaceTokens(tokens, isFirst, filename) {
42
- this.result = tokens;
43
- this.modName = NakoLexer.filenameToModName(filename);
44
- // 関数の定義があれば funclist を更新
45
- NakoLexer.preDefineFunc(tokens, this.logger, this.funclist, this.moduleExport);
46
- this._replaceWord(this.result);
47
- if (isFirst) {
48
- if (this.result.length > 0) {
49
- const eof = this.result[this.result.length - 1];
50
- this.result.push({
51
- type: 'eol',
52
- line: eof.line,
53
- column: 0,
54
- indent: -1,
55
- file: eof.file,
56
- josi: '',
57
- value: '---',
58
- startOffset: eof.startOffset,
59
- endOffset: eof.endOffset,
60
- rawJosi: ''
61
- }); // 改行
62
- this.result.push({
63
- type: 'eof',
64
- line: eof.line,
65
- indent: -1,
66
- column: 0,
67
- file: eof.file,
68
- josi: '',
69
- value: '',
70
- startOffset: eof.startOffset,
71
- endOffset: eof.endOffset,
72
- rawJosi: ''
73
- }); // ファイル末尾
74
- }
75
- else {
76
- this.result.push({
77
- type: 'eol',
78
- line: 0,
79
- column: 0,
80
- indent: -1,
81
- file: '',
82
- josi: '',
83
- value: '---',
84
- startOffset: 0,
85
- endOffset: 0,
86
- rawJosi: ''
87
- }); // 改行
88
- this.result.push({
89
- type: 'eof',
90
- line: 0,
91
- column: 0,
92
- indent: -1,
93
- file: '',
94
- josi: '',
95
- value: '',
96
- startOffset: 0,
97
- endOffset: 0,
98
- rawJosi: ''
99
- }); // ファイル末尾
100
- }
101
- }
102
- return this.result;
103
- }
104
- /**
105
- * ファイル内で定義されている関数名を列挙する。結果はfunclistに書き込む。その他のトークンの置換処理も行う。
106
- * シンタックスハイライトの処理から呼び出すためにstaticメソッドにしている。
107
- */
108
- static preDefineFunc(tokens, logger, funclist, moduleexport) {
109
- // 関数を先読みして定義
110
- let i = 0;
111
- let isFuncPointer = false;
112
- const readArgs = () => {
113
- const args = [];
114
- const keys = {};
115
- if (tokens[i].type !== '(') {
116
- return [];
117
- }
118
- i++;
119
- while (tokens[i]) {
120
- const t = tokens[i];
121
- i++;
122
- if (t.type === ')') {
123
- break;
124
- }
125
- if (t.type === 'func') {
126
- isFuncPointer = true;
127
- }
128
- else if (t.type !== '|' && t.type !== 'comma') {
129
- if (isFuncPointer) {
130
- t.funcPointer = true;
131
- isFuncPointer = false;
132
- }
133
- args.push(t);
134
- if (!keys[t.value]) {
135
- keys[t.value] = [];
136
- }
137
- keys[t.value].push(t.josi);
138
- }
139
- }
140
- const varnames = [];
141
- const funcPointers = [];
142
- const result = [];
143
- const already = {};
144
- for (const arg of args) {
145
- if (!already[arg.value]) {
146
- const josi = keys[arg.value];
147
- result.push(josi);
148
- varnames.push(arg.value);
149
- if (arg.funcPointer) {
150
- funcPointers.push(arg.value);
151
- }
152
- else {
153
- funcPointers.push(null);
154
- }
155
- already[arg.value] = true;
156
- }
157
- }
158
- return [result, varnames, funcPointers];
159
- };
160
- // トークンを一つずつ確認
161
- while (i < tokens.length) {
162
- // タイプの置換
163
- const t = tokens[i];
164
- if (t.type === 'not' && tokens.length - i > 3) {
165
- let prevToken = { type: 'eol' };
166
- if (i >= 1) {
167
- prevToken = tokens[i - 1];
168
- }
169
- if (prevToken.type === 'eol') {
170
- let nextToken = tokens[i + 1];
171
- if (nextToken.type === 'word' && nextToken.value === 'モジュール公開既定値') {
172
- nextToken.type = 'モジュール公開既定値';
173
- nextToken = tokens[i + 2];
174
- if (nextToken.type === 'string' && nextToken.value === '非公開') {
175
- const modName = NakoLexer.filenameToModName(t.file);
176
- moduleexport.set(modName, false);
177
- i += 3;
178
- continue;
179
- }
180
- else if (nextToken.type === 'string' && nextToken.value === '公開') {
181
- const modName = NakoLexer.filenameToModName(t.file);
182
- moduleexport.set(modName, true);
183
- i += 3;
184
- continue;
185
- }
186
- }
187
- }
188
- }
189
- // 無名関数の定義:「xxには**」があった場合 ... 暗黙的な関数定義とする
190
- if (t.type === 'word' && t.josi === 'には') {
191
- t.josi = 'には';
192
- tokens.splice(i + 1, 0, { type: 'def_func', value: '関数', indent: t.indent, line: t.line, column: t.column, file: t.file, josi: '', startOffset: t.endOffset, endOffset: t.endOffset, rawJosi: '', tag: '無名関数' });
193
- i++;
194
- continue;
195
- }
196
- // 永遠に繰り返す→永遠の間に置換 #1686
197
- if (t.type === 'word' && t.value === '永遠' && t.josi === 'に') {
198
- const t2 = tokens[i + 1];
199
- if (t2.value === '繰返') {
200
- t2.value = '間';
201
- t2.josi = 'の';
202
- }
203
- i++;
204
- continue;
205
- }
206
- // N回をN|回に置換
207
- if (t.type === 'word' && t.josi === '' && t.value.length >= 2) {
208
- if (t.value.match(/回$/)) {
209
- t.value = t.value.substring(0, t.value.length - 1);
210
- // N回を挿入
211
- if (!t.endOffset) {
212
- t.endOffset = 1;
213
- }
214
- const kai = { type: '回', value: '回', indent: t.indent, line: t.line, column: t.column, file: t.file, josi: '', startOffset: t.endOffset - 1, endOffset: t.endOffset, rawJosi: '' };
215
- tokens.splice(i + 1, 0, kai);
216
- t.endOffset--;
217
- i++;
218
- }
219
- }
220
- // 予約語の置換
221
- if (t.type === 'word') {
222
- const rtype = reservedWords.get(t.value);
223
- if (rtype) {
224
- t.type = rtype;
225
- }
226
- if (t.value === 'そう') {
227
- t.value = 'それ';
228
- }
229
- }
230
- // 関数定義の確認
231
- if (t.type !== 'def_test' && t.type !== 'def_func') {
232
- i++;
233
- continue;
234
- }
235
- // 無名関数か普通関数定義かを判定する (1つ前が改行かどうかで判定)
236
- let isMumei = true;
237
- let prevToken = { type: 'eol' };
238
- if (i >= 1) {
239
- prevToken = tokens[i - 1];
240
- }
241
- if (prevToken.type === 'eol') {
242
- isMumei = false;
243
- }
244
- // 関数名や引数を得る
245
- const defToken = t;
246
- i++; // skip "●" or "関数"
247
- let josi = [];
248
- let varnames = [];
249
- let funcPointers = [];
250
- let funcName = '';
251
- let funcNameToken = null;
252
- let isExport = null;
253
- // 関数の属性指定
254
- if (tokens[i] && tokens[i].type === '{') {
255
- i++;
256
- const attr = tokens[i] && tokens[i].type === 'word' ? tokens[i].value : '';
257
- if (attr === '公開') {
258
- isExport = true;
259
- }
260
- else if (attr === '非公開') {
261
- isExport = false;
262
- }
263
- else if (attr === 'エクスポート') {
264
- isExport = true;
265
- }
266
- else {
267
- logger.warn(`不明な関数属性『${String(attr)}』が指定されています。`);
268
- }
269
- i++;
270
- if (tokens[i] && tokens[i].type === '}') {
271
- i++;
272
- }
273
- }
274
- // 関数名の前に引数定義
275
- if (tokens[i] && tokens[i].type === '(') {
276
- [josi, varnames, funcPointers] = readArgs();
277
- }
278
- // 関数名を得る
279
- if (!isMumei && tokens[i] && tokens[i].type === 'word') {
280
- funcNameToken = tokens[i++];
281
- funcName = funcNameToken.value;
282
- }
283
- // 関数名の後で引数定義
284
- if (josi.length === 0 && tokens[i] && tokens[i].type === '(') {
285
- [josi, varnames, funcPointers] = readArgs();
286
- }
287
- // 名前のある関数定義ならば関数テーブルに関数名を登録
288
- // 無名関数は登録しないように気をつける
289
- if (funcName !== '' && funcNameToken) {
290
- const modName = NakoLexer.filenameToModName(t.file);
291
- funcName = modName + '__' + funcName;
292
- if (funclist.has(funcName)) { // 関数の二重定義を警告
293
- // main__は省略 #1223
294
- const dispName = funcName.replace(/^main__/, '');
295
- logger.warn(`関数『${dispName}』は既に定義されています。`, defToken);
296
- }
297
- funcNameToken.value = funcName;
298
- funclist.set(funcName, {
299
- type: 'func',
300
- josi,
301
- fn: null,
302
- asyncFn: false,
303
- isExport,
304
- varnames,
305
- funcPointers
306
- });
307
- }
308
- // 無名関数のために
309
- const metaValue = {
310
- 'type': 'func',
311
- josi,
312
- varnames,
313
- funcPointers
314
- };
315
- defToken.meta = metaValue;
316
- }
317
- }
318
- /** 文字列を{と}の部分で分割する。中括弧が対応していない場合nullを返す。 */
319
- splitStringEx(code) {
320
- /** @type {string[]} */
321
- const list = [];
322
- // "A{B}C{D}E" -> ["A", "B}C", "D}E"] -> ["A", "B", "C", "D", "E"]
323
- // "A{B}C}D{E}F" -> ["A", "B}C}D", "E}F"] -> ["A", "B", "C}D", "E", "F"]
324
- const arr = code.split(/[{{]/);
325
- list.push(arr[0]);
326
- for (const s of arr.slice(1)) {
327
- const end = s.replace('}', '}').indexOf('}');
328
- if (end === -1) {
329
- return null;
330
- }
331
- list.push(s.slice(0, end), s.slice(end + 1));
332
- }
333
- return list;
334
- }
335
- _replaceWord(tokens) {
336
- let comment = [];
337
- let i = 0;
338
- let isFuncPointer = false;
339
- const namespaceStack = [];
340
- const getLastType = () => {
341
- if (i <= 0) {
342
- return 'eol';
343
- }
344
- return tokens[i - 1].type;
345
- };
346
- let modSelf = (tokens.length > 0) ? NakoLexer.filenameToModName(tokens[0].file) : 'main';
347
- while (i < tokens.length) {
348
- const t = tokens[i];
349
- // モジュール名の変更に対応
350
- if ((t.type === 'word' || t.type === 'func') && t.value === '名前空間設定') {
351
- if (isFuncPointer) {
352
- throw new InternalLexerError('名前空間設定の関数参照を取得することはできません。', t.startOffset === undefined ? 0 : t.startOffset, t.endOffset === undefined ? 0 : t.endOffset, t.line, t.file);
353
- }
354
- namespaceStack.push(modSelf);
355
- modSelf = tokens[i - 1].value;
356
- }
357
- if ((t.type === 'word' || t.type === 'func') && t.value === '名前空間ポップ') {
358
- if (isFuncPointer) {
359
- throw new InternalLexerError('名前空間ポップの関数参照を取得することはできません。', t.startOffset === undefined ? 0 : t.startOffset, t.endOffset === undefined ? 0 : t.endOffset, t.line, t.file);
360
- }
361
- const space = namespaceStack.pop();
362
- if (space) {
363
- modSelf = space;
364
- }
365
- }
366
- // 関数を強制的に置換( word => func )
367
- if (t.type === 'word' && t.value !== 'それ') {
368
- // 関数を変換
369
- const funcName = String(t.value);
370
- if (funcName.indexOf('__') < 0) {
371
- // 自身のモジュール名を検索
372
- const gname1 = `${modSelf}__${funcName}`;
373
- const gfo1 = this.funclist.get(gname1);
374
- if (gfo1 && gfo1.type === 'func') {
375
- const tt = t;
376
- tt.type = isFuncPointer ? 'func_pointer' : 'func';
377
- tt.meta = gfo1;
378
- tt.value = gname1;
379
- if (isFuncPointer) {
380
- isFuncPointer = false;
381
- tokens.splice(i - 1, 1);
382
- }
383
- continue;
384
- }
385
- // モジュール関数を置換
386
- for (const mod of this.modList) {
387
- const gname = `${mod}__${funcName}`;
388
- const gfo = this.funclist.get(gname);
389
- const exportDefault = this.moduleExport.get(mod);
390
- if (gfo && gfo.type === 'func' && (gfo.isExport === true || (gfo.isExport !== false && exportDefault !== false))) {
391
- const tt = t;
392
- tt.type = isFuncPointer ? 'func_pointer' : 'func';
393
- tt.meta = gfo;
394
- tt.value = gname;
395
- if (isFuncPointer) {
396
- isFuncPointer = false;
397
- tokens.splice(i - 1, 1);
398
- }
399
- break;
400
- }
401
- }
402
- }
403
- const fo = this.funclist.get(funcName);
404
- if (fo && fo.type === 'func') {
405
- const tt = t;
406
- tt.type = isFuncPointer ? 'func_pointer' : 'func';
407
- tt.meta = fo;
408
- if (isFuncPointer) {
409
- isFuncPointer = false;
410
- tokens.splice(i - 1, 1);
411
- continue;
412
- }
413
- }
414
- }
415
- // 関数ポインタの前置詞を検出
416
- if (isFuncPointer) {
417
- // 無効な関数参照の指定がある。
418
- }
419
- isFuncPointer = false;
420
- if (t.type === 'func' && t.value === '{関数}') {
421
- i++;
422
- isFuncPointer = true;
423
- continue;
424
- }
425
- // 数字につくマイナス記号を判定
426
- // (ng) 5 - 3 || word - 3
427
- // (ok) (行頭)-3 || 1 * -3 || Aに -3を 足す
428
- if (t.type === '-' && tokens[i + 1]) {
429
- const tokenType = tokens[i + 1].type;
430
- if (tokenType === 'number' || tokenType === 'bigint') {
431
- // 一つ前の語句が、(行頭|演算子|助詞付きの語句)なら 負数である
432
- const ltype = getLastType();
433
- if (ltype === 'eol' || opPriority[ltype] || tokens[i - 1].josi !== '') {
434
- tokens.splice(i, 1); // remove '-'
435
- if (tokenType === 'number') {
436
- tokens[i].value *= -1;
437
- }
438
- else {
439
- tokens[i].value = '-' + String(tokens[i].value);
440
- }
441
- }
442
- }
443
- }
444
- // 助詞の「は」を = に展開
445
- if (t.josi === undefined) {
446
- t.josi = '';
447
- }
448
- if (t.josi === 'は') {
449
- if (!t.rawJosi) {
450
- t.rawJosi = t.josi;
451
- }
452
- const startOffset = (t.endOffset === undefined) ? undefined : t.endOffset - t.rawJosi.length;
453
- tokens.splice(i + 1, 0, {
454
- type: 'eq',
455
- indent: t.indent,
456
- line: t.line,
457
- column: t.column,
458
- file: t.file,
459
- startOffset,
460
- endOffset: t.endOffset,
461
- josi: '',
462
- rawJosi: '',
463
- value: undefined
464
- });
465
- i += 2;
466
- t.josi = t.rawJosi = '';
467
- t.endOffset = startOffset;
468
- continue;
469
- }
470
- // 「とは」を一つの単語にする
471
- if (t.josi === 'とは') {
472
- if (!t.rawJosi) {
473
- t.rawJosi = t.josi;
474
- }
475
- const startOffset = t.endOffset === undefined ? undefined : t.endOffset - t.rawJosi.length;
476
- tokens.splice(i + 1, 0, {
477
- type: 'とは',
478
- indent: t.indent,
479
- line: t.line,
480
- column: t.column,
481
- file: t.file,
482
- startOffset,
483
- endOffset: t.endOffset,
484
- josi: '',
485
- rawJosi: '',
486
- value: undefined
487
- });
488
- t.josi = t.rawJosi = '';
489
- t.endOffset = startOffset;
490
- i += 2;
491
- continue;
492
- }
493
- // 助詞のならばをトークンとする
494
- if (tararebaMap[t.josi]) {
495
- const josi = (t.josi === 'でなければ' || t.josi === 'なければ') ? 'でなければ' : 'ならば';
496
- if (!t.rawJosi) {
497
- t.rawJosi = josi;
498
- }
499
- const startOffset = t.endOffset === undefined ? undefined : t.endOffset - t.rawJosi.length;
500
- tokens.splice(i + 1, 0, {
501
- type: 'ならば',
502
- value: josi,
503
- indent: t.indent,
504
- line: t.line,
505
- column: t.column,
506
- file: t.file,
507
- startOffset,
508
- endOffset: t.endOffset,
509
- josi: '',
510
- rawJosi: ''
511
- });
512
- t.josi = t.rawJosi = '';
513
- t.endOffset = startOffset;
514
- i += 2;
515
- continue;
516
- }
517
- // '_' + 改行 を飛ばす (演算子直後に改行を入れたい場合に使う)
518
- if (t.type === '_eol') {
519
- tokens.splice(i, 1);
520
- continue;
521
- }
522
- // コメントを飛ばす
523
- if (t.type === 'line_comment' || t.type === 'range_comment') {
524
- comment.push(t.value);
525
- tokens.splice(i, 1);
526
- continue;
527
- }
528
- // 改行にコメントを埋め込む
529
- if (t.type === 'eol') {
530
- t.value = comment.join('/');
531
- comment = [];
532
- }
533
- i++;
534
- }
535
- }
536
- /**
537
- * インデントの個数を数える
538
- * @returns 戻り値として[インデント数, 読み飛ばすべき文字数]を返す
539
- */
540
- countIndent(src) {
541
- let indent = 0;
542
- for (let i = 0; i < src.length; i++) {
543
- const c = src.charAt(i);
544
- const n = isIndentChars(c);
545
- if (n === 0) {
546
- return [indent, i];
547
- }
548
- indent += n;
549
- }
550
- return [indent, src.length];
551
- }
552
- /**
553
- * ソースコードをトークンに分割する
554
- * @param src なでしこのソースコード
555
- * @param line 先頭行の行番号
556
- * @param filename ファイル名
557
- */
558
- tokenize(src, line, filename) {
559
- const srcLength = src.length;
560
- const result = [];
561
- let columnCurrent;
562
- let lineCurrent;
563
- let column = 1;
564
- let isDefTest = false;
565
- let indent = 0;
566
- // 最初にインデントを数える
567
- const ia = this.countIndent(src);
568
- indent = ia[0]; // インデント数
569
- src = src.substring(ia[1]); // 読み飛ばす文字数
570
- column += ia[1];
571
- while (src !== '') {
572
- let ok = false;
573
- // 各ルールについて
574
- for (const rule of rules) {
575
- // 正規表現でマッチ
576
- const m = rule.pattern.exec(src);
577
- if (!m) {
578
- continue;
579
- }
580
- let ruleName = rule.name;
581
- ok = true;
582
- // 空白ならスキップ
583
- if (rule.name === 'space') {
584
- column += m[0].length;
585
- src = src.substring(m[0].length);
586
- continue;
587
- }
588
- // マッチしたルールがコールバックを持つなら
589
- if (rule.cbParser) {
590
- // コールバックを呼ぶ
591
- let rp;
592
- if (isDefTest && rule.name === 'word') {
593
- rp = rule.cbParser(src, false);
594
- }
595
- else {
596
- try {
597
- rp = rule.cbParser(src);
598
- }
599
- catch (e) {
600
- throw new NakoLexerError(e.message, srcLength - src.length, srcLength - src.length + 1, line, filename);
601
- }
602
- }
603
- // ルール取得後の特別な処理
604
- if (rule.name === 'string_ex') {
605
- // 展開あり文字列(埋め込み式) → aaa{x}bbb{x}cccc
606
- const list = this.splitStringEx(rp.res);
607
- if (list === null) {
608
- throw new InternalLexerError('展開あり文字列で値の埋め込み{...}が対応していません。', srcLength - src.length, srcLength - rp.src.length, line, filename);
609
- }
610
- if (list.length === 1) {
611
- // 展開なし(埋め込み式なし)の場合
612
- result.push({ type: 'string', value: list[0], josi: rp.josi, indent, file: filename, line, column, preprocessedCodeOffset: srcLength - src.length, preprocessedCodeLength: src.length - rp.src.length });
613
- line += rp.numEOL;
614
- column += src.length - rp.src.length;
615
- src = rp.src;
616
- if (rp.numEOL > 0) {
617
- column = 1;
618
- }
619
- break;
620
- }
621
- // 展開あり(埋め込み式あり)の場合
622
- result.push({ type: '(', value: '(', josi: '', indent, file: filename, line, column, preprocessedCodeOffset: srcLength - src.length, preprocessedCodeLength: 0 });
623
- let offset = 0;
624
- for (let i = 0; i < list.length; i++) {
625
- if (i % 2 === 0) {
626
- result.push({
627
- type: 'string',
628
- value: list[i],
629
- file: filename,
630
- josi: '',
631
- indent,
632
- line,
633
- column,
634
- preprocessedCodeOffset: srcLength - src.length + offset,
635
- preprocessedCodeLength: list[i].length + 2
636
- });
637
- // 先頭なら'"...{'、それ以外なら'}...{'、最後は何でも良い
638
- offset += list[i].length + 2;
639
- }
640
- else {
641
- result.push({ type: '&', value: '&', josi: '', indent, file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset, preprocessedCodeLength: 0 });
642
- result.push({ type: '(', value: '(', josi: '', indent, file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset, preprocessedCodeLength: 0 });
643
- result.push({ type: 'code', value: list[i], josi: '', indent, file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset, preprocessedCodeLength: list[i].length });
644
- result.push({ type: ')', value: ')', josi: '', indent, file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset + list[i].length, preprocessedCodeLength: 0 });
645
- result.push({ type: '&', value: '&', josi: '', indent, file: filename, line, column, preprocessedCodeOffset: srcLength - src.length + offset + list[i].length, preprocessedCodeLength: 0 });
646
- offset += list[i].length;
647
- }
648
- }
649
- line += rp.numEOL;
650
- column += src.length - rp.src.length;
651
- src = rp.src;
652
- if (rp.numEOL > 0) {
653
- column = 1;
654
- }
655
- result.push({ type: ')', value: ')', josi: rp.josi, indent, file: filename, line, column, preprocessedCodeOffset: srcLength - src.length, preprocessedCodeLength: 0 });
656
- break;
657
- }
658
- // 結果を格納
659
- columnCurrent = column;
660
- column += src.length - rp.src.length;
661
- result.push({ type: rule.name, value: rp.res, josi: rp.josi, indent, line, column: columnCurrent, file: filename, preprocessedCodeOffset: srcLength - src.length, preprocessedCodeLength: src.length - rp.src.length });
662
- src = rp.src;
663
- line += rp.numEOL;
664
- if (rp.numEOL > 0) {
665
- column = 1;
666
- }
667
- break;
668
- }
669
- // ソースを進める前に位置を計算
670
- const srcOffset = srcLength - src.length;
671
- // 値を変換する必要があるか?
672
- let value = m[0];
673
- if (rule.cb) {
674
- value = rule.cb(value);
675
- }
676
- // ソースを進める
677
- columnCurrent = column;
678
- lineCurrent = line;
679
- column += m[0].length;
680
- src = src.substring(m[0].length);
681
- // 改行の時の処理
682
- if ((rule.name === 'eol' && value === '\n') || rule.name === '_eol') {
683
- value = line++;
684
- column = 1;
685
- }
686
- // 数値なら単位を持つか? --- #994
687
- if (rule.name === 'number') {
688
- // 単位があれば読み飛ばす
689
- const um = unitRE.exec(src);
690
- if (um) {
691
- src = src.substring(um[0].length);
692
- column += m[0].length;
693
- }
694
- // CSSの単位なら自動的に文字列として認識させる #1811
695
- const cssUnit = cssUnitRE.exec(src);
696
- if (cssUnit) {
697
- ruleName = 'string';
698
- src = src.substring(cssUnit[0].length);
699
- column += m[0].length;
700
- value += cssUnit[0];
701
- }
702
- }
703
- let josi = '';
704
- if (rule.readJosi) {
705
- // 正規表現で助詞があるか読み取る
706
- const j = josiRE.exec(src);
707
- if (j) {
708
- column += j[0].length;
709
- josi = j[0].replace(/^\s+/, '');
710
- src = src.substring(j[0].length);
711
- // 助詞の直後にあるカンマを無視 #877
712
- if (src.charAt(0) === ',') {
713
- src = src.substring(1);
714
- }
715
- // 「**である」なら削除 #939 #974
716
- if (removeJosiMap[josi]) {
717
- josi = '';
718
- }
719
- // 「もの」構文 (#1614)
720
- if (josi.substring(0, 2) === 'もの') {
721
- josi = josi.substring(2);
722
- }
723
- }
724
- }
725
- switch (ruleName) {
726
- case 'def_test': {
727
- isDefTest = true;
728
- break;
729
- }
730
- case 'eol': { // eolの処理はほかに↑と↓にある
731
- isDefTest = false;
732
- break;
733
- }
734
- default: {
735
- break;
736
- }
737
- }
738
- // ここまで‰(#682) を処理
739
- if (ruleName === 'dec_lineno') {
740
- line--;
741
- continue;
742
- }
743
- result.push({
744
- type: ruleName,
745
- value,
746
- indent,
747
- line: lineCurrent,
748
- column: columnCurrent,
749
- file: filename,
750
- josi,
751
- preprocessedCodeOffset: srcOffset,
752
- preprocessedCodeLength: (srcLength - src.length) - srcOffset
753
- });
754
- // 改行のとき次の行のインデントを調べる。なお、改行の後は必ずcolumnが1になる。インデント構文のため、一行に2つ以上の文を含むときを考慮する。(core #66)
755
- if (ruleName === 'eol' && column === 1) {
756
- const ia = this.countIndent(src);
757
- indent = ia[0];
758
- column += ia[1];
759
- src = src.substring(ia[1]); // インデントを飛ばす
760
- }
761
- break;
762
- }
763
- if (!ok) {
764
- throw new InternalLexerError('未知の語句: ' + src.substring(0, 3) + '...', srcLength - src.length, srcLength - srcLength + 3, line, filename);
765
- }
766
- }
767
- return result;
768
- }
769
- // トークン配列をtype文字列に変換
770
- static tokensToTypeStr(tokens, sep) {
771
- const a = tokens.map((v) => {
772
- return v.type;
773
- });
774
- return a.join(sep);
775
- }
776
- /**
777
- * ファイル名からモジュール名へ変換
778
- * @param {string} filename
779
- * @returns {string}
780
- */
781
- static filenameToModName(filename) {
782
- if (!filename) {
783
- return 'main';
784
- }
785
- // パスがあればパスを削除
786
- filename = filename.replace(/[\\:]/g, '/'); // Windowsのpath記号を/に置換
787
- if (filename.indexOf('/') >= 0) {
788
- const a = filename.split('/');
789
- filename = a[a.length - 1];
790
- }
791
- filename = filename.replace(/\.nako3?$/, '');
792
- return filename;
793
- }
794
- }