nadesiko3 3.3.48 → 3.3.49

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