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.
- package/package.json +1 -1
- package/release/_hash.txt +24 -24
- package/release/_script-tags.txt +13 -13
- package/release/command.json +1 -1
- package/release/command.json.js +1 -1
- package/release/command_cnako3.json +1 -1
- package/release/command_list.json +1 -1
- package/release/editor.js +1 -1
- package/release/nako_gen_async.js +1 -1
- package/release/stats.json +1 -1
- package/release/version.js +1 -1
- package/release/wnako3.js +1 -1
- package/release/wnako3webworker.js +1 -1
- package/release/yoyakugo.json +3 -0
- package/src/nako_from_dncl.js +97 -88
- package/src/nako_gen.js +38 -0
- package/src/nako_lex_rules.js +2 -2
- package/src/nako_parser3.js +138 -46
- package/src/nako_reserved_words.js +3 -0
- package/src/nako_version.js +2 -2
- package/src/plugin_math.js +8 -0
- package/src/plugin_system.js +57 -0
- package/test/common/basic_test.js +11 -0
- package/test/common/dncl_test.js +24 -0
- package/test/common/lex_test.js +3 -0
- package/test/common/plugin_system_test.js +4 -0
package/release/yoyakugo.json
CHANGED
package/src/nako_from_dncl.js
CHANGED
|
@@ -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
|
-
|
|
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 +=
|
|
93
|
-
src = src.substring(1)
|
|
161
|
+
result += nextChar()
|
|
94
162
|
continue
|
|
95
163
|
}
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
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
|
package/src/nako_lex_rules.js
CHANGED
|
@@ -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) }
|
package/src/nako_parser3.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
1072
|
+
if (this.check('ここから')) { this.get() }
|
|
952
1073
|
// 代入
|
|
953
|
-
if (this.check('代入')) {
|
|
954
|
-
|
|
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
|
'エラー監視': 'エラー監視', // 例外処理:エラーならばと対
|
package/src/nako_version.js
CHANGED
package/src/plugin_math.js
CHANGED
|
@@ -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: [['の']],
|