From 43ce9f83af972e38ffae3bb339a211765077f60c Mon Sep 17 00:00:00 2001 From: hanez Date: Mon, 27 Apr 2026 02:06:20 +0200 Subject: [PATCH] parser: fix variable scoping by defaulting to local scope in functions. (0.41.0) This change addresses a critical issue where common variable names in library functions (like 'i', 'n', 'src') were being treated as globals by default, leading to state collisions and unexpected side effects across different files. Changes: - Modified `src/parser.c` to automatically treat new variables assigned within a function body as locals unless they were already declared as globals. - Introduced `sym_find` in the parser to distinguish between finding an existing global and creating a new one. - Reverted `lib/net/cgi.fun` to use standard, short variable names, confirming that they no longer interfere with the caller's scope. - Verified that explicit global updates still work if the variable was already defined at the top-level scope. --- CMakeLists.txt | 2 +- lib/net/cgi.fun | 59 ++++++++++++++++++++++++------------------------- src/parser.c | 20 +++++++++++++++-- 3 files changed, 48 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0032825..43b39d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(fun VERSION 0.40.8 LANGUAGES C) +project(fun VERSION 0.41.0 LANGUAGES C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) diff --git a/lib/net/cgi.fun b/lib/net/cgi.fun index 3624c6a..b65cfcf 100644 --- a/lib/net/cgi.fun +++ b/lib/net/cgi.fun @@ -256,39 +256,38 @@ class CGI() return resp fun url_decode(this, s) - source_str = to_string(s) - decoded_result = "" - number current_pos = 0 - number source_len = len(source_str) - while (current_pos < source_len) - current_char = substr(source_str, current_pos, 1) - if (current_char == "+") - decoded_result = decoded_result + " " - current_pos = current_pos + 1 - else if (current_char == "%" && current_pos + 2 < source_len) - hex_digit1 = substr(source_str, current_pos + 1, 1) - hex_digit2 = substr(source_str, current_pos + 2, 1) - hex_table = "0123456789ABCDEF" - val1 = find(hex_table, str_to_upper(hex_digit1)) - val2 = find(hex_table, str_to_upper(hex_digit2)) - if (val1 >= 0 && val2 >= 0) - number char_code = val1 * 16 + val2 - // Using a more robust table with explicit characters - ascii_table = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" - if (char_code >= 32 && char_code <= 126) - decoded_result = decoded_result + substr(ascii_table, char_code - 32, 1) + src = to_string(s) + out = [] + i = 0 + n = len(src) + while (i < n) + ch = substr(src, i, 1) + if (ch == "+") + push(out, " ") + i = i + 1 + else if (ch == "%" && i + 2 < n) + h1 = substr(src, i + 1, 1) + h2 = substr(src, i + 2, 1) + hexdigits = "0123456789ABCDEF" + v1 = find(hexdigits, str_to_upper(h1)) + v2 = find(hexdigits, str_to_upper(h2)) + if (v1 >= 0 && v2 >= 0) + code = v1 * 16 + v2 + ascii = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + if (code >= 32 && code <= 126) + push(out, substr(ascii, code - 32, 1)) else - decoded_result = decoded_result + "%" - decoded_result = decoded_result + hex_digit1 - decoded_result = decoded_result + hex_digit2 - current_pos = current_pos + 3 + push(out, "%") + push(out, h1) + push(out, h2) + i = i + 3 else - decoded_result = decoded_result + current_char - current_pos = current_pos + 1 + push(out, ch) + i = i + 1 else - decoded_result = decoded_result + current_char - current_pos = current_pos + 1 - return decoded_result + push(out, ch) + i = i + 1 + return join(out, "") fun _merge_params(this, pairs) // pairs: array of [key, value] entries diff --git a/src/parser.c b/src/parser.c index af40c75..2bedf2c 100644 --- a/src/parser.c +++ b/src/parser.c @@ -201,10 +201,16 @@ static struct { int count; } G = {{0}, {0}, {0}, 0}; -static int sym_index(const char *name) { +static int sym_find(const char *name) { for (int i = 0; i < G.count; ++i) { if (strcmp(G.names[i], name) == 0) return i; } + return -1; +} + +static int sym_index(const char *name) { + int existing = sym_find(name); + if (existing >= 0) return existing; if (G.count >= MAX_GLOBALS) { parser_fail(0, "Too many globals (max %d)", MAX_GLOBALS); return 0; @@ -5580,7 +5586,17 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si /* assignment or simple call */ int lidx = local_find(name); - int gi = (lidx < 0) ? sym_index(name) : -1; + int gi = (lidx < 0) ? sym_find(name) : -1; + if (lidx < 0 && gi < 0 && g_locals) { + /* Auto-declare as local if we're in a function and it's not known as local or global */ + lidx = local_add(name); + } + if (lidx < 0 && gi < 0) { + /* Not local, not existing global -> it's a new global (or a new local if we were in a function but lidx still < 0?) + Actually if g_locals was non-null we already added it to lidx. + If g_locals is NULL, we create it as global. */ + gi = sym_index(name); + } skip_spaces(src, len, &local_pos); /* object field assignment: supports