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.
- package/batch/command.txt +115 -113
- package/core/package.json +2 -2
- package/core/src/README.md +98 -0
- 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 +21 -27
- package/release/_hash.txt +36 -36
- 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/command_list.json +1 -1
- package/release/edit_main.js +2 -2
- package/release/edit_main.js.map +3 -3
- package/release/editor.js +2 -2
- package/release/plugin_datetime.js +1 -1
- package/release/plugin_datetime.js.map +3 -3
- package/release/plugin_markup.js +28 -28
- package/release/plugin_markup.js.map +3 -3
- package/release/version.js +2 -2
- package/release/version_main.js +2 -2
- package/release/version_main.js.map +2 -2
- package/release/wnako3.js +85 -77
- package/release/wnako3.js.map +3 -3
- package/release/wnako3webworker.js +106 -98
- package/release/wnako3webworker.js.map +3 -3
- package/src/cnako3mod.mjs +1 -2
- package/src/cnako3mod.mts +1 -2
- package/src/nako_version.mjs +2 -2
- package/src/nako_version.mts +2 -2
- package/src/plugin_httpserver.mjs +184 -13
- package/src/plugin_httpserver.mts +176 -10
- package/src/plugin_node.mjs +0 -1
- package/src/plugin_node.mts +2 -3
- package/src/wnako3mod.mjs +3 -0
- package/src/wnako3mod.mts +3 -0
- package/test/common/wnako3mod_test.mjs +48 -0
- package/test/node/package_json_test.mjs +17 -0
- package/test/node/plugin_httpserver_test.mjs +239 -0
- 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/tools/nako3server/index.nako3 +4 -2
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)で階乗計算して戻す。' +
|
|
@@ -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.
|
|
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": "^
|
|
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.
|
|
95
|
-
"
|
|
96
|
-
"
|
|
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.
|
|
98
|
+
"esbuild": "0.28.1",
|
|
102
99
|
"eslint": "^9.39.3",
|
|
103
100
|
"eslint-plugin-import": "^2.32.0",
|
|
104
|
-
"eslint-plugin-n": "^
|
|
105
|
-
"eslint-plugin-promise": "^7.
|
|
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.
|
|
105
|
+
"hotkeys-js": "^4.0.4",
|
|
109
106
|
"module-alias": "^2.3.4",
|
|
110
|
-
"multer": "^2.
|
|
111
|
-
"npm-check-updates": "^22.
|
|
107
|
+
"multer": "^2.2.0",
|
|
108
|
+
"npm-check-updates": "^22.2.3",
|
|
112
109
|
"prop-types": "^15.8.1",
|
|
113
|
-
"react": "^19.2.
|
|
114
|
-
"react-dom": "^19.2.
|
|
110
|
+
"react": "^19.2.7",
|
|
111
|
+
"react-dom": "^19.2.7",
|
|
115
112
|
"testdouble": "^3.20.2",
|
|
116
|
-
"typescript": "^
|
|
117
|
-
"typescript-eslint": "^8.
|
|
118
|
-
"
|
|
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.
|
|
124
|
-
"fs-extra": "^11.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.
|
|
128
|
-
"node-fetch": "^3.3.2",
|
|
122
|
+
"marked": "^18.0.5",
|
|
129
123
|
"opener": "^1.5.2",
|
|
130
|
-
"shell-quote": "^1.8.
|
|
131
|
-
"smol-toml": "1.
|
|
124
|
+
"shell-quote": "^1.8.4",
|
|
125
|
+
"smol-toml": "1.7.0"
|
|
132
126
|
}
|
|
133
127
|
}
|