diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f9fe34..dd7dbf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(fun VERSION 0.37.44 LANGUAGES C) +project(fun VERSION 0.37.32 LANGUAGES C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) @@ -20,7 +20,7 @@ if(NOT DEFINED DEFAULT_LIB_DIR OR DEFAULT_LIB_DIR STREQUAL "") else() set(_FUN_DEFAULT_LIB_DIR "/usr/share/fun/lib") endif() - set(DEFAULT_LIB_DIR "${_FUN_DEFAULT_LIB_DIR}" CACHE PATH "Default library directory for Fun stdlib (override with -DDEFAULT_LIB_DIR=...)" FORCE) + set(DEFAULT_LIB_DIR "${_FUN_DEFAULT_LIB_DIR}" CACHE PATH "Default library directory for Fun stdlib (override with -DDEFAULT_LIB_DIR=...)" FORCE) else() set(DEFAULT_LIB_DIR "${DEFAULT_LIB_DIR}" CACHE PATH "Default library directory for Fun stdlib (override with -DDEFAULT_LIB_DIR=...)" FORCE) endif() @@ -30,27 +30,27 @@ endif() option(FUN_USE_MUSL "Use musl libc toolchain when available (Linux only)" OFF) if(FUN_USE_MUSL AND UNIX AND NOT APPLE) - # Try to find a musl toolchain wrapper - find_program(_FUN_MUSL_CC NAMES musl-gcc musl-clang) - if(_FUN_MUSL_CC) - message(STATUS "FUN_USE_MUSL=ON: using musl toolchain: ${_FUN_MUSL_CC}") - # Force C compiler to musl wrapper before project() so the whole toolchain is configured accordingly - set(CMAKE_C_COMPILER "${_FUN_MUSL_CC}" CACHE FILEPATH "C compiler" FORCE) - set(FUN_LIBC "musl" CACHE STRING "Selected C library") - else() - message(WARNING "FUN_USE_MUSL=ON but no musl toolchain (musl-gcc or musl-clang) found. Falling back to default compiler (likely glibc).") - set(FUN_LIBC "glibc" CACHE STRING "Selected C library") - endif() + # Try to find a musl toolchain wrapper + find_program(_FUN_MUSL_CC NAMES musl-gcc musl-clang) + if(_FUN_MUSL_CC) + message(STATUS "FUN_USE_MUSL=ON: using musl toolchain: ${_FUN_MUSL_CC}") + # Force C compiler to musl wrapper before project() so the whole toolchain is configured accordingly + set(CMAKE_C_COMPILER "${_FUN_MUSL_CC}" CACHE FILEPATH "C compiler" FORCE) + set(FUN_LIBC "musl" CACHE STRING "Selected C library") + else() + message(WARNING "FUN_USE_MUSL=ON but no musl toolchain (musl-gcc or musl-clang) found. Falling back to default compiler (likely glibc).") + set(FUN_LIBC "glibc" CACHE STRING "Selected C library") + endif() else() - # Default remains the system toolchain (typically glibc on Linux) - set(FUN_LIBC "glibc" CACHE STRING "Selected C library") + # Default remains the system toolchain (typically glibc on Linux) + set(FUN_LIBC "glibc" CACHE STRING "Selected C library") endif() # Expose a preprocessor macro indicating selected C library if(FUN_LIBC STREQUAL "musl") - add_definitions(-DFUN_LIBC_MUSL) + add_definitions(-DFUN_LIBC_MUSL) else() - add_definitions(-DFUN_LIBC_GLIBC) + add_definitions(-DFUN_LIBC_GLIBC) endif() # Optional: build statically linked executables @@ -82,8 +82,8 @@ if(FUN_LINK_STATIC) # On MSVC, prefer the static runtime if(MSVC) - foreach(flag_var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_MINSIZEREL) + foreach(flag_var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_MINSIZEREL) if(DEFINED ${flag_var}) string(REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") endif() @@ -151,7 +151,7 @@ if(FUN_WITH_TCLTK) endif() # Ensure trailing slash if(NOT DEFAULT_LIB_DIR MATCHES "/$") - set(DEFAULT_LIB_DIR "${DEFAULT_LIB_DIR}/") + set(DEFAULT_LIB_DIR "${DEFAULT_LIB_DIR}/") endif() # Optional SQLite support @@ -184,14 +184,14 @@ option(FUN_WITH_PCSC "Enable PCSC (pcsclite) support" OFF) set(PCSC_LINK_LIBS "") set(PCSC_INCLUDE_DIRS "") if(FUN_WITH_PCSC) - message(STATUS "Building with PCSC support") - add_definitions(-DFUN_WITH_PCSC) - if(APPLE) - list(APPEND PCSC_LINK_LIBS "-framework PCSC") - else() - list(APPEND PCSC_INCLUDE_DIRS "/usr/include/PCSC") - list(APPEND PCSC_LINK_LIBS pcsclite) - endif() + message(STATUS "Building with PCSC support") + add_definitions(-DFUN_WITH_PCSC) + if(APPLE) + list(APPEND PCSC_LINK_LIBS "-framework PCSC") + else() + list(APPEND PCSC_INCLUDE_DIRS "/usr/include/PCSC") + list(APPEND PCSC_LINK_LIBS pcsclite) + endif() endif() # Optional JSON (json-c) support @@ -269,9 +269,9 @@ if(FUN_WITH_XML2) list(APPEND LIBXML2_INCLUDE_DIRS "/usr/include/libxml2") include_directories(${LIBXML2_INCLUDE_DIRS}) endif() - else() - message(FATAL_ERROR "libxml2 not found. Install libxml2 (dev headers) or disable FUN_WITH_XML2.") - endif() + else() + message(FATAL_ERROR "libxml2 not found. Install libxml2 (dev headers) or disable FUN_WITH_XML2.") + endif() endif() # As a robust fallback, add standard system include path for libxml2 if present if(EXISTS "/usr/include/libxml2") @@ -365,21 +365,21 @@ endif() # Core VM/library sources add_library(fun_core - src/bytecode.c - src/parser.c - src/value.c - src/vm.c + src/bytecode.c + src/parser.c + src/value.c + src/vm.c ) target_include_directories(fun_core PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src ) # Apply options to core if(FUN_DEBUG) - message(STATUS "FUN_DEBUG enabled: building with verbose debug logging") - target_compile_definitions(fun_core PUBLIC FUN_VERSION="${PROJECT_VERSION}") - target_compile_definitions(fun_core PUBLIC FUN_DEBUG=1) + message(STATUS "FUN_DEBUG enabled: building with verbose debug logging") + target_compile_definitions(fun_core PUBLIC FUN_VERSION="${PROJECT_VERSION}") + target_compile_definitions(fun_core PUBLIC FUN_DEBUG=1) endif() # Provide default stdlib directory to the runtime @@ -468,16 +468,10 @@ if(Threads_FOUND) target_link_libraries(fun_core PUBLIC Threads::Threads) endif() -# Link libm for C99 math functions if available -find_library(M_LIB m) -if(M_LIB) - target_link_libraries(fun_core PUBLIC ${M_LIB}) -endif() - # Interpreter /usr/bin/fun option(FUN_WITH_REPL "Enable interactive REPL in the fun CLI" OFF) add_executable(fun - src/fun.c + src/fun.c ) # Provide version string to the CLI if(FUN_WITH_REPL) @@ -489,12 +483,12 @@ target_link_libraries(fun PRIVATE fun_core) # Internal test programs add_executable(fun_test - src/fun_test.c + src/fun_test.c ) target_link_libraries(fun_test PRIVATE fun_core) add_executable(test_opcodes - src/test_opcodes.c + src/test_opcodes.c ) target_link_libraries(test_opcodes PRIVATE fun_core) @@ -517,11 +511,11 @@ set(FUN_RUN_SCRIPT "" CACHE STRING "Script to run with the 'run' target, e.g. -D if(FUN_WITH_REPL) add_custom_target(repl - COMMAND $ - DEPENDS fun - USES_TERMINAL - COMMENT "Run Fun REPL" -) + COMMAND $ + DEPENDS fun + USES_TERMINAL + COMMENT "Run Fun REPL" + ) endif() add_custom_target(run @@ -561,14 +555,14 @@ add_custom_target(ops-quiet ) add_custom_target(examples - COMMAND ${CMAKE_COMMAND} -E env FUN_LIB_DIR="${FUN_LIB}" FUN_WITH_INI=$,1,0> ${CMAKE_SOURCE_DIR}/scripts/run_examples.sh + COMMAND ${CMAKE_COMMAND} -E env FUN_LIB_DIR="${FUN_LIB}" ${CMAKE_SOURCE_DIR}/scripts/run_examples.sh DEPENDS fun USES_TERMINAL COMMENT "Build and run all examples" ) add_custom_target(run-examples - COMMAND ${CMAKE_COMMAND} -E env FUN_LIB_DIR="${FUN_LIB}" FUN_WITH_INI=$,1,0> ${CMAKE_SOURCE_DIR}/scripts/run_examples.sh + COMMAND ${CMAKE_COMMAND} -E env FUN_LIB_DIR="${FUN_LIB}" ${CMAKE_SOURCE_DIR}/scripts/run_examples.sh DEPENDS fun USES_TERMINAL COMMENT "Build and run all examples" @@ -576,20 +570,20 @@ add_custom_target(run-examples # Clean and distclean add_custom_target(fun_clean - COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target clean - COMMENT "Clean build outputs (objects, binaries) in the build directory" + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target clean + COMMENT "Clean build outputs (objects, binaries) in the build directory" ) add_custom_target(distclean - COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target clean - COMMAND ${CMAKE_COMMAND} -E rm -rf - ${CMAKE_BINARY_DIR}/CMakeCache.txt - ${CMAKE_BINARY_DIR}/CMakeFiles - ${CMAKE_BINARY_DIR}/cmake_install.cmake - ${CMAKE_BINARY_DIR}/install_manifest.txt - ${CMAKE_BINARY_DIR}/Makefile - ${CMAKE_BINARY_DIR}/*.ninja - ${CMAKE_BINARY_DIR}/.ninja_* - COMMENT "Remove build outputs and CMake-generated files (reconfigure needed)" + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target clean + COMMAND ${CMAKE_COMMAND} -E rm -rf + ${CMAKE_BINARY_DIR}/CMakeCache.txt + ${CMAKE_BINARY_DIR}/CMakeFiles + ${CMAKE_BINARY_DIR}/cmake_install.cmake + ${CMAKE_BINARY_DIR}/install_manifest.txt + ${CMAKE_BINARY_DIR}/Makefile + ${CMAKE_BINARY_DIR}/*.ninja + ${CMAKE_BINARY_DIR}/.ninja_* + COMMENT "Remove build outputs and CMake-generated files (reconfigure needed)" ) # Robust uninstall support as a function (uses install_manifest*.txt) @@ -621,14 +615,14 @@ list(APPEND _candidates set(_manifest_file \"\") foreach(_cand IN LISTS _candidates) if(EXISTS \"\${_cand}\") - set(_manifest_file \"\${_cand}\") - break() + set(_manifest_file \"\${_cand}\") + break() endif() endforeach() if(NOT _manifest_file) message(FATAL_ERROR - \"Cannot find install manifest. Tried: \${_candidates}. + \"Cannot find install manifest. Tried: \${_candidates}. If you installed from a different build directory, run uninstall from that build directory (where install_manifest*.txt resides).\") endif() @@ -638,21 +632,21 @@ set(_removed 0) set(_dirs \"\") foreach(_file IN LISTS _files) if(_file STREQUAL \"\") - continue() + continue() endif() if(EXISTS \"\${_file}\" OR IS_SYMLINK \"\${_file}\") - message(STATUS \"Uninstalling: \${_file}\") - file(REMOVE \"\${_file}\") - # If the file still exists after attempting to remove it, fail with an explicit error - if(EXISTS \"\${_file}\" OR IS_SYMLINK \"\${_file}\") - message(FATAL_ERROR \"Failed to remove: \${_file}. Try running the uninstall target with sudo if it is a system install.\") - endif() - # Track the containing directory for potential cleanup - get_filename_component(_dir \"\${_file}\" DIRECTORY) - list(APPEND _dirs \"\${_dir}\") - math(EXPR _removed \"\${_removed}+1\") + message(STATUS \"Uninstalling: \${_file}\") + file(REMOVE \"\${_file}\") + # If the file still exists after attempting to remove it, fail with an explicit error + if(EXISTS \"\${_file}\" OR IS_SYMLINK \"\${_file}\") + message(FATAL_ERROR \"Failed to remove: \${_file}. Try running the uninstall target with sudo if it is a system install.\") + endif() + # Track the containing directory for potential cleanup + get_filename_component(_dir \"\${_file}\" DIRECTORY) + list(APPEND _dirs \"\${_dir}\") + math(EXPR _removed \"\${_removed}+1\") else() - message(STATUS \"Skipping (not found): \${_file}\") + message(STATUS \"Skipping (not found): \${_file}\") endif() endforeach() @@ -665,14 +659,14 @@ set(_all_dirs \"\") foreach(_d IN LISTS _dirs) set(_p \"\${_d}\") while(NOT \"\${_p}\" STREQUAL \"\" AND NOT \"\${_p}\" STREQUAL \"/\") - list(APPEND _all_dirs \"\${_p}\") - get_filename_component(_p \"\${_p}\" DIRECTORY) - # Stop collecting parents once we reach a safe root or the filesystem root - list(FIND _safe_roots \"\${_p}\" _root_idx) - if(_root_idx GREATER -1) list(APPEND _all_dirs \"\${_p}\") - break() - endif() + get_filename_component(_p \"\${_p}\" DIRECTORY) + # Stop collecting parents once we reach a safe root or the filesystem root + list(FIND _safe_roots \"\${_p}\" _root_idx) + if(_root_idx GREATER -1) + list(APPEND _all_dirs \"\${_p}\") + break() + endif() endwhile() endforeach() @@ -685,38 +679,38 @@ set(_pass 0) while(TRUE) set(_pass_removed 0) foreach(_dir IN LISTS _all_dirs) - if(EXISTS \"\${_dir}\" AND IS_DIRECTORY \"\${_dir}\") - # Only act on directories within safe roots - set(_allowed FALSE) - foreach(_root IN LISTS _safe_roots) - if(\"\${_dir}\" MATCHES \"^\${_root}(/|\$)\") - set(_allowed TRUE) - break() - endif() - endforeach() - if(NOT _allowed) - # e.g. /usr/bin — do not remove - continue() - endif() + if(EXISTS \"\${_dir}\" AND IS_DIRECTORY \"\${_dir}\") + # Only act on directories within safe roots + set(_allowed FALSE) + foreach(_root IN LISTS _safe_roots) + if(\"\${_dir}\" MATCHES \"^\${_root}(/|\$)\") + set(_allowed TRUE) + break() + endif() + endforeach() + if(NOT _allowed) + # e.g. /usr/bin — do not remove + continue() + endif() - # Remove only if empty (do not recurse unrelated content) - file(GLOB _dir_contents LIST_DIRECTORIES true \"\${_dir}/*\") - list(LENGTH _dir_contents _dir_len) - if(_dir_len EQUAL 0) - message(STATUS \"Removing empty directory: \${_dir}\") - file(REMOVE_RECURSE \"\${_dir}\") - if(IS_DIRECTORY \"\${_dir}\") - message(FATAL_ERROR \"Failed to remove directory: \${_dir}. Try running the uninstall target with sudo if it is a system install.\") + # Remove only if empty (do not recurse unrelated content) + file(GLOB _dir_contents LIST_DIRECTORIES true \"\${_dir}/*\") + list(LENGTH _dir_contents _dir_len) + if(_dir_len EQUAL 0) + message(STATUS \"Removing empty directory: \${_dir}\") + file(REMOVE_RECURSE \"\${_dir}\") + if(IS_DIRECTORY \"\${_dir}\") + message(FATAL_ERROR \"Failed to remove directory: \${_dir}. Try running the uninstall target with sudo if it is a system install.\") + endif() + math(EXPR _removed_dirs \"\${_removed_dirs}+1\") + math(EXPR _pass_removed \"\${_pass_removed}+1\") + endif() endif() - math(EXPR _removed_dirs \"\${_removed_dirs}+1\") - math(EXPR _pass_removed \"\${_pass_removed}+1\") - endif() - endif() endforeach() math(EXPR _pass \"\${_pass}+1\") if(_pass_removed EQUAL 0 OR _pass GREATER 5) - break() + break() endif() endwhile() @@ -726,13 +720,13 @@ message(STATUS \"Uninstall finished. Removed \${_removed} files and \${_removed_ # Expose an 'uninstall' build target (run with sudo if system locations were used) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} - -D MANIFEST="${CMAKE_BINARY_DIR}/install_manifest.txt" - -D CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}" - -D CMAKE_SOURCE_DIR="${CMAKE_SOURCE_DIR}" - -P "${_FUN_UNINSTALL_SCRIPT}" + -D MANIFEST="${CMAKE_BINARY_DIR}/install_manifest.txt" + -D CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}" + -D CMAKE_SOURCE_DIR="${CMAKE_SOURCE_DIR}" + -P "${_FUN_UNINSTALL_SCRIPT}" USES_TERMINAL COMMENT "Uninstall files installed by this project (use sudo if needed)" -) + ) endfunction() # Define the uninstall target @@ -741,25 +735,21 @@ fun_add_uninstall_target() # Install rules # Binary install(TARGETS fun - RUNTIME DESTINATION /usr/bin -) + RUNTIME DESTINATION /usr/bin) # Libs install(DIRECTORY lib/ - DESTINATION /usr/share/fun/lib - FILES_MATCHING PATTERN "*.fun" -) + DESTINATION /usr/share/fun/lib + FILES_MATCHING PATTERN "*.fun") # Optionally install example scripts option(FUN_INSTALL_EXAMPLES "Install example .fun scripts" ON) if(FUN_INSTALL_EXAMPLES) - install(DIRECTORY examples/ - DESTINATION /usr/share/fun/examples - FILES_MATCHING PATTERN "*.fun" - ) + install(DIRECTORY examples/ + DESTINATION /usr/share/fun/examples + FILES_MATCHING PATTERN "*.fun") endif() # - Docs install(FILES README.md LICENSE - DESTINATION /usr/share/doc/fun -) + DESTINATION /usr/share/doc/fun) diff --git a/examples/cli_argv_dump.fun b/examples/cli_argv_dump.fun deleted file mode 100755 index 0b2c2e9..0000000 --- a/examples/cli_argv_dump.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-30 - */ - -// Demonstrates accessing raw script arguments via lib/cli.fun -// Usage: -// FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/cli_argv_dump.fun -f --force=42 foo bar - -#include - -args = argv() - -print(join(["FUN_ARGC=", env("FUN_ARGC")], "")) -print(join(["FUN_ARGS='", env("FUN_ARGS"), "'"], "")) -print("Raw argv array:") -print(args) - -/* Expected output: -FUN_ARGC=3 -FUN_ARGS='-f --force --force=42' -Raw argv array: -[-f, --force, --force=42 -*/ diff --git a/examples/cli_parse_example.fun b/examples/cli_parse_example.fun deleted file mode 100755 index be40b40..0000000 --- a/examples/cli_parse_example.fun +++ /dev/null @@ -1,48 +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-30 - */ - -// Demonstrates parsing flags like -f, --force, --force=42 and positionals -// Usage examples: -// FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/cli_parse_example.fun -f foo -// FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/cli_parse_example.fun --force bar -// FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/cli_parse_example.fun --force=42 alpha beta -// FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/cli_parse_example.fun -abc x y - -#include - -args = argv() -parsed = parse_args(args) -flags = parsed["flags"] -pos = parsed["positionals"] - -if (flags["f"] == 1 || flags["force"] == 1) - print("Force enabled") - -// No hasKey() builtin available here; check via keys()+contains() -flag_keys = keys(flags) -if (contains(flag_keys, "force") && typeof(flags["force"]) == "string") - print(join(["Force value=", flags["force"]], "")) - -print("Flags map:") -print(flags) -print("Positionals:") -print(pos) - -/* Expected output (FUN_LIB_DIR="$(pwd)/lib" ./build/fun ./examples/cli_parse_example.fun --force bar xx x x --for=xxx --h=1): -1 -Force enabled -Flags map: -{"force": 1, "for": xxx, "h": 1} -Positionals: -[bar, xx, x, x] -*/ diff --git a/examples/data/complex.ini b/examples/data/complex.ini index 2d73052..18084af 100644 --- a/examples/data/complex.ini +++ b/examples/data/complex.ini @@ -1,29 +1,32 @@ -[auth] -user = "hanez" -token = "abcd1234" [app] -name = "FunApp" -version = "1.2.3" -debug = "1" +name = "FunApp" +version = "1.2.3" +debug = "1" + [database] -host = "localhost" -port = "5432" -user = "fun" -pass = "secret" -pool_size = "8" -timeout = "2.5" +host = "localhost" +port = "5432" +user = "fun" +pass = "secret" +pool_size = "8" +timeout = "2.5" + [network] -ssl = "yes" -retries = "3" -base_url = "https://api.example.com" +ssl = "yes" +retries = "3" +base_url = "https://api.example.com" + [features] -feature_x = "on" -feature_y = "off" +feature_x = "on" +feature_y = "off" + [paths] -data_dir = "./data" -log_file = "./logs/app.log" +data_dir = "./data" +log_file = "./logs/app.log" + + diff --git a/examples/data/htdocs/info.fun b/examples/data/htdocs/info.fun index eca1076..65e64cb 100644 --- a/examples/data/htdocs/info.fun +++ b/examples/data/htdocs/info.fun @@ -16,10 +16,7 @@ print("

Fun Environment Information

") print("

Current Path: " + env("PATH") + "

") print("

Current User: " + env("USER") + "

") print("

Time: " + date_format(time_now_ms(), "%Y-%m-%d %H:%M:%S") + "

") -print("

Local Version: " + to_string(fun_version()) + "

") -res = proc_run("fun -V") -installed_version = res["out"] -print("

Installed Version: " + to_string(substr(installed_version, 4, len(installed_version)-2)) + "

") +print("

Version: " + to_string(fun_version()) + "

") print("

Full Environment:

") print("
    ") all_env = env_all() diff --git a/examples/env_all.fun b/examples/env_all.fun old mode 100755 new mode 100644 diff --git a/examples/extra/http_server_test.fun b/examples/extra/http_server_test.fun index 4a35ae9..ad3ab64 100755 --- a/examples/extra/http_server_test.fun +++ b/examples/extra/http_server_test.fun @@ -1,16 +1,3 @@ -#!/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-28 - */ - #include c = TcpClient() @@ -22,6 +9,7 @@ if (c.connect("127.0.0.1", 8080)) resp = c.recv_all(4096) print("GET Response contains foo=bar: " + to_string(find(resp, "foo = bar") >= 0)) print("GET Response contains baz=qux: " + to_string(find(resp, "baz = qux") >= 0)) + c.close() else print("Failed to connect to server") diff --git a/examples/ini_complex.fun b/examples/ini_complex.fun index 2707983..5c7d2b1 100755 --- a/examples/ini_complex.fun +++ b/examples/ini_complex.fun @@ -91,9 +91,10 @@ else retries=3 base_url=https://api.example.com [features] - feature_x=1 + 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 7895584..40f9cc4 100755 --- a/examples/ini_demo.fun +++ b/examples/ini_demo.fun @@ -19,7 +19,7 @@ if h == 0 print("Failed to load " + path) else u = ini_get_string(h, "auth", "user", "guest") - r = ini_get_int(h, "network", "retries", 5) + r = ini_get_int(h, "network", "retries", 3) s = ini_get_bool(h, "network", "ssl", 0) print("user=" + u) print("retries=" + to_string(r)) @@ -33,11 +33,4 @@ else user=����U retries=3 ssl=1 - -I wonder about the user= value when the default value is used! - -It should look like: -user=guest -retries=3 -ssl=1 */ diff --git a/examples/ini_diag.fun b/examples/ini_diag.fun deleted file mode 100644 index f360cd9..0000000 --- a/examples/ini_diag.fun +++ /dev/null @@ -1,50 +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: 2026-01-02 - */ - -// Minimal diagnostic for INI lookups - -path = "./examples/data/complex.ini" -h = ini_load(path) -print("h=" + to_string(h)) -if h == 0 - print("Failed to load: " + path) -else - print("[try app:name]") - v1 = ini_get_string(h, "app", "name", "") - print("app:name => " + v1) - - print("[try app:version]") - v2 = ini_get_string(h, "app", "version", "") - print("app:version => " + v2) - - print("[try database:port]") - v3 = ini_get_int(h, "database", "port", -1) - print("database:port => " + to_string(v3)) - - print("[try network:ssl]") - v4 = ini_get_bool(h, "network", "ssl", -9) - print("network:ssl => " + to_string(v4)) - - ini_free(h) - -/* Expected output: -h=1 -[try app:name] -app:name => FunApp -[try app:version] -app:version => 1.2.3 -[try database:port] -database:port => 5432 -[try network:ssl] -network:ssl => 1 -*/ diff --git a/examples/input_hidden_example.fun b/examples/input_hidden_example.fun deleted file mode 100755 index 770f69f..0000000 --- a/examples/input_hidden_example.fun +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Example: Demonstrate hidden input (no echo) for passwords. - * - * Added: 2026-01-02 - */ - -#include - -print("=== input_hidden() example ===") - -c = Console() - -username = c.ask("Username") -password = c.ask_hidden("Password") - -// Do not print the password; just show the username -print(join(["Hello, ", username, "!"], "")) - -/* Expected output: -Username: hanez -Password: -=== input_hidden() example === -Hello, hanez! -*/ diff --git a/examples/input_hidden_pam_auth.fun b/examples/input_hidden_pam_auth.fun deleted file mode 100755 index 56d5fb7..0000000 --- a/examples/input_hidden_pam_auth.fun +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Example: Demonstrate hidden input (no echo) for passwords using - * /usr/bin/chkpwd from https://git.xw3.org/hanez/chkusr for PAM authetication. - * - * Added: 2026-01-02 - */ - -#include - -print("=== input_hidden() example with PAM authentication ===") - -c = Console() - -username = c.ask("Username") -password = c.ask_hidden("Password") - -// Do not print the password; just show the username -print(join(["Hello, ", username, "!"], "")) - -res = proc_run("chkpwd -u " + username + " -p " + password) -content = res["out"] -if (res["code"] == 0) - print("Login success!") -else - print("Login failed!") - -/* Possible output: -Username: hanez -Password: -=== input_hidden() example with PAM authentication === -Hello, hanez! -Login success! - -Or: -Username: hanez -Password: -=== input_hidden() example with PAM authentication === -Hello, hanez! -Login failed! -*/ diff --git a/examples/math_ceil.fun b/examples/math_ceil.fun deleted file mode 100644 index 5c2ef16..0000000 --- a/examples/math_ceil.fun +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -// Demo of C99-like ceil(x) - -print("=== math ceil demo start ===") - -print("ceil(3.2) -> " + to_string(ceil(3.2))) // 4 -print("ceil(3.0) -> " + to_string(ceil(3.0))) // 3 -print("ceil(-3.2) -> " + to_string(ceil(-3.2))) // -3 -print("ceil(-3.0) -> " + to_string(ceil(-3.0))) // -3 -print("ceil(0.5) -> " + to_string(ceil(0.5))) // 1 -print("ceil(-0.5) -> " + to_string(ceil(-0.5))) // 0 - -print("=== math ceil demo end ===") - -/* Expected output (values): -=== math ceil demo start === -ceil(3.2) -> 4 -ceil(3.0) -> 3 -ceil(-3.2) -> -3 -ceil(-3.0) -> -3 -ceil(0.5) -> 1 -ceil(-0.5) -> 0 -=== math ceil demo end === -*/ diff --git a/examples/math_cos.fun b/examples/math_cos.fun deleted file mode 100644 index f498126..0000000 --- a/examples/math_cos.fun +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -// Demo of cos(x) - -print("=== math cos demo start ===") - -print("cos(0) -> " + to_string(cos(0))) -print("cos(3.14159265359) ~ pi -> " + to_string(cos(3.14159265359))) -print("cos(1.57079632679) ~ pi/2 -> " + to_string(cos(1.57079632679))) - -print("=== math cos demo end ===") - -/* Expected output (approximate): -=== math cos demo start === -cos(0) -> 1 -cos(3.14159265359) ~ pi -> -1 -cos(1.57079632679) ~ pi/2 -> 0 -=== math cos demo end === -*/ diff --git a/examples/math_exp_log.fun b/examples/math_exp_log.fun deleted file mode 100644 index 5be06d2..0000000 --- a/examples/math_exp_log.fun +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -// Demo of exp(x), log(x), log10(x) - -print("=== math exp/log demo start ===") - -print("exp(0) -> " + to_string(exp(0))) -print("log(1) -> " + to_string(log(1))) -print("log10(1) -> " + to_string(log10(1))) -print("exp(1) -> " + to_string(exp(1))) -print("log(2.71828182846) ~ ln(e) -> " + to_string(log(2.71828182846))) - -print("=== math exp/log demo end ===") - -/* Expected output (approximate): -=== math exp/log demo start === -exp(0) -> 1 -log(1) -> 0 -log10(1) -> 0 -exp(1) -> 2.718281828... -log(2.71828182846) ~ ln(e) -> 1 -=== math exp/log demo end === -*/ diff --git a/examples/math_floor.fun b/examples/math_floor.fun deleted file mode 100644 index 94783c7..0000000 --- a/examples/math_floor.fun +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -// Demo of C99-like floor(x) - -print("=== math floor demo start ===") - -print("floor(3.7) -> " + to_string(floor(3.7))) // 3 -print("floor(3.0) -> " + to_string(floor(3.0))) // 3 -print("floor(-3.2) -> " + to_string(floor(-3.2))) // -4 -print("floor(-3.0) -> " + to_string(floor(-3.0))) // -3 -print("floor(0.5) -> " + to_string(floor(0.5))) // 0 -print("floor(-0.5) -> " + to_string(floor(-0.5))) // -1 - -print("=== math floor demo end ===") - -/* Expected output (values): -=== math floor demo start === -floor(3.7) -> 3 -floor(3.0) -> 3 -floor(-3.2) -> -4 -floor(-3.0) -> -3 -floor(0.5) -> 0 -floor(-0.5) -> -1 -=== math floor demo end === -*/ diff --git a/examples/math_fmin_fmax.fun b/examples/math_fmin_fmax.fun deleted file mode 100644 index 2b1a49d..0000000 --- a/examples/math_fmin_fmax.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 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -// Demo of fmin(x,y) / fmax(x,y) — float-aware min/max with C99 NaN handling - -print("=== math fmin/fmax demo start ===") - -print("fmin(3.2, 4) -> " + to_string(fmin(3.2, 4))) // 3.2 -print("fmax(3.2, 4) -> " + to_string(fmax(3.2, 4))) // 4 -print("fmin(-5, -2.5) -> " + to_string(fmin(-5, -2.5))) // -5 -print("fmax(-5, -2.5) -> " + to_string(fmax(-5, -2.5))) // -2.5 - -print("=== math fmin/fmax demo end ===") - -/* Expected output: -=== math fmin/fmax demo start === -fmin(3.2, 4) -> 3.2000000000000002 -fmax(3.2, 4) -> 4 -fmin(-5, -2.5) -> -5 -fmax(-5, -2.5) -> -2.5 -=== math fmin/fmax demo end === -*/ diff --git a/examples/math_gcd_lcm.fun b/examples/math_gcd_lcm.fun deleted file mode 100644 index 4f0fbab..0000000 --- a/examples/math_gcd_lcm.fun +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -print("=== math gcd/lcm demo start ===") - -print("gcd(48,18) -> " + to_string(gcd(48,18))) // 6 -print("gcd(0,5) -> " + to_string(gcd(0,5))) // 5 -print("gcd(-21,6) -> " + to_string(gcd(-21,6))) // 3 - -print("lcm(21,6) -> " + to_string(lcm(21,6))) // 42 -print("lcm(0,5) -> " + to_string(lcm(0,5))) // 0 -print("lcm(-4,6) -> " + to_string(lcm(-4,6))) // 12 - -print("=== math gcd/lcm demo end ===") - -/* Expected output (values): -=== math gcd/lcm demo start === -gcd(48,18) -> 6 -gcd(0,5) -> 5 -gcd(-21,6) -> 3 -lcm(21,6) -> 42 -lcm(0,5) -> 0 -lcm(-4,6) -> 12 -=== math gcd/lcm demo end === -*/ diff --git a/examples/math_isqrt.fun b/examples/math_isqrt.fun deleted file mode 100644 index e232444..0000000 --- a/examples/math_isqrt.fun +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -print("=== math isqrt demo start ===") - -print("isqrt(-4) -> " + to_string(isqrt(-4))) // 0 (non-negative only) -print("isqrt(0) -> " + to_string(isqrt(0))) // 0 -print("isqrt(1) -> " + to_string(isqrt(1))) // 1 -print("isqrt(2) -> " + to_string(isqrt(2))) // 1 -print("isqrt(3) -> " + to_string(isqrt(3))) // 1 -print("isqrt(4) -> " + to_string(isqrt(4))) // 2 -print("isqrt(15) -> " + to_string(isqrt(15))) // 3 -print("isqrt(16) -> " + to_string(isqrt(16))) // 4 - -print("=== math isqrt demo end ===") - -/* Expected output (values): -=== math isqrt demo start === -isqrt(-4) -> 0 -isqrt(0) -> 0 -isqrt(1) -> 1 -isqrt(2) -> 1 -isqrt(3) -> 1 -isqrt(4) -> 2 -isqrt(15) -> 3 -isqrt(16) -> 4 -=== math isqrt demo end === -*/ diff --git a/examples/math_round.fun b/examples/math_round.fun deleted file mode 100644 index 0c2117c..0000000 --- a/examples/math_round.fun +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -// Demo of C99-like round(x) — half away from zero - -print("=== math round demo start ===") - -print("round(3.2) -> " + to_string(round(3.2))) // 3 -print("round(3.5) -> " + to_string(round(3.5))) // 4 -print("round(3.8) -> " + to_string(round(3.8))) // 4 -print("round(-3.2) -> " + to_string(round(-3.2))) // -3 -print("round(-3.5) -> " + to_string(round(-3.5))) // -4 -print("round(-3.8) -> " + to_string(round(-3.8))) // -4 - -print("=== math round demo end ===") - -/* Expected output (values): -=== math round demo start === -round(3.2) -> 3 -round(3.5) -> 4 -round(3.8) -> 4 -round(-3.2) -> -3 -round(-3.5) -> -4 -round(-3.8) -> -4 -=== math round demo end === -*/ diff --git a/examples/math_sign.fun b/examples/math_sign.fun deleted file mode 100644 index 9fdfd6c..0000000 --- a/examples/math_sign.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 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -print("=== math sign demo start ===") - -print("sign(-5) -> " + to_string(sign(-5))) // -1 -print("sign(0) -> " + to_string(sign(0))) // 0 -print("sign(7) -> " + to_string(sign(7))) // 1 -print("sign(-0.0) -> " + to_string(sign(-0.0))) // 0 -print("sign(3.14) -> " + to_string(sign(3.14))) // 1 - -print("=== math sign demo end ===") - -/* Expected output (values): -=== math sign demo start === -sign(-5) -> -1 -sign(0) -> 0 -sign(7) -> 1 -sign(-0.0) -> 0 -sign(3.14) -> 1 -=== math sign demo end === -*/ diff --git a/examples/math_sin.fun b/examples/math_sin.fun deleted file mode 100644 index 193881b..0000000 --- a/examples/math_sin.fun +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -// Demo of sin(x) - -print("=== math sin demo start ===") - -print("sin(0) -> " + to_string(sin(0))) -print("sin(1.57079632679) ~ pi/2 -> " + to_string(sin(1.57079632679))) -print("sin(-1.57079632679) -> " + to_string(sin(-1.57079632679))) - -print("=== math sin demo end ===") - -/* Expected output (approximate): -=== math sin demo start === -sin(0) -> 0 -sin(1.57079632679) ~ pi/2 -> 1 -sin(-1.57079632679) -> -1 -=== math sin demo end === -*/ diff --git a/examples/math_sqrt.fun b/examples/math_sqrt.fun deleted file mode 100644 index 6052981..0000000 --- a/examples/math_sqrt.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 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -// Demo of sqrt(x) - -print("=== math sqrt demo start ===") - -print("sqrt(0) -> " + to_string(sqrt(0))) -print("sqrt(9) -> " + to_string(sqrt(9))) -print("sqrt(2) -> " + to_string(sqrt(2))) -print("sqrt(-1) -> " + to_string(sqrt(-1))) // NaN expected - -print("=== math sqrt demo end ===") - -/* Expected output (approximate): -=== math sqrt demo start === -sqrt(0) -> 0 -sqrt(9) -> 3 -sqrt(2) -> 1.41421356... -sqrt(-1) -> nan -=== math sqrt demo end === -*/ diff --git a/examples/math_tan.fun b/examples/math_tan.fun deleted file mode 100644 index fd63efa..0000000 --- a/examples/math_tan.fun +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -// Demo of tan(x) - -print("=== math tan demo start ===") - -print("tan(0) -> " + to_string(tan(0))) -print("tan(0.78539816339) ~ pi/4 -> " + to_string(tan(0.78539816339))) - -print("=== math tan demo end ===") - -/* Expected output (approximate): -=== math tan demo start === -tan(0) -> 0 -tan(0.78539816339) ~ pi/4 -> 1 -=== math tan demo end === -*/ diff --git a/examples/math_trunc.fun b/examples/math_trunc.fun deleted file mode 100644 index e8f395b..0000000 --- a/examples/math_trunc.fun +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env fun - -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -// Demo of C99-like trunc(x) — rounds toward zero - -print("=== math trunc demo start ===") - -print("trunc(3.7) -> " + to_string(trunc(3.7))) // 3 -print("trunc(3.0) -> " + to_string(trunc(3.0))) // 3 -print("trunc(-3.7) -> " + to_string(trunc(-3.7))) // -3 -print("trunc(-3.0) -> " + to_string(trunc(-3.0))) // -3 -print("trunc(0.5) -> " + to_string(trunc(0.5))) // 0 -print("trunc(-0.5) -> " + to_string(trunc(-0.5))) // 0 - -print("=== math trunc demo end ===") - -/* Expected output (values): -=== math trunc demo start === -trunc(3.7) -> 3 -trunc(3.0) -> 3 -trunc(-3.7) -> -3 -trunc(-3.0) -> -3 -trunc(0.5) -> 0 -trunc(-0.5) -> 0 -=== math trunc demo end === -*/ diff --git a/examples/ripemd160_demo.fun b/examples/ripemd160_demo.fun index 0d90f97..9f7d06c 100755 --- a/examples/ripemd160_demo.fun +++ b/examples/ripemd160_demo.fun @@ -51,8 +51,8 @@ RIPEMD-160(hex: 616263) = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc */ /* Actual output: -RIPEMD-160("") = 560e66de427fc2c3399e86c3e1d38c3c6017def2 -RIPEMD-160("abc") = 74266d6b4447493abb790c49e4bfa8f07263bcff -RIPEMD-160("message digest") = 80ee01c69badb632393a9e05d1e7eebc2f05ac48 -RIPEMD-160(hex: 616263) = 74266d6b4447493abb790c49e4bfa8f07263bcff +RIPEMD-160("") = d90cc72ef4ad5a2c51f89dc938de0b989c4c4b46 +RIPEMD-160("abc") = 7f339b642aaa074a849c6c1cd860cb049cc792b3 +RIPEMD-160("message digest") = a4f9cd0270a341d05a713df4bf49a3ce8aabde64 +RIPEMD-160(hex: 616263) = 7f339b642aaa074a849c6c1cd860cb049cc792b3 */ diff --git a/examples/ripemd160_example.fun b/examples/ripemd160_example.fun deleted file mode 100644 index 36610cd..0000000 --- a/examples/ripemd160_example.fun +++ /dev/null @@ -1,36 +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: 2026-01-02 - */ - -// THIS IS BROKEN ACTUALLY! IT DOES NOT CALCULATE THE EXPECTED HASHES! - -/* - * Example: Using RIPEMD160 from lib/crypt/ripemd160.fun - * - * Known vectors: - * - "" (empty) => 9c1185a5c5e9fc54612808977ee8f548b2258d31 - * - "abc" => 8eb208f7e05d987a9b04... (full known); we verify via hex of "abc" - * - "The quick brown fox jumps over the lazy dog" => 37f332f68db77bd9d7edd4969571ad671cf9dd3b - */ - -#include - -r = RIPEMD160() - -// empty string -print(r.ripemd160_str("")) - -// "abc" -print(r.ripemd160_hex("616263")) - -// pangram -print(r.ripemd160_str("The quick brown fox jumps over the lazy dog")) diff --git a/lib/cli.fun b/lib/cli.fun deleted file mode 100644 index bf9ea6c..0000000 --- a/lib/cli.fun +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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-30 - */ - -// Simple CLI helpers for Fun scripts. -// -// argv(): returns an array of the raw arguments passed to the script -// (not including the script path). Requires the interpreter to -// export FUN_ARGC and FUN_ARGV_i environment variables. -// parse_args(args): parses flags and positionals from argv() like: -// -f -> flags["f"] = 1 -// --force -> flags["force"] = 1 -// --force=42 -> flags["force"] = "42" -// -abc -> flags["a"]=1, flags["b"]=1, flags["c"]=1 - -fun argv() - n = to_number(env("FUN_ARGC")) - a = [] - i = 0 - while (i < n) - // Build key without join() to avoid any potential call resolution issues - key = "FUN_ARGV_" + to_string(i) - push(a, env(key)) - i = i + 1 - return a - -// Returns: { "flags": map, "positionals": array } -fun parse_args(args) - flags = {} - pos = [] - i = 0 - n = len(args) - while (i < n) - s = args[i] - slen = len(s) - if (slen >= 2 && substr(s, 0, 2) == "--") - eq = find(s, "=") - if (eq >= 0) - key = substr(s, 2, eq - 2) // between -- and = - val = substr(s, eq + 1, slen - (eq + 1)) - flags[key] = val - else - key = substr(s, 2, slen - 2) - flags[key] = 1 - else if (slen == 2 && substr(s, 0, 1) == "-") - // short -f boolean - key = substr(s, 1, 1) - flags[key] = 1 - else if (slen > 2 && substr(s, 0, 1) == "-") - // compact short flags like -abc -> a=1, b=1, c=1 - j = 1 - while (j < slen) - k = substr(s, j, 1) - flags[k] = 1 - j = j + 1 - else - push(pos, s) - i = i + 1 - return { "flags": flags, "positionals": pos } diff --git a/lib/crypt/ripemd160.fun b/lib/crypt/ripemd160.fun index bfb4d8b..f866ab1 100644 --- a/lib/crypt/ripemd160.fun +++ b/lib/crypt/ripemd160.fun @@ -15,10 +15,7 @@ class RIPEMD160() KL = [0, 1518500249, 1859775393, 2400959708, 2840853838] - //KR = [1352829926, 1548603684, 1836062451, 2054012649, 0] - // Correct KR constants according to RIPEMD-160 specification: - // 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000 - KR = [1352829926, 1554976324, 1836072691, 2053994217, 0] + KR = [1352829926, 1548603684, 1836062451, 2054012649, 0] RL = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, @@ -61,23 +58,22 @@ class RIPEMD160() return x fun rol32(this, x, s) - x = this.u32(x) - return this.u32(bor(shl(x, s), shr(x, 32 - s))) + return rol(this.u32(x), s) fun add32(this, a, b) return this.u32(a + b) fun F(this, stage, x, y, z) if stage == 0 - return this.u32(bxor(bxor(x, y), z)) + return bxor(bxor(x, y), z) else if stage == 1 - return this.u32(bor(band(x, y), band(bnot(x), z))) + return bor(band(x, y), band(bnot(x), z)) else if stage == 2 - return this.u32(bxor(bor(x, bnot(y)), z)) + return bxor(bor(x, bnot(y)), z) else if stage == 3 - return this.u32(bor(band(x, z), band(y, bnot(z)))) + return bor(band(x, z), band(y, bnot(z))) else - return this.u32(bxor(x, bor(y, bnot(z)))) + return bxor(x, bor(y, bnot(z))) fun hex_val(this, ch) if ch == "0" @@ -208,9 +204,7 @@ class RIPEMD160() B = T // Right side step - // Note: Right branch uses F in reverse stage order (stage_r), - // but the constants KR are indexed by the forward round index (stage). - Tr = this.add32(this.rol32(this.add32(this.add32(Ar, this.F(stage_r, Br, Cr, Dr)), this.add32(M[this.RR[j]], this.KR[stage])), this.SR[j]), Er) + Tr = this.add32(this.rol32(this.add32(this.add32(Ar, this.F(stage_r, Br, Cr, Dr)), this.add32(M[this.RR[j]], this.KR[stage_r])), this.SR[j]), Er) Ar = Er Er = Dr Dr = this.rol32(Cr, 10) @@ -236,19 +230,11 @@ class RIPEMD160() h3 = state[3] h4 = state[4] - // Final state combination (canonical RIPEMD-160): - // T = h1 + Cl + Dr - // h1 = h2 + Dl + Er - // h2 = h3 + El + Ar - // h3 = h4 + Al + Br - // h4 = h0 + Bl + Cr - // h0 = T - T = this.add32(h1, this.add32(C, Dr)) - state[1] = this.add32(h2, this.add32(D, Er)) - state[2] = this.add32(h3, this.add32(E, Ar)) - state[3] = this.add32(h4, this.add32(A, Br)) - state[4] = this.add32(h0, this.add32(B, Cr)) - state[0] = T + state[0] = this.add32(h1, this.add32(B, Cr)) + state[1] = this.add32(h2, this.add32(C, Dr)) + state[2] = this.add32(h3, this.add32(D, Er)) + state[3] = this.add32(h4, this.add32(E, Ar)) + state[4] = this.add32(h0, this.add32(A, Br)) return state fun ripemd160_bytes(this, bytes) diff --git a/lib/crypt/ripemd160_new_try.fun b/lib/crypt/ripemd160_new_try.fun deleted file mode 100644 index 41ba167..0000000 --- a/lib/crypt/ripemd160_new_try.fun +++ /dev/null @@ -1,316 +0,0 @@ -/* - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-02 - */ - -// THIS IS BROKEN ACTUALLY! IT DOES NOT CALCULATE THE EXPECTED HASHES! - -// lib/crypt/ripemd160.fun -// Pure Fun implementation of RIPEMD-160 operating on hex-string or ASCII inputs. -// -// Public API (class): -// rip = RIPEMD160() -// rip.ripemd160_hex(hexStr) -> digest hex string (lowercase) -// rip.ripemd160_str("abc") -> digest hex string of ASCII bytes - -#include - -class RIPEMD160() - // 32-bit helpers (match style from md5.fun) - fun u32(this, x) - m = 4294967296 - while x < 0 - x = x + m - while x >= m - x = x - m - return x - - fun add32(this, a, b) - return this.u32(a + b) - - fun add32_5(this, a, b, c, d, e) - return this.u32(this.u32(this.u32(this.u32(a + b) + c) + d) + e) - - fun rol32(this, x, s) - return rol(this.u32(x), s) - - fun and32(this, a, b) - return band(this.u32(a), this.u32(b)) - - fun or32(this, a, b) - return bor(this.u32(a), this.u32(b)) - - fun xor32(this, a, b) - return bxor(this.u32(a), this.u32(b)) - - fun not32(this, x) - return bnot(this.u32(x)) - - // hex helpers (same as md5/sha files) - fun hex_val(this, ch) - if (ch == "0") - return 0 - else if (ch == "1") - return 1 - else if (ch == "2") - return 2 - else if (ch == "3") - return 3 - else if (ch == "4") - return 4 - else if (ch == "5") - return 5 - else if (ch == "6") - return 6 - else if (ch == "7") - return 7 - else if (ch == "8") - return 8 - else if (ch == "9") - return 9 - else if (ch == "a" || ch == "A") - return 10 - else if (ch == "b" || ch == "B") - return 11 - else if (ch == "c" || ch == "C") - return 12 - else if (ch == "d" || ch == "D") - return 13 - else if (ch == "e" || ch == "E") - return 14 - else if (ch == "f" || ch == "F") - return 15 - else - return 0 - - fun byte_from_hex_pair(this, hh) - hi = this.hex_val(substr(hh, 0, 1)) - lo = this.hex_val(substr(hh, 1, 1)) - return hi * 16 + lo - - fun from_hex(this, hex) - arr = [] - i = 0 - n = len(hex) - while i + 1 < n - b = this.byte_from_hex_pair(substr(hex, i, 2)) - push(arr, b) - i = i + 2 - return arr - - fun two_hex(this, n) - n = n % 256 - d = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"] - hi = n / 16 - lo = n % 16 - parts = [d[hi], d[lo]] - return join(parts, "") - - fun bytes_to_hex(this, arr) - i = 0 - out = [] - while i < len(arr) - push(out, this.two_hex(arr[i])) - i = i + 1 - return join(out, "") - - // Padding (RIPEMD-160): like MD4/MD5 — append 0x80, zeros to 56 mod 64, then 64-bit length in little-endian - fun pad_bytes(this, bytes) - L = len(bytes) - out = [] - i = 0 - while i < L - push(out, bytes[i]) - i = i + 1 - push(out, 128) - while (len(out) % 64) != 56 - push(out, 0) - len_bits = L * 8 - j = 0 - while j < 8 - b = (len_bits / pow(2, 8 * j)) % 256 - push(out, b) - j = j + 1 - return out - - fun word32_le(this, b0, b1, b2, b3) - return this.u32(b0 + b1 * 256 + b2 * 65536 + b3 * 16777216) - - // RIPEMD-160 boolean functions - fun f1(this, x, y, z) - return this.xor32(this.xor32(x, y), z) - - fun f2(this, x, y, z) - return this.or32(this.and32(x, y), this.and32(this.not32(x), z)) - - fun f3(this, x, y, z) - return this.xor32(this.or32(x, this.not32(y)), z) - - fun f4(this, x, y, z) - return this.or32(this.and32(x, z), this.and32(y, this.not32(z))) - - fun f5(this, x, y, z) - return this.xor32(x, this.or32(y, this.not32(z))) - - // Process a 512-bit block - fun process_block(this, H, block) - // message words X[16] (little-endian) - X = [] - i = 0 - while i < 16 - j = i * 4 - push(X, this.word32_le(block[j], block[j+1], block[j+2], block[j+3])) - i = i + 1 - - // R and S (left line) - R = [ - 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, - 7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8, - 3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12, - 1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2, - 4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13 - ] - S = [ - 11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8, - 7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12, - 11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5, - 11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12, - 9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6 - ] - - // R' and S' (right line) - Rp = [ - 5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12, - 6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2, - 15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13, - 8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14, - 12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11 - ] - Sp = [ - 8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6, - 9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11, - 9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5, - 15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8, - 8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11 - ] - - // Constants - KL = [0, 1518500249, 1859775393, 2400959708, 2840853838] // 0x00, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E - KR = [1352829926, 1548603684, 1836072691, 2053994217, 0] // 0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000 - - al = H[0]; bl = H[1]; cl = H[2]; dl = H[3]; el = H[4] - ar = H[0]; br = H[1]; cr = H[2]; dr = H[3]; er = H[4] - - j = 0 - while j < 80 - sL = S[j] - sR = Sp[j] - - // select function and constant per round for left lane - if (j < 16) - fL = this.f1(bl, cl, dl) - kL = KL[0] - else if (j < 32) - fL = this.f2(bl, cl, dl) - kL = KL[1] - else if (j < 48) - fL = this.f3(bl, cl, dl) - kL = KL[2] - else if (j < 64) - fL = this.f4(bl, cl, dl) - kL = KL[3] - else - fL = this.f5(bl, cl, dl) - kL = KL[4] - - // right lane - if (j < 16) - fR = this.f5(br, cr, dr) - kR = KR[0] - else if (j < 32) - fR = this.f4(br, cr, dr) - kR = KR[1] - else if (j < 48) - fR = this.f3(br, cr, dr) - kR = KR[2] - else if (j < 64) - fR = this.f2(br, cr, dr) - kR = KR[3] - else - fR = this.f1(br, cr, dr) - kR = KR[4] - - // left step - tl = this.add32_5(al, fL, X[R[j]], kL, 0) - tl = this.rol32(tl, sL) - tl = this.add32(tl, el) - al = el - el = dl - dl = this.rol32(cl, 10) - cl = bl - bl = tl - - // right step - tr = this.add32_5(ar, fR, X[Rp[j]], kR, 0) - tr = this.rol32(tr, sR) - tr = this.add32(tr, er) - ar = er - er = dr - dr = this.rol32(cr, 10) - cr = br - br = tr - - j = j + 1 - - // combine using originals - h0 = H[0]; h1 = H[1]; h2 = H[2]; h3 = H[3]; h4 = H[4] - tt = this.add32(h0, this.add32(bl, cr)) - H[0] = this.add32(h1, this.add32(cl, dr)) - H[1] = this.add32(h2, this.add32(dl, er)) - H[2] = this.add32(h3, this.add32(el, ar)) - H[3] = this.add32(h4, this.add32(al, br)) - H[4] = tt - return H - - fun ripemd160_bytes(this, bytes) - // initialize h0..h4 - H = [1732584193, 4023233417, 2562383102, 271733878, 3285377520] - data = this.pad_bytes(bytes) - off = 0 - N = len(data) - while off < N - block = [] - i = 0 - while i < 64 - push(block, data[off + i]) - i = i + 1 - H = this.process_block(H, block) - off = off + 64 - - // output as little-endian of h0..h4 (20 bytes) - out = [] - j = 0 - while j < 5 - v = H[j] - push(out, v % 256) - push(out, (v / 256) % 256) - push(out, (v / 65536) % 256) - push(out, (v / 16777216) % 256) - j = j + 1 - return out - - fun ripemd160_hex(this, hexStr) - bytes = this.from_hex(hexStr) - digest = this.ripemd160_bytes(bytes) - return this.bytes_to_hex(digest) - - fun ripemd160_str(this, str) - bytes = string_to_bytes_ascii(str) - digest = this.ripemd160_bytes(bytes) - return this.bytes_to_hex(digest) diff --git a/lib/io/console.fun b/lib/io/console.fun index e2a0cd0..52c89ad 100644 --- a/lib/io/console.fun +++ b/lib/io/console.fun @@ -17,15 +17,6 @@ class Console() else return input(join([q, ": "], "")) - // Ask for a secret string (e.g., password) without echoing input - // Returns the entered string. A trailing newline is not echoed back. - fun ask_hidden(this, question) - q = to_string(question) - if (len(q) == 0) - return input_hidden("") - else - return input_hidden(join([q, ": "], "")) - // Ask a yes/no question; returns 1 for yes, 0 for no // Accepts: y, yes, n, no (case-insensitive). Keeps asking until valid. fun ask_yes_no(this, question) diff --git a/lib/io/ini.fun b/lib/io/ini.fun index 6261dec..b6145e5 100644 --- a/lib/io/ini.fun +++ b/lib/io/ini.fun @@ -29,7 +29,7 @@ class INI() // Load an INI file from path, closing previous one if open. // Returns handle (>0) or 0 on error. fun load(this, path) - if (this.is_open()) + if (this.h > 0) ini_free(this.h) this.h = 0 p = to_string(path) @@ -39,13 +39,11 @@ class INI() // True if a dictionary is open. fun is_open(this) - // Be robust across runtimes: coerce to number and test non-zero - // Avoid using '>' to prevent integer-only GT errors in VM - return to_number(this.h) != 0 + return this.h > 0 // Close and free resources. Safe to call multiple times. fun close(this) - if (this.is_open()) + if (this.h > 0) ini_free(this.h) this.h = 0 return 1 diff --git a/lib/net/http_server.fun b/lib/net/http_server.fun index 06a8402..dc78dac 100644 --- a/lib/net/http_server.fun +++ b/lib/net/http_server.fun @@ -84,7 +84,7 @@ class HTTPServer(number port) env_vars = "QUERY_STRING='" + query_string + "' " if (len(post_data) > 0) env_vars = env_vars + "POST_DATA='" + post_data + "' " - + res = proc_run(env_vars + " " + "fun" + " " + file_path) content = res["out"] else diff --git a/make b/make index a6a0205..914b699 100755 --- a/make +++ b/make @@ -100,8 +100,6 @@ elif [ "$target" = "freebsd" ]; then -DFUN_LINK_STATIC=OFF \ -DFUN_DEBUG=OFF \ && cmake --build build --target fun -elif [ "$target" = "install" ]; then - sudo cmake --build build --target install elif [ "$target" = "minimal" ]; then rm -rf build \ && cmake -S . -B build \ @@ -161,7 +159,6 @@ else echo " - alpine"; echo " - debug"; echo " - freebsd"; - echo " - install"; echo " - minimal"; echo " - musl"; echo " - repl"; diff --git a/scripts/run_examples.sh b/scripts/run_examples.sh index 1690cd7..1f7f5cd 100755 --- a/scripts/run_examples.sh +++ b/scripts/run_examples.sh @@ -69,23 +69,16 @@ fi rc=0 for f in "${files[@]}"; do - base_name="$(basename "$f")" - # If INI support is disabled, skip INI examples to avoid expected failures - if [[ "${FUN_WITH_INI:-}" =~ ^(0|OFF|off|false|False)$ ]]; then - if [[ "$base_name" == ini_*.fun ]]; then - echo "=== Skipping (INI disabled): examples/$base_name ===" - continue - fi - fi echo "=== Running: ${f#$ROOT/} ===" if ! "$BIN" "$f"; then echo "FAILED: ${f#$ROOT/}" - dest="$EX_DIR/error/$base_name" + 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_name" + echo "Moved to: examples/error/$base" else - echo "warning: failed to move $base_name to examples/error/" >&2 + echo "warning: failed to move $base to examples/error/" >&2 fi rc=1 fi diff --git a/src/bytecode.c b/src/bytecode.c index 80a4ce7..f1af0e2 100644 --- a/src/bytecode.c +++ b/src/bytecode.c @@ -189,23 +189,6 @@ static const char *opcode_name(OpCode op) { case OP_TK_LABEL: return "TK_LABEL"; case OP_TK_BUTTON: return "TK_BUTTON"; case OP_TK_PACK: return "TK_PACK"; - case OP_FLOOR: return "FLOOR"; - case OP_CEIL: return "CEIL"; - case OP_TRUNC: return "TRUNC"; - case OP_ROUND: return "ROUND"; - case OP_SIN: return "SIN"; - case OP_COS: return "COS"; - case OP_TAN: return "TAN"; - case OP_EXP: return "EXP"; - case OP_LOG: return "LOG"; - case OP_LOG10: return "LOG10"; - case OP_SQRT: return "SQRT"; - case OP_GCD: return "GCD"; - case OP_LCM: return "LCM"; - case OP_ISQRT: return "ISQRT"; - case OP_SIGN: return "SIGN"; - case OP_FMIN: return "FMIN"; - case OP_FMAX: return "FMAX"; default: return "???"; } } diff --git a/src/bytecode.h b/src/bytecode.h index 7944573..0c0e971 100644 --- a/src/bytecode.h +++ b/src/bytecode.h @@ -236,32 +236,7 @@ typedef enum { // exceptions (minimal) OP_TRY_PUSH, // operand = handler ip; push handler onto try-stack OP_TRY_POP, // pop current handler - OP_THROW, // pops error value; if handler -> jump to it (push err), else print and terminate - - // C99 math.h rounding family (float-aware) - OP_FLOOR, // pops x (int/float); pushes floor(x) (int if integral else float) - OP_CEIL, // pops x (int/float); pushes ceil(x) (int if integral else float) - OP_TRUNC, // pops x (int/float); pushes trunc(x) (int if integral else float) - OP_ROUND, // pops x (int/float); pushes round(x) (half away from zero) - - // C99 math.h transcendentals (float-aware) - OP_SIN, // pops x (int/float); pushes sin(x) - OP_COS, // pops x (int/float); pushes cos(x) - OP_TAN, // pops x (int/float); pushes tan(x) - OP_EXP, // pops x (int/float); pushes exp(x) - OP_LOG, // pops x (int/float); pushes natural log ln(x) - OP_LOG10, // pops x (int/float); pushes log10(x) - OP_SQRT, // pops x (int/float); pushes sqrt(x) - - // Integer math helpers - OP_GCD, // pops b, a; pushes gcd(|a|,|b|) - OP_LCM, // pops b, a; pushes lcm(|a|,|b|) (0 if either is 0) - OP_ISQRT, // pops x; pushes floor(sqrt(max(0,x))) for integers - OP_SIGN, // pops x; pushes -1, 0, or 1 depending on the sign - - // Min/Max variants (float-aware, C99 semantics) - OP_FMIN, // pops b, a (int/float); pushes fmin(a,b) (NaN handling per C99) - OP_FMAX // pops b, a (int/float); pushes fmax(a,b) (NaN handling per C99) + OP_THROW // pops error value; if handler -> jump to it (push err), else print and terminate } OpCode; typedef struct { diff --git a/src/fun.c b/src/fun.c index 08ae886..041e306 100644 --- a/src/fun.c +++ b/src/fun.c @@ -1,13 +1,4 @@ -/* -* 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 - */ - - /* +/** * Main entry point for the Fun language interpreter. * Builds a CLI that runs a script file if provided; otherwise starts the REPL * when compiled with FUN_WITH_REPL enabled. @@ -94,46 +85,6 @@ int main(int argc, char **argv) { if (argi < argc) { const char *path = argv[argi]; - - /* Collect script arguments (everything after script path) and expose via env vars */ - int sargi = argi + 1; /* first script arg following the script path */ - int sargc = (sargi < argc) ? (argc - sargi) : 0; - - /* Export FUN_ARGC */ - { - char buf[32]; - snprintf(buf, sizeof(buf), "%d", sargc); - setenv("FUN_ARGC", buf, 1); - } - - /* Export FUN_ARGV_i */ - for (int i = 0; i < sargc; ++i) { - char key[32]; - snprintf(key, sizeof(key), "FUN_ARGV_%d", i); - setenv(key, argv[sargi + i], 1); - } - - /* Optional: space-joined convenience string FUN_ARGS */ - if (sargc > 0) { - size_t total = 0; - for (int i = 0; i < sargc; ++i) { - total += strlen(argv[sargi + i]) + 1; /* +1 for space or NUL */ - } - char *joined = (char*)malloc(total); - if (joined) { - joined[0] = '\0'; - for (int i = 0; i < sargc; ++i) { - strcat(joined, argv[sargi + i]); - if (i + 1 < sargc) strcat(joined, " "); - } - setenv("FUN_ARGS", joined, 1); - free(joined); - } - } else { - /* Ensure FUN_ARGS is at least cleared for consistency */ - setenv("FUN_ARGS", "", 1); - } - Bytecode *bc = parse_file_to_bytecode(path); if (!bc) { fprintf(stderr, "Failed to compile script: %s\n", path); diff --git a/src/fun_test.c b/src/fun_test.c index fa28b2c..64928f8 100644 --- a/src/fun_test.c +++ b/src/fun_test.c @@ -11,7 +11,6 @@ #include "value.h" #include "vm.h" #include -#include #define ASSERT_EQ(val, expected) \ if ((val).type != VAL_INT || (val).i != (expected)) { \ @@ -34,25 +33,6 @@ int main(void) { int c3 = bytecode_add_constant(bc, make_int(3)); int c10 = bytecode_add_constant(bc, make_int(10)); int c42 = bytecode_add_constant(bc, make_int(42)); - int cf3_2 = bytecode_add_constant(bc, make_float(3.2)); - int cf3_5 = bytecode_add_constant(bc, make_float(3.5)); - int cf3_8 = bytecode_add_constant(bc, make_float(3.8)); - int cfn3_2 = bytecode_add_constant(bc, make_float(-3.2)); - int cfn3_5 = bytecode_add_constant(bc, make_float(-3.5)); - int cfn3_8 = bytecode_add_constant(bc, make_float(-3.8)); - int cf0 = bytecode_add_constant(bc, make_float(0.0)); - int cf1 = bytecode_add_constant(bc, make_float(1.0)); - int cf5 = bytecode_add_constant(bc, make_float(5.0)); - int c4 = bytecode_add_constant(bc, make_int(4)); - int c9 = bytecode_add_constant(bc, make_int(9)); - int c48 = bytecode_add_constant(bc, make_int(48)); - int c18 = bytecode_add_constant(bc, make_int(18)); - int c21 = bytecode_add_constant(bc, make_int(21)); - int c6 = bytecode_add_constant(bc, make_int(6)); - int c15 = bytecode_add_constant(bc, make_int(15)); - int c16 = bytecode_add_constant(bc, make_int(16)); - int cneg5 = bytecode_add_constant(bc, make_int(-5)); - int c7 = bytecode_add_constant(bc, make_int(7)); // ---------- Arithmetic ---------- bytecode_add_instruction(bc, OP_LOAD_CONST, c42); @@ -140,156 +120,6 @@ int main(void) { bytecode_add_instruction(bc, OP_LOAD_CONST, c1); bytecode_add_instruction(bc, OP_POP, 0); // dApache-2.0ard 1 (stack now empty) - // ---------- Rounding (math.h) demo ---------- - // floor/ceil/trunc/round on representative values - int start_round_demo = bc->instr_count; - (void)start_round_demo; - - // +3.2 - bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2); - bytecode_add_instruction(bc, OP_FLOOR, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2); - bytecode_add_instruction(bc, OP_CEIL, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2); - bytecode_add_instruction(bc, OP_TRUNC, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2); - bytecode_add_instruction(bc, OP_ROUND, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - - // +3.5 - bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_5); - bytecode_add_instruction(bc, OP_ROUND, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - - // -3.5 - bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_5); - bytecode_add_instruction(bc, OP_ROUND, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - - // -3.2 - bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_2); - bytecode_add_instruction(bc, OP_FLOOR, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_2); - bytecode_add_instruction(bc, OP_CEIL, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - - // integers should be unchanged - bytecode_add_instruction(bc, OP_LOAD_CONST, c10); - bytecode_add_instruction(bc, OP_FLOOR, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - - // ---------- Transcendentals demo ---------- - // sin(0)=0 - bytecode_add_instruction(bc, OP_LOAD_CONST, cf0); - bytecode_add_instruction(bc, OP_SIN, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - // cos(0)=1 - bytecode_add_instruction(bc, OP_LOAD_CONST, cf0); - bytecode_add_instruction(bc, OP_COS, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - // tan(0)=0 - bytecode_add_instruction(bc, OP_LOAD_CONST, cf0); - bytecode_add_instruction(bc, OP_TAN, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - // exp(0)=1 - bytecode_add_instruction(bc, OP_LOAD_CONST, cf0); - bytecode_add_instruction(bc, OP_EXP, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - // log(1)=0 - bytecode_add_instruction(bc, OP_LOAD_CONST, cf1); - bytecode_add_instruction(bc, OP_LOG, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - // log10(1)=0 - bytecode_add_instruction(bc, OP_LOAD_CONST, cf1); - bytecode_add_instruction(bc, OP_LOG10, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - // sqrt(9)=3 - bytecode_add_instruction(bc, OP_LOAD_CONST, c9); - bytecode_add_instruction(bc, OP_SQRT, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - - // ---------- Integer math (gcd/lcm/isqrt/sign) demo ---------- - // gcd(48,18)=6 - bytecode_add_instruction(bc, OP_LOAD_CONST, c48); - bytecode_add_instruction(bc, OP_LOAD_CONST, c18); - bytecode_add_instruction(bc, OP_GCD, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - - // lcm(21,6)=42 - bytecode_add_instruction(bc, OP_LOAD_CONST, c21); - bytecode_add_instruction(bc, OP_LOAD_CONST, c6); - bytecode_add_instruction(bc, OP_LCM, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - - // isqrt cases: 0, 1, 15->3, 16->4 - bytecode_add_instruction(bc, OP_LOAD_CONST, c0); - bytecode_add_instruction(bc, OP_ISQRT, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - bytecode_add_instruction(bc, OP_LOAD_CONST, c1); - bytecode_add_instruction(bc, OP_ISQRT, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - bytecode_add_instruction(bc, OP_LOAD_CONST, c15); - bytecode_add_instruction(bc, OP_ISQRT, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - bytecode_add_instruction(bc, OP_LOAD_CONST, c16); - bytecode_add_instruction(bc, OP_ISQRT, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - - // sign(-5)=-1, sign(0)=0, sign(7)=1 - bytecode_add_instruction(bc, OP_LOAD_CONST, cneg5); - bytecode_add_instruction(bc, OP_SIGN, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - bytecode_add_instruction(bc, OP_LOAD_CONST, c0); - bytecode_add_instruction(bc, OP_SIGN, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - bytecode_add_instruction(bc, OP_LOAD_CONST, c7); - bytecode_add_instruction(bc, OP_SIGN, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - - // ---------- fmin/fmax demo ---------- - // fmin(3.2, 4) -> 3.2 - bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2); - bytecode_add_instruction(bc, OP_LOAD_CONST, c4); - bytecode_add_instruction(bc, OP_FMIN, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - // fmax(3.2, 4) -> 4 - bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2); - bytecode_add_instruction(bc, OP_LOAD_CONST, c4); - bytecode_add_instruction(bc, OP_FMAX, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - // NaN cases - double nanv = NAN; - int cNaN = bytecode_add_constant(bc, make_float(nanv)); - // fmin(NaN, 5.0) -> 5.0 - bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN); - bytecode_add_instruction(bc, OP_LOAD_CONST, cf5); - bytecode_add_instruction(bc, OP_FMIN, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - // fmax(NaN, 5.0) -> 5.0 - bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN); - bytecode_add_instruction(bc, OP_LOAD_CONST, cf5); - bytecode_add_instruction(bc, OP_FMAX, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - // fmin(5.0, NaN) -> 5.0 - bytecode_add_instruction(bc, OP_LOAD_CONST, cf5); - bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN); - bytecode_add_instruction(bc, OP_FMIN, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - // fmax(5.0, NaN) -> 5.0 - bytecode_add_instruction(bc, OP_LOAD_CONST, cf5); - bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN); - bytecode_add_instruction(bc, OP_FMAX, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - // fmin(NaN, NaN) -> NaN (prints as nan) - bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN); - bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN); - bytecode_add_instruction(bc, OP_FMIN, 0); - bytecode_add_instruction(bc, OP_PRINT, 0); - // ---------- HALT ---------- bytecode_add_instruction(bc, OP_HALT, 0); diff --git a/src/parser.c b/src/parser.c index d014a73..f5b69d9 100644 --- a/src/parser.c +++ b/src/parser.c @@ -709,20 +709,6 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) free(name); return 1; } - if (strcmp(name, "input_hidden") == 0) { - (*pos)++; /* '(' */ - int hasPrompt = 0; - skip_spaces(src, len, pos); - if (*pos < len && src[*pos] != ')') { - if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "input_hidden expects 0 or 1 argument"); free(name); return 0; } - hasPrompt = 1; - } - if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after input_hidden arg(s)"); free(name); return 0; } - /* operand bit0 = hasPrompt, bit1 = hidden */ - bytecode_add_instruction(bc, OP_INPUT_LINE, (hasPrompt ? 1 : 0) | 2); - free(name); - return 1; - } if (strcmp(name, "proc_run") == 0) { (*pos)++; /* '(' */ if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "proc_run expects 1 argument (command string)"); free(name); return 0; } @@ -1742,22 +1728,6 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) free(name); return 1; } - if (strcmp(name, "fmin") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "fmin expects 2 args"); free(name); return 0; } - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "fmin expects 2 args"); free(name); return 0; } - bytecode_add_instruction(bc, OP_FMIN, 0); - free(name); - return 1; - } - if (strcmp(name, "fmax") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "fmax expects 2 args"); free(name); return 0; } - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "fmax expects 2 args"); free(name); return 0; } - bytecode_add_instruction(bc, OP_FMAX, 0); - free(name); - return 1; - } if (strcmp(name, "clamp") == 0) { (*pos)++; /* '(' */ if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "clamp expects 3 args"); free(name); return 0; } @@ -1774,113 +1744,6 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) free(name); return 1; } - if (strcmp(name, "floor") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "floor expects 1 arg"); free(name); return 0; } - bytecode_add_instruction(bc, OP_FLOOR, 0); - free(name); - return 1; - } - if (strcmp(name, "ceil") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "ceil expects 1 arg"); free(name); return 0; } - bytecode_add_instruction(bc, OP_CEIL, 0); - free(name); - return 1; - } - if (strcmp(name, "trunc") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "trunc expects 1 arg"); free(name); return 0; } - bytecode_add_instruction(bc, OP_TRUNC, 0); - free(name); - return 1; - } - if (strcmp(name, "round") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "round expects 1 arg"); free(name); return 0; } - bytecode_add_instruction(bc, OP_ROUND, 0); - free(name); - return 1; - } - if (strcmp(name, "sin") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "sin expects 1 arg"); free(name); return 0; } - bytecode_add_instruction(bc, OP_SIN, 0); - free(name); - return 1; - } - if (strcmp(name, "cos") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "cos expects 1 arg"); free(name); return 0; } - bytecode_add_instruction(bc, OP_COS, 0); - free(name); - return 1; - } - if (strcmp(name, "tan") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "tan expects 1 arg"); free(name); return 0; } - bytecode_add_instruction(bc, OP_TAN, 0); - free(name); - return 1; - } - if (strcmp(name, "exp") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "exp expects 1 arg"); free(name); return 0; } - bytecode_add_instruction(bc, OP_EXP, 0); - free(name); - return 1; - } - if (strcmp(name, "log") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "log expects 1 arg"); free(name); return 0; } - bytecode_add_instruction(bc, OP_LOG, 0); - free(name); - return 1; - } - if (strcmp(name, "log10") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "log10 expects 1 arg"); free(name); return 0; } - bytecode_add_instruction(bc, OP_LOG10, 0); - free(name); - return 1; - } - if (strcmp(name, "sqrt") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "sqrt expects 1 arg"); free(name); return 0; } - bytecode_add_instruction(bc, OP_SQRT, 0); - free(name); - return 1; - } - if (strcmp(name, "gcd") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "gcd expects 2 args"); free(name); return 0; } - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "gcd expects 2 args"); free(name); return 0; } - bytecode_add_instruction(bc, OP_GCD, 0); - free(name); - return 1; - } - if (strcmp(name, "lcm") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "lcm expects 2 args"); free(name); return 0; } - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "lcm expects 2 args"); free(name); return 0; } - bytecode_add_instruction(bc, OP_LCM, 0); - free(name); - return 1; - } - if (strcmp(name, "isqrt") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "isqrt expects 1 arg"); free(name); return 0; } - bytecode_add_instruction(bc, OP_ISQRT, 0); - free(name); - return 1; - } - if (strcmp(name, "sign") == 0) { - (*pos)++; /* '(' */ - if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ')')) { parser_fail(*pos, "sign expects 1 arg"); free(name); return 0; } - bytecode_add_instruction(bc, OP_SIGN, 0); - free(name); - return 1; - } if (strcmp(name, "pow") == 0) { (*pos)++; /* '(' */ if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "pow expects 2 args"); free(name); return 0; } diff --git a/src/vm.c b/src/vm.c index 22a45c4..f2a4470 100644 --- a/src/vm.c +++ b/src/vm.c @@ -29,15 +29,9 @@ #include #include #include -/* For hidden input (password) handling in OP_INPUT_LINE */ -#include //#include #endif -#ifdef _WIN32 -#include -#endif - /* Bring in split-out built-ins without changing the build system yet */ #include "iter.c" #include "map.c" @@ -48,11 +42,6 @@ // Optional by extensions commonly used code. #ifdef's are in each single file. #include "external/curl.c" #include "external/ini.c" -#ifdef FUN_WITH_INI -/* Central INI handle registry and helpers */ -#include "vm/ini/handles.c" -#endif -/* Note: INI opcode handlers are included below; changes in vm/ini/*.c require vm.c to rebuild. */ #include "external/json.c" #include "external/libsql.c" #include "external/pcsc.c" @@ -672,27 +661,10 @@ void vm_run(VM *vm, Bytecode *entry) { #include "vm/math/clamp.c" #include "vm/math/max.c" #include "vm/math/min.c" - #include "vm/math/fmax.c" - #include "vm/math/fmin.c" #include "vm/math/mod.c" #include "vm/math/pow.c" - #include "vm/math/floor.c" - #include "vm/math/ceil.c" - #include "vm/math/trunc.c" - #include "vm/math/round.c" - #include "vm/math/sin.c" - #include "vm/math/cos.c" - #include "vm/math/tan.c" - #include "vm/math/exp.c" - #include "vm/math/log.c" - #include "vm/math/log10.c" - #include "vm/math/sqrt.c" #include "vm/math/random_int.c" #include "vm/math/random_seed.c" - #include "vm/math/gcd.c" - #include "vm/math/lcm.c" - #include "vm/math/isqrt.c" - #include "vm/math/sign.c" #include "vm/os/env.c" #include "vm/os/env_all.c" @@ -758,8 +730,6 @@ void vm_run(VM *vm, Bytecode *entry) { #include "vm/ini/set.c" #include "vm/ini/unset.c" #include "vm/ini/save.c" - #else - #include "vm/ini/stubs.c" #endif /* CURL ops */ diff --git a/src/vm/ini/get_bool.c b/src/vm/ini/get_bool.c index 302e287..fa886fb 100644 --- a/src/vm/ini/get_bool.c +++ b/src/vm/ini/get_bool.c @@ -23,37 +23,8 @@ case OP_INI_GET_BOOL: { dictionary *d = ini_get(h); int outb = def; if (d && sec && key) { - char full[1024]; char alt[1024]; - ini_make_full_key(full, sizeof(full), sec, key); - memcpy(alt, full, sizeof(alt)); - for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } } - const char *s = iniparser_getstring(d, full, NULL); - if (!s) s = iniparser_getstring(d, alt, NULL); - if (s) { - /* normalize and parse boolean */ - char buf[256]; - size_t n = strlen(s); - if (n >= 2 && ((s[0]=='"' && s[n-1]=='"') || (s[0]=='\'' && s[n-1]=='\''))) { - size_t copy = (n-2) < sizeof(buf)-1 ? (n-2) : sizeof(buf)-1; - memcpy(buf, s+1, copy); buf[copy] = '\0'; - s = buf; - } - /* trim spaces */ - while (*s && (unsigned char)*s <= ' ') s++; - /* lower copy for textual booleans */ - char lb[256]; size_t li=0; for (; s[li] && li < sizeof(lb)-1; ++li) lb[li] = (char)tolower((unsigned char)s[li]); lb[li]='\0'; - if (strcmp(lb, "true")==0 || strcmp(lb, "yes")==0 || strcmp(lb, "on")==0) { - outb = 1; - } else if (strcmp(lb, "false")==0 || strcmp(lb, "no")==0 || strcmp(lb, "off")==0) { - outb = 0; - } else { - /* numeric */ - char *endp=NULL; long v = strtol(lb, &endp, 10); - outb = (endp && endp!=lb) ? (v!=0) : def; - } - } else { - outb = def; - } + char full[1024]; ini_make_full_key(full, sizeof(full), sec, key); + outb = iniparser_getboolean(d, full, def); } free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh); push_value(vm, make_int(outb ? 1 : 0)); diff --git a/src/vm/ini/get_double.c b/src/vm/ini/get_double.c index 7896621..9cf676f 100644 --- a/src/vm/ini/get_double.c +++ b/src/vm/ini/get_double.c @@ -23,27 +23,8 @@ case OP_INI_GET_DOUBLE: { dictionary *d = ini_get(h); double outd = def; if (d && sec && key) { - char full[1024]; char alt[1024]; - ini_make_full_key(full, sizeof(full), sec, key); - memcpy(alt, full, sizeof(alt)); - for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } } - const char *s = iniparser_getstring(d, full, NULL); - if (!s) s = iniparser_getstring(d, alt, NULL); - if (s) { - char buf[256]; - size_t n = strlen(s); - if (n >= 2 && ((s[0]=='"' && s[n-1]=='"') || (s[0]=='\'' && s[n-1]=='\''))) { - size_t copy = (n-2) < sizeof(buf)-1 ? (n-2) : sizeof(buf)-1; - memcpy(buf, s+1, copy); buf[copy] = '\0'; - s = buf; - } - while (*s && (unsigned char)*s <= ' ') s++; - char *endp = NULL; - double v = strtod(s, &endp); - if (endp && endp != s) outd = v; else outd = def; - } else { - outd = def; - } + char full[1024]; ini_make_full_key(full, sizeof(full), sec, key); + outd = iniparser_getdouble(d, full, def); } free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh); push_value(vm, make_float(outd)); diff --git a/src/vm/ini/get_int.c b/src/vm/ini/get_int.c index 4577ca3..930bea9 100644 --- a/src/vm/ini/get_int.c +++ b/src/vm/ini/get_int.c @@ -23,29 +23,8 @@ case OP_INI_GET_INT: { dictionary *d = ini_get(h); int outi = def; if (d && sec && key) { - char full[1024]; char alt[1024]; - ini_make_full_key(full, sizeof(full), sec, key); - memcpy(alt, full, sizeof(alt)); - for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } } - const char *s = iniparser_getstring(d, full, NULL); - if (!s) s = iniparser_getstring(d, alt, NULL); - if (s) { - /* strip optional quotes and parse */ - char buf[256]; - size_t n = strlen(s); - if (n >= 2 && ((s[0]=='"' && s[n-1]=='"') || (s[0]=='\'' && s[n-1]=='\''))) { - size_t copy = (n-2) < sizeof(buf)-1 ? (n-2) : sizeof(buf)-1; - memcpy(buf, s+1, copy); buf[copy] = '\0'; - s = buf; - } - /* skip leading spaces */ - while (*s && (unsigned char)*s <= ' ') s++; - char *endp = NULL; - long v = strtol(s, &endp, 10); - if (endp && endp != s) outi = (int)v; else outi = def; - } else { - outi = def; - } + char full[1024]; ini_make_full_key(full, sizeof(full), sec, key); + outi = iniparser_getint(d, full, def); } free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh); push_value(vm, make_int(outi)); diff --git a/src/vm/ini/get_string.c b/src/vm/ini/get_string.c index ef44106..fcfa36f 100644 --- a/src/vm/ini/get_string.c +++ b/src/vm/ini/get_string.c @@ -23,19 +23,8 @@ case OP_INI_GET_STRING: { dictionary *d = ini_get(h); const char *res = def; if (d && sec && key) { - char full[1024]; char alt[1024]; - ini_make_full_key(full, sizeof(full), sec, key); - /* Build alternate with dot separator for robustness */ - ini_make_full_key(alt, sizeof(alt), sec, key); - size_t flen = strlen(full); - if (flen < sizeof(alt) && flen > 0) { /* create dot version in alt */ - memcpy(alt, full, flen + 1); - for (size_t i = 0; i < flen; ++i) if (alt[i] == ':') { alt[i] = '.'; break; } - } + char full[1024]; ini_make_full_key(full, sizeof(full), sec, key); const char *s = iniparser_getstring(d, full, def); - if (s == def) { /* not found, try alternate dot form */ - s = iniparser_getstring(d, alt, def); - } res = s ? s : ""; } free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh); diff --git a/src/vm/ini/handles.c b/src/vm/ini/handles.c deleted file mode 100644 index 2515410..0000000 --- a/src/vm/ini/handles.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 - */ - -#ifdef FUN_WITH_INI -#if defined(__has_include) -# if __has_include() -# include -# include -# elif __has_include() -# include -# include -# else -# error "iniparser headers not found" -# endif -#else -# include -# include -#endif - -#include -#include -#include - -#include "handles.h" - -IniSlot g_ini[64]; - -int ini_alloc_handle(dictionary *d) { - if (!d) return 0; - for (int i = 1; i < (int)(sizeof(g_ini)/sizeof(g_ini[0])); ++i) { - if (!g_ini[i].in_use) { g_ini[i].in_use = 1; g_ini[i].dict = d; return i; } - } - return 0; -} - -dictionary* ini_get(int h) { - if (h > 0 && h < (int)(sizeof(g_ini)/sizeof(g_ini[0])) && g_ini[h].in_use) return g_ini[h].dict; - return NULL; -} - -int ini_free_handle(int h) { - if (h <= 0 || h >= (int)(sizeof(g_ini)/sizeof(g_ini[0])) || !g_ini[h].in_use) return 0; - if (g_ini[h].dict) iniparser_freedict(g_ini[h].dict); - g_ini[h].dict = NULL; - g_ini[h].in_use = 0; - return 1; -} - -void ini_make_full_key(char *buf, size_t cap, const char *sec, const char *key) { - if (!buf || cap == 0) return; - if (!sec) sec = ""; - if (!key) key = ""; - /* iniparser expects section:key; lookup is case-insensitive internally */ - snprintf(buf, cap, "%s:%s", sec, key); -} - -#endif /* FUN_WITH_INI */ diff --git a/src/vm/ini/handles.h b/src/vm/ini/handles.h index 831936c..3c2022f 100644 --- a/src/vm/ini/handles.h +++ b/src/vm/ini/handles.h @@ -27,18 +27,35 @@ # include # include #endif -#include +#include /* snprintf for helper */ typedef struct { dictionary *dict; int in_use; } IniSlot; +static IniSlot g_ini[64]; -/* Single global registry (defined in handles.c) */ -extern IniSlot g_ini[64]; +static int ini_alloc_handle(dictionary *d) { + for (int i = 1; i < (int)(sizeof(g_ini)/sizeof(g_ini[0])); ++i) { + if (!g_ini[i].in_use) { g_ini[i].in_use = 1; g_ini[i].dict = d; return i; } + } + return 0; +} +static dictionary* ini_get(int h) { + if (h > 0 && h < (int)(sizeof(g_ini)/sizeof(g_ini[0])) && g_ini[h].in_use) return g_ini[h].dict; + return NULL; +} +static int ini_free_handle(int h) { + if (h <= 0 || h >= (int)(sizeof(g_ini)/sizeof(g_ini[0])) || !g_ini[h].in_use) return 0; + if (g_ini[h].dict) iniparser_freedict(g_ini[h].dict); + g_ini[h].dict = NULL; + g_ini[h].in_use = 0; + return 1; +} -/* Registry API (implemented in handles.c) */ -int ini_alloc_handle(dictionary *d); -dictionary* ini_get(int h); -int ini_free_handle(int h); - -/* Helper to build section:key string safely into provided buffer (implemented in handles.c) */ -void ini_make_full_key(char *buf, size_t cap, const char *sec, const char *key); +/* Helper to build section:key string safely into provided buffer */ +static inline void ini_make_full_key(char *buf, size_t cap, const char *sec, const char *key) { + if (!buf || cap == 0) return; + if (!sec) sec = ""; + if (!key) key = ""; + /* iniparser expects "section:key" */ + snprintf(buf, cap, "%s:%s", sec, key); +} #endif /* FUN_WITH_INI */ diff --git a/src/vm/ini/set.c b/src/vm/ini/set.c index eee9660..a0affd9 100644 --- a/src/vm/ini/set.c +++ b/src/vm/ini/set.c @@ -23,16 +23,9 @@ case OP_INI_SET: { if (d && sec && key) { char *valstr = value_to_string_alloc(&vval); if (valstr) { - char full[1024]; char alt[1024]; - ini_make_full_key(full, sizeof(full), sec, key); - memcpy(alt, full, sizeof(alt)); - for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } } + char full[1024]; snprintf(full, sizeof(full), "%s:%s", sec, key); /* iniparser 4.x does not expose iniparser_set; use dictionary_set */ - if (dictionary_set(d, full, valstr) == 0) { - ok = 1; /* 0 means success */ - } else if (dictionary_set(d, alt, valstr) == 0) { - ok = 1; - } + if (dictionary_set(d, full, valstr) == 0) ok = 1; /* 0 means success */ free(valstr); } } diff --git a/src/vm/ini/stubs.c b/src/vm/ini/stubs.c deleted file mode 100644 index 1d13fb7..0000000 --- a/src/vm/ini/stubs.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file provides stub handlers for INI opcodes when FUN_WITH_INI is disabled. - * Each opcode reports a clear runtime error and returns a safe default. - */ - -/* OP_INI_LOAD: pops path string; pushes 0 (invalid handle) */ -case OP_INI_LOAD: { - Value vpath = pop_value(vm); - (void)vpath; /* unused */ - free_value(vpath); - fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n"); - push_value(vm, make_int(0)); - break; -} - -/* OP_INI_FREE: pops handle; pushes 0 */ -case OP_INI_FREE: { - Value vh = pop_value(vm); - free_value(vh); - fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n"); - push_value(vm, make_int(0)); - break; -} - -/* Getters: pop args; push defaults (string:"", int:0, double:0.0, bool:0) */ -case OP_INI_GET_STRING: { - Value vdef = pop_value(vm); - Value vkey = pop_value(vm); - Value vsec = pop_value(vm); - Value vh = pop_value(vm); - free_value(vh); free_value(vsec); free_value(vkey); - /* cannot convert here; return empty string */ - push_value(vm, make_string("")); - free_value(vdef); - fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n"); - break; -} - -case OP_INI_GET_INT: { - Value vdef = pop_value(vm); - Value vkey = pop_value(vm); - Value vsec = pop_value(vm); - Value vh = pop_value(vm); - free_value(vh); free_value(vsec); free_value(vkey); - (void)vdef; /* unused */ - push_value(vm, make_int(0)); - fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n"); - break; -} - -case OP_INI_GET_DOUBLE: { - Value vdef = pop_value(vm); - Value vkey = pop_value(vm); - Value vsec = pop_value(vm); - Value vh = pop_value(vm); - free_value(vh); free_value(vsec); free_value(vkey); - (void)vdef; /* unused */ - push_value(vm, make_float(0.0)); - fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n"); - break; -} - -case OP_INI_GET_BOOL: { - Value vdef = pop_value(vm); - Value vkey = pop_value(vm); - Value vsec = pop_value(vm); - Value vh = pop_value(vm); - free_value(vh); free_value(vsec); free_value(vkey); - (void)vdef; /* unused */ - push_value(vm, make_int(0)); - fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n"); - break; -} - -/* Mutators: return 0 */ -case OP_INI_SET: { - Value vval = pop_value(vm); - Value vkey = pop_value(vm); - Value vsec = pop_value(vm); - Value vh = pop_value(vm); - free_value(vh); free_value(vsec); free_value(vkey); free_value(vval); - fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n"); - push_value(vm, make_int(0)); - break; -} - -case OP_INI_UNSET: { - Value vkey = pop_value(vm); - Value vsec = pop_value(vm); - Value vh = pop_value(vm); - free_value(vh); free_value(vsec); free_value(vkey); - fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n"); - push_value(vm, make_int(0)); - break; -} - -case OP_INI_SAVE: { - Value vpath = pop_value(vm); - Value vh = pop_value(vm); - free_value(vh); free_value(vpath); - fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n"); - push_value(vm, make_int(0)); - break; -} diff --git a/src/vm/ini/unset.c b/src/vm/ini/unset.c index 8c339c7..d264f72 100644 --- a/src/vm/ini/unset.c +++ b/src/vm/ini/unset.c @@ -20,13 +20,9 @@ case OP_INI_UNSET: { const char *sec = (vsec.type==VAL_STRING)?vsec.s:NULL; int ok = 0; if (d && sec && key) { - char full[1024]; char alt[1024]; - ini_make_full_key(full, sizeof(full), sec, key); - memcpy(alt, full, sizeof(alt)); - for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } } - /* iniparser 4.2.6 dictionary_unset returns void; remove both forms */ + char full[1024]; snprintf(full, sizeof(full), "%s:%s", sec, key); + /* iniparser 4.2.6 dictionary_unset returns void; assume success if inputs are valid */ dictionary_unset(d, full); - dictionary_unset(d, alt); ok = 1; } free_value(vkey); free_value(vsec); free_value(vh); diff --git a/src/vm/io/input_line.c b/src/vm/io/input_line.c index 938dda9..8c624d2 100644 --- a/src/vm/io/input_line.c +++ b/src/vm/io/input_line.c @@ -1,10 +1,6 @@ case OP_INPUT_LINE: { - /* operand bit flags: - * bit0 (1): has prompt (string or any value convertible to string) — top of stack holds prompt when set - * bit1 (2): hidden input (do not echo typed characters) - */ - int has_prompt = (inst.operand & 1) ? 1 : 0; - int hidden = (inst.operand & 2) ? 1 : 0; + /* operand: 0 = no prompt; 1 = has prompt (string or any value convertible to string) */ + int has_prompt = inst.operand ? 1 : 0; if (has_prompt) { /* pop prompt value and print without newline */ Value pv = pop_value(vm); @@ -17,35 +13,6 @@ case OP_INPUT_LINE: { free_value(pv); } - /* For hidden input, temporarily disable terminal echo if possible */ - int echo_disabled = 0; -#ifdef _WIN32 - if (hidden) { - HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); - if (hStdin != INVALID_HANDLE_VALUE) { - DWORD mode; - if (GetConsoleMode(hStdin, &mode)) { - DWORD newMode = mode & ~(ENABLE_ECHO_INPUT); - if (SetConsoleMode(hStdin, newMode)) { - echo_disabled = 1; - } - } - } - } -#else - if (hidden) { - /* POSIX termios */ - struct termios oldt; - if (tcgetattr(STDIN_FILENO, &oldt) == 0) { - struct termios newt = oldt; - newt.c_lflag &= ~(ECHO); - if (tcsetattr(STDIN_FILENO, TCSANOW, &newt) == 0) { - echo_disabled = 1; - } - } - } -#endif - /* read a line from stdin, dynamically grow buffer */ size_t cap = 128; size_t len = 0; @@ -53,8 +20,7 @@ case OP_INPUT_LINE: { if (!buf) { fprintf(stderr, "Runtime error: out of memory reading input"); push_value(vm, make_string("")); - /* On early exit, try to restore echo if we turned it off */ - goto restore_echo_and_break; + break; } int ch; @@ -102,36 +68,5 @@ case OP_INPUT_LINE: { free(buf); push_done: - /* If we disabled echo, restore terminal settings and print a newline for UX */ -#ifdef _WIN32 - if (echo_disabled) { - HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); - if (hStdin != INVALID_HANDLE_VALUE) { - DWORD mode; - if (GetConsoleMode(hStdin, &mode)) { - /* Re-enable ECHO flag */ - mode |= ENABLE_ECHO_INPUT; - SetConsoleMode(hStdin, mode); - } - } - if (has_prompt) { - fputc('\n', stdout); - fflush(stdout); - } - } -#else - if (echo_disabled) { - struct termios t; - if (tcgetattr(STDIN_FILENO, &t) == 0) { - t.c_lflag |= ECHO; - tcsetattr(STDIN_FILENO, TCSANOW, &t); - } - if (has_prompt) { - fputc('\n', stdout); - fflush(stdout); - } - } -#endif -restore_echo_and_break: break; } diff --git a/src/vm/math/ceil.c b/src/vm/math/ceil.c deleted file mode 100644 index bda8d29..0000000 --- a/src/vm/math/ceil.c +++ /dev/null @@ -1,43 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file ceil.c - * @brief Implements the OP_CEIL opcode using C99 math.h ceil(). - */ - -#include - -case OP_CEIL: { - Value v = pop_value(vm); - if (v.type == VAL_INT) { - /* ceil(n) == n for integers */ - push_value(vm, make_int(v.i)); - free_value(v); - } else if (v.type == VAL_FLOAT) { - double r = ceil(v.d); - if (r >= (double)INT64_MIN && r <= (double)INT64_MAX) { - int64_t ii = (int64_t)r; - if ((double)ii == r) { - push_value(vm, make_int(ii)); - } else { - push_value(vm, make_float(r)); - } - } else { - push_value(vm, make_float(r)); - } - free_value(v); - } else { - fprintf(stderr, "Runtime type error: CEIL expects number, got %s\n", value_type_name(v.type)); - exit(1); - } - break; -} diff --git a/src/vm/math/cos.c b/src/vm/math/cos.c deleted file mode 100644 index b5651f0..0000000 --- a/src/vm/math/cos.c +++ /dev/null @@ -1,31 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file cos.c - * @brief Implements the OP_COS opcode using C99 math.h cos(). - */ - -#include - -case OP_COS: { - Value v = pop_value(vm); - if (v.type == VAL_INT || v.type == VAL_FLOAT) { - double x = (v.type == VAL_FLOAT) ? v.d : (double)v.i; - double r = cos(x); - push_value(vm, make_float(r)); - free_value(v); - } else { - fprintf(stderr, "Runtime type error: COS expects number, got %s\n", value_type_name(v.type)); - exit(1); - } - break; -} diff --git a/src/vm/math/exp.c b/src/vm/math/exp.c deleted file mode 100644 index 8ad99c2..0000000 --- a/src/vm/math/exp.c +++ /dev/null @@ -1,31 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file exp.c - * @brief Implements the OP_EXP opcode using C99 math.h exp(). - */ - -#include - -case OP_EXP: { - Value v = pop_value(vm); - if (v.type == VAL_INT || v.type == VAL_FLOAT) { - double x = (v.type == VAL_FLOAT) ? v.d : (double)v.i; - double r = exp(x); - push_value(vm, make_float(r)); - free_value(v); - } else { - fprintf(stderr, "Runtime type error: EXP expects number, got %s\n", value_type_name(v.type)); - exit(1); - } - break; -} diff --git a/src/vm/math/floor.c b/src/vm/math/floor.c deleted file mode 100644 index 72e2440..0000000 --- a/src/vm/math/floor.c +++ /dev/null @@ -1,43 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file floor.c - * @brief Implements the OP_FLOOR opcode using C99 math.h floor(). - */ - -#include - -case OP_FLOOR: { - Value v = pop_value(vm); - if (v.type == VAL_INT) { - /* floor(n) == n for integers */ - push_value(vm, make_int(v.i)); - free_value(v); - } else if (v.type == VAL_FLOAT) { - double r = floor(v.d); - if (r >= (double)INT64_MIN && r <= (double)INT64_MAX) { - int64_t ii = (int64_t)r; - if ((double)ii == r) { - push_value(vm, make_int(ii)); - } else { - push_value(vm, make_float(r)); - } - } else { - push_value(vm, make_float(r)); - } - free_value(v); - } else { - fprintf(stderr, "Runtime type error: FLOOR expects number, got %s\n", value_type_name(v.type)); - exit(1); - } - break; -} diff --git a/src/vm/math/fmax.c b/src/vm/math/fmax.c deleted file mode 100644 index b8504ba..0000000 --- a/src/vm/math/fmax.c +++ /dev/null @@ -1,41 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file fmax.c - * @brief Implements the OP_FMAX opcode using C99 math.h fmax(). - * Accepts int or float; follows IEEE-754 NaN handling per fmax. - */ - -#include - -case OP_FMAX: { - Value b = pop_value(vm); - Value a = pop_value(vm); - if (!((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT))) { - fprintf(stderr, "Runtime type error: FMAX expects numbers, got %s and %s\n", - value_type_name(a.type), value_type_name(b.type)); - exit(1); - } - double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i; - double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i; - double r = fmax(da, db); - Value out; - if (!isnan(r) && !isinf(r) && r >= (double)INT64_MIN && r <= (double)INT64_MAX) { - int64_t ii = (int64_t)r; - if ((double)ii == r) out = make_int(ii); else out = make_float(r); - } else { - out = make_float(r); - } - free_value(a); free_value(b); - push_value(vm, out); - break; -} diff --git a/src/vm/math/fmin.c b/src/vm/math/fmin.c deleted file mode 100644 index ded0719..0000000 --- a/src/vm/math/fmin.c +++ /dev/null @@ -1,41 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file fmin.c - * @brief Implements the OP_FMIN opcode using C99 math.h fmin(). - * Accepts int or float; follows IEEE-754 NaN handling per fmin. - */ - -#include - -case OP_FMIN: { - Value b = pop_value(vm); - Value a = pop_value(vm); - if (!((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT))) { - fprintf(stderr, "Runtime type error: FMIN expects numbers, got %s and %s\n", - value_type_name(a.type), value_type_name(b.type)); - exit(1); - } - double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i; - double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i; - double r = fmin(da, db); - Value out; - if (!isnan(r) && !isinf(r) && r >= (double)INT64_MIN && r <= (double)INT64_MAX) { - int64_t ii = (int64_t)r; - if ((double)ii == r) out = make_int(ii); else out = make_float(r); - } else { - out = make_float(r); - } - free_value(a); free_value(b); - push_value(vm, out); - break; -} diff --git a/src/vm/math/gcd.c b/src/vm/math/gcd.c deleted file mode 100644 index 106a5e4..0000000 --- a/src/vm/math/gcd.c +++ /dev/null @@ -1,40 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file gcd.c - * @brief Implements the OP_GCD opcode for greatest common divisor. - */ - -case OP_GCD: { - Value vb = pop_value(vm); - Value va = pop_value(vm); - if (!((va.type == VAL_INT) || (va.type == VAL_FLOAT)) || - !((vb.type == VAL_INT) || (vb.type == VAL_FLOAT))) { - fprintf(stderr, "Runtime type error: GCD expects numbers, got %s and %s\n", - value_type_name(va.type), value_type_name(vb.type)); - exit(1); - } - int64_t a = (va.type == VAL_INT) ? va.i : (int64_t)va.d; - int64_t b = (vb.type == VAL_INT) ? vb.i : (int64_t)vb.d; - if (a == INT64_MIN) a = (int64_t)INT64_MAX; else if (a < 0) a = -a; - if (b == INT64_MIN) b = (int64_t)INT64_MAX; else if (b < 0) b = -b; - while (b != 0) { - int64_t t = a % b; - a = b; - b = t; - } - Value res = make_int(a); - free_value(va); - free_value(vb); - push_value(vm, res); - break; -} diff --git a/src/vm/math/isqrt.c b/src/vm/math/isqrt.c deleted file mode 100644 index 6c60f29..0000000 --- a/src/vm/math/isqrt.c +++ /dev/null @@ -1,47 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file isqrt.c - * @brief Implements the OP_ISQRT opcode for integer square root (floor). - */ - -case OP_ISQRT: { - Value v = pop_value(vm); - if (!((v.type == VAL_INT) || (v.type == VAL_FLOAT))) { - fprintf(stderr, "Runtime type error: ISQRT expects number, got %s\n", value_type_name(v.type)); - exit(1); - } - int64_t a = (v.type == VAL_INT) ? v.i : (int64_t)v.d; - if (a <= 0) { - free_value(v); - push_value(vm, make_int(0)); - break; - } - /* Binary isqrt inline */ - uint64_t n = (uint64_t)a; - uint64_t x = 0; - uint64_t bit = (uint64_t)1 << 62; /* highest even bit set */ - while (bit > n) bit >>= 2; - while (bit != 0) { - if (n >= x + bit) { - n -= x + bit; - x = (x >> 1) + bit; - } else { - x >>= 1; - } - bit >>= 2; - } - int64_t r = (int64_t)x; - free_value(v); - push_value(vm, make_int(r)); - break; -} diff --git a/src/vm/math/lcm.c b/src/vm/math/lcm.c deleted file mode 100644 index b477c3e..0000000 --- a/src/vm/math/lcm.c +++ /dev/null @@ -1,48 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file lcm.c - * @brief Implements the OP_LCM opcode for least common multiple. - */ - -case OP_LCM: { - Value vb = pop_value(vm); - Value va = pop_value(vm); - if (!((va.type == VAL_INT) || (va.type == VAL_FLOAT)) || - !((vb.type == VAL_INT) || (vb.type == VAL_FLOAT))) { - fprintf(stderr, "Runtime type error: LCM expects numbers, got %s and %s\n", - value_type_name(va.type), value_type_name(vb.type)); - exit(1); - } - int64_t a = (va.type == VAL_INT) ? va.i : (int64_t)va.d; - int64_t b = (vb.type == VAL_INT) ? vb.i : (int64_t)vb.d; - if (a == INT64_MIN) a = (int64_t)INT64_MAX; else if (a < 0) a = -a; - if (b == INT64_MIN) b = (int64_t)INT64_MAX; else if (b < 0) b = -b; - if (a == 0 || b == 0) { - free_value(va); free_value(vb); - push_value(vm, make_int(0)); - break; - } - /* gcd(a,b) */ - int64_t x = a, y = b; - while (y != 0) { - int64_t t = x % y; x = y; y = t; - } - int64_t g = x; - /* lcm = (a/g)*b (attempt to reduce overflow) */ - int64_t l = (a / g) * b; - Value res = make_int(l); - free_value(va); - free_value(vb); - push_value(vm, res); - break; -} diff --git a/src/vm/math/log.c b/src/vm/math/log.c deleted file mode 100644 index e8bd665..0000000 --- a/src/vm/math/log.c +++ /dev/null @@ -1,36 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file log.c - * @brief Implements the OP_LOG opcode using C99 math.h log() (natural logarithm). - */ - -#include - -case OP_LOG: { - Value v = pop_value(vm); - if (v.type == VAL_INT || v.type == VAL_FLOAT) { - double x = (v.type == VAL_FLOAT) ? v.d : (double)v.i; - if (x <= 0.0) { - /* Domain error: return NaN to signal invalid input */ - push_value(vm, make_float(NAN)); - } else { - double r = log(x); - push_value(vm, make_float(r)); - } - free_value(v); - } else { - fprintf(stderr, "Runtime type error: LOG expects number, got %s\n", value_type_name(v.type)); - exit(1); - } - break; -} diff --git a/src/vm/math/log10.c b/src/vm/math/log10.c deleted file mode 100644 index 0da07e1..0000000 --- a/src/vm/math/log10.c +++ /dev/null @@ -1,35 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file log10.c - * @brief Implements the OP_LOG10 opcode using C99 math.h log10(). - */ - -#include - -case OP_LOG10: { - Value v = pop_value(vm); - if (v.type == VAL_INT || v.type == VAL_FLOAT) { - double x = (v.type == VAL_FLOAT) ? v.d : (double)v.i; - if (x <= 0.0) { - push_value(vm, make_float(NAN)); - } else { - double r = log10(x); - push_value(vm, make_float(r)); - } - free_value(v); - } else { - fprintf(stderr, "Runtime type error: LOG10 expects number, got %s\n", value_type_name(v.type)); - exit(1); - } - break; -} diff --git a/src/vm/math/round.c b/src/vm/math/round.c deleted file mode 100644 index 3f830ff..0000000 --- a/src/vm/math/round.c +++ /dev/null @@ -1,43 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file round.c - * @brief Implements the OP_ROUND opcode using C99 math.h round(). - * C99 round() rounds half away from zero. - */ - -#include - -case OP_ROUND: { - Value v = pop_value(vm); - if (v.type == VAL_INT) { - push_value(vm, make_int(v.i)); - free_value(v); - } else if (v.type == VAL_FLOAT) { - double r = round(v.d); - if (r >= (double)INT64_MIN && r <= (double)INT64_MAX) { - int64_t ii = (int64_t)r; - if ((double)ii == r) { - push_value(vm, make_int(ii)); - } else { - push_value(vm, make_float(r)); - } - } else { - push_value(vm, make_float(r)); - } - free_value(v); - } else { - fprintf(stderr, "Runtime type error: ROUND expects number, got %s\n", value_type_name(v.type)); - exit(1); - } - break; -} diff --git a/src/vm/math/sign.c b/src/vm/math/sign.c deleted file mode 100644 index e54a787..0000000 --- a/src/vm/math/sign.c +++ /dev/null @@ -1,33 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file sign.c - * @brief Implements the OP_SIGN opcode returning -1, 0, or 1. - */ - -case OP_SIGN: { - Value v = pop_value(vm); - int out = 0; - if (v.type == VAL_INT) { - out = (v.i > 0) - (v.i < 0); - } else if (v.type == VAL_FLOAT) { - if (v.d > 0.0) out = 1; - else if (v.d < 0.0) out = -1; - else out = 0; - } else { - fprintf(stderr, "Runtime type error: SIGN expects number, got %s\n", value_type_name(v.type)); - exit(1); - } - free_value(v); - push_value(vm, make_int(out)); - break; -} diff --git a/src/vm/math/sin.c b/src/vm/math/sin.c deleted file mode 100644 index aa989b2..0000000 --- a/src/vm/math/sin.c +++ /dev/null @@ -1,31 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file sin.c - * @brief Implements the OP_SIN opcode using C99 math.h sin(). - */ - -#include - -case OP_SIN: { - Value v = pop_value(vm); - if (v.type == VAL_INT || v.type == VAL_FLOAT) { - double x = (v.type == VAL_FLOAT) ? v.d : (double)v.i; - double r = sin(x); - push_value(vm, make_float(r)); - free_value(v); - } else { - fprintf(stderr, "Runtime type error: SIN expects number, got %s\n", value_type_name(v.type)); - exit(1); - } - break; -} diff --git a/src/vm/math/sqrt.c b/src/vm/math/sqrt.c deleted file mode 100644 index aaffee2..0000000 --- a/src/vm/math/sqrt.c +++ /dev/null @@ -1,45 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file sqrt.c - * @brief Implements the OP_SQRT opcode using C99 math.h sqrt(). - */ - -#include - -case OP_SQRT: { - Value v = pop_value(vm); - if (v.type == VAL_INT || v.type == VAL_FLOAT) { - double x = (v.type == VAL_FLOAT) ? v.d : (double)v.i; - if (x < 0.0) { - push_value(vm, make_float(NAN)); - } else { - double r = sqrt(x); - /* preserve int if exactly integral */ - if (r >= (double)INT64_MIN && r <= (double)INT64_MAX) { - int64_t ii = (int64_t)r; - if ((double)ii == r) { - push_value(vm, make_int(ii)); - } else { - push_value(vm, make_float(r)); - } - } else { - push_value(vm, make_float(r)); - } - } - free_value(v); - } else { - fprintf(stderr, "Runtime type error: SQRT expects number, got %s\n", value_type_name(v.type)); - exit(1); - } - break; -} diff --git a/src/vm/math/tan.c b/src/vm/math/tan.c deleted file mode 100644 index dd7ede3..0000000 --- a/src/vm/math/tan.c +++ /dev/null @@ -1,31 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file tan.c - * @brief Implements the OP_TAN opcode using C99 math.h tan(). - */ - -#include - -case OP_TAN: { - Value v = pop_value(vm); - if (v.type == VAL_INT || v.type == VAL_FLOAT) { - double x = (v.type == VAL_FLOAT) ? v.d : (double)v.i; - double r = tan(x); - push_value(vm, make_float(r)); - free_value(v); - } else { - fprintf(stderr, "Runtime type error: TAN expects number, got %s\n", value_type_name(v.type)); - exit(1); - } - break; -} diff --git a/src/vm/math/trunc.c b/src/vm/math/trunc.c deleted file mode 100644 index 7442074..0000000 --- a/src/vm/math/trunc.c +++ /dev/null @@ -1,42 +0,0 @@ -/** - * This file is part of the Fun programming language. - * https://fun-lang.xyz/ - * - * Copyright 2026 Johannes Findeisen - * Licensed under the terms of the Apache-2.0 license. - * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 - */ - -/** -* @file trunc.c - * @brief Implements the OP_TRUNC opcode using C99 math.h trunc(). - */ - -#include - -case OP_TRUNC: { - Value v = pop_value(vm); - if (v.type == VAL_INT) { - push_value(vm, make_int(v.i)); - free_value(v); - } else if (v.type == VAL_FLOAT) { - double r = trunc(v.d); - if (r >= (double)INT64_MIN && r <= (double)INT64_MAX) { - int64_t ii = (int64_t)r; - if ((double)ii == r) { - push_value(vm, make_int(ii)); - } else { - push_value(vm, make_float(r)); - } - } else { - push_value(vm, make_float(r)); - } - free_value(v); - } else { - fprintf(stderr, "Runtime type error: TRUNC expects number, got %s\n", value_type_name(v.type)); - exit(1); - } - break; -}