Compare commits
15 commits
83281bf2de
...
4a6903e58a
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a6903e58a | |||
| c062b685e1 | |||
| 194c06e4aa | |||
| 2a20b6f01a | |||
| d9372ce874 | |||
| 0fe3e8e357 | |||
| 8b04622e55 | |||
| 1f6bc2b96a | |||
| b32f7e839b | |||
| 4997ad1e4b | |||
| dc9aac38da | |||
| 9cd76937a3 | |||
| 6124d1e7fd | |||
| e144b5119d | |||
| e6b9bd7488 |
68 changed files with 2731 additions and 216 deletions
252
CMakeLists.txt
252
CMakeLists.txt
|
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(fun VERSION 0.37.32 LANGUAGES C)
|
project(fun VERSION 0.37.44 LANGUAGES C)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
|
@ -20,7 +20,7 @@ if(NOT DEFINED DEFAULT_LIB_DIR OR DEFAULT_LIB_DIR STREQUAL "")
|
||||||
else()
|
else()
|
||||||
set(_FUN_DEFAULT_LIB_DIR "/usr/share/fun/lib")
|
set(_FUN_DEFAULT_LIB_DIR "/usr/share/fun/lib")
|
||||||
endif()
|
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()
|
else()
|
||||||
set(DEFAULT_LIB_DIR "${DEFAULT_LIB_DIR}" CACHE PATH "Default library directory for Fun stdlib (override with -DDEFAULT_LIB_DIR=...)" FORCE)
|
set(DEFAULT_LIB_DIR "${DEFAULT_LIB_DIR}" CACHE PATH "Default library directory for Fun stdlib (override with -DDEFAULT_LIB_DIR=...)" FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -30,27 +30,27 @@ endif()
|
||||||
option(FUN_USE_MUSL "Use musl libc toolchain when available (Linux only)" OFF)
|
option(FUN_USE_MUSL "Use musl libc toolchain when available (Linux only)" OFF)
|
||||||
|
|
||||||
if(FUN_USE_MUSL AND UNIX AND NOT APPLE)
|
if(FUN_USE_MUSL AND UNIX AND NOT APPLE)
|
||||||
# Try to find a musl toolchain wrapper
|
# Try to find a musl toolchain wrapper
|
||||||
find_program(_FUN_MUSL_CC NAMES musl-gcc musl-clang)
|
find_program(_FUN_MUSL_CC NAMES musl-gcc musl-clang)
|
||||||
if(_FUN_MUSL_CC)
|
if(_FUN_MUSL_CC)
|
||||||
message(STATUS "FUN_USE_MUSL=ON: using musl toolchain: ${_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
|
# 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(CMAKE_C_COMPILER "${_FUN_MUSL_CC}" CACHE FILEPATH "C compiler" FORCE)
|
||||||
set(FUN_LIBC "musl" CACHE STRING "Selected C library")
|
set(FUN_LIBC "musl" CACHE STRING "Selected C library")
|
||||||
else()
|
else()
|
||||||
message(WARNING "FUN_USE_MUSL=ON but no musl toolchain (musl-gcc or musl-clang) found. Falling back to default compiler (likely glibc).")
|
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")
|
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")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Expose a preprocessor macro indicating selected C library
|
# Expose a preprocessor macro indicating selected C library
|
||||||
if(FUN_LIBC STREQUAL "musl")
|
if(FUN_LIBC STREQUAL "musl")
|
||||||
add_definitions(-DFUN_LIBC_MUSL)
|
add_definitions(-DFUN_LIBC_MUSL)
|
||||||
else()
|
else()
|
||||||
add_definitions(-DFUN_LIBC_GLIBC)
|
add_definitions(-DFUN_LIBC_GLIBC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Optional: build statically linked executables
|
# Optional: build statically linked executables
|
||||||
|
|
@ -83,7 +83,7 @@ if(FUN_LINK_STATIC)
|
||||||
# On MSVC, prefer the static runtime
|
# On MSVC, prefer the static runtime
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
foreach(flag_var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
|
foreach(flag_var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
|
||||||
CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_MINSIZEREL)
|
CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_MINSIZEREL)
|
||||||
if(DEFINED ${flag_var})
|
if(DEFINED ${flag_var})
|
||||||
string(REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
|
string(REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -151,7 +151,7 @@ if(FUN_WITH_TCLTK)
|
||||||
endif()
|
endif()
|
||||||
# Ensure trailing slash
|
# Ensure trailing slash
|
||||||
if(NOT DEFAULT_LIB_DIR MATCHES "/$")
|
if(NOT DEFAULT_LIB_DIR MATCHES "/$")
|
||||||
set(DEFAULT_LIB_DIR "${DEFAULT_LIB_DIR}/")
|
set(DEFAULT_LIB_DIR "${DEFAULT_LIB_DIR}/")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Optional SQLite support
|
# Optional SQLite support
|
||||||
|
|
@ -184,14 +184,14 @@ option(FUN_WITH_PCSC "Enable PCSC (pcsclite) support" OFF)
|
||||||
set(PCSC_LINK_LIBS "")
|
set(PCSC_LINK_LIBS "")
|
||||||
set(PCSC_INCLUDE_DIRS "")
|
set(PCSC_INCLUDE_DIRS "")
|
||||||
if(FUN_WITH_PCSC)
|
if(FUN_WITH_PCSC)
|
||||||
message(STATUS "Building with PCSC support")
|
message(STATUS "Building with PCSC support")
|
||||||
add_definitions(-DFUN_WITH_PCSC)
|
add_definitions(-DFUN_WITH_PCSC)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
list(APPEND PCSC_LINK_LIBS "-framework PCSC")
|
list(APPEND PCSC_LINK_LIBS "-framework PCSC")
|
||||||
else()
|
else()
|
||||||
list(APPEND PCSC_INCLUDE_DIRS "/usr/include/PCSC")
|
list(APPEND PCSC_INCLUDE_DIRS "/usr/include/PCSC")
|
||||||
list(APPEND PCSC_LINK_LIBS pcsclite)
|
list(APPEND PCSC_LINK_LIBS pcsclite)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Optional JSON (json-c) support
|
# Optional JSON (json-c) support
|
||||||
|
|
@ -269,9 +269,9 @@ if(FUN_WITH_XML2)
|
||||||
list(APPEND LIBXML2_INCLUDE_DIRS "/usr/include/libxml2")
|
list(APPEND LIBXML2_INCLUDE_DIRS "/usr/include/libxml2")
|
||||||
include_directories(${LIBXML2_INCLUDE_DIRS})
|
include_directories(${LIBXML2_INCLUDE_DIRS})
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "libxml2 not found. Install libxml2 (dev headers) or disable FUN_WITH_XML2.")
|
message(FATAL_ERROR "libxml2 not found. Install libxml2 (dev headers) or disable FUN_WITH_XML2.")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
# As a robust fallback, add standard system include path for libxml2 if present
|
# As a robust fallback, add standard system include path for libxml2 if present
|
||||||
if(EXISTS "/usr/include/libxml2")
|
if(EXISTS "/usr/include/libxml2")
|
||||||
|
|
@ -365,21 +365,21 @@ endif()
|
||||||
|
|
||||||
# Core VM/library sources
|
# Core VM/library sources
|
||||||
add_library(fun_core
|
add_library(fun_core
|
||||||
src/bytecode.c
|
src/bytecode.c
|
||||||
src/parser.c
|
src/parser.c
|
||||||
src/value.c
|
src/value.c
|
||||||
src/vm.c
|
src/vm.c
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(fun_core PUBLIC
|
target_include_directories(fun_core PUBLIC
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
)
|
)
|
||||||
|
|
||||||
# Apply options to core
|
# Apply options to core
|
||||||
if(FUN_DEBUG)
|
if(FUN_DEBUG)
|
||||||
message(STATUS "FUN_DEBUG enabled: building with verbose debug logging")
|
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_VERSION="${PROJECT_VERSION}")
|
||||||
target_compile_definitions(fun_core PUBLIC FUN_DEBUG=1)
|
target_compile_definitions(fun_core PUBLIC FUN_DEBUG=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Provide default stdlib directory to the runtime
|
# Provide default stdlib directory to the runtime
|
||||||
|
|
@ -468,10 +468,16 @@ if(Threads_FOUND)
|
||||||
target_link_libraries(fun_core PUBLIC Threads::Threads)
|
target_link_libraries(fun_core PUBLIC Threads::Threads)
|
||||||
endif()
|
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
|
# Interpreter /usr/bin/fun
|
||||||
option(FUN_WITH_REPL "Enable interactive REPL in the fun CLI" OFF)
|
option(FUN_WITH_REPL "Enable interactive REPL in the fun CLI" OFF)
|
||||||
add_executable(fun
|
add_executable(fun
|
||||||
src/fun.c
|
src/fun.c
|
||||||
)
|
)
|
||||||
# Provide version string to the CLI
|
# Provide version string to the CLI
|
||||||
if(FUN_WITH_REPL)
|
if(FUN_WITH_REPL)
|
||||||
|
|
@ -483,12 +489,12 @@ target_link_libraries(fun PRIVATE fun_core)
|
||||||
|
|
||||||
# Internal test programs
|
# Internal test programs
|
||||||
add_executable(fun_test
|
add_executable(fun_test
|
||||||
src/fun_test.c
|
src/fun_test.c
|
||||||
)
|
)
|
||||||
target_link_libraries(fun_test PRIVATE fun_core)
|
target_link_libraries(fun_test PRIVATE fun_core)
|
||||||
|
|
||||||
add_executable(test_opcodes
|
add_executable(test_opcodes
|
||||||
src/test_opcodes.c
|
src/test_opcodes.c
|
||||||
)
|
)
|
||||||
target_link_libraries(test_opcodes PRIVATE fun_core)
|
target_link_libraries(test_opcodes PRIVATE fun_core)
|
||||||
|
|
||||||
|
|
@ -511,11 +517,11 @@ set(FUN_RUN_SCRIPT "" CACHE STRING "Script to run with the 'run' target, e.g. -D
|
||||||
|
|
||||||
if(FUN_WITH_REPL)
|
if(FUN_WITH_REPL)
|
||||||
add_custom_target(repl
|
add_custom_target(repl
|
||||||
COMMAND $<TARGET_FILE:fun>
|
COMMAND $<TARGET_FILE:fun>
|
||||||
DEPENDS fun
|
DEPENDS fun
|
||||||
USES_TERMINAL
|
USES_TERMINAL
|
||||||
COMMENT "Run Fun REPL"
|
COMMENT "Run Fun REPL"
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_custom_target(run
|
add_custom_target(run
|
||||||
|
|
@ -555,14 +561,14 @@ add_custom_target(ops-quiet
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_target(examples
|
add_custom_target(examples
|
||||||
COMMAND ${CMAKE_COMMAND} -E env FUN_LIB_DIR="${FUN_LIB}" ${CMAKE_SOURCE_DIR}/scripts/run_examples.sh
|
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
|
||||||
DEPENDS fun
|
DEPENDS fun
|
||||||
USES_TERMINAL
|
USES_TERMINAL
|
||||||
COMMENT "Build and run all examples"
|
COMMENT "Build and run all examples"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_target(run-examples
|
add_custom_target(run-examples
|
||||||
COMMAND ${CMAKE_COMMAND} -E env FUN_LIB_DIR="${FUN_LIB}" ${CMAKE_SOURCE_DIR}/scripts/run_examples.sh
|
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
|
||||||
DEPENDS fun
|
DEPENDS fun
|
||||||
USES_TERMINAL
|
USES_TERMINAL
|
||||||
COMMENT "Build and run all examples"
|
COMMENT "Build and run all examples"
|
||||||
|
|
@ -570,20 +576,20 @@ add_custom_target(run-examples
|
||||||
|
|
||||||
# Clean and distclean
|
# Clean and distclean
|
||||||
add_custom_target(fun_clean
|
add_custom_target(fun_clean
|
||||||
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target clean
|
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target clean
|
||||||
COMMENT "Clean build outputs (objects, binaries) in the build directory"
|
COMMENT "Clean build outputs (objects, binaries) in the build directory"
|
||||||
)
|
)
|
||||||
add_custom_target(distclean
|
add_custom_target(distclean
|
||||||
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target clean
|
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target clean
|
||||||
COMMAND ${CMAKE_COMMAND} -E rm -rf
|
COMMAND ${CMAKE_COMMAND} -E rm -rf
|
||||||
${CMAKE_BINARY_DIR}/CMakeCache.txt
|
${CMAKE_BINARY_DIR}/CMakeCache.txt
|
||||||
${CMAKE_BINARY_DIR}/CMakeFiles
|
${CMAKE_BINARY_DIR}/CMakeFiles
|
||||||
${CMAKE_BINARY_DIR}/cmake_install.cmake
|
${CMAKE_BINARY_DIR}/cmake_install.cmake
|
||||||
${CMAKE_BINARY_DIR}/install_manifest.txt
|
${CMAKE_BINARY_DIR}/install_manifest.txt
|
||||||
${CMAKE_BINARY_DIR}/Makefile
|
${CMAKE_BINARY_DIR}/Makefile
|
||||||
${CMAKE_BINARY_DIR}/*.ninja
|
${CMAKE_BINARY_DIR}/*.ninja
|
||||||
${CMAKE_BINARY_DIR}/.ninja_*
|
${CMAKE_BINARY_DIR}/.ninja_*
|
||||||
COMMENT "Remove build outputs and CMake-generated files (reconfigure needed)"
|
COMMENT "Remove build outputs and CMake-generated files (reconfigure needed)"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Robust uninstall support as a function (uses install_manifest*.txt)
|
# Robust uninstall support as a function (uses install_manifest*.txt)
|
||||||
|
|
@ -615,14 +621,14 @@ list(APPEND _candidates
|
||||||
set(_manifest_file \"\")
|
set(_manifest_file \"\")
|
||||||
foreach(_cand IN LISTS _candidates)
|
foreach(_cand IN LISTS _candidates)
|
||||||
if(EXISTS \"\${_cand}\")
|
if(EXISTS \"\${_cand}\")
|
||||||
set(_manifest_file \"\${_cand}\")
|
set(_manifest_file \"\${_cand}\")
|
||||||
break()
|
break()
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
if(NOT _manifest_file)
|
if(NOT _manifest_file)
|
||||||
message(FATAL_ERROR
|
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).\")
|
If you installed from a different build directory, run uninstall from that build directory (where install_manifest*.txt resides).\")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
@ -632,21 +638,21 @@ set(_removed 0)
|
||||||
set(_dirs \"\")
|
set(_dirs \"\")
|
||||||
foreach(_file IN LISTS _files)
|
foreach(_file IN LISTS _files)
|
||||||
if(_file STREQUAL \"\")
|
if(_file STREQUAL \"\")
|
||||||
continue()
|
continue()
|
||||||
endif()
|
endif()
|
||||||
if(EXISTS \"\${_file}\" OR IS_SYMLINK \"\${_file}\")
|
if(EXISTS \"\${_file}\" OR IS_SYMLINK \"\${_file}\")
|
||||||
message(STATUS \"Uninstalling: \${_file}\")
|
message(STATUS \"Uninstalling: \${_file}\")
|
||||||
file(REMOVE \"\${_file}\")
|
file(REMOVE \"\${_file}\")
|
||||||
# If the file still exists after attempting to remove it, fail with an explicit error
|
# If the file still exists after attempting to remove it, fail with an explicit error
|
||||||
if(EXISTS \"\${_file}\" OR IS_SYMLINK \"\${_file}\")
|
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.\")
|
message(FATAL_ERROR \"Failed to remove: \${_file}. Try running the uninstall target with sudo if it is a system install.\")
|
||||||
endif()
|
endif()
|
||||||
# Track the containing directory for potential cleanup
|
# Track the containing directory for potential cleanup
|
||||||
get_filename_component(_dir \"\${_file}\" DIRECTORY)
|
get_filename_component(_dir \"\${_file}\" DIRECTORY)
|
||||||
list(APPEND _dirs \"\${_dir}\")
|
list(APPEND _dirs \"\${_dir}\")
|
||||||
math(EXPR _removed \"\${_removed}+1\")
|
math(EXPR _removed \"\${_removed}+1\")
|
||||||
else()
|
else()
|
||||||
message(STATUS \"Skipping (not found): \${_file}\")
|
message(STATUS \"Skipping (not found): \${_file}\")
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
|
@ -659,14 +665,14 @@ set(_all_dirs \"\")
|
||||||
foreach(_d IN LISTS _dirs)
|
foreach(_d IN LISTS _dirs)
|
||||||
set(_p \"\${_d}\")
|
set(_p \"\${_d}\")
|
||||||
while(NOT \"\${_p}\" STREQUAL \"\" AND NOT \"\${_p}\" STREQUAL \"/\")
|
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}\")
|
list(APPEND _all_dirs \"\${_p}\")
|
||||||
get_filename_component(_p \"\${_p}\" DIRECTORY)
|
break()
|
||||||
# Stop collecting parents once we reach a safe root or the filesystem root
|
endif()
|
||||||
list(FIND _safe_roots \"\${_p}\" _root_idx)
|
|
||||||
if(_root_idx GREATER -1)
|
|
||||||
list(APPEND _all_dirs \"\${_p}\")
|
|
||||||
break()
|
|
||||||
endif()
|
|
||||||
endwhile()
|
endwhile()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
|
@ -679,38 +685,38 @@ set(_pass 0)
|
||||||
while(TRUE)
|
while(TRUE)
|
||||||
set(_pass_removed 0)
|
set(_pass_removed 0)
|
||||||
foreach(_dir IN LISTS _all_dirs)
|
foreach(_dir IN LISTS _all_dirs)
|
||||||
if(EXISTS \"\${_dir}\" AND IS_DIRECTORY \"\${_dir}\")
|
if(EXISTS \"\${_dir}\" AND IS_DIRECTORY \"\${_dir}\")
|
||||||
# Only act on directories within safe roots
|
# Only act on directories within safe roots
|
||||||
set(_allowed FALSE)
|
set(_allowed FALSE)
|
||||||
foreach(_root IN LISTS _safe_roots)
|
foreach(_root IN LISTS _safe_roots)
|
||||||
if(\"\${_dir}\" MATCHES \"^\${_root}(/|\$)\")
|
if(\"\${_dir}\" MATCHES \"^\${_root}(/|\$)\")
|
||||||
set(_allowed TRUE)
|
set(_allowed TRUE)
|
||||||
break()
|
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.\")
|
|
||||||
endif()
|
|
||||||
math(EXPR _removed_dirs \"\${_removed_dirs}+1\")
|
|
||||||
math(EXPR _pass_removed \"\${_pass_removed}+1\")
|
|
||||||
endif()
|
|
||||||
endif()
|
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.\")
|
||||||
|
endif()
|
||||||
|
math(EXPR _removed_dirs \"\${_removed_dirs}+1\")
|
||||||
|
math(EXPR _pass_removed \"\${_pass_removed}+1\")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
math(EXPR _pass \"\${_pass}+1\")
|
math(EXPR _pass \"\${_pass}+1\")
|
||||||
if(_pass_removed EQUAL 0 OR _pass GREATER 5)
|
if(_pass_removed EQUAL 0 OR _pass GREATER 5)
|
||||||
break()
|
break()
|
||||||
endif()
|
endif()
|
||||||
endwhile()
|
endwhile()
|
||||||
|
|
||||||
|
|
@ -720,13 +726,13 @@ message(STATUS \"Uninstall finished. Removed \${_removed} files and \${_removed_
|
||||||
# Expose an 'uninstall' build target (run with sudo if system locations were used)
|
# Expose an 'uninstall' build target (run with sudo if system locations were used)
|
||||||
add_custom_target(uninstall
|
add_custom_target(uninstall
|
||||||
COMMAND ${CMAKE_COMMAND}
|
COMMAND ${CMAKE_COMMAND}
|
||||||
-D MANIFEST="${CMAKE_BINARY_DIR}/install_manifest.txt"
|
-D MANIFEST="${CMAKE_BINARY_DIR}/install_manifest.txt"
|
||||||
-D CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}"
|
-D CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}"
|
||||||
-D CMAKE_SOURCE_DIR="${CMAKE_SOURCE_DIR}"
|
-D CMAKE_SOURCE_DIR="${CMAKE_SOURCE_DIR}"
|
||||||
-P "${_FUN_UNINSTALL_SCRIPT}"
|
-P "${_FUN_UNINSTALL_SCRIPT}"
|
||||||
USES_TERMINAL
|
USES_TERMINAL
|
||||||
COMMENT "Uninstall files installed by this project (use sudo if needed)"
|
COMMENT "Uninstall files installed by this project (use sudo if needed)"
|
||||||
)
|
)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# Define the uninstall target
|
# Define the uninstall target
|
||||||
|
|
@ -735,21 +741,25 @@ fun_add_uninstall_target()
|
||||||
# Install rules
|
# Install rules
|
||||||
# Binary
|
# Binary
|
||||||
install(TARGETS fun
|
install(TARGETS fun
|
||||||
RUNTIME DESTINATION /usr/bin)
|
RUNTIME DESTINATION /usr/bin
|
||||||
|
)
|
||||||
|
|
||||||
# Libs
|
# Libs
|
||||||
install(DIRECTORY lib/
|
install(DIRECTORY lib/
|
||||||
DESTINATION /usr/share/fun/lib
|
DESTINATION /usr/share/fun/lib
|
||||||
FILES_MATCHING PATTERN "*.fun")
|
FILES_MATCHING PATTERN "*.fun"
|
||||||
|
)
|
||||||
|
|
||||||
# Optionally install example scripts
|
# Optionally install example scripts
|
||||||
option(FUN_INSTALL_EXAMPLES "Install example .fun scripts" ON)
|
option(FUN_INSTALL_EXAMPLES "Install example .fun scripts" ON)
|
||||||
if(FUN_INSTALL_EXAMPLES)
|
if(FUN_INSTALL_EXAMPLES)
|
||||||
install(DIRECTORY examples/
|
install(DIRECTORY examples/
|
||||||
DESTINATION /usr/share/fun/examples
|
DESTINATION /usr/share/fun/examples
|
||||||
FILES_MATCHING PATTERN "*.fun")
|
FILES_MATCHING PATTERN "*.fun"
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# - Docs
|
# - Docs
|
||||||
install(FILES README.md LICENSE
|
install(FILES README.md LICENSE
|
||||||
DESTINATION /usr/share/doc/fun)
|
DESTINATION /usr/share/doc/fun
|
||||||
|
)
|
||||||
|
|
|
||||||
32
examples/cli_argv_dump.fun
Executable file
32
examples/cli_argv_dump.fun
Executable file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/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
|
||||||
|
*/
|
||||||
48
examples/cli_parse_example.fun
Executable file
48
examples/cli_parse_example.fun
Executable file
|
|
@ -0,0 +1,48 @@
|
||||||
|
#!/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,32 +1,29 @@
|
||||||
|
[auth]
|
||||||
|
user = "hanez"
|
||||||
|
token = "abcd1234"
|
||||||
|
|
||||||
[app]
|
[app]
|
||||||
name = "FunApp"
|
name = "FunApp"
|
||||||
version = "1.2.3"
|
version = "1.2.3"
|
||||||
debug = "1"
|
debug = "1"
|
||||||
|
|
||||||
|
|
||||||
[database]
|
[database]
|
||||||
host = "localhost"
|
host = "localhost"
|
||||||
port = "5432"
|
port = "5432"
|
||||||
user = "fun"
|
user = "fun"
|
||||||
pass = "secret"
|
pass = "secret"
|
||||||
pool_size = "8"
|
pool_size = "8"
|
||||||
timeout = "2.5"
|
timeout = "2.5"
|
||||||
|
|
||||||
|
|
||||||
[network]
|
[network]
|
||||||
ssl = "yes"
|
ssl = "yes"
|
||||||
retries = "3"
|
retries = "3"
|
||||||
base_url = "https://api.example.com"
|
base_url = "https://api.example.com"
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
feature_x = "on"
|
feature_x = "on"
|
||||||
feature_y = "off"
|
feature_y = "off"
|
||||||
|
|
||||||
|
|
||||||
[paths]
|
[paths]
|
||||||
data_dir = "./data"
|
data_dir = "./data"
|
||||||
log_file = "./logs/app.log"
|
log_file = "./logs/app.log"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,10 @@ print("<h1>Fun Environment Information</h1>")
|
||||||
print("<p><strong>Current Path:</strong> " + env("PATH") + "</p>")
|
print("<p><strong>Current Path:</strong> " + env("PATH") + "</p>")
|
||||||
print("<p><strong>Current User:</strong> " + env("USER") + "</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>Time:</strong> " + date_format(time_now_ms(), "%Y-%m-%d %H:%M:%S") + "</p>")
|
||||||
print("<p><strong>Version:</strong> " + to_string(fun_version()) + "</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("<h2>Full Environment:</h2>")
|
print("<h2>Full Environment:</h2>")
|
||||||
print("<ul>")
|
print("<ul>")
|
||||||
all_env = env_all()
|
all_env = env_all()
|
||||||
|
|
|
||||||
0
examples/env_all.fun
Normal file → Executable file
0
examples/env_all.fun
Normal file → Executable file
|
|
@ -1,3 +1,16 @@
|
||||||
|
#!/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>
|
#include <io/socket.fun>
|
||||||
|
|
||||||
c = TcpClient()
|
c = TcpClient()
|
||||||
|
|
@ -9,7 +22,6 @@ if (c.connect("127.0.0.1", 8080))
|
||||||
resp = c.recv_all(4096)
|
resp = c.recv_all(4096)
|
||||||
print("GET Response contains foo=bar: " + to_string(find(resp, "foo = bar") >= 0))
|
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))
|
print("GET Response contains baz=qux: " + to_string(find(resp, "baz = qux") >= 0))
|
||||||
|
|
||||||
c.close()
|
c.close()
|
||||||
else
|
else
|
||||||
print("Failed to connect to server")
|
print("Failed to connect to server")
|
||||||
|
|
|
||||||
|
|
@ -91,10 +91,9 @@ else
|
||||||
retries=3
|
retries=3
|
||||||
base_url=https://api.example.com
|
base_url=https://api.example.com
|
||||||
[features]
|
[features]
|
||||||
feature_x=0
|
feature_x=1
|
||||||
feature_y=0
|
feature_y=0
|
||||||
[paths]
|
[paths]
|
||||||
data_dir=./data
|
data_dir=./data
|
||||||
log_file=./logs/app.log
|
log_file=./logs/app.log
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ if h == 0
|
||||||
print("Failed to load " + path)
|
print("Failed to load " + path)
|
||||||
else
|
else
|
||||||
u = ini_get_string(h, "auth", "user", "guest")
|
u = ini_get_string(h, "auth", "user", "guest")
|
||||||
r = ini_get_int(h, "network", "retries", 3)
|
r = ini_get_int(h, "network", "retries", 5)
|
||||||
s = ini_get_bool(h, "network", "ssl", 0)
|
s = ini_get_bool(h, "network", "ssl", 0)
|
||||||
print("user=" + u)
|
print("user=" + u)
|
||||||
print("retries=" + to_string(r))
|
print("retries=" + to_string(r))
|
||||||
|
|
@ -33,4 +33,11 @@ else
|
||||||
user=<EFBFBD><EFBFBD><EFBFBD><EFBFBD>U
|
user=<EFBFBD><EFBFBD><EFBFBD><EFBFBD>U
|
||||||
retries=3
|
retries=3
|
||||||
ssl=1
|
ssl=1
|
||||||
|
|
||||||
|
I wonder about the user= value when the default value is used!
|
||||||
|
|
||||||
|
It should look like:
|
||||||
|
user=guest
|
||||||
|
retries=3
|
||||||
|
ssl=1
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
50
examples/ini_diag.fun
Normal file
50
examples/ini_diag.fun
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
#!/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
|
||||||
|
*/
|
||||||
29
examples/input_hidden_example.fun
Executable file
29
examples/input_hidden_example.fun
Executable file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/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!
|
||||||
|
*/
|
||||||
45
examples/input_hidden_pam_auth.fun
Executable file
45
examples/input_hidden_pam_auth.fun
Executable file
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/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!
|
||||||
|
*/
|
||||||
36
examples/math_ceil.fun
Normal file
36
examples/math_ceil.fun
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/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 ===
|
||||||
|
*/
|
||||||
30
examples/math_cos.fun
Normal file
30
examples/math_cos.fun
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/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 ===
|
||||||
|
*/
|
||||||
34
examples/math_exp_log.fun
Normal file
34
examples/math_exp_log.fun
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/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 ===
|
||||||
|
*/
|
||||||
36
examples/math_floor.fun
Normal file
36
examples/math_floor.fun
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/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 ===
|
||||||
|
*/
|
||||||
32
examples/math_fmin_fmax.fun
Normal file
32
examples/math_fmin_fmax.fun
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/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 ===
|
||||||
|
*/
|
||||||
35
examples/math_gcd_lcm.fun
Normal file
35
examples/math_gcd_lcm.fun
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/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 ===
|
||||||
|
*/
|
||||||
38
examples/math_isqrt.fun
Normal file
38
examples/math_isqrt.fun
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/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 ===
|
||||||
|
*/
|
||||||
36
examples/math_round.fun
Normal file
36
examples/math_round.fun
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/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 ===
|
||||||
|
*/
|
||||||
32
examples/math_sign.fun
Normal file
32
examples/math_sign.fun
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/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 ===
|
||||||
|
*/
|
||||||
30
examples/math_sin.fun
Normal file
30
examples/math_sin.fun
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/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 ===
|
||||||
|
*/
|
||||||
32
examples/math_sqrt.fun
Normal file
32
examples/math_sqrt.fun
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/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 ===
|
||||||
|
*/
|
||||||
28
examples/math_tan.fun
Normal file
28
examples/math_tan.fun
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/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 ===
|
||||||
|
*/
|
||||||
36
examples/math_trunc.fun
Normal file
36
examples/math_trunc.fun
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/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:
|
/* Actual output:
|
||||||
RIPEMD-160("") = d90cc72ef4ad5a2c51f89dc938de0b989c4c4b46
|
RIPEMD-160("") = 560e66de427fc2c3399e86c3e1d38c3c6017def2
|
||||||
RIPEMD-160("abc") = 7f339b642aaa074a849c6c1cd860cb049cc792b3
|
RIPEMD-160("abc") = 74266d6b4447493abb790c49e4bfa8f07263bcff
|
||||||
RIPEMD-160("message digest") = a4f9cd0270a341d05a713df4bf49a3ce8aabde64
|
RIPEMD-160("message digest") = 80ee01c69badb632393a9e05d1e7eebc2f05ac48
|
||||||
RIPEMD-160(hex: 616263) = 7f339b642aaa074a849c6c1cd860cb049cc792b3
|
RIPEMD-160(hex: 616263) = 74266d6b4447493abb790c49e4bfa8f07263bcff
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
36
examples/ripemd160_example.fun
Normal file
36
examples/ripemd160_example.fun
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/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
Normal file
66
lib/cli.fun
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* 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,7 +15,10 @@
|
||||||
|
|
||||||
class RIPEMD160()
|
class RIPEMD160()
|
||||||
KL = [0, 1518500249, 1859775393, 2400959708, 2840853838]
|
KL = [0, 1518500249, 1859775393, 2400959708, 2840853838]
|
||||||
KR = [1352829926, 1548603684, 1836062451, 2054012649, 0]
|
//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]
|
||||||
|
|
||||||
RL = [
|
RL = [
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
|
@ -58,22 +61,23 @@ class RIPEMD160()
|
||||||
return x
|
return x
|
||||||
|
|
||||||
fun rol32(this, x, s)
|
fun rol32(this, x, s)
|
||||||
return rol(this.u32(x), s)
|
x = this.u32(x)
|
||||||
|
return this.u32(bor(shl(x, s), shr(x, 32 - s)))
|
||||||
|
|
||||||
fun add32(this, a, b)
|
fun add32(this, a, b)
|
||||||
return this.u32(a + b)
|
return this.u32(a + b)
|
||||||
|
|
||||||
fun F(this, stage, x, y, z)
|
fun F(this, stage, x, y, z)
|
||||||
if stage == 0
|
if stage == 0
|
||||||
return bxor(bxor(x, y), z)
|
return this.u32(bxor(bxor(x, y), z))
|
||||||
else if stage == 1
|
else if stage == 1
|
||||||
return bor(band(x, y), band(bnot(x), z))
|
return this.u32(bor(band(x, y), band(bnot(x), z)))
|
||||||
else if stage == 2
|
else if stage == 2
|
||||||
return bxor(bor(x, bnot(y)), z)
|
return this.u32(bxor(bor(x, bnot(y)), z))
|
||||||
else if stage == 3
|
else if stage == 3
|
||||||
return bor(band(x, z), band(y, bnot(z)))
|
return this.u32(bor(band(x, z), band(y, bnot(z))))
|
||||||
else
|
else
|
||||||
return bxor(x, bor(y, bnot(z)))
|
return this.u32(bxor(x, bor(y, bnot(z))))
|
||||||
|
|
||||||
fun hex_val(this, ch)
|
fun hex_val(this, ch)
|
||||||
if ch == "0"
|
if ch == "0"
|
||||||
|
|
@ -204,7 +208,9 @@ class RIPEMD160()
|
||||||
B = T
|
B = T
|
||||||
|
|
||||||
// Right side step
|
// Right side step
|
||||||
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)
|
// 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)
|
||||||
Ar = Er
|
Ar = Er
|
||||||
Er = Dr
|
Er = Dr
|
||||||
Dr = this.rol32(Cr, 10)
|
Dr = this.rol32(Cr, 10)
|
||||||
|
|
@ -230,11 +236,19 @@ class RIPEMD160()
|
||||||
h3 = state[3]
|
h3 = state[3]
|
||||||
h4 = state[4]
|
h4 = state[4]
|
||||||
|
|
||||||
state[0] = this.add32(h1, this.add32(B, Cr))
|
// Final state combination (canonical RIPEMD-160):
|
||||||
state[1] = this.add32(h2, this.add32(C, Dr))
|
// T = h1 + Cl + Dr
|
||||||
state[2] = this.add32(h3, this.add32(D, Er))
|
// h1 = h2 + Dl + Er
|
||||||
state[3] = this.add32(h4, this.add32(E, Ar))
|
// h2 = h3 + El + Ar
|
||||||
state[4] = this.add32(h0, this.add32(A, Br))
|
// 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
|
||||||
return state
|
return state
|
||||||
|
|
||||||
fun ripemd160_bytes(this, bytes)
|
fun ripemd160_bytes(this, bytes)
|
||||||
|
|
|
||||||
316
lib/crypt/ripemd160_new_try.fun
Normal file
316
lib/crypt/ripemd160_new_try.fun
Normal file
|
|
@ -0,0 +1,316 @@
|
||||||
|
/*
|
||||||
|
* 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,6 +17,15 @@ class Console()
|
||||||
else
|
else
|
||||||
return input(join([q, ": "], ""))
|
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
|
// Ask a yes/no question; returns 1 for yes, 0 for no
|
||||||
// Accepts: y, yes, n, no (case-insensitive). Keeps asking until valid.
|
// Accepts: y, yes, n, no (case-insensitive). Keeps asking until valid.
|
||||||
fun ask_yes_no(this, question)
|
fun ask_yes_no(this, question)
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class INI()
|
||||||
// Load an INI file from path, closing previous one if open.
|
// Load an INI file from path, closing previous one if open.
|
||||||
// Returns handle (>0) or 0 on error.
|
// Returns handle (>0) or 0 on error.
|
||||||
fun load(this, path)
|
fun load(this, path)
|
||||||
if (this.h > 0)
|
if (this.is_open())
|
||||||
ini_free(this.h)
|
ini_free(this.h)
|
||||||
this.h = 0
|
this.h = 0
|
||||||
p = to_string(path)
|
p = to_string(path)
|
||||||
|
|
@ -39,11 +39,13 @@ class INI()
|
||||||
|
|
||||||
// True if a dictionary is open.
|
// True if a dictionary is open.
|
||||||
fun is_open(this)
|
fun is_open(this)
|
||||||
return this.h > 0
|
// 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
|
||||||
|
|
||||||
// Close and free resources. Safe to call multiple times.
|
// Close and free resources. Safe to call multiple times.
|
||||||
fun close(this)
|
fun close(this)
|
||||||
if (this.h > 0)
|
if (this.is_open())
|
||||||
ini_free(this.h)
|
ini_free(this.h)
|
||||||
this.h = 0
|
this.h = 0
|
||||||
return 1
|
return 1
|
||||||
|
|
|
||||||
3
make
3
make
|
|
@ -100,6 +100,8 @@ elif [ "$target" = "freebsd" ]; then
|
||||||
-DFUN_LINK_STATIC=OFF \
|
-DFUN_LINK_STATIC=OFF \
|
||||||
-DFUN_DEBUG=OFF \
|
-DFUN_DEBUG=OFF \
|
||||||
&& cmake --build build --target fun
|
&& cmake --build build --target fun
|
||||||
|
elif [ "$target" = "install" ]; then
|
||||||
|
sudo cmake --build build --target install
|
||||||
elif [ "$target" = "minimal" ]; then
|
elif [ "$target" = "minimal" ]; then
|
||||||
rm -rf build \
|
rm -rf build \
|
||||||
&& cmake -S . -B build \
|
&& cmake -S . -B build \
|
||||||
|
|
@ -159,6 +161,7 @@ else
|
||||||
echo " - alpine";
|
echo " - alpine";
|
||||||
echo " - debug";
|
echo " - debug";
|
||||||
echo " - freebsd";
|
echo " - freebsd";
|
||||||
|
echo " - install";
|
||||||
echo " - minimal";
|
echo " - minimal";
|
||||||
echo " - musl";
|
echo " - musl";
|
||||||
echo " - repl";
|
echo " - repl";
|
||||||
|
|
|
||||||
|
|
@ -69,16 +69,23 @@ fi
|
||||||
|
|
||||||
rc=0
|
rc=0
|
||||||
for f in "${files[@]}"; do
|
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/} ==="
|
echo "=== Running: ${f#$ROOT/} ==="
|
||||||
if ! "$BIN" "$f"; then
|
if ! "$BIN" "$f"; then
|
||||||
echo "FAILED: ${f#$ROOT/}"
|
echo "FAILED: ${f#$ROOT/}"
|
||||||
base="$(basename "$f")"
|
dest="$EX_DIR/error/$base_name"
|
||||||
dest="$EX_DIR/error/$base"
|
|
||||||
# Move the failing example to the error folder
|
# Move the failing example to the error folder
|
||||||
if mv -f "$f" "$dest"; then
|
if mv -f "$f" "$dest"; then
|
||||||
echo "Moved to: examples/error/$base"
|
echo "Moved to: examples/error/$base_name"
|
||||||
else
|
else
|
||||||
echo "warning: failed to move $base to examples/error/" >&2
|
echo "warning: failed to move $base_name to examples/error/" >&2
|
||||||
fi
|
fi
|
||||||
rc=1
|
rc=1
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,23 @@ static const char *opcode_name(OpCode op) {
|
||||||
case OP_TK_LABEL: return "TK_LABEL";
|
case OP_TK_LABEL: return "TK_LABEL";
|
||||||
case OP_TK_BUTTON: return "TK_BUTTON";
|
case OP_TK_BUTTON: return "TK_BUTTON";
|
||||||
case OP_TK_PACK: return "TK_PACK";
|
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 "???";
|
default: return "???";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,32 @@ typedef enum {
|
||||||
// exceptions (minimal)
|
// exceptions (minimal)
|
||||||
OP_TRY_PUSH, // operand = handler ip; push handler onto try-stack
|
OP_TRY_PUSH, // operand = handler ip; push handler onto try-stack
|
||||||
OP_TRY_POP, // pop current handler
|
OP_TRY_POP, // pop current handler
|
||||||
OP_THROW // pops error value; if handler -> jump to it (push err), else print and terminate
|
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)
|
||||||
} OpCode;
|
} OpCode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
||||||
51
src/fun.c
51
src/fun.c
|
|
@ -1,4 +1,13 @@
|
||||||
/**
|
/*
|
||||||
|
* 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.
|
* Main entry point for the Fun language interpreter.
|
||||||
* Builds a CLI that runs a script file if provided; otherwise starts the REPL
|
* Builds a CLI that runs a script file if provided; otherwise starts the REPL
|
||||||
* when compiled with FUN_WITH_REPL enabled.
|
* when compiled with FUN_WITH_REPL enabled.
|
||||||
|
|
@ -85,6 +94,46 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
if (argi < argc) {
|
if (argi < argc) {
|
||||||
const char *path = argv[argi];
|
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);
|
Bytecode *bc = parse_file_to_bytecode(path);
|
||||||
if (!bc) {
|
if (!bc) {
|
||||||
fprintf(stderr, "Failed to compile script: %s\n", path);
|
fprintf(stderr, "Failed to compile script: %s\n", path);
|
||||||
|
|
|
||||||
170
src/fun_test.c
170
src/fun_test.c
|
|
@ -11,6 +11,7 @@
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#define ASSERT_EQ(val, expected) \
|
#define ASSERT_EQ(val, expected) \
|
||||||
if ((val).type != VAL_INT || (val).i != (expected)) { \
|
if ((val).type != VAL_INT || (val).i != (expected)) { \
|
||||||
|
|
@ -33,6 +34,25 @@ int main(void) {
|
||||||
int c3 = bytecode_add_constant(bc, make_int(3));
|
int c3 = bytecode_add_constant(bc, make_int(3));
|
||||||
int c10 = bytecode_add_constant(bc, make_int(10));
|
int c10 = bytecode_add_constant(bc, make_int(10));
|
||||||
int c42 = bytecode_add_constant(bc, make_int(42));
|
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 ----------
|
// ---------- Arithmetic ----------
|
||||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c42);
|
bytecode_add_instruction(bc, OP_LOAD_CONST, c42);
|
||||||
|
|
@ -120,6 +140,156 @@ int main(void) {
|
||||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||||
bytecode_add_instruction(bc, OP_POP, 0); // dApache-2.0ard 1 (stack now empty)
|
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 ----------
|
// ---------- HALT ----------
|
||||||
bytecode_add_instruction(bc, OP_HALT, 0);
|
bytecode_add_instruction(bc, OP_HALT, 0);
|
||||||
|
|
||||||
|
|
|
||||||
137
src/parser.c
137
src/parser.c
|
|
@ -709,6 +709,20 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
free(name);
|
free(name);
|
||||||
return 1;
|
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) {
|
if (strcmp(name, "proc_run") == 0) {
|
||||||
(*pos)++; /* '(' */
|
(*pos)++; /* '(' */
|
||||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "proc_run expects 1 argument (command string)"); free(name); return 0; }
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "proc_run expects 1 argument (command string)"); free(name); return 0; }
|
||||||
|
|
@ -1728,6 +1742,22 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
free(name);
|
free(name);
|
||||||
return 1;
|
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) {
|
if (strcmp(name, "clamp") == 0) {
|
||||||
(*pos)++; /* '(' */
|
(*pos)++; /* '(' */
|
||||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "clamp expects 3 args"); free(name); return 0; }
|
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "clamp expects 3 args"); free(name); return 0; }
|
||||||
|
|
@ -1744,6 +1774,113 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
free(name);
|
free(name);
|
||||||
return 1;
|
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) {
|
if (strcmp(name, "pow") == 0) {
|
||||||
(*pos)++; /* '(' */
|
(*pos)++; /* '(' */
|
||||||
if (!emit_expression(bc, src, len, pos) || !consume_char(src, len, pos, ',')) { parser_fail(*pos, "pow expects 2 args"); free(name); return 0; }
|
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,9 +29,15 @@
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
/* For hidden input (password) handling in OP_INPUT_LINE */
|
||||||
|
#include <termios.h>
|
||||||
//#include <arpa/inet.h>
|
//#include <arpa/inet.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Bring in split-out built-ins without changing the build system yet */
|
/* Bring in split-out built-ins without changing the build system yet */
|
||||||
#include "iter.c"
|
#include "iter.c"
|
||||||
#include "map.c"
|
#include "map.c"
|
||||||
|
|
@ -42,6 +48,11 @@
|
||||||
// Optional by extensions commonly used code. #ifdef's are in each single file.
|
// Optional by extensions commonly used code. #ifdef's are in each single file.
|
||||||
#include "external/curl.c"
|
#include "external/curl.c"
|
||||||
#include "external/ini.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/json.c"
|
||||||
#include "external/libsql.c"
|
#include "external/libsql.c"
|
||||||
#include "external/pcsc.c"
|
#include "external/pcsc.c"
|
||||||
|
|
@ -661,10 +672,27 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
#include "vm/math/clamp.c"
|
#include "vm/math/clamp.c"
|
||||||
#include "vm/math/max.c"
|
#include "vm/math/max.c"
|
||||||
#include "vm/math/min.c"
|
#include "vm/math/min.c"
|
||||||
|
#include "vm/math/fmax.c"
|
||||||
|
#include "vm/math/fmin.c"
|
||||||
#include "vm/math/mod.c"
|
#include "vm/math/mod.c"
|
||||||
#include "vm/math/pow.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_int.c"
|
||||||
#include "vm/math/random_seed.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.c"
|
||||||
#include "vm/os/env_all.c"
|
#include "vm/os/env_all.c"
|
||||||
|
|
@ -730,6 +758,8 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
#include "vm/ini/set.c"
|
#include "vm/ini/set.c"
|
||||||
#include "vm/ini/unset.c"
|
#include "vm/ini/unset.c"
|
||||||
#include "vm/ini/save.c"
|
#include "vm/ini/save.c"
|
||||||
|
#else
|
||||||
|
#include "vm/ini/stubs.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* CURL ops */
|
/* CURL ops */
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,37 @@ case OP_INI_GET_BOOL: {
|
||||||
dictionary *d = ini_get(h);
|
dictionary *d = ini_get(h);
|
||||||
int outb = def;
|
int outb = def;
|
||||||
if (d && sec && key) {
|
if (d && sec && key) {
|
||||||
char full[1024]; ini_make_full_key(full, sizeof(full), sec, key);
|
char full[1024]; char alt[1024];
|
||||||
outb = iniparser_getboolean(d, full, def);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
|
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
|
||||||
push_value(vm, make_int(outb ? 1 : 0));
|
push_value(vm, make_int(outb ? 1 : 0));
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,27 @@ case OP_INI_GET_DOUBLE: {
|
||||||
dictionary *d = ini_get(h);
|
dictionary *d = ini_get(h);
|
||||||
double outd = def;
|
double outd = def;
|
||||||
if (d && sec && key) {
|
if (d && sec && key) {
|
||||||
char full[1024]; ini_make_full_key(full, sizeof(full), sec, key);
|
char full[1024]; char alt[1024];
|
||||||
outd = iniparser_getdouble(d, full, def);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
|
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
|
||||||
push_value(vm, make_float(outd));
|
push_value(vm, make_float(outd));
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,29 @@ case OP_INI_GET_INT: {
|
||||||
dictionary *d = ini_get(h);
|
dictionary *d = ini_get(h);
|
||||||
int outi = def;
|
int outi = def;
|
||||||
if (d && sec && key) {
|
if (d && sec && key) {
|
||||||
char full[1024]; ini_make_full_key(full, sizeof(full), sec, key);
|
char full[1024]; char alt[1024];
|
||||||
outi = iniparser_getint(d, full, def);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
|
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
|
||||||
push_value(vm, make_int(outi));
|
push_value(vm, make_int(outi));
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,19 @@ case OP_INI_GET_STRING: {
|
||||||
dictionary *d = ini_get(h);
|
dictionary *d = ini_get(h);
|
||||||
const char *res = def;
|
const char *res = def;
|
||||||
if (d && sec && key) {
|
if (d && sec && key) {
|
||||||
char full[1024]; ini_make_full_key(full, sizeof(full), 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; }
|
||||||
|
}
|
||||||
const char *s = iniparser_getstring(d, full, def);
|
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 : "";
|
res = s ? s : "";
|
||||||
}
|
}
|
||||||
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
|
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
|
||||||
|
|
|
||||||
63
src/vm/ini/handles.c
Normal file
63
src/vm/ini/handles.c
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* 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,35 +27,18 @@
|
||||||
# include <iniparser/iniparser.h>
|
# include <iniparser/iniparser.h>
|
||||||
# include <iniparser/dictionary.h>
|
# include <iniparser/dictionary.h>
|
||||||
#endif
|
#endif
|
||||||
#include <stdio.h> /* snprintf for helper */
|
#include <stddef.h>
|
||||||
|
|
||||||
typedef struct { dictionary *dict; int in_use; } IniSlot;
|
typedef struct { dictionary *dict; int in_use; } IniSlot;
|
||||||
static IniSlot g_ini[64];
|
|
||||||
|
|
||||||
static int ini_alloc_handle(dictionary *d) {
|
/* Single global registry (defined in handles.c) */
|
||||||
for (int i = 1; i < (int)(sizeof(g_ini)/sizeof(g_ini[0])); ++i) {
|
extern IniSlot g_ini[64];
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper to build section:key string safely into provided buffer */
|
/* Registry API (implemented in handles.c) */
|
||||||
static inline void ini_make_full_key(char *buf, size_t cap, const char *sec, const char *key) {
|
int ini_alloc_handle(dictionary *d);
|
||||||
if (!buf || cap == 0) return;
|
dictionary* ini_get(int h);
|
||||||
if (!sec) sec = "";
|
int ini_free_handle(int h);
|
||||||
if (!key) key = "";
|
|
||||||
/* iniparser expects "section:key" */
|
/* Helper to build section:key string safely into provided buffer (implemented in handles.c) */
|
||||||
snprintf(buf, cap, "%s:%s", sec, key);
|
void ini_make_full_key(char *buf, size_t cap, const char *sec, const char *key);
|
||||||
}
|
|
||||||
#endif /* FUN_WITH_INI */
|
#endif /* FUN_WITH_INI */
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,16 @@ case OP_INI_SET: {
|
||||||
if (d && sec && key) {
|
if (d && sec && key) {
|
||||||
char *valstr = value_to_string_alloc(&vval);
|
char *valstr = value_to_string_alloc(&vval);
|
||||||
if (valstr) {
|
if (valstr) {
|
||||||
char full[1024]; snprintf(full, sizeof(full), "%s:%s", 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.x does not expose iniparser_set; use dictionary_set */
|
/* iniparser 4.x does not expose iniparser_set; use dictionary_set */
|
||||||
if (dictionary_set(d, full, valstr) == 0) ok = 1; /* 0 means success */
|
if (dictionary_set(d, full, valstr) == 0) {
|
||||||
|
ok = 1; /* 0 means success */
|
||||||
|
} else if (dictionary_set(d, alt, valstr) == 0) {
|
||||||
|
ok = 1;
|
||||||
|
}
|
||||||
free(valstr);
|
free(valstr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
104
src/vm/ini/stubs.c
Normal file
104
src/vm/ini/stubs.c
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* 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,9 +20,13 @@ case OP_INI_UNSET: {
|
||||||
const char *sec = (vsec.type==VAL_STRING)?vsec.s:NULL;
|
const char *sec = (vsec.type==VAL_STRING)?vsec.s:NULL;
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
if (d && sec && key) {
|
if (d && sec && key) {
|
||||||
char full[1024]; snprintf(full, sizeof(full), "%s:%s", sec, key);
|
char full[1024]; char alt[1024];
|
||||||
/* iniparser 4.2.6 dictionary_unset returns void; assume success if inputs are valid */
|
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 */
|
||||||
dictionary_unset(d, full);
|
dictionary_unset(d, full);
|
||||||
|
dictionary_unset(d, alt);
|
||||||
ok = 1;
|
ok = 1;
|
||||||
}
|
}
|
||||||
free_value(vkey); free_value(vsec); free_value(vh);
|
free_value(vkey); free_value(vsec); free_value(vh);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
case OP_INPUT_LINE: {
|
case OP_INPUT_LINE: {
|
||||||
/* operand: 0 = no prompt; 1 = has prompt (string or any value convertible to string) */
|
/* operand bit flags:
|
||||||
int has_prompt = inst.operand ? 1 : 0;
|
* 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;
|
||||||
if (has_prompt) {
|
if (has_prompt) {
|
||||||
/* pop prompt value and print without newline */
|
/* pop prompt value and print without newline */
|
||||||
Value pv = pop_value(vm);
|
Value pv = pop_value(vm);
|
||||||
|
|
@ -13,6 +17,35 @@ case OP_INPUT_LINE: {
|
||||||
free_value(pv);
|
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 */
|
/* read a line from stdin, dynamically grow buffer */
|
||||||
size_t cap = 128;
|
size_t cap = 128;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
|
|
@ -20,7 +53,8 @@ case OP_INPUT_LINE: {
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
fprintf(stderr, "Runtime error: out of memory reading input");
|
fprintf(stderr, "Runtime error: out of memory reading input");
|
||||||
push_value(vm, make_string(""));
|
push_value(vm, make_string(""));
|
||||||
break;
|
/* On early exit, try to restore echo if we turned it off */
|
||||||
|
goto restore_echo_and_break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ch;
|
int ch;
|
||||||
|
|
@ -68,5 +102,36 @@ case OP_INPUT_LINE: {
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
||||||
push_done:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
43
src/vm/math/ceil.c
Normal file
43
src/vm/math/ceil.c
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
31
src/vm/math/cos.c
Normal file
31
src/vm/math/cos.c
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
31
src/vm/math/exp.c
Normal file
31
src/vm/math/exp.c
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
43
src/vm/math/floor.c
Normal file
43
src/vm/math/floor.c
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
41
src/vm/math/fmax.c
Normal file
41
src/vm/math/fmax.c
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
41
src/vm/math/fmin.c
Normal file
41
src/vm/math/fmin.c
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
40
src/vm/math/gcd.c
Normal file
40
src/vm/math/gcd.c
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
47
src/vm/math/isqrt.c
Normal file
47
src/vm/math/isqrt.c
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
48
src/vm/math/lcm.c
Normal file
48
src/vm/math/lcm.c
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
36
src/vm/math/log.c
Normal file
36
src/vm/math/log.c
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
35
src/vm/math/log10.c
Normal file
35
src/vm/math/log10.c
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
43
src/vm/math/round.c
Normal file
43
src/vm/math/round.c
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
33
src/vm/math/sign.c
Normal file
33
src/vm/math/sign.c
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
31
src/vm/math/sin.c
Normal file
31
src/vm/math/sin.c
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
45
src/vm/math/sqrt.c
Normal file
45
src/vm/math/sqrt.c
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
31
src/vm/math/tan.c
Normal file
31
src/vm/math/tan.c
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
42
src/vm/math/trunc.c
Normal file
42
src/vm/math/trunc.c
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* 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