nadesiko3 3.2.36 → 3.2.37

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.
@@ -18,6 +18,8 @@
18
18
  "定",
19
19
  "逐次実行",
20
20
  "条件分岐",
21
+ "増",
22
+ "減",
21
23
  "変数",
22
24
  "定数",
23
25
  "エラー監視",
@@ -77,20 +77,65 @@ function dncl2nako(src, filename) {
77
77
  'を実行し,そうでなければ': '違えば',
78
78
  'を実行し、そうでなければ': '違えば',
79
79
  'を繰り返す': 'ここまで',
80
- '改行なしで表示': '継続表示',
80
+ '改行なしで表示': '連続無改行表示',
81
81
  'のすべての値を0にする': '=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]',
82
82
  'ずつ増やしながら':'ずつ増やし繰り返す',
83
83
  'ずつ減らしながら':'ずつ減らし繰り返す',
84
84
  }
85
-
85
+ let peekChar = () => src.charAt(0)
86
+ let nextChar = () => {
87
+ let ch = src.charAt(0)
88
+ src = src.substring(1)
89
+ return ch
90
+ }
91
+ // 文字列を判定するフラグ
92
+ let flagStr = false
93
+ let poolStr = ''
94
+ let endStr = ''
95
+ // 結果
86
96
  let result = ''
87
97
  while (src !='') {
88
98
  // 代入記号を変更
89
99
  const ch = src.charAt(0)
100
+ if (flagStr) {
101
+ if (ch === endStr) {
102
+ result += poolStr + endStr
103
+ poolStr = ''
104
+ flagStr = false
105
+ nextChar()
106
+ continue
107
+ }
108
+ poolStr += nextChar()
109
+ continue
110
+ }
111
+ // 文字列?
112
+ if (ch == '"') {
113
+ flagStr = true
114
+ endStr = '"'
115
+ poolStr = nextChar()
116
+ continue
117
+ }
118
+ if (ch == '「') {
119
+ flagStr = true
120
+ endStr = '」'
121
+ poolStr = nextChar()
122
+ continue
123
+ }
124
+ if (ch == '『') {
125
+ flagStr = true
126
+ endStr = '』'
127
+ poolStr = nextChar()
128
+ continue
129
+ }
90
130
  // 空白を飛ばす
91
131
  if (ch === ' ' || ch === ' ' || ch == '\t') {
92
- result += ch
93
- src = src.substring(1)
132
+ result += nextChar()
133
+ continue
134
+ }
135
+ // 表示を連続表示に置き換える
136
+ if (src.substring(0, 3) === 'を表示') {
137
+ result += 'を連続表示'
138
+ src = src.substring(3)
94
139
  continue
95
140
  }
96
141
  // 1行先読み
@@ -101,74 +146,6 @@ function dncl2nako(src, filename) {
101
146
  } else {
102
147
  line = src
103
148
  }
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)
116
- continue
117
- }
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
170
- }
171
- }
172
149
  // 『もしj>hakosuならばhakosu←jを実行する』のような単文のもし文
173
150
  const rif = line.match(/^もし(.+)を実行する(。|.)*/)
174
151
  if (rif) {
@@ -193,8 +170,7 @@ function dncl2nako(src, filename) {
193
170
  }
194
171
 
195
172
  // 1文字削る
196
- src = src.substring(1)
197
- result += ch
173
+ result += nextChar()
198
174
  }
199
175
  return result
200
176
  }
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)
@@ -1376,6 +1379,24 @@ try {
1376
1379
  return `(${left} ${op} ${right})`
1377
1380
  }
1378
1381
 
1382
+ convInc (node) {
1383
+ // もし値が省略されていたら、変数「それ」に代入する
1384
+ let value = null
1385
+ if (this.speedMode.invalidSore === 0) { value = this.varname('それ') }
1386
+ if (node.value) { value = this._convGen(node.value, true) }
1387
+ if (value == null) {
1388
+ throw NakoSyntaxError.fromNode('加算する先の変数名がありません。', node)
1389
+ }
1390
+ // 変数名
1391
+ const name = node.name.value
1392
+ const res = this.findVar(name)
1393
+ let code = ''
1394
+ if (res === null) { this.varsSet.names.add(name) }
1395
+ code += `if (typeof(${this.varname(name)}) === 'undefined') { ${this.varname(name)} = 0; }`
1396
+ code += `${this.varname(name)} += ${value}`
1397
+ return ';' + this.convLineno(node, false) + code + '\n'
1398
+ }
1399
+
1379
1400
  convLet (node) {
1380
1401
  // もし値が省略されていたら、変数「それ」に代入する
1381
1402
  let value = null
@@ -96,6 +96,7 @@ 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,18 +104,11 @@ class NakoParser extends NakoParserBase {
103
104
  if (this.check('逐次実行')) { return this.yTikuji() }
104
105
  if (this.accept(['抜ける'])) { return { type: 'break', josi: '', ...map, end: this.peekSourceMap() } }
105
106
  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
- }
107
+ if (this.accept(['require', 'string', '取込'])) { return { type: 'require', value: this.y[1].value, josi: '', ...map, end: this.peekSourceMap() } }
115
108
  if (this.accept(['not', '非同期モード'])) { return this.yASyncMode() }
116
109
  if (this.accept(['not', 'DNCLモード'])) { return this.yDNCLMode() }
117
110
  if (this.accept(['not', 'string', 'モード設定'])) { return this.ySetGenMode(this.y[1].value) }
111
+
118
112
  // 関数呼び出し演算子
119
113
  if (this.check2(['func', '←'])) { return this.yCallOp() }
120
114
  if (this.check2(['func', 'eq'])) {
@@ -128,7 +122,9 @@ class NakoParser extends NakoParserBase {
128
122
  if (this.accept([this.yLet])) { return this.y[0] }
129
123
  if (this.accept([this.yDefTest])) { return this.y[0] }
130
124
  if (this.accept([this.yDefFunc])) { return this.y[0] }
131
- if (this.accept([this.yCall])) { // 関数呼び出しの他、各種構文の実装
125
+
126
+ // 関数呼び出しの他、各種構文の実装
127
+ if (this.accept([this.yCall])) {
132
128
  const c1 = this.y[0]
133
129
  if (c1.josi === 'して') { // 連文をblockとして接続する(もし構文、逐次実行構文などのため)
134
130
  const c2 = this.ySentence()
@@ -944,52 +940,107 @@ class NakoParser extends NakoParserBase {
944
940
  }
945
941
  }
946
942
 
943
+ /** @returns {import('./nako3').Ast | null | undefined} */
944
+ yDainyu () {
945
+ const map = this.peekSourceMap()
946
+ const dainyu = this.get() // 代入
947
+ if (dainyu === null) { return null }
948
+ const value = this.popStack(['を'])
949
+ const word = this.popStack(['へ', 'に'])
950
+ if (!word || (word.type !== 'word' && word.type !== 'func' && word.type !== '配列参照')) {
951
+ throw NakoSyntaxError.fromNode('代入文で代入先の変数が見当たりません。『(変数名)に(値)を代入』のように使います。', dainyu)
952
+ }
953
+ // 配列への代入
954
+ if (word.type === '配列参照') {
955
+ return {
956
+ type: 'let_array',
957
+ name: word.name,
958
+ index: word.index,
959
+ value: value,
960
+ josi: '',
961
+ checkInit: this.flagCheckArrayInit,
962
+ ...map, end: this.peekSourceMap()
963
+ };
964
+ }
965
+ // 一般的な変数への代入
966
+ return {
967
+ type: 'let', name: word,
968
+ value: value, josi: '',
969
+ ...map, end: this.peekSourceMap()
970
+ }
971
+ }
972
+
973
+ /** @returns {import('./nako3').Ast | null | undefined} */
974
+ ySadameru () {
975
+ const map = this.peekSourceMap()
976
+ const sadameru = this.get() // 定める
977
+ if (sadameru === null) { return null }
978
+ const word = this.popStack(['を'])
979
+ const value = this.popStack(['へ', 'に'])
980
+ console.log(word)
981
+ if (!word || (word.type !== 'word' && word.type !== 'func' && word.type !== '配列参照')) {
982
+ throw NakoSyntaxError.fromNode('『定める』文で定数が見当たりません。『(定数名)を(値)に定める』のように使います。', sadameru)
983
+ }
984
+ return {
985
+ type: 'def_local_var', name: word, vartype: '定数',
986
+ value: value, josi: '',
987
+ ...map, end: this.peekSourceMap()
988
+ };
989
+ }
990
+
991
+ /** @returns {import('./nako3').Ast | null | undefined} */
992
+ yIncDec () {
993
+ const map = this.peekSourceMap()
994
+ const action = this.get() // (増やす|減らす)
995
+ if (action === null) { return null }
996
+
997
+ // 『Nずつ増やして繰り返す』文か?
998
+ if (this.check('繰返')) {
999
+ this.pushStack({type: 'word', value: action.value, josi: action.josi, ...map, end: this.peekSourceMap})
1000
+ return this.yFor();
1001
+ }
1002
+
1003
+ // スタックから引数をポップ
1004
+ let value = this.popStack(['だけ', '']);
1005
+ const word = this.popStack(['を'])
1006
+ if (!word || (word.type !== 'word' && word.type !== '配列参照')) {
1007
+ throw NakoSyntaxError.fromNode(
1008
+ `『${action.type}』文で定数が見当たりません。『(変数名)を(値)だけ${action.type}』のように使います。`,
1009
+ action)
1010
+ }
1011
+
1012
+ // 減らすなら-1かける
1013
+ if (action.value === '減') {
1014
+ value = { type: 'op', operator: '*', left: value, right: {type: 'number', value: -1, line: action.line}, josi: '', ...map }
1015
+ }
1016
+
1017
+ return {
1018
+ type: 'inc',
1019
+ name: word,
1020
+ value: value,
1021
+ josi: action.josi,
1022
+ ...map, end: this.peekSourceMap()
1023
+ };
1024
+ }
1025
+
947
1026
  /** @returns {import('./nako3').Ast | null | undefined} */
948
1027
  yCall () {
949
1028
  if (this.isEOF()) { return null }
1029
+
1030
+ // スタックに積んでいく
950
1031
  while (!this.isEOF()) {
951
- const map = this.peekSourceMap()
1032
+ if (this.check('ここから')) { this.get() }
952
1033
  // 代入
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
- }
1034
+ if (this.check('代入')) { return this.yDainyu(); }
1035
+ if (this.check('定める')) { return this.ySadameru(); }
984
1036
  // 制御構文
985
- if (this.check('ここから')) { this.get() }
986
1037
  if (this.check('回')) { return this.yRepeatTime() }
987
1038
  if (this.check('間')) { return this.yWhile() }
988
1039
  if (this.check('繰返') || this.check('増繰返') || this.check('減繰返')) { return this.yFor() }
989
1040
  if (this.check('反復')) { return this.yForEach() }
990
1041
  if (this.check('条件分岐')) { return this.ySwitch() }
991
- // 戻す
992
1042
  if (this.check('戻る')) { return this.yReturn() }
1043
+ if (this.check('増') || this.check('減')) { return this.yIncDec() }
993
1044
  // C言語風関数
994
1045
  if (this.check2([['func', 'word'], '(']) && this.peek().josi === '') { // C言語風
995
1046
  const t = this.yValue()
@@ -1020,6 +1071,7 @@ class NakoParser extends NakoParserBase {
1020
1071
  }
1021
1072
  break
1022
1073
  } // end of while
1074
+
1023
1075
  // 助詞が余ってしまった場合
1024
1076
  if (this.stack.length > 0) {
1025
1077
  this.logger.debug('--- stack dump ---\n' + JSON.stringify(this.stack, null, 2) + '\npeek: ' + JSON.stringify(this.peek(), null, 2))
@@ -23,6 +23,8 @@ module.exports = {
23
23
  '定': '定める',
24
24
  '逐次実行': '逐次実行',
25
25
  '条件分岐': '条件分岐',
26
+ '増': '増',
27
+ '減': '減',
26
28
  '変数': '変数',
27
29
  '定数': '定数',
28
30
  'エラー監視': 'エラー監視', // 例外処理:エラーならばと対
@@ -1,8 +1,8 @@
1
1
  // なでしこバージョン
2
2
  const nakoVersion = {
3
- version: '3.2.36',
3
+ version: '3.2.37',
4
4
  major: 3,
5
5
  minor: 2,
6
- patch: 36
6
+ patch: 37
7
7
  }
8
8
  module.exports = nakoVersion
@@ -202,6 +202,30 @@ const PluginSystem = {
202
202
  },
203
203
  return_none: true
204
204
  },
205
+ '連続表示': { // @引数に指定した引数を全て表示する // @れんぞく表示
206
+ type: 'func',
207
+ josi: [['と', 'を']],
208
+ isVariableJosi: true,
209
+ pure: true,
210
+ fn: function (...a) {
211
+ const sys = a.pop()
212
+ const v = a.join('')
213
+ sys.__exec('表示', [v, sys])
214
+ },
215
+ return_none: true
216
+ },
217
+ '連続無改行表示': { // @引数に指定した引数を全て表示する(改行しない) // @れんぞくむかいぎょうひょうじ
218
+ type: 'func',
219
+ josi: [['と', 'を']],
220
+ isVariableJosi: true,
221
+ pure: true,
222
+ fn: function (...a) {
223
+ const sys = a.pop()
224
+ const v = a.join('')
225
+ sys.__exec('継続表示', [v, sys])
226
+ },
227
+ return_none: true
228
+ },
205
229
  '表示ログ': { type: 'const', value: '' }, // @ひょうじろぐ
206
230
  '表示ログクリア': { // @表示ログを空にする // @ひょうじろぐくりあ
207
231
  type: 'func',
@@ -314,4 +314,15 @@ describe('basic', () => {
314
314
  it('もし文で「ならば」の前の空行でエラー #1079', () => {
315
315
  cmp('A=5;もし、A > 3 ならば「OK」と表示;', 'OK')
316
316
  })
317
+ it('『増やす』『減らす』文の追加 #1145', () => {
318
+ // 増やす
319
+ cmp('A=0;Aを1増やす。Aと表示;', '1')
320
+ cmp('A=0;Aを123だけ増やす。Aと表示;', '123')
321
+ // 減らす
322
+ cmp('A=10;Aを1減らす。Aと表示;', '9')
323
+ cmp('A=10;Aを5だけ減らす。Aと表示;', '5')
324
+ // 初期化しないで使うと0になる
325
+ cmp('Aを3増やす;Aと表示;', '3')
326
+ cmp('Aを3減らす;Aと表示;', '-3')
327
+ })
317
328
  })
@@ -58,6 +58,8 @@ describe('dncl (#1140)', () => {
58
58
  it('表示エミュレート', () => {
59
59
  cmpNako('!DNCLモード\nx←10,y←20\nxと"-"とyを表示する。', '10-20')
60
60
  cmpNako('!DNCLモード\nx←10,y←20\n"("とxと","とyと")"を表示する。', '(10,20)')
61
+ cmpNako('!DNCLモード\nx←10,y←20,z←30\n"("とxと","とyと","とzと")"を表示する。', '(10,20,30)')
62
+ cmpNako('!DNCLモード\nx←10,y←20\n「<」とxと","とyと「>」を表示する。', '<10,20>')
61
63
  })
62
64
  })
63
65