nadesiko3 3.7.19 → 3.7.21

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 (52) hide show
  1. package/batch/command.txt +115 -113
  2. package/core/package.json +2 -2
  3. package/core/src/README.md +98 -0
  4. package/core/src/nako_ast.mts +1 -0
  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_gen.mjs +54 -0
  8. package/core/src/nako_gen.mts +61 -0
  9. package/core/src/nako_parser3.mjs +45 -7
  10. package/core/src/nako_parser3.mts +43 -7
  11. package/core/test/array_test.mjs +16 -0
  12. package/core/test/func_call.mjs +11 -0
  13. package/core/test/func_scope.mjs +112 -0
  14. package/package.json +21 -27
  15. package/release/_hash.txt +36 -36
  16. package/release/_script-tags.txt +16 -16
  17. package/release/command.json +1 -1
  18. package/release/command.json.js +1 -1
  19. package/release/command_cnako3.json +1 -1
  20. package/release/command_list.json +1 -1
  21. package/release/edit_main.js +2 -2
  22. package/release/edit_main.js.map +3 -3
  23. package/release/editor.js +2 -2
  24. package/release/plugin_datetime.js +1 -1
  25. package/release/plugin_datetime.js.map +3 -3
  26. package/release/plugin_markup.js +28 -28
  27. package/release/plugin_markup.js.map +3 -3
  28. package/release/version.js +2 -2
  29. package/release/version_main.js +2 -2
  30. package/release/version_main.js.map +2 -2
  31. package/release/wnako3.js +85 -77
  32. package/release/wnako3.js.map +3 -3
  33. package/release/wnako3webworker.js +106 -98
  34. package/release/wnako3webworker.js.map +3 -3
  35. package/src/cnako3mod.mjs +1 -2
  36. package/src/cnako3mod.mts +1 -2
  37. package/src/nako_version.mjs +2 -2
  38. package/src/nako_version.mts +2 -2
  39. package/src/plugin_httpserver.mjs +184 -13
  40. package/src/plugin_httpserver.mts +176 -10
  41. package/src/plugin_node.mjs +0 -1
  42. package/src/plugin_node.mts +2 -3
  43. package/src/wnako3mod.mjs +3 -0
  44. package/src/wnako3mod.mts +3 -0
  45. package/test/common/wnako3mod_test.mjs +48 -0
  46. package/test/node/package_json_test.mjs +17 -0
  47. package/test/node/plugin_httpserver_test.mjs +239 -0
  48. package/tools/nako3edit/html/edit.html +21 -0
  49. package/tools/nako3edit/html/nako3edit.css +10 -0
  50. package/tools/nako3edit/index.mjs +15 -4
  51. package/tools/nako3server/index.mjs +15 -4
  52. package/tools/nako3server/index.nako3 +4 -2
@@ -32,6 +32,11 @@ interface PerformanceMonitor {
32
32
  systemFunctionBody: number; // システム関数(呼び出しコードを除く)
33
33
  mumeiId: number;
34
34
  }
35
+ interface FunctionContext {
36
+ isAnonymous: boolean;
37
+ varsIndex: number;
38
+ usesClosure: boolean;
39
+ }
35
40
  interface FindVarResult {
36
41
  i: number;
37
42
  name: string;
@@ -80,6 +85,7 @@ export class NakoGen {
80
85
  // 変数管理
81
86
  private varslistSet: VarsSet[] // [システム変数一覧, グローバル変数一覧, ローカル変数一覧]で変数セットを記録
82
87
  private varsSet: VarsSet // ローカルな変数を記録
88
+ private functionContextStack: FunctionContext[] // 関数生成時のレキシカルスコープ情報
83
89
  public debugOption: NakoDebugOption
84
90
  // public
85
91
  numAsyncFn: number
@@ -140,6 +146,7 @@ export class NakoGen {
140
146
  /** スタックトップ */
141
147
  this.varsSet = { isFunction: false, names: new Set(), readonly: new Set() }
142
148
  this.varslistSet[2] = this.varsSet
149
+ this.functionContextStack = []
143
150
 
144
151
  // 現在定義中の関数名
145
152
  this.defFuncName = ''
@@ -333,6 +340,11 @@ export class NakoGen {
333
340
  code += 'const __v0 = __self.__v0 = __self.__varslist[0];\n'
334
341
  code += 'const __v1 = __self.__v1 = __self.__varslist[1];\n'
335
342
  code += 'const __vars = __self.__vars = __self.__varslist[2];\n'
343
+ code += 'const __nako_make_closure = (local, parent) => ({\n' +
344
+ ' has: (key) => local.has(key) || (parent !== null && parent.has(key)),\n' +
345
+ ' get: (key) => local.has(key) ? local.get(key) : (parent !== null ? parent.get(key) : undefined),\n' +
346
+ ' set: (key, value) => { if (local.has(key) || parent === null || !parent.has(key)) { local.set(key, value); } else { parent.set(key, value); } return value; }\n' +
347
+ '});\n'
336
348
  code += `const __modList = __self.__modList = ${JSON.stringify(com.getModList())}\n`
337
349
  code += 'const __line = (lineno) => { __self.__v0.set(\'__line\', lineno); }\n'
338
350
  code += '__v0.set(\'__line\', \'l0:__getDefFuncCode\');\n'
@@ -572,6 +584,9 @@ export class NakoGen {
572
584
  case 'calc_func':
573
585
  code += this.convCallFunc(node as AstCallFunc, isExpression)
574
586
  break
587
+ case 'call_value':
588
+ code += this.convCallValue(node as AstBlocks, isExpression)
589
+ break
575
590
  case 'if':
576
591
  code += this.convIf(node as AstIf)
577
592
  break
@@ -656,6 +671,28 @@ export class NakoGen {
656
671
  js_set: this.varname_set(name, String(jsvalue))
657
672
  }
658
673
  }
674
+ // 外側の無名関数のローカル変数をクロージャとして参照する #2268
675
+ const currentFunc = this.functionContextStack[this.functionContextStack.length - 1]
676
+ if (this.varslistSet.length > 4 && currentFunc?.isAnonymous) {
677
+ const currentIndex = this.varslistSet.length - 1
678
+ for (let i = currentIndex - 1; i >= 3; i--) {
679
+ if (this.varslistSet[i].names.has(name)) {
680
+ for (const context of this.functionContextStack) {
681
+ if (context.isAnonymous && context.varsIndex > i) {
682
+ context.usesClosure = true
683
+ }
684
+ }
685
+ const nameJson = JSON.stringify(name)
686
+ return {
687
+ i,
688
+ name,
689
+ isTop: false,
690
+ js: `__nako_closure.get(${nameJson})`,
691
+ js_set: `__nako_closure.set(${nameJson}, ${jsvalue ?? 'undefined'})`
692
+ }
693
+ }
694
+ }
695
+ }
659
696
  // __varslist ?
660
697
  for (let i = 2; i >= 0; i--) {
661
698
  if (this.varslistSet[i].names.has(name)) {
@@ -872,6 +909,12 @@ export class NakoGen {
872
909
  this.varsSet = { isFunction: true, names: initialNames, readonly: new Set() }
873
910
  // ローカル変数をPUSHする
874
911
  this.varslistSet.push(this.varsSet)
912
+ const funcContext: FunctionContext = {
913
+ isAnonymous: name === '',
914
+ varsIndex: this.varslistSet.length - 1,
915
+ usesClosure: false
916
+ }
917
+ this.functionContextStack.push(funcContext)
875
918
  // JSの引数と引数をバインド
876
919
  if (isExtractJS) {
877
920
  variableDeclarations += indent + 'var 引数 = arguments;\n'
@@ -978,6 +1021,10 @@ export class NakoGen {
978
1021
  const lineInfo = ' ' + this.convLineno(node, true, 1) + '\n'
979
1022
  code = tof + performanceMonitorInjectAtStart + pushStack + variableDeclarations + lineInfo + code + popStack
980
1023
  code += endOfFunction
1024
+ if (funcContext.isAnonymous && funcContext.usesClosure) {
1025
+ const parentClosure = `(typeof __nako_closure === 'undefined' ? null : __nako_closure)`
1026
+ code = `(function(__nako_closure) {\n return ${code}\n})(__nako_make_closure(__self.__vars, ${parentClosure}))`
1027
+ }
981
1028
 
982
1029
  // 名前があれば、関数を登録する
983
1030
  if (name) {
@@ -991,6 +1038,7 @@ export class NakoGen {
991
1038
  this.usedAsyncFn = oldUsedAsyncFn // 以前の値を戻す
992
1039
 
993
1040
  this.varslistSet.pop()
1041
+ this.functionContextStack.pop()
994
1042
  this.varsSet = this.varslistSet[this.varslistSet.length - 1]
995
1043
  if (name) { this.__self.__varslist[1].set(name, code) }
996
1044
  this.defFuncName = '' // 関数名をクリア
@@ -1727,6 +1775,19 @@ export class NakoGen {
1727
1775
  return code
1728
1776
  }
1729
1777
 
1778
+ convCallValue(node: AstBlocks, isExpression: boolean): string {
1779
+ const callee = this._convGen(node.blocks[0], true)
1780
+ const args = node.blocks.slice(1).map((arg) => this._convGen(arg, true))
1781
+ args.push('__self')
1782
+ const funcCall = `${callee}(${args.join(',')})`
1783
+ if (isExpression) {
1784
+ return funcCall
1785
+ }
1786
+ const sorePrefex = (this.speedMode.invalidSore === 0) ? '__self.__setSore(' : ''
1787
+ const sorePostfix = (this.speedMode.invalidSore === 0) ? ')' : ''
1788
+ return this.convLineno(node, false) + `${sorePrefex}${funcCall}${sorePostfix};\n`
1789
+ }
1790
+
1730
1791
  convRenbun(node: AstOperator): string {
1731
1792
  const right = this._convGen(node.blocks[1], true)
1732
1793
  const left = this._convGen(node.blocks[0], false)
@@ -907,7 +907,7 @@ export class NakoParser extends NakoParserBase {
907
907
  }
908
908
  return ans;
909
909
  }
910
- yGetArgParen(y) {
910
+ yGetArgParen(y, funcName) {
911
911
  let isClose = false;
912
912
  const si = this.stack.length;
913
913
  while (!this.isEOF()) {
@@ -927,7 +927,8 @@ export class NakoParser extends NakoParserBase {
927
927
  break;
928
928
  }
929
929
  if (!isClose) {
930
- throw NakoSyntaxError.fromNode(`C風関数『${y[0].value}』でカッコが閉じていません`, y[0]);
930
+ const name = funcName || y[0].value || this.nodeToStr(y[0], { depth: 0, typeName: '関数' }, false);
931
+ throw NakoSyntaxError.fromNode(`C風関数『${name}』でカッコが閉じていません`, y[0]);
931
932
  }
932
933
  const a = [];
933
934
  while (si < this.stack.length) {
@@ -938,6 +939,30 @@ export class NakoParser extends NakoParserBase {
938
939
  }
939
940
  return a;
940
941
  }
942
+ /** 関数の戻り値を続けてC風呼び出しする */
943
+ yApplyCallValue(callee) {
944
+ let node = callee;
945
+ while (this.check('(')) {
946
+ this.get(); // skip '('
947
+ const args = this.yGetArgParen([node], '関数呼び出しの結果');
948
+ if (!this.check(')')) {
949
+ throw NakoSyntaxError.fromNode('C風関数呼び出しのエラー', node);
950
+ }
951
+ const close = this.get();
952
+ node = {
953
+ type: 'call_value',
954
+ blocks: [node, ...args],
955
+ josi: close?.josi || '',
956
+ startOffset: node.startOffset,
957
+ endOffset: close?.endOffset,
958
+ line: node.line,
959
+ column: node.column,
960
+ file: node.file,
961
+ end: this.peekSourceMap()
962
+ };
963
+ }
964
+ return node;
965
+ }
941
966
  /** @returns {AstRepeatTimes | null} */
942
967
  yRepeatTime() {
943
968
  const map = this.peekSourceMap();
@@ -1775,7 +1800,7 @@ export class NakoParser extends NakoParserBase {
1775
1800
  }
1776
1801
  // 「**して、**」の場合も一度切る
1777
1802
  if (RenbunJosi.indexOf(t.josi) >= 0) {
1778
- funcNode.josi = 'して';
1803
+ funcNode.josi = (this.isReadingCalc && t.josi === 'には') ? '' : 'して';
1779
1804
  return funcNode;
1780
1805
  }
1781
1806
  // 続き
@@ -2411,7 +2436,7 @@ export class NakoParser extends NakoParserBase {
2411
2436
  return null;
2412
2437
  }
2413
2438
  // 助詞がある? つまり、関数呼び出しがある?
2414
- if (t.josi === '') {
2439
+ if (t.josi === '' && !this.canNextFuncTakeNoJosiArg()) {
2415
2440
  return t;
2416
2441
  } // 値だけの場合
2417
2442
  // 関数の呼び出しがあるなら、スタックに載せて関数読み出しを呼ぶ
@@ -2451,6 +2476,18 @@ export class NakoParser extends NakoParserBase {
2451
2476
  }
2452
2477
  return fCalc;
2453
2478
  }
2479
+ /** 次の関数が空助詞の値を引数として受け取れるか */
2480
+ canNextFuncTakeNoJosiArg() {
2481
+ if (!this.check('func')) {
2482
+ return false;
2483
+ }
2484
+ const func = this.peek();
2485
+ const josiList = func?.meta?.josi;
2486
+ if (!josiList) {
2487
+ return false;
2488
+ }
2489
+ return josiList.some((josi) => josi.indexOf('') >= 0);
2490
+ }
2454
2491
  /** @returns {Ast | null} */
2455
2492
  yValueKakko() {
2456
2493
  if (!this.check('(')) {
@@ -2604,7 +2641,7 @@ export class NakoParser extends NakoParserBase {
2604
2641
  }
2605
2642
  asyncFn = !!meta.asyncFn;
2606
2643
  }
2607
- return {
2644
+ const funcNode = {
2608
2645
  type: 'func',
2609
2646
  name: funcName,
2610
2647
  blocks: args,
@@ -2614,6 +2651,7 @@ export class NakoParser extends NakoParserBase {
2614
2651
  ...map,
2615
2652
  end: this.peekSourceMap()
2616
2653
  };
2654
+ return this.yApplyCallValue(funcNode);
2617
2655
  }
2618
2656
  throw NakoSyntaxError.fromNode('C風関数呼び出しのエラー', funcNameToken || NewEmptyToken());
2619
2657
  }
@@ -2788,7 +2826,7 @@ export class NakoParser extends NakoParserBase {
2788
2826
  const t = this.getCur();
2789
2827
  const word = this.getVarNameRef(t);
2790
2828
  // word[n] || word@n
2791
- if (word.josi === '' && this.checkTypes(['[', '@'])) {
2829
+ if ((word.josi === '' && this.checkTypes(['[', '@'])) || (word.josi !== '' && this.check('@'))) {
2792
2830
  const ast = {
2793
2831
  type: 'ref_array', // 配列参照
2794
2832
  name: word,
@@ -2808,7 +2846,7 @@ export class NakoParser extends NakoParserBase {
2808
2846
  return ast;
2809
2847
  }
2810
2848
  // オブジェクトプロパティ構文(参照) word$prop (#1793)
2811
- if (word.josi === '' && (this.check2(['$', 'word']) || this.check2(['$', 'string']))) {
2849
+ if (this.check2(['$', 'word']) || this.check2(['$', 'string'])) {
2812
2850
  const propList = [];
2813
2851
  let josi = '';
2814
2852
  while (this.check('$')) {
@@ -813,7 +813,7 @@ export class NakoParser extends NakoParserBase {
813
813
  return ans
814
814
  }
815
815
 
816
- yGetArgParen(y: Ast[]): Ast[] { // C言語風呼び出しでカッコの中を取得
816
+ yGetArgParen(y: Ast[], funcName?: string): Ast[] { // C言語風呼び出しでカッコの中を取得
817
817
  let isClose = false
818
818
  const si = this.stack.length
819
819
  while (!this.isEOF()) {
@@ -831,7 +831,8 @@ export class NakoParser extends NakoParserBase {
831
831
  break
832
832
  }
833
833
  if (!isClose) {
834
- throw NakoSyntaxError.fromNode(`C風関数『${(y[0] as AstStrValue).value}』でカッコが閉じていません`, y[0])
834
+ const name = funcName || (y[0] as AstStrValue).value || this.nodeToStr(y[0], { depth: 0, typeName: '関数' }, false)
835
+ throw NakoSyntaxError.fromNode(`C風関数『${name}』でカッコが閉じていません`, y[0])
835
836
  }
836
837
  const a: Ast[] = []
837
838
  while (si < this.stack.length) {
@@ -841,6 +842,31 @@ export class NakoParser extends NakoParserBase {
841
842
  return a
842
843
  }
843
844
 
845
+ /** 関数の戻り値を続けてC風呼び出しする */
846
+ yApplyCallValue(callee: Ast): Ast {
847
+ let node = callee
848
+ while (this.check('(')) {
849
+ this.get() // skip '('
850
+ const args = this.yGetArgParen([node], '関数呼び出しの結果')
851
+ if (!this.check(')')) {
852
+ throw NakoSyntaxError.fromNode('C風関数呼び出しのエラー', node)
853
+ }
854
+ const close = this.get()
855
+ node = {
856
+ type: 'call_value',
857
+ blocks: [node, ...args],
858
+ josi: close?.josi || '',
859
+ startOffset: node.startOffset,
860
+ endOffset: close?.endOffset,
861
+ line: node.line,
862
+ column: node.column,
863
+ file: node.file,
864
+ end: this.peekSourceMap()
865
+ } as AstBlocks
866
+ }
867
+ return node
868
+ }
869
+
844
870
  /** @returns {AstRepeatTimes | null} */
845
871
  yRepeatTime(): AstRepeatTimes | null {
846
872
  const map = this.peekSourceMap()
@@ -1542,7 +1568,7 @@ export class NakoParser extends NakoParserBase {
1542
1568
 
1543
1569
  // 「**して、**」の場合も一度切る
1544
1570
  if (RenbunJosi.indexOf(t.josi) >= 0) {
1545
- funcNode.josi = 'して'
1571
+ funcNode.josi = (this.isReadingCalc && t.josi === 'には') ? '' : 'して'
1546
1572
  return funcNode
1547
1573
  }
1548
1574
  // 続き
@@ -2139,7 +2165,7 @@ export class NakoParser extends NakoParserBase {
2139
2165
  const t = this.yGetArg()
2140
2166
  if (!t) { return null }
2141
2167
  // 助詞がある? つまり、関数呼び出しがある?
2142
- if (t.josi === '') { return t } // 値だけの場合
2168
+ if (t.josi === '' && !this.canNextFuncTakeNoJosiArg()) { return t } // 値だけの場合
2143
2169
  // 関数の呼び出しがあるなら、スタックに載せて関数読み出しを呼ぶ
2144
2170
  const tmpReadingCalc = this.isReadingCalc
2145
2171
  this.isReadingCalc = true
@@ -2176,6 +2202,15 @@ export class NakoParser extends NakoParserBase {
2176
2202
  return fCalc
2177
2203
  }
2178
2204
 
2205
+ /** 次の関数が空助詞の値を引数として受け取れるか */
2206
+ canNextFuncTakeNoJosiArg(): boolean {
2207
+ if (!this.check('func')) { return false }
2208
+ const func = this.peek() as TokenCallFunc | null
2209
+ const josiList = func?.meta?.josi
2210
+ if (!josiList) { return false }
2211
+ return josiList.some((josi) => josi.indexOf('') >= 0)
2212
+ }
2213
+
2179
2214
  /** @returns {Ast | null} */
2180
2215
  yValueKakko(): Ast | null {
2181
2216
  if (!this.check('(')) { return null }
@@ -2318,7 +2353,7 @@ export class NakoParser extends NakoParserBase {
2318
2353
  }
2319
2354
  asyncFn = !!meta.asyncFn
2320
2355
  }
2321
- return {
2356
+ const funcNode = {
2322
2357
  type: 'func',
2323
2358
  name: funcName,
2324
2359
  blocks: args,
@@ -2328,6 +2363,7 @@ export class NakoParser extends NakoParserBase {
2328
2363
  ...map,
2329
2364
  end: this.peekSourceMap()
2330
2365
  } as AstCallFunc
2366
+ return this.yApplyCallValue(funcNode)
2331
2367
  }
2332
2368
  throw NakoSyntaxError.fromNode('C風関数呼び出しのエラー', funcNameToken || NewEmptyToken())
2333
2369
  }
@@ -2497,7 +2533,7 @@ export class NakoParser extends NakoParserBase {
2497
2533
  const word = this.getVarNameRef(t)
2498
2534
 
2499
2535
  // word[n] || word@n
2500
- if (word.josi === '' && this.checkTypes(['[', '@'])) {
2536
+ if ((word.josi === '' && this.checkTypes(['[', '@'])) || (word.josi !== '' && this.check('@'))) {
2501
2537
  const ast: Ast = {
2502
2538
  type: 'ref_array', // 配列参照
2503
2539
  name: word,
@@ -2514,7 +2550,7 @@ export class NakoParser extends NakoParserBase {
2514
2550
  }
2515
2551
 
2516
2552
  // オブジェクトプロパティ構文(参照) word$prop (#1793)
2517
- if (word.josi === '' && (this.check2(['$', 'word']) || this.check2(['$', 'string']))) {
2553
+ if (this.check2(['$', 'word']) || this.check2(['$', 'string'])) {
2518
2554
  const propList: Ast[] = []
2519
2555
  let josi = ''
2520
2556
  while (this.check('$')) {
@@ -109,6 +109,22 @@ describe('array_test', async () => {
109
109
  await cmp('([{"犬": "わんわん"}])[0]$犬を表示', 'わんわん')
110
110
  await cmp('([{"犬": ["わんわん"]}])[0]$犬@0を表示', 'わんわん')
111
111
  })
112
+ it('$の前に助詞を書けるようにする #2313', async () => {
113
+ // 参照
114
+ await cmp('色見本={"赤": "#F00", "青": "#00F"};色見本の$赤を表示。', '#F00')
115
+ await cmp('色見本={"赤": "#F00", "青": "#00F"};色見本から$赤を表示。', '#F00')
116
+ // 代入
117
+ await cmp('色見本={"赤": "#F00", "青": "#00F"};色見本の$赤は「#FF0000」;色見本の$赤を表示。', '#FF0000')
118
+ await cmp('色見本={}; 色見本で$赤は「#F00」; 色見本で$赤を表示。', '#F00')
119
+ })
120
+ it('@の前に助詞を書けるようにする(参照) #2313', async () => {
121
+ await cmp('色見本={"赤": "#F00", "青": "#00F"};色見本の@"赤"を表示。', '#F00')
122
+ await cmp('色見本={"赤": "#F00", "青": "#00F"};色見本から@"赤"を表示。', '#F00')
123
+ })
124
+ it('@の前に助詞を書けるようにする(代入) #2313', async () => {
125
+ await cmp('色見本={"赤": "#F00", "青": "#00F"};色見本の@"赤"は「#FF0000」;色見本の@"赤"を表示。', '#FF0000')
126
+ await cmp('色見本={}; 色見本で@"赤"は「#F00」; 色見本で@"赤"を表示。', '#F00')
127
+ })
112
128
  it('配列+オブジェクトプロパティ #2139', async () => {
113
129
  await cmp('A=[{"b":10}]; A[0].b = 20; A[0].bを表示。', '20')
114
130
  })
@@ -58,6 +58,17 @@ describe('関数呼び出しテスト', async () => {
58
58
  it('**には**構文 - 配列カスタムソート', async () => {
59
59
  await cmp('A=[5,1,3];Aを配列カスタムソートするには(a,b);それはb-a;ここまで;Aを「:」で配列結合して表示', '5:3:1')
60
60
  })
61
+ it('代入文と**には**構文を組み合わせて戻り値を取得する #1994', async () => {
62
+ await cmp(`
63
+ TIDは0.001秒後には
64
+ 「時間だよ」を表示
65
+ ここまで
66
+ TIDのタイマー停止
67
+ もし(TID!=undefined)ならば
68
+ 「OK」を表示
69
+ ここまで
70
+ `, 'OK')
71
+ })
61
72
  it('階乗計算 - 再帰', async () => {
62
73
  await cmp('●(VをAのBで)階乗計算とは;' +
63
74
  'もし、Bが0以下ならば、Vを戻す。;(V*A)をAの(B-1)で階乗計算して戻す。' +
@@ -0,0 +1,112 @@
1
+ /* eslint-disable no-undef */
2
+ import { describe, it } from 'node:test'
3
+ import assert from 'assert'
4
+ import { NakoCompiler } from '../src/nako3.mjs'
5
+
6
+ describe('func_scope', async () => {
7
+ const cmp = async (/** @type {string} */ code, /** @type {string} */ res) => {
8
+ const nako = new NakoCompiler()
9
+ nako.getLogger().debug('code=' + code)
10
+ assert.strictEqual((await nako.runAsync(code, 'main.nako3')).log, res)
11
+ }
12
+
13
+ it('ローカル関数が外側の引数を参照できる #2268', async () => {
14
+ await cmp(`
15
+ curry_add = 関数(あ)
16
+ inner = 関数(い)
17
+ それはあ + い
18
+ ここまで
19
+ innerを戻す
20
+ ここまで
21
+ curry_add(1)(2)を表示
22
+ `, '3')
23
+ })
24
+
25
+ it('戻り値のローカル関数を「それ」から呼び出しても外側の引数を参照できる #2268', async () => {
26
+ await cmp(`
27
+ curry_add = 関数(あ)
28
+ inner = 関数(い)
29
+ それはあ + い
30
+ ここまで
31
+ innerを戻す
32
+ ここまで
33
+ curry_add(1)
34
+ それ(2)を表示
35
+ `, '3')
36
+ })
37
+
38
+ it('戻り値のローカル関数を変数に代入しても外側の引数を参照できる #2268', async () => {
39
+ await cmp(`
40
+ curry_add = 関数(あ)
41
+ inner = 関数(い)
42
+ それはあ + い
43
+ ここまで
44
+ innerを戻す
45
+ ここまで
46
+ curry_add(1)
47
+ それをFに代入
48
+ F(2)を表示
49
+ `, '3')
50
+ })
51
+
52
+ it('カリー化した関数を変数に保持しても外側の引数を参照できる #2268', async () => {
53
+ await cmp(`
54
+ curry_add = 関数(あ)
55
+ inner = 関数(い)
56
+ それはあ + い
57
+ ここまで
58
+ innerを戻す
59
+ ここまで
60
+ inc = curry_add(1)
61
+ inc(2)を表示
62
+ `, '3')
63
+ })
64
+
65
+ it('ローカル関数が外側のローカル変数を参照できる #2268', async () => {
66
+ await cmp(`
67
+ make_add = 関数(あ)
68
+ baseとは変数
69
+ base = あ + 10
70
+ inner = 関数(い)
71
+ それはbase + い
72
+ ここまで
73
+ innerを戻す
74
+ ここまで
75
+ add11 = make_add(1)
76
+ add11(2)を表示
77
+ `, '13')
78
+ })
79
+
80
+ it('戻り値のローカル関数を文として呼んだとき「それ無効」を反映する #2268', async () => {
81
+ await cmp(`
82
+ curry_add = 関数(あ)
83
+ inner = 関数(い)
84
+ それはあ + い
85
+ ここまで
86
+ innerを戻す
87
+ ここまで
88
+ inc = curry_add(1)
89
+ それは99
90
+ 「それ無効」で実行速度優先
91
+ inc(2)
92
+ ここまで
93
+ それを表示
94
+ `, '99')
95
+ })
96
+
97
+ it('関数呼び出しの結果のカッコ未閉じエラーで関数名がundefinedにならない #2268', async () => {
98
+ const nako = new NakoCompiler()
99
+ await assert.rejects(
100
+ () => nako.runAsync(`
101
+ curry_add = 関数(あ)
102
+ inner = 関数(い)
103
+ それはあ + い
104
+ ここまで
105
+ innerを戻す
106
+ ここまで
107
+ curry_add(1)(2を表示
108
+ `, 'main.nako3'),
109
+ /C風関数『関数呼び出しの結果』でカッコが閉じていません/
110
+ )
111
+ })
112
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nadesiko3",
3
- "version": "3.7.19",
3
+ "version": "3.7.21",
4
4
  "description": "Japanese Programming Language",
5
5
  "type": "module",
6
6
  "main": "src/index.mjs",
@@ -84,50 +84,44 @@
84
84
  "utils": "/utils"
85
85
  },
86
86
  "devDependencies": {
87
- "@babel/core": "^7.29.0",
88
- "@babel/preset-env": "^7.29.2",
89
87
  "@eslint/js": "^9.39.3",
90
88
  "@types/fs-extra": "^11.0.4",
91
- "@types/node": "^25.0.10",
89
+ "@types/node": "^26.0.0",
92
90
  "@types/opener": "^1.4.3",
93
91
  "@types/shell-quote": "^1.7.5",
94
- "ace-builds": "^1.43.6",
95
- "babel-loader": "^10.1.1",
96
- "body-parser": "^2.2.2",
97
- "browserslist": "^4.28.1",
92
+ "ace-builds": "^1.44.0",
93
+ "body-parser": "^2.3.0",
94
+ "browserslist": "^4.28.2",
98
95
  "buffer": "^6.0.3",
99
96
  "chai": "^6.2.2",
100
97
  "cross-env": "^10.1.0",
101
- "esbuild": "0.28.0",
98
+ "esbuild": "0.28.1",
102
99
  "eslint": "^9.39.3",
103
100
  "eslint-plugin-import": "^2.32.0",
104
- "eslint-plugin-n": "^17.24.0",
105
- "eslint-plugin-promise": "^7.2.1",
101
+ "eslint-plugin-n": "^18.1.0",
102
+ "eslint-plugin-promise": "^7.3.0",
106
103
  "express": "^5.2.1",
107
104
  "fetch-mock": "^12.6.0",
108
- "hotkeys-js": "^4.0.2",
105
+ "hotkeys-js": "^4.0.4",
109
106
  "module-alias": "^2.3.4",
110
- "multer": "^2.1.1",
111
- "npm-check-updates": "^22.0.1",
107
+ "multer": "^2.2.0",
108
+ "npm-check-updates": "^22.2.3",
112
109
  "prop-types": "^15.8.1",
113
- "react": "^19.2.4",
114
- "react-dom": "^19.2.4",
110
+ "react": "^19.2.7",
111
+ "react-dom": "^19.2.7",
115
112
  "testdouble": "^3.20.2",
116
- "typescript": "^5.9.3",
117
- "typescript-eslint": "^8.56.1",
118
- "url-loader": "^4.1.1",
119
- "util": "^0.12.5",
120
- "webpack": "^5.105.4"
113
+ "typescript": "^6.0.3",
114
+ "typescript-eslint": "^8.61.1",
115
+ "util": "^0.12.5"
121
116
  },
122
117
  "dependencies": {
123
- "dayjs": "^1.11.19",
124
- "fs-extra": "^11.3.3",
118
+ "dayjs": "^1.11.21",
119
+ "fs-extra": "^11.3.5",
125
120
  "html": "^1.0.0",
126
121
  "iconv-lite": "^0.7.2",
127
- "marked": "^18.0.2",
128
- "node-fetch": "^3.3.2",
122
+ "marked": "^18.0.5",
129
123
  "opener": "^1.5.2",
130
- "shell-quote": "^1.8.3",
131
- "smol-toml": "1.6.1"
124
+ "shell-quote": "^1.8.4",
125
+ "smol-toml": "1.7.0"
132
126
  }
133
127
  }