nadesiko3 3.2.36 → 3.2.40

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.
@@ -6,6 +6,7 @@
6
6
  "繰返",
7
7
  "増繰返",
8
8
  "減繰返",
9
+ "後判定",
9
10
  "反復",
10
11
  "抜",
11
12
  "続",
@@ -18,6 +19,8 @@
18
19
  "定",
19
20
  "逐次実行",
20
21
  "条件分岐",
22
+ "増",
23
+ "減",
21
24
  "変数",
22
25
  "定数",
23
26
  "エラー監視",
@@ -56,12 +56,40 @@ function make_spaces(n) {
56
56
  function dncl2nako(src, filename) {
57
57
  // 全角半角を統一
58
58
  src = conv2half(src)
59
- // 行頭の「|」はインデントを表す記号
59
+ // 行頭の「|」はインデントを表す記号なので無視する
60
+ // 後判定の「繰り返し,」を「後判定で繰り返す」に置換する
60
61
  const a = src.split('\n')
61
62
  for (let i = 0; i < a.length; i++) {
62
- a[i] = a[i].replace(/^(\s*[|\s]+)(.*$)/, (m0, m1, m2) => {
63
+ const line = a[i]
64
+ a[i] = line.replace(/^(\s*[|\s]+)(.*$)/, (m0, m1, m2) => {
63
65
  return make_spaces(m1.length) + m2
64
66
  })
67
+ const line2 = line.replace(/^\s+/, '').replace(/\s+$/, '')
68
+ if (line2 === '繰り返し,' || line2 === '繰り返し') {
69
+ a[i] = '後判定で繰り返し'
70
+ }
71
+ const r = line.match(/^\s*を,?(.+)になるまで(繰り返す|実行する)/)
72
+ if (r) {
73
+ a[i] = `ここまで、(${r[1]})になるまでの間`
74
+ continue
75
+ }
76
+ // 『もしj>hakosuならばhakosu←jを実行する』のような単文のもし文
77
+ const rif = line.match(/^もし(.+)を実行する(。|.)*/)
78
+ if (rif) {
79
+ const sent = dncl2nako(rif[1], filename)
80
+ a[i] = `もし、${sent};`
81
+ continue
82
+ }
83
+ //'のすべての値を0にする'
84
+ //'のすべての要素を0にする'
85
+ //'のすべての要素に0を代入する'
86
+ const rall = line.match(/^(.+?)のすべての(要素|値)(を|に)(.+?)(にする|を代入)/)
87
+ if (rall) {
88
+ const varname = rall[1]
89
+ const v = rall[4]
90
+ a[i] = `${varname} = [${v},${v},${v},${v},${v},${v},${v},${v},${v},${v},${v},${v},${v},${v},${v},${v},${v},${v},${v},${v},${v}]`
91
+ continue
92
+ }
65
93
  }
66
94
  src = a.join('\n')
67
95
  // ---------------------------------
@@ -77,106 +105,88 @@ function dncl2nako(src, filename) {
77
105
  'を実行し,そうでなければ': '違えば',
78
106
  'を実行し、そうでなければ': '違えば',
79
107
  'を繰り返す': 'ここまで',
80
- '改行なしで表示': '継続表示',
81
- 'のすべての値を0にする': '=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]',
108
+ '改行なしで表示': '連続無改行表示',
82
109
  'ずつ増やしながら':'ずつ増やし繰り返す',
83
110
  'ずつ減らしながら':'ずつ減らし繰り返す',
111
+ '二進で表示': '二進表示',
112
+ 'でないならば': 'でなければ',
84
113
  }
85
-
114
+ let peekChar = () => src.charAt(0)
115
+ let nextChar = () => {
116
+ let ch = src.charAt(0)
117
+ src = src.substring(1)
118
+ return ch
119
+ }
120
+ // 文字列を判定するフラグ
121
+ let flagStr = false
122
+ let poolStr = ''
123
+ let endStr = ''
124
+ // 結果
86
125
  let result = ''
87
126
  while (src !='') {
88
127
  // 代入記号を変更
89
128
  const ch = src.charAt(0)
129
+ if (flagStr) {
130
+ if (ch === endStr) {
131
+ result += poolStr + endStr
132
+ poolStr = ''
133
+ flagStr = false
134
+ nextChar()
135
+ continue
136
+ }
137
+ poolStr += nextChar()
138
+ continue
139
+ }
140
+ // 文字列?
141
+ if (ch == '"') {
142
+ flagStr = true
143
+ endStr = '"'
144
+ poolStr = nextChar()
145
+ continue
146
+ }
147
+ if (ch == '「') {
148
+ flagStr = true
149
+ endStr = '」'
150
+ poolStr = nextChar()
151
+ continue
152
+ }
153
+ if (ch == '『') {
154
+ flagStr = true
155
+ endStr = '』'
156
+ poolStr = nextChar()
157
+ continue
158
+ }
90
159
  // 空白を飛ばす
91
160
  if (ch === ' ' || ch === ' ' || ch == '\t') {
92
- result += ch
93
- src = src.substring(1)
161
+ result += nextChar()
94
162
  continue
95
163
  }
96
- // 1行先読み
97
- let line = ''
98
- const i = src.indexOf('\n')
99
- if (i >= 0) {
100
- line = src.substring(0, i)
101
- } else {
102
- line = src
164
+ // 表示を連続表示に置き換える
165
+ const ch3 = src.substring(0, 3)
166
+ if (ch3 === 'を表示') {
167
+ result += 'を連続表示'
168
+ src = src.substring(3)
169
+ continue
103
170
  }
104
- //「var n 増やす」を「var = var + 1」と置き換える
105
- const r = line.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*を\s*([0-9a-zA-Z_]+)\s*(増やす|減らす)/)
106
- if (r) {
107
- const var_name = r[1]
108
- const inc_val = r[2]
109
- const inc_dec = r[3]
110
- if (inc_dec == '増やす') {
111
- result += `${var_name} = ${var_name} + ${inc_val};`
112
- } else {
113
- result += `${var_name} = ${var_name} - ${inc_val};`
114
- }
115
- src = src.substring(r[0].length)
171
+ if (src.substring(0, 4) === 'を 表示') {
172
+ result += 'を連続表示'
173
+ src = src.substring(4)
116
174
  continue
117
175
  }
118
- //「S1とS2とS3を表示する」を「(S1)&(S2)&S3)を表示」と置き換える
119
- // あまりスマートではないが手抜きで
120
- if (line.indexOf('表示') >= 0) {
121
- const r6 = line.match(/^(.+?)と(.+?)と(.+?)と(.+?)と(.+?)と(.+?)(を|と)表示/)
122
- if (r6) {
123
- const s1 = r6[1]
124
- const s2 = r6[2]
125
- const s3 = r6[3]
126
- const s4 = r6[4]
127
- const s5 = r6[5]
128
- const s6 = r6[6]
129
- result += `(${s1})&(${s2})&(${s3})&(${s4})&(${s5})&(${s6})を表示`
130
- src = src.substring(r6[0].length)
131
- continue
132
- }
133
- const r5 = line.match(/^(.+?)と(.+?)と(.+?)と(.+?)と(.+?)(を|と)表示/)
134
- if (r5) {
135
- const s1 = r5[1]
136
- const s2 = r5[2]
137
- const s3 = r5[3]
138
- const s4 = r5[4]
139
- const s5 = r5[5]
140
- result += `(${s1})&(${s2})&(${s3})&(${s4})&(${s5})を表示`
141
- src = src.substring(r5[0].length)
142
- continue
143
- }
144
- const r4 = line.match(/^(.+?)と(.+?)と(.+?)と(.+?)(を|と)表示/)
145
- if (r4) {
146
- const s1 = r4[1]
147
- const s2 = r4[2]
148
- const s3 = r4[3]
149
- const s4 = r4[4]
150
- result += `(${s1})&(${s2})&(${s3})&(${s4})を表示`
151
- src = src.substring(r4[0].length)
152
- continue
153
- }
154
- const r3 = line.match(/^(.+?)と(.+?)と(.+?)(を|と)表示/)
155
- if (r3) {
156
- const s1 = r3[1]
157
- const s2 = r3[2]
158
- const s3 = r3[3]
159
- result += `(${s1})&(${s2})&(${s3})を表示`
160
- src = src.substring(r3[0].length)
161
- continue
162
- }
163
- const r2 = line.match(/^(.+?)と(.+?)を表示/)
164
- if (r2) {
165
- const s1 = r2[1]
166
- const s2 = r2[2]
167
- result += `(${s1})&(${s2})を表示`
168
- src = src.substring(r2[0].length)
169
- continue
176
+ // 乱数を乱数範囲に置き換える
177
+ if (src.substring(0, 2) === '乱数' && src.substring(0, 4) !== '乱数範囲') {
178
+ result += '乱数範囲'
179
+ src = src.substring(2)
180
+ continue
181
+ }
182
+ // 増やす・減らすの前に「だけ」を追加する #1149
183
+ if (ch3 === '増やす' || ch3 === '減らす') {
184
+ if (result.substring(result.length - 2) !== 'だけ') {
185
+ result += 'だけ'
170
186
  }
187
+ result += ch3
188
+ src = src.substring(3)
171
189
  }
172
- // 『もしj>hakosuならばhakosu←jを実行する』のような単文のもし文
173
- const rif = line.match(/^もし(.+)を実行する(。|.)*/)
174
- if (rif) {
175
- const sent = dncl2nako(rif[1], filename)
176
- result += `もし、${sent};`
177
- src = src.substring(rif[0].length)
178
- continue
179
- }
180
190
  // 一覧から単純な変換
181
191
  {
182
192
  let flag = false
@@ -193,8 +203,7 @@ function dncl2nako(src, filename) {
193
203
  }
194
204
 
195
205
  // 1文字削る
196
- src = src.substring(1)
197
- result += ch
206
+ result += nextChar()
198
207
  }
199
208
  return result
200
209
  }
package/src/nako_gen.js CHANGED
@@ -476,6 +476,9 @@ try {
476
476
  case 'let':
477
477
  code += this.convLet(node)
478
478
  break
479
+ case 'inc':
480
+ code += this.convInc(node)
481
+ break
479
482
  case 'word':
480
483
  case 'variable':
481
484
  code += this.convGetVar(node)
@@ -519,6 +522,9 @@ try {
519
522
  case 'while':
520
523
  code += this.convWhile(node)
521
524
  break
525
+ case 'atohantei':
526
+ code += this.convAtohantei(node)
527
+ break
522
528
  case 'switch':
523
529
  code += this.convSwitch(node)
524
530
  break
@@ -1043,6 +1049,20 @@ try {
1043
1049
  return this.convLineno(node, false) + code
1044
1050
  }
1045
1051
 
1052
+ convAtohantei (node) {
1053
+ const id = this.loop_id++
1054
+ const varId = `$nako_i${id}`
1055
+ const cond = this._convGen(node.cond, true)
1056
+ const block = this.convGenLoop(node.block)
1057
+ const code =
1058
+ `for(;;) {\n` +
1059
+ ` ${block}\n` +
1060
+ ` let ${varId} = ${cond};\n` +
1061
+ ` if (${varId}) { continue } else { break }\n` +
1062
+ '}\n\n'
1063
+ return this.convLineno(node, false) + code
1064
+ }
1065
+
1046
1066
  convSwitch (node) {
1047
1067
  const value = this._convGen(node.value, true)
1048
1068
  const cases = node.cases
@@ -1376,6 +1396,24 @@ try {
1376
1396
  return `(${left} ${op} ${right})`
1377
1397
  }
1378
1398
 
1399
+ convInc (node) {
1400
+ // もし値が省略されていたら、変数「それ」に代入する
1401
+ let value = null
1402
+ if (this.speedMode.invalidSore === 0) { value = this.varname('それ') }
1403
+ if (node.value) { value = this._convGen(node.value, true) }
1404
+ if (value == null) {
1405
+ throw NakoSyntaxError.fromNode('加算する先の変数名がありません。', node)
1406
+ }
1407
+ // 変数名
1408
+ const name = node.name.value
1409
+ const res = this.findVar(name)
1410
+ let code = ''
1411
+ if (res === null) { this.varsSet.names.add(name) }
1412
+ code += `if (typeof(${this.varname(name)}) === 'undefined') { ${this.varname(name)} = 0; }`
1413
+ code += `${this.varname(name)} += ${value}`
1414
+ return ';' + this.convLineno(node, false) + code + '\n'
1415
+ }
1416
+
1379
1417
  convLet (node) {
1380
1418
  // もし値が省略されていたら、変数「それ」に代入する
1381
1419
  let value = null
@@ -154,7 +154,7 @@ function cbWordParser (src, isTrimOkurigana = true) {
154
154
  // 助詞の判定
155
155
  const j = josiRE.exec(src)
156
156
  if (j) {
157
- josi = j[0]
157
+ josi = j[0].replace(/^\s+/, '')
158
158
  src = src.substr(j[0].length)
159
159
  // 助詞の直後にある「,」を飛ばす #877
160
160
  if (src.charAt(0) === ',') { src = src.substr(1) }
@@ -229,7 +229,7 @@ function cbString (beginTag, closeTag, src) {
229
229
  // 文字列直後の助詞を取得
230
230
  const j = josiRE.exec(src)
231
231
  if (j) {
232
- josi = j[0]
232
+ josi = j[0].replace(/^\s+/, '')
233
233
  src = src.substr(j[0].length)
234
234
  // 助詞の後のカンマ #877
235
235
  if (src.charAt(0) === ',') { src = src.substr(1) }
@@ -96,25 +96,20 @@ class NakoParser extends NakoParserBase {
96
96
  /** @returns {Ast | null} */
97
97
  ySentence () {
98
98
  const map = this.peekSourceMap()
99
+
99
100
  // 最初の語句が決まっている構文
100
101
  if (this.check('eol')) { return this.yEOL() }
101
102
  if (this.check('もし')) { return this.yIF() }
103
+ if (this.check('後判定')) { return this.yAtohantei() }
102
104
  if (this.check('エラー監視')) { return this.yTryExcept() }
103
105
  if (this.check('逐次実行')) { return this.yTikuji() }
104
106
  if (this.accept(['抜ける'])) { return { type: 'break', josi: '', ...map, end: this.peekSourceMap() } }
105
107
  if (this.accept(['続ける'])) { return { type: 'continue', josi: '', ...map, end: this.peekSourceMap() } }
106
- if (this.accept(['require', 'string', '取込'])) {
107
- return {
108
- type: 'require',
109
- value: this.y[1].value,
110
- josi: '',
111
- ...map,
112
- end: this.peekSourceMap()
113
- }
114
- }
108
+ if (this.accept(['require', 'string', '取込'])) { return { type: 'require', value: this.y[1].value, josi: '', ...map, end: this.peekSourceMap() } }
115
109
  if (this.accept(['not', '非同期モード'])) { return this.yASyncMode() }
116
110
  if (this.accept(['not', 'DNCLモード'])) { return this.yDNCLMode() }
117
111
  if (this.accept(['not', 'string', 'モード設定'])) { return this.ySetGenMode(this.y[1].value) }
112
+
118
113
  // 関数呼び出し演算子
119
114
  if (this.check2(['func', '←'])) { return this.yCallOp() }
120
115
  if (this.check2(['func', 'eq'])) {
@@ -128,7 +123,9 @@ class NakoParser extends NakoParserBase {
128
123
  if (this.accept([this.yLet])) { return this.y[0] }
129
124
  if (this.accept([this.yDefTest])) { return this.y[0] }
130
125
  if (this.accept([this.yDefFunc])) { return this.y[0] }
131
- if (this.accept([this.yCall])) { // 関数呼び出しの他、各種構文の実装
126
+
127
+ // 関数呼び出しの他、各種構文の実装
128
+ if (this.accept([this.yCall])) {
132
129
  const c1 = this.y[0]
133
130
  if (c1.josi === 'して') { // 連文をblockとして接続する(もし構文、逐次実行構文などのため)
134
131
  const c2 = this.ySentence()
@@ -281,12 +278,13 @@ class NakoParser extends NakoParserBase {
281
278
  let a = this.yGetArg()
282
279
  if (!a) { return null }
283
280
  // console.log('yIFCond=', a, this.peek())
281
+
284
282
  // チェック : AがBならば
285
283
  if (a.josi === 'が') {
286
284
  const tmpI = this.index
287
285
  const b = this.yGetArg()
288
286
  const naraba = this.get()
289
- if (b && b.type !== 'func' && naraba && naraba.type === 'ならば') {
287
+ if ((b && b.type !== 'func') && (naraba && naraba.type === 'ならば')) {
290
288
  return {
291
289
  type: 'op',
292
290
  operator: (naraba.value === 'でなければ') ? 'noteq' : 'eq',
@@ -733,6 +731,42 @@ class NakoParser extends NakoParserBase {
733
731
  }
734
732
  }
735
733
 
734
+ /** @returns {Ast | null} */
735
+ yAtohantei () {
736
+ const map = this.peekSourceMap()
737
+ if (this.check('後判定')) { this.get() } // skip 後判定
738
+ if (this.check('繰返')) { this.get() } // skip 繰り返す
739
+ if (this.check('ここから')) { this.get() }
740
+ const block = this.yBlock()
741
+ if (this.check('ここまで')) { this.get() }
742
+ if (this.check('comma')) { this.get() }
743
+ let cond = this.yGetArg() // 条件
744
+ let bUntil = false
745
+ const t = this.peek()
746
+ if (t && t.value === 'なる' && (t.josi === 'まで' || t.josi === 'までの')) {
747
+ this.get() // skip なるまで
748
+ bUntil = true
749
+ }
750
+ if (this.check('間')) { this.get() } // skip 間
751
+ if (bUntil) { // 条件を反転する
752
+ cond = {
753
+ type: 'not',
754
+ value: cond,
755
+ josi: '',
756
+ ...map,
757
+ end: this.peekSourceMap()
758
+ }
759
+ }
760
+ return {
761
+ type: 'atohantei',
762
+ cond,
763
+ block,
764
+ josi: '',
765
+ ...map,
766
+ end: this.peekSourceMap()
767
+ }
768
+ }
769
+
736
770
  /** @returns {Ast | null} */
737
771
  yFor () {
738
772
  const map = this.peekSourceMap()
@@ -747,7 +781,9 @@ class NakoParser extends NakoParserBase {
747
781
  if (incdec.type == 'word' && (incdec.value === '増' || incdec.value === '減')) {
748
782
  kurikaesu.type = incdec.value + kurikaesu.type
749
783
  // ↑ typeを増繰返 | 減繰返 に変換
750
- } else {
784
+ }
785
+ // 普通の繰り返しの場合
786
+ else {
751
787
  this.stack.push(incdec) // 違ったので改めて追加
752
788
  }
753
789
  let vInc = null
@@ -944,52 +980,107 @@ class NakoParser extends NakoParserBase {
944
980
  }
945
981
  }
946
982
 
983
+ /** @returns {import('./nako3').Ast | null | undefined} */
984
+ yDainyu () {
985
+ const map = this.peekSourceMap()
986
+ const dainyu = this.get() // 代入
987
+ if (dainyu === null) { return null }
988
+ const value = this.popStack(['を'])
989
+ const word = this.popStack(['へ', 'に'])
990
+ if (!word || (word.type !== 'word' && word.type !== 'func' && word.type !== '配列参照')) {
991
+ throw NakoSyntaxError.fromNode('代入文で代入先の変数が見当たりません。『(変数名)に(値)を代入』のように使います。', dainyu)
992
+ }
993
+ // 配列への代入
994
+ if (word.type === '配列参照') {
995
+ return {
996
+ type: 'let_array',
997
+ name: word.name,
998
+ index: word.index,
999
+ value: value,
1000
+ josi: '',
1001
+ checkInit: this.flagCheckArrayInit,
1002
+ ...map, end: this.peekSourceMap()
1003
+ };
1004
+ }
1005
+ // 一般的な変数への代入
1006
+ return {
1007
+ type: 'let', name: word,
1008
+ value: value, josi: '',
1009
+ ...map, end: this.peekSourceMap()
1010
+ }
1011
+ }
1012
+
1013
+ /** @returns {import('./nako3').Ast | null | undefined} */
1014
+ ySadameru () {
1015
+ const map = this.peekSourceMap()
1016
+ const sadameru = this.get() // 定める
1017
+ if (sadameru === null) { return null }
1018
+ const word = this.popStack(['を'])
1019
+ const value = this.popStack(['へ', 'に'])
1020
+ console.log(word)
1021
+ if (!word || (word.type !== 'word' && word.type !== 'func' && word.type !== '配列参照')) {
1022
+ throw NakoSyntaxError.fromNode('『定める』文で定数が見当たりません。『(定数名)を(値)に定める』のように使います。', sadameru)
1023
+ }
1024
+ return {
1025
+ type: 'def_local_var', name: word, vartype: '定数',
1026
+ value: value, josi: '',
1027
+ ...map, end: this.peekSourceMap()
1028
+ };
1029
+ }
1030
+
1031
+ /** @returns {import('./nako3').Ast | null | undefined} */
1032
+ yIncDec () {
1033
+ const map = this.peekSourceMap()
1034
+ const action = this.get() // (増やす|減らす)
1035
+ if (action === null) { return null }
1036
+
1037
+ // 『Nずつ増やして繰り返す』文か?
1038
+ if (this.check('繰返')) {
1039
+ this.pushStack({type: 'word', value: action.value, josi: action.josi, ...map, end: this.peekSourceMap})
1040
+ return this.yFor();
1041
+ }
1042
+
1043
+ // スタックから引数をポップ
1044
+ let value = this.popStack(['だけ', '']);
1045
+ const word = this.popStack(['を'])
1046
+ if (!word || (word.type !== 'word' && word.type !== '配列参照')) {
1047
+ throw NakoSyntaxError.fromNode(
1048
+ `『${action.type}』文で定数が見当たりません。『(変数名)を(値)だけ${action.type}』のように使います。`,
1049
+ action)
1050
+ }
1051
+
1052
+ // 減らすなら-1かける
1053
+ if (action.value === '減') {
1054
+ value = { type: 'op', operator: '*', left: value, right: {type: 'number', value: -1, line: action.line}, josi: '', ...map }
1055
+ }
1056
+
1057
+ return {
1058
+ type: 'inc',
1059
+ name: word,
1060
+ value: value,
1061
+ josi: action.josi,
1062
+ ...map, end: this.peekSourceMap()
1063
+ };
1064
+ }
1065
+
947
1066
  /** @returns {import('./nako3').Ast | null | undefined} */
948
1067
  yCall () {
949
1068
  if (this.isEOF()) { return null }
1069
+
1070
+ // スタックに積んでいく
950
1071
  while (!this.isEOF()) {
951
- const map = this.peekSourceMap()
1072
+ if (this.check('ここから')) { this.get() }
952
1073
  // 代入
953
- if (this.check('代入')) {
954
- const dainyu = this.get()
955
- const value = this.popStack(['を'])
956
- const word = this.popStack(['へ', 'に'])
957
- if (!word || (word.type !== 'word' && word.type !== 'func' && word.type !== '配列参照')) {
958
- throw NakoSyntaxError.fromNode('代入文で代入先の変数が見当たりません。', dainyu)
959
- }
960
-
961
- switch (word.type) {
962
- case '配列参照': // 配列への代入
963
- return { type: 'let_array', name: word.name, index: word.index, value: value, josi: '', checkInit: this.flagCheckArrayInit, ...map, end: this.peekSourceMap() }
964
- default:
965
- return { type: 'let', name: word, value: value, josi: '', ...map, end: this.peekSourceMap() }
966
- }
967
- }
968
- if (this.check('定める')) {
969
- const dainyu = this.get()
970
- const word = this.popStack(['を'])
971
- const value = this.popStack(['に'])
972
- if (!word || word.type !== 'word') {
973
- throw NakoSyntaxError.fromNode('代入文で代入先の変数が見当たりません。', dainyu)
974
- }
975
- return {
976
- type: 'def_local_var',
977
- name: word,
978
- vartype: '定数',
979
- value: value,
980
- ...map,
981
- end: this.peekSourceMap()
982
- }
983
- }
1074
+ if (this.check('代入')) { return this.yDainyu(); }
1075
+ if (this.check('定める')) { return this.ySadameru(); }
984
1076
  // 制御構文
985
- if (this.check('ここから')) { this.get() }
986
1077
  if (this.check('回')) { return this.yRepeatTime() }
987
1078
  if (this.check('間')) { return this.yWhile() }
988
1079
  if (this.check('繰返') || this.check('増繰返') || this.check('減繰返')) { return this.yFor() }
989
1080
  if (this.check('反復')) { return this.yForEach() }
990
1081
  if (this.check('条件分岐')) { return this.ySwitch() }
991
- // 戻す
992
1082
  if (this.check('戻る')) { return this.yReturn() }
1083
+ if (this.check('増') || this.check('減')) { return this.yIncDec() }
993
1084
  // C言語風関数
994
1085
  if (this.check2([['func', 'word'], '(']) && this.peek().josi === '') { // C言語風
995
1086
  const t = this.yValue()
@@ -1020,6 +1111,7 @@ class NakoParser extends NakoParserBase {
1020
1111
  }
1021
1112
  break
1022
1113
  } // end of while
1114
+
1023
1115
  // 助詞が余ってしまった場合
1024
1116
  if (this.stack.length > 0) {
1025
1117
  this.logger.debug('--- stack dump ---\n' + JSON.stringify(this.stack, null, 2) + '\npeek: ' + JSON.stringify(this.peek(), null, 2))
@@ -11,6 +11,7 @@ module.exports = {
11
11
  '繰返': '繰返',
12
12
  '増繰返': '増繰返', // (#1140)
13
13
  '減繰返': '減繰返',
14
+ '後判定': '後判定', // (#1147)
14
15
  '反復': '反復',
15
16
  '抜': '抜ける',
16
17
  '続': '続ける',
@@ -23,6 +24,8 @@ module.exports = {
23
24
  '定': '定める',
24
25
  '逐次実行': '逐次実行',
25
26
  '条件分岐': '条件分岐',
27
+ '増': '増',
28
+ '減': '減',
26
29
  '変数': '変数',
27
30
  '定数': '定数',
28
31
  'エラー監視': 'エラー監視', // 例外処理:エラーならばと対
@@ -1,8 +1,8 @@
1
1
  // なでしこバージョン
2
2
  const nakoVersion = {
3
- version: '3.2.36',
3
+ version: '3.2.40',
4
4
  major: 3,
5
5
  minor: 2,
6
- patch: 36
6
+ patch: 40
7
7
  }
8
8
  module.exports = nakoVersion
@@ -220,6 +220,14 @@ const PluginMath = {
220
220
  return Math.floor(Math.random() * a)
221
221
  }
222
222
  },
223
+ '乱数範囲': { // @AからBまでの範囲の乱数を返す // @らんすうはんい
224
+ type: 'func',
225
+ josi: [['から'],['までの','の']],
226
+ pure: true,
227
+ fn: function (a, b) {
228
+ return (Math.floor(Math.random() * (b - a + 1)) + a)
229
+ }
230
+ },
223
231
  'SQRT': { // @Aの平方根を返す // @SQRT
224
232
  type: 'func',
225
233
  josi: [['の']],