diff --git a/CMakeLists.txt b/CMakeLists.txt index 328dea4..3f305a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(fun VERSION 0.37.17 LANGUAGES C) +project(fun VERSION 0.37.13 LANGUAGES C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) diff --git a/examples/boolean_decl.fun b/examples/boolean_decl.fun index c49ed3d..4caaa6c 100755 --- a/examples/boolean_decl.fun +++ b/examples/boolean_decl.fun @@ -15,9 +15,3 @@ boolean b = false print("b => " + to_string(b)) /* Try typeof via built-in function if available */ print("typeof(b) => " + typeof(b)) - -/* Expected output: -b => false -typeof(b) => Boolean -*/ - diff --git a/examples/booleans.fun b/examples/booleans.fun index b7ed476..840c64a 100755 --- a/examples/booleans.fun +++ b/examples/booleans.fun @@ -52,25 +52,3 @@ if f print("if f: branch taken (unexpected)") else print("if f: else branch taken") - -/* Expected output: -typeof(t) => Boolean -typeof(f) => Boolean -t literal => true -f literal => false -true && true => true -true && false => false -false || true => 1 -false || false => 0 -!true => false -!false => true -true == true => true -true != false => true -true == 1 => true -false == 0 => true -1 == true => true -0 != true => true -if t: branch taken -if f: else branch taken -*/ - diff --git a/examples/cast_demo.fun b/examples/cast_demo.fun index a18c28b..f937b0c 100755 --- a/examples/cast_demo.fun +++ b/examples/cast_demo.fun @@ -52,26 +52,3 @@ print(typeof(cast(42, "Function"))) // -> "Nil" print(cast(cast("42", "Number"), "String")) // -> "42" print("=== Done ===") - -/* Expected output: -=== CAST demo === -123 -0 -42 -100 -String -0 -1 -Array -1 -42 -String -0 -Nil -Function -Nil -42 -=== Done === -*/ - - diff --git a/examples/conversions_showcase.fun b/examples/conversions_showcase.fun old mode 100755 new mode 100644 diff --git a/examples/curl_download.fun b/examples/curl_download.fun index 958f791..a0f72de 100755 --- a/examples/curl_download.fun +++ b/examples/curl_download.fun @@ -22,8 +22,3 @@ if ok == 1 print("Downloaded to " + path) else print("Download failed") - -/* Expected output: -Downloaded to ./downloaded.png -*/ - diff --git a/examples/curl_get_json.fun b/examples/curl_get_json.fun index ff24f5c..713cf88 100755 --- a/examples/curl_get_json.fun +++ b/examples/curl_get_json.fun @@ -23,9 +23,3 @@ print("Raw length: " + to_string(len(resp))) obj = json_parse(resp) if obj != nil print("Title: " + obj["slideshow"]["title"]) - -/* Expected output: -Raw length: 429 -Title: Sample Slide Show -*/ - diff --git a/examples/curl_post.fun b/examples/curl_post.fun index 802ea56..988c458 100755 --- a/examples/curl_post.fun +++ b/examples/curl_post.fun @@ -30,30 +30,3 @@ if obj != nil if obj != nil print("Origin: " + to_string(obj["origin"])) - -/* Possible output: -Response: { - "args": {}, - "data": "", - "files": {}, - "form": { - "lang": "fun", - "name": "Fun" - }, - "headers": { - "Accept": "*/*", - "Content-Length": "17", - "Content-Type": "application/x-www-form-urlencoded", - "Host": "httpbin.org", - "X-Amzn-Trace-Id": "Root=1-6944834b-74f499e251c322713e7dd9a8" - }, - "json": null, - "origin": "5.252.226.107", - "url": "https://httpbin.org/post" -} - -Content-Type: application/x-www-form-urlencoded -Host: httpbin.org -Origin: 5.252.226.107 -*/ - diff --git a/examples/echo_example.fun b/examples/echo_example.fun index 6672c49..017e89c 100755 --- a/examples/echo_example.fun +++ b/examples/echo_example.fun @@ -19,7 +19,5 @@ echo("Hello, ") echo("world") print("!") -/* Expected output: -Hello, world! -*/ - +// Expected output: +// Hello, world! diff --git a/examples/expressions_test.fun b/examples/expressions_test.fun index 54eb6b7..43f5348 100755 --- a/examples/expressions_test.fun +++ b/examples/expressions_test.fun @@ -59,20 +59,3 @@ print(a < b && n >= 10) // expect 1 print((a + b) == 5 && flag) // expect 1 print("=== Expressions test end ===") - -/* Expected output: -=== Expressions test start === -16 -10 --10 -1 -true -false -true -1 -true -true -true -=== Expressions test end === -*/ - diff --git a/examples/for_range_test.fun b/examples/for_range_test.fun index 4a8c3e2..23ce66e 100755 --- a/examples/for_range_test.fun +++ b/examples/for_range_test.fun @@ -78,7 +78,7 @@ for x in range(1, 3) print("=== for/range test end ===") -/* Expected output: +/* === for/range test start === 0 1 @@ -112,4 +112,3 @@ print("=== for/range test end ===") 4 === for/range test end === */ - diff --git a/examples/functions_test.fun b/examples/functions_test.fun index 9b18f3e..0851f0f 100755 --- a/examples/functions_test.fun +++ b/examples/functions_test.fun @@ -57,17 +57,3 @@ number n = 5 print(add(n, 10)) // expect 15 print("=== Functions test end ===") - -/* Expected output: -=== Functions test start === -5 -7 -7 -4 -6 -6 -42 -15 -=== Functions test end === -*/ - diff --git a/examples/have_fun.fun b/examples/have_fun.fun index cf25a5e..8a0acc4 100755 --- a/examples/have_fun.fun +++ b/examples/have_fun.fun @@ -14,8 +14,3 @@ */ print("Have fun!") - -/* Expected output: -Have fun! -*/ - diff --git a/examples/have_fun_function.fun b/examples/have_fun_function.fun index 1355868..a9d9afb 100755 --- a/examples/have_fun_function.fun +++ b/examples/have_fun_function.fun @@ -12,7 +12,7 @@ // Have fun in Fun string s = "Have fun!" - + fun have_fun() print("Have fun!") print(s) @@ -20,13 +20,3 @@ fun have_fun() print(n) have_fun() - -print("Have fun!") - -/* Expected output: -Have fun! -Have fun! -10 -Have fun! -*/ - diff --git a/examples/hex_example.fun b/examples/hex_example.fun deleted file mode 100755 index 84865e2..0000000 --- a/examples/hex_example.fun +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2025 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2025-12-18 - */ - -/* - * Example: Using hex utility functions with fixed values - * This demonstrates hex_to_bytes, bytes_to_hex, and hex_to_dec. - */ - -#include - -print("--- Hex utility demo (fixed values) ---") - -// Using a fixed hex string for verification -hexstr = "50a76f143d67ce173962cd2eea5a86a4" -print("Fixed Hex string:") -print(hexstr) - -print("Hex length (should be 32):") -print(to_string(len(hexstr))) - -print("Hex bytes array:") -bytes = hex_to_bytes(hexstr) -print(to_string(bytes)) - -print("Hex dump to bytes:") -print(bytes) - -print("Back to hex from bytes:") -print(bytes_to_hex(bytes)) - -print("Hex as decimal (first 4 bytes - 0x50a76f14):") -print(to_string(hex_to_dec(substr(hexstr, 0, 8)))) - -print("Hex as decimal (all bytes):") -print(to_string(hex_to_dec(hexstr))) - -print("Decimal to hex (4135093009263527588):") -print(dec_to_hex(4135093009263527588)) - -print("Decimal to hex (1353150228):") -print(dec_to_hex(1353150228)) - -/* Expected output: ---- Hex utility demo (fixed values) --- -Fixed Hex string: -50a76f143d67ce173962cd2eea5a86a4 -Hex length (should be 32): -32 -Hex bytes array: -[array n=16] -Hex dump to bytes:[80, 167, 111, 20, 61, 103, 206, 23, 57, 98, 205, 46, 234, 90, 134, 164] -Back to hex from bytes: -50a76f143d67ce173962cd2eea5a86a4 -Hex as decimal (first 4 bytes - 0x50a76f14): -1353150228 -Hex as decimal (all bytes): -4135093009263527588 -Decimal to hex (4135093009263527588): -3962cd2eea5a86a4 -Decimal to hex (1353150228): -50a76f14 -*/ diff --git a/examples/if_else_test.fun b/examples/if_else_test.fun index 376030b..dddb3f0 100755 --- a/examples/if_else_test.fun +++ b/examples/if_else_test.fun @@ -41,12 +41,3 @@ if (n >= 10) print("answer") // expect: answer print("=== if/else-if/else done ===") - -/* Expected output: -=== if/else-if/else test === -big -42 -answer -=== if/else-if/else done === -*/ - diff --git a/examples/include_lib.fun b/examples/include_lib.fun index 4710a88..67b886d 100755 --- a/examples/include_lib.fun +++ b/examples/include_lib.fun @@ -11,7 +11,7 @@ // Demonstrates system library includes after installation to /usr/lib/fun -// includes can also be done with a leading # like in C, but some programmers maybe like more clean code without a +// includes can also be done with a leading # like in C, but some programmers maybe like more clean code without a // leading #. include include @@ -24,10 +24,3 @@ number y = 32 print("add(" + to_string(x) + ", " + to_string(y) + ") = " + to_string(add(x, y))) print("times(" + to_string(x) + ", " + to_string(y) + ") = " + to_string(times(x, y))) - -/* Expected output: -== include lib demo == -Hello from system lib! -add(10, 32) = 42 -times(10, 32) = 320 -*/ diff --git a/examples/include_local.fun b/examples/include_local.fun index be33c83..7941efe 100755 --- a/examples/include_local.fun +++ b/examples/include_local.fun @@ -27,9 +27,3 @@ print("sum(" + to_string(a) + ", " + to_string(b) + ") = " + to_string(sum(a, b) // To include from the system library directory (/usr/lib/fun), use angle brackets: // #include -/* Expected output: -== include local demo == -Hello, Fun -sum(2, 3) = 5 -*/ - diff --git a/examples/include_local_util.fun b/examples/include_local_util.fun index 7dd9b24..a39710c 100755 --- a/examples/include_local_util.fun +++ b/examples/include_local_util.fun @@ -14,8 +14,3 @@ fun greet(name) fun sum(a, b) return a + b - -/* Expected output: -None, it's a lib... -*/ - diff --git a/examples/ini_class_demo.fun b/examples/ini_class_demo.fun index 4b4392b..90e6598 100755 --- a/examples/ini_class_demo.fun +++ b/examples/ini_class_demo.fun @@ -44,15 +44,3 @@ ok = ini.save(nil) print("saved=" + to_string(ok)) ini.close() - -/* Expected output: -[app] - name=FunApp - version=1.2.3 - debug=1 -[database] - host=localhost - port=5432 -saved=1 -*/ - diff --git a/examples/ini_complex.fun b/examples/ini_complex.fun index 5c7d2b1..eb60f75 100755 --- a/examples/ini_complex.fun +++ b/examples/ini_complex.fun @@ -73,28 +73,3 @@ else // Clean up ini_free(h) - -/* Expected output: -[app] - name=FunApp - version=1.2.3 - debug=1 -[database] - host=localhost - port=5432 - user=fun - pass=secret - pool_size=8 - timeout=2.5 -[network] - ssl=1 - retries=3 - base_url=https://api.example.com -[features] - feature_x=0 - feature_y=0 -[paths] - data_dir=./data - log_file=./logs/app.log -*/ - diff --git a/examples/ini_demo.fun b/examples/ini_demo.fun index 40f9cc4..81c96f1 100755 --- a/examples/ini_demo.fun +++ b/examples/ini_demo.fun @@ -28,9 +28,3 @@ else if ok ini_save(h, path) ini_free(h) - -/* Expected output: -user=����U -retries=3 -ssl=1 -*/ diff --git a/examples/ini_subsections.fun b/examples/ini_subsections.fun index 1feb4ca..95c8529 100755 --- a/examples/ini_subsections.fun +++ b/examples/ini_subsections.fun @@ -68,26 +68,3 @@ else print(" max_files=" + to_string(logs_max_files)) ini_free(h) - -/* Expected output: -[server] - host=example.org - port=8080 -[server.tls] - enabled=1 - version=1.3 - ciphers=TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256 -[users.admin] - name=alice - active=1 - quota_gb=100 -[users.guest] - name=bob - active=0 - quota_gb=5 -[paths.logs] - dir=./var/log/fun - rotate=1 - max_files=7 -*/ - diff --git a/examples/json_showcase.fun b/examples/json_showcase.fun index 6f34fdf..72c46e9 100755 --- a/examples/json_showcase.fun +++ b/examples/json_showcase.fun @@ -53,7 +53,7 @@ print(len(cfg["users"])) // number of users // Derive a small summary map summary = {} -summary["user_count"] = len(cfg["users"]) +summary["user_count"] = len(cfg["users"]) summary["first_user_name"] = cfg["users"][1]["name"] summary["features_enabled"] = cfg["features"]["enabled"] @@ -63,45 +63,3 @@ print(json.stringify(summary, 1)) out_path = "/tmp/fun_complex_out.json" ok = json.to_file(out_path, cfg, 1) print(ok) // 1 on success - -/* Expected output: --- JSON: parse from string and pretty print -- -Dump: -{"name": Ada, "active": true, "score": 99.5, "count": 42, "tags": [C, Ada, Math], "extra": nil} -Ada -true -42 -3 -{ - "name":"Ada", - "active":true, - "score":99.5, - "count":42, - "tags":[ - "C", - "Ada", - "Math" - ], - "extra":null -} --- JSON: load from file, inspect, and save pretty to /tmp -- -Dump: -nil -Dump: -{"project": {"name": Fun, "version": 0.27.2, "website": https://fun-lang.xyz, "license": {"name": Apache-2.0, "url": https://opensource.org/license/apache-2-0}}, "features": {"enabled": [arrays, maps, json, pcsc], "experimental": {"repl": true, "sockets": true, "odbc": false, "notes": nil}}, "users": [{"id": 1, "name": Ada, "roles": [admin, math], "active": true, "score": 99.5, "prefs": {"theme": dark, "editor": {"tabWidth": 2, "font": Fira Code}}}, {"id": 2, "name": Linus, "roles": [user, kernel], "active": false, "score": 88, "prefs": {"theme": light, "editor": {"tabWidth": 8, "font": Monospace}}}], "metrics": {"counters": [0, 1, 1, 2, 3, 5, 8], "latency_ms": {"p50": 1.23, "p90": 3.21, "p99": 12.34}, "builds": 1234567890123456789, "last_release_ts": 1732406400000}, "matrix": [[1, 2, 3], [4, 5, 6], [7, 8, 9]], "notes": UTF-8 ✓ – emojis: 🚀🔥, "null_field": nil} -Fun -0.27.2 -2 -{ - "user_count":2, - "first_user_name":"Linus", - "features_enabled":[ - "arrays", - "maps", - "json", - "pcsc" - ] -} -1 -*/ - diff --git a/examples/namespaced_mod.fun b/examples/namespaced_mod.fun index 8d39007..764466e 100755 --- a/examples/namespaced_mod.fun +++ b/examples/namespaced_mod.fun @@ -1,6 +1,6 @@ #!/usr/bin/env fun -// The shebang line makes no sense here because this is a library which will -// never be executed, but it shows that it is not wrong. +// The shebang line makes no sense here because this is a library which will +// never be executed, but it shows that it is not wrong. /* * This file is part of the Fun programming language. @@ -23,8 +23,3 @@ class Greeter(string prefix) // Methods must declare 'this' as the first parameter fun say(this, name) print(this.prefix + " " + to_string(name)) - -/* Expected output: -None, it's a lib. -*/ - diff --git a/examples/os_env.fun b/examples/os_env.fun index 31abf69..6969212 100755 --- a/examples/os_env.fun +++ b/examples/os_env.fun @@ -25,11 +25,3 @@ fun greet() else print("Hello, " + user + "!") greet() - -/* Possible output: -HOME=/home/hanez -SHELL=/bin/zsh -FUN_NOT_SET= -Hello, hanez! -*/ - diff --git a/examples/pcre2_opcodes.fun b/examples/pcre2_opcodes.fun index 1e6a94e..43eba00 100755 --- a/examples/pcre2_opcodes.fun +++ b/examples/pcre2_opcodes.fun @@ -28,22 +28,22 @@ print(pcre2_test(pattern, text, flags)) m = pcre2_match(pattern, text, flags) if (m != nil) print("first full:") - print(m["full"]) + print(m["full"]) print("span:") - print(m["start"]) + print(m["start"]) print("..") - print(m["end"]) + print(m["end"]) print("groups count:") print(len(m["groups"])) all = pcre2_findall("\\w+", text, flags) for x in all print("all:") - print(x["full"]) + print(x["full"]) print("@") - print(x["start"]) + print(x["start"]) print("..") - print(x["end"]) + print(x["end"]) print("") print("-- More regex demos --") @@ -59,28 +59,28 @@ email_flags = OR(flags, 1) // UTF | I print("Emails (findall):") emails = pcre2_findall(email_pat, email_text, email_flags) for e in emails - print(e["full"]) + print(e["full"]) // Demo 2: URLs (very simple, for demo purposes) url_text = "See http://example.com and https://fun-lang.xyz/docs?x=1#top" url_pat = "https?://[A-Za-z0-9._~:/?#[@]!$&'()*+,;=%-]+" print("URLs:") for u in pcre2_findall(url_pat, url_text, flags) - print(u["full"]) + print(u["full"]) // Demo 3: IPv4 addresses ip_text = "ping 8.8.8.8 and 192.168.0.1; not 999.999.999.999" ip_pat = "(?:(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)" print("IPv4:") for ip in pcre2_findall(ip_pat, ip_text, flags) - print(ip["full"]) + print(ip["full"]) // Demo 4: Dates (YYYY-MM-DD) date_text = "Born 1999-12-31, updated 2025-11-25, bad 2025-13-40" date_pat = "(\\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])" print("Dates (with groups Y/M/D):") for d in pcre2_findall(date_pat, date_text, flags) - print(d["full"]) + print(d["full"]) print(join(d["groups"], "/")) // Demo 5: Hex colors (#RRGGBB) @@ -88,14 +88,14 @@ color_text = "Palette: #FF00FF, #1a2b3c, not #abcd or #12345g" color_pat = "#[0-9A-Fa-f]{6}" print("Hex colors:") for c in pcre2_findall(color_pat, color_text, flags) - print(c["full"]) + print(c["full"]) // Demo 6: Quoted strings with escapes q_text = 'say "hi there" and "indented" plus "quote\\"inside"' q_pat = '"([^"\\\\]|\\\\.)*"' print("Quoted strings (with escapes):") for q in pcre2_findall(q_pat, q_text, flags) - print(q["full"]) + print(q["full"]) // Demo 7: Multiline anchors with /m (M flag) ml_text = "first line\nSecond line\nthird" @@ -103,7 +103,7 @@ ml_pat = "^(\\w+)" ml_flags = OR(flags, 2) // UTF | M print("Multiline ^ anchors (first token of each line):") for ml in pcre2_findall(ml_pat, ml_text, ml_flags) - print(ml["full"]) + print(ml["full"]) // Demo 8: Dotall vs non-dotall ds_text = "BEGIN\nline1\nline2\nEND" @@ -119,14 +119,14 @@ wb_text = "The theater and the THE can differ." wb_pat = "\\bthe\\b" print("Word boundary, case-insensitive:") for w in pcre2_findall(wb_pat, wb_text, OR(flags, 1)) - print(w["full"]) + print(w["full"]) // Demo 10: Lookahead — word followed by number la_text = "foo 123, bar, baz 9" la_pat = "\\w+(?=\\s+\\d+)" print("Lookahead (word before number):") for a in pcre2_findall(la_pat, la_text, flags) - print(a["full"]) + print(a["full"]) // Demo 11: Non-greedy vs greedy ng_text = "onetwo" @@ -134,81 +134,7 @@ greedy = ".*" lazy = ".*?" print("Greedy:") for g in pcre2_findall(greedy, ng_text, OR(flags, 4)) // DOTALL ensures '.' covers any - print(g["full"]) + print(g["full"]) print("Non-greedy:") for l in pcre2_findall(lazy, ng_text, OR(flags, 4)) - print(l["full"]) - -/* Expected output: --- PCRE2 builtins (no class) -- -test: -1 -first full: -Hello -span: -0 -.. -5 -groups count: -1 -all: -Hello -@ -0 -.. -5 -all: -123 -@ -6 -.. -9 -all: -world -@ -10 -.. -15 - --- More regex demos -- -Emails (findall): -one@example.com -Two@Example.COM -URLs: -IPv4: -8.8.8.8 -192.168.0.1 -Dates (with groups Y/M/D): -1999-12-31 -1999/12/31 -2025-11-25 -2025/11/25 -Hex colors: -#FF00FF -#1a2b3c -Quoted strings (with escapes): -"hi there" -"indented" -"quote\"inside" -Multiline ^ anchors (first token of each line): -first -Second -third -Dotall OFF (should fail): -0 -Dotall ON (should match): -1 -Word boundary, case-insensitive: -The -the -THE -Lookahead (word before number): -foo -baz -Greedy: -onetwo -Non-greedy: -one -two -*/ - + print(l["full"]) diff --git a/examples/random_number_example.fun b/examples/random_number_example.fun index cdd1785..9ad5213 100755 --- a/examples/random_number_example.fun +++ b/examples/random_number_example.fun @@ -23,20 +23,12 @@ print("--- random_number(len) demo ---") len_bytes = 16 hexstr = random_number(len_bytes) -print("Requested bytes:") -print(to_string(len_bytes)) -print("Hex string:") -print(hexstr) -print("Hex bytes array:") -print(to_string(hex_to_bytes(hexstr))) -echo("Hex dump to bytes:") +print("Requested bytes: " + to_string(len_bytes)) +print("Hex string: " + hexstr) +print("Hex bytes array: " + to_string(hex_to_bytes(hexstr))) +echo("Hex dump to bytes: ") print(hex_to_bytes(hexstr)) -print("Hex as decimal (first 4 bytes):") -print(to_string(hex_to_dec(substr(hexstr, 0, 8)))) -print("Hex as decimal (all bytes):") -print(to_string(hex_to_dec(hexstr))) -print("Hex length (should be 2*bytes = 32):") -print(to_string(len(hexstr))) +print("Hex length (should be 2*bytes = 32): " + to_string(len(hexstr))) // Zero length returns empty string empty = random_number(0) @@ -48,19 +40,11 @@ print("32 bytes -> " + to_string(len(hex64)) + " hex chars") /* Possible output: --- random_number(len) demo --- -Requested bytes: -16 -Hex string: -50a76f143d67ce173962cd2eea5a86a4 -Hex bytes array: -[array n=16] -Hex dump to bytes:[80, 167, 111, 20, 61, 103, 206, 23, 57, 98, 205, 46, 234, 90, 134, 164] -Hex as decimal (first 4 bytes): -1353150228 -Hex as decimal (all bytes): -4135093009263527588 -Hex length (should be 2*bytes = 32): -32 +Requested bytes: 16 +Hex string: 8497373c7fb52c6cb7f1e1fda5bb6a60 +Hex bytes array: [array n=16] +Hex dump to bytes: [132, 151, 55, 60, 127, 181, 44, 108, 183, 241, 225, 253, 165, 187, 106, 96] +Hex length (should be 2*bytes = 32): 32 Empty (0 bytes) -> length: 0 value: 32 bytes -> 64 hex chars */ diff --git a/examples/sha1_demo.fun b/examples/sha1_demo.fun index 1298cd0..cd74404 100755 --- a/examples/sha1_demo.fun +++ b/examples/sha1_demo.fun @@ -46,5 +46,3 @@ SHA-1('abc') = e6cd2bcee460c45d41565ac877d866a159a16e19 SHA-1(616263 hex) = e6cd2bcee460c45d41565ac877d866a159a16e19 SHA-1('') = da39a3ee5e6b4b0d3255bfef95601890afd80709 === done === -*/ - diff --git a/examples/sha384_example.fun b/examples/sha384_example.fun index 5a5f71d..8fcf388 100755 --- a/examples/sha384_example.fun +++ b/examples/sha384_example.fun @@ -27,9 +27,3 @@ print(s.sha384_hex("616263")) // Raw string input print(s.sha384_str("abc")) - -/* Expected output: -cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7 -cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7 -*/ - diff --git a/examples/short_circuit_test.fun b/examples/short_circuit_test.fun index 5da8c00..1dca7b8 100755 --- a/examples/short_circuit_test.fun +++ b/examples/short_circuit_test.fun @@ -8,7 +8,7 @@ * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 */ - + // Short-circuit demo for || and && print("=== short-circuit demo ===") @@ -43,15 +43,3 @@ else print(0) print("=== end ===") - -/* Expected output: -=== short-circuit demo === -1 -0 -OR-NEEDED -1 -AND-NEEDED -1 -=== end === -*/ - diff --git a/examples/strings_test.fun b/examples/strings_test.fun index cd89339..b000ab0 100755 --- a/examples/strings_test.fun +++ b/examples/strings_test.fun @@ -8,7 +8,7 @@ * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 */ - + // Strings test: concatenation with variables and literals, functions returning strings print("=== strings test start ===") @@ -42,16 +42,3 @@ if (a != "") print("a is non-empty") print("=== strings test end ===") - -/* Expected output: -=== strings test start === -Hello, World! -Hi there -x -x -Hello, Fun -Hi, Hello, You -a is non-empty -=== strings test end === -*/ - diff --git a/examples/tcp_http_get.fun b/examples/tcp_http_get.fun index 79169a2..9c5f463 100755 --- a/examples/tcp_http_get.fun +++ b/examples/tcp_http_get.fun @@ -32,21 +32,3 @@ else data = sock_recv(fd, 8192) print(data) sock_close(fd) - -/* Possible output: -HTTP/1.1 200 OK -Date: Thu, 18 Dec 2025 23:05:52 GMT -Content-Type: text/html -Connection: close -Vary: accept-encoding -Server: cloudflare -Last-Modified: Wed, 17 Dec 2025 03:51:06 GMT -Accept-Ranges: bytes -Cache-Control: max-age=14400 -cf-cache-status: REVALIDATED -CF-RAY: 9b024eb71d91e51d-TXL - -Example Domain

Example Domain

This domain is for use in documentation examples without needing permission. Avoid use in operations.

Learn more

- -*/ - diff --git a/examples/tcp_http_get_class.fun b/examples/tcp_http_get_class.fun index 6ebaae4..10fb802 100755 --- a/examples/tcp_http_get_class.fun +++ b/examples/tcp_http_get_class.fun @@ -30,22 +30,3 @@ else resp = c.recv_all(8192) print(resp) c.close() - -/* Possible output: -HTTP/1.1 200 OK -Date: Thu, 18 Dec 2025 23:06:52 GMT -Content-Type: text/html -Connection: close -Vary: accept-encoding -Server: cloudflare -Last-Modified: Wed, 17 Dec 2025 03:51:06 GMT -Accept-Ranges: bytes -Age: 59 -Cache-Control: max-age=14400 -cf-cache-status: HIT -CF-RAY: 9b02502e3f993237-TXL - -Example Domain

Example Domain

This domain is for use in documentation examples without needing permission. Avoid use in operations.

Learn more

- -*/ - diff --git a/examples/test_dec_to_hex.fun b/examples/test_dec_to_hex.fun deleted file mode 100644 index 05a2f69..0000000 --- a/examples/test_dec_to_hex.fun +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2025 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2025-12-18 - */ - -#include - -hexstr = "ff" -print("Hex to dec: " + to_string(hex_to_dec(hexstr))) -print("Dec to hex: " + dec_to_hex(hex_to_dec(hexstr))) - -/* Expected output: -Hex to dec: 255 -Dec to hex: ff -*/ diff --git a/examples/test_hex_to_dec.fun b/examples/test_hex_to_dec.fun deleted file mode 100755 index c655b99..0000000 --- a/examples/test_hex_to_dec.fun +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2025 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2025-12-18 - */ - -#include - -print("Testing hex_to_dec:") -print("0x0 -> " + to_string(hex_to_dec("0"))) -print("0xf -> " + to_string(hex_to_dec("f"))) -print("0x10 -> " + to_string(hex_to_dec("10"))) -print("0xff -> " + to_string(hex_to_dec("ff"))) -print("0x100 -> " + to_string(hex_to_dec("100"))) -print("0xdeadbeef -> " + to_string(hex_to_dec("deadbeef"))) - -/* Expected output: -Testing hex_to_dec: -0x0 -> 0 -0xf -> 15 -0x10 -> 16 -0xff -> 255 -0x100 -> 256 -0xdeadbeef -> 3735928559 -*/ diff --git a/examples/test_include.fun b/examples/test_include.fun deleted file mode 100644 index bf218b7..0000000 --- a/examples/test_include.fun +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2025 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2025-12-18 - */ - -#include -print(dec_to_hex(255)) - -/* Expected output: -ff -*/ diff --git a/examples/tk_hello.fun b/examples/tk_hello.fun index e4a3fe2..6110b77 100755 --- a/examples/tk_hello.fun +++ b/examples/tk_hello.fun @@ -27,8 +27,3 @@ tk.pack("ok") // Enter GUI loop (no-op if built without FUN_WITH_TCLTK) tk.loop() - -/* Expected output: -A GUI... ;) -*/ - diff --git a/examples/try_catch_finally.fun b/examples/try_catch_finally.fun index a2c15fd..392300e 100755 --- a/examples/try_catch_finally.fun +++ b/examples/try_catch_finally.fun @@ -24,10 +24,3 @@ catch err finally print("finally always runs (syntactically)") print("after try") - -/* Expected output: -before try -inside try body -after try -*/ - diff --git a/examples/type_safety.fun b/examples/type_safety.fun index a672e86..3d8e263 100755 --- a/examples/type_safety.fun +++ b/examples/type_safety.fun @@ -70,32 +70,3 @@ print(typeof(nn)) // -> "Nil" print("") print("Done. Uncomment lines above to see type errors in action.") - -/* Expected output: -== Dynamic variable (untyped) == -Number -100 -String -hello - -== Typed variables remain type-stable == -String -hello -Number -0 -0 -Sint64 -456 - -== Integer width clamping == --126 -56 -255 -44 - -== Nil-typed variables must stay Nil == -Nil - -Done. Uncomment lines above to see type errors in action. -*/ - diff --git a/examples/type_safety_fails.fun b/examples/type_safety_fails.fun index b1ac0c9..ca1c45b 100755 --- a/examples/type_safety_fails.fun +++ b/examples/type_safety_fails.fun @@ -19,12 +19,3 @@ print("Now trying to assign a number to a string variable...") s = 42 // Runtime TypeError: expected String (assignment rejected) print("This line will not execute due to the type error") - -/* Expected output: -Reassigning typed variables to another type should fail -String -hello -Now trying to assign a number to a string variable... -TypeError: expected String -*/ - diff --git a/examples/typeof_features.fun b/examples/typeof_features.fun index 95f2d11..54d765b 100755 --- a/examples/typeof_features.fun +++ b/examples/typeof_features.fun @@ -70,24 +70,3 @@ print("ui8 value = " + to_string(ui8) + ", typeof(ui8) = " + typeof(ui8)) // Uin si8 = si8 - 5 print("si8 value = " + to_string(si8) + ", typeof(si8) = " + typeof(si8)) // Sint8 - -/* Expected output: -typeof(si8) = Sint8 -typeof(si16) = Sint16 -typeof(si32) = Sint32 -typeof(si64) = Sint64 -typeof(ui8) = Uint8 -typeof(ui16) = Uint16 -typeof(ui32) = Uint32 -typeof(ui64) = Uint64 -typeof(n) = Sint64 -typeof(s) = String -typeof(arr) = Array -typeof(m) = Map -typeof(f) = Function -typeof(x) = Nil -typeof(ui8 + 1) = Number -typeof(si16 - 2) = Number -ui8 value = 9, typeof(ui8) = Uint8 -si8 value = -6, typeof(si8) = Sint8 -*/ diff --git a/examples/types_integers.fun b/examples/types_integers.fun index 94898fe..8cff2c7 100755 --- a/examples/types_integers.fun +++ b/examples/types_integers.fun @@ -73,32 +73,3 @@ print("pt['y'] = " + to_string(pt["y"]) + " :: " + typeof(pt["y"])) // Conversions print("to_string(123) => " + to_string(123)) print("to_number(\"456\") => " + to_string(to_number("456"))) - -/* Expected output: -a = 42 :: Sint64 -b = -7 :: Sint64 -zero = 0 :: Sint64 -sum (a + b) = 35 -diff (a - b) = 49 -prod (a * 3) = 126 -quot (a / 2) = 21 -rem (a % 5) = 2 -a > b = 1 -a >= b = 1 -a < b = 0 -a <= b = 0 -a == b = false -a != b = true -t = true :: Boolean -f = false :: Boolean -t && (a > 0) = true -f || (a < 0) = 0 -sum 1..5 = 15 -nums length = 5 -nums[2] = 3 -pt['x'] = 10 :: Number -pt['y'] = -3 :: Number -to_string(123) => 123 -to_number("456") => 456 -*/ - diff --git a/examples/types_overview.fun b/examples/types_overview.fun index 859f64d..4c1c9b8 100755 --- a/examples/types_overview.fun +++ b/examples/types_overview.fun @@ -77,45 +77,3 @@ print(typeof(p)) // -> "Class" print("") print("=== Done ===") - -/* Expected output: -=== Dynamic (untyped) === -Number -String - -=== Numbers and integer subtypes === -Sint64 -42 --126 -4464 -255 -0 - -=== Strings === -String -hello - -=== Nil === -Nil -nil - -=== Arrays === -Array -3 -2 -[1, 2] - -=== Maps === -Map -v - -=== Functions === -Function -12 - -=== Classes and Class-typed variables === -Class - -=== Done === -*/ - diff --git a/examples/while_test.fun b/examples/while_test.fun index cd7aaaf..1ddb2a5 100755 --- a/examples/while_test.fun +++ b/examples/while_test.fun @@ -8,7 +8,7 @@ * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 */ - + // While loops feature test: simple loops, nested with if/else, and functions print("=== while test start ===") @@ -42,21 +42,3 @@ fun countdown(n) print(countdown(3)) // expect: 3,2,1 then 0 print("=== while test end ===") - -/* Expected output: -=== while test start === -0 -1 -2 -3 -4 -3 -99 -1 -3 -2 -1 -0 -=== while test end === -*/ - diff --git a/lib/hex.fun b/lib/hex.fun index 819ce3f..378e9e5 100644 --- a/lib/hex.fun +++ b/lib/hex.fun @@ -77,25 +77,3 @@ fun bytes_to_hex(arr) res = res + two_hex(arr[i]) i = i + 1 return res - -fun hex_to_dec(hex) - s = to_string(hex) - number res = 0 - number i = 0 - number n = len(s) - while i < n - res = res * 16 + hex_val(substr(s, i, 1)) - i = i + 1 - return res - -fun dec_to_hex(n) - number val = n - if (val == 0) - return "0" - hexd = "0123456789abcdef" - res = "" - while val > 0 - number m = val % 16 - res = substr(hexd, m, 1) + res - val = val / 16 - return res diff --git a/src/parser.c b/src/parser.c index fea4e2f..fbc3758 100644 --- a/src/parser.c +++ b/src/parser.c @@ -568,35 +568,19 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) } if (strcmp(name, "typeof") == 0) { (*pos)++; /* '(' */ - /* Special handling for typeof() to return declared subtype for integers */ + /* Disable compile-time shortcut for typeof(); always evaluate at runtime */ size_t peek = *pos; char *vname = NULL; int handled = 0; if (read_identifier_into(src, len, &peek, &vname)) { + /* allow spaces before ')' */ skip_spaces(src, len, &peek); if (peek < len && src[peek] == ')') { - int meta = 0; - int lidx = local_find(vname); - if (lidx >= 0) { - meta = g_locals->types[lidx]; - } else { - int gi = sym_index(vname); - if (gi >= 0) meta = G.types[gi]; - } - - if (meta != 0 && meta != TYPE_META_STRING && meta != TYPE_META_BOOLEAN && meta != TYPE_META_NIL && meta != TYPE_META_CLASS && meta != TYPE_META_FLOAT) { - /* Integer subtype: ±bits */ - int abs_bits = meta < 0 ? -meta : meta; - const char *tname = (meta < 0) - ? (abs_bits==64? "Sint64" : (abs_bits==32? "Sint32" : (abs_bits==16? "Sint16" : "Sint8"))) - : (abs_bits==64? "Uint64" : (abs_bits==32? "Uint32" : (abs_bits==16? "Uint16" : "Uint8"))); - int ci = bytecode_add_constant(bc, make_string(tname)); - bytecode_add_instruction(bc, OP_LOAD_CONST, ci); - *pos = peek + 1; /* consume name and ')' */ - handled = 1; - } + /* Fall back to runtime evaluation path */ free(vname); + /* handled remains 0 so we go to general-case below */ } else { + /* not a simple identifier-only typeof */ free(vname); } } @@ -644,7 +628,6 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) int ciMap2 = bytecode_add_constant(bc, make_string("Map")); bytecode_add_instruction(bc, OP_LOAD_CONST, ciMap2); /* ["Map"] */ } - int j_end2 = bytecode_add_instruction(bc, OP_JUMP, 0); int after_map = bc->instr_count; /* not map: compute typeof(v) */ @@ -653,7 +636,6 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) /* end */ bytecode_set_operand(bc, j_end, bc->instr_count); - bytecode_set_operand(bc, j_end2, bc->instr_count); } free(name); @@ -2776,9 +2758,53 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si } bytecode_set_operand(bc, j_skip_err, bc->instr_count); - if (abs_bits > 0) { - bytecode_add_instruction(bc, (decl_bits < 0) ? OP_SCLAMP : OP_UCLAMP, abs_bits); + /* range check instead of clamp */ + int64_t minV = 0, maxV = 0; + if (decl_bits < 0) { + /* signed */ + if (abs_bits >= 64) { minV = INT64_MIN; maxV = INT64_MAX; } + else { maxV = (1LL << (abs_bits - 1)) - 1; minV = - (1LL << (abs_bits - 1)); } + } else { + /* unsigned */ + if (abs_bits >= 63) { minV = 0; maxV = INT64_MAX; } + else { minV = 0; maxV = (1LL << abs_bits) - 1; } } + int ciMin = bytecode_add_constant(bc, make_int(minV)); + int ciMax = bytecode_add_constant(bc, make_int(maxV)); + + /* if (v < min) -> error */ + bytecode_add_instruction(bc, OP_DUP, 0); + bytecode_add_instruction(bc, OP_LOAD_CONST, ciMin); + bytecode_add_instruction(bc, OP_LT, 0); + int j_after_min = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0); + { + const char *tname = (decl_bits < 0) + ? (abs_bits==64? "int64" : (abs_bits==32? "int32" : (abs_bits==16? "int16" : "int8"))) + : (abs_bits==64? "uint64" : (abs_bits==32? "uint32" : (abs_bits==16? "uint16" : "uint8"))); + char buf[128]; + snprintf(buf, sizeof(buf), "OverflowError: value out of range for %s", tname); + int ciMsg = bytecode_add_constant(bc, make_string(buf)); + bytecode_add_instruction(bc, OP_LOAD_CONST, ciMsg); + bytecode_add_instruction(bc, OP_THROW, 0); + } + bytecode_set_operand(bc, j_after_min, bc->instr_count); + + /* if (v > max) -> error */ + bytecode_add_instruction(bc, OP_DUP, 0); + bytecode_add_instruction(bc, OP_LOAD_CONST, ciMax); + bytecode_add_instruction(bc, OP_GT, 0); + int j_after_max = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0); + { + const char *tname = (decl_bits < 0) + ? (abs_bits==64? "int64" : (abs_bits==32? "int32" : (abs_bits==16? "int16" : "int8"))) + : (abs_bits==64? "uint64" : (abs_bits==32? "uint32" : (abs_bits==16? "uint16" : "uint8"))); + char buf[128]; + snprintf(buf, sizeof(buf), "OverflowError: value out of range for %s", tname); + int ciMsg = bytecode_add_constant(bc, make_string(buf)); + bytecode_add_instruction(bc, OP_LOAD_CONST, ciMsg); + bytecode_add_instruction(bc, OP_THROW, 0); + } + bytecode_set_operand(bc, j_after_max, bc->instr_count); } } @@ -3121,10 +3147,50 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si } bytecode_set_operand(bc, j_skip_err, bc->instr_count); - if (abs_bits > 0) { - bytecode_add_instruction(bc, (meta < 0) ? OP_SCLAMP : OP_UCLAMP, abs_bits); - } + /* range check instead of clamp */ + int64_t minV = 0, maxV = 0; + if (meta < 0) { + if (abs_bits >= 64) { minV = INT64_MIN; maxV = INT64_MAX; } + else { maxV = (1LL << (abs_bits - 1)) - 1; minV = - (1LL << (abs_bits - 1)); } + } else { + if (abs_bits >= 63) { minV = 0; maxV = INT64_MAX; } + else { minV = 0; maxV = (1LL << abs_bits) - 1; } } + int ciMin = bytecode_add_constant(bc, make_int(minV)); + int ciMax = bytecode_add_constant(bc, make_int(maxV)); + + bytecode_add_instruction(bc, OP_DUP, 0); + bytecode_add_instruction(bc, OP_LOAD_CONST, ciMin); + bytecode_add_instruction(bc, OP_LT, 0); + int j_after_min = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0); + { + const char *tname = (meta < 0) + ? (abs_bits==64? "int64" : (abs_bits==32? "int32" : (abs_bits==16? "int16" : "int8"))) + : (abs_bits==64? "uint64" : (abs_bits==32? "uint32" : (abs_bits==16? "uint16" : "uint8"))); + char buf[128]; + snprintf(buf, sizeof(buf), "OverflowError: value out of range for %s", tname); + int ciMsg2 = bytecode_add_constant(bc, make_string(buf)); + bytecode_add_instruction(bc, OP_LOAD_CONST, ciMsg2); + bytecode_add_instruction(bc, OP_THROW, 0); + } + bytecode_set_operand(bc, j_after_min, bc->instr_count); + + bytecode_add_instruction(bc, OP_DUP, 0); + bytecode_add_instruction(bc, OP_LOAD_CONST, ciMax); + bytecode_add_instruction(bc, OP_GT, 0); + int j_after_max = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0); + { + const char *tname = (meta < 0) + ? (abs_bits==64? "int64" : (abs_bits==32? "int32" : (abs_bits==16? "int16" : "int8"))) + : (abs_bits==64? "uint64" : (abs_bits==32? "uint32" : (abs_bits==16? "uint16" : "uint8"))); + char buf[128]; + snprintf(buf, sizeof(buf), "OverflowError: value out of range for %s", tname); + int ciMsg3 = bytecode_add_constant(bc, make_string(buf)); + bytecode_add_instruction(bc, OP_LOAD_CONST, ciMsg3); + bytecode_add_instruction(bc, OP_THROW, 0); + } + bytecode_set_operand(bc, j_after_max, bc->instr_count); + } /* dynamic (meta==0): no enforcement */ if (lidx >= 0) { diff --git a/src/parser_utils.c b/src/parser_utils.c index 477f965..8fbeb34 100644 --- a/src/parser_utils.c +++ b/src/parser_utils.c @@ -159,7 +159,7 @@ static int read_identifier_into(const char *src, size_t len, size_t *pos, char * return 0; } -static uint64_t parse_int_literal_value(const char *src, size_t len, size_t *pos, int *ok) { +static int64_t parse_int_literal_value(const char *src, size_t len, size_t *pos, int *ok) { size_t p = *pos; skip_spaces(src, len, &p); int sign = 1; @@ -173,31 +173,31 @@ static uint64_t parse_int_literal_value(const char *src, size_t len, size_t *pos if ((p + 1) < len && src[p] == '0' && (src[p + 1] == 'x' || src[p + 1] == 'X')) { p += 2; if (p >= len || !isxdigit((unsigned char)src[p])) { *ok = 0; return 0; } - uint64_t val = 0; + int64_t val = 0; while (p < len && isxdigit((unsigned char)src[p])) { char c = src[p]; int d = (c >= '0' && c <= '9') ? (c - '0') : (c >= 'a' && c <= 'f') ? (c - 'a' + 10) : (c >= 'A' && c <= 'F') ? (c - 'A' + 10) : 0; - val = (val << 4) + (uint64_t)d; + val = (val << 4) + d; p++; } *pos = p; *ok = 1; - return (uint64_t)((int64_t)sign * (int64_t)val); + return sign * val; } /* Decimal fallback */ if (!isdigit((unsigned char)src[p])) { *ok = 0; return 0; } - uint64_t val = 0; + int64_t val = 0; while (p < len && isdigit((unsigned char)src[p])) { - val = val * 10 + (uint64_t)(src[p] - '0'); + val = val * 10 + (src[p] - '0'); p++; } *pos = p; *ok = 1; - return (uint64_t)((int64_t)sign * (int64_t)val); + return sign * val; } /* === Include preprocessor ===