nadesiko3 3.2.30 → 3.2.34

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 (39) hide show
  1. package/demo/index.html +0 -1
  2. package/package.json +13 -5
  3. package/release/_hash.txt +28 -28
  4. package/release/_script-tags.txt +13 -13
  5. package/release/command.json +1 -1
  6. package/release/command.json.js +1 -1
  7. package/release/command_cnako3.json +1 -1
  8. package/release/command_list.json +1 -1
  9. package/release/editor.js +1 -1
  10. package/release/josi.json +1 -0
  11. package/release/nako_gen_async.js +1 -1
  12. package/release/plugin_markup.js +1 -1
  13. package/release/stats.json +1 -1
  14. package/release/version.js +1 -1
  15. package/release/wnako3.js +1 -1
  16. package/release/wnako3webworker.js +1 -1
  17. package/release/wnako3webworker.js.LICENSE.txt +4 -0
  18. package/release/yoyakugo.json +3 -0
  19. package/src/nako3.js +6 -1
  20. package/src/nako_from_dncl.js +239 -0
  21. package/src/nako_gen.js +24 -9
  22. package/src/nako_gen_async.js +97 -10
  23. package/src/nako_josi_list.js +1 -1
  24. package/src/nako_parser3.js +147 -25
  25. package/src/nako_parser_base.js +3 -0
  26. package/src/nako_reserved_words.js +4 -1
  27. package/src/nako_version.js +2 -2
  28. package/src/plugin_browser.js +14 -0
  29. package/src/plugin_browser_ajax.js +4 -3
  30. package/src/plugin_browser_canvas.js +0 -1
  31. package/src/plugin_browser_chart.js +0 -2
  32. package/src/plugin_browser_dom_basic.js +0 -2
  33. package/src/plugin_browser_dom_parts.js +273 -79
  34. package/src/plugin_node.js +2 -2
  35. package/src/plugin_system.js +408 -20
  36. package/test/common/dncl_test.js +57 -0
  37. package/test/common/flow_test.js +14 -0
  38. package/test/common/plugin_system_test.js +117 -0
  39. package/test/common/variable_scope_test.js +3 -0
@@ -47,6 +47,59 @@ const PluginSystem = {
47
47
  // タイマーに関する処理(タイマーは「!クリア」で全部停止する)
48
48
  sys.__timeout = []
49
49
  sys.__interval = []
50
+ // 日付処理などに使う
51
+ const z2 = sys.__zero2 = (s) => {
52
+ s = '00' + s
53
+ return s.substring(s.length - 2)
54
+ }
55
+ sys.__zero = (s, keta) => {
56
+ let zeroS = ''
57
+ for (let i = 0; i < keta; i++) {zeroS += '0'}
58
+ s = zeroS + s
59
+ return s.substring(s.length - keta)
60
+ }
61
+ sys.__formatDate = (t) => {
62
+ return t.getFullYear() + '/' + z2(t.getMonth() + 1) + '/' + z2(t.getDate())
63
+ }
64
+ sys.__formatTime = (t) => {
65
+ return z2(t.getHours()) + ':' + z2(t.getSeconds()) + ':' + z2(t.getMinutes())
66
+ }
67
+ sys.__formatDateTime = (t, fmt) => {
68
+ const dateStr = t.getFullYear() + '/' + z2(t.getMonth() + 1) + '/' + z2(t.getDate())
69
+ const timeStr = z2(t.getHours()) + ':' + z2(t.getMinutes()) + ':' + z2(t.getSeconds())
70
+ if (fmt.match(/^\d+\/\d+\/\d+\s+\d+:\d+:\d+$/)) {
71
+ return dateStr + ' ' + timeStr
72
+ }
73
+ if (fmt.match(/^\d+\/\d+\/\d+$/)) {
74
+ return dateStr
75
+ }
76
+ if (fmt.match(/^\d+:\d+:\d+$/)) {
77
+ return timeStr
78
+ }
79
+ return dateStr + ' ' + timeStr
80
+ }
81
+ sys.__str2date = (s) => {
82
+ // trim
83
+ s = ('' + s).replace(/(^\s+|\s+$)/, '')
84
+ // is unix time
85
+ if (s.match(/^(\d+|\d+\.\d+)$/)) {
86
+ return new Date(parseFloat(s) * 1000);
87
+ }
88
+ // is time ?
89
+ if (s.match(/^\d+\:\d+(\:\d+)?$/)) {
90
+ const t = new Date()
91
+ const a = (s + ':0').split(':')
92
+ return new Date(`${t.getFullYear()}-${t.getMonth()+1}-${t.getDate()} ${a[0]}:${a[1]}:${a[2]}`)
93
+ }
94
+ // replace splitter to '/'
95
+ s = s.replace(/[\-\s\:]/g, '/')
96
+ s += '/0/0/0' // 日付だけのときのために時間分を足す
97
+ const a = s.split('/')
98
+ const dateStr = `${a[0]}-${a[1]}-${a[2]} ${z2(a[3])}:${z2(a[4])}:${z2(a[5])}`
99
+ return new Date(dateStr)
100
+ }
101
+ // 『継続表示』のための一時変数(『表示』実行で初期化)
102
+ sys.__printPool = ''
50
103
  }
51
104
  },
52
105
  '!クリア': {
@@ -131,11 +184,24 @@ const PluginSystem = {
131
184
  josi: [['を', 'と']],
132
185
  pure: true,
133
186
  fn: function (s, sys) {
187
+ // 継続表示の一時プールを出力
188
+ s = sys.__printPool + s
189
+ sys.__printPool = ''
190
+ //
134
191
  sys.__varslist[0]['表示ログ'] += (s + '\n')
135
192
  sys.logger.send('stdout', s + '')
136
193
  },
137
194
  return_none: true
138
195
  },
196
+ '継続表示': { // @Sを改行なしで表示 // @けいぞくひょうじ
197
+ type: 'func',
198
+ josi: [['を', 'と']],
199
+ pure: true,
200
+ fn: function (s, sys) {
201
+ sys.__printPool += s
202
+ },
203
+ return_none: true
204
+ },
139
205
  '表示ログ': { type: 'const', value: '' }, // @ひょうじろぐ
140
206
  '表示ログクリア': { // @表示ログを空にする // @ひょうじろぐくりあ
141
207
  type: 'func',
@@ -215,6 +281,22 @@ const PluginSystem = {
215
281
  return a % b
216
282
  }
217
283
  },
284
+ '二乗': { // @Aを二乗する // @にじょう
285
+ type: 'func',
286
+ josi: [['の', 'を']],
287
+ pure: true,
288
+ fn: function (a) {
289
+ return a * a
290
+ }
291
+ },
292
+ 'べき乗': { // @AのB乗を求める // @べきじょう
293
+ type: 'func',
294
+ josi: [['の'], ['の']],
295
+ pure: true,
296
+ fn: function (a, b) {
297
+ return Math.pow(a, b)
298
+ }
299
+ },
218
300
  '以上': { // @AがB以上か // @いじょう
219
301
  type: 'func',
220
302
  josi: [['が'], ['']],
@@ -420,7 +502,11 @@ const PluginSystem = {
420
502
  'ナデシコ': { // @なでしこのコードCODEを実行する // @なでしこする
421
503
  type: 'func',
422
504
  josi: [['を', 'で']],
505
+ pure: false,
423
506
  fn: function (code, sys) {
507
+ if (sys.__genMode === '非同期モード') {
508
+ throw new Error('非同期モードでは「ナデシコ」は利用できません。')
509
+ }
424
510
  sys.__varslist[0]['表示ログ'] = ''
425
511
  sys.__self.runEx(code, 'immediate-code.nako3', { resetEnv: false, resetLog: true })
426
512
  const out = sys.__varslist[0]['表示ログ'] + ''
@@ -434,6 +520,9 @@ const PluginSystem = {
434
520
  type: 'func',
435
521
  josi: [['を', 'で']],
436
522
  fn: function (code, sys) {
523
+ if (sys.__genMode === '非同期モード') {
524
+ throw new Error('非同期モードでは「ナデシコ続」は利用できません。')
525
+ }
437
526
  sys.__self.runEx(code, 'immediate-code.nako3', { resetEnv: false, resetLog: false })
438
527
  const out = sys.__varslist[0]['表示ログ'] + ''
439
528
  if (out) {
@@ -1601,7 +1690,7 @@ const PluginSystem = {
1601
1690
  const row = []
1602
1691
  res.push(row)
1603
1692
  for (let c = 0; c < rows; c++) {
1604
- row[c] = a[c][r] ? a[c][r] : ''
1693
+ row[c] = (a[c][r] !== undefined) ? a[c][r] : ''
1605
1694
  }
1606
1695
  }
1607
1696
  return res
@@ -1809,9 +1898,9 @@ const PluginSystem = {
1809
1898
  pure: false,
1810
1899
  fn: function (n, sys) {
1811
1900
  if (sys.__genMode === '非同期モード') {
1812
- sys.async = true
1901
+ const sysenv = sys.setAsync(sys)
1813
1902
  setTimeout(() => {
1814
- sys.nextAsync(sys)
1903
+ sys.compAsync(sys, sysenv)
1815
1904
  }, n * 1000)
1816
1905
  } else {
1817
1906
  if (sys.resolve === undefined) { throw new Error('『秒待機』命令は『!非同期モード』で使ってください。') }
@@ -1850,6 +1939,7 @@ const PluginSystem = {
1850
1939
  // 使用中リストに追加したIDを削除
1851
1940
  const i = sys.__timeout.indexOf(timerId)
1852
1941
  if (i >= 0) { sys.__timeout.splice(i, 1) }
1942
+ if (sys.__genMode === '非同期モード') { sys.newenv = true }
1853
1943
  try {
1854
1944
  f(timerId, sys)
1855
1945
  } catch (e) {
@@ -1874,6 +1964,7 @@ const PluginSystem = {
1874
1964
  if (typeof f === 'string') { f = sys.__findFunc(f, '秒毎') }
1875
1965
  // タイマーをセット
1876
1966
  const timerId = setInterval(() => {
1967
+ if (sys.__genMode === '非同期モード') { sys.newenv = true }
1877
1968
  f(timerId, sys)
1878
1969
  }, parseFloat(n) * 1000)
1879
1970
  // タイマーIDを追加
@@ -1932,6 +2023,7 @@ const PluginSystem = {
1932
2023
  return_none: true
1933
2024
  },
1934
2025
  // @日時処理(簡易)
2026
+ '元号データ': { type: 'const', value: [{ '元号': '令和', '改元日': '2019/05/01' }, { '元号': '平成', '改元日': '1989/01/08' }, { '元号': '昭和', '改元日': '1926/12/25' }, { '元号': '大正', '改元日': '1912/07/30' }, { '元号': '明治', '改元日': '1868/10/23' }] }, // @げんごうでーた
1935
2027
  '今': { // @現在時刻を「HH:mm:ss」の形式で返す // @いま
1936
2028
  type: 'func',
1937
2029
  josi: [],
@@ -1951,20 +2043,99 @@ const PluginSystem = {
1951
2043
  pure: true,
1952
2044
  fn: function () {
1953
2045
  const now = new Date()
1954
- return now.getTime() / 1000
2046
+ return Math.floor(now.getTime() / 1000)
2047
+ }
2048
+ },
2049
+ 'システム時間ミリ秒': { // @現在のUNIX時間 (UTC(1970/1/1)からの経過秒数) をミリ秒で返す // @しすてむじかんみりびょう
2050
+ type: 'func',
2051
+ josi: [],
2052
+ pure: true,
2053
+ fn: function () {
2054
+ const now = new Date()
2055
+ return now.getTime()
1955
2056
  }
1956
2057
  },
1957
2058
  '今日': { // @今日の日付を「YYYY/MM/DD」の形式で返す // @きょう
2059
+ type: 'func',
2060
+ josi: [],
2061
+ pure: false,
2062
+ fn: function (sys) {
2063
+ return sys.__formatDate(new Date())
2064
+ }
2065
+ },
2066
+ '明日': { // @明日の日付を「YYYY/MM/DD」の形式で返す // @あした
2067
+ type: 'func',
2068
+ josi: [],
2069
+ pure: false,
2070
+ fn: function (sys) {
2071
+ const t = Date.now() + (24 * 60 * 60 * 1000)
2072
+ return sys.__formatDate(new Date(t))
2073
+ }
2074
+ },
2075
+ '昨日': { // @昨日の日付を「YYYY/MM/DD」の形式で返す // @きのう
2076
+ type: 'func',
2077
+ josi: [],
2078
+ pure: false,
2079
+ fn: function (sys) {
2080
+ const t = Date.now() - (24 * 60 * 60 * 1000)
2081
+ return sys.__formatDate(new Date(t))
2082
+ }
2083
+ },
2084
+ '今年': { // @今年が何年かを西暦で返す // @ことし
1958
2085
  type: 'func',
1959
2086
  josi: [],
1960
2087
  pure: true,
1961
2088
  fn: function () {
1962
- const z2 = (n) => {
1963
- n = '00' + n
1964
- return n.substr(n.length - 2, 2)
1965
- }
1966
- const t = new Date()
1967
- return t.getFullYear() + '/' + z2(t.getMonth() + 1) + '/' + z2(t.getDate())
2089
+ return (new Date()).getFullYear()
2090
+ }
2091
+ },
2092
+ '来年': { // @来年が何年かを西暦で返す // @らいねん
2093
+ type: 'func',
2094
+ josi: [],
2095
+ pure: true,
2096
+ fn: function () {
2097
+ return (new Date()).getFullYear() + 1
2098
+ }
2099
+ },
2100
+ '去年': { // @去年が何年かを西暦で返す // @きょねん
2101
+ type: 'func',
2102
+ josi: [],
2103
+ pure: true,
2104
+ fn: function () {
2105
+ return (new Date()).getFullYear() - 1
2106
+ }
2107
+ },
2108
+ '今月': { // @今月が何月かを返す // @こんげつ
2109
+ type: 'func',
2110
+ josi: [],
2111
+ pure: true,
2112
+ fn: function () {
2113
+ return (new Date()).getMonth() + 1
2114
+ }
2115
+ },
2116
+ '来月': { // @来月が何月かを返す // @らいげつ
2117
+ type: 'func',
2118
+ josi: [],
2119
+ pure: true,
2120
+ fn: function () {
2121
+ return (new Date()).getMonth() + 2
2122
+ }
2123
+ },
2124
+ '先月': { // @先月が何月かをかえす // @せんげつ
2125
+ type: 'func',
2126
+ josi: [],
2127
+ pure: true,
2128
+ fn: function () {
2129
+ return (new Date()).getMonth()
2130
+ }
2131
+ },
2132
+ '曜日': { // @Sに指定した日付の曜日をで返す。不正な日付の場合は今日の曜日番号を返す。 // @ようび
2133
+ type: 'func',
2134
+ josi: [['の']],
2135
+ pure: false,
2136
+ fn: function (s, sys) {
2137
+ const d = sys.__str2date(s)
2138
+ return '日月火水木金土'.charAt(d.getDay() % 7)
1968
2139
  }
1969
2140
  },
1970
2141
  '曜日番号取得': { // @Sに指定した日付の曜日番号をで返す。不正な日付の場合は今日の曜日番号を返す。(0=日/1=月/2=火/3=水/4=木/5=金/6=土) // @ようびばんごうしゅとく
@@ -1977,6 +2148,223 @@ const PluginSystem = {
1977
2148
  return t.getDay()
1978
2149
  }
1979
2150
  },
2151
+ 'UNIXTIME変換': { // @日時SをUNIX時間 (UTC(1970/1/1)からの経過秒数) に変換して返す(v1非互換) // @UNIXTIMEへんかん
2152
+ type: 'func',
2153
+ josi: [['の','を','から']],
2154
+ pure: false,
2155
+ fn: function (s, sys) {
2156
+ const d = sys.__str2date(s)
2157
+ return d.getTime() / 1000;
2158
+ }
2159
+ },
2160
+ 'UNIX時間変換': { // @日時SをUNIX時間 (UTC(1970/1/1)からの経過秒数) に変換して返す(v1非互換) // @UNIXじかんへんかん
2161
+ type: 'func',
2162
+ josi: [['の','を','から']],
2163
+ pure: false,
2164
+ fn: function (s, sys) {
2165
+ const d = sys.__str2date(s)
2166
+ return d.getTime() / 1000;
2167
+ }
2168
+ },
2169
+ '日時変換': { // @UNIX時間 (UTC(1970/1/1)からの経過秒数) を「YYYY/MM/DD HH:mm:ss」の形式に変換 // @にちじへんかん
2170
+ type: 'func',
2171
+ josi: [['を', 'から']],
2172
+ pure: false,
2173
+ fn: function (tm, sys) {
2174
+ const t = tm * 1000
2175
+ return sys.__formatDateTime(new Date(t), '2022/01/01 00:00:00')
2176
+ }
2177
+ },
2178
+ '日時書式変換': { // @UNIX時間TM(または日付文字列)を「YYYY/MM/DD HH:mm:ss」または「YY-M-D H:m:s」その他、W:曜日、WWW:曜日英、MMM:月英、ccc:ミリ秒の書式に変換 // @にちじしょしきへんかん
2179
+ type: 'func',
2180
+ josi: [['を'], ['で']],
2181
+ pure: false,
2182
+ fn: function (tm, fmt, sys) {
2183
+ const t = sys.__str2date(tm)
2184
+ fmt = fmt.replace(/(YYYY|ccc|WWW|MMM|YY|MM|DD|HH|mm|ss|[MDHmsW])/g, (m) => {
2185
+ switch (m) {
2186
+ case 'YYYY': return t.getFullYear()
2187
+ case 'YY': return ('' + t.getFullYear()).substring(2)
2188
+ case 'MM': return sys.__zero2(t.getMonth() + 1)
2189
+ case 'DD': return sys.__zero2(t.getDate())
2190
+ case 'M': return (t.getMonth() + 1)
2191
+ case 'D': return (t.getDate())
2192
+ case 'HH': return sys.__zero2(t.getHours())
2193
+ case 'mm': return sys.__zero2(t.getMinutes())
2194
+ case 'ss': return sys.__zero2(t.getSeconds())
2195
+ case 'ccc': return sys.__zero(t.getMilliseconds(), 3)
2196
+ case 'H': return (t.getHours())
2197
+ case 'm': return (t.getMinutes())
2198
+ case 's': return (t.getSeconds())
2199
+ case 'W': return '日月火水木金土'.charAt(t.getDay() % 7)
2200
+ case 'WWW': return ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][t.getDay() % 7]
2201
+ case 'MMM': return ['Jan','Feb','Mar','Apr','May','Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][t.getMonth()]
2202
+ }
2203
+ return m
2204
+ })
2205
+ return fmt
2206
+ }
2207
+ },
2208
+ '和暦変換': { // @Sを和暦に変換する。Sは明治以降の日付が有効。 // @われきへんかん
2209
+ type: 'func',
2210
+ josi: [['を']],
2211
+ pure: false,
2212
+ fn: function (s, sys) {
2213
+ const d = sys.__str2date(s)
2214
+ const t = d.getTime()
2215
+ for (const era of sys.__v0['元号データ']) {
2216
+ const gengo = era['元号']
2217
+ const d2 = sys.__str2date(era['改元日'])
2218
+ const t2 = d2.getTime()
2219
+ if (t2 <= t) {
2220
+ let y = (d.getFullYear() - d2.getFullYear()) + 1
2221
+ if (y == 1) {y = '元'}
2222
+ return gengo + y + '年' + sys.__zero2(d.getMonth() + 1) + '月' + sys.__zero2(d.getDate()) + '日'
2223
+ }
2224
+ }
2225
+ throw new Error('『和暦変換』は明示以前の日付には対応していません。')
2226
+ }
2227
+ },
2228
+ '年数差': { // @日付AとBの差を年数で求めて返す。A<Bなら正の数、そうでないなら負の数を返す (v1非互換)。 // @ねんすうさ
2229
+ type: 'func',
2230
+ josi: [['と', 'から'], ['の', 'までの']],
2231
+ pure: false,
2232
+ fn: function (a, b, sys) {
2233
+ const t1 = sys.__str2date(a)
2234
+ const t2 = sys.__str2date(b)
2235
+ return (t2.getFullYear() - t1.getFullYear())
2236
+ }
2237
+ },
2238
+ '月数差': { // @日付AとBの差を月数で求めて返す。A<Bなら正の数、そうでないなら負の数を返す (v1非互換)。 // @げっすうさ
2239
+ type: 'func',
2240
+ josi: [['と', 'から'], ['の', 'までの']],
2241
+ pure: false,
2242
+ fn: function (a, b, sys) {
2243
+ const t1 = sys.__str2date(a)
2244
+ const t2 = sys.__str2date(b)
2245
+ return ((t2.getFullYear() * 12 + t2.getMonth()) -
2246
+ (t1.getFullYear() * 12 + t1.getMonth()))
2247
+ }
2248
+ },
2249
+ '日数差': { // @日付AとBの差を日数で求めて返す。A<Bなら正の数、そうでないなら負の数を返す。 // @にっすうさ
2250
+ type: 'func',
2251
+ josi: [['と', 'から'], ['の', 'までの']],
2252
+ pure: false,
2253
+ fn: function (a, b, sys) {
2254
+ const t1 = Math.ceil(sys.__str2date(a).getTime() / 1000)
2255
+ const t2 = Math.ceil(sys.__str2date(b).getTime() / 1000)
2256
+ const days = Math.ceil((t2 - t1) / (60 * 60 * 24))
2257
+ return days
2258
+ }
2259
+ },
2260
+ '時間差': { // @時間AとBの時間の差を求めて返す。A<Bなら正の数、そうでないなら負の数を返す。 // @じかんさ
2261
+ type: 'func',
2262
+ josi: [['と', 'から'], ['の', 'までの']],
2263
+ pure: false,
2264
+ fn: function (a, b, sys) {
2265
+ const t1 = Math.ceil(sys.__str2date(a).getTime() / 1000)
2266
+ const t2 = Math.ceil(sys.__str2date(b).getTime() / 1000)
2267
+ const hours = Math.ceil((t2 - t1) / (60 * 60))
2268
+ return hours
2269
+ }
2270
+ },
2271
+ '分差': { // @時間AとBの分数の差を求めて返す。A<Bなら正の数、そうでないなら負の数を返す。 // @ふんさ
2272
+ type: 'func',
2273
+ josi: [['と', 'から'], ['の', 'までの']],
2274
+ pure: false,
2275
+ fn: function (a, b, sys) {
2276
+ const t1 = Math.ceil(sys.__str2date(a).getTime() / 1000)
2277
+ const t2 = Math.ceil(sys.__str2date(b).getTime() / 1000)
2278
+ const min = Math.ceil((t2 - t1) / (60))
2279
+ return min
2280
+ }
2281
+ },
2282
+ '秒差': { // @時間AとBの差を秒差で求めて返す。A<Bなら正の数、そうでないなら負の数を返す。 // @びょうさ
2283
+ type: 'func',
2284
+ josi: [['と', 'から'], ['の', 'までの']],
2285
+ pure: false,
2286
+ fn: function (a, b, sys) {
2287
+ const t1 = Math.ceil(sys.__str2date(a).getTime() / 1000)
2288
+ const t2 = Math.ceil(sys.__str2date(b).getTime() / 1000)
2289
+ const sec = Math.ceil((t2 - t1))
2290
+ return sec
2291
+ }
2292
+ },
2293
+ '日時差': { // @日時AとBの差を種類unitで返す。A<Bなら正の数、そうでないなら負の数を返す (v1非互換)。 // @にちじさ
2294
+ type: 'func',
2295
+ josi: [['と', 'から'], ['の', 'までの'], ['による']],
2296
+ pure: false,
2297
+ fn: function (a, b, unit, sys) {
2298
+ switch (unit) {
2299
+ case '年': return sys.__exec('年数差', [a, b, sys])
2300
+ case '月': return sys.__exec('月数差', [a, b, sys])
2301
+ case '日': return sys.__exec('日数差', [a, b, sys])
2302
+ case '時間': return sys.__exec('時間差', [a, b, sys])
2303
+ case '分': return sys.__exec('分差', [a, b, sys])
2304
+ case '秒': return sys.__exec('秒差', [a, b, sys])
2305
+ }
2306
+ throw new Error('『日時差』で不明な単位です。')
2307
+ }
2308
+ },
2309
+ '時間加算': { // @時間SにAを加えて返す。Aには「(+|-)hh:nn:dd」で指定する。 // @じかんかさん
2310
+ type: 'func',
2311
+ josi: [['に'], ['を']],
2312
+ pure: false,
2313
+ fn: function (s, a, sys) {
2314
+ let op = a.charAt(0)
2315
+ if (op === '-' || op === '+') {
2316
+ a = a.substring(1)
2317
+ }
2318
+ const d = sys.__str2date(s)
2319
+ const aa = (a + ':0:0').split(':')
2320
+ let sec = parseInt(aa[0]) * 60 * 60 +
2321
+ parseInt(aa[1]) * 60 +
2322
+ parseInt(aa[2])
2323
+ if (op === '-') {sec *= -1}
2324
+ const rd = new Date(d.getTime() + (sec * 1000))
2325
+ return sys.__formatDateTime(rd, s)
2326
+ }
2327
+ },
2328
+ '日付加算': { // @日付SにAを加えて返す。Aには「(+|-)yyyy/mm/dd」で指定する。 // @ひづけかさん
2329
+ type: 'func',
2330
+ josi: [['に'], ['を']],
2331
+ pure: false,
2332
+ fn: function (s, a, sys) {
2333
+ let op = 1
2334
+ let opc = a.charAt(0)
2335
+ if (opc === '-' || opc === '+') {
2336
+ a = a.substring(1)
2337
+ if (opc === '-') {op *= -1}
2338
+ }
2339
+ const d = sys.__str2date(s)
2340
+ const aa = (a + '/0/0').split('/')
2341
+ const addY = parseInt(aa[0]) * op
2342
+ const addM = parseInt(aa[1]) * op
2343
+ const addD = parseInt(aa[2]) * op
2344
+ d.setFullYear(d.getFullYear() + addY)
2345
+ d.setMonth(d.getMonth() + addM)
2346
+ d.setDate(d.getDate() + addD)
2347
+ return sys.__formatDateTime(d, s)
2348
+ }
2349
+ },
2350
+ '日時加算': { // @日時SにAを加えて返す。Aは「(+|-)1(年|ヶ月|日|週|時間|分|秒)」のように指定する (v1非互換)。 // @にちじかさん
2351
+ type: 'func',
2352
+ josi: [['に'], ['を']],
2353
+ pure: false,
2354
+ fn: function (s, a, sys) {
2355
+ const r = ('' + a).match(/([+|-]?)(\d+)(年|ヶ月|日|週間|時間|分|秒)$/)
2356
+ if (!r) {throw new Error('『日付加算』は『(+|-)1(年|ヶ月|日|時間|分|秒)』の書式で指定します。')}
2357
+ switch (r[3]) {
2358
+ case '年': return sys.__exec('日付加算', [s, `${r[1]}${r[2]}/0/0`, sys])
2359
+ case 'ヶ月': return sys.__exec('日付加算', [s, `${r[1]}0/${r[2]}/0`, sys])
2360
+ case '週間': return sys.__exec('日付加算', [s, `${r[1]}0/0/${r[2]*7}`, sys])
2361
+ case '日': return sys.__exec('日付加算', [s, `${r[1]}0/0/${r[2]}`, sys])
2362
+ case '時間': return sys.__exec('時間加算', [s, `${r[1]}${r[2]}:0:0`, sys])
2363
+ case '分': return sys.__exec('時間加算', [s, `${r[1]}0:${r[2]}:0`, sys])
2364
+ case '秒': return sys.__exec('時間加算', [s, `${r[1]}0:0:${r[2]}`, sys])
2365
+ }
2366
+ }
2367
+ },
1980
2368
  '時間ミリ秒取得': { // @ミリ秒単位の時間を数値で返す。結果は実装に依存する。 // @じかんみりびょうしゅとく
1981
2369
  type: 'func',
1982
2370
  josi: [],
@@ -2124,13 +2512,13 @@ const PluginSystem = {
2124
2512
  josi: [['を', 'から']],
2125
2513
  pure: true,
2126
2514
  fn: function (text) {
2127
- // browser?
2128
- if (window.btoa) {
2129
- let utf8str = String.fromCharCode.apply(null, new TextEncoder('UTF-8').encode(text))
2130
- return btoa(utf8str)
2131
- } else {
2132
- return Buffer.from(text).toString('base64')
2133
- }
2515
+ // browser?
2516
+ if (window.btoa) {
2517
+ const utf8str = String.fromCharCode.apply(null, new TextEncoder('UTF-8').encode(text))
2518
+ return btoa(utf8str)
2519
+ } else {
2520
+ return Buffer.from(text).toString('base64')
2521
+ }
2134
2522
  }
2135
2523
  },
2136
2524
  'BASE64デコード': { // @BASE64デコードして返す // @BASE64でこーど
@@ -2139,9 +2527,9 @@ const PluginSystem = {
2139
2527
  pure: true,
2140
2528
  fn: function (text) {
2141
2529
  if (window.atob) {
2142
- const decoded_utf8str = atob(text)
2143
- const decoded_array = new Uint8Array(Array.prototype.map.call(decoded_utf8str, c => c.charCodeAt()))
2144
- return new TextDecoder('UTF-8').decode(decoded_array);
2530
+ const decodedUtf8str = atob(text)
2531
+ const decodedArray = new Uint8Array(Array.prototype.map.call(decodedUtf8str, c => c.charCodeAt()))
2532
+ return new TextDecoder('UTF-8').decode(decodedArray)
2145
2533
  } else {
2146
2534
  return Buffer.from(text, 'base64').toString()
2147
2535
  }
@@ -0,0 +1,57 @@
1
+ const assert = require('assert')
2
+ const NakoDncl = require('../../src/nako_from_dncl')
3
+ const NakoCompiler = require('../../src/nako3')
4
+
5
+ describe('dncl (#1140)', () => {
6
+ const cmp = (src, expected) => {
7
+ src = NakoDncl.convert(src)
8
+ src = src.replace(/\s+$/, '')
9
+ expected = expected.replace(/\s+$/, '')
10
+ assert.strictEqual(src, expected)
11
+ }
12
+ const nako = new NakoCompiler()
13
+ const cmpNako = (code, res) => {
14
+ nako.logger.debug('code=' + code)
15
+ assert.strictEqual(nako.run(code).log, res)
16
+ }
17
+
18
+ it('代入文', () => {
19
+ cmp('!DNCLモード\n'+
20
+ 'A←3\n',
21
+ //---
22
+ '!DNCLモード\n'+
23
+ 'A=3\n')
24
+ cmp('!DNCLモード\n'+
25
+ 'A←3, B←5\n',
26
+ //---
27
+ '!DNCLモード\n'+
28
+ 'A=3, B=5\n')
29
+ })
30
+ it('もし文', () => {
31
+ cmp('!DNCLモード\n'+
32
+ 'もしA=5ならば\n「OK」と表示\nを実行する',
33
+ //---
34
+ '!DNCLモード\n'+
35
+ 'もしA=5ならば\n「OK」と表示\nここまで')
36
+ })
37
+ // 実行テスト
38
+ it('簡単な実行テスト', () => {
39
+ cmpNako('!DNCLモード\nA←5,B←6,C←7。Cを表示する', '7')
40
+ cmpNako('!DNCLモード\nA=[]。A[1]=3;A[2]=3;Aの要素数を表示する', '2')
41
+ })
42
+ it('配列の入れ替え', () => {
43
+ cmpNako('!DNCLモード\nA={{11,12,13},{21,22,23}}。A[1,2]を表示する', '21')
44
+ cmpNako('!DNCLモード\nA={}。A[1]={11,12,13}, A[2]={21,22,23};A[2,1]を表示する', '12')
45
+ cmpNako('!DNCLモード\nA={{11,12,13},{21,22,23}}。B=A[3,2];Bを表示する', '23')
46
+ cmpNako('!DNCLモード\nA={{11,12,13},{21,22,23}}。C=2;AA=A[1+C,1];AAを表示', '13')
47
+ cmpNako('!DNCLモード\nA={{11,12,13},{21,22,23}}。X=2;Y=1;AA=A[X,Y+1];AAを表示', '22')
48
+ })
49
+ it('配列の自動初期化', () => {
50
+ cmpNako('!DNCLモード\nA[1]←111。AをJSONエンコードして表示する', '[111]')
51
+ cmpNako('!DNCLモード\nA[1]=1,A[2]=2;AをJSONエンコードして表示する', '[1,2]')
52
+ })
53
+ it('インデントを|で表現する', () => {
54
+ cmpNako('!DNCLモード\nA=3;もしA>1ならば\n|A←A+1\nを実行する。\nAを表示する。', '4')
55
+ })
56
+ })
57
+
@@ -356,5 +356,19 @@ describe('flow_test', () => {
356
356
  cmp('A=3;もし、A=5あるいはA=3ならば「OK」と表示。違えば「NG」と表示。', 'OK')
357
357
  cmp('A=空;B=3;もし(A=空)あるいは(B=空)ならば、"OK"と表示;違えば「NG」と表示。', 'OK')
358
358
  })
359
+ it('「増繰り返す」「減繰り返す」を追加#1140', () => {
360
+ cmp('Nを0から4まで2ずつ増やし繰り返す\nNを表示\nここまで\n', '0\n2\n4')
361
+ cmp('A=2;Nを0から4までAずつ増やし繰り返す\nNを表示\nここまで\n', '0\n2\n4')
362
+ cmp('Nを4から0まで2ずつ減らし繰り返す\nNを表示\nここまで\n', '4\n2\n0')
363
+ })
364
+ it('「増やして繰り返す」「減らして繰り返す」を追加#1140', () => {
365
+ // トークンをスタックからポップして処理するので確認
366
+ cmp('Nを0から4まで2ずつ増やして繰り返す\nNを表示\nここまで\n', '0\n2\n4')
367
+ cmp('A=2;Nを0から4までAずつ増やして繰り返す\nNを表示\nここまで\n', '0\n2\n4')
368
+ cmp('Nを4から0まで2ずつ減らして繰り返す\nNを表示\nここまで\n', '4\n2\n0')
369
+ })
370
+ it('ならばの直前に空白があるとエラー(#1141)', () => {
371
+ cmp('A=30。もし、A>5 ならば、「OK」と表示。', 'OK')
372
+ })
359
373
  })
360
374