diff --git a/kerboscript_tests/user_functions/functest29.ks b/kerboscript_tests/user_functions/functest29.ks new file mode 100644 index 000000000..af4c00fee --- /dev/null +++ b/kerboscript_tests/user_functions/functest29.ks @@ -0,0 +1,14 @@ +print "Testing default function scoping rules". + +run once "functest29_lib.ks". + +print "Should say Func1: " + func1(). +if (func1() <> "Func1") print "!!!!!!!!!! ERROR ERROR ERROR !!!!!!!!!!!" + char(7) + char(7). +local f2 is getfunc2(). +print "Should say Func2: " + f2(). +if (f2() <> "Func2") print "!!!!!!!!!! ERROR ERROR ERROR !!!!!!!!!!!" + char(7) + char(7). + +local f3 is getfunc3(). +print "Should say Func3: " + f3(). +if (f3() <> "Func3") print "!!!!!!!!!! ERROR ERROR ERROR !!!!!!!!!!!" + char(7) + char(7). + diff --git a/kerboscript_tests/user_functions/functest29_lib.ks b/kerboscript_tests/user_functions/functest29_lib.ks new file mode 100644 index 000000000..ad3354c3e --- /dev/null +++ b/kerboscript_tests/user_functions/functest29_lib.ks @@ -0,0 +1,17 @@ +function func1 { + return "Func1". +} + +function getfunc2 { + function sameName { // <--------+--- These two innner functions have the same name + return "Func2". // <-----+--|--- But have different effects when called. + } // | | + return sameName@. // <--+--|--|--- These should return the 2 different versions. +} // | | | + // | | | +function getfunc3 { // | | | + function sameName { // <--|--|--' + return "Func3". // <--|--' + } // | + return sameName@. // <--' +} // diff --git a/kerboscript_tests/user_functions/functest30.ks b/kerboscript_tests/user_functions/functest30.ks new file mode 100644 index 000000000..f34a8145d --- /dev/null +++ b/kerboscript_tests/user_functions/functest30.ks @@ -0,0 +1,17 @@ +print "Testing explicit function scope keywords override.". + +run once "functest30_lib.ks". +local del is outer(). + +print "Should say global1: " + global1(). +if (global1() <> "global1") print "!!!!!!!!!! ERROR ERRROR !!!!!!!!!!" + char(7) + char (7). +print "Should say global2: " + global2(). +if (global2() <> "global2") print "!!!!!!!!!! ERROR ERRROR !!!!!!!!!!" + char(7) + char (7). +print "Should say global3: " + global3(). +if (global3() <> "global3") print "!!!!!!!!!! ERROR ERRROR !!!!!!!!!!" + char(7) + char (7). +print "Should say local2: " + del(). +if (del() <> "local2") print "!!!!!!!!!! ERROR ERRROR !!!!!!!!!!" + char(7) + char (7). + +print " ". +print "PROGRAM SHOULD NOW FAIL WITH 'local1' NOT FOUND.". +print local1(). diff --git a/kerboscript_tests/user_functions/functest30_lib.ks b/kerboscript_tests/user_functions/functest30_lib.ks new file mode 100644 index 000000000..78cbbfb7a --- /dev/null +++ b/kerboscript_tests/user_functions/functest30_lib.ks @@ -0,0 +1,11 @@ +global function global1 { return "global1". } +function global2 { return "global2". } +local function local1 { return "local1". } + +function outer { + function local2 { return "local2". } // local by default + global function global3 { return "global3". } // explicit global keyword despite being nested + function local1 { return local2(). } // Note this local1 should mask the outer local1 + + return local1@. // via this delegate, it actually will call local2(). +} diff --git a/src/kOS.Safe/Compilation/KS/Compiler.cs b/src/kOS.Safe/Compilation/KS/Compiler.cs index ad30140da..f85a53c93 100644 --- a/src/kOS.Safe/Compilation/KS/Compiler.cs +++ b/src/kOS.Safe/Compilation/KS/Compiler.cs @@ -2775,14 +2775,21 @@ private StorageModifier GetStorageModifierForDeclare(ParseNode node) ParseNode lastSubNode = node.Nodes[node.Nodes.Count-1]; // Default varies depending on which kind of statement it is. - // locks are default global, and functions declared at file - // scope are default global, while everything else is default local: StorageModifier modifier = StorageModifier.LOCAL; - if (lastSubNode.Token.Type == TokenType.declare_lock_clause || - lastSubNode.Token.Type == TokenType.declare_function_clause) + // locks are default global: + if (lastSubNode.Token.Type == TokenType.declare_lock_clause) { modifier = StorageModifier.GLOBAL; } + // functions declared at file scope are default global. inner functions are default local: + else if (lastSubNode.Token.Type == TokenType.declare_function_clause) + { + ParseNode containingNode = GetContainingBlockNode(node); + if (containingNode != null && containingNode.Token.Type == TokenType.Start) // file scope + modifier = StorageModifier.GLOBAL; + else + modifier = StorageModifier.LOCAL; + } bool storageKeywordMissing = true; @@ -2817,7 +2824,6 @@ private StorageModifier GetStorageModifierForDeclare(ParseNode node) LineCol location = GetLineCol(node); throw new KOSCommandInvalidHereException(location, "GLOBAL", "in a parameter declaration", "in a variable declaration"); } - return modifier; }