diff --git a/CMakeLists.txt b/CMakeLists.txt index e249662..ae8213f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(fun VERSION 0.39.16 LANGUAGES C) +project(fun VERSION 0.40.0 LANGUAGES C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) diff --git a/examples/functions_test.fun b/examples/functions/functions_test.fun similarity index 100% rename from examples/functions_test.fun rename to examples/functions/functions_test.fun diff --git a/examples/functions/nested_functions.fun b/examples/functions/nested_functions.fun new file mode 100755 index 0000000..28a0386 --- /dev/null +++ b/examples/functions/nested_functions.fun @@ -0,0 +1,69 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Demonstrates nested functions that are only visible inside the + * outer function where they are defined. This example avoids + * capturing outer variables (closures) and instead passes values + * explicitly, which works with the current implementation. + * + * Added: 2026-04-02 + */ + +print("=== Nested functions (local to outer function) ===") + +// Outer function defines two inner helpers that are only usable inside outer +fun outer(a, b) + // Defined inside outer; not visible as a global symbol + fun times3(x) + return x * 3 + end + + // Another local helper; also only visible within outer + fun sum_plus1(x, y) + return x + y + 1 + end + + // Use the local helpers + t1 = times3(a) + t2 = times3(b) + return sum_plus1(t1, t2) +end + +print("outer(2, 5) -> expected 2*3 + 5*3 + 1 = 22") +print(outer(2, 5)) + +print("") +print("=== Deeper nesting without variable capture ===") + +// Demonstrates function-inside-function-inside-function. +// Each level avoids referencing outer locals directly; values +// are threaded through parameters instead. +fun demo_deep(n) + fun one(x) + fun two(y) + fun three(z) + return z + 1 + end + return three(y) + 1 + end + return two(x) + 1 + end + return one(n) +end + +print("demo_deep(4) -> expected 7") +print(demo_deep(4)) + +/* +Expected output: +=== Nested functions (local to outer function) === +outer(2, 5) -> expected 2*3 + 5*3 + 1 = 22 +22 + +=== Deeper nesting without variable capture === +demo_deep(4) -> expected 7 +7 +*/ diff --git a/examples/snippets/nested.fun b/examples/snippets/nested.fun new file mode 100644 index 0000000..c7bd4b5 --- /dev/null +++ b/examples/snippets/nested.fun @@ -0,0 +1,22 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Demonstrates nested functions that are only visible inside the + * outer function where they are defined. This example avoids + * capturing outer variables (closures) and instead passes values + * explicitly, which works with the current implementation. + * + * Added: 2026-04-02 + */ + +fun outer(x) + fun inner(y) + return y * 2 + end + return inner(x) + inner(3) +end + +print(outer(5)) diff --git a/examples/snippets/parse_test2.fun b/examples/snippets/parse_test2.fun deleted file mode 100755 index 248a940..0000000 --- a/examples/snippets/parse_test2.fun +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env fun - -#include - -cgi = CGI() -pairs = cgi._parse_urlencoded("a=1&b=2&c=3") -i = 0 -n = len(pairs) -while (i < n) - p = pairs[i] - print(to_string(p[0]) + "=" + to_string(p[1])) - i = i + 1 diff --git a/examples/snippets/test_parse.fun b/examples/snippets/test_parse.fun index 248a940..4772b4b 100755 --- a/examples/snippets/test_parse.fun +++ b/examples/snippets/test_parse.fun @@ -1,5 +1,17 @@ #!/usr/bin/env fun +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Demonstrates nested functions that are only visible inside the + * outer function where they are defined. This example avoids + * capturing outer variables (closures) and instead passes values + * explicitly, which works with the current implementation. + * + * Added: 2026-04-02 + */ + #include cgi = CGI() diff --git a/src/parser.c b/src/parser.c index 3b1cae2..2834a8f 100644 --- a/src/parser.c +++ b/src/parser.c @@ -6692,10 +6692,34 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos, } #endif - /* bind function to global: LOAD_CONST ; STORE_GLOBAL fgi */ + /* If we are inside another function (prev != NULL), bind this function as a local + * so that it is only visible within the current function scope. Otherwise, bind + * it as a global (top-level behavior unchanged). */ int fci = bytecode_add_constant(bc, make_function(fn_bc)); bytecode_add_instruction(bc, OP_LOAD_CONST, fci); - bytecode_add_instruction(bc, OP_STORE_GLOBAL, fgi); + if (prev != NULL) { + /* Bind into the OUTER function's local env (prev), not the inner temp env. */ + LocalEnv *save_env = g_locals; + g_locals = prev; + /* declare a local with the function name if not present */ + int lidx = local_find(fname); + if (lidx < 0) { + lidx = local_add(fname); + } + if (lidx < 0) { + /* failed to allocate local slot */ + g_locals = save_env; /* restore before returning */ + g_locals = prev; + free(fname); + return; + } + bytecode_add_instruction(bc, OP_STORE_LOCAL, lidx); + /* restore current env (will be reset to prev below) */ + g_locals = save_env; + } else { + /* top-level: global binding (backward compatible) */ + bytecode_add_instruction(bc, OP_STORE_GLOBAL, fgi); + } g_locals = prev; free(fname);