From 92aacff71a0d6a14f3e481d85e6b4d891c350d00 Mon Sep 17 00:00:00 2001 From: hanez Date: Tue, 23 Dec 2025 20:49:07 +0100 Subject: [PATCH] All incomplete examples have been updated and some corrections have been made to the parser to make them work. (0.37.17) --- CMakeLists.txt | 2 +- examples/boolean_decl.fun | 6 ++ examples/booleans.fun | 22 ++++++ examples/cast_demo.fun | 23 ++++++ examples/curl_download.fun | 5 ++ examples/curl_get_json.fun | 6 ++ examples/curl_post.fun | 27 +++++++ examples/echo_example.fun | 6 +- examples/expressions_test.fun | 17 +++++ examples/for_range_test.fun | 3 +- examples/functions_test.fun | 14 ++++ examples/have_fun.fun | 5 ++ examples/have_fun_function.fun | 12 +++- examples/if_else_test.fun | 9 +++ examples/include_lib.fun | 9 ++- examples/include_local.fun | 6 ++ examples/include_local_util.fun | 5 ++ examples/ini_class_demo.fun | 12 ++++ examples/ini_complex.fun | 25 +++++++ examples/ini_demo.fun | 6 ++ examples/ini_subsections.fun | 23 ++++++ examples/json_showcase.fun | 44 +++++++++++- examples/namespaced_mod.fun | 9 ++- examples/os_env.fun | 8 +++ examples/pcre2_opcodes.fun | 108 +++++++++++++++++++++++----- examples/sha1_demo.fun | 2 + examples/sha384_example.fun | 6 ++ examples/short_circuit_test.fun | 14 +++- examples/strings_test.fun | 15 +++- examples/tcp_http_get.fun | 18 +++++ examples/tcp_http_get_class.fun | 19 +++++ examples/tk_hello.fun | 5 ++ examples/try_catch_finally.fun | 7 ++ examples/type_safety.fun | 29 ++++++++ examples/type_safety_fails.fun | 9 +++ examples/typeof_features.fun | 21 ++++++ examples/types_integers.fun | 29 ++++++++ examples/types_overview.fun | 42 +++++++++++ examples/while_test.fun | 20 +++++- src/parser.c | 122 ++++++++------------------------ src/parser_utils.c | 14 ++-- 41 files changed, 654 insertions(+), 130 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 743767a..328dea4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(fun VERSION 0.37.15 LANGUAGES C) +project(fun VERSION 0.37.17 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 4caaa6c..c49ed3d 100755 --- a/examples/boolean_decl.fun +++ b/examples/boolean_decl.fun @@ -15,3 +15,9 @@ 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 840c64a..b7ed476 100755 --- a/examples/booleans.fun +++ b/examples/booleans.fun @@ -52,3 +52,25 @@ 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 f937b0c..a18c28b 100755 --- a/examples/cast_demo.fun +++ b/examples/cast_demo.fun @@ -52,3 +52,26 @@ 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/curl_download.fun b/examples/curl_download.fun index a0f72de..958f791 100755 --- a/examples/curl_download.fun +++ b/examples/curl_download.fun @@ -22,3 +22,8 @@ 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 713cf88..ff24f5c 100755 --- a/examples/curl_get_json.fun +++ b/examples/curl_get_json.fun @@ -23,3 +23,9 @@ 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 988c458..802ea56 100755 --- a/examples/curl_post.fun +++ b/examples/curl_post.fun @@ -30,3 +30,30 @@ 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 017e89c..6672c49 100755 --- a/examples/echo_example.fun +++ b/examples/echo_example.fun @@ -19,5 +19,7 @@ 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 43f5348..54eb6b7 100755 --- a/examples/expressions_test.fun +++ b/examples/expressions_test.fun @@ -59,3 +59,20 @@ 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 23ce66e..4a8c3e2 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,3 +112,4 @@ print("=== for/range test end ===") 4 === for/range test end === */ + diff --git a/examples/functions_test.fun b/examples/functions_test.fun index 0851f0f..9b18f3e 100755 --- a/examples/functions_test.fun +++ b/examples/functions_test.fun @@ -57,3 +57,17 @@ 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 8a0acc4..cf25a5e 100755 --- a/examples/have_fun.fun +++ b/examples/have_fun.fun @@ -14,3 +14,8 @@ */ print("Have fun!") + +/* Expected output: +Have fun! +*/ + diff --git a/examples/have_fun_function.fun b/examples/have_fun_function.fun index a9d9afb..1355868 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,3 +20,13 @@ fun have_fun() print(n) have_fun() + +print("Have fun!") + +/* Expected output: +Have fun! +Have fun! +10 +Have fun! +*/ + diff --git a/examples/if_else_test.fun b/examples/if_else_test.fun index dddb3f0..376030b 100755 --- a/examples/if_else_test.fun +++ b/examples/if_else_test.fun @@ -41,3 +41,12 @@ 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 67b886d..4710a88 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,3 +24,10 @@ 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 7941efe..be33c83 100755 --- a/examples/include_local.fun +++ b/examples/include_local.fun @@ -27,3 +27,9 @@ 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 a39710c..7dd9b24 100755 --- a/examples/include_local_util.fun +++ b/examples/include_local_util.fun @@ -14,3 +14,8 @@ 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 90e6598..4b4392b 100755 --- a/examples/ini_class_demo.fun +++ b/examples/ini_class_demo.fun @@ -44,3 +44,15 @@ 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 eb60f75..5c7d2b1 100755 --- a/examples/ini_complex.fun +++ b/examples/ini_complex.fun @@ -73,3 +73,28 @@ 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 81c96f1..40f9cc4 100755 --- a/examples/ini_demo.fun +++ b/examples/ini_demo.fun @@ -28,3 +28,9 @@ 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 95c8529..1feb4ca 100755 --- a/examples/ini_subsections.fun +++ b/examples/ini_subsections.fun @@ -68,3 +68,26 @@ 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 72c46e9..6f34fdf 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,3 +63,45 @@ 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 764466e..8d39007 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,3 +23,8 @@ 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 6969212..31abf69 100755 --- a/examples/os_env.fun +++ b/examples/os_env.fun @@ -25,3 +25,11 @@ 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 43eba00..1e6a94e 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,7 +134,81 @@ 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"]) + 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 +*/ + diff --git a/examples/sha1_demo.fun b/examples/sha1_demo.fun index cd74404..1298cd0 100755 --- a/examples/sha1_demo.fun +++ b/examples/sha1_demo.fun @@ -46,3 +46,5 @@ 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 8fcf388..5a5f71d 100755 --- a/examples/sha384_example.fun +++ b/examples/sha384_example.fun @@ -27,3 +27,9 @@ 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 1dca7b8..5da8c00 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,3 +43,15 @@ 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 b000ab0..cd89339 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,3 +42,16 @@ 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 9c5f463..79169a2 100755 --- a/examples/tcp_http_get.fun +++ b/examples/tcp_http_get.fun @@ -32,3 +32,21 @@ 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 10fb802..6ebaae4 100755 --- a/examples/tcp_http_get_class.fun +++ b/examples/tcp_http_get_class.fun @@ -30,3 +30,22 @@ 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/tk_hello.fun b/examples/tk_hello.fun index 6110b77..e4a3fe2 100755 --- a/examples/tk_hello.fun +++ b/examples/tk_hello.fun @@ -27,3 +27,8 @@ 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 392300e..a2c15fd 100755 --- a/examples/try_catch_finally.fun +++ b/examples/try_catch_finally.fun @@ -24,3 +24,10 @@ 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 3d8e263..a672e86 100755 --- a/examples/type_safety.fun +++ b/examples/type_safety.fun @@ -70,3 +70,32 @@ 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 ca1c45b..b1ac0c9 100755 --- a/examples/type_safety_fails.fun +++ b/examples/type_safety_fails.fun @@ -19,3 +19,12 @@ 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 54d765b..95f2d11 100755 --- a/examples/typeof_features.fun +++ b/examples/typeof_features.fun @@ -70,3 +70,24 @@ 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 8cff2c7..94898fe 100755 --- a/examples/types_integers.fun +++ b/examples/types_integers.fun @@ -73,3 +73,32 @@ 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 4c1c9b8..859f64d 100755 --- a/examples/types_overview.fun +++ b/examples/types_overview.fun @@ -77,3 +77,45 @@ 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 1ddb2a5..cd7aaaf 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,3 +42,21 @@ 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/src/parser.c b/src/parser.c index fbc3758..fea4e2f 100644 --- a/src/parser.c +++ b/src/parser.c @@ -568,19 +568,35 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) } if (strcmp(name, "typeof") == 0) { (*pos)++; /* '(' */ - /* Disable compile-time shortcut for typeof(); always evaluate at runtime */ + /* Special handling for typeof() to return declared subtype for integers */ 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] == ')') { - /* Fall back to runtime evaluation path */ + 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; + } free(vname); - /* handled remains 0 so we go to general-case below */ } else { - /* not a simple identifier-only typeof */ free(vname); } } @@ -628,6 +644,7 @@ 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) */ @@ -636,6 +653,7 @@ 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); @@ -2758,53 +2776,9 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si } bytecode_set_operand(bc, j_skip_err, bc->instr_count); - /* 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; } + if (abs_bits > 0) { + bytecode_add_instruction(bc, (decl_bits < 0) ? OP_SCLAMP : OP_UCLAMP, abs_bits); } - 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); } } @@ -3147,50 +3121,10 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si } bytecode_set_operand(bc, j_skip_err, bc->instr_count); - /* 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; } + if (abs_bits > 0) { + bytecode_add_instruction(bc, (meta < 0) ? OP_SCLAMP : OP_UCLAMP, abs_bits); + } } - 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 8fbeb34..477f965 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 int64_t parse_int_literal_value(const char *src, size_t len, size_t *pos, int *ok) { +static uint64_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 int64_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; } - int64_t val = 0; + uint64_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) + d; + val = (val << 4) + (uint64_t)d; p++; } *pos = p; *ok = 1; - return sign * val; + return (uint64_t)((int64_t)sign * (int64_t)val); } /* Decimal fallback */ if (!isdigit((unsigned char)src[p])) { *ok = 0; return 0; } - int64_t val = 0; + uint64_t val = 0; while (p < len && isdigit((unsigned char)src[p])) { - val = val * 10 + (src[p] - '0'); + val = val * 10 + (uint64_t)(src[p] - '0'); p++; } *pos = p; *ok = 1; - return sign * val; + return (uint64_t)((int64_t)sign * (int64_t)val); } /* === Include preprocessor ===