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
@@ -0,0 +1,152 @@
1
+ /**
2
+ * @fileOverview なでしこ3 plugin_system の分割ファイル
3
+ *
4
+ * タイマー処理の命令を定義する。
5
+ * このファイルは単独のプラグインではなく、plugin_system.mts へマージされる。(#2351)
6
+ */
7
+ import { NakoRuntimeError } from './nako_errors.mjs'
8
+ import { NakoSystem } from './plugin_api.mjs'
9
+
10
+ export default {
11
+ // @タイマー
12
+ '秒待': { // @ N秒の間待機する // @びょうまつ
13
+ type: 'func',
14
+ josi: [['']],
15
+ pure: true,
16
+ asyncFn: true,
17
+ fn: function(n: any, sys:any): Promise<void> {
18
+ return new Promise((resolve, reject) => {
19
+ try {
20
+ // タイマーを仕掛ける
21
+ const timerId = setTimeout(() => {
22
+ // タイマー使用中リストに追加したIDを削除
23
+ const i = sys.__timeout.indexOf(timerId)
24
+ if (i >= 0) { sys.__timeout.splice(i, 1) }
25
+ // Promiseを終了
26
+ resolve()
27
+ }, parseFloat(n) * 1000)
28
+ // タイマー使用中リストに追加
29
+ sys.__timeout.push(timerId)
30
+ } catch (err: any) {
31
+ reject(err)
32
+ }
33
+ })
34
+ },
35
+ return_none: true
36
+ },
37
+ '秒待機': { // @ N秒の間待機する(『秒待』と同じ) // @びょうたいき
38
+ type: 'func',
39
+ josi: [['']],
40
+ pure: true,
41
+ asyncFn: true,
42
+ fn: async function(n: any, sys: any) {
43
+ const p = sys.__exec('秒待', [n, sys])
44
+ return await p
45
+ },
46
+ return_none: true
47
+ },
48
+ '秒逐次待機': { // @ (非推奨) 逐次実行構文にて、N秒の間待機する (廃止予定) // @びょうちくじたいき
49
+ type: 'func',
50
+ josi: [['']],
51
+ pure: true,
52
+ asyncFn: true,
53
+ fn: async function(n: any, sys: any) {
54
+ const p = sys.__exec('秒待', [n, sys])
55
+ return await p
56
+ },
57
+ return_none: true
58
+ },
59
+ '秒後': { // @無名関数(あるいは、文字列で関数名を指定)FをN秒後に実行する。変数『対象』にタイマーIDを代入する。 // @びょうご
60
+ type: 'func',
61
+ josi: [['を'], ['']],
62
+ pure: true,
63
+ fn: function(f: any, n: any, sys: any) {
64
+ // 文字列で指定された関数をオブジェクトに変換
65
+ if (typeof f === 'string') { f = sys.__findFunc(f, '秒後') }
66
+ // 1回限りのタイマーをセット
67
+ const timerId = setTimeout(() => {
68
+ // 使用中リストに追加したIDを削除
69
+ const i = sys.__timeout.indexOf(timerId)
70
+ if (i >= 0) { sys.__timeout.splice(i, 1) }
71
+ try {
72
+ f(timerId, sys)
73
+ } catch (e: any) {
74
+ let err = e
75
+ if (!(e instanceof NakoRuntimeError)) {
76
+ err = new NakoRuntimeError(e, sys.__getSysVar('__line'))
77
+ }
78
+ sys.logger.error(err)
79
+ }
80
+ }, parseFloat(n) * 1000)
81
+ sys.__timeout.unshift(timerId)
82
+ sys.__setSysVar('対象', timerId)
83
+ return timerId
84
+ }
85
+ },
86
+ '秒毎': { // @無名関数(あるいは、文字列で関数名を指定)FをN秒ごとに実行する(『タイマー停止』で停止できる)。変数『対象』にタイマーIDを代入する。 // @びょうごと
87
+ type: 'func',
88
+ josi: [['を'], ['']],
89
+ pure: false,
90
+ fn: function(f: any, n: any, sys: any) {
91
+ // 文字列で指定された関数をオブジェクトに変換
92
+ if (typeof f === 'string') { f = sys.__findFunc(f, '秒毎') }
93
+ // タイマーをセット
94
+ const timerId = setInterval(() => {
95
+ f(timerId, sys)
96
+ }, parseFloat(n) * 1000)
97
+ // タイマーIDを追加
98
+ sys.__interval.unshift(timerId)
99
+ sys.__setSysVar('対象', timerId)
100
+ return timerId
101
+ }
102
+ },
103
+ '秒タイマー開始時': { // @無名関数(あるいは、文字列で関数名を指定)FをN秒ごとに実行する(『秒毎』と同じ) // @びょうたいまーかいししたとき
104
+ type: 'func',
105
+ josi: [['を'], ['']],
106
+ pure: false,
107
+ fn: function(f: any, n: any, sys: any) {
108
+ return sys.__exec('秒毎', [f, n, sys])
109
+ }
110
+ },
111
+ 'タイマー停止': { // @『秒毎』『秒後』や『秒タイマー開始』で開始したタイマーを停止する // @たいまーていし
112
+ type: 'func',
113
+ josi: [['の', 'で']],
114
+ pure: true,
115
+ fn: function(timerId: any, sys: any) {
116
+ const i = sys.__interval.indexOf(timerId)
117
+ if (i >= 0) {
118
+ sys.__interval.splice(i, 1)
119
+ clearInterval(timerId)
120
+ return true
121
+ }
122
+ const j = sys.__timeout.indexOf(timerId)
123
+ if (j >= 0) {
124
+ sys.__timeout.splice(j, 1)
125
+ clearTimeout(timerId)
126
+ return true
127
+ }
128
+ return false
129
+ },
130
+ return_none: false
131
+ },
132
+ '全タイマー停止': { // @『秒毎』『秒後』や『秒タイマー開始』で開始したタイマーを全部停止する // @ぜんたいまーていし
133
+ type: 'func',
134
+ josi: [],
135
+ pure: true,
136
+ fn: function(sys: NakoSystem) {
137
+ // clearInterval
138
+ for (let i = 0; i < sys.__interval.length; i++) {
139
+ const timerId = sys.__interval[i]
140
+ clearInterval(timerId)
141
+ }
142
+ sys.__interval = []
143
+ // clearTimeout
144
+ for (let i = 0; i < sys.__timeout.length; i++) {
145
+ const timerId = sys.__timeout[i]
146
+ clearTimeout(timerId)
147
+ }
148
+ sys.__timeout = []
149
+ },
150
+ return_none: true
151
+ },
152
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * @fileOverview なでしこ3 plugin_system の分割ファイル
3
+ *
4
+ * 型変換の命令を定義する。
5
+ * このファイルは単独のプラグインではなく、plugin_system.mts へマージされる。(#2351)
6
+ */
7
+ export default {
8
+ // @型変換
9
+ '変数型確認': { // @変数Vの型を返す // @へんすうかたかくにん
10
+ type: 'func',
11
+ josi: [['の']],
12
+ pure: true,
13
+ fn: function(v: any) {
14
+ return (typeof v)
15
+ }
16
+ },
17
+ 'TYPEOF': { // @変数Vの型を返す // @TYPEOF
18
+ type: 'func',
19
+ josi: [['の']],
20
+ pure: true,
21
+ fn: function(v: any) {
22
+ return (typeof v)
23
+ }
24
+ },
25
+ '文字列変換': { // @値Vを文字列に変換 // @もじれつへんかん
26
+ type: 'func',
27
+ josi: [['を']],
28
+ pure: true,
29
+ fn: function(v: any): string {
30
+ return String(v)
31
+ }
32
+ },
33
+ 'TOSTR': { // @値Vを文字列に変換 // @TOSTR
34
+ type: 'func',
35
+ josi: [['を']],
36
+ pure: true,
37
+ fn: function(v: any): string {
38
+ return String(v)
39
+ }
40
+ },
41
+ '整数変換': { // @値Vを整数に変換 // @せいすうへんかん
42
+ type: 'func',
43
+ josi: [['を']],
44
+ pure: true,
45
+ fn: function(v: any): number {
46
+ return parseInt(v)
47
+ }
48
+ },
49
+ 'TOINT': { // @値Vを整数に変換 // @TOINT
50
+ type: 'func',
51
+ josi: [['を']],
52
+ pure: true,
53
+ fn: function(v: any): number {
54
+ return parseInt(v)
55
+ }
56
+ },
57
+ '実数変換': { // @値Vを実数に変換 // @じっすうへんかん
58
+ type: 'func',
59
+ josi: [['を']],
60
+ pure: true,
61
+ fn: function(v: any): number {
62
+ return parseFloat(v)
63
+ }
64
+ },
65
+ 'TOFLOAT': { // @値Vを実数に変換 // @TOFLOAT
66
+ type: 'func',
67
+ josi: [['を']],
68
+ pure: true,
69
+ fn: function(v: any): number {
70
+ return parseFloat(v)
71
+ }
72
+ },
73
+ 'INT': { // @値Vを整数に変換 // @INT
74
+ type: 'func',
75
+ josi: [['の']],
76
+ pure: true,
77
+ fn: function(v: any): number {
78
+ return parseInt(v)
79
+ }
80
+ },
81
+ 'FLOAT': { // @値Vを実数に変換 // @FLOAT
82
+ type: 'func',
83
+ josi: [['の']],
84
+ pure: true,
85
+ fn: function(v: any): number {
86
+ return parseFloat(v)
87
+ }
88
+ },
89
+ 'NAN判定': { // @値VがNaNかどうかを判定(命令『非数判定』を使う事を推奨) // @NANはんてい
90
+ type: 'func',
91
+ josi: [['を']],
92
+ pure: true,
93
+ fn: function(v: any): boolean {
94
+ return isNaN(v)
95
+ }
96
+ },
97
+ '非数判定': { // @値Vが非数かどうかを判定(NAN判定より堅牢) // @ひすうはんてい
98
+ type: 'func',
99
+ josi: [['を']],
100
+ pure: true,
101
+ fn: function(v: any): boolean {
102
+ // https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN
103
+ return Number.isNaN(v)
104
+ }
105
+ },
106
+ 'HEX': { // @値Vを16進数に変換 // @HEX
107
+ type: 'func',
108
+ josi: [['の']],
109
+ pure: true,
110
+ fn: function(a: any): string {
111
+ return parseInt(a).toString(16)
112
+ }
113
+ },
114
+ '進数変換': { // @値VをN進数に変換 // @しんすうへんかん
115
+ type: 'func',
116
+ josi: [['を', 'の'], ['']],
117
+ pure: true,
118
+ fn: function(v: any, n: number): string {
119
+ return parseInt(v).toString(n)
120
+ }
121
+ },
122
+ '二進': { // @値Vを2進数に変換 // @にしん
123
+ type: 'func',
124
+ josi: [['を', 'の', 'から']],
125
+ pure: true,
126
+ fn: function(v: any): string {
127
+ return parseInt(v).toString(2)
128
+ }
129
+ },
130
+ '二進表示': { // @値Vを2進数に変換して表示 // @にしんひょうじ
131
+ type: 'func',
132
+ josi: [['を', 'の', 'から']],
133
+ pure: true,
134
+ fn: function(v: any, sys: any) {
135
+ const s = parseInt(v).toString(2)
136
+ sys.__exec('表示', [s, sys])
137
+ }
138
+ },
139
+ 'RGB': { // @HTML用のカラーコードを返すRGB(R,G,B)で各値は0-255 // @RGB
140
+ type: 'func',
141
+ josi: [['と'], ['の'], ['で']],
142
+ pure: true,
143
+ fn: function(r: any, g: any, b: any): string {
144
+ const z2 = (v: any): string => {
145
+ const v2: string = '00' + (parseInt(String(v)).toString(16))
146
+ return v2.substring(v2.length - 2, v2.length)
147
+ }
148
+ return '#' + z2(r) + z2(g) + z2(b)
149
+ }
150
+ },
151
+ }
@@ -0,0 +1,193 @@
1
+ /**
2
+ * @fileOverview なでしこ3 plugin_system の分割ファイル
3
+ *
4
+ * URLエンコード・パラメータ処理・BASE64・パス操作の命令を定義する。
5
+ * このファイルは単独のプラグインではなく、plugin_system.mts へマージされる。(#2351)
6
+ */
7
+ import { NakoSystem } from './plugin_api.mjs'
8
+
9
+ export default {
10
+ // @URLエンコードとパラメータ
11
+ 'URLエンコード': { // @URLエンコードして返す // @URLえんこーど
12
+ type: 'func',
13
+ josi: [['を', 'から']],
14
+ pure: true,
15
+ fn: function(text: any) {
16
+ return encodeURIComponent(text)
17
+ }
18
+ },
19
+ 'URLデコード': { // @URLデコードして返す // @URLでこーど
20
+ type: 'func',
21
+ josi: [['を', 'へ', 'に']],
22
+ pure: true,
23
+ fn: function(text: any) {
24
+ return decodeURIComponent(text)
25
+ }
26
+ },
27
+ 'URLパラメータ解析': { // @URLパラメータを解析してハッシュで返す // @URLぱらめーたかいせき
28
+ type: 'func',
29
+ josi: [['を', 'の', 'から']],
30
+ pure: true,
31
+ fn: function(url: string, sys: any) {
32
+ const res: any = {}
33
+ if (typeof url !== 'string') {
34
+ return res
35
+ }
36
+ const p = url.split('?')
37
+ if (p.length <= 1) {
38
+ return res
39
+ }
40
+ const params = p[1].split('&')
41
+ for (const line of params) {
42
+ if (line === '') { continue }
43
+ const eqIdx = line.indexOf('=')
44
+ let k: string
45
+ let v: string
46
+ if (eqIdx < 0) {
47
+ k = line
48
+ v = ''
49
+ } else {
50
+ k = line.substring(0, eqIdx)
51
+ v = line.substring(eqIdx + 1)
52
+ }
53
+ const decodedKey = sys.__exec('URLデコード', [k])
54
+ const decodedVal = sys.__exec('URLデコード', [v])
55
+ res[decodedKey] = decodedVal
56
+ }
57
+ return res
58
+ }
59
+ },
60
+
61
+ // @BASE64
62
+ 'BASE64エンコード': { // @BASE64エンコードして返す // @BASE64えんこーど
63
+ type: 'func',
64
+ josi: [['を', 'から']],
65
+ pure: true,
66
+ fn: function(text: any) {
67
+ // browser?
68
+ if (typeof (window) !== 'undefined' && (window as any).btoa) {
69
+ const u8a: any = new TextEncoder().encode(text)
70
+ const utf8str = String.fromCharCode.apply(null, u8a)
71
+ return btoa(utf8str)
72
+ }
73
+ // Node?
74
+ if (typeof (Buffer) !== 'undefined') {
75
+ return Buffer.from(text).toString('base64')
76
+ }
77
+ throw new Error('『BASE64エンコード』は利用できません。')
78
+ }
79
+ },
80
+ 'BASE64デコード': { // @BASE64デコードして返す // @BASE64でこーど
81
+ type: 'func',
82
+ josi: [['を', 'へ', 'に']],
83
+ pure: true,
84
+ fn: function(text: any) {
85
+ if (typeof (window) !== 'undefined' && (window as any).atob) {
86
+ const decodedUtf8str = atob(text)
87
+ const dec: any = Array.prototype.map.call(decodedUtf8str, c => c.charCodeAt())
88
+ const decodedArray = new Uint8Array(dec)
89
+ return new TextDecoder('UTF-8').decode(decodedArray)
90
+ }
91
+ // Node?
92
+ if (typeof (Buffer) !== 'undefined') {
93
+ return Buffer.from(text, 'base64').toString()
94
+ }
95
+ throw new Error('『BASE64デコード』は利用できません。')
96
+ }
97
+ },
98
+
99
+ // @パス操作
100
+ '拡張子抽出': { // @ファイル名Sから拡張子を抽出する // @かくちょうしちゅうしゅつ
101
+ type: 'func',
102
+ josi: [['から', 'の']],
103
+ pure: true,
104
+ fn: function(fname: string, sys: NakoSystem) {
105
+ if (fname === null || fname === undefined) { return '' }
106
+ const sep = sys.pathSeparator || '/'
107
+ if (fname.indexOf(sep) >= 0) { // パス記号があればファイル名を抽出
108
+ const parts = fname.split(sep)
109
+ fname = parts[parts.length - 1]
110
+ }
111
+ const m = fname.match(/(\.[a-zA-Z0-9_\-+]+)$/)
112
+ if (m) { return m[1] }
113
+ return ''
114
+ }
115
+ },
116
+ '拡張子変更': { // @ファイル名Aの拡張子をBに変更して返す // @かくちょうしへんこう
117
+ type: 'func',
118
+ josi: [['の', 'を', 'から'], ['に', 'へ']],
119
+ pure: true,
120
+ fn: function(fname: string, ext: string, sys: NakoSystem) {
121
+ if (fname === null || fname === undefined) { return ext }
122
+ const sep = sys.pathSeparator || '/'
123
+ const pathList = fname.split(sep)
124
+ const filename = pathList[pathList.length - 1]
125
+ const pathStr = pathList.slice(0, -1).join(sep)
126
+ const rawExt = (ext ?? '').trim()
127
+ let extWithDot = rawExt
128
+ if (rawExt !== '' ) {
129
+ extWithDot = rawExt.startsWith('.') ? rawExt : '.' + rawExt
130
+ }
131
+ const newFilename = filename.replace(/(\.[a-zA-Z0-9_\-+]+)?$/, extWithDot)
132
+ return sys.__exec('終端パス追加', [pathStr, sys]) + newFilename
133
+ }
134
+ },
135
+ '終端パス追加': { // @パスSの終端にパス区切り文字を追加して返す // @しゅうたんぱすついか
136
+ type: 'func',
137
+ josi: [['に', 'へ']],
138
+ pure: true,
139
+ fn: function(path: string, sys: NakoSystem) {
140
+ const sep = sys.pathSeparator || '/'
141
+ if (path === undefined || path === null || path === '') {
142
+ return ''
143
+ }
144
+ if (path.endsWith(sep)) {
145
+ return path
146
+ }
147
+ return path + sep
148
+ }
149
+ },
150
+ '終端パス除去': { // @フォルダ名DIRの末尾にあるパス記号を削除する // @しゅうたんぱすじょきょ
151
+ type: 'func',
152
+ josi: [['の', 'から']],
153
+ pure: true,
154
+ fn: function(dir: string, sys: NakoSystem) {
155
+ const sep = sys.pathSeparator || '/'
156
+ if (!dir) { return '' }
157
+ if (dir.endsWith(sep)) {
158
+ return dir.substring(0, dir.length - 1)
159
+ } else {
160
+ return dir
161
+ }
162
+ }
163
+ },
164
+ '終端パス削除': { // @フォルダ名DIRの末尾にあるパス記号を削除する // @しゅうたんぱすさくじょ
165
+ type: 'func',
166
+ josi: [['の', 'から']],
167
+ pure: true,
168
+ fn: function(dir: string, sys: NakoSystem) {
169
+ return sys.__exec('終端パス除去', [dir, sys])
170
+ }
171
+ },
172
+ 'ファイル名抽出': { // @パスPATHからファイル名を抽出して返す // @ふぁいるめいちゅうしゅつ
173
+ type: 'func',
174
+ josi: [['の', 'から']],
175
+ pure: true,
176
+ fn: function(dir: string, sys: NakoSystem) {
177
+ const sep = sys.pathSeparator || '/'
178
+ const parts = dir.split(sep)
179
+ return parts[parts.length - 1]
180
+ }
181
+ },
182
+ 'パス抽出': { // @パスPATHからディレクトリ部分を抽出して返す // @ぱすちゅうしゅつ
183
+ type: 'func',
184
+ josi: [['の', 'から']],
185
+ pure: true,
186
+ fn: function(dir: string, sys: NakoSystem) {
187
+ const sep = sys.pathSeparator || '/'
188
+ const parts = dir.split(sep)
189
+ parts.pop()
190
+ return parts.join(sep)
191
+ }
192
+ }
193
+ }
@@ -16,7 +16,7 @@ const PluginTOML = {
16
16
  type: 'func',
17
17
  josi: [],
18
18
  pure: true,
19
- fn: function(sys: any) {
19
+ fn: function(_sys: any) {
20
20
  }
21
21
  },
22
22
  // @TOML
@@ -24,7 +24,7 @@ const PluginTOML = {
24
24
  type: 'func',
25
25
  josi: [['を', 'の', 'から']],
26
26
  pure: true,
27
- fn: function(s: string, sys: any) {
27
+ fn: function(s: string, _sys: any) {
28
28
  return TOML.parse(s)
29
29
  }
30
30
  },
@@ -32,7 +32,7 @@ const PluginTOML = {
32
32
  type: 'func',
33
33
  josi: [['を', 'から', 'の']],
34
34
  pure: true,
35
- fn: function(s: any, sys: any) {
35
+ fn: function(s: any, _sys: any) {
36
36
  return TOML.stringify(s)
37
37
  }
38
38
  }
@@ -0,0 +1,39 @@
1
+ # core/test/fixtures
2
+
3
+ テストが読み込む固定データを置くディレクトリです。
4
+ `npm run test:core` のテストグロブは `core/test/*.mjs` なので、
5
+ このディレクトリのファイルがテストとして実行されることはありません。
6
+
7
+ ## 構文解析器のゴールデン AST (#2364)
8
+
9
+ | ファイル | 役割 |
10
+ |---|---|
11
+ | `parser_corpus.mjs` | 構文解析のコーパス(なでしこコード集)と、AST をプレーン化するヘルパー |
12
+ | `parser_ast_golden.json` | コーパスを構文解析した結果の AST を固定したもの |
13
+ | `make_parser_ast_golden.mjs` | `parser_ast_golden.json` を生成するスクリプト |
14
+
15
+ `core/test/nako_parser_test.mjs` が、現在の構文解析結果と
16
+ `parser_ast_golden.json` を `assert.deepStrictEqual` で比較します。
17
+ AST の全キー・全値に加えて**キーの並び順**まで比較するため、
18
+ 構文解析器に手を入れて結果が少しでも変われば必ず検出できます。
19
+
20
+ ### ゴールデンを再生成する
21
+
22
+ コーパス(`parser_corpus.mjs` の `PARSER_CORPUS`)を追加・変更したときや、
23
+ **意図的に**構文解析の結果を変更したときに再生成します。
24
+
25
+ ```bash
26
+ npm run build:tsc && node core/test/fixtures/make_parser_ast_golden.mjs
27
+ ```
28
+
29
+ `core/src/*.mjs` はビルド生成物(git 管理外)なので、
30
+ **再生成の前に必ずビルドしてください**。ビルドを忘れると古いコードの
31
+ AST がゴールデンになってしまいます。
32
+
33
+ ### 差分が出たときの読み方
34
+
35
+ 構文解析器のリファクタリング中にこのテストが落ちた場合、それは
36
+ **リファクタリングで挙動が変わってしまった**ことを意味します。
37
+ ゴールデンを再生成して通す前に、差分が意図したものか必ず確認してください。
38
+ `assert.deepStrictEqual` は差分箇所を表示するので、
39
+ どのノードのどのキーが変わったかを追えます。
@@ -0,0 +1,31 @@
1
+ /* eslint-disable no-undef */
2
+ /**
3
+ * `parser_ast_golden.json` を生成するスクリプト (#2364)
4
+ *
5
+ * コーパス(`parser_corpus.mjs` の PARSER_CORPUS)を構文解析し、
6
+ * その AST をゴールデンファイルとして書き出す。
7
+ *
8
+ * 実行前に必ずビルドすること(core/src/*.mjs はビルド生成物のため)。
9
+ *
10
+ * npm run build:tsc
11
+ * node core/test/fixtures/make_parser_ast_golden.mjs
12
+ *
13
+ * このディレクトリは `core/test/*.mjs` のテストグロブに含まれないので、
14
+ * テスト実行時にこのファイルが走ることはない。
15
+ */
16
+ import fs from 'node:fs'
17
+ import { PARSER_CORPUS, parseToPlainAst } from './parser_corpus.mjs'
18
+
19
+ const golden = {}
20
+ for (const [name, code] of Object.entries(PARSER_CORPUS)) {
21
+ try {
22
+ golden[name] = parseToPlainAst(code)
23
+ } catch (err) {
24
+ console.error(`構文解析に失敗しました: ${name}`)
25
+ throw err
26
+ }
27
+ }
28
+
29
+ const outPath = new URL('./parser_ast_golden.json', import.meta.url)
30
+ fs.writeFileSync(outPath, JSON.stringify(golden, null, 1) + '\n', 'utf8')
31
+ console.log(`${Object.keys(golden).length} 件のゴールデン AST を書き出しました`)