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
@@ -37,6 +37,37 @@ interface FunctionContext {
37
37
  varsIndex: number;
38
38
  usesClosure: boolean;
39
39
  }
40
+
41
+ /** 生成したコードの各行を n段だけインデントする (空行は捨てる) */
42
+ function indentLines (text: string, n: number): string {
43
+ let result = ''
44
+ for (const line of text.split('\n')) {
45
+ if (line !== '') {
46
+ result += ' '.repeat(n) + line + '\n'
47
+ }
48
+ }
49
+ return result
50
+ }
51
+
52
+ /**
53
+ * パフォーマンスモニタの計測結果を `__self.__performance_monitor[key]` に記録するコードを生成する。
54
+ * ユーザ関数・システム関数本体・システム関数の3箇所で共通して使う。(#2333)
55
+ * @param timeVar 経過時間(マイクロ秒)が入っているJS変数名
56
+ */
57
+ function genPerfMonitorUpdate (timeVar: string): string {
58
+ const rec = `{ called:1, totel_usec: ${timeVar}, min_usec: ${timeVar}, max_usec: ${timeVar}, type: type }`
59
+ return 'if (!__self.__performance_monitor) {\n' +
60
+ '__self.__performance_monitor={};\n' +
61
+ `__self.__performance_monitor[key] = ${rec};\n` +
62
+ '} else if (!__self.__performance_monitor[key]) {\n' +
63
+ `__self.__performance_monitor[key] = ${rec};\n` +
64
+ '} else {\n' +
65
+ '__self.__performance_monitor[key].called++;\n' +
66
+ `__self.__performance_monitor[key].totel_usec+=${timeVar};\n` +
67
+ `if(__self.__performance_monitor[key].min_usec>${timeVar}){__self.__performance_monitor[key].min_usec=${timeVar};}\n` +
68
+ `if(__self.__performance_monitor[key].max_usec<${timeVar}){__self.__performance_monitor[key].max_usec=${timeVar};}\n` +
69
+ '}'
70
+ }
40
71
  interface FindVarResult {
41
72
  i: number;
42
73
  name: string;
@@ -44,6 +75,15 @@ interface FindVarResult {
44
75
  js: string,
45
76
  js_set: string
46
77
  }
78
+ /** 関数呼び出しコードを組み立てるための部品 (convCallFunc) */
79
+ interface CallCodeParts {
80
+ funcDef: string; // 'function' または 'async function'
81
+ funcCall: string; // 関数を呼び出す式
82
+ funcBegin: string; // 呼び出しの直前に実行するコード
83
+ funcEnd: string; // 呼び出しの後に必ず実行するコード
84
+ isAsync: boolean; // 非同期関数の呼び出しか
85
+ sysPerfKey: string | null; // システム関数の計測キー。計測しないときはnull
86
+ }
47
87
  /** コード生成オプション */
48
88
  export class NakoGenOptions {
49
89
  isTest: boolean
@@ -872,31 +912,9 @@ export class NakoGen {
872
912
  let performanceMonitorInjectAtStart = ''
873
913
  let performanceMonitorInjectAtEnd = ''
874
914
  if (this.performanceMonitor.userFunction !== 0) {
875
- let key = name
876
- if (!key) {
877
- if (typeof this.performanceMonitor.mumeiId === 'undefined') {
878
- this.performanceMonitor.mumeiId = 0
879
- }
880
- this.performanceMonitor.mumeiId++
881
- key = `anous_${this.performanceMonitor.mumeiId}`
882
- }
883
- performanceMonitorInjectAtStart = 'const performanceMonitorEnd = (function (key, type) {\n' +
884
- 'const uf_start = performance.now() * 1000;\n' +
885
- 'return function () {\n' +
886
- 'const el_time = performance.now() * 1000 - uf_start;\n' +
887
- 'if (!__self.__performance_monitor) {\n' +
888
- '__self.__performance_monitor={};\n' +
889
- '__self.__performance_monitor[key] = { called:1, totel_usec: el_time, min_usec: el_time, max_usec: el_time, type: type };\n' +
890
- '} else if (!__self.__performance_monitor[key]) {\n' +
891
- '__self.__performance_monitor[key] = { called:1, totel_usec: el_time, min_usec: el_time, max_usec: el_time, type: type };\n' +
892
- '} else {\n' +
893
- '__self.__performance_monitor[key].called++;\n' +
894
- '__self.__performance_monitor[key].totel_usec+=el_time;\n' +
895
- 'if(__self.__performance_monitor[key].min_usec>el_time){__self.__performance_monitor[key].min_usec=el_time;}\n' +
896
- 'if(__self.__performance_monitor[key].max_usec<el_time){__self.__performance_monitor[key].max_usec=el_time;}\n' +
897
- `}};})('${key}', 'user');` +
898
- 'try {\n'
899
- performanceMonitorInjectAtEnd = '} finally { performanceMonitorEnd(); }\n'
915
+ const inject = this.genPerfMonitorInject(this.getPerfMonitorKey(name))
916
+ performanceMonitorInjectAtStart = inject[0]
917
+ performanceMonitorInjectAtEnd = inject[1]
900
918
  }
901
919
  let variableDeclarations = ''
902
920
  const indent = ' '
@@ -1019,7 +1037,9 @@ export class NakoGen {
1019
1037
  const tof = (this.usedAsyncFn) ? topOfFunctionAsync : topOfFunction
1020
1038
  // 関数コード全体を構築
1021
1039
  const lineInfo = ' ' + this.convLineno(node, true, 1) + '\n'
1022
- code = tof + performanceMonitorInjectAtStart + pushStack + variableDeclarations + lineInfo + code + popStack
1040
+ // パフォーマンスモニタのinjectは、PUSH STACKのtry/finallyの内側に入れる。
1041
+ // 外側に置くと __localvars の宣言をまたいでしまい ReferenceError になる (#2333)
1042
+ code = tof + pushStack + performanceMonitorInjectAtStart + variableDeclarations + lineInfo + code + popStack
1023
1043
  code += endOfFunction
1024
1044
  if (funcContext.isAnonymous && funcContext.usesClosure) {
1025
1045
  const parentClosure = `(typeof __nako_closure === 'undefined' ? null : __nako_closure)`
@@ -1412,8 +1432,8 @@ export class NakoGen {
1412
1432
  * @param {boolean} isExpression
1413
1433
  */
1414
1434
  convPerformanceMonitor(node: AstBlocks, isExpression: boolean): string {
1415
- const prev = { ...this.performanceMonitor }
1416
1435
  if (!node.options) { return '' }
1436
+ const prev = { ...this.performanceMonitor }
1417
1437
  if (node.options['ユーザ関数']) {
1418
1438
  this.performanceMonitor.userFunction++
1419
1439
  }
@@ -1430,6 +1450,63 @@ export class NakoGen {
1430
1450
  }
1431
1451
  }
1432
1452
 
1453
+ /**
1454
+ * パフォーマンスモニタの計測キーを決める。名前が無ければ無名関数用のIDを採番する。(#2333)
1455
+ * @param name 関数名
1456
+ * @param suffix キーに付ける接尾辞 ('_body' / '_sys' など)
1457
+ */
1458
+ private getPerfMonitorKey (name: string, suffix = ''): string {
1459
+ if (name) { return `${name}${suffix}` }
1460
+ this.performanceMonitor.mumeiId++
1461
+ return `anous_${this.performanceMonitor.mumeiId}${suffix}`
1462
+ }
1463
+
1464
+ /**
1465
+ * コードを、実行時間を計測する即時実行関数で包む。(#2333)
1466
+ * システム関数本体・システム関数の計測で使う。
1467
+ * @param body 計測対象。isExprなら式、そうでなければ文の並び
1468
+ * @param key 計測結果を記録するキー
1469
+ * @param type 計測の種別
1470
+ * @param opts isExpr:bodyが式か / isAsync:非同期か / startVar,timeVar:使用するJS変数名
1471
+ */
1472
+ private wrapPerfMonitor (body: string, key: string, type: string,
1473
+ opts: { isExpr: boolean, isAsync: boolean, startVar: string, timeVar: string }): string {
1474
+ const funcDef = opts.isAsync ? 'async function' : 'function'
1475
+ // 式なら値を返す必要がある。文ならそのまま実行する。
1476
+ const inner = opts.isExpr ? `return ${body};\n` : `${body}\n`
1477
+ // 末尾に改行を置かないこと。行頭のセミコロンは cleanGeneratedCode で消えてしまい、
1478
+ // 直後の文が `(` で始まると関数呼び出しとして繋がってしまう (#2333)
1479
+ let code = `(${funcDef} (key, type) {\n` +
1480
+ `const ${opts.startVar} = performance.now() * 1000;\n` +
1481
+ 'try {\n' +
1482
+ inner +
1483
+ '} finally {\n' +
1484
+ `const ${opts.timeVar} = performance.now() * 1000 - ${opts.startVar};\n` +
1485
+ genPerfMonitorUpdate(opts.timeVar) +
1486
+ `}})('${key}', '${type}')`
1487
+ // 非同期関数を包んだ場合、待たないと計測対象が完了しない (#2333)
1488
+ if (opts.isAsync) { code = `await ${code}` }
1489
+ // 文として包んだ場合は、ここで文を閉じる
1490
+ return opts.isExpr ? code : code + ';\n'
1491
+ }
1492
+
1493
+ /**
1494
+ * ユーザ関数の実行時間を計測するために、関数の本体の前後へ挿入するコードを返す。(#2333)
1495
+ * @param key 計測結果を記録するキー
1496
+ * @returns [関数本体の直前に挿入するコード, 関数本体の直後に挿入するコード]
1497
+ */
1498
+ private genPerfMonitorInject (key: string): [string, string] {
1499
+ const injectAtStart = 'const performanceMonitorEnd = (function (key, type) {\n' +
1500
+ 'const uf_start = performance.now() * 1000;\n' +
1501
+ 'return function () {\n' +
1502
+ 'const el_time = performance.now() * 1000 - uf_start;\n' +
1503
+ genPerfMonitorUpdate('el_time') +
1504
+ `};})('${key}', 'user');` +
1505
+ 'try {\n'
1506
+ const injectAtEnd = '} finally { performanceMonitorEnd(); }\n'
1507
+ return [injectAtStart, injectAtEnd]
1508
+ }
1509
+
1433
1510
  convWhile(node: AstWhile): string {
1434
1511
  const exprAst = node.blocks[0]
1435
1512
  const blockAst = node.blocks[1]
@@ -1534,18 +1611,14 @@ export class NakoGen {
1534
1611
  }
1535
1612
 
1536
1613
  /**
1537
- * 関数の呼び出し
1538
- * @param {Ast} node
1539
- * @param {boolean} isExpression
1540
- * @returns string コード
1614
+ * どの関数を呼び出すのか関数を特定する (convCallFunc用)
1615
+ * @returns 変数の検索結果 res と、関数の定義 func
1541
1616
  */
1542
- convCallFunc(node: AstCallFunc, isExpression: boolean): string {
1543
- const funcName = NakoGen.getFuncName(node.name)
1617
+ private resolveCallTarget (funcName: string, node: AstCallFunc): { res: FindVarResult, func: any } {
1544
1618
  const res = this.findVar(funcName)
1545
1619
  if (res === null) {
1546
1620
  throw NakoSyntaxError.fromNode(`関数『${funcName}』が見当たりません。有効プラグイン=[` + this.getPluginList().join(', ') + ']', node)
1547
1621
  }
1548
- // どの関数を呼び出すのか関数を特定する
1549
1622
  let func
1550
1623
  if (res.i === 0) { // plugin function
1551
1624
  func = this.__self.getFunc(funcName)
@@ -1558,6 +1631,155 @@ export class NakoGen {
1558
1631
  // 無名関数の可能性
1559
1632
  if (func === undefined) { func = { return_none: false, asyncFn: !!node.asyncFn } }
1560
1633
  }
1634
+ return { res, func }
1635
+ }
1636
+
1637
+ /**
1638
+ * 関数内からpureでないプラグイン関数を呼び出すとき、呼び出しの前後で
1639
+ * ローカル変数を __self.__locals と同期するコードを生成する。
1640
+ * @returns 呼び出し前に実行するコード begin と、呼び出し後に実行するコード end
1641
+ */
1642
+ private genLocalVarsSyncCode (): { begin: string, end: string } {
1643
+ let begin = ''
1644
+ let end = ''
1645
+ // 展開されたローカル変数の列挙
1646
+ const localVars = []
1647
+ for (const name of Array.from(this.varsSet.names.values())) {
1648
+ if (NakoGen.isValidIdentifier(name)) {
1649
+ localVars.push({ str: JSON.stringify(name), js: this.varname_get(name) })
1650
+ }
1651
+ }
1652
+
1653
+ // --- 実行前 ---
1654
+ // 全ての展開されていないローカル変数を __self.__locals にコピーする
1655
+ begin += '__self.__locals = __vars;\n'
1656
+ // 全ての展開されたローカル変数を __self.__locals に保存する
1657
+ if (localVars.length > 0) {
1658
+ begin += '/* 全ての展開されたローカル変数を __self.__locals に保存 */\n'
1659
+ for (const v of localVars) {
1660
+ begin += `__self.__locals.set(${v.str}, ${v.js});\n`
1661
+ }
1662
+ }
1663
+
1664
+ // --- 実行後 ---
1665
+ // 全ての展開されたローカル変数を __self.__locals から受け取る
1666
+ // 「それ」は関数の実行結果を受け取るために使うためスキップ。
1667
+ if (localVars.length > 0) {
1668
+ end += '/* 全ての展開されたローカル変数を __self.__locals から受け取る */\n'
1669
+ for (const v of localVars) {
1670
+ if (v.js !== 'それ') {
1671
+ end += `__self.__varslist[2].set(${v.str}, __self.__locals.get(${v.str}));\n`
1672
+ }
1673
+ }
1674
+ }
1675
+ return { begin, end }
1676
+ }
1677
+
1678
+ /**
1679
+ * 引数のリストを連結してJSの実引数のコードにする。
1680
+ * 必要に応じて、引数のundefinedチェックのコードを挟む。
1681
+ */
1682
+ private genCallArgsCode (funcName: string, res: FindVarResult, args: string[], node: AstCallFunc): string {
1683
+ if ((!this.warnUndefinedCallingUserFunc && res.i !== 0) || (!this.warnUndefinedCallingSystemFunc && res.i === 0)) {
1684
+ return args.join(',')
1685
+ }
1686
+ // 引数チェックの例外 #1260
1687
+ const noCheckFuncs: {[key: string]: boolean} = { 'TYPEOF': true, '変数型確認': true }
1688
+ const argsA: string[] = []
1689
+ args.forEach((arg: string) => {
1690
+ if (arg === '__self' || noCheckFuncs[funcName] === true) { // #1260
1691
+ argsA.push(`${arg}`)
1692
+ } else {
1693
+ // 引数のundefinedチェックのコードを入れる
1694
+ const msg = (res.i === 0) ? '命令『$0』の引数にundefinedを渡しています。' : 'ユーザ命令『$0』の引数にundefinedを渡しています。'
1695
+ const poolIndex = this.addConstPool(msg, [funcName], node.file, node.line)
1696
+ // argが空になる対策 #1315
1697
+ const argStr = (arg === '') ? '""' : arg
1698
+ argsA.push(`(__self.chk(${argStr}, ${poolIndex}))`)
1699
+ }
1700
+ })
1701
+ return argsA.join(', ')
1702
+ }
1703
+
1704
+ /**
1705
+ * 関数の戻り値を変数「それ」に代入するためのラッパを返す。
1706
+ * @returns [前置するコード, 後置するコード]
1707
+ */
1708
+ private getSoreWrap (): [string, string] {
1709
+ if (this.speedMode.invalidSore !== 0) { return ['', ''] }
1710
+ return ['__self.__setSore(', ')']
1711
+ }
1712
+
1713
+ /** 戻り値のない関数呼び出しのコードを組み立てる */
1714
+ private genVoidCallCode (node: AstCallFunc, parts: CallCodeParts): string {
1715
+ const { funcCall, funcBegin, funcEnd } = parts
1716
+ let code: string
1717
+ if (funcEnd === '') {
1718
+ code = `/*VOID関数呼出*/${funcBegin}${funcCall}\n`
1719
+ } else {
1720
+ code = `/*VOID関数呼出(前後処理付)*/${funcBegin}try {\n${indentLines(funcCall, 1)};\n} finally {\n${indentLines(funcEnd, 1)}}\n`
1721
+ }
1722
+ // パフォーマンスモニタ:システム関数。ここでのcodeは式ではなく文なので、文として包む (#2333)
1723
+ if (parts.sysPerfKey) {
1724
+ code = this.wrapPerfMonitor(code, parts.sysPerfKey, 'system',
1725
+ { isExpr: false, isAsync: parts.isAsync, startVar: 'sf_start', timeVar: 'sl_time' })
1726
+ }
1727
+ // 行番号を追加
1728
+ return this.convLineno(node, false) + code
1729
+ }
1730
+
1731
+ /** 戻り値のある関数呼び出しのコードを組み立てる */
1732
+ private genValueCallCode (node: AstCallFunc, isExpression: boolean, parts: CallCodeParts): string {
1733
+ const { funcDef, funcCall, funcBegin, funcEnd, isAsync } = parts
1734
+ // 関数の戻り値を「それ」に記録する
1735
+ const [sorePrefix, sorePostfix] = this.getSoreWrap()
1736
+ let code: string
1737
+ if (funcBegin === '' && funcEnd === '') {
1738
+ code = `${sorePrefix}${funcCall}${sorePostfix}`
1739
+ } else if (funcEnd === '') {
1740
+ const funcBody = `${sorePrefix}${funcCall}${sorePostfix}`
1741
+ const funcObj = `${funcDef}(){ return ${funcBody} }`
1742
+ const funcCallThis = `(${funcObj}).call(this)`
1743
+ code = `/* funcCallThis1 */${funcCallThis}`
1744
+ } else { // つまり、pure=falseの場合
1745
+ const varI = `$nako_i${this.loopId}`
1746
+ this.loopId++
1747
+ code = `/* funcCallThis2 */(${funcDef}(){\n` +
1748
+ indentLines(funcBegin, 1) + '\n' +
1749
+ indentLines('try {', 1) + '\n' +
1750
+ indentLines(`let ${varI} = ${funcCall};`, 2) + '\n' +
1751
+ indentLines(`return ${varI};`, 2) + '\n' +
1752
+ indentLines('} finally {', 2) + '\n' +
1753
+ indentLines(funcEnd, 1) + '\n' +
1754
+ indentLines('}', 1) + '\n' +
1755
+ '}).call(this)'
1756
+ if (isAsync) {
1757
+ code = `await (${code})`
1758
+ }
1759
+ code = `${sorePrefix}${code}${sorePostfix}`
1760
+ }
1761
+ // パフォーマンスモニタ:システム関数。ここでのcodeは式なので、値を返す形で包む (#2333)
1762
+ if (parts.sysPerfKey) {
1763
+ code = this.wrapPerfMonitor(code, parts.sysPerfKey, 'system',
1764
+ { isExpr: true, isAsync, startVar: 'sf_start', timeVar: 'sl_time' })
1765
+ }
1766
+ // ...して
1767
+ if (node.josi === 'して' || (node.josi === '' && !isExpression)) {
1768
+ code = this.convLineno(node, false) + code
1769
+ code += ';\n'
1770
+ }
1771
+ return code
1772
+ }
1773
+
1774
+ /**
1775
+ * 関数の呼び出し
1776
+ * @param {Ast} node
1777
+ * @param {boolean} isExpression
1778
+ * @returns string コード
1779
+ */
1780
+ convCallFunc(node: AstCallFunc, isExpression: boolean): string {
1781
+ const funcName = NakoGen.getFuncName(node.name)
1782
+ const { res, func } = this.resolveCallTarget(funcName, node)
1561
1783
  // 関数の参照渡しか?
1562
1784
  if (node.type === 'func_pointer') {
1563
1785
  return res.js
@@ -1589,73 +1811,15 @@ export class NakoGen {
1589
1811
  // 関数内 (__varslist.length > 3) からプラグイン関数 (res.i === 0) を呼び出すとき、 そのプラグイン関数がpureでなければ
1590
1812
  // 呼び出しの直前に全てのローカル変数をthis.__localsに入れる。
1591
1813
  if (res.i === 0 && this.varslistSet.length > 3 && func.pure !== true && this.speedMode.forcePure === 0) { // undefinedはfalseとみなす
1592
- // 展開されたローカル変数の列挙
1593
- const localVars = []
1594
- for (const name of Array.from(this.varsSet.names.values())) {
1595
- if (NakoGen.isValidIdentifier(name)) {
1596
- localVars.push({ str: JSON.stringify(name), js: this.varname_get(name) })
1597
- }
1598
- }
1599
-
1600
- // --- 実行前 ---
1601
- // 全ての展開されていないローカル変数を __self.__locals にコピーする
1602
- funcBegin += '__self.__locals = __vars;\n'
1603
- // 全ての展開されたローカル変数を __self.__locals に保存する
1604
- if (localVars.length > 0) {
1605
- funcBegin += '/* 全ての展開されたローカル変数を __self.__locals に保存 */\n'
1606
- for (const v of localVars) {
1607
- funcBegin += `__self.__locals.set(${v.str}, ${v.js});\n`
1608
- }
1609
- }
1610
-
1611
- // --- 実行後 ---
1612
- // 全ての展開されたローカル変数を __self.__locals から受け取る
1613
- // 「それ」は関数の実行結果を受け取るために使うためスキップ。
1614
- if (localVars.length > 0) {
1615
- funcEnd += '/* 全ての展開されたローカル変数を __self.__locals から受け取る */\n'
1616
- for (const v of localVars) {
1617
- if (v.js !== 'それ') {
1618
- funcEnd += `__self.__varslist[2].set(${v.str}, __self.__locals[${v.str}]);\n`
1619
- }
1620
- }
1621
- }
1814
+ const sync = this.genLocalVarsSyncCode()
1815
+ funcBegin += sync.begin
1816
+ funcEnd += sync.end
1622
1817
  }
1623
1818
  // 変数「それ」が補完されていることをヒントとして出力
1624
1819
  if (argsOpts.sore) { funcBegin += '/*[sore]*/' }
1625
1820
 
1626
- const indent = (text: string, n: number) => {
1627
- let result = ''
1628
- for (const line of text.split('\n')) {
1629
- if (line !== '') {
1630
- result += ' '.repeat(n) + line + '\n'
1631
- }
1632
- }
1633
- return result
1634
- }
1635
-
1636
- // 引数チェックの例外 #1260
1637
- const noCheckFuncs: {[key: string]: boolean} = { 'TYPEOF': true, '変数型確認': true }
1638
1821
  // 関数呼び出しコードの構築
1639
- let argsCode: string
1640
- if ((!this.warnUndefinedCallingUserFunc && res.i !== 0) || (!this.warnUndefinedCallingSystemFunc && res.i === 0)) {
1641
- argsCode = args.join(',')
1642
- } else {
1643
- const argsA: string[] = []
1644
- args.forEach((arg: string) => {
1645
- if (arg === '__self' || noCheckFuncs[funcName] === true) { // #1260
1646
- argsA.push(`${arg}`)
1647
- } else {
1648
- // 引数のundefinedチェックのコードを入れる
1649
- const msg = (res.i === 0) ? '命令『$0』の引数にundefinedを渡しています。' : 'ユーザ命令『$0』の引数にundefinedを渡しています。'
1650
- const poolIndex = this.addConstPool(msg, [funcName], node.file, node.line)
1651
- // argが空になる対策 #1315
1652
- const argStr = (arg === '') ? '""' : arg
1653
- argsA.push(`(__self.chk(${argStr}, ${poolIndex}))`)
1654
- }
1655
- })
1656
- argsCode = argsA.join(', ')
1657
- }
1658
-
1822
+ const argsCode = this.genCallArgsCode(funcName, res, args, node)
1659
1823
  let funcCall = `${res.js}(${argsCode})`
1660
1824
  if (func.asyncFn) {
1661
1825
  funcDef = `async ${funcDef}`
@@ -1668,111 +1832,20 @@ export class NakoGen {
1668
1832
  funcBegin += `const __local_async${varI} = __self.__vars;\n`
1669
1833
  funcEnd += `__self.__vars = __local_async${varI};\n`
1670
1834
  }
1835
+ // パフォーマンスモニタ:システム関数本体 (呼び出しコードを除く)
1671
1836
  if (res.i === 0 && this.performanceMonitor.systemFunctionBody !== 0) {
1672
- let key = funcName
1673
- if (!key) {
1674
- if (typeof this.performanceMonitor.mumeiId === 'undefined') {
1675
- this.performanceMonitor.mumeiId = 0
1676
- }
1677
- this.performanceMonitor.mumeiId++
1678
- key = `anous_${this.performanceMonitor.mumeiId}`
1679
- }
1680
- funcCall = `(${funcDef} (key, type) {\n` +
1681
- 'const sbf_start = performance.now() * 1000;\n' +
1682
- 'try {\n' +
1683
- 'return ' + funcCall + ';\n' +
1684
- '} finally {\n' +
1685
- 'const sbl_time = performance.now() * 1000 - sbf_start;\n' +
1686
- 'if (!__self.__performance_monitor) {\n' +
1687
- '__self.__performance_monitor={};\n' +
1688
- '__self.__performance_monitor[key] = { called:1, totel_usec: sbl_time, min_usec: sbl_time, max_usec: sbl_time, type: type };\n' +
1689
- '} else if (!__self.__performance_monitor[key]) {\n' +
1690
- '__self.__performance_monitor[key] = { called:1, totel_usec: sbl_time, min_usec: sbl_time, max_usec: sbl_time, type: type };\n' +
1691
- '} else {\n' +
1692
- '__self.__performance_monitor[key].called++;\n' +
1693
- '__self.__performance_monitor[key].totel_usec+=sbl_time;\n' +
1694
- 'if(__self.__performance_monitor[key].min_usec>sbl_time){__self.__performance_monitor[key].min_usec=sbl_time;}\n' +
1695
- 'if(__self.__performance_monitor[key].max_usec<sbl_time){__self.__performance_monitor[key].max_usec=sbl_time;}\n' +
1696
- `}}})('${funcName}_body', 'sysbody')\n`
1697
- }
1698
-
1699
- let code = ''
1700
- if (func.return_none) {
1701
- // ------------------------------------
1702
- // 戻り値のない関数の場合
1703
- // ------------------------------------
1704
- if (funcEnd === '') {
1705
- code = `/*VOID関数呼出*/${funcBegin}${funcCall}\n`
1706
- } else {
1707
- code = `/*VOID関数呼出(前後処理付)*/${funcBegin}try {\n${indent(funcCall, 1)};\n} finally {\n${indent(funcEnd, 1)}}\n`
1708
- }
1709
- // 行番号を追加
1710
- code = this.convLineno(node, false) + code
1711
- } else {
1712
- // ------------------------------------
1713
- // 戻り値のある関数の場合
1714
- // ------------------------------------
1715
- let sorePrefex = ''
1716
- let sorePostfix = ''
1717
- if (this.speedMode.invalidSore === 0) {
1718
- // 関数の戻り値を記録
1719
- sorePrefex = '__self.__setSore('
1720
- sorePostfix = ')'
1721
- }
1722
- if (funcBegin === '' && funcEnd === '') {
1723
- code = `${sorePrefex}${funcCall}${sorePostfix}`
1724
- } else {
1725
- if (funcEnd === '') {
1726
- const funcBody = `${sorePrefex}${funcCall}${sorePostfix}`
1727
- const funcObj = `${funcDef}(){ return ${funcBody} }`
1728
- const funcCallThis = `(${funcObj}).call(this)`
1729
- code = `/* funcCallThis1 */${funcCallThis}`
1730
- } else { // つまり、pure=falseの場合
1731
- const varI = `$nako_i${this.loopId}`
1732
- this.loopId++
1733
- code = `/* funcCallThis2 */(${funcDef}(){\n` +
1734
- indent(funcBegin, 1) + '\n' +
1735
- indent('try {', 1) + '\n' +
1736
- indent(`let ${varI} = ${funcCall};`, 2) + '\n' +
1737
- indent(`return ${varI};`, 2) + '\n' +
1738
- indent('} finally {', 2) + '\n' +
1739
- indent(funcEnd, 1) + '\n' +
1740
- indent('}', 1) + '\n' +
1741
- '}).call(this)'
1742
- if (func.asyncFn) {
1743
- code = `await (${code})`
1744
- }
1745
- code = `${sorePrefex}${code}${sorePostfix}`
1746
- }
1747
- }
1748
- // ...して
1749
- if (node.josi === 'して' || (node.josi === '' && !isExpression)) {
1750
- code = this.convLineno(node, false) + code
1751
- code += ';\n'
1752
- }
1753
- }
1754
-
1755
- if (res.i === 0 && this.performanceMonitor.systemFunction !== 0) {
1756
- code = '(function (key, type) {\n' +
1757
- 'const sf_start = performance.now() * 1000;\n' +
1758
- 'try {\n' +
1759
- 'return ' + code + ';\n' +
1760
- '} finally {\n' +
1761
- 'const sl_time = performance.now() * 1000 - sf_start;\n' +
1762
- 'if (!__self.__performance_monitor) {\n' +
1763
- '__self.__performance_monitor={};\n' +
1764
- '__self.__performance_monitor[key] = { called:1, totel_usec: sl_time, min_usec: sl_time, max_usec: sl_time, type: type };\n' +
1765
- '} else if (!__self.__performance_monitor[key]) {\n' +
1766
- '__self.__performance_monitor[key] = { called:1, totel_usec: sl_time, min_usec: sl_time, max_usec: sl_time, type: type };\n' +
1767
- '} else {\n' +
1768
- '__self.__performance_monitor[key].called++;\n' +
1769
- '__self.__performance_monitor[key].totel_usec+=sl_time;\n' +
1770
- 'if(__self.__performance_monitor[key].min_usec>sl_time){__self.__performance_monitor[key].min_usec=sl_time;}\n' +
1771
- 'if(__self.__performance_monitor[key].max_usec<sl_time){__self.__performance_monitor[key].max_usec=sl_time;}\n' +
1772
- `}}})('${funcName}_sys', 'system')\n`
1837
+ funcCall = this.wrapPerfMonitor(funcCall, this.getPerfMonitorKey(funcName, '_body'), 'sysbody',
1838
+ { isExpr: true, isAsync: !!func.asyncFn, startVar: 'sbf_start', timeVar: 'sbl_time' })
1773
1839
  }
1840
+ // パフォーマンスモニタ:システム関数 (呼び出しコードを含む) はコードの組み立て中に適用する
1841
+ const sysPerfKey = (res.i === 0 && this.performanceMonitor.systemFunction !== 0)
1842
+ ? this.getPerfMonitorKey(funcName, '_sys')
1843
+ : null
1774
1844
 
1775
- return code
1845
+ const parts: CallCodeParts = { funcDef, funcCall, funcBegin, funcEnd, isAsync: !!func.asyncFn, sysPerfKey }
1846
+ return (func.return_none)
1847
+ ? this.genVoidCallCode(node, parts)
1848
+ : this.genValueCallCode(node, isExpression, parts)
1776
1849
  }
1777
1850
 
1778
1851
  convCallValue(node: AstBlocks, isExpression: boolean): string {
@@ -1783,9 +1856,8 @@ export class NakoGen {
1783
1856
  if (isExpression) {
1784
1857
  return funcCall
1785
1858
  }
1786
- const sorePrefex = (this.speedMode.invalidSore === 0) ? '__self.__setSore(' : ''
1787
- const sorePostfix = (this.speedMode.invalidSore === 0) ? ')' : ''
1788
- return this.convLineno(node, false) + `${sorePrefex}${funcCall}${sorePostfix};\n`
1859
+ const [sorePrefix, sorePostfix] = this.getSoreWrap()
1860
+ return this.convLineno(node, false) + `${sorePrefix}${funcCall}${sorePostfix};\n`
1789
1861
  }
1790
1862
 
1791
1863
  convRenbun(node: AstOperator): string {
@@ -2337,8 +2409,25 @@ ${syncMain}(__self)
2337
2409
  // デバッグメッセージ
2338
2410
  let codeImportFiles = ''
2339
2411
  const importNames = []
2412
+ // プラグインとして登録せず、コピーだけが必要なファイル
2413
+ // (plugin_system_*.mjs は plugin_system.mjs 側でマージ済みのため二重登録しない) #2351
2414
+ const noRegisterFiles = [
2415
+ 'nako_errors.mjs',
2416
+ 'plugin_system_debug.mjs',
2417
+ 'plugin_system_math.mjs',
2418
+ 'plugin_system_string.mjs',
2419
+ 'plugin_system_array.mjs',
2420
+ 'plugin_system_datetime.mjs',
2421
+ 'plugin_system_url.mjs',
2422
+ 'plugin_system_types.mjs',
2423
+ 'plugin_system_json.mjs',
2424
+ 'plugin_system_regexp.mjs',
2425
+ 'plugin_system_dict.mjs',
2426
+ 'plugin_system_stdio.mjs',
2427
+ 'plugin_system_timer.mjs'
2428
+ ]
2340
2429
  for (const f of opt.importFiles) {
2341
- if (f === 'nako_errors.mjs') { continue }
2430
+ if (noRegisterFiles.includes(f)) { continue }
2342
2431
  const ff = 'nako3runtime_' + f.replace(/\.(js|mjs)$/, '').replace(/[^a-zA-Z0-9_]/g, '_')
2343
2432
  importNames.push(ff)
2344
2433
  codeImportFiles += `import ${ff} from './nako3runtime/${f}'\n`
@@ -221,7 +221,7 @@ const INDENT_MODE_KEYWORDS = ['!インデント構文', '!ここまでだるい'
221
221
  /** インデント構文 --- インデントを見て"ここまで"を自動挿入 (#596) */
222
222
  export function convertIndentSyntax(tokens: Token[]): Token[] {
223
223
  // インデント構文の変換が必要か?
224
- if (!useIndentSynax(tokens)) { return tokens }
224
+ if (!useIndentSyntax(tokens)) { return tokens }
225
225
  // 『ここまで』があったらエラーを出す
226
226
  for (const t of tokens) {
227
227
  if (t.type === 'ここまで') {
@@ -321,7 +321,7 @@ export function convertIndentSyntax(tokens: Token[]): Token[] {
321
321
  return result
322
322
  }
323
323
 
324
- function useIndentSynax(tokens: Token[]) : boolean {
324
+ function useIndentSyntax(tokens: Token[]) : boolean {
325
325
  // インデント構文が必要かチェック (最初の100個をチェック)
326
326
  for (let i = 0; i < tokens.length; i++) {
327
327
  if (i > 100) { break }
@@ -42,7 +42,7 @@ export const rules: NakoLexRule[] = [
42
42
  { name: 'eol', pattern: /^\n/ },
43
43
  { name: 'eol', pattern: /^;/ },
44
44
  // eslint-disable-next-line no-irregular-whitespace
45
- { name: 'space', pattern: /^(\x20|\x09| |・|⎿ |└||)+/ }, // #877,#1015
45
+ { name: 'space', pattern: /^(\x20|\t| |・|⎿ |└||)+/ }, // #877,#1015
46
46
  { name: 'comma', pattern: /^,/ },
47
47
  { name: 'line_comment', pattern: /^#[^\n]*/ },
48
48
  { name: 'line_comment', pattern: /^\/\/[^\n]*/ },