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("")
+all_env = env_all()
+all_keys = keys(all_env)
+i = 0
+while i < len(all_keys)
+ key = all_keys[i]
+ print("- " + key + "=" + all_env[key] + "
")
+ i = i + 1
+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;
+}