nadesiko3 3.7.19 → 3.7.20
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 +2 -2
- package/core/package.json +1 -1
- package/core/src/nako_ast.mts +1 -0
- package/core/src/nako_core_version.mjs +2 -2
- package/core/src/nako_core_version.mts +2 -2
- package/core/src/nako_gen.mjs +54 -0
- package/core/src/nako_gen.mts +61 -0
- package/core/src/nako_parser3.mjs +45 -7
- package/core/src/nako_parser3.mts +43 -7
- package/core/test/array_test.mjs +16 -0
- package/core/test/func_call.mjs +11 -0
- package/core/test/func_scope.mjs +112 -0
- package/package.json +2 -2
- package/release/_hash.txt +28 -28
- package/release/_script-tags.txt +16 -16
- package/release/command.json +1 -1
- package/release/command.json.js +1 -1
- package/release/command_cnako3.json +1 -1
- package/release/edit_main.js +1 -1
- package/release/edit_main.js.map +2 -2
- package/release/editor.js +1 -1
- package/release/version.js +1 -1
- package/release/version_main.js +1 -1
- package/release/version_main.js.map +1 -1
- package/release/wnako3.js +84 -76
- package/release/wnako3.js.map +3 -3
- package/release/wnako3webworker.js +106 -98
- package/release/wnako3webworker.js.map +3 -3
- package/src/nako_version.mjs +2 -2
- package/src/nako_version.mts +2 -2
- package/src/plugin_node.mts +2 -2
- package/tools/nako3edit/html/edit.html +21 -0
- package/tools/nako3edit/html/nako3edit.css +10 -0
- package/tools/nako3edit/index.mjs +15 -4
- package/tools/nako3server/index.mjs +15 -4
package/batch/command.txt
CHANGED
|
@@ -789,10 +789,10 @@
|
|
|
789
789
|
| 関数 | フォルダ作成 | PATHの/PATHを/PATHに/PATHへ | ディレクトリPATHを作成して返す(再帰的に作成) | ふぉるださくせい | https://github.com/kujirahand/nadesiko3/tree/master/src/plugin_node.mts#L682
|
|
790
790
|
| 変数 | ファイルコピーデフォルト動作 | | '上書禁止' | ふぁいるこぴーでふぉるとどうさ | https://github.com/kujirahand/nadesiko3/tree/master/src/plugin_node.mts#L686
|
|
791
791
|
| 関数 | ファイルコピー | AからBに/AをBへ | パスAをパスBへファイルコピーする(『ファイルコピーデフォルト動作』が「上書」「上書き」「overwrite」なら上書きコピー、それ以外はコピー先が存在するなら失敗)(非同期関数) | ふぁいるこぴー | https://github.com/kujirahand/nadesiko3/tree/master/src/plugin_node.mts#L692
|
|
792
|
-
| 関数 | ファイル上書コピー | AからBに/AをBへ | パスAをパスBへファイルコピーする(
|
|
792
|
+
| 関数 | ファイル上書コピー | AからBに/AをBへ | パスAをパスBへファイルコピーする(コピー先に内容をマージしてコピー)(非同期関数) | ふぁいるうわがきこぴー | https://github.com/kujirahand/nadesiko3/tree/master/src/plugin_node.mts#L707
|
|
793
793
|
| 関数 | ファイルコピー時 | CALLBACKでAからBに/AをBへ | パスAをパスBへファイルコピーしてcallbackを実行 | ふぁいるこぴーしたとき | https://github.com/kujirahand/nadesiko3/tree/master/src/plugin_node.mts#L717
|
|
794
794
|
| 関数 | ファイル移動 | AからBに/AをBへ | パスAをパスBへ移動する(『ファイルコピーデフォルト動作』が「上書」「上書き」「overwrite」なら上書き移動、それ以外は移動先が存在するなら失敗)(非同期関数) | ふぁいるいどう | https://github.com/kujirahand/nadesiko3/tree/master/src/plugin_node.mts#L730
|
|
795
|
-
| 関数 | ファイル上書移動 | AからBに/AをBへ | パスAをパスBへ移動する(
|
|
795
|
+
| 関数 | ファイル上書移動 | AからBに/AをBへ | パスAをパスBへ移動する(移動先に内容をマージしてコピー後、元を削除)(非同期関数) | ふぁいるうわがきいどう | https://github.com/kujirahand/nadesiko3/tree/master/src/plugin_node.mts#L748
|
|
796
796
|
| 関数 | ファイル処理時 | Fを/Fで | ファイルコピー・移動の処理状況をコールバックFで報告する(変数『対象』に{"件数":N,"現在":M}をセット) | ふぁいるしょりじ | https://github.com/kujirahand/nadesiko3/tree/master/src/plugin_node.mts#L761
|
|
797
797
|
| 関数 | ファイル処理強制停止 | | 『ファイルコピー』『ファイル移動』などの処理を強制停止する | ふぁいるしょりきょうせいていし | https://github.com/kujirahand/nadesiko3/tree/master/src/plugin_node.mts#L773
|
|
798
798
|
| 関数 | ファイル移動時 | CALLBACKでAからBに/AをBへ | パスAをパスBへ移動してcallbackを実行 | ふぁいるいどうしたとき | https://github.com/kujirahand/nadesiko3/tree/master/src/plugin_node.mts#L782
|
package/core/package.json
CHANGED
package/core/src/nako_ast.mts
CHANGED
package/core/src/nako_gen.mjs
CHANGED
|
@@ -65,6 +65,7 @@ export class NakoGen {
|
|
|
65
65
|
/** スタックトップ */
|
|
66
66
|
this.varsSet = { isFunction: false, names: new Set(), readonly: new Set() };
|
|
67
67
|
this.varslistSet[2] = this.varsSet;
|
|
68
|
+
this.functionContextStack = [];
|
|
68
69
|
// 現在定義中の関数名
|
|
69
70
|
this.defFuncName = '';
|
|
70
71
|
// 1以上のとき高速化する。
|
|
@@ -249,6 +250,11 @@ export class NakoGen {
|
|
|
249
250
|
code += 'const __v0 = __self.__v0 = __self.__varslist[0];\n';
|
|
250
251
|
code += 'const __v1 = __self.__v1 = __self.__varslist[1];\n';
|
|
251
252
|
code += 'const __vars = __self.__vars = __self.__varslist[2];\n';
|
|
253
|
+
code += 'const __nako_make_closure = (local, parent) => ({\n' +
|
|
254
|
+
' has: (key) => local.has(key) || (parent !== null && parent.has(key)),\n' +
|
|
255
|
+
' get: (key) => local.has(key) ? local.get(key) : (parent !== null ? parent.get(key) : undefined),\n' +
|
|
256
|
+
' set: (key, value) => { if (local.has(key) || parent === null || !parent.has(key)) { local.set(key, value); } else { parent.set(key, value); } return value; }\n' +
|
|
257
|
+
'});\n';
|
|
252
258
|
code += `const __modList = __self.__modList = ${JSON.stringify(com.getModList())}\n`;
|
|
253
259
|
code += 'const __line = (lineno) => { __self.__v0.set(\'__line\', lineno); }\n';
|
|
254
260
|
code += '__v0.set(\'__line\', \'l0:__getDefFuncCode\');\n';
|
|
@@ -491,6 +497,9 @@ export class NakoGen {
|
|
|
491
497
|
case 'calc_func':
|
|
492
498
|
code += this.convCallFunc(node, isExpression);
|
|
493
499
|
break;
|
|
500
|
+
case 'call_value':
|
|
501
|
+
code += this.convCallValue(node, isExpression);
|
|
502
|
+
break;
|
|
494
503
|
case 'if':
|
|
495
504
|
code += this.convIf(node);
|
|
496
505
|
break;
|
|
@@ -574,6 +583,28 @@ export class NakoGen {
|
|
|
574
583
|
js_set: this.varname_set(name, String(jsvalue))
|
|
575
584
|
};
|
|
576
585
|
}
|
|
586
|
+
// 外側の無名関数のローカル変数をクロージャとして参照する #2268
|
|
587
|
+
const currentFunc = this.functionContextStack[this.functionContextStack.length - 1];
|
|
588
|
+
if (this.varslistSet.length > 4 && currentFunc?.isAnonymous) {
|
|
589
|
+
const currentIndex = this.varslistSet.length - 1;
|
|
590
|
+
for (let i = currentIndex - 1; i >= 3; i--) {
|
|
591
|
+
if (this.varslistSet[i].names.has(name)) {
|
|
592
|
+
for (const context of this.functionContextStack) {
|
|
593
|
+
if (context.isAnonymous && context.varsIndex > i) {
|
|
594
|
+
context.usesClosure = true;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
const nameJson = JSON.stringify(name);
|
|
598
|
+
return {
|
|
599
|
+
i,
|
|
600
|
+
name,
|
|
601
|
+
isTop: false,
|
|
602
|
+
js: `__nako_closure.get(${nameJson})`,
|
|
603
|
+
js_set: `__nako_closure.set(${nameJson}, ${jsvalue ?? 'undefined'})`
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
577
608
|
// __varslist ?
|
|
578
609
|
for (let i = 2; i >= 0; i--) {
|
|
579
610
|
if (this.varslistSet[i].names.has(name)) {
|
|
@@ -787,6 +818,12 @@ export class NakoGen {
|
|
|
787
818
|
this.varsSet = { isFunction: true, names: initialNames, readonly: new Set() };
|
|
788
819
|
// ローカル変数をPUSHする
|
|
789
820
|
this.varslistSet.push(this.varsSet);
|
|
821
|
+
const funcContext = {
|
|
822
|
+
isAnonymous: name === '',
|
|
823
|
+
varsIndex: this.varslistSet.length - 1,
|
|
824
|
+
usesClosure: false
|
|
825
|
+
};
|
|
826
|
+
this.functionContextStack.push(funcContext);
|
|
790
827
|
// JSの引数と引数をバインド
|
|
791
828
|
if (isExtractJS) {
|
|
792
829
|
variableDeclarations += indent + 'var 引数 = arguments;\n';
|
|
@@ -903,6 +940,10 @@ export class NakoGen {
|
|
|
903
940
|
const lineInfo = ' ' + this.convLineno(node, true, 1) + '\n';
|
|
904
941
|
code = tof + performanceMonitorInjectAtStart + pushStack + variableDeclarations + lineInfo + code + popStack;
|
|
905
942
|
code += endOfFunction;
|
|
943
|
+
if (funcContext.isAnonymous && funcContext.usesClosure) {
|
|
944
|
+
const parentClosure = `(typeof __nako_closure === 'undefined' ? null : __nako_closure)`;
|
|
945
|
+
code = `(function(__nako_closure) {\n return ${code}\n})(__nako_make_closure(__self.__vars, ${parentClosure}))`;
|
|
946
|
+
}
|
|
906
947
|
// 名前があれば、関数を登録する
|
|
907
948
|
if (name) {
|
|
908
949
|
const nameFuncValue = this.nakoFuncList.get(name);
|
|
@@ -914,6 +955,7 @@ export class NakoGen {
|
|
|
914
955
|
}
|
|
915
956
|
this.usedAsyncFn = oldUsedAsyncFn; // 以前の値を戻す
|
|
916
957
|
this.varslistSet.pop();
|
|
958
|
+
this.functionContextStack.pop();
|
|
917
959
|
this.varsSet = this.varslistSet[this.varslistSet.length - 1];
|
|
918
960
|
if (name) {
|
|
919
961
|
this.__self.__varslist[1].set(name, code);
|
|
@@ -1650,6 +1692,18 @@ export class NakoGen {
|
|
|
1650
1692
|
}
|
|
1651
1693
|
return code;
|
|
1652
1694
|
}
|
|
1695
|
+
convCallValue(node, isExpression) {
|
|
1696
|
+
const callee = this._convGen(node.blocks[0], true);
|
|
1697
|
+
const args = node.blocks.slice(1).map((arg) => this._convGen(arg, true));
|
|
1698
|
+
args.push('__self');
|
|
1699
|
+
const funcCall = `${callee}(${args.join(',')})`;
|
|
1700
|
+
if (isExpression) {
|
|
1701
|
+
return funcCall;
|
|
1702
|
+
}
|
|
1703
|
+
const sorePrefex = (this.speedMode.invalidSore === 0) ? '__self.__setSore(' : '';
|
|
1704
|
+
const sorePostfix = (this.speedMode.invalidSore === 0) ? ')' : '';
|
|
1705
|
+
return this.convLineno(node, false) + `${sorePrefex}${funcCall}${sorePostfix};\n`;
|
|
1706
|
+
}
|
|
1653
1707
|
convRenbun(node) {
|
|
1654
1708
|
const right = this._convGen(node.blocks[1], true);
|
|
1655
1709
|
const left = this._convGen(node.blocks[0], false);
|
package/core/src/nako_gen.mts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
2553
|
+
if (this.check2(['$', 'word']) || this.check2(['$', 'string'])) {
|
|
2518
2554
|
const propList: Ast[] = []
|
|
2519
2555
|
let josi = ''
|
|
2520
2556
|
while (this.check('$')) {
|
package/core/test/array_test.mjs
CHANGED
|
@@ -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
|
})
|
package/core/test/func_call.mjs
CHANGED
|
@@ -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)で階乗計算して戻す。' +
|