diff --git a/examples/os_env.fun b/examples/os_env.fun new file mode 100644 index 0000000..4b2506f --- /dev/null +++ b/examples/os_env.fun @@ -0,0 +1,18 @@ +#!/usr/bin/env fun + +// Environment variables via env(name): +// - Returns the value as a string. +// - If the variable is not set, returns an empty string. + +print("HOME=" + env("HOME")) +print("SHELL=" + env("SHELL")) +print("FUN_NOT_SET=" + env("FUN_NOT_SET")) + +// You can use it in scripts, e.g.: +fun greet() + user = env("USER") + if user == "" + print("Hello, mysterious friend!") + else + print("Hello, " + user + "!") +greet() diff --git a/src/bytecode.h b/src/bytecode.h index 9033ada..f475bab 100644 --- a/src/bytecode.h +++ b/src/bytecode.h @@ -110,7 +110,10 @@ typedef enum { // file I/O OP_READ_FILE, // pops path string; pushes content string (or "") - OP_WRITE_FILE // pops data string, path string; pushes 1/0 + OP_WRITE_FILE, // pops data string, path string; pushes 1/0 + + // OS + OP_ENV // pops name string; pushes value string (or "") } OpCode; typedef struct { diff --git a/src/parser.c b/src/parser.c index faf8f53..0308c3d 100644 --- a/src/parser.c +++ b/src/parser.c @@ -663,6 +663,14 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) free(name); return 1; } + if (strcmp(name, "env") == 0) { + (*pos)++; /* '(' */ + if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "env expects 1 argument"); free(name); return 0; } + if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after env arg"); free(name); return 0; } + bytecode_add_instruction(bc, OP_ENV, 0); + free(name); + return 1; + } /* string ops */ if (strcmp(name, "split") == 0) { (*pos)++; /* '(' */ diff --git a/src/vm.c b/src/vm.c index d2d63a3..f3a4a0d 100644 --- a/src/vm.c +++ b/src/vm.c @@ -266,6 +266,7 @@ void vm_run(VM *vm, Bytecode *entry) { #include "vm/io/read_file.c" #include "vm/io/write_file.c" + #include "vm/os/env.c" #include "vm/logic/and.c" #include "vm/logic/eq.c" diff --git a/src/vm.h b/src/vm.h index cb2e0ff..496fc53 100644 --- a/src/vm.h +++ b/src/vm.h @@ -33,7 +33,7 @@ static const char *opcode_names[] = { "ENUMERATE","ZIP", "MIN","MAX","CLAMP","ABS","POW","RANDOM_SEED","RANDOM_INT", "MAKE_MAP","KEYS","VALUES","HAS_KEY", - "READ_FILE","WRITE_FILE" + "READ_FILE","WRITE_FILE","ENV" }; typedef struct { @@ -76,7 +76,7 @@ void vm_dump_globals(VM *vm); void vm_run(VM *vm, Bytecode *entry); static inline int opcode_is_valid(int op) { - return op >= OP_NOP && op <= OP_WRITE_FILE; // all current opcodes + return op >= OP_NOP && op <= OP_ENV; // all current opcodes } #endif diff --git a/src/vm/os/env.c b/src/vm/os/env.c new file mode 100644 index 0000000..d90f473 --- /dev/null +++ b/src/vm/os/env.c @@ -0,0 +1,14 @@ +case OP_ENV: { + Value key = pop_value(vm); + if (key.type != VAL_STRING) { + fprintf(stderr, "Runtime type error: ENV expects string name\n"); + free_value(key); + exit(1); + } + const char *name = key.s ? key.s : ""; + const char *val = getenv(name); + /* Return empty string if not set (consistent with read_file fallback style) */ + push_value(vm, make_string(val ? val : "")); + free_value(key); + break; +}