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
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { Token, NewEmptyToken } from './nako_types.mjs'
|
|
4
4
|
import { NakoIndentError } from '../src/nako_errors.mjs'
|
|
5
|
+
import { debugTokens, newToken } from './nako_tools.mjs'
|
|
6
|
+
|
|
7
|
+
const IS_DEBUG = false
|
|
5
8
|
|
|
6
9
|
function isSkipWord (t: Token): boolean {
|
|
7
10
|
if (t.type === '違えば') { return true }
|
|
@@ -11,23 +14,51 @@ function isSkipWord (t: Token): boolean {
|
|
|
11
14
|
|
|
12
15
|
/** インラインインデント構文 --- 末尾の":"をインデントを考慮して"ここまで"を挿入 (#1215) */
|
|
13
16
|
export function convertInlineIndent (tokens: Token[]): Token[] {
|
|
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
|
+
//
|
|
14
26
|
const lines: Token[][] = splitTokens(tokens, 'eol')
|
|
15
27
|
const blockIndents: number[] = []
|
|
16
28
|
let checkICount = -1
|
|
29
|
+
let jsonObjLevel = 0
|
|
30
|
+
let jsonArrayLevel = 0
|
|
31
|
+
const checkJsonSyntax = (line: Token[]) => {
|
|
32
|
+
// JSONのオブジェクトがあるか?
|
|
33
|
+
line.forEach((t: Token) => {
|
|
34
|
+
if (t.type === '{') { jsonObjLevel++ }
|
|
35
|
+
if (t.type === '}') { jsonObjLevel-- }
|
|
36
|
+
if (t.type === '[') { jsonArrayLevel++ }
|
|
37
|
+
if (t.type === ']') { jsonArrayLevel-- }
|
|
38
|
+
})
|
|
39
|
+
}
|
|
17
40
|
for (let i = 0; i < lines.length; i++) {
|
|
18
41
|
const line = lines[i]
|
|
19
|
-
|
|
20
|
-
if (line
|
|
42
|
+
// 空行は飛ばす || コメント行だけの行も飛ばす
|
|
43
|
+
if (IsEmptyLine(line)) { continue }
|
|
44
|
+
const leftToken = GetLeftTokens(line)
|
|
45
|
+
// JSONの途中であればブロックの変更は行わない
|
|
46
|
+
if (jsonObjLevel > 0 || jsonArrayLevel > 0) {
|
|
47
|
+
checkJsonSyntax(line)
|
|
48
|
+
continue
|
|
49
|
+
}
|
|
21
50
|
// インデントの終了を確認する必要があるか?
|
|
22
51
|
if (checkICount >= 0) {
|
|
23
|
-
const lineICount: number =
|
|
52
|
+
const lineICount: number = leftToken.indent
|
|
24
53
|
while (checkICount >= lineICount) {
|
|
25
|
-
const tFirst: Token =
|
|
26
|
-
|
|
27
|
-
|
|
54
|
+
const tFirst: Token = leftToken
|
|
55
|
+
// console.log('@@', lineICount, '>>', checkICount, tFirst.type)
|
|
56
|
+
if (isSkipWord(tFirst) && (checkICount === lineICount)) { // 「違えば」や「エラーならば」
|
|
57
|
+
// 「ここまで」の挿入不要 / ただしネストした際の「違えば」(上記の5の状態なら必要)
|
|
28
58
|
} else {
|
|
29
59
|
// ここまでを挿入する
|
|
30
|
-
lines[i - 1].push(
|
|
60
|
+
lines[i - 1].push(newToken('ここまで', 'ここまで', tFirst))
|
|
61
|
+
lines[i - 1].push(newToken('eol', '\n', tFirst))
|
|
31
62
|
}
|
|
32
63
|
blockIndents.pop()
|
|
33
64
|
if (blockIndents.length > 0) {
|
|
@@ -38,6 +69,10 @@ export function convertInlineIndent (tokens: Token[]): Token[] {
|
|
|
38
69
|
}
|
|
39
70
|
}
|
|
40
71
|
}
|
|
72
|
+
// JSONの途中であればブロックの変更は行わない
|
|
73
|
+
checkJsonSyntax(line)
|
|
74
|
+
if (jsonObjLevel > 0 || jsonArrayLevel > 0) { continue }
|
|
75
|
+
// 末尾の「:」をチェック
|
|
41
76
|
const tLast: Token = getLastTokenWithoutEOL(line)
|
|
42
77
|
if (tLast.type === ':') {
|
|
43
78
|
// 末尾の「:」を削除
|
|
@@ -46,12 +81,27 @@ export function convertInlineIndent (tokens: Token[]): Token[] {
|
|
|
46
81
|
blockIndents.push(checkICount)
|
|
47
82
|
}
|
|
48
83
|
}
|
|
49
|
-
if (lines.length > 0) {
|
|
84
|
+
if (lines.length > 0 && blockIndents.length > 0) {
|
|
85
|
+
// トークン情報を得るため、直近のトークンを得る
|
|
86
|
+
let t = tokens[0]
|
|
87
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
88
|
+
const line = lines[i]
|
|
89
|
+
if (line.length > 0) {
|
|
90
|
+
t = line[line.length - 1]
|
|
91
|
+
break
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// ここまでを差し込む
|
|
50
95
|
for (let i = 0; i < blockIndents.length; i++) {
|
|
51
|
-
lines[lines.length - 1].push(
|
|
96
|
+
lines[lines.length - 1].push(newToken('ここまで', 'ここまで', t))
|
|
97
|
+
lines[lines.length - 1].push(newToken('eol', '\n', t))
|
|
52
98
|
}
|
|
53
99
|
}
|
|
54
|
-
|
|
100
|
+
const result = joinTokenLines(lines)
|
|
101
|
+
if (IS_DEBUG) {
|
|
102
|
+
console.log('###', debugTokens(result))
|
|
103
|
+
}
|
|
104
|
+
return result
|
|
55
105
|
}
|
|
56
106
|
|
|
57
107
|
/** 行ごとに分割していたトークンをくっつける */
|
|
@@ -61,21 +111,10 @@ export function joinTokenLines (lines: Token[][]): Token[] {
|
|
|
61
111
|
for (const t of line) {
|
|
62
112
|
r.push(t)
|
|
63
113
|
}
|
|
64
|
-
// debug
|
|
65
|
-
// console.log('@@join=', mkIndent(line[0] ? line[0].indent : 0), line.map(t => (t.type + '_' + t.value + t.josi + ':' + t.indent)).join(' | '))
|
|
66
114
|
}
|
|
67
|
-
// console.log('@@@-----')
|
|
68
115
|
return r
|
|
69
116
|
}
|
|
70
117
|
|
|
71
|
-
function mkIndent (num: number): string {
|
|
72
|
-
let s = ''
|
|
73
|
-
for (let i = 0; i < num; i++) {
|
|
74
|
-
s += ' '
|
|
75
|
-
}
|
|
76
|
-
return s
|
|
77
|
-
}
|
|
78
|
-
|
|
79
118
|
function getLastTokenWithoutEOL (line: Token[]): Token {
|
|
80
119
|
const len: number = line.length
|
|
81
120
|
if (len === 0) { return NewEmptyToken('?') }
|
|
@@ -107,6 +146,27 @@ export function splitTokens (tokens: Token[], delimiter: string): Token[][] {
|
|
|
107
146
|
return result
|
|
108
147
|
}
|
|
109
148
|
|
|
149
|
+
/** トークン行が空かどうか調べる */
|
|
150
|
+
function IsEmptyLine (line: Token[]): boolean {
|
|
151
|
+
if (line.length === 0) { return true }
|
|
152
|
+
for (let j = 0; j < line.length; j++) {
|
|
153
|
+
const ty = line[j].type
|
|
154
|
+
if (ty === 'eol' || ty === 'line_comment' || ty === 'range_comment') { continue }
|
|
155
|
+
return false
|
|
156
|
+
}
|
|
157
|
+
return true
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** コメントを除去した最初のトークンを返す */
|
|
161
|
+
function GetLeftTokens (line: Token[]): Token {
|
|
162
|
+
for (let i = 0; i < line.length; i++) {
|
|
163
|
+
const t = line[i].type
|
|
164
|
+
if (t === 'eol' || t === 'line_comment' || t === 'range_comment') { continue }
|
|
165
|
+
return line[i]
|
|
166
|
+
}
|
|
167
|
+
return line[0]
|
|
168
|
+
}
|
|
169
|
+
|
|
110
170
|
// インデント構文のキーワード
|
|
111
171
|
const INDENT_MODE_KEYWORDS = ['!インデント構文', '!ここまでだるい', '💡インデント構文', '💡ここまでだるい']
|
|
112
172
|
|
|
@@ -121,14 +181,34 @@ export function convertIndentSyntax (tokens: Token[]): Token[] {
|
|
|
121
181
|
throw new NakoIndentError('インデント構文が有効化されているときに『ここまで』を使うことはできません。', t.line, t.file)
|
|
122
182
|
}
|
|
123
183
|
}
|
|
124
|
-
|
|
184
|
+
// JSON構文のチェック
|
|
185
|
+
let jsonObjLevel = 0
|
|
186
|
+
let jsonArrayLevel = 0
|
|
187
|
+
const checkJsonSyntax = (line: Token[]) => {
|
|
188
|
+
// JSONのオブジェクトがあるか?
|
|
189
|
+
line.forEach((t: Token) => {
|
|
190
|
+
if (t.type === '{') { jsonObjLevel++ }
|
|
191
|
+
if (t.type === '}') { jsonObjLevel-- }
|
|
192
|
+
if (t.type === '[') { jsonArrayLevel++ }
|
|
193
|
+
if (t.type === ']') { jsonArrayLevel-- }
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
// 行ごとにトークンを分割
|
|
197
|
+
const blockIndents: number[][] = []
|
|
125
198
|
const lines = splitTokens(tokens, 'eol')
|
|
126
199
|
let lastI = 0
|
|
200
|
+
// 各行を確認する
|
|
127
201
|
for (let i = 0; i < lines.length; i++) {
|
|
128
202
|
const line = lines[i]
|
|
129
|
-
|
|
130
|
-
if (line
|
|
131
|
-
|
|
203
|
+
// 空行は飛ばす || コメント行だけの行も飛ばす
|
|
204
|
+
if (IsEmptyLine(line)) { continue }
|
|
205
|
+
// JSON構文のチェック
|
|
206
|
+
if (jsonArrayLevel > 0 || jsonObjLevel > 0) {
|
|
207
|
+
checkJsonSyntax(line)
|
|
208
|
+
continue
|
|
209
|
+
}
|
|
210
|
+
const leftToken = GetLeftTokens(line)
|
|
211
|
+
const curI: number = leftToken.indent
|
|
132
212
|
if (curI === lastI) { continue }
|
|
133
213
|
// ブロックの終了?
|
|
134
214
|
// 0: 3回
|
|
@@ -140,34 +220,52 @@ export function convertIndentSyntax (tokens: Token[]): Token[] {
|
|
|
140
220
|
// ブロックの終了?
|
|
141
221
|
if (lastI >= 0) {
|
|
142
222
|
while (lastI > curI) {
|
|
143
|
-
|
|
144
|
-
|
|
223
|
+
const blockIndentTopLast = blockIndents[blockIndents.length - 1][1]
|
|
224
|
+
// console.log('@@[', i, ']', lastI, '>', curI, '@', blockIndentTopLast, leftToken.type)
|
|
225
|
+
if (isSkipWord(leftToken) && blockIndentTopLast === curI) {
|
|
226
|
+
// 「違えば」などなら不要 (ただし、違えばがネストしている場合は必要)
|
|
145
227
|
} else {
|
|
146
|
-
lines[i - 1]
|
|
228
|
+
const t = lines[i - 1][0]
|
|
229
|
+
lines[i - 1].push(newToken('ここまで', 'ここまで', t))
|
|
230
|
+
lines[i - 1].push(newToken('eol', '\n', t))
|
|
147
231
|
}
|
|
148
|
-
// console.log('@@@pop', lastI, '>=', curI, ':', line[0])
|
|
149
232
|
blockIndents.pop()
|
|
150
233
|
if (blockIndents.length > 0) {
|
|
151
|
-
lastI = blockIndents[blockIndents.length - 1]
|
|
234
|
+
lastI = blockIndents[blockIndents.length - 1][0]
|
|
152
235
|
} else {
|
|
153
236
|
lastI = 0
|
|
154
237
|
break
|
|
155
238
|
}
|
|
156
239
|
}
|
|
157
240
|
}
|
|
241
|
+
if (jsonArrayLevel > 0 || jsonObjLevel > 0) { continue }
|
|
242
|
+
// JSON構文のチェック
|
|
243
|
+
checkJsonSyntax(line)
|
|
158
244
|
// ブロックの開始?
|
|
159
245
|
if (curI > lastI) {
|
|
160
|
-
blockIndents.push(curI)
|
|
246
|
+
blockIndents.push([curI, lastI])
|
|
161
247
|
// console.log('@@@push', curI)
|
|
162
248
|
lastI = curI
|
|
163
249
|
continue
|
|
164
250
|
}
|
|
165
251
|
}
|
|
252
|
+
// 末尾に「ここまで」を追加する
|
|
166
253
|
for (let i = 0; i < blockIndents.length; i++) {
|
|
167
|
-
|
|
254
|
+
// トークン情報を得るため、直近のトークンを得る
|
|
255
|
+
let t = tokens[0]
|
|
256
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
257
|
+
const line = lines[i]
|
|
258
|
+
if (line.length > 0) {
|
|
259
|
+
t = line[line.length - 1]
|
|
260
|
+
break
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
lines[lines.length - 1].push(newToken('ここまで', 'ここまで', t))
|
|
264
|
+
lines[lines.length - 1].push(newToken('eol', '\n', t))
|
|
168
265
|
}
|
|
169
|
-
|
|
170
|
-
|
|
266
|
+
const result = joinTokenLines(lines)
|
|
267
|
+
// console.log('###', debugTokens(result))
|
|
268
|
+
return result
|
|
171
269
|
}
|
|
172
270
|
|
|
173
271
|
function useIndentSynax (tokens: Token[]) : boolean {
|
|
@@ -17,7 +17,7 @@ export const rules = [
|
|
|
17
17
|
{ name: 'eol', pattern: /^\n/ },
|
|
18
18
|
{ name: 'eol', pattern: /^;/ },
|
|
19
19
|
// eslint-disable-next-line no-control-regex
|
|
20
|
-
{ name: 'space', pattern: /^(\x20|\x09
|
|
20
|
+
{ name: 'space', pattern: /^(\x20|\x09|・|⎿ |└||)+/ },
|
|
21
21
|
{ name: 'comma', pattern: /^,/ },
|
|
22
22
|
{ name: 'line_comment', pattern: /^#[^\n]*/ },
|
|
23
23
|
{ name: 'line_comment', pattern: /^\/\/[^\n]*/ },
|
|
@@ -48,8 +48,9 @@ export const rules = [
|
|
|
48
48
|
{ name: 'lteq', pattern: /^(≦|<=|=<)/ },
|
|
49
49
|
{ name: 'noteq', pattern: /^(≠|<>|!=)/ },
|
|
50
50
|
{ name: '←', pattern: /^(←|<--)/ },
|
|
51
|
+
{ name: 'eq', pattern: /^(==|🟰🟰)/ },
|
|
51
52
|
{ name: 'eq', pattern: /^(=|🟰)/ },
|
|
52
|
-
{ name: 'line_comment', pattern: /^(!|💡)(インデント構文|ここまでだるい|DNCL
|
|
53
|
+
{ name: 'line_comment', pattern: /^(!|💡)(インデント構文|ここまでだるい|DNCLモード|DNCL2モード|DNCL2)[^\n]*/ },
|
|
53
54
|
{ name: 'not', pattern: /^(!|💡)/ },
|
|
54
55
|
{ name: 'gt', pattern: /^>/ },
|
|
55
56
|
{ name: 'lt', pattern: /^</ },
|
|
@@ -36,7 +36,7 @@ export const rules: NakoLexRule[] = [
|
|
|
36
36
|
{ name: 'eol', pattern: /^\n/ },
|
|
37
37
|
{ name: 'eol', pattern: /^;/ },
|
|
38
38
|
// eslint-disable-next-line no-control-regex
|
|
39
|
-
{ name: 'space', pattern: /^(\x20|\x09
|
|
39
|
+
{ name: 'space', pattern: /^(\x20|\x09|・|⎿ |└||)+/ }, // #877,#1015
|
|
40
40
|
{ name: 'comma', pattern: /^,/ },
|
|
41
41
|
{ name: 'line_comment', pattern: /^#[^\n]*/ },
|
|
42
42
|
{ name: 'line_comment', pattern: /^\/\/[^\n]*/ },
|
|
@@ -67,8 +67,9 @@ export const rules: NakoLexRule[] = [
|
|
|
67
67
|
{ name: 'lteq', pattern: /^(≦|<=|=<)/ },
|
|
68
68
|
{ name: 'noteq', pattern: /^(≠|<>|!=)/ },
|
|
69
69
|
{ name: '←', pattern: /^(←|<--)/ }, // 関数呼び出し演算子 #891 #899
|
|
70
|
+
{ name: 'eq', pattern: /^(==|🟰🟰)/ },
|
|
70
71
|
{ name: 'eq', pattern: /^(=|🟰)/ },
|
|
71
|
-
{ name: 'line_comment', pattern: /^(!|💡)(インデント構文|ここまでだるい|DNCL
|
|
72
|
+
{ name: 'line_comment', pattern: /^(!|💡)(インデント構文|ここまでだるい|DNCLモード|DNCL2モード|DNCL2)[^\n]*/ }, // #1184
|
|
72
73
|
{ name: 'not', pattern: /^(!|💡)/ }, // #1184
|
|
73
74
|
{ name: 'gt', pattern: /^>/ },
|
|
74
75
|
{ name: 'lt', pattern: /^</ },
|
package/core/src/nako_lexer.mjs
CHANGED
|
@@ -424,6 +424,12 @@ export class NakoLexer {
|
|
|
424
424
|
case ' ':
|
|
425
425
|
indent++;
|
|
426
426
|
break;
|
|
427
|
+
case '|':
|
|
428
|
+
indent++;
|
|
429
|
+
break;
|
|
430
|
+
case '|':
|
|
431
|
+
indent += 2;
|
|
432
|
+
break;
|
|
427
433
|
case ' ':
|
|
428
434
|
indent += 2;
|
|
429
435
|
break;
|
|
@@ -433,6 +439,21 @@ export class NakoLexer {
|
|
|
433
439
|
case '・':
|
|
434
440
|
indent += 2;
|
|
435
441
|
break;
|
|
442
|
+
case '⎿':
|
|
443
|
+
indent += 2;
|
|
444
|
+
break;
|
|
445
|
+
case '└':
|
|
446
|
+
indent += 2;
|
|
447
|
+
break;
|
|
448
|
+
case '│':
|
|
449
|
+
indent += 2;
|
|
450
|
+
break;
|
|
451
|
+
case '┃':
|
|
452
|
+
indent += 2;
|
|
453
|
+
break;
|
|
454
|
+
case '┗':
|
|
455
|
+
indent += 2;
|
|
456
|
+
break;
|
|
436
457
|
default:
|
|
437
458
|
return [indent, i];
|
|
438
459
|
}
|
|
@@ -455,8 +476,9 @@ export class NakoLexer {
|
|
|
455
476
|
let indent = 0;
|
|
456
477
|
// 最初にインデントを数える
|
|
457
478
|
const ia = this.countIndent(src);
|
|
458
|
-
indent = ia[0];
|
|
459
|
-
src = src.substring(ia[1]);
|
|
479
|
+
indent = ia[0]; // インデント数
|
|
480
|
+
src = src.substring(ia[1]); // 読み飛ばす文字数
|
|
481
|
+
column += ia[1];
|
|
460
482
|
while (src !== '') {
|
|
461
483
|
let ok = false;
|
|
462
484
|
// 各ルールについて
|
|
@@ -587,7 +609,7 @@ export class NakoLexer {
|
|
|
587
609
|
isDefTest = true;
|
|
588
610
|
break;
|
|
589
611
|
}
|
|
590
|
-
case 'eol': {
|
|
612
|
+
case 'eol': { // eolの処理はほかに↑と↓にある
|
|
591
613
|
isDefTest = false;
|
|
592
614
|
break;
|
|
593
615
|
}
|
|
@@ -614,6 +636,8 @@ export class NakoLexer {
|
|
|
614
636
|
if (rule.name === 'eol') { // 改行のとき次の行のインデントを調べる
|
|
615
637
|
const ia = this.countIndent(src);
|
|
616
638
|
indent = ia[0];
|
|
639
|
+
column += ia[1];
|
|
640
|
+
src = src.substring(ia[1]); // インデントを飛ばす
|
|
617
641
|
}
|
|
618
642
|
break;
|
|
619
643
|
}
|
package/core/src/nako_lexer.mts
CHANGED
|
@@ -411,6 +411,12 @@ export class NakoLexer {
|
|
|
411
411
|
case ' ':
|
|
412
412
|
indent++
|
|
413
413
|
break
|
|
414
|
+
case '|':
|
|
415
|
+
indent++
|
|
416
|
+
break
|
|
417
|
+
case '|':
|
|
418
|
+
indent += 2
|
|
419
|
+
break
|
|
414
420
|
case ' ':
|
|
415
421
|
indent += 2
|
|
416
422
|
break
|
|
@@ -420,6 +426,21 @@ export class NakoLexer {
|
|
|
420
426
|
case '・':
|
|
421
427
|
indent += 2
|
|
422
428
|
break
|
|
429
|
+
case '⎿':
|
|
430
|
+
indent += 2
|
|
431
|
+
break
|
|
432
|
+
case '└':
|
|
433
|
+
indent += 2
|
|
434
|
+
break
|
|
435
|
+
case '│':
|
|
436
|
+
indent += 2
|
|
437
|
+
break
|
|
438
|
+
case '┃':
|
|
439
|
+
indent += 2
|
|
440
|
+
break
|
|
441
|
+
case '┗':
|
|
442
|
+
indent += 2
|
|
443
|
+
break
|
|
423
444
|
default:
|
|
424
445
|
return [indent, i]
|
|
425
446
|
}
|
|
@@ -443,8 +464,9 @@ export class NakoLexer {
|
|
|
443
464
|
let indent = 0
|
|
444
465
|
// 最初にインデントを数える
|
|
445
466
|
const ia: number[] = this.countIndent(src)
|
|
446
|
-
indent = ia[0]
|
|
447
|
-
src = src.substring(ia[1])
|
|
467
|
+
indent = ia[0] // インデント数
|
|
468
|
+
src = src.substring(ia[1]) // 読み飛ばす文字数
|
|
469
|
+
column += ia[1]
|
|
448
470
|
while (src !== '') {
|
|
449
471
|
let ok = false
|
|
450
472
|
// 各ルールについて
|
|
@@ -585,7 +607,7 @@ export class NakoLexer {
|
|
|
585
607
|
isDefTest = true
|
|
586
608
|
break
|
|
587
609
|
}
|
|
588
|
-
case 'eol': {
|
|
610
|
+
case 'eol': { // eolの処理はほかに↑と↓にある
|
|
589
611
|
isDefTest = false
|
|
590
612
|
break
|
|
591
613
|
}
|
|
@@ -613,6 +635,8 @@ export class NakoLexer {
|
|
|
613
635
|
if (rule.name === 'eol') { // 改行のとき次の行のインデントを調べる
|
|
614
636
|
const ia = this.countIndent(src)
|
|
615
637
|
indent = ia[0]
|
|
638
|
+
column += ia[1]
|
|
639
|
+
src = src.substring(ia[1]) // インデントを飛ばす
|
|
616
640
|
}
|
|
617
641
|
break
|
|
618
642
|
}
|
|
@@ -130,7 +130,10 @@ export class NakoParser extends NakoParserBase {
|
|
|
130
130
|
return this.yASyncMode();
|
|
131
131
|
}
|
|
132
132
|
if (this.accept(['DNCLモード'])) {
|
|
133
|
-
return this.yDNCLMode();
|
|
133
|
+
return this.yDNCLMode(1);
|
|
134
|
+
}
|
|
135
|
+
if (this.accept(['DNCL2モード'])) {
|
|
136
|
+
return this.yDNCLMode(2);
|
|
134
137
|
}
|
|
135
138
|
if (this.accept(['not', 'string', 'モード設定'])) {
|
|
136
139
|
return this.ySetGenMode(this.y[1].value);
|
|
@@ -184,13 +187,18 @@ export class NakoParser extends NakoParserBase {
|
|
|
184
187
|
this.genMode = '非同期モード';
|
|
185
188
|
return { type: 'eol', ...map, end: this.peekSourceMap() };
|
|
186
189
|
}
|
|
187
|
-
/**
|
|
188
|
-
yDNCLMode() {
|
|
190
|
+
/** set DNCL mode */
|
|
191
|
+
yDNCLMode(ver) {
|
|
189
192
|
const map = this.peekSourceMap();
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
193
|
+
if (ver === 1) {
|
|
194
|
+
// 配列インデックスは1から
|
|
195
|
+
this.arrayIndexFrom = 1;
|
|
196
|
+
// 配列アクセスをJSと逆順で指定する
|
|
197
|
+
this.flagReverseArrayIndex = true;
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
// ver2はPythonに近いとのこと
|
|
201
|
+
}
|
|
194
202
|
// 配列代入時自動で初期化チェックする
|
|
195
203
|
this.flagCheckArrayInit = true;
|
|
196
204
|
return { type: 'eol', ...map, end: this.peekSourceMap() };
|
|
@@ -362,16 +370,38 @@ export class NakoParser extends NakoParserBase {
|
|
|
362
370
|
if (!a) {
|
|
363
371
|
return null;
|
|
364
372
|
}
|
|
365
|
-
// console.log('yIFCond=', a
|
|
366
|
-
// チェック : A
|
|
373
|
+
// console.log('@@yIFCond=', a)
|
|
374
|
+
// チェック : Aならば
|
|
375
|
+
if (a.josi === 'ならば') {
|
|
376
|
+
return a;
|
|
377
|
+
}
|
|
378
|
+
if (a.josi === 'でなければ') {
|
|
379
|
+
a = { type: 'not', value: a, josi: '', ...map, end: this.peekSourceMap() };
|
|
380
|
+
return a;
|
|
381
|
+
}
|
|
382
|
+
// チェック : AがBならば --- 「関数B(A)」のとき
|
|
383
|
+
if ((a.josi !== '') && (this.check('func'))) {
|
|
384
|
+
// もし文で関数呼び出しがある場合
|
|
385
|
+
this.stack.push(a);
|
|
386
|
+
a = this.yCall();
|
|
387
|
+
}
|
|
388
|
+
else
|
|
389
|
+
// チェック : AがBならば --- 「A = B」のとき
|
|
367
390
|
if (a.josi === 'が') {
|
|
368
391
|
const tmpI = this.index;
|
|
369
392
|
const b = this.yGetArg();
|
|
370
|
-
|
|
371
|
-
|
|
393
|
+
if (!b) {
|
|
394
|
+
throw NakoSyntaxError.fromNode('もし文の条件「AがBならば」でBがないか条件が複雑過ぎます。' +
|
|
395
|
+
this.nodeToStr(this.peek(), { depth: 1 }, false), map);
|
|
396
|
+
}
|
|
397
|
+
if (this.check('ならば')) {
|
|
398
|
+
const naraba = this.get() || { 'value': 'ならば' };
|
|
399
|
+
b.josi = naraba.value;
|
|
400
|
+
}
|
|
401
|
+
if (b && (b.josi === 'ならば' || b.josi === 'でなければ')) {
|
|
372
402
|
return {
|
|
373
403
|
type: 'op',
|
|
374
|
-
operator: (
|
|
404
|
+
operator: (b.josi === 'でなければ') ? 'noteq' : 'eq',
|
|
375
405
|
left: a,
|
|
376
406
|
right: b,
|
|
377
407
|
josi: '',
|
|
@@ -381,8 +411,8 @@ export class NakoParser extends NakoParserBase {
|
|
|
381
411
|
}
|
|
382
412
|
this.index = tmpI;
|
|
383
413
|
}
|
|
384
|
-
|
|
385
|
-
|
|
414
|
+
// もし文で追加の関数呼び出しがある場合
|
|
415
|
+
if (!this.check('ならば')) {
|
|
386
416
|
this.stack.push(a);
|
|
387
417
|
a = this.yCall();
|
|
388
418
|
}
|
|
@@ -393,6 +423,7 @@ export class NakoParser extends NakoParserBase {
|
|
|
393
423
|
throw NakoSyntaxError.fromNode('もし文で『ならば』がないか、条件が複雑過ぎます。' + this.nodeToStr(this.peek(), { depth: 1 }, false) + 'の直前に『ならば』を書いてください。', smap);
|
|
394
424
|
}
|
|
395
425
|
const naraba = this.get();
|
|
426
|
+
// 否定形のチェック
|
|
396
427
|
if (naraba && naraba.value === 'でなければ') {
|
|
397
428
|
a = {
|
|
398
429
|
type: 'not',
|
|
@@ -106,7 +106,8 @@ export class NakoParser extends NakoParserBase {
|
|
|
106
106
|
if (this.accept(['続ける'])) { return { type: 'continue', josi: '', ...map, end: this.peekSourceMap() } }
|
|
107
107
|
if (this.accept(['require', 'string', '取込'])) { return this.yRequire() }
|
|
108
108
|
if (this.accept(['not', '非同期モード'])) { return this.yASyncMode() }
|
|
109
|
-
if (this.accept(['DNCLモード'])) { return this.yDNCLMode() }
|
|
109
|
+
if (this.accept(['DNCLモード'])) { return this.yDNCLMode(1) }
|
|
110
|
+
if (this.accept(['DNCL2モード'])) { return this.yDNCLMode(2) }
|
|
110
111
|
if (this.accept(['not', 'string', 'モード設定'])) { return this.ySetGenMode(this.y[1].value) }
|
|
111
112
|
|
|
112
113
|
// 関数呼び出し演算子
|
|
@@ -150,13 +151,17 @@ export class NakoParser extends NakoParserBase {
|
|
|
150
151
|
return { type: 'eol', ...map, end: this.peekSourceMap() }
|
|
151
152
|
}
|
|
152
153
|
|
|
153
|
-
/**
|
|
154
|
-
yDNCLMode (): Ast {
|
|
154
|
+
/** set DNCL mode */
|
|
155
|
+
yDNCLMode (ver: number): Ast {
|
|
155
156
|
const map = this.peekSourceMap()
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
if (ver === 1) {
|
|
158
|
+
// 配列インデックスは1から
|
|
159
|
+
this.arrayIndexFrom = 1
|
|
160
|
+
// 配列アクセスをJSと逆順で指定する
|
|
161
|
+
this.flagReverseArrayIndex = true
|
|
162
|
+
} else {
|
|
163
|
+
// ver2はPythonに近いとのこと
|
|
164
|
+
}
|
|
160
165
|
// 配列代入時自動で初期化チェックする
|
|
161
166
|
this.flagCheckArrayInit = true
|
|
162
167
|
return { type: 'eol', ...map, end: this.peekSourceMap() }
|
|
@@ -310,17 +315,36 @@ export class NakoParser extends NakoParserBase {
|
|
|
310
315
|
const map = this.peekSourceMap()
|
|
311
316
|
let a: Ast | null = this.yGetArg()
|
|
312
317
|
if (!a) { return null }
|
|
313
|
-
// console.log('yIFCond=', a
|
|
314
|
-
|
|
315
|
-
|
|
318
|
+
// console.log('@@yIFCond=', a)
|
|
319
|
+
// チェック : Aならば
|
|
320
|
+
if (a.josi === 'ならば') { return a }
|
|
321
|
+
if (a.josi === 'でなければ') {
|
|
322
|
+
a = { type: 'not', value: a, josi: '', ...map, end: this.peekSourceMap() }
|
|
323
|
+
return a
|
|
324
|
+
}
|
|
325
|
+
// チェック : AがBならば --- 「関数B(A)」のとき
|
|
326
|
+
if ((a.josi !== '') && (this.check('func'))) {
|
|
327
|
+
// もし文で関数呼び出しがある場合
|
|
328
|
+
this.stack.push(a)
|
|
329
|
+
a = this.yCall()
|
|
330
|
+
} else
|
|
331
|
+
// チェック : AがBならば --- 「A = B」のとき
|
|
316
332
|
if (a.josi === 'が') {
|
|
317
333
|
const tmpI = this.index
|
|
318
334
|
const b = this.yGetArg()
|
|
319
|
-
|
|
320
|
-
|
|
335
|
+
if (!b) {
|
|
336
|
+
throw NakoSyntaxError.fromNode(
|
|
337
|
+
'もし文の条件「AがBならば」でBがないか条件が複雑過ぎます。' +
|
|
338
|
+
this.nodeToStr(this.peek(), { depth: 1 }, false), map)
|
|
339
|
+
}
|
|
340
|
+
if (this.check('ならば')) {
|
|
341
|
+
const naraba = this.get() || { 'value': 'ならば' }
|
|
342
|
+
b.josi = naraba.value
|
|
343
|
+
}
|
|
344
|
+
if (b && (b.josi === 'ならば' || b.josi === 'でなければ')) {
|
|
321
345
|
return {
|
|
322
346
|
type: 'op',
|
|
323
|
-
operator: (
|
|
347
|
+
operator: (b.josi === 'でなければ') ? 'noteq' : 'eq',
|
|
324
348
|
left: a,
|
|
325
349
|
right: b,
|
|
326
350
|
josi: '',
|
|
@@ -328,11 +352,10 @@ export class NakoParser extends NakoParserBase {
|
|
|
328
352
|
end: this.peekSourceMap()
|
|
329
353
|
}
|
|
330
354
|
}
|
|
331
|
-
|
|
332
355
|
this.index = tmpI
|
|
333
356
|
}
|
|
334
|
-
|
|
335
|
-
|
|
357
|
+
// もし文で追加の関数呼び出しがある場合
|
|
358
|
+
if (!this.check('ならば')) {
|
|
336
359
|
this.stack.push(a)
|
|
337
360
|
a = this.yCall()
|
|
338
361
|
}
|
|
@@ -344,8 +367,8 @@ export class NakoParser extends NakoParserBase {
|
|
|
344
367
|
throw NakoSyntaxError.fromNode(
|
|
345
368
|
'もし文で『ならば』がないか、条件が複雑過ぎます。' + this.nodeToStr(this.peek(), { depth: 1 }, false) + 'の直前に『ならば』を書いてください。', smap)
|
|
346
369
|
}
|
|
347
|
-
|
|
348
370
|
const naraba = this.get()
|
|
371
|
+
// 否定形のチェック
|
|
349
372
|
if (naraba && naraba.value === 'でなければ') {
|
|
350
373
|
a = {
|
|
351
374
|
type: 'not',
|
|
@@ -355,7 +378,6 @@ export class NakoParser extends NakoParserBase {
|
|
|
355
378
|
end: this.peekSourceMap()
|
|
356
379
|
}
|
|
357
380
|
}
|
|
358
|
-
|
|
359
381
|
return a
|
|
360
382
|
}
|
|
361
383
|
|
|
@@ -186,6 +186,9 @@ export class NakoPrepare {
|
|
|
186
186
|
if (flagStr2) {
|
|
187
187
|
if (ch2 === endOfStr) {
|
|
188
188
|
flagStr2 = false;
|
|
189
|
+
if (endOfStr === '*/') {
|
|
190
|
+
endOfStr = '*/';
|
|
191
|
+
} // 強制変換
|
|
189
192
|
res.push(new ConvertResult(str + endOfStr, src.getSourcePosition(left)));
|
|
190
193
|
i += 2;
|
|
191
194
|
left = i;
|
|
@@ -263,6 +266,16 @@ export class NakoPrepare {
|
|
|
263
266
|
str = '';
|
|
264
267
|
continue;
|
|
265
268
|
}
|
|
269
|
+
// 複数行コメント内を飛ばす
|
|
270
|
+
if (ch2 === '/*') {
|
|
271
|
+
res.push(new ConvertResult('/*', src.getSourcePosition(left))); // 強制変換
|
|
272
|
+
i += 2;
|
|
273
|
+
left = i;
|
|
274
|
+
flagStr2 = true;
|
|
275
|
+
endOfStr = '*/';
|
|
276
|
+
str = '';
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
266
279
|
// 複数行コメント内を飛ばす (#731)
|
|
267
280
|
if (ch2 === '/*') {
|
|
268
281
|
res.push(new ConvertResult(ch2, src.getSourcePosition(left)));
|