Compare commits
No commits in common. "4a6903e58aeacfb66fcb7879ac8176076c60d954" and "83281bf2de4adcea16b8281857fbfaf02dbab2bb" have entirely different histories.
4a6903e58a
...
83281bf2de
68 changed files with 216 additions and 2731 deletions
252
CMakeLists.txt
252
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 $<TARGET_FILE:fun>
|
||||
DEPENDS fun
|
||||
USES_TERMINAL
|
||||
COMMENT "Run Fun REPL"
|
||||
)
|
||||
COMMAND $<TARGET_FILE:fun>
|
||||
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=$<IF:$<BOOL:${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=$<IF:$<BOOL:${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)
|
||||
|
|
|
|||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 <cli.fun>
|
||||
|
||||
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
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 <cli.fun>
|
||||
|
||||
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]
|
||||
*/
|
||||
|
|
@ -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"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,7 @@ print("<h1>Fun Environment Information</h1>")
|
|||
print("<p><strong>Current Path:</strong> " + env("PATH") + "</p>")
|
||||
print("<p><strong>Current User:</strong> " + env("USER") + "</p>")
|
||||
print("<p><strong>Time:</strong> " + date_format(time_now_ms(), "%Y-%m-%d %H:%M:%S") + "</p>")
|
||||
print("<p><strong>Local Version:</strong> " + to_string(fun_version()) + "</p>")
|
||||
res = proc_run("fun -V")
|
||||
installed_version = res["out"]
|
||||
print("<p><strong>Installed Version:</strong> " + to_string(substr(installed_version, 4, len(installed_version)-2)) + "</p>")
|
||||
print("<p><strong>Version:</strong> " + to_string(fun_version()) + "</p>")
|
||||
print("<h2>Full Environment:</h2>")
|
||||
print("<ul>")
|
||||
all_env = env_all()
|
||||
|
|
|
|||
0
examples/env_all.fun
Executable file → Normal file
0
examples/env_all.fun
Executable file → Normal file
|
|
@ -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 <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2025-12-28
|
||||
*/
|
||||
|
||||
#include <io/socket.fun>
|
||||
|
||||
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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -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=<EFBFBD><EFBFBD><EFBFBD><EFBFBD>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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 <you@hanez.org>
|
||||
* 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", "<def>")
|
||||
print("app:name => " + v1)
|
||||
|
||||
print("[try app:version]")
|
||||
v2 = ini_get_string(h, "app", "version", "<def>")
|
||||
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
|
||||
*/
|
||||
|
|
@ -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 <io/console.fun>
|
||||
|
||||
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!
|
||||
*/
|
||||
|
|
@ -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 <io/console.fun>
|
||||
|
||||
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!
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 ===
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 ===
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 ===
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 ===
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 ===
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 ===
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 ===
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 ===
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 ===
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 ===
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 ===
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 ===
|
||||
*/
|
||||
|
|
@ -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 <you@hanez.org>
|
||||
* 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 ===
|
||||
*/
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 <crypt/ripemd160.fun>
|
||||
|
||||
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"))
|
||||
66
lib/cli.fun
66
lib/cli.fun
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* 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 }
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 <strings.fun>
|
||||
|
||||
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)
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
3
make
3
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";
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 "???";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
51
src/fun.c
51
src/fun.c
|
|
@ -1,13 +1,4 @@
|
|||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* 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);
|
||||
|
|
|
|||
170
src/fun_test.c
170
src/fun_test.c
|
|
@ -11,7 +11,6 @@
|
|||
#include "value.h"
|
||||
#include "vm.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#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);
|
||||
|
||||
|
|
|
|||
137
src/parser.c
137
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; }
|
||||
|
|
|
|||
30
src/vm.c
30
src/vm.c
|
|
@ -29,15 +29,9 @@
|
|||
#include <netdb.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
/* For hidden input (password) handling in OP_INPUT_LINE */
|
||||
#include <termios.h>
|
||||
//#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#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 */
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* 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(<iniparser/iniparser.h>)
|
||||
# include <iniparser/iniparser.h>
|
||||
# include <iniparser/dictionary.h>
|
||||
# elif __has_include(<iniparser.h>)
|
||||
# include <iniparser.h>
|
||||
# include <dictionary.h>
|
||||
# else
|
||||
# error "iniparser headers not found"
|
||||
# endif
|
||||
#else
|
||||
# include <iniparser/iniparser.h>
|
||||
# include <iniparser/dictionary.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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 */
|
||||
|
|
@ -27,18 +27,35 @@
|
|||
# include <iniparser/iniparser.h>
|
||||
# include <iniparser/dictionary.h>
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
#include <stdio.h> /* 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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* 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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue