From 2927f883403c8bfb230e5db7c93de9e3144a8661 Mon Sep 17 00:00:00 2001 From: hanez Date: Thu, 11 Dec 2025 03:14:20 +0100 Subject: [PATCH] Some more housekeeping. Destroys nothing, but makes some things easier. (0.37.3) --- CMakeLists.txt | 2 +- demo.fun | 325 ++++++++---------- examples/{ => error}/debug_reporting.fun | 0 examples/{ => error}/exit_example.fun | 0 examples/{ => error}/fail.fun | 0 examples/{ => error}/repl_on_error.fun | 0 examples/{ => error}/try_catch_with_error.fun | 0 scripts/run_examples.sh | 16 + 8 files changed, 156 insertions(+), 187 deletions(-) rename examples/{ => error}/debug_reporting.fun (100%) rename examples/{ => error}/exit_example.fun (100%) rename examples/{ => error}/fail.fun (100%) rename examples/{ => error}/repl_on_error.fun (100%) rename examples/{ => error}/try_catch_with_error.fun (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index ad5119f..5cd1750 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(fun VERSION 0.37.2 LANGUAGES C) +project(fun VERSION 0.37.3 LANGUAGES C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) diff --git a/demo.fun b/demo.fun index e7a9324..c8153e3 100755 --- a/demo.fun +++ b/demo.fun @@ -1,200 +1,153 @@ #!/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-09-30 + * Interactive demo runner for all examples in ./examples + * - Asks y/n before running each feature demo + * - Executes each example as a subprocess */ -// Fun Interactive Demo -// Run: FUN_LIB_DIR="$(pwd)/lib" ./build/fun demo.fun (Linux/macOS/FreeBSD) -// set FUN_LIB_DIR=%CD%\lib && build-debug\fun.exe demo.fun (Windows CMD) -// $env:FUN_LIB_DIR="$PWD\lib"; .\build\fun.exe demo.fun (Windows PowerShell) +#include -// Use a stdlib helper from the repository (fallback to ./lib via preprocessor) -#include +fun pick_fun_bin() + // Prefer an explicit FUN_BIN override; otherwise rely on PATH + b = env("FUN_BIN") + if b != "" + return b + return "fun" -print("") -print("=== Fun Interactive Demo ===") +fun run_example(bin, path) + // Ensure examples can locate stdlib when run from repo root. + // We execute via the shell so env assignment + redirection works. + cmd = join(["sh -c '\nFUN_LIB_DIR=./lib ", bin, " ", path, " 2>&1\n'"], "") + print("-- output begin --") + code = system(cmd) + print("-- output end --") + print(join(["exit code: ", to_string(code)], "")) + return code -print("") -print("== Basics: dynamic vs typed variables and typeof ==") -x = 123 -print("x=" + to_string(x) + " typeof=" + typeof(x)) -x = "hello" -print("x=" + to_string(x) + " typeof=" + typeof(x)) +fun main() + c = Console() + bin = pick_fun_bin() -number n = 42 -print("n=" + to_string(n) + " typeof=" + typeof(n)) -n = n + 8 -print("n=" + to_string(n) + " typeof=" + typeof(n)) -// Uncomment to see a runtime type error and halt the program: -// n = "oops" + print("=== Fun language feature showcase (interactive) ===") + print(join(["Using interpreter: ", bin], "")) + print("Tip: set FUN_BIN=/path/to/fun to override. Stdlib is passed via FUN_LIB_DIR=./lib\n") -boolean flag = 0 -print("flag=" + to_string(flag) + " typeof=" + typeof(flag)) -flag = 2 // gets clamped to 1 -print("flag(after clamp 2)=" + to_string(flag)) + // List of example scripts. Keep paths relative to repo root where this demo resides. + // If you add/remove examples, update this list. + files = [ + "examples/arrays.fun", + "examples/arrays_advanced.fun", + "examples/arrays_iter.fun", + "examples/boolean_decl.fun", + "examples/booleans.fun", + "examples/builtins_conversions.fun", + "examples/builtins_extended.fun", + "examples/builtins_maps_and_more.fun", + "examples/byte_for_demo.fun", + "examples/byte_overflow_try_catch.fun", + "examples/cast_demo.fun", + "examples/class_constructor.fun", + "examples/classes_demo.fun", + "examples/crc32_example.fun", + "examples/crc32c_example.fun", + "examples/curl_download.fun", + "examples/curl_get_json.fun", + "examples/curl_post.fun", + "examples/datetime_basic.fun", + "examples/datetime_extended.fun", + "examples/datetime_timer.fun", + "examples/debug_reporting.fun", + "examples/echo_example.fun", + "examples/exit_example.fun", + "examples/expressions_test.fun", + "examples/fail.fun", + "examples/file_io.fun", + "examples/file_print_for_file_line_by_line.fun", + "examples/floats.fun", + "examples/for_range_test.fun", + "examples/functions_test.fun", + "examples/have_fun.fun", + "examples/have_fun_function.fun", + "examples/if_else_test.fun", + "examples/include_lib.fun", + "examples/include_local.fun", + "examples/include_local_util.fun", + "examples/include_namespace.fun", + "examples/inheritance_demo.fun", + "examples/ini_class_demo.fun", + "examples/ini_complex.fun", + "examples/ini_demo.fun", + "examples/ini_subsections.fun", + "examples/input_example.fun", + "examples/json_showcase.fun", + "examples/libsql_example.fun", + "examples/loops_break_continue.fun", + "examples/md5_demo.fun", + "examples/namespaced_mod.fun", + "examples/nested_loops.fun", + "examples/objects_basic.fun", + "examples/objects_more.fun", + "examples/os_env.fun", + "examples/pcre2_opcodes.fun", + "examples/pcre2_showcase.fun", + "examples/pcsc_example.fun", + "examples/process_example.fun", + "examples/regex_demo.fun", + "examples/regex_procedural.fun", + "examples/repl_on_error.fun", + "examples/sha1_demo.fun", + "examples/sha256_demo.fun", + "examples/sha256_str_demo.fun", + "examples/sha384_example.fun", + "examples/sha512_demo.fun", + "examples/sha512_str_demo.fun", + "examples/short_circuit_test.fun", + "examples/signed_ints.fun", + "examples/sqlite_example.fun", + "examples/stdlib_showcase.fun", + "examples/strings_test.fun", + "examples/tcp_http_get.fun", + "examples/tcp_http_get_class.fun", + "examples/thread_class_example.fun", + "examples/threads_demo.fun", + "examples/tk_hello.fun", + "examples/try_catch_finally.fun", + "examples/try_catch_with_error.fun", + "examples/typeof.fun", + "examples/typeof_features.fun", + "examples/type_safety.fun", + "examples/type_safety_fails.fun", + "examples/types_integers.fun", + "examples/types_overview.fun", + "examples/uint_types.fun", + "examples/unix_socket_echo.fun", + "examples/while_test.fun", + "examples/xml_access_catalog.fun", + "examples/xml_access_employees.fun", + "examples/xml_access_ns.fun", + "examples/xml_class_example.fun", + "examples/xml_minimal.fun" + ] -int8 si = 130 // clamps to 127 -uint8 u8 = 300 // clamps to 255 -print("int8 si=" + to_string(si) + ", uint8 u8=" + to_string(u8)) + failures = [] -nil nothing = nil -print("nothing typeof=" + typeof(nothing)) + for f in files + q = join(["Run ", f, "?"], "") + if c.ask_yes_no(q) + print(join(["=== Running: ", f, " ==="], "")) + code = run_example(bin, f) + if code != 0 + failures.push(f) + print("") + else + print(join(["Skipped: ", f], "")) -print("") -print("== Arithmetic, comparisons, and logic ==") -print("3+4=" + to_string(3 + 4)) -print("10-3=" + to_string(10 - 3)) -print("6*7=" + to_string(6 * 7)) -print("20/3=" + to_string(20 / 3)) -print("20%3=" + to_string(20 % 3)) -print("2<3=" + to_string(2 < 3) + ", 3<=3=" + to_string(3 <= 3)) -print("5>2=" + to_string(5 > 2) + ", 2>=2=" + to_string(2 >= 2)) -print("2==2=" + to_string(2 == 2) + ", 2!=3=" + to_string(2 != 3)) -print("(1 && 0)=" + to_string(1 && 0) + ", (0 || 1)=" + to_string(0 || 1) + ", !0=" + to_string(!0)) + if len(failures) == 0 + print("All selected examples completed successfully.") + else + print("Some selected examples failed:") + for ff in failures + print(join([" - ", ff], "")) -print("") -print("== Strings and arrays ==") -s = "alpha,beta,gamma" -parts = split(s, ",") -print("split -> len=" + to_string(len(parts))) -print("join(parts,'|')=" + join(parts, "|")) -print("substr('abcdef', 2, 3)=" + substr("abcdef", 2, 3)) -print("find('hello world','world')=" + to_string(find("hello world", "world"))) - -arr = [1, 2, 3] -print("arr len=" + to_string(len(arr))) -push(arr, 4) -print("after push 4 -> len=" + to_string(len(arr))) -v = pop(arr) -print("popped=" + to_string(v) + " len=" + to_string(len(arr))) -insert(arr, 1, 99) // [1,99,2,3] -print("after insert(1,99) arr[1]=" + to_string(arr[1])) -set(arr, 2, 55) // [1,99,55,3] -print("after set(2,55) arr[2]=" + to_string(arr[2])) -print("contains(arr, 55)=" + to_string(contains(arr, 55)) + ", indexOf(arr, 99)=" + to_string(indexOf(arr, 99))) -print("slice arr[1:3] len=" + to_string(len(arr[1:3]))) -print("join(arr, ',')=" + join(arr, ",")) - -print("") -print("== Enumerate and zip ==") -for p in enumerate(["a", "b", "c"]) - print("idx=" + to_string(p[0]) + " val=" + to_string(p[1])) -z = zip([1, 2], ["x", "y"]) -for pair in z - print("(" + to_string(pair[0]) + "," + to_string(pair[1]) + ")") - -print("") -print("== Maps (dictionaries) ==") -m = {"name": "Alice", "age": 30} -print("has(m,'age')=" + to_string(has(m, "age"))) -print("m['name']=" + to_string(m["name"])) -m.age = 31 -print("m.age after = " + to_string(m.age)) -print("keys: " + join(keys(m), ",")) -print("values count=" + to_string(len(values(m)))) - -print("") -print("== Functions and higher-order ops (map/filter/reduce) ==") -fun greet(name) - print("Hello, " + to_string(name) + "!") -greet("Fun") - -fun double(x) - return x * 2 - -nums = [1, 2, 3, 4, 5] -twice = map(nums, double) -print("len(map)=" + to_string(len(twice)) + " first=" + to_string(twice[0])) - -fun isEven(x) - return (x % 2) == 0 -evens = filter(nums, isEven) -print("filter evens len=" + to_string(len(evens))) - -fun sum(acc, x) - return acc + x -total = reduce(nums, 0, sum) -print("reduce sum=" + to_string(total)) - -print("") -print("== If / else-if / else and loops ==") -val = 7 -if (val < 0) - print("neg") -else if (val == 0) - print("zero") -else - print("pos") - -print("for range(0, 5):") -for i in range(0, 5) - print(i) - -print("for in array:") -for x in ["h", "i", "!"] - print(x) - -print("while loop (count to 3):") -c = 0 -while (c < 3) - print(c) - c = c + 1 - -print("") -print("== Classes (with 'this' and methods) ==") -class Person(string name, number age) - // default field values (can be overridden by constructor params) - full = name + " (" + to_string(age) + ")" - - // required: first param is 'this' - fun say(this) - print("I am " + to_string(this.full)) - - // typeof(instance) will return this string for Maps tagged with __class - fun toString(this) - return "Person" - -p = Person("Alice", 30) -p.say() -print("typeof p: " + typeof(p)) - -print("") -print("== Math and bitwise helpers ==") -print("min(3,9)=" + to_string(min(3, 9)) + ", max(3,9)=" + to_string(max(3, 9))) -print("clamp(15,0,10)=" + to_string(clamp(15, 0, 10)) + ", abs(-5)=" + to_string(abs(-5))) -print("pow(2,10)=" + to_string(pow(2, 10))) -print("band(0xF0,0x3C)=" + to_string(band(0xF0, 0x3C))) -print("bor(0x0F,0x30)=" + to_string(bor(0x0F, 0x30))) -print("bxor(0xFF,0x0F)=" + to_string(bxor(0xFF, 0x0F))) -print("bnot(0x0F)=" + to_string(bnot(0x0F))) -print("shl(1,4)=" + to_string(shl(1, 4)) + ", shr(128,3)=" + to_string(shr(128, 3))) -print("rol(0x12,1)=" + to_string(rol(0x12, 1)) + ", ror(0x12,1)=" + to_string(ror(0x12, 1))) - -print("") -print("== Random numbers ==") -random(12345) // seed -print("randomInt(1,10) -> " + to_string(randomInt(1, 10))) - -print("") -print("== File IO and environment ==") -write_ok = write_file("demo_tmp.txt", "Hello from Fun!\n") -print("write_file ok=" + to_string(write_ok)) -content = read_file("demo_tmp.txt") -print("read_file len=" + to_string(len(content))) -print("PATH starts with: " + substr(env("PATH"), 0, 24)) - -print("") -print("== Library include demo ==") -print("add(2,3) from utils/math.fun -> " + to_string(add(2, 3))) -print("times(4,5) from utils/math.fun -> " + to_string(times(4, 5))) - -print("") -print("=== Demo complete. Have Fun! ===") +main() diff --git a/examples/debug_reporting.fun b/examples/error/debug_reporting.fun similarity index 100% rename from examples/debug_reporting.fun rename to examples/error/debug_reporting.fun diff --git a/examples/exit_example.fun b/examples/error/exit_example.fun similarity index 100% rename from examples/exit_example.fun rename to examples/error/exit_example.fun diff --git a/examples/fail.fun b/examples/error/fail.fun similarity index 100% rename from examples/fail.fun rename to examples/error/fail.fun diff --git a/examples/repl_on_error.fun b/examples/error/repl_on_error.fun similarity index 100% rename from examples/repl_on_error.fun rename to examples/error/repl_on_error.fun diff --git a/examples/try_catch_with_error.fun b/examples/error/try_catch_with_error.fun similarity index 100% rename from examples/try_catch_with_error.fun rename to examples/error/try_catch_with_error.fun diff --git a/scripts/run_examples.sh b/scripts/run_examples.sh index 9d814cd..1f7f5cd 100755 --- a/scripts/run_examples.sh +++ b/scripts/run_examples.sh @@ -50,6 +50,14 @@ if [[ ! -x "$BIN" ]]; then exit 2 fi +# Ensure stdlib is discoverable for examples unless user already set it +if [[ -z "${FUN_LIB_DIR:-}" ]]; then + export FUN_LIB_DIR="$ROOT/lib" +fi + +# Ensure error bucket exists +mkdir -p "$EX_DIR/error" + shopt -s nullglob files=("$EX_DIR"/*.fun) shopt -u nullglob @@ -64,6 +72,14 @@ for f in "${files[@]}"; do echo "=== Running: ${f#$ROOT/} ===" if ! "$BIN" "$f"; then echo "FAILED: ${f#$ROOT/}" + base="$(basename "$f")" + dest="$EX_DIR/error/$base" + # Move the failing example to the error folder + if mv -f "$f" "$dest"; then + echo "Moved to: examples/error/$base" + else + echo "warning: failed to move $base to examples/error/" >&2 + fi rc=1 fi done