nadesiko3 3.3.3 → 3.3.6

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 (64) hide show
  1. package/batch/browsers.template.md +3 -0
  2. package/batch/build_browsers.nako3 +72 -0
  3. package/batch/build_command.nako3 +44 -0
  4. package/batch/build_nako_version.nako3 +14 -0
  5. package/batch/calc_hash.nako3 +29 -0
  6. package/batch/cmd_txt2json.nako3 +72 -0
  7. package/batch/command.txt +1047 -0
  8. package/batch/command_nakopad.txt +997 -0
  9. package/batch/download-extlib.nako3 +43 -0
  10. package/batch/gen_command_nakopad.nako3 +57 -0
  11. package/batch/jsplugin2text.nako3 +269 -0
  12. package/batch/pickup_command.nako3 +84 -0
  13. package/batch/pickup_reserved_words.nako3 +11 -0
  14. package/batch/publish_version.nako3 +46 -0
  15. package/batch/show_agents.js +14 -0
  16. package/batch/turtle2js.nako3 +21 -0
  17. package/demo/browsers.html +1 -1
  18. package/demo/nako3/taberu.nako3 +5 -0
  19. package/doc/browsers.md +1 -1
  20. package/package.json +8 -6
  21. package/release/_hash.txt +24 -24
  22. package/release/_script-tags.txt +14 -14
  23. package/release/command.json +1 -1
  24. package/release/command.json.js +1 -1
  25. package/release/command_cnako3.json +1 -1
  26. package/release/command_list.json +1 -1
  27. package/release/editor.js +1 -1
  28. package/release/nako_gen_async.js +1 -1
  29. package/release/nako_gen_async.js.LICENSE.txt +20 -0
  30. package/release/plugin_caniuse.js +1 -1
  31. package/release/stats.json +1 -1
  32. package/release/version.js +1 -1
  33. package/release/wnako3.js +1 -1
  34. package/src/browsers.mjs +1 -1
  35. package/src/browsers.txt +0 -1
  36. package/src/cnako3mod.mjs +88 -64
  37. package/src/nako3.mjs +72 -53
  38. package/src/nako_gen.mjs +6 -12
  39. package/src/nako_global.mjs +2 -2
  40. package/src/nako_lex_rules.mjs +1 -1
  41. package/src/nako_lexer.mjs +83 -22
  42. package/src/nako_parser3.mjs +137 -28
  43. package/src/nako_parser_base.mjs +75 -6
  44. package/src/nako_version.mjs +2 -2
  45. package/src/plugin_system.mjs +27 -9
  46. package/test/common/nako_lexer_test.mjs +33 -0
  47. package/test/common/plugin_system_test.mjs +1 -1
  48. package/test/node/async_test.mjs +1 -2
  49. package/test/node/commander_ja_test.mjs +1 -1
  50. package/test/node/error_message_test.mjs +8 -6
  51. package/test/node/node_test.mjs +2 -2
  52. package/test/node/{plugin_browser_ut_ajax_test.mjs → plugin_browser_ut_ajax_test.mjs.todo} +2 -2
  53. package/test/node/plugin_browser_ut_location_test.mjs +1 -1
  54. package/test/node/plugin_markup_test.mjs +3 -3
  55. package/test/node/plugin_math_test.mjs +2 -2
  56. package/test/node/plugin_node_test.mjs +3 -3
  57. package/test/node/plugin_test.mjs +22 -4
  58. package/test/node/require_nako3_test.mjs +2 -2
  59. package/test/node/scope1.nako3 +10 -0
  60. package/test/node/scope2.nako3 +12 -0
  61. package/test/node/side_effects_test.mjs +4 -4
  62. package/test/node/wnako3_editor_test.mjs +17 -5
  63. package/tools/nako3edit/html/edit.html +1 -1
  64. package/tools/nako3edit/index.nako3 +3 -0
@@ -3,7 +3,8 @@
3
3
  import { opPriority } from './nako_parser_const.mjs'
4
4
 
5
5
  // 予約語句
6
- // (memo)「回」「間」「繰返」「反復」「抜」「続」「戻」「代入」などは replaceWord で word から変換
6
+ // (memo)「回」「間」「繰返」「反復」「抜」「続」「戻」「代入」などは _replaceWord で word から変換
7
+ /** @types {Record<string, string>} */
7
8
  import reservedWords from './nako_reserved_words.mjs'
8
9
 
9
10
  // 助詞の一覧
@@ -15,7 +16,7 @@ import { rules, unitRE } from './nako_lex_rules.mjs'
15
16
  import { NakoLexerError, InternalLexerError } from './nako_errors.mjs'
16
17
 
17
18
  /**
18
- * @typedef {import('./nako3').TokenWithSourceMap} TokenWithSourceMap
19
+ * @typedef {import('./nako3.mjs').TokenWithSourceMap} TokenWithSourceMap
19
20
  * @typedef {{
20
21
  * type: string;
21
22
  * value: unknown;
@@ -40,37 +41,43 @@ import { NakoLexerError, InternalLexerError } from './nako_errors.mjs'
40
41
  * value: unknown
41
42
  * }
42
43
  * )>} FuncList
43
- */
44
+ */
44
45
 
45
46
  export class NakoLexer {
46
47
  /**
47
- * @param {import("./nako_logger")} logger
48
+ * @param {import("./nako_logger.mjs").NakoLogger} logger
48
49
  */
49
50
  constructor (logger) {
51
+ /* @type {import("./nako_logger.mjs").NakoLogger} */
50
52
  this.logger = logger
51
- /** @type {FuncList} */
53
+ /** 字句解析した際,確認された関数の一覧 @type {FuncList} */
52
54
  this.funclist = {}
55
+ /** 字句解析した際,取り込むモジュール一覧
56
+ * nako3::lex で更新される
57
+ * @type {Array<string>}
58
+ */
59
+ this.modList = []
53
60
  /** @type {TokenWithSourceMap[]} */
54
61
  this.result = []
62
+ /** モジュール名 */
63
+ this.modName = 'inline'
55
64
  }
56
65
 
57
66
  setFuncList (listObj) {
58
67
  this.funclist = listObj
59
68
  }
60
69
 
61
- setInput (code, line, filename) {
62
- // 最初に全部を区切ってしまう
63
- return this.tokenize(code, line, filename)
64
- }
65
-
66
70
  /**
67
71
  * @param {TokenWithSourceMap[]} tokens
72
+ * @param {boolean} isFirst
73
+ * @param {string} filename
68
74
  */
69
- setInput2 (tokens, isFirst) {
75
+ replaceTokens (tokens, isFirst, filename) {
70
76
  this.result = tokens
77
+ this.modName = NakoLexer.filenameToModName(filename)
71
78
  // 関数の定義があれば funclist を更新
72
79
  NakoLexer.preDefineFunc(tokens, this.logger, this.funclist)
73
- this.replaceWord(this.result)
80
+ this._replaceWord(this.result)
74
81
 
75
82
  if (isFirst) {
76
83
  if (this.result.length > 0) {
@@ -129,7 +136,7 @@ export class NakoLexer {
129
136
  * ファイル内で定義されている関数名を列挙する。結果はfunclistに書き込む。その他のトークンの置換処理も行う。
130
137
  * シンタックスハイライトの処理から呼び出すためにstaticメソッドにしている。
131
138
  * @param {TokenWithSourceMap[]} tokens
132
- * @param {import('./nako_logger')} logger
139
+ * @param {import('./nako_logger.mjs').NakoLogger} logger
133
140
  * @param {FuncList} funclist
134
141
  */
135
142
  static preDefineFunc (tokens, logger, funclist) {
@@ -144,7 +151,9 @@ export class NakoLexer {
144
151
  while (tokens[i]) {
145
152
  const t = tokens[i]
146
153
  i++
147
- if (t.type === ')') { break } else if (t.type === 'func') { isFuncPointer = true } else if (t.type !== '|' && t.type !== 'comma') {
154
+ if (t.type === ')') { break }
155
+ if (t.type === 'func') { isFuncPointer = true }
156
+ else if (t.type !== '|' && t.type !== 'comma') {
148
157
  if (isFuncPointer) {
149
158
  t.funcPointer = true
150
159
  isFuncPointer = false
@@ -186,7 +195,7 @@ export class NakoLexer {
186
195
  // N回をN|回に置換
187
196
  if (t.type === 'word' && t.josi === '' && t.value.length >= 2) {
188
197
  if (t.value.match(/回$/)) {
189
- t.value = t.value.substr(0, t.value.length - 1)
198
+ t.value = t.value.substring(0, t.value.length - 1)
190
199
  tokens.splice(i + 1, 0, { type: '回', value: '回', line: t.line, column: t.column, file: t.file, josi: '', startOffset: t.endOffset - 1, endOffset: t.endOffset, rawJosi: '' })
191
200
  t.endOffset--
192
201
  i++
@@ -214,12 +223,14 @@ export class NakoLexer {
214
223
  let varnames = []
215
224
  let funcPointers = []
216
225
  let funcName = ''
226
+ let funcNameToken
217
227
  // 関数名の前に引数定義
218
228
  if (tokens[i] && tokens[i].type === '(') { [josi, varnames, funcPointers] = readArgs() }
219
229
 
220
230
  // 関数名を得る
221
231
  if (!isMumei && tokens[i] && tokens[i].type === 'word') {
222
- funcName = tokens[i++].value
232
+ funcNameToken = tokens[i++]
233
+ funcName = funcNameToken.value
223
234
  }
224
235
 
225
236
  // 関数名の後で引数定義
@@ -228,9 +239,12 @@ export class NakoLexer {
228
239
  // 名前のある関数定義ならば関数テーブルに関数名を登録
229
240
  // 無名関数は登録しないように気をつける
230
241
  if (funcName !== '') {
242
+ const modName = NakoLexer.filenameToModName(t.file)
243
+ funcName = modName + '__' + funcName
231
244
  if (funcName in funclist) { // 関数の二重定義を警告
232
245
  logger.warn(`関数『${funcName}』は既に定義されています。`, defToken)
233
246
  }
247
+ funcNameToken.value = funcName
234
248
  funclist[funcName] = {
235
249
  type: 'func',
236
250
  josi,
@@ -240,7 +254,6 @@ export class NakoLexer {
240
254
  funcPointers
241
255
  }
242
256
  }
243
-
244
257
  // 無名関数のために
245
258
  defToken.meta = { josi, varnames, funcPointers }
246
259
  }
@@ -273,22 +286,47 @@ export class NakoLexer {
273
286
  /**
274
287
  * @param {TokenWithSourceMap[]} tokens
275
288
  */
276
- replaceWord (tokens) {
289
+ _replaceWord (tokens) {
277
290
  let comment = []
278
291
  let i = 0
279
292
  const getLastType = () => {
280
293
  if (i <= 0) { return 'eol' }
281
294
  return tokens[i - 1].type
282
295
  }
296
+ const modSelf = (tokens.length > 0) ? NakoLexer.filenameToModName(tokens[0].file) : 'inline'
283
297
  while (i < tokens.length) {
284
298
  const t = tokens[i]
299
+ // 関数を強制的に置換( word => func )
285
300
  if (t.type === 'word' && t.value !== 'それ') {
286
301
  // 関数を変換
287
- const fo = this.funclist[t.value]
302
+ const funcName = t.value
303
+ if (funcName.indexOf('__') < 0) {
304
+ // 自身のモジュール名を検索
305
+ const gname1 = `${modSelf}__${funcName}`
306
+ const gfo1 = this.funclist[gname1]
307
+ if (gfo1 && gfo1.type === 'func') {
308
+ t.type = 'func'
309
+ t.meta = gfo1
310
+ t.value = gname1
311
+ continue
312
+ }
313
+ // モジュール関数を置換
314
+ for (let mod of this.modList) {
315
+ const gname = `${mod}__${funcName}`
316
+ const gfo = this.funclist[gname]
317
+ if (gfo && gfo.type === 'func') {
318
+ t.type = 'func'
319
+ t.meta = gfo
320
+ t.value = gname
321
+ break
322
+ }
323
+ }
324
+ if (t.type === 'func') { continue }
325
+ }
326
+ const fo = this.funclist[funcName]
288
327
  if (fo && fo.type === 'func') {
289
328
  t.type = 'func'
290
329
  t.meta = fo
291
- continue
292
330
  }
293
331
  }
294
332
  // 数字につくマイナス記号を判定
@@ -406,7 +444,7 @@ export class NakoLexer {
406
444
  // 空白ならスキップ
407
445
  if (rule.name === 'space') {
408
446
  column += m[0].length
409
- src = src.substr(m[0].length)
447
+ src = src.substring(m[0].length)
410
448
  continue
411
449
  }
412
450
  // マッチしたルールがコールバックを持つなら
@@ -562,7 +600,7 @@ export class NakoLexer {
562
600
  break
563
601
  }
564
602
  if (!ok) {
565
- throw new InternalLexerError('未知の語句: ' + src.substr(0, 3) + '...',
603
+ throw new InternalLexerError('未知の語句: ' + src.substring(0, 3) + '...',
566
604
  srcLength - src.length,
567
605
  srcLength - srcLength + 3,
568
606
  line
@@ -571,4 +609,27 @@ export class NakoLexer {
571
609
  }
572
610
  return result
573
611
  }
612
+ // トークン配列をtype文字列に変換
613
+ static tokensToTypeStr(tokens, sep) {
614
+ const a = tokens.map((v) => {
615
+ return v.type
616
+ })
617
+ return a.join(sep)
618
+ }
619
+ /**
620
+ * ファイル名からモジュール名へ変換
621
+ * @param {string} filename
622
+ * @returns {string}
623
+ */
624
+ static filenameToModName(filename) {
625
+ if (!filename) { return 'inline' }
626
+ // パスがあればパスを削除
627
+ filename = filename.replace(/[\\:]/g, '/') // Windowsのpath記号を/に置換
628
+ if (filename.indexOf('/') >= 0) {
629
+ const a = filename.split('/')
630
+ filename = a[a.length - 1]
631
+ }
632
+ filename = filename.replace(/\.nako3?$/, '')
633
+ return filename
634
+ }
574
635
  }
@@ -4,6 +4,7 @@
4
4
  import { opPriority, keizokuJosi } from './nako_parser_const.mjs'
5
5
  import { NakoParserBase } from './nako_parser_base.mjs'
6
6
  import { NakoSyntaxError } from './nako_errors.mjs'
7
+ import { NakoLexer } from './nako_lexer.mjs'
7
8
 
8
9
  /**
9
10
  * @type {string[]}
@@ -12,20 +13,24 @@ const operatorList = []
12
13
  for (const key in opPriority) { operatorList.push(key) }
13
14
 
14
15
  /**
15
- * @typedef {import('./nako3').TokenWithSourceMap} TokenWithSourceMap
16
- * @typedef {import('./nako3').Ast} Ast
16
+ * 構文解析を行うクラス
17
+ * @typedef {import('./nako3.mjs').TokenWithSourceMap} TokenWithSourceMap
18
+ * @typedef {import('./nako3.mjs').Ast} Ast
17
19
  */
18
-
19
20
  // @ts-ignore
20
21
  export class NakoParser extends NakoParserBase {
21
22
  /**
23
+ * 構文解析を実行する
22
24
  * @param {TokenWithSourceMap[]} tokens 字句解析済みのトークンの配列
25
+ * @param {string} filename 解析対象のモジュール名
23
26
  * @return {Ast} AST(構文木)
24
27
  */
25
- parse (tokens) {
28
+ parse (tokens, filename) {
26
29
  this.reset()
27
30
  /** @type {TokenWithSourceMap[]} */
28
31
  this.tokens = tokens
32
+ this.modName = NakoLexer.filenameToModName(filename)
33
+ this.modList.push(this.modName)
29
34
  // 解析開始
30
35
  return this.startParser()
31
36
  }
@@ -107,7 +112,7 @@ export class NakoParser extends NakoParserBase {
107
112
  if (this.check('逐次実行')) { return this.yTikuji() }
108
113
  if (this.accept(['抜ける'])) { return { type: 'break', josi: '', ...map, end: this.peekSourceMap() } }
109
114
  if (this.accept(['続ける'])) { return { type: 'continue', josi: '', ...map, end: this.peekSourceMap() } }
110
- if (this.accept(['require', 'string', '取込'])) { return { type: 'require', value: this.y[1].value, josi: '', ...map, end: this.peekSourceMap() } }
115
+ if (this.accept(['require', 'string', '取込'])) { return this.yRequire() }
111
116
  if (this.accept(['not', '非同期モード'])) { return this.yASyncMode() }
112
117
  if (this.accept(['not', 'DNCLモード'])) { return this.yDNCLMode() }
113
118
  if (this.accept(['not', 'string', 'モード設定'])) { return this.ySetGenMode(this.y[1].value) }
@@ -172,6 +177,27 @@ export class NakoParser extends NakoParserBase {
172
177
  return { type: 'eol', ...map, end: this.peekSourceMap() }
173
178
  }
174
179
 
180
+ /** @returns {Ast} */
181
+ yRequire () {
182
+ const nameToken = this.y[1]
183
+ const filename = nameToken.value
184
+ const modName = NakoLexer.filenameToModName(filename)
185
+ if (this.modList.indexOf(modName) < 0) {
186
+ // 優先度が最も高いのは modList[0]
187
+ // [memo] モジュールの検索優先度は、下に書くほど高くなる
188
+ const modSelf = this.modList.shift()
189
+ this.modList.unshift(modName)
190
+ this.modList.unshift(modSelf)
191
+ }
192
+ return {
193
+ type: 'require',
194
+ value: filename,
195
+ josi: '',
196
+ ...this.peekSourceMap(),
197
+ end: this.peekSourceMap()
198
+ }
199
+ }
200
+
175
201
  /** @returns {Ast} */
176
202
  yBlock () {
177
203
  const map = this.peekSourceMap()
@@ -243,9 +269,12 @@ export class NakoParser extends NakoParserBase {
243
269
  if (this.check('とは')) { this.get() }
244
270
  let block = null
245
271
  let multiline = false
272
+ let funcFn = false
246
273
  if (this.check('ここから')) { multiline = true }
247
274
  if (this.check('eol')) { multiline = true }
248
275
  try {
276
+ this.funcLevel++
277
+ this.usedFuncFn = false
249
278
  if (multiline) {
250
279
  this.saveStack()
251
280
  block = this.yBlock()
@@ -256,6 +285,8 @@ export class NakoParser extends NakoParserBase {
256
285
  block = this.ySentence()
257
286
  this.loadStack()
258
287
  }
288
+ this.funcLevel--
289
+ funcFn = this.usedFuncFn
259
290
  } catch (err) {
260
291
  this.logger.debug(this.nodeToStr(funcName, { depth: 0, typeName: '関数' }, true) +
261
292
  'の定義で以下のエラーがありました。\n' + err.message, def)
@@ -268,6 +299,7 @@ export class NakoParser extends NakoParserBase {
268
299
  name: funcName,
269
300
  args: defArgs,
270
301
  block,
302
+ funcFn,
271
303
  josi: '',
272
304
  ...map,
273
305
  end: this.peekSourceMap()
@@ -975,6 +1007,7 @@ export class NakoParser extends NakoParserBase {
975
1007
  // 「,」を飛ばす
976
1008
  if (this.check('comma')) { this.get() }
977
1009
  // ブロックを読む
1010
+ this.funcLevel++
978
1011
  this.saveStack()
979
1012
  const block = this.yBlock()
980
1013
  // 末尾の「ここまで」をチェック - もしなければエラーにする #1045
@@ -983,6 +1016,7 @@ export class NakoParser extends NakoParserBase {
983
1016
  }
984
1017
  this.get() // skip ここまで
985
1018
  this.loadStack()
1019
+ this.funcLevel--
986
1020
  return {
987
1021
  type: 'func_obj',
988
1022
  args,
@@ -1031,7 +1065,6 @@ export class NakoParser extends NakoParserBase {
1031
1065
  if (sadameru === null) { return null }
1032
1066
  const word = this.popStack(['を'])
1033
1067
  const value = this.popStack(['へ', 'に'])
1034
- console.log(word)
1035
1068
  if (!word || (word.type !== 'word' && word.type !== 'func' && word.type !== '配列参照')) {
1036
1069
  throw NakoSyntaxError.fromNode('『定める』文で定数が見当たりません。『(定数名)を(値)に定める』のように使います。', sadameru)
1037
1070
  }
@@ -1174,6 +1207,9 @@ export class NakoParser extends NakoParserBase {
1174
1207
  // 最近使った関数を記録
1175
1208
  this.recentlyCalledFunc.push({name: t.value, ...f})
1176
1209
 
1210
+ // 呼び出す関数が非同期呼び出しが必要(asyncFn)ならマーク
1211
+ if (f && f.asyncFn) { this.usedFuncFn = true }
1212
+
1177
1213
  // 関数の引数を取り出す処理
1178
1214
  const args = []
1179
1215
  let nullCount = 0
@@ -1211,9 +1247,17 @@ export class NakoParser extends NakoParserBase {
1211
1247
  if (nullCount >= 2 && (valueCount > 0 || t.josi === '' || keizokuJosi.indexOf(t.josi) >= 0)) { throw NakoSyntaxError.fromNode(`関数『${t.value}』の引数が不足しています。`, t) }
1212
1248
 
1213
1249
  const funcNode = { type: 'func', name: t.value, args: args, josi: t.josi, ...map, end: this.peekSourceMap() }
1250
+ // 「プラグイン名設定」か
1251
+ if (funcNode.name === 'プラグイン名設定') {
1252
+ let fname = args[0].value
1253
+ if (fname === 'メイン') { fname = args[0].file }
1254
+ this.modName = NakoLexer.filenameToModName(fname)
1255
+ }
1256
+
1214
1257
  // 言い切りならそこで一度切る
1215
1258
  if (t.josi === '') { return funcNode }
1216
1259
 
1260
+
1217
1261
  // **して、** の場合も一度切る
1218
1262
  if (keizokuJosi.indexOf(t.josi) >= 0) {
1219
1263
  funcNode.josi = 'して'
@@ -1307,10 +1351,12 @@ export class NakoParser extends NakoParserBase {
1307
1351
  throw new Error('値が空です。')
1308
1352
  }
1309
1353
  if (this.check('comma')) { this.get() } // skip comma (ex) name1=val1, name2=val2
1354
+ const nameToken = this.getVarName(this.y[0])
1355
+ const valueToken = this.y[2]
1310
1356
  return {
1311
1357
  type: 'let',
1312
- name: this.y[0],
1313
- value: this.y[2],
1358
+ name: nameToken,
1359
+ value: valueToken,
1314
1360
  ...map,
1315
1361
  end: this.peekSourceMap()
1316
1362
  }
@@ -1348,7 +1394,7 @@ export class NakoParser extends NakoParserBase {
1348
1394
 
1349
1395
  // ローカル変数定義
1350
1396
  if (this.accept(['word', 'とは'])) {
1351
- const word = this.y[0]
1397
+ let word = this.getVarName(this.y[0])
1352
1398
  if (!this.checkTypes(['変数', '定数'])) {
1353
1399
  throw NakoSyntaxError.fromNode('ローカル変数『' + word.value + '』の定義エラー', word)
1354
1400
  }
@@ -1372,9 +1418,10 @@ export class NakoParser extends NakoParserBase {
1372
1418
  }
1373
1419
  // ローカル変数定義(その2)
1374
1420
  if (this.accept(['変数', 'word', 'eq', this.yCalc])) {
1421
+ let word = this.getVarName(this.y[1])
1375
1422
  return {
1376
1423
  type: 'def_local_var',
1377
- name: this.y[1],
1424
+ name: word,
1378
1425
  vartype: '変数',
1379
1426
  value: this.y[3],
1380
1427
  ...map,
@@ -1383,9 +1430,10 @@ export class NakoParser extends NakoParserBase {
1383
1430
  }
1384
1431
 
1385
1432
  if (this.accept(['定数', 'word', 'eq', this.yCalc])) {
1433
+ let word = this.getVarName(this.y[1])
1386
1434
  return {
1387
1435
  type: 'def_local_var',
1388
- name: this.y[1],
1436
+ name: word,
1389
1437
  vartype: '定数',
1390
1438
  value: this.y[3],
1391
1439
  ...map,
@@ -1406,6 +1454,7 @@ export class NakoParser extends NakoParserBase {
1406
1454
  } else {
1407
1455
  throw NakoSyntaxError.fromNode('複数定数の代入文でエラー。『定数[A,B,C]=[1,2,3]』の書式で記述してください。', this.y[0])
1408
1456
  }
1457
+ names.value = this.getVarNameList(names.value)
1409
1458
  return {
1410
1459
  type: 'def_local_varlist',
1411
1460
  names: names.value,
@@ -1428,6 +1477,7 @@ export class NakoParser extends NakoParserBase {
1428
1477
  } else {
1429
1478
  throw NakoSyntaxError.fromNode('複数変数の代入文でエラー。『変数[A,B,C]=[1,2,3]』の書式で記述してください。', this.y[0])
1430
1479
  }
1480
+ names.value = this.getVarNameList(names.value)
1431
1481
  return {
1432
1482
  type: 'def_local_varlist',
1433
1483
  names: names.value,
@@ -1442,9 +1492,11 @@ export class NakoParser extends NakoParserBase {
1442
1492
  if (this.check2(['word', 'comma', 'word'])) {
1443
1493
  // 2 word
1444
1494
  if (this.accept(['word', 'comma', 'word', 'eq', this.yCalc])) {
1495
+ let names = [this.y[0], this.y[2]]
1496
+ names = this.getVarNameList(names)
1445
1497
  return {
1446
1498
  type: 'def_local_varlist',
1447
- names: [this.y[0], this.y[2]],
1499
+ names,
1448
1500
  vartype: '変数',
1449
1501
  value: this.y[4],
1450
1502
  ...map,
@@ -1453,9 +1505,11 @@ export class NakoParser extends NakoParserBase {
1453
1505
  }
1454
1506
  // 3 word
1455
1507
  if (this.accept(['word', 'comma', 'word', 'comma', 'word', 'eq', this.yCalc])) {
1508
+ let names = [this.y[0], this.y[2], this.y[4]]
1509
+ names = this.getVarNameList(names)
1456
1510
  return {
1457
1511
  type: 'def_local_varlist',
1458
- names: [this.y[0], this.y[2], this.y[4]],
1512
+ names,
1459
1513
  vartype: '変数',
1460
1514
  value: this.y[6],
1461
1515
  ...map,
@@ -1464,9 +1518,11 @@ export class NakoParser extends NakoParserBase {
1464
1518
  }
1465
1519
  // 4 word
1466
1520
  if (this.accept(['word', 'comma', 'word', 'comma', 'word', 'comma', 'word', 'eq', this.yCalc])) {
1521
+ let names = [this.y[0], this.y[2], this.y[4], this.y[6]]
1522
+ names = this.getVarNameList(names)
1467
1523
  return {
1468
1524
  type: 'def_local_varlist',
1469
- names: [this.y[0], this.y[2], this.y[4], this.y[6]],
1525
+ names,
1470
1526
  vartype: '変数',
1471
1527
  value: this.y[8],
1472
1528
  ...map,
@@ -1475,9 +1531,11 @@ export class NakoParser extends NakoParserBase {
1475
1531
  }
1476
1532
  // 5 word
1477
1533
  if (this.accept(['word', 'comma', 'word', 'comma', 'word', 'comma', 'word', 'comma', 'word', 'eq', this.yCalc])) {
1534
+ let names = [this.y[0], this.y[2], this.y[4], this.y[6], this.y[8]]
1535
+ names = this.getVarNameList(names)
1478
1536
  return {
1479
1537
  type: 'def_local_varlist',
1480
- names: [this.y[0], this.y[2], this.y[4], this.y[6], this.y[8]],
1538
+ names,
1481
1539
  vartype: '変数',
1482
1540
  value: this.y[10],
1483
1541
  ...map,
@@ -1528,7 +1586,7 @@ export class NakoParser extends NakoParserBase {
1528
1586
  if (this.accept(['word', '@', this.yValue, 'eq', this.yCalc])) {
1529
1587
  return {
1530
1588
  type: 'let_array',
1531
- name: this.y[0],
1589
+ name: this.getVarName(this.y[0]),
1532
1590
  index: [this.checkArrayIndex(this.y[2])],
1533
1591
  value: this.y[4],
1534
1592
  ...map,
@@ -1540,7 +1598,7 @@ export class NakoParser extends NakoParserBase {
1540
1598
  if (this.accept(['word', '@', this.yValue, '@', this.yValue, 'eq', this.yCalc])) {
1541
1599
  return {
1542
1600
  type: 'let_array',
1543
- name: this.y[0],
1601
+ name: this.getVarName(this.y[0]),
1544
1602
  index: this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4])]),
1545
1603
  value: this.y[6],
1546
1604
  ...map,
@@ -1552,7 +1610,7 @@ export class NakoParser extends NakoParserBase {
1552
1610
  if (this.accept(['word', '@', this.yValue, '@', this.yValue, '@', this.yValue, 'eq', this.yCalc])) {
1553
1611
  return {
1554
1612
  type: 'let_array',
1555
- name: this.y[0],
1613
+ name: this.getVarName(this.y[0]),
1556
1614
  index: this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4]), this.checkArrayIndex(this.y[6])]),
1557
1615
  value: this.y[8],
1558
1616
  ...map,
@@ -1564,7 +1622,7 @@ export class NakoParser extends NakoParserBase {
1564
1622
  if (this.accept(['word', '@', this.yValue, 'comma', this.yValue, 'eq', this.yCalc])) {
1565
1623
  return {
1566
1624
  type: 'let_array',
1567
- name: this.y[0],
1625
+ name: this.getVarName(this.y[0]),
1568
1626
  index: this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4])]),
1569
1627
  value: this.y[6],
1570
1628
  ...map,
@@ -1576,7 +1634,7 @@ export class NakoParser extends NakoParserBase {
1576
1634
  if (this.accept(['word', '@', this.yValue, 'comma', this.yValue, 'comma', this.yValue, 'eq', this.yCalc])) {
1577
1635
  return {
1578
1636
  type: 'let_array',
1579
- name: this.y[0],
1637
+ name: this.getVarName(this.y[0]),
1580
1638
  index: this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4]), this.checkArrayIndex(this.y[6])]),
1581
1639
  value: this.y[8],
1582
1640
  ...map,
@@ -1592,7 +1650,7 @@ export class NakoParser extends NakoParserBase {
1592
1650
  if (this.accept(['word', '[', this.yCalc, ']', 'eq', this.yCalc])) {
1593
1651
  return {
1594
1652
  type: 'let_array',
1595
- name: this.y[0],
1653
+ name: this.getVarName(this.y[0]),
1596
1654
  index: [this.checkArrayIndex(this.y[2])],
1597
1655
  value: this.y[5],
1598
1656
  ...map,
@@ -1604,7 +1662,7 @@ export class NakoParser extends NakoParserBase {
1604
1662
  if (this.accept(['word', '[', this.yCalc, ']', '[', this.yCalc, ']', 'eq', this.yCalc])) {
1605
1663
  return {
1606
1664
  type: 'let_array',
1607
- name: this.y[0],
1665
+ name: this.getVarName(this.y[0]),
1608
1666
  index: this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[5])]),
1609
1667
  value: this.y[8],
1610
1668
  tag: '2',
@@ -1615,7 +1673,7 @@ export class NakoParser extends NakoParserBase {
1615
1673
  if (this.accept(['word', '[', this.yCalc, 'comma', this.yCalc, ']', 'eq', this.yCalc])) {
1616
1674
  return {
1617
1675
  type: 'let_array',
1618
- name: this.y[0],
1676
+ name: this.getVarName(this.y[0]),
1619
1677
  index: this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4])]),
1620
1678
  value: this.y[7],
1621
1679
  tag: '2',
@@ -1628,7 +1686,7 @@ export class NakoParser extends NakoParserBase {
1628
1686
  if (this.accept(['word', '[', this.yCalc, ']', '[', this.yCalc, ']', '[', this.yCalc, ']', 'eq', this.yCalc])) {
1629
1687
  return {
1630
1688
  type: 'let_array',
1631
- name: this.y[0],
1689
+ name: this.getVarName(this.y[0]),
1632
1690
  index: this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[5]), this.checkArrayIndex(this.y[8])]),
1633
1691
  value: this.y[11],
1634
1692
  ...map,
@@ -1638,7 +1696,7 @@ export class NakoParser extends NakoParserBase {
1638
1696
  if (this.accept(['word', '[', this.yCalc, 'comma', this.yCalc, 'comma', this.yCalc, ']', 'eq', this.yCalc])) {
1639
1697
  return {
1640
1698
  type: 'let_array',
1641
- name: this.y[0],
1699
+ name: this.getVarName(this.y[0]),
1642
1700
  index: this.checkArrayReverse([this.checkArrayIndex(this.y[2]), this.checkArrayIndex(this.y[4]), this.checkArrayIndex(this.y[6])]),
1643
1701
  value: this.y[9],
1644
1702
  ...map,
@@ -1746,7 +1804,7 @@ export class NakoParser extends NakoParserBase {
1746
1804
  // 一語関数
1747
1805
  const splitType = operatorList.concat(['eol', ')', ']', 'ならば', '回', '間', '反復', '条件分岐'])
1748
1806
  if (this.check2(['func', splitType])) {
1749
- const f = this.get()
1807
+ const f = this.getVarNameRef(this.get())
1750
1808
  return {
1751
1809
  type: 'func',
1752
1810
  name: f.value,
@@ -1762,7 +1820,7 @@ export class NakoParser extends NakoParserBase {
1762
1820
  if (this.accept([['func', 'word'], '(', this.yGetArgParen, ')'])) {
1763
1821
  return {
1764
1822
  type: 'func',
1765
- name: this.y[0].value,
1823
+ name: this.getVarNameRef(this.y[0]).value,
1766
1824
  args: this.y[2],
1767
1825
  josi: this.y[3].josi,
1768
1826
  ...map,
@@ -1843,7 +1901,7 @@ export class NakoParser extends NakoParserBase {
1843
1901
  yValueWord () {
1844
1902
  const map = this.peekSourceMap()
1845
1903
  if (this.check('word')) {
1846
- const word = this.get()
1904
+ const word = this.getVarNameRef(this.get())
1847
1905
  if (this.skipRefArray) { return word }
1848
1906
 
1849
1907
  // word[n] || word@n
@@ -1867,6 +1925,57 @@ export class NakoParser extends NakoParserBase {
1867
1925
  return null
1868
1926
  }
1869
1927
 
1928
+ /** 変数名を検索して解決する
1929
+ * @param {TokenWithSourceMap} word
1930
+ * @return {TokenWithSourceMap}
1931
+ */
1932
+ getVarName(word) {
1933
+ // check word name
1934
+ const f = this.findVar(word.value)
1935
+ if (!f) { // 変数が見つからない
1936
+ if (this.funcLevel === 0 && word.value.indexOf('__') < 0) {
1937
+ const gname = this.modName + '__' + word.value
1938
+ word.value = gname
1939
+ this.funclist[gname] = {type: 'var', value: ''}
1940
+ } else {
1941
+ this.localvars[word.value] = {type: 'var', value: ''}
1942
+ }
1943
+ }
1944
+ if (f && f.scope === 'global') {
1945
+ word.value = f.name
1946
+ }
1947
+ return word
1948
+ }
1949
+
1950
+ /** 変数名を検索して解決する
1951
+ * @param {TokenWithSourceMap} word
1952
+ * @return {TokenWithSourceMap}
1953
+ */
1954
+ getVarNameRef(word) {
1955
+ // check word name
1956
+ const f = this.findVar(word.value)
1957
+ if (!f) { // 変数が見つからない
1958
+ if (this.funcLevel === 0 && word.value.indexOf('__') < 0) {
1959
+ word.value = this.modName + '__' + word.value
1960
+ }
1961
+ }
1962
+ if (f && f.scope === 'global') {
1963
+ word.value = f.name
1964
+ }
1965
+ return word
1966
+ }
1967
+
1968
+ /** 複数の変数名を検索して解決する
1969
+ * @param {TokenWithSourceMap[]} words
1970
+ * @return {TokenWithSourceMap[]}
1971
+ */
1972
+ getVarNameList(words) {
1973
+ for (let i = 0; i < words.length; i++) {
1974
+ words[i] = this.getVarName(words[i])
1975
+ }
1976
+ return words
1977
+ }
1978
+
1870
1979
  yJSONObjectValue () {
1871
1980
  const a = []
1872
1981
  const firstToken = this.peek()