@@ -34,6 +34,40 @@ class Return {
34
34
class Break {
35
35
}
36
36
37
+ class Scope {
38
+ constructor ( parent = null ) {
39
+ this . vars = { } ;
40
+ this . parent = parent ;
41
+ }
42
+ isDefined ( name ) {
43
+ for ( let scope = this ; scope ; scope = scope . parent ) {
44
+ if ( scope . vars [ name ] !== undefined ) {
45
+ return true ;
46
+ }
47
+ }
48
+ return false ;
49
+ }
50
+ getVar ( name ) {
51
+ for ( let scope = this ; scope ; scope = scope . parent ) {
52
+ if ( scope . vars [ name ] !== undefined ) {
53
+ return scope . vars [ name ] ;
54
+ }
55
+ }
56
+ throw new Error ( "定義されていない変数 " + name + " が使われました" ) ;
57
+ }
58
+ setVar ( name , o , forcelocal = false ) {
59
+ if ( ! forcelocal ) {
60
+ for ( let scope = this ; scope ; scope = scope . parent ) {
61
+ if ( scope . vars [ name ] !== undefined ) {
62
+ scope . vars [ name ] = o ;
63
+ return ;
64
+ }
65
+ }
66
+ }
67
+ this . vars [ name ] = o ;
68
+ }
69
+ }
70
+
37
71
export class DNCL3 {
38
72
constructor ( s , callbackoutput ) {
39
73
this . s = s . replaceAll ( "\r" , "" ) ;
@@ -811,55 +845,56 @@ export class DNCL3 {
811
845
if ( c . type == "eof" ) break ;
812
846
}
813
847
}
814
- getArrayIndex ( ast ) {
815
- const prop = this . calcExpression ( ast ) ;
848
+ getArrayIndex ( ast , scope ) {
849
+ const prop = this . calcExpression ( ast , scope ) ;
816
850
if ( prop < 0 || typeof prop == "string" && parseInt ( prop ) . toString ( ) != prop ) {
817
851
throw new Error ( "配列には0または正の整数のみ指定可能です" ) ;
818
852
}
819
853
return prop ;
820
854
}
821
- runBlock ( ast ) {
855
+ runBlock ( ast , scope ) {
822
856
const body = ast . type == "BlockStatement" ||
823
857
ast . type == "Program" ? ast . body :
824
858
ast . type == "SequenceExpression" ? ast . expressions : [ ast ] ;
825
859
for ( const cmd of body ) {
826
860
//console.log(cmd)
827
861
if ( cmd . type == "ExpressionStatement" ) {
828
- this . runBlock ( cmd . expression ) ;
862
+ this . runBlock ( cmd . expression , scope ) ;
829
863
} else if ( cmd . type == "AssignmentExpression" ) {
830
864
const name = this . getVarName ( cmd . left ) ;
831
- if ( this . vars [ name ] !== undefined && isConstantName ( name ) ) {
865
+ if ( scope . isDefined ( name ) && isConstantName ( name ) ) {
832
866
throw new Error ( "定数には再代入できません" ) ;
833
867
}
834
868
if ( cmd . left . type == "Identifier" ) {
835
- this . vars [ name ] = this . calcExpression ( cmd . right ) ;
869
+ scope . setVar ( name , this . calcExpression ( cmd . right , scope ) ) ;
836
870
} else if ( cmd . left . type == "MemberExpression" ) {
837
- if ( this . vars [ name ] === undefined ) {
838
- this . vars [ name ] = [ ] ;
871
+ if ( ! scope . isDefined ( name ) ) {
872
+ scope . setVar ( name , [ ] ) ;
839
873
}
840
- const idx = this . getArrayIndex ( cmd . left . property ) ;
841
- this . vars [ name ] [ idx ] = this . calcExpression ( cmd . right ) ;
874
+ const idx = this . getArrayIndex ( cmd . left . property , scope ) ;
875
+ scope . getVar ( name ) [ idx ] = this . calcExpression ( cmd . right , scope ) ;
842
876
} else {
843
877
throw new Error ( "非対応の type です " + cmd . left . type ) ;
844
878
}
845
879
} else if ( cmd . type == "CallExpression" ) {
846
880
const name = cmd . callee . name ;
847
881
if ( name == "print" ) {
848
- this . output ( cmd . arguments . map ( i => this . calcExpression ( i ) ) . join ( " " ) ) ;
882
+ this . output ( cmd . arguments . map ( i => this . calcExpression ( i , scope ) ) . join ( " " ) ) ;
849
883
} else {
850
- if ( this . vars [ name ] === undefined ) {
884
+ if ( ! scope . isDefined ( name ) ) {
851
885
throw new Error ( "定義されていない関数 " + name + " が使われました" ) ;
852
886
}
853
- const func = this . vars [ name ] ;
887
+ const func = scope . getVar ( name ) ;
854
888
if ( ast . arguments . length != func . params . length ) {
855
889
throw new Error ( "引数の数が合っていません" ) ;
856
890
}
891
+ const scope2 = new Scope ( scope ) ;
857
892
for ( let i = 0 ; i < ast . arguments . length ; i ++ ) {
858
893
const localvarname = func . params [ i ] . name ;
859
- this . vars [ localvarname ] = this . calcExpression ( ast . arguments [ i ] ) ;
894
+ scope2 . setVar ( localvarname , this . calcExpression ( ast . arguments [ i ] , scope ) , true ) ;
860
895
}
861
896
try {
862
- this . runBlock ( func . body ) ;
897
+ this . runBlock ( func . body , scope2 ) ;
863
898
//throw new Error("関数が値を返しませんでした");
864
899
} catch ( e ) {
865
900
if ( e instanceof Return ) {
@@ -869,18 +904,18 @@ export class DNCL3 {
869
904
}
870
905
}
871
906
} else if ( cmd . type == "IfStatement" ) {
872
- const cond = this . calcExpression ( cmd . test ) ;
907
+ const cond = this . calcExpression ( cmd . test , scope ) ;
873
908
if ( cond ) {
874
- this . runBlock ( cmd . consequent ) ;
909
+ this . runBlock ( cmd . consequent , scope ) ;
875
910
} else if ( cmd . alternate ) {
876
- this . runBlock ( cmd . alternate ) ;
911
+ this . runBlock ( cmd . alternate , scope ) ;
877
912
}
878
913
} else if ( cmd . type == "WhileStatement" ) {
879
914
try {
880
915
for ( let i = 0 ; ; i ++ ) {
881
- const cond = this . calcExpression ( cmd . test ) ;
916
+ const cond = this . calcExpression ( cmd . test , scope ) ;
882
917
if ( ! cond ) break ;
883
- this . runBlock ( cmd . body ) ;
918
+ this . runBlock ( cmd . body , scope ) ;
884
919
if ( i >= MAX_LOOP ) {
885
920
throw new Error ( MAX_LOOP + "回の繰り返し上限に達しました" ) ;
886
921
}
@@ -893,8 +928,8 @@ export class DNCL3 {
893
928
} else if ( cmd . type == "DoWhileStatement" ) {
894
929
try {
895
930
for ( let i = 0 ; ; i ++ ) {
896
- this . runBlock ( cmd . body ) ;
897
- const cond = this . calcExpression ( cmd . test ) ;
931
+ this . runBlock ( cmd . body , scope ) ;
932
+ const cond = this . calcExpression ( cmd . test , scope ) ;
898
933
if ( ! cond ) break ;
899
934
if ( i >= MAX_LOOP ) {
900
935
throw new Error ( MAX_LOOP + "回の繰り返し上限に達しました" ) ;
@@ -906,13 +941,13 @@ export class DNCL3 {
906
941
}
907
942
}
908
943
} else if ( cmd . type == "ForStatement" ) {
909
- this . runBlock ( cmd . init ) ;
944
+ this . runBlock ( cmd . init , scope ) ;
910
945
try {
911
946
for ( let i = 0 ; ; i ++ ) {
912
- const cond = this . calcExpression ( cmd . test ) ;
947
+ const cond = this . calcExpression ( cmd . test , scope ) ;
913
948
if ( ! cond ) break ;
914
- this . runBlock ( cmd . body ) ;
915
- this . runBlock ( cmd . update ) ;
949
+ this . runBlock ( cmd . body , scope ) ;
950
+ this . runBlock ( cmd . update , scope ) ;
916
951
if ( i >= MAX_LOOP ) {
917
952
throw new Error ( MAX_LOOP + "回の繰り返し上限に達しました" ) ;
918
953
}
@@ -924,12 +959,12 @@ export class DNCL3 {
924
959
}
925
960
} else if ( cmd . type == "FunctionDeclaration" ) {
926
961
const name = cmd . id . name ;
927
- if ( this . vars [ name ] !== undefined ) {
962
+ if ( scope . isDefined ( name ) ) {
928
963
throw new Error ( "すでに宣言済みに名前では関数を定義できません" ) ;
929
964
}
930
- this . vars [ name ] = cmd ;
965
+ scope . setVar ( name , cmd ) ;
931
966
} else if ( cmd . type == "ReturnStatement" ) {
932
- const val = this . calcExpression ( cmd . argument ) ;
967
+ const val = this . calcExpression ( cmd . argument , scope ) ;
933
968
throw new Return ( val ) ;
934
969
} else if ( cmd . type == "BreakStatement" ) {
935
970
throw new Break ( ) ;
@@ -939,7 +974,8 @@ export class DNCL3 {
939
974
}
940
975
}
941
976
run ( ) {
942
- this . runBlock ( this . ast ) ;
977
+ this . scope = new Scope ( ) ;
978
+ this . runBlock ( this . ast , this . scope ) ;
943
979
//console.log(this.vars);
944
980
}
945
981
getVarName ( ast ) {
@@ -949,30 +985,30 @@ export class DNCL3 {
949
985
else throw new Error ( "非対応の type です " + ast . type ) ;
950
986
}
951
987
}
952
- calcExpression ( ast ) {
988
+ calcExpression ( ast , scope ) {
953
989
if ( ast . type == "Literal" ) {
954
990
return ast . value ;
955
991
} else if ( ast . type == "Identifier" ) {
956
- if ( this . vars [ ast . name ] === undefined ) {
992
+ if ( ! scope . isDefined ( ast . name ) ) {
957
993
//console.log("var", this.vars)
958
994
throw new Error ( "初期化されていない変数 " + ast . name + " が使われました" ) ;
959
995
}
960
- return this . vars [ ast . name ] ;
996
+ return scope . getVar ( ast . name ) ;
961
997
} else if ( ast . type == "MemberExpression" ) {
962
998
const name = this . getVarName ( ast ) ;
963
- if ( this . vars [ name ] === undefined ) {
999
+ if ( ! scope . isDefined ( name ) ) {
964
1000
throw new Error ( "初期化されていない配列 " + name + " が使われました" ) ;
965
1001
}
966
- const idx = this . getArrayIndex ( ast . property ) ;
967
- const v = this . vars [ name ] ;
1002
+ const idx = this . getArrayIndex ( ast . property , scope ) ;
1003
+ const v = scope . getVar ( name ) ;
968
1004
if ( typeof v == "string" ) {
969
1005
if ( idx >= 0 && idx < v . length ) return v [ idx ] ;
970
1006
return "" ;
971
1007
} else {
972
1008
return v [ idx ] ;
973
1009
}
974
1010
} else if ( ast . type == "UnaryExpression" ) {
975
- const n = this . calcExpression ( ast . argument ) ;
1011
+ const n = this . calcExpression ( ast . argument , scope ) ;
976
1012
if ( ast . operator == "not" ) {
977
1013
return ! n ;
978
1014
} else if ( ast . operator == "-" ) {
@@ -981,11 +1017,11 @@ export class DNCL3 {
981
1017
throw new Error ( "対応していない演算子 " + ast . operator + " です" ) ;
982
1018
}
983
1019
} else if ( ast . type == "ArrayExpression" ) {
984
- const ar = ast . elements . map ( i => this . calcExpression ( i ) ) ;
1020
+ const ar = ast . elements . map ( i => this . calcExpression ( i , scope ) ) ;
985
1021
return ar ;
986
1022
} else if ( ast . type == "BinaryExpression" || ast . type == "LogicalExpression" ) {
987
- const n = this . calcExpression ( ast . left ) ;
988
- const m = this . calcExpression ( ast . right ) ;
1023
+ const n = this . calcExpression ( ast . left , scope ) ;
1024
+ const m = this . calcExpression ( ast . right , scope ) ;
989
1025
const op = ast . operator ;
990
1026
if ( typeof n == "string" || typeof m == "string" ) {
991
1027
if ( op != "+" && op != "==" && op != "!=" ) throw new Error ( "文字列では使用できない演算子です: " + op ) ;
@@ -1027,26 +1063,27 @@ export class DNCL3 {
1027
1063
if ( ast . arguments . length > 1 ) {
1028
1064
throw new Error ( "引数の数が合っていません" ) ;
1029
1065
}
1030
- const q = ast . arguments . length ? this . calcExpression ( ast . arguments [ 0 ] ) : "入力してください" ;
1066
+ const q = ast . arguments . length ? this . calcExpression ( ast . arguments [ 0 ] , scope ) : "入力してください" ;
1031
1067
const s = prompt ( q ) ;
1032
1068
if ( s == null ) return "" ;
1033
1069
const f = parseFloat ( s ) ;
1034
1070
if ( ! isNaN ( f ) && f . toString ( ) == s ) return f ;
1035
1071
return s ;
1036
1072
}
1037
- if ( this . vars [ name ] === undefined ) {
1073
+ if ( ! scope . isDefined ( name ) ) {
1038
1074
throw new Error ( "定義されていない関数 " + name + " が使われました" ) ;
1039
1075
}
1040
- const func = this . vars [ name ] ;
1076
+ const func = scope . getVar ( name ) ;
1041
1077
if ( ast . arguments . length != func . params . length ) {
1042
1078
throw new Error ( "引数の数が合っていません" ) ;
1043
1079
}
1080
+ const scope2 = new Scope ( scope ) ;
1044
1081
for ( let i = 0 ; i < ast . arguments . length ; i ++ ) {
1045
1082
const localvarname = func . params [ i ] . name ;
1046
- this . vars [ localvarname ] = this . calcExpression ( ast . arguments [ i ] ) ;
1083
+ scope2 . setVar ( localvarname , this . calcExpression ( ast . arguments [ i ] , scope ) , true ) ;
1047
1084
}
1048
1085
try {
1049
- this . runBlock ( func . body ) ;
1086
+ this . runBlock ( func . body , scope2 ) ;
1050
1087
throw new Error ( "関数が値を返しませんでした" ) ;
1051
1088
} catch ( e ) {
1052
1089
if ( e instanceof Return ) {
@@ -1059,11 +1096,14 @@ export class DNCL3 {
1059
1096
}
1060
1097
}
1061
1098
getVars ( ) {
1099
+ const vars = this . scope . vars ;
1062
1100
const res = { } ;
1063
- for ( const name in this . vars ) {
1064
- const o = this . vars [ name ] ;
1101
+ for ( const name in vars ) {
1102
+ const o = vars [ name ] ;
1065
1103
if ( typeof o == "object" && o . type == "FunctionDeclaration" ) {
1066
1104
res [ name ] = "[function]" ;
1105
+ } else if ( typeof o == "function" ) {
1106
+ res [ name ] = "[function in js]" ;
1067
1107
} else {
1068
1108
res [ name ] = o ;
1069
1109
}
0 commit comments