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.
Files changed (44) hide show
  1. package/batch/command.txt +15 -15
  2. package/core/package.json +4 -1
  3. package/core/src/nako3.mjs +5 -2
  4. package/core/src/nako3.mts +5 -4
  5. package/core/src/nako_core_version.mjs +2 -2
  6. package/core/src/nako_core_version.mts +2 -2
  7. package/core/src/nako_from_dncl.mjs +0 -254
  8. package/core/src/nako_from_dncl.mts +0 -247
  9. package/core/src/nako_from_dncl2.mjs +313 -0
  10. package/core/src/nako_from_dncl2.mts +299 -0
  11. package/core/src/nako_gen.mjs +1 -1
  12. package/core/src/nako_gen.mts +1 -1
  13. package/core/src/nako_indent_inline.mjs +152 -34
  14. package/core/src/nako_indent_inline.mts +132 -34
  15. package/core/src/nako_lex_rules.mjs +3 -2
  16. package/core/src/nako_lex_rules.mts +3 -2
  17. package/core/src/nako_lexer.mjs +27 -3
  18. package/core/src/nako_lexer.mts +27 -3
  19. package/core/src/nako_parser3.mjs +45 -14
  20. package/core/src/nako_parser3.mts +40 -18
  21. package/core/src/nako_prepare.mjs +13 -0
  22. package/core/src/nako_prepare.mts +11 -0
  23. package/core/src/nako_tools.mjs +53 -0
  24. package/core/src/nako_tools.mts +46 -0
  25. package/core/test/basic_test.mjs +16 -8
  26. package/core/test/dncl2_test.mjs +207 -0
  27. package/core/test/flow_test.mjs +42 -0
  28. package/core/test/indent_test.mjs +74 -50
  29. package/core/test/inline_indent_test.mjs +47 -0
  30. package/core/test/lex_test.mjs +1 -0
  31. package/demo/js/a.js +30 -0
  32. package/package.json +1 -1
  33. package/release/_hash.txt +16 -16
  34. package/release/_script-tags.txt +14 -14
  35. package/release/nako_gen_async.js +1 -1
  36. package/release/stats.json +1 -1
  37. package/release/version.js +1 -1
  38. package/release/wnako3.js +1 -1
  39. package/release/wnako3webworker.js +1 -1
  40. package/src/cnako3mod.mjs +43 -33
  41. package/src/cnako3mod.mts +45 -35
  42. package/src/nako_version.mjs +2 -2
  43. package/src/nako_version.mts +2 -2
  44. 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
- if (line.length === 0) { continue }
20
- if (line[0].type === 'eol') { continue }
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 = lines[i][0].indent
52
+ const lineICount: number = leftToken.indent
24
53
  while (checkICount >= lineICount) {
25
- const tFirst: Token = line[0]
26
- if (isSkipWord(tFirst)) { // 「違えば」の直前には「ここまで」不要
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(NewEmptyToken('ここまで', '', lineICount, tFirst.line))
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(NewEmptyToken('ここまで'))
96
+ lines[lines.length - 1].push(newToken('ここまで', 'ここまで', t))
97
+ lines[lines.length - 1].push(newToken('eol', '\n', t))
52
98
  }
53
99
  }
54
- return joinTokenLines(lines)
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
- const blockIndents: number[] = []
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
- if (line.length === 0) { continue } // 空行は飛ばす
130
- if (line[0].type === 'eol') { continue } // 空行は飛ばす
131
- const curI: number = line[0].indent
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
- if (isSkipWord(line[0])) {
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].push(NewEmptyToken('ここまで'))
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
- lines[lines.length - 1].push(NewEmptyToken('ここまで'))
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
- return joinTokenLines(lines)
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モード)[^\n]*/ },
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|・)+/ }, // #877,#1015
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モード)[^\n]*/ }, // #1184
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: /^</ },
@@ -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
  }
@@ -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
- /** @returns {Ast} */
188
- yDNCLMode() {
190
+ /** set DNCL mode */
191
+ yDNCLMode(ver) {
189
192
  const map = this.peekSourceMap();
190
- // 配列インデックスは1から
191
- this.arrayIndexFrom = 1;
192
- // 配列アクセスをJSと逆順で指定する
193
- this.flagReverseArrayIndex = true;
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, this.peek())
366
- // チェック : AがBならば
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
- const naraba = this.get();
371
- if ((b && b.type !== 'func') && (naraba && naraba.type === 'ならば')) {
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: (naraba.value === 'でなければ') ? 'noteq' : 'eq',
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
- if (a.josi !== '') {
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
- /** @returns {Ast} */
154
- yDNCLMode (): Ast {
154
+ /** set DNCL mode */
155
+ yDNCLMode (ver: number): Ast {
155
156
  const map = this.peekSourceMap()
156
- // 配列インデックスは1から
157
- this.arrayIndexFrom = 1
158
- // 配列アクセスをJSと逆順で指定する
159
- this.flagReverseArrayIndex = true
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, this.peek())
314
-
315
- // チェック : AがBならば
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
- const naraba = this.get()
320
- if ((b && b.type !== 'func') && (naraba && naraba.type === 'ならば')) {
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: (naraba.value === 'でなければ') ? 'noteq' : 'eq',
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
- if (a.josi !== '') {
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)));