diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e8bd14..65ff1d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(fun VERSION 0.37.29 LANGUAGES C) +project(fun VERSION 0.37.30 LANGUAGES C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) @@ -378,10 +378,12 @@ target_include_directories(fun_core PUBLIC # Apply options to core if(FUN_DEBUG) message(STATUS "FUN_DEBUG enabled: building with verbose debug logging") + target_compile_definitions(fun_core PUBLIC FUN_VERSION="${PROJECT_VERSION}") target_compile_definitions(fun_core PUBLIC FUN_DEBUG=1) endif() # Provide default stdlib directory to the runtime +target_compile_definitions(fun_core PUBLIC FUN_VERSION="${PROJECT_VERSION}") target_compile_definitions(fun_core PUBLIC DEFAULT_LIB_DIR="${DEFAULT_LIB_DIR}") # PCSC include and link (if enabled) @@ -472,7 +474,6 @@ add_executable(fun src/fun.c ) # Provide version string to the CLI -target_compile_definitions(fun PRIVATE FUN_VERSION=\"${PROJECT_VERSION}\") if(FUN_WITH_REPL) # Enable REPL compilation path and add the REPL source target_compile_definitions(fun PRIVATE FUN_WITH_REPL=1) diff --git a/examples/data/htdocs/info.fun b/examples/data/htdocs/info.fun index 1e48bae..93c3665 100644 --- a/examples/data/htdocs/info.fun +++ b/examples/data/htdocs/info.fun @@ -11,8 +11,7 @@ * Added: 2025-12-28 */ -result = proc_run("fun -V") -content = result["out"] +content = fun_version() print("") print("

Fun Environment Information

") @@ -20,4 +19,14 @@ print("

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

") print("

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

") print("

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

") print("

Version: " + to_string(content) + "

") +print("

Full Environment:

") +print("") print("") diff --git a/examples/env_all.fun b/examples/env_all.fun new file mode 100644 index 0000000..ccbd93b --- /dev/null +++ b/examples/env_all.fun @@ -0,0 +1,28 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-12-28 + */ + +all_env = env_all() +keys = keys(all_env) + +print("Number of environment variables: " + to_string(len(keys))) +print("--------------------------------------------------") + +i = 0 +while i < len(keys) + key = keys[i] + print(key + "=" + all_env[key]) + i = i + 1 + +/* Possible output: +Depends on your environment. +*/ diff --git a/examples/version.fun b/examples/version.fun new file mode 100755 index 0000000..84d0456 --- /dev/null +++ b/examples/version.fun @@ -0,0 +1,4 @@ +#!/usr/bin/env fun + +print(to_string(fun_version())) + diff --git a/lib/net/http_server.fun b/lib/net/http_server.fun index cf430d2..3d36b03 100644 --- a/lib/net/http_server.fun +++ b/lib/net/http_server.fun @@ -61,9 +61,8 @@ class HTTPServer(number port) content = "" if (str_ends_with(path, ".fun")) - res = proc_run("fun " + full_path) + res = proc_run("fun" + " " + full_path) content = res["out"] - //#include <\"full_path\"> else content = read_file(full_path) diff --git a/src/bytecode.c b/src/bytecode.c index 6bf4c6e..f1af0e2 100644 --- a/src/bytecode.c +++ b/src/bytecode.c @@ -131,6 +131,8 @@ static const char *opcode_name(OpCode op) { case OP_TIME_NOW_MS: return "TIME_NOW_MS"; case OP_CLOCK_MONO_MS: return "CLOCK_MONO_MS"; case OP_DATE_FORMAT: return "DATE_FORMAT"; + case OP_ENV_ALL: return "ENV_ALL"; + case OP_FUN_VERSION: return "FUN_VERSION"; case OP_THREAD_SPAWN: return "THREAD_SPAWN"; case OP_THREAD_JOIN: return "THREAD_JOIN"; case OP_SLEEP_MS: return "SLEEP_MS"; diff --git a/src/bytecode.h b/src/bytecode.h index 25158e4..0c0e971 100644 --- a/src/bytecode.h +++ b/src/bytecode.h @@ -126,6 +126,8 @@ typedef enum { OP_TIME_NOW_MS, // pushes current wall-clock time in milliseconds since Unix epoch OP_CLOCK_MONO_MS, // pushes monotonic clock in milliseconds (not wall time) OP_DATE_FORMAT, // pops fmt string, ms epoch (int); pushes formatted date string using strftime + OP_ENV_ALL, // pushes map of all environment variables + OP_FUN_VERSION, // pushes version string // Threads OP_THREAD_SPAWN, // operand: 0=no args, 1=has args; pops [args?], fn; pushes thread id (int>0) diff --git a/src/fun.c b/src/fun.c index afd7305..041e306 100644 --- a/src/fun.c +++ b/src/fun.c @@ -9,6 +9,7 @@ #include "parser.h" #include #include +#include #include #ifdef FUN_WITH_REPL @@ -42,6 +43,9 @@ static void print_usage(const char *prog) { } int main(int argc, char **argv) { + /* Set FUN_EXECUTABLE environment variable to the path of this binary */ + setenv("FUN_EXECUTABLE", argv[0], 1); + VM vm; vm_init(&vm); diff --git a/src/parser.c b/src/parser.c index 717ca89..f5b69d9 100644 --- a/src/parser.c +++ b/src/parser.c @@ -757,6 +757,20 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) free(name); return 1; } + if (strcmp(name, "env_all") == 0) { + (*pos)++; /* '(' */ + if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "env_all expects ()"); free(name); return 0; } + bytecode_add_instruction(bc, OP_ENV_ALL, 0); + free(name); + return 1; + } + if (strcmp(name, "fun_version") == 0) { + (*pos)++; /* '(' */ + if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "fun_version expects ()"); free(name); return 0; } + bytecode_add_instruction(bc, OP_FUN_VERSION, 0); + free(name); + return 1; + } if (strcmp(name, "os_list_dir") == 0) { (*pos)++; /* '(' */ if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "os_list_dir expects (path)"); free(name); return 0; } diff --git a/src/vm.c b/src/vm.c index 5ad2021..f2a4470 100644 --- a/src/vm.c +++ b/src/vm.c @@ -667,6 +667,8 @@ void vm_run(VM *vm, Bytecode *entry) { #include "vm/math/random_seed.c" #include "vm/os/env.c" + #include "vm/os/env_all.c" + #include "vm/os/fun_version.c" #include "vm/os/sleep_ms.c" #include "vm/os/thread_join.c" #include "vm/os/thread_spawn.c" diff --git a/src/vm/os/env_all.c b/src/vm/os/env_all.c new file mode 100644 index 0000000..61b76cc --- /dev/null +++ b/src/vm/os/env_all.c @@ -0,0 +1,35 @@ +/** + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-12-28 + */ + +// Get all environment variables of the operation system and push them as a map. + +case OP_ENV_ALL: { + extern char **environ; + Value m = make_map_empty(); + if (environ) { + for (char **env = environ; *env; ++env) { + char *entry = *env; + char *equals = strchr(entry, '='); + if (equals) { + size_t key_len = equals - entry; + char *key = malloc(key_len + 1); + if (key) { + memcpy(key, entry, key_len); + key[key_len] = '\0'; + map_set(&m, key, make_string(equals + 1)); + free(key); + } + } + } + } + push_value(vm, m); + break; +} diff --git a/src/vm/os/fun_version.c b/src/vm/os/fun_version.c new file mode 100644 index 0000000..f2ca4b6 --- /dev/null +++ b/src/vm/os/fun_version.c @@ -0,0 +1,20 @@ +/** + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-12-28 + */ + +// Pushes the current Fun version string onto the stack. + +case OP_FUN_VERSION: { +#ifndef FUN_VERSION +#define FUN_VERSION "0.0.0-dev" +#endif + push_value(vm, make_string(FUN_VERSION)); + break; +}