nadesiko3 3.3.63 → 3.3.64
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/batch/command.txt +15 -15
- package/core/package.json +4 -1
- package/core/src/nako3.mjs +5 -2
- package/core/src/nako3.mts +5 -4
- package/core/src/nako_core_version.mjs +2 -2
- package/core/src/nako_core_version.mts +2 -2
- package/core/src/nako_from_dncl.mjs +0 -254
- package/core/src/nako_from_dncl.mts +0 -247
- package/core/src/nako_from_dncl2.mjs +313 -0
- package/core/src/nako_from_dncl2.mts +299 -0
- package/core/src/nako_gen.mjs +1 -1
- package/core/src/nako_gen.mts +1 -1
- package/core/src/nako_indent_inline.mjs +152 -34
- package/core/src/nako_indent_inline.mts +132 -34
- package/core/src/nako_lex_rules.mjs +3 -2
- package/core/src/nako_lex_rules.mts +3 -2
- package/core/src/nako_lexer.mjs +27 -3
- package/core/src/nako_lexer.mts +27 -3
- package/core/src/nako_parser3.mjs +45 -14
- package/core/src/nako_parser3.mts +40 -18
- package/core/src/nako_prepare.mjs +13 -0
- package/core/src/nako_prepare.mts +11 -0
- package/core/src/nako_tools.mjs +53 -0
- package/core/src/nako_tools.mts +46 -0
- package/core/test/basic_test.mjs +16 -8
- package/core/test/dncl2_test.mjs +207 -0
- package/core/test/flow_test.mjs +42 -0
- package/core/test/indent_test.mjs +74 -50
- package/core/test/inline_indent_test.mjs +47 -0
- package/core/test/lex_test.mjs +1 -0
- package/demo/js/a.js +30 -0
- package/package.json +1 -1
- package/release/_hash.txt +16 -16
- package/release/_script-tags.txt +14 -14
- 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/src/cnako3mod.mjs +43 -33
- package/src/cnako3mod.mts +45 -35
- package/src/nako_version.mjs +2 -2
- package/src/nako_version.mts +2 -2
- package/src/plugin_browser_canvas.mjs +5 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DNCL ver2 に対応する構文
|
|
3
|
+
*/
|
|
4
|
+
// import { NakoIndentError } from './nako_errors.mjs'
|
|
5
|
+
import { Token, NewEmptyToken } from './nako_types.mjs'
|
|
6
|
+
import { joinTokenLines, splitTokens } from './nako_indent_inline.mjs'
|
|
7
|
+
import { newToken, debugTokens } from './nako_tools.mjs'
|
|
8
|
+
|
|
9
|
+
const IS_DEBUG = false
|
|
10
|
+
const DNCL_ARRAY_INIT_COUNT = 30
|
|
11
|
+
|
|
12
|
+
// DNCL2モードのキーワード
|
|
13
|
+
const DNCL2_KEYWORDS = ['!DNCL2モード', '💡DNCL2モード', '!DNCL2', '💡DNCL2']
|
|
14
|
+
|
|
15
|
+
// 単純な置換チェック
|
|
16
|
+
const DNCL_SIMPLES: { [key: string]: string[] } = {
|
|
17
|
+
'←:←': ['eq', '='],
|
|
18
|
+
'÷:÷': ['÷÷', '÷÷'],
|
|
19
|
+
'{:{': ['[', '['],
|
|
20
|
+
'}:}': [']', ']'],
|
|
21
|
+
'word:and': ['and', 'かつ'],
|
|
22
|
+
'word:or': ['or', 'または'],
|
|
23
|
+
'word:乱数': ['word', '乱数範囲'],
|
|
24
|
+
'word:表示': ['word', '連続表示']
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* DNCLのソースコードをなでしこに変換する
|
|
29
|
+
*/
|
|
30
|
+
export function convertDNCL2 (tokens: Token[]): Token[] {
|
|
31
|
+
if (!useDNCL2mode(tokens)) { return tokens }
|
|
32
|
+
|
|
33
|
+
// 一行ずつに分ける
|
|
34
|
+
const lines = splitTokens(tokens, 'eol')
|
|
35
|
+
for (let i = 0; i < lines.length; i++) {
|
|
36
|
+
const line = lines[i]
|
|
37
|
+
if (line.length <= 1) { continue } // 空行は飛ばす
|
|
38
|
+
|
|
39
|
+
// --- 制御構文の変換 ---
|
|
40
|
+
// もし(条件)でないならば → もし(条件)でなければ
|
|
41
|
+
const nai = findTokens(line, ['word:ない'])
|
|
42
|
+
if (nai >= 1) {
|
|
43
|
+
const tt = line[nai]
|
|
44
|
+
if (tt.josi === 'ならば') {
|
|
45
|
+
line[nai - 1].josi = 'でなければ'
|
|
46
|
+
line.splice(nai, 1)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// そうでなければ(そう|でなければ) → 違えば
|
|
50
|
+
for (let ni = 0; ni < line.length; ni++) {
|
|
51
|
+
const t = line[ni]
|
|
52
|
+
if (t.value === 'そう' && t.josi === 'でなければ') {
|
|
53
|
+
t.type = '違えば'
|
|
54
|
+
t.value = '違えば'
|
|
55
|
+
t.josi = ''
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// 'を実行し,そうでなければ': '違えば',
|
|
59
|
+
for (;;) {
|
|
60
|
+
const ni = findTokens(line, ['word:を実行', 'comma:,', 'word:そう'])
|
|
61
|
+
if (ni < 0) { break }
|
|
62
|
+
const sou = line[ni + 2]
|
|
63
|
+
if (sou.josi === 'でなければ') {
|
|
64
|
+
sou.type = '違えば'
|
|
65
|
+
sou.value = '違えば'
|
|
66
|
+
sou.josi = ''
|
|
67
|
+
line.splice(ni, 3, sou)
|
|
68
|
+
continue
|
|
69
|
+
} else if (sou.josi === 'で') {
|
|
70
|
+
const nakumosi = line[ni + 3]
|
|
71
|
+
if (nakumosi.value.substring(0, 4) === 'なくもし') {
|
|
72
|
+
sou.type = '違えば'
|
|
73
|
+
sou.value = '違えば'
|
|
74
|
+
sou.josi = ''
|
|
75
|
+
line.splice(ni, 3, sou)
|
|
76
|
+
if (nakumosi.value.length > 4) {
|
|
77
|
+
const nakumosiTudukiStr = nakumosi.value.substring(4)
|
|
78
|
+
const nakumosiToken = NewEmptyToken('word', nakumosiTudukiStr, nakumosi.indent, nakumosi.line, nakumosi.file)
|
|
79
|
+
if (nakumosiTudukiStr.match(/^\d/)) { nakumosiToken.type = 'number' }
|
|
80
|
+
line.splice(ni + 2, 0, nakumosiToken)
|
|
81
|
+
nakumosi.value = nakumosi.value.substring(0, 4)
|
|
82
|
+
}
|
|
83
|
+
nakumosi.type = 'もし'
|
|
84
|
+
nakumosi.value = 'もし'
|
|
85
|
+
nakumosi.josi = ''
|
|
86
|
+
continue
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
break
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Iを1から100まで1(ずつ)|増やしな(が)|ら
|
|
93
|
+
for (;;) {
|
|
94
|
+
const ni = findTokens(line, ['word:増', 'word:ら'])
|
|
95
|
+
if (ni < 0) { break }
|
|
96
|
+
const fu = line[ni]
|
|
97
|
+
fu.type = 'word'
|
|
98
|
+
fu.value = '増繰返'
|
|
99
|
+
fu.josi = ''
|
|
100
|
+
line.splice(ni, 2, fu)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Iを1から100まで1(ずつ)|増やしな(が)|ら
|
|
104
|
+
for (;;) {
|
|
105
|
+
const ni = findTokens(line, ['word:減', 'word:ら'])
|
|
106
|
+
if (ni < 0) { break }
|
|
107
|
+
const fu = line[ni]
|
|
108
|
+
fu.type = 'word'
|
|
109
|
+
fu.value = '減繰返'
|
|
110
|
+
fu.josi = ''
|
|
111
|
+
line.splice(ni, 2, fu)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Iを1から100まで1(ずつ)|増やしな(が)|ら繰り返(す)
|
|
115
|
+
for (;;) {
|
|
116
|
+
const ni = findTokens(line, ['word:増', 'word:ら繰り返'])
|
|
117
|
+
if (ni < 0) { break }
|
|
118
|
+
const fu = line[ni]
|
|
119
|
+
fu.type = 'word'
|
|
120
|
+
fu.value = '増繰返'
|
|
121
|
+
fu.josi = ''
|
|
122
|
+
line.splice(ni, 2, fu)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Iを1から100まで1(ずつ)|増やしな(が)|ら繰り返す
|
|
126
|
+
for (;;) {
|
|
127
|
+
const ni = findTokens(line, ['word:減', 'word:ら繰り返'])
|
|
128
|
+
if (ni < 0) { break }
|
|
129
|
+
const fu = line[ni]
|
|
130
|
+
fu.type = 'word'
|
|
131
|
+
fu.value = '減繰返'
|
|
132
|
+
fu.josi = ''
|
|
133
|
+
line.splice(ni, 2, fu)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// --- 配列変数周りの変換 ---
|
|
137
|
+
for (let i = 0; i < line.length; i++) {
|
|
138
|
+
// 配列|Hindoの|すべての|(要素に|値に)|10を|代入する
|
|
139
|
+
if (tokenEq([['word:配列', 'word:配列変数'], 'word', 'word:すべて', ['word:要素', 'word:値'], '*', 'word:代入'], line, i)) {
|
|
140
|
+
const varToken = line[i + 1]
|
|
141
|
+
varToken.josi = ''
|
|
142
|
+
const valToken = line[i + 4]
|
|
143
|
+
valToken.josi = ''
|
|
144
|
+
line.splice(i, 6,
|
|
145
|
+
varToken, newToken('eq', '=', varToken),
|
|
146
|
+
newToken('word', '掛'), newToken('(', '('),
|
|
147
|
+
newToken('[', '['), valToken, newToken(']', ']'), newToken('comma', ','),
|
|
148
|
+
newToken('number', DNCL_ARRAY_INIT_COUNT), newToken(')', ')'))
|
|
149
|
+
i += 6 // skip
|
|
150
|
+
}
|
|
151
|
+
// Hensuの|すべての|(要素を|値を)|0に|する
|
|
152
|
+
if (tokenEq(['word', 'word:すべて', ['word:要素', 'word:値'], ['number', 'string', 'word'], 'word:する'], line, i)) {
|
|
153
|
+
const varToken = line[i]
|
|
154
|
+
varToken.josi = ''
|
|
155
|
+
const valToken = line[i + 3]
|
|
156
|
+
valToken.josi = ''
|
|
157
|
+
line.splice(i, 5,
|
|
158
|
+
varToken, newToken('eq', '=', varToken),
|
|
159
|
+
newToken('word', '掛'), newToken('(', '('),
|
|
160
|
+
newToken('[', '['), valToken, newToken(']', ']'), newToken('comma', ','),
|
|
161
|
+
newToken('number', DNCL_ARRAY_INIT_COUNT), newToken(')', ')'))
|
|
162
|
+
}
|
|
163
|
+
// 配列変数 | xxを | 初期化する
|
|
164
|
+
if (tokenEq([['word:配列変数', 'word:配列'], 'word', 'word:初期化'], line, i)) {
|
|
165
|
+
const varToken = line[i + 1]
|
|
166
|
+
varToken.josi = ''
|
|
167
|
+
line.splice(i, 3,
|
|
168
|
+
varToken, newToken('eq', '=', varToken),
|
|
169
|
+
newToken('word', '掛'), newToken('(', '('),
|
|
170
|
+
newToken('[', '['), newToken('number', 0), newToken(']', ']'), newToken('comma', ','),
|
|
171
|
+
newToken('number', DNCL_ARRAY_INIT_COUNT), newToken(')', ')'))
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// --- その他の変換 ---
|
|
176
|
+
// 二進で表示 (255) → 二進表示(255)
|
|
177
|
+
for (;;) {
|
|
178
|
+
const ni = findTokens(line, ['word:二進', 'word:表示'])
|
|
179
|
+
if (ni < 0) { break }
|
|
180
|
+
line[ni].value = '二進表示'
|
|
181
|
+
line[ni].josi = ''
|
|
182
|
+
line.splice(ni + 1, 1)
|
|
183
|
+
}
|
|
184
|
+
// '改行なしで表示' → '連続無改行表示'
|
|
185
|
+
for (;;) {
|
|
186
|
+
const ni = findTokens(line, ['word:改行', 'word:表示'])
|
|
187
|
+
if (ni < 0) { break }
|
|
188
|
+
// ここ「改行なしで表示」でも「改行ありで表示」でも同じになってしまう
|
|
189
|
+
// なでしこの制限のため仕方なし
|
|
190
|
+
// 「改行ありで表示」は今のところDNCLに存在しないので無視する
|
|
191
|
+
// もし将来的に区別が必要なら、プリプロセス処理でマクロ的に置換処理を行うことで対応できると思う
|
|
192
|
+
const t = line[ni]
|
|
193
|
+
t.value = '連続無改行表示'
|
|
194
|
+
t.josi = ''
|
|
195
|
+
line.splice(ni + 1, 1)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 一つずつチェック
|
|
199
|
+
let j = 0
|
|
200
|
+
while (j < line.length) {
|
|
201
|
+
const t = line[j]
|
|
202
|
+
// 減と増の分割
|
|
203
|
+
if (t.type === 'word' && t.value.length >= 2) {
|
|
204
|
+
const c = t.value.charAt(t.value.length - 1)
|
|
205
|
+
if (c === '減' || c === '増') {
|
|
206
|
+
t.value = t.value.substring(0, t.value.length - 1)
|
|
207
|
+
t.josi = 'だけ'
|
|
208
|
+
line.splice(j + 1, 0, NewEmptyToken('word', c, t.indent, t.line, t.file))
|
|
209
|
+
}
|
|
210
|
+
j++
|
|
211
|
+
continue
|
|
212
|
+
}
|
|
213
|
+
j++
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 最後に単純な置換を行う
|
|
218
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
219
|
+
const t = tokens[i]
|
|
220
|
+
const a = DNCL_SIMPLES[t.type + ':' + t.value]
|
|
221
|
+
if (a !== undefined) {
|
|
222
|
+
t.type = a[0]
|
|
223
|
+
t.value = a[1]
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
tokens = joinTokenLines(lines)
|
|
227
|
+
if (IS_DEBUG) {
|
|
228
|
+
console.log('@@@---DNCL:tokens---')
|
|
229
|
+
console.log(debugTokens(tokens))
|
|
230
|
+
console.log('@@@/---DNCL:tokens---')
|
|
231
|
+
}
|
|
232
|
+
return tokens
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* トークンが合致するかを確認する
|
|
237
|
+
* @param typeValues ['word:それ']のようなタイプ名と値の配列/'*'でワイルドカードが使える/":"がなればタイプだけ確認/配列で選択
|
|
238
|
+
* @param lines 差し替え
|
|
239
|
+
* @param fromIndex 検索場所
|
|
240
|
+
* @returns 合致したかどうか
|
|
241
|
+
*/
|
|
242
|
+
function tokenEq (typeValues: any[], lines: Token[], fromIndex: number): boolean {
|
|
243
|
+
const check = (pattern: string|Array<string>, t: Token): boolean => {
|
|
244
|
+
if (pattern instanceof Array) {
|
|
245
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
246
|
+
if (check(pattern[i], t)) { return true }
|
|
247
|
+
}
|
|
248
|
+
return false
|
|
249
|
+
}
|
|
250
|
+
if (pattern === '*') { return true }
|
|
251
|
+
if (pattern.indexOf(':') < 0) {
|
|
252
|
+
if (pattern === t.type) { return true } else { return false }
|
|
253
|
+
}
|
|
254
|
+
const tv = `${t.type}:${t.value}`
|
|
255
|
+
if (pattern === tv) { return true }
|
|
256
|
+
return false
|
|
257
|
+
}
|
|
258
|
+
for (let i = 0; i < typeValues.length; i++) {
|
|
259
|
+
const idx = i + fromIndex
|
|
260
|
+
if (idx >= lines.length) { return false }
|
|
261
|
+
const pat = typeValues[i]
|
|
262
|
+
const t = lines[idx]
|
|
263
|
+
if (!check(pat, t)) { return false }
|
|
264
|
+
}
|
|
265
|
+
return true
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function findTokens (tokens: Token[], findTypeValue: string[]): number {
|
|
269
|
+
const findA = findTypeValue.map(s => s.split(':'))
|
|
270
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
271
|
+
let flag = true
|
|
272
|
+
for (let j = 0; j < findA.length; j++) {
|
|
273
|
+
const f = findA[j]
|
|
274
|
+
const idx = i + j
|
|
275
|
+
if (idx >= tokens.length) { return -1 }
|
|
276
|
+
if (tokens[idx].type === f[0] && tokens[idx].value === f[1]) {
|
|
277
|
+
continue
|
|
278
|
+
} else {
|
|
279
|
+
flag = false
|
|
280
|
+
break
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (flag) { return i }
|
|
284
|
+
}
|
|
285
|
+
return -1
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function useDNCL2mode (tokens: Token[]): boolean {
|
|
289
|
+
// 先頭の100語調べる
|
|
290
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
291
|
+
if (i > 100) { break }
|
|
292
|
+
const t = tokens[i]
|
|
293
|
+
if (t.type === 'line_comment' && DNCL2_KEYWORDS.indexOf(t.value) >= 0) {
|
|
294
|
+
t.type = 'DNCL2モード'
|
|
295
|
+
return true
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return false
|
|
299
|
+
}
|
package/core/src/nako_gen.mjs
CHANGED
|
@@ -823,7 +823,7 @@ export class NakoGen {
|
|
|
823
823
|
// codeInit?
|
|
824
824
|
if (node.checkInit) {
|
|
825
825
|
const arrayDefCode = '[0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0]';
|
|
826
|
-
codeInit += `\n/*配列初期化*/if (!(${name} instanceof Array)) { ${name} = ${arrayDefCode}; console.log('初期化:${name}') };`;
|
|
826
|
+
codeInit += `\n/*配列初期化*/if (!(${name} instanceof Array)) { ${name} = ${arrayDefCode}; /*console.log('初期化:${name}')*/ };`;
|
|
827
827
|
for (let i = 0; i < list.length - 1; i++) {
|
|
828
828
|
const idx = this._convGen(list[i], true);
|
|
829
829
|
codeArray += `[${idx}]`;
|
package/core/src/nako_gen.mts
CHANGED
|
@@ -886,7 +886,7 @@ export class NakoGen {
|
|
|
886
886
|
// codeInit?
|
|
887
887
|
if (node.checkInit) {
|
|
888
888
|
const arrayDefCode = '[0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0]'
|
|
889
|
-
codeInit += `\n/*配列初期化*/if (!(${name} instanceof Array)) { ${name} = ${arrayDefCode}; console.log('初期化:${name}') };`
|
|
889
|
+
codeInit += `\n/*配列初期化*/if (!(${name} instanceof Array)) { ${name} = ${arrayDefCode}; /*console.log('初期化:${name}')*/ };`
|
|
890
890
|
for (let i = 0; i < list.length - 1; i++) {
|
|
891
891
|
const idx = this._convGen(list[i], true)
|
|
892
892
|
codeArray += `[${idx}]`
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/** インデント構文を処理するモジュール */
|
|
2
2
|
import { NewEmptyToken } from './nako_types.mjs';
|
|
3
3
|
import { NakoIndentError } from '../src/nako_errors.mjs';
|
|
4
|
+
import { debugTokens, newToken } from './nako_tools.mjs';
|
|
5
|
+
const IS_DEBUG = false;
|
|
4
6
|
function isSkipWord(t) {
|
|
5
7
|
if (t.type === '違えば') {
|
|
6
8
|
return true;
|
|
@@ -12,28 +14,62 @@ function isSkipWord(t) {
|
|
|
12
14
|
}
|
|
13
15
|
/** インラインインデント構文 --- 末尾の":"をインデントを考慮して"ここまで"を挿入 (#1215) */
|
|
14
16
|
export function convertInlineIndent(tokens) {
|
|
17
|
+
//
|
|
18
|
+
// 0:もし、A=0ならば:
|
|
19
|
+
// 2: もし、B=0ならば:
|
|
20
|
+
// 4: 「A=0,B=0」を表示。
|
|
21
|
+
// 2: 違えば:
|
|
22
|
+
// 4: 「A=0,B!=0」を表示。
|
|
23
|
+
// 5:違えば:
|
|
24
|
+
// 6: 「A!=0」を表示。
|
|
25
|
+
//
|
|
15
26
|
const lines = splitTokens(tokens, 'eol');
|
|
16
27
|
const blockIndents = [];
|
|
17
28
|
let checkICount = -1;
|
|
29
|
+
let jsonObjLevel = 0;
|
|
30
|
+
let jsonArrayLevel = 0;
|
|
31
|
+
const checkJsonSyntax = (line) => {
|
|
32
|
+
// JSONのオブジェクトがあるか?
|
|
33
|
+
line.forEach((t) => {
|
|
34
|
+
if (t.type === '{') {
|
|
35
|
+
jsonObjLevel++;
|
|
36
|
+
}
|
|
37
|
+
if (t.type === '}') {
|
|
38
|
+
jsonObjLevel--;
|
|
39
|
+
}
|
|
40
|
+
if (t.type === '[') {
|
|
41
|
+
jsonArrayLevel++;
|
|
42
|
+
}
|
|
43
|
+
if (t.type === ']') {
|
|
44
|
+
jsonArrayLevel--;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
18
48
|
for (let i = 0; i < lines.length; i++) {
|
|
19
49
|
const line = lines[i];
|
|
20
|
-
|
|
50
|
+
// 空行は飛ばす || コメント行だけの行も飛ばす
|
|
51
|
+
if (IsEmptyLine(line)) {
|
|
21
52
|
continue;
|
|
22
53
|
}
|
|
23
|
-
|
|
54
|
+
const leftToken = GetLeftTokens(line);
|
|
55
|
+
// JSONの途中であればブロックの変更は行わない
|
|
56
|
+
if (jsonObjLevel > 0 || jsonArrayLevel > 0) {
|
|
57
|
+
checkJsonSyntax(line);
|
|
24
58
|
continue;
|
|
25
59
|
}
|
|
26
60
|
// インデントの終了を確認する必要があるか?
|
|
27
61
|
if (checkICount >= 0) {
|
|
28
|
-
const lineICount =
|
|
62
|
+
const lineICount = leftToken.indent;
|
|
29
63
|
while (checkICount >= lineICount) {
|
|
30
|
-
const tFirst =
|
|
31
|
-
|
|
32
|
-
|
|
64
|
+
const tFirst = leftToken;
|
|
65
|
+
// console.log('@@', lineICount, '>>', checkICount, tFirst.type)
|
|
66
|
+
if (isSkipWord(tFirst) && (checkICount === lineICount)) { // 「違えば」や「エラーならば」
|
|
67
|
+
// 「ここまで」の挿入不要 / ただしネストした際の「違えば」(上記の5の状態なら必要)
|
|
33
68
|
}
|
|
34
69
|
else {
|
|
35
70
|
// ここまでを挿入する
|
|
36
|
-
lines[i - 1].push(
|
|
71
|
+
lines[i - 1].push(newToken('ここまで', 'ここまで', tFirst));
|
|
72
|
+
lines[i - 1].push(newToken('eol', '\n', tFirst));
|
|
37
73
|
}
|
|
38
74
|
blockIndents.pop();
|
|
39
75
|
if (blockIndents.length > 0) {
|
|
@@ -45,6 +81,12 @@ export function convertInlineIndent(tokens) {
|
|
|
45
81
|
}
|
|
46
82
|
}
|
|
47
83
|
}
|
|
84
|
+
// JSONの途中であればブロックの変更は行わない
|
|
85
|
+
checkJsonSyntax(line);
|
|
86
|
+
if (jsonObjLevel > 0 || jsonArrayLevel > 0) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
// 末尾の「:」をチェック
|
|
48
90
|
const tLast = getLastTokenWithoutEOL(line);
|
|
49
91
|
if (tLast.type === ':') {
|
|
50
92
|
// 末尾の「:」を削除
|
|
@@ -53,12 +95,27 @@ export function convertInlineIndent(tokens) {
|
|
|
53
95
|
blockIndents.push(checkICount);
|
|
54
96
|
}
|
|
55
97
|
}
|
|
56
|
-
if (lines.length > 0) {
|
|
98
|
+
if (lines.length > 0 && blockIndents.length > 0) {
|
|
99
|
+
// トークン情報を得るため、直近のトークンを得る
|
|
100
|
+
let t = tokens[0];
|
|
101
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
102
|
+
const line = lines[i];
|
|
103
|
+
if (line.length > 0) {
|
|
104
|
+
t = line[line.length - 1];
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// ここまでを差し込む
|
|
57
109
|
for (let i = 0; i < blockIndents.length; i++) {
|
|
58
|
-
lines[lines.length - 1].push(
|
|
110
|
+
lines[lines.length - 1].push(newToken('ここまで', 'ここまで', t));
|
|
111
|
+
lines[lines.length - 1].push(newToken('eol', '\n', t));
|
|
59
112
|
}
|
|
60
113
|
}
|
|
61
|
-
|
|
114
|
+
const result = joinTokenLines(lines);
|
|
115
|
+
if (IS_DEBUG) {
|
|
116
|
+
console.log('###', debugTokens(result));
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
62
119
|
}
|
|
63
120
|
/** 行ごとに分割していたトークンをくっつける */
|
|
64
121
|
export function joinTokenLines(lines) {
|
|
@@ -67,19 +124,9 @@ export function joinTokenLines(lines) {
|
|
|
67
124
|
for (const t of line) {
|
|
68
125
|
r.push(t);
|
|
69
126
|
}
|
|
70
|
-
// debug
|
|
71
|
-
// console.log('@@join=', mkIndent(line[0] ? line[0].indent : 0), line.map(t => (t.type + '_' + t.value + t.josi + ':' + t.indent)).join(' | '))
|
|
72
127
|
}
|
|
73
|
-
// console.log('@@@-----')
|
|
74
128
|
return r;
|
|
75
129
|
}
|
|
76
|
-
function mkIndent(num) {
|
|
77
|
-
let s = '';
|
|
78
|
-
for (let i = 0; i < num; i++) {
|
|
79
|
-
s += ' ';
|
|
80
|
-
}
|
|
81
|
-
return s;
|
|
82
|
-
}
|
|
83
130
|
function getLastTokenWithoutEOL(line) {
|
|
84
131
|
const len = line.length;
|
|
85
132
|
if (len === 0) {
|
|
@@ -115,6 +162,31 @@ export function splitTokens(tokens, delimiter) {
|
|
|
115
162
|
}
|
|
116
163
|
return result;
|
|
117
164
|
}
|
|
165
|
+
/** トークン行が空かどうか調べる */
|
|
166
|
+
function IsEmptyLine(line) {
|
|
167
|
+
if (line.length === 0) {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
for (let j = 0; j < line.length; j++) {
|
|
171
|
+
const ty = line[j].type;
|
|
172
|
+
if (ty === 'eol' || ty === 'line_comment' || ty === 'range_comment') {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
/** コメントを除去した最初のトークンを返す */
|
|
180
|
+
function GetLeftTokens(line) {
|
|
181
|
+
for (let i = 0; i < line.length; i++) {
|
|
182
|
+
const t = line[i].type;
|
|
183
|
+
if (t === 'eol' || t === 'line_comment' || t === 'range_comment') {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
return line[i];
|
|
187
|
+
}
|
|
188
|
+
return line[0];
|
|
189
|
+
}
|
|
118
190
|
// インデント構文のキーワード
|
|
119
191
|
const INDENT_MODE_KEYWORDS = ['!インデント構文', '!ここまでだるい', '💡インデント構文', '💡ここまでだるい'];
|
|
120
192
|
/** インデント構文 --- インデントを見て"ここまで"を自動挿入 (#596) */
|
|
@@ -130,18 +202,44 @@ export function convertIndentSyntax(tokens) {
|
|
|
130
202
|
throw new NakoIndentError('インデント構文が有効化されているときに『ここまで』を使うことはできません。', t.line, t.file);
|
|
131
203
|
}
|
|
132
204
|
}
|
|
205
|
+
// JSON構文のチェック
|
|
206
|
+
let jsonObjLevel = 0;
|
|
207
|
+
let jsonArrayLevel = 0;
|
|
208
|
+
const checkJsonSyntax = (line) => {
|
|
209
|
+
// JSONのオブジェクトがあるか?
|
|
210
|
+
line.forEach((t) => {
|
|
211
|
+
if (t.type === '{') {
|
|
212
|
+
jsonObjLevel++;
|
|
213
|
+
}
|
|
214
|
+
if (t.type === '}') {
|
|
215
|
+
jsonObjLevel--;
|
|
216
|
+
}
|
|
217
|
+
if (t.type === '[') {
|
|
218
|
+
jsonArrayLevel++;
|
|
219
|
+
}
|
|
220
|
+
if (t.type === ']') {
|
|
221
|
+
jsonArrayLevel--;
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
};
|
|
225
|
+
// 行ごとにトークンを分割
|
|
133
226
|
const blockIndents = [];
|
|
134
227
|
const lines = splitTokens(tokens, 'eol');
|
|
135
228
|
let lastI = 0;
|
|
229
|
+
// 各行を確認する
|
|
136
230
|
for (let i = 0; i < lines.length; i++) {
|
|
137
231
|
const line = lines[i];
|
|
138
|
-
|
|
232
|
+
// 空行は飛ばす || コメント行だけの行も飛ばす
|
|
233
|
+
if (IsEmptyLine(line)) {
|
|
139
234
|
continue;
|
|
140
|
-
}
|
|
141
|
-
|
|
235
|
+
}
|
|
236
|
+
// JSON構文のチェック
|
|
237
|
+
if (jsonArrayLevel > 0 || jsonObjLevel > 0) {
|
|
238
|
+
checkJsonSyntax(line);
|
|
142
239
|
continue;
|
|
143
|
-
}
|
|
144
|
-
const
|
|
240
|
+
}
|
|
241
|
+
const leftToken = GetLeftTokens(line);
|
|
242
|
+
const curI = leftToken.indent;
|
|
145
243
|
if (curI === lastI) {
|
|
146
244
|
continue;
|
|
147
245
|
}
|
|
@@ -155,16 +253,19 @@ export function convertIndentSyntax(tokens) {
|
|
|
155
253
|
// ブロックの終了?
|
|
156
254
|
if (lastI >= 0) {
|
|
157
255
|
while (lastI > curI) {
|
|
158
|
-
|
|
159
|
-
|
|
256
|
+
const blockIndentTopLast = blockIndents[blockIndents.length - 1][1];
|
|
257
|
+
// console.log('@@[', i, ']', lastI, '>', curI, '@', blockIndentTopLast, leftToken.type)
|
|
258
|
+
if (isSkipWord(leftToken) && blockIndentTopLast === curI) {
|
|
259
|
+
// 「違えば」などなら不要 (ただし、違えばがネストしている場合は必要)
|
|
160
260
|
}
|
|
161
261
|
else {
|
|
162
|
-
lines[i - 1]
|
|
262
|
+
const t = lines[i - 1][0];
|
|
263
|
+
lines[i - 1].push(newToken('ここまで', 'ここまで', t));
|
|
264
|
+
lines[i - 1].push(newToken('eol', '\n', t));
|
|
163
265
|
}
|
|
164
|
-
// console.log('@@@pop', lastI, '>=', curI, ':', line[0])
|
|
165
266
|
blockIndents.pop();
|
|
166
267
|
if (blockIndents.length > 0) {
|
|
167
|
-
lastI = blockIndents[blockIndents.length - 1];
|
|
268
|
+
lastI = blockIndents[blockIndents.length - 1][0];
|
|
168
269
|
}
|
|
169
270
|
else {
|
|
170
271
|
lastI = 0;
|
|
@@ -172,19 +273,36 @@ export function convertIndentSyntax(tokens) {
|
|
|
172
273
|
}
|
|
173
274
|
}
|
|
174
275
|
}
|
|
276
|
+
if (jsonArrayLevel > 0 || jsonObjLevel > 0) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
// JSON構文のチェック
|
|
280
|
+
checkJsonSyntax(line);
|
|
175
281
|
// ブロックの開始?
|
|
176
282
|
if (curI > lastI) {
|
|
177
|
-
blockIndents.push(curI);
|
|
283
|
+
blockIndents.push([curI, lastI]);
|
|
178
284
|
// console.log('@@@push', curI)
|
|
179
285
|
lastI = curI;
|
|
180
286
|
continue;
|
|
181
287
|
}
|
|
182
288
|
}
|
|
289
|
+
// 末尾に「ここまで」を追加する
|
|
183
290
|
for (let i = 0; i < blockIndents.length; i++) {
|
|
184
|
-
|
|
291
|
+
// トークン情報を得るため、直近のトークンを得る
|
|
292
|
+
let t = tokens[0];
|
|
293
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
294
|
+
const line = lines[i];
|
|
295
|
+
if (line.length > 0) {
|
|
296
|
+
t = line[line.length - 1];
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
lines[lines.length - 1].push(newToken('ここまで', 'ここまで', t));
|
|
301
|
+
lines[lines.length - 1].push(newToken('eol', '\n', t));
|
|
185
302
|
}
|
|
186
|
-
|
|
187
|
-
|
|
303
|
+
const result = joinTokenLines(lines);
|
|
304
|
+
// console.log('###', debugTokens(result))
|
|
305
|
+
return result;
|
|
188
306
|
}
|
|
189
307
|
function useIndentSynax(tokens) {
|
|
190
308
|
// インデント構文が必要かチェック (最初の100個をチェック)
|