From 4e69a3ce63c13ce59bcb0139c22592babd4ed450 Mon Sep 17 00:00:00 2001 From: hanez Date: Fri, 1 May 2026 02:20:25 +0200 Subject: [PATCH] Added tons of AI generated docs and a lot of cleanups. No code changes. (0.41.5) --- src/array_utils.c | 28 ++- src/bytecode.c | 62 ++++++ src/bytecode.h | 20 +- src/extensions/curl.c | 44 ++++- src/extensions/ini.c | 10 +- src/extensions/json.c | 38 +++- src/extensions/openssl.c | 65 +++++-- src/extensions/pcre2.c | 10 +- src/extensions/pcsc.c | 38 +++- src/extensions/sqlite.c | 50 ++++- src/extensions/xml2.c | 75 +++++++- src/fun.c | 26 ++- src/fun_test.c | 20 ++ src/funstx.c | 68 ++++++- src/iter.c | 32 +++- src/map.c | 60 ++++++ src/parser.c | 326 +++++++++++++++++++++++++++++++- src/parser.h | 39 +++- src/parser_utils.c | 140 ++++++++++++++ src/repl.c | 128 ++++++++++++- src/repl.h | 25 ++- src/rust/src/lib.rs | 82 +++++++- src/rust/src/vm/mod.rs | 29 ++- src/str_utils.c | 48 +++++ src/string.c | 59 ++++++ src/test_opcodes.c | 18 ++ src/value.c | 222 ++++++++++++++++++++++ src/value.h | 100 +++++++--- src/vm.c | 260 ++++++++++++++++++++++++- src/vm.h | 119 +++++++++--- src/vm/arithmetic/div.c | 20 +- src/vm/arithmetic/mul.c | 18 +- src/vm/arithmetic/sub.c | 18 +- src/vm/arrays/clear.c | 29 +-- src/vm/arrays/make_array.c | 2 +- src/vm/arrays/push.c | 15 +- src/vm/arrays/remove.c | 14 +- src/vm/arrays/set.c | 17 +- src/vm/bitwise/band.c | 24 ++- src/vm/bitwise/bnot.c | 24 ++- src/vm/bitwise/bor.c | 24 ++- src/vm/bitwise/bxor.c | 24 ++- src/vm/bitwise/rol.c | 19 +- src/vm/bitwise/ror.c | 19 +- src/vm/bitwise/shl.c | 19 +- src/vm/bitwise/shr.c | 20 +- src/vm/core/throw.c | 26 +++ src/vm/core/try_pop.c | 16 ++ src/vm/core/try_push.c | 19 ++ src/vm/cpp/add.cpp | 36 +++- src/vm/curl/download.c | 38 +++- src/vm/curl/get.c | 39 +++- src/vm/curl/post.c | 39 +++- src/vm/echo.c | 18 +- src/vm/ini/free.c | 26 ++- src/vm/ini/get_bool.c | 25 ++- src/vm/ini/get_double.c | 19 +- src/vm/ini/get_int.c | 19 +- src/vm/ini/get_string.c | 19 +- src/vm/ini/handles.c | 11 +- src/vm/ini/handles.h | 37 +++- src/vm/ini/load.c | 20 +- src/vm/ini/save.c | 19 +- src/vm/ini/set.c | 20 +- src/vm/ini/stubs.c | 11 +- src/vm/ini/unset.c | 20 +- src/vm/io/input_line.c | 50 +++++ src/vm/json/from_file.c | 24 ++- src/vm/json/parse.c | 22 ++- src/vm/json/stringify.c | 21 +- src/vm/json/to_file.c | 21 +- src/vm/line.c | 12 ++ src/vm/math/ceil.c | 24 ++- src/vm/math/cos.c | 20 +- src/vm/math/exp.c | 16 +- src/vm/math/floor.c | 24 ++- src/vm/math/fmax.c | 2 - src/vm/math/fmin.c | 2 - src/vm/math/gcd.c | 14 +- src/vm/math/isqrt.c | 14 +- src/vm/math/lcm.c | 14 +- src/vm/math/log.c | 14 +- src/vm/math/log10.c | 10 +- src/vm/math/round.c | 2 - src/vm/math/sign.c | 2 - src/vm/math/sin.c | 19 +- src/vm/math/sqrt.c | 11 +- src/vm/math/tan.c | 16 +- src/vm/math/trunc.c | 2 - src/vm/openssl/md5.c | 7 +- src/vm/openssl/ripemd160.c | 2 - src/vm/openssl/sha256.c | 2 - src/vm/openssl/sha512.c | 2 - src/vm/os/clock_mono_ms.c | 2 - src/vm/os/date_format.c | 2 - src/vm/os/env.c | 12 +- src/vm/os/env_all.c | 11 +- src/vm/os/fd_poll_read.c | 12 +- src/vm/os/fd_poll_write.c | 12 +- src/vm/os/fd_set_nonblock.c | 12 +- src/vm/os/fun_version.c | 8 +- src/vm/os/list_dir.c | 12 +- src/vm/os/proc_run.c | 12 +- src/vm/os/proc_system.c | 11 +- src/vm/os/random_number.c | 17 +- src/vm/os/serial_close.c | 12 +- src/vm/os/serial_config.c | 12 +- src/vm/os/serial_open.c | 12 +- src/vm/os/serial_recv.c | 12 +- src/vm/os/serial_send.c | 12 +- src/vm/os/sleep_ms.c | 13 +- src/vm/os/socket_close.c | 12 +- src/vm/os/socket_recv.c | 12 +- src/vm/os/socket_send.c | 12 +- src/vm/os/socket_tcp_accept.c | 11 +- src/vm/os/socket_tcp_connect.c | 13 +- src/vm/os/socket_tcp_listen.c | 11 +- src/vm/os/socket_unix_connect.c | 11 +- src/vm/os/socket_unix_listen.c | 12 +- src/vm/os/thread_common.c | 14 +- src/vm/os/thread_join.c | 12 +- src/vm/os/thread_spawn.c | 12 +- src/vm/os/time_now_ms.c | 2 - src/vm/pcre2/findall.c | 34 +++- src/vm/pcre2/match.c | 32 +++- src/vm/pcre2/test.c | 27 +++ src/vm/pcsc/connect.c | 18 +- src/vm/pcsc/disconnect.c | 17 +- src/vm/pcsc/establish.c | 16 +- src/vm/pcsc/list_readers.c | 16 +- src/vm/pcsc/release.c | 16 +- src/vm/pcsc/transmit.c | 22 ++- src/vm/rust/get_sp.c | 27 ++- src/vm/rust/hello.c | 27 ++- src/vm/rust/hello_args.c | 30 ++- src/vm/rust/hello_args_return.c | 33 +++- src/vm/rust/set_exit.c | 29 ++- src/vm/sclamp.c | 14 ++ src/vm/sqlite/close.c | 10 +- src/vm/sqlite/exec.c | 10 +- src/vm/sqlite/open.c | 10 +- src/vm/sqlite/query.c | 10 +- src/vm/strings/regex_match.c | 26 ++- src/vm/strings/regex_replace.c | 26 ++- src/vm/strings/regex_search.c | 29 ++- src/vm/typeof.c | 14 ++ src/vm/uclamp.c | 13 ++ src/vm/xml/name.c | 30 ++- src/vm/xml/parse.c | 28 ++- src/vm/xml/root.c | 26 ++- src/vm/xml/text.c | 27 ++- 151 files changed, 3905 insertions(+), 435 deletions(-) diff --git a/src/array_utils.c b/src/array_utils.c index d9377ec..967830e 100644 --- a/src/array_utils.c +++ b/src/array_utils.c @@ -9,8 +9,20 @@ #include "value.h" -/* array utilities */ +/** + * @file array_utils.c + * @brief Utility functions for operating on Value arrays. + */ +/** + * @brief Check if an array Value contains a given element. + * + * Performs linear search using value_equals() on copied elements. + * + * @param v Array Value to inspect (may be NULL). + * @param needle Value to search for. + * @return 1 if found, 0 otherwise or if v is not an array. + */ int array_contains(const Value *v, const Value *needle) { if (!v || v->type != VAL_ARRAY || !v->arr) return 0; int n = array_length(v); @@ -25,6 +37,13 @@ int array_contains(const Value *v, const Value *needle) { return 0; } +/** + * @brief Find the index of the first occurrence of an element in an array. + * + * @param v Array Value to search (may be NULL). + * @param needle Value to look for. + * @return Zero-based index when found; -1 if not found or if v is not an array. + */ int array_index_of(const Value *v, const Value *needle) { if (!v || v->type != VAL_ARRAY || !v->arr) return -1; int n = array_length(v); @@ -39,6 +58,13 @@ int array_index_of(const Value *v, const Value *needle) { return -1; } +/** + * @brief Remove all elements from an array Value, freeing their contents. + * + * Uses array_pop() repeatedly to clear the array. + * + * @param v Array Value to clear (in place). No-op if not an array. + */ void array_clear(Value *v) { if (!v || v->type != VAL_ARRAY || !v->arr) return; int n = array_length(v); diff --git a/src/bytecode.c b/src/bytecode.c index f5fcbf5..3490aa4 100644 --- a/src/bytecode.c +++ b/src/bytecode.c @@ -11,6 +11,19 @@ #include #include +/** + * @file bytecode.c + * @brief Bytecode container utilities: creation, mutation, dump helpers. + */ + +/** + * @brief Allocate and initialize an empty Bytecode object. + * + * Initializes instruction and constant arrays to empty and clears metadata. + * Caller owns the returned pointer and must free it with bytecode_free(). + * + * @return Newly allocated Bytecode*, or NULL on allocation failure. + */ Bytecode *bytecode_new(void) { Bytecode *bc = (Bytecode *)malloc(sizeof(Bytecode)); bc->instructions = NULL; @@ -22,12 +35,30 @@ Bytecode *bytecode_new(void) { return bc; } +/** + * @brief Append a constant to a Bytecode's constant table. + * + * The value is deep-copied into the table; the caller retains ownership of v + * and may free it independently. + * + * @param bc Target bytecode (must not be NULL). + * @param v Value to store (copied). + * @return The index of the stored constant (zero-based). + */ int bytecode_add_constant(Bytecode *bc, Value v) { bc->constants = (Value *)realloc(bc->constants, sizeof(Value) * (bc->const_count + 1)); bc->constants[bc->const_count] = copy_value(&v); return bc->const_count++; } +/** + * @brief Append a single instruction to the instruction stream. + * + * @param bc Target bytecode (must not be NULL). + * @param op Opcode to emit. + * @param operand Operand value (semantics depend on opcode). + * @return The index of the emitted instruction (zero-based). + */ int bytecode_add_instruction(Bytecode *bc, OpCode op, int32_t operand) { bc->instructions = (Instruction *)realloc(bc->instructions, sizeof(Instruction) * (bc->instr_count + 1)); bc->instructions[bc->instr_count].op = op; @@ -35,12 +66,29 @@ int bytecode_add_instruction(Bytecode *bc, OpCode op, int32_t operand) { return bc->instr_count++; } +/** + * @brief Patch the operand of a previously emitted instruction. + * + * Silently ignores out-of-bounds indices. + * + * @param bc Target bytecode. + * @param idx Instruction index to patch. + * @param operand New operand value. + */ void bytecode_set_operand(Bytecode *bc, int idx, int32_t operand) { if (idx >= 0 && idx < bc->instr_count) { bc->instructions[idx].operand = operand; } } +/** + * @brief Free a Bytecode and all memory it owns. + * + * Frees constants (deep), instruction array, and metadata strings. + * Accepts NULL and is then a no-op. + * + * @param bc Bytecode to free (may be NULL). + */ void bytecode_free(Bytecode *bc) { if (!bc) return; for (int i = 0; i < bc->const_count; ++i) { @@ -53,6 +101,12 @@ void bytecode_free(Bytecode *bc) { free(bc); } +/** + * @brief Convert an opcode enum to a short mnemonic string. + * + * @param op Opcode value. + * @return Read-only C string with the mnemonic, or "???" if unknown. + */ static const char *opcode_name(OpCode op) { switch (op) { case OP_NOP: @@ -358,6 +412,14 @@ static const char *opcode_name(OpCode op) { } } +/** + * @brief Print a human-readable dump of constants and instructions to stdout. + * + * Intended for debugging and tests. Formats constants with print_value() + * and shows each instruction index, mnemonic and operand. + * + * @param bc Bytecode to dump (prints "" if NULL). + */ void bytecode_dump(const Bytecode *bc) { if (!bc) { printf("\n"); diff --git a/src/bytecode.h b/src/bytecode.h index c456d19..58b10fb 100644 --- a/src/bytecode.h +++ b/src/bytecode.h @@ -7,13 +7,31 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file bytecode.h + * @brief Definitions for the Fun VM bytecode: opcodes, instruction format, and bytecode container API. + * + * This header declares the VM's operation codes (@ref OpCode), the compact + * instruction representation (@ref Instruction), and the owning bytecode + * container (@ref Bytecode) together with minimal constructor/manipulation + * helpers. The concrete execution semantics for each opcode are implemented + * in the VM (see vm.c and vm/* handlers). + */ #ifndef FUN_BYTECODE_H #define FUN_BYTECODE_H #include "value.h" #include -// VM opcodes +/** + * @brief VM operation codes executed by the Fun virtual machine. + * + * Unless stated otherwise, opcodes operate on the VM stack. Comments on each + * opcode describe stack effects using a left-to-right pop order and the value + * pushed as a result. For example, "pops b, a; pushes a+b" means the + * instruction will pop first b then a from the stack and finally push the + * result of a+b. + */ typedef enum { OP_NOP, OP_LOAD_CONST, // operand = constant index diff --git a/src/extensions/curl.c b/src/extensions/curl.c index bf400ce..f3a3718 100644 --- a/src/extensions/curl.c +++ b/src/extensions/curl.c @@ -1,21 +1,47 @@ -/* +/** * 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 + */ + +/** + * @file curl.c + * @brief libcurl helpers and buffers used by HTTP-related VM opcodes. * - * Added: 2025-12-11 (2025-12-11 migrated from src/vm/libsql/common.c) + * Declares small buffer helpers and libcurl write callbacks that support + * network-related opcodes when FUN_WITH_CURL is enabled. */ /* Ensure libcurl headers and helpers are defined at file scope (not inside vm_run) */ #ifdef FUN_WITH_CURL #include + +/** + * @brief Simple growable buffer for libcurl write callbacks. + * + * The buffer grows via realloc as more data arrives. The content is kept + * NUL-terminated for convenience. + */ typedef struct { - char *d; - size_t n; + char *d; /**< Data pointer (NUL-terminated). */ + size_t n; /**< Number of bytes stored (excluding NUL). */ } FunCurlBuf; + +/** + * @brief libcurl write callback that appends data to a FunCurlBuf. + * + * Reallocates the destination buffer as needed and keeps it NUL-terminated. + * + * @param ptr Pointer to incoming data block provided by libcurl. + * @param sz Size of each data element. + * @param nm Number of elements in this block. + * @param ud User data; must be a FunCurlBuf*. + * @return Number of bytes actually handled (sz*nm) on success, 0 on failure + * to signal an error to libcurl. + */ static size_t fun_curl_write_cb(void *ptr, size_t sz, size_t nm, void *ud) { size_t add = sz * nm; FunCurlBuf *b = (FunCurlBuf *)ud; @@ -27,6 +53,16 @@ static size_t fun_curl_write_cb(void *ptr, size_t sz, size_t nm, void *ud) { b->d[b->n] = '\0'; return add; } + +/** + * @brief libcurl write callback that writes directly to a FILE*. + * + * @param ptr Pointer to incoming data. + * @param sz Size of each element. + * @param nm Number of elements. + * @param ud User data; must be a FILE* opened for writing in binary mode. + * @return Number of elements written (as returned by fwrite). + */ static size_t fun_curl_file_write_cb(void *ptr, size_t sz, size_t nm, void *ud) { FILE *f = (FILE *)ud; return fwrite(ptr, sz, nm, f); diff --git a/src/extensions/ini.c b/src/extensions/ini.c index 6dc40d3..56b8153 100644 --- a/src/extensions/ini.c +++ b/src/extensions/ini.c @@ -1,12 +1,18 @@ -/* +/** * 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 + */ + +/** + * @file ini.c + * @brief INI parsing helpers and VM opcode support via iniparser. * - * Added: 2025-12-11 (2025-12-11 migrated from src/vm.c) + * Provides includes and declarations required for INI-related opcodes when + * FUN_WITH_INI is enabled at build time. */ #ifdef FUN_WITH_INI diff --git a/src/extensions/json.c b/src/extensions/json.c index a9f6685..1c70d0e 100644 --- a/src/extensions/json.c +++ b/src/extensions/json.c @@ -1,12 +1,19 @@ -/* +/** * 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 + */ + +/** + * @file json.c + * @brief JSON extension helpers and VM opcode cases (conditional build). * - * Added: 2025-12-11 (2025-12-11 migrated from src/vm.c) + * Provides conversions between json-c objects and the Fun Value type and + * supplies VM opcode case implementations when FUN_WITH_JSON is enabled. + * This unit may be included or compiled conditionally based on build flags. */ /* json-c helpers and VM opcode cases (included from vm.c) */ @@ -19,6 +26,23 @@ #include /* --- Conversion helpers between json-c and Fun Value --- */ +/** + * @brief Convert a json-c object into a Fun Value. + * + * Maps json-c primitive and compound types to the closest Fun Value + * representation. + * - null -> Nil + * - boolean -> Bool + * - number (int/double) -> Int/Float + * - string -> String + * - array -> Array (recursively converted) + * - object -> Map (values recursively converted) + * + * @param j Pointer to a json_object; may be NULL. + * @return A Value representing the converted JSON data. Ownership of the + * returned Value belongs to the caller and must be freed with + * free_value() when no longer needed. + */ static Value json_to_fun(json_object *j) { if (!j) return make_nil(); enum json_type t = json_object_get_type(j); @@ -62,6 +86,16 @@ static Value json_to_fun(json_object *j) { } } +/** + * @brief Convert a Fun Value into a json-c object. + * + * Produces a newly-allocated json_object tree representing the supplied + * Value. Unsupported Fun types are stringified using a placeholder. + * + * @param v Pointer to the source Value. Must not be NULL. + * @return Newly created json_object* on success. The caller owns the + * returned object and must release it with json_object_put(). + */ static json_object *fun_to_json(const Value *v) { switch (v->type) { case VAL_NIL: diff --git a/src/extensions/openssl.c b/src/extensions/openssl.c index d2c6988..ee347f9 100644 --- a/src/extensions/openssl.c +++ b/src/extensions/openssl.c @@ -1,12 +1,20 @@ -/* +/** * 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 + */ + +/** + * @file openssl.c + * @brief OpenSSL-based hashing helpers used by crypto-related opcodes. * - * Added: 2026-02-19 + * Provides thin wrappers around OpenSSL EVP routines to compute common + * digests (MD5, RIPEMD-160, SHA-256, SHA-512). When FUN_WITH_OPENSSL is + * disabled, these helpers fall back to returning empty strings to keep the + * VM behavior consistent. */ /* @@ -23,10 +31,16 @@ int EVP_MD_get_size(const EVP_MD *md); #endif #include -/* Compute MD5 hex of input buffer; returns malloc'ed C string (lowercase hex). - * Caller must free(). Returns NULL on failure. When OpenSSL is disabled, - * returns an allocated empty string ("") to keep behavior consistent with - * other optional extensions. */ +/** + * @brief Compute MD5 digest and return it as a lowercase hex string. + * + * @param data Pointer to input bytes (may be NULL if len==0). + * @param len Number of input bytes. + * @return Newly-allocated NUL-terminated hex string on success; NULL on + * failure. When FUN_WITH_OPENSSL is disabled, returns an allocated + * empty string to keep behavior consistent. + * @note The caller owns the returned buffer and must free() it. + */ static char *fun_openssl_md5_hex(const unsigned char *data, size_t len) { static const char hexdig[] = "0123456789abcdef"; if (!data && len != 0) return NULL; @@ -68,8 +82,15 @@ static char *fun_openssl_md5_hex(const unsigned char *data, size_t len) { #endif } -/* Compute SHA-256 hex of input buffer; returns malloc'ed C string (lowercase hex). - * Fallback when OpenSSL disabled: empty string. */ +/** + * @brief Compute SHA-256 digest and return it as a lowercase hex string. + * + * @param data Pointer to input bytes (may be NULL if len==0). + * @param len Number of input bytes. + * @return Newly-allocated hex string on success; NULL on failure. When + * FUN_WITH_OPENSSL is disabled, returns an allocated empty string. + * @note The caller must free() the returned buffer. + */ static char *fun_openssl_sha256_hex(const unsigned char *data, size_t len) { static const char hexdig[] = "0123456789abcdef"; if (!data && len != 0) return NULL; @@ -110,8 +131,15 @@ static char *fun_openssl_sha256_hex(const unsigned char *data, size_t len) { #endif } -/* Compute SHA-512 hex of input buffer; returns malloc'ed C string (lowercase hex). - * Fallback when OpenSSL disabled: empty string. */ +/** + * @brief Compute SHA-512 digest and return it as a lowercase hex string. + * + * @param data Pointer to input bytes (may be NULL if len==0). + * @param len Number of input bytes. + * @return Newly-allocated hex string on success; NULL on failure. When + * FUN_WITH_OPENSSL is disabled, returns an allocated empty string. + * @note The caller must free() the returned buffer. + */ static char *fun_openssl_sha512_hex(const unsigned char *data, size_t len) { static const char hexdig[] = "0123456789abcdef"; if (!data && len != 0) return NULL; @@ -152,10 +180,19 @@ static char *fun_openssl_sha512_hex(const unsigned char *data, size_t len) { #endif } -/* Compute RIPEMD-160 hex of input buffer; returns malloc'ed C string (lowercase hex). - * Note: On OpenSSL 3.x, RIPEMD160 may require the legacy provider; if unavailable, - * EVP_ripemd160() can return NULL. In that case we return NULL and the VM opcode - * will fall back to empty string behavior. When OpenSSL is disabled, return empty string. */ +/** + * @brief Compute RIPEMD-160 digest and return it as a lowercase hex string. + * + * On some OpenSSL builds (e.g., 3.x without legacy provider), RIPEMD-160 may + * be unavailable and EVP_ripemd160() can return NULL. + * + * @param data Pointer to input bytes (may be NULL if len==0). + * @param len Number of input bytes. + * @return Newly-allocated hex string on success; NULL if the digest is + * unavailable or another error occurs. When FUN_WITH_OPENSSL is + * disabled, returns an allocated empty string. + * @note The caller must free() the returned buffer. + */ static char *fun_openssl_ripemd160_hex(const unsigned char *data, size_t len) { static const char hexdig[] = "0123456789abcdef"; if (!data && len != 0) return NULL; diff --git a/src/extensions/pcre2.c b/src/extensions/pcre2.c index 0e2f2fa..8ebc739 100644 --- a/src/extensions/pcre2.c +++ b/src/extensions/pcre2.c @@ -1,12 +1,18 @@ -/* +/** * 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 + */ + +/** + * @file pcre2.c + * @brief PCRE2 configuration header and includes for regex-related opcodes. * - * Added: 2025-12-11 (2025-12-11 migrated from src/vm/libsql/common.c) + * Ensures PCRE2 code unit width is defined consistently prior to including + * when FUN_WITH_PCRE2 is enabled. */ /* Ensure PCRE2 is configured consistently across the whole translation unit. diff --git a/src/extensions/pcsc.c b/src/extensions/pcsc.c index 9508cc9..8d535d9 100644 --- a/src/extensions/pcsc.c +++ b/src/extensions/pcsc.c @@ -1,18 +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-11 (2025-12-11 migrated from src/vm.c) */ -/* -PCSC helpers: registries and helper functions. -Included the file scope from vm.c. -*/ +/** + * @file pcsc.c + * @brief PC/SC smartcard helper registries and lookup utilities. + * + * Provides small fixed-size registries for PC/SC contexts and card handles, + * plus allocation and lookup helpers used by VM opcodes when FUN_WITH_PCSC + * is enabled. + */ #ifdef FUN_WITH_PCSC #if defined(__has_include) @@ -44,6 +46,11 @@ typedef struct { static pcsc_ctx_entry g_pcsc_ctx[8]; static pcsc_card_entry g_pcsc_card[32]; +/** + * @brief Allocate a free context slot in the PC/SC registry. + * + * @return A 1-based slot id on success, or 0 if no free slot is available. + */ static int pcsc_alloc_ctx_slot(void) { for (int i = 0; i < (int)(sizeof(g_pcsc_ctx) / sizeof(g_pcsc_ctx[0])); ++i) { if (!g_pcsc_ctx[i].in_use) { @@ -55,6 +62,11 @@ static int pcsc_alloc_ctx_slot(void) { return 0; } +/** + * @brief Allocate a free card slot in the PC/SC registry. + * + * @return A 1-based slot id on success, or 0 if no free slot is available. + */ static int pcsc_alloc_card_slot(void) { for (int i = 0; i < (int)(sizeof(g_pcsc_card) / sizeof(g_pcsc_card[0])); ++i) { if (!g_pcsc_card[i].in_use) { @@ -67,6 +79,12 @@ static int pcsc_alloc_card_slot(void) { return 0; } +/** + * @brief Lookup a context slot by id. + * + * @param id 1-based context id previously returned by pcsc_alloc_ctx_slot(). + * @return Pointer to the registry entry if valid and in use; NULL otherwise. + */ static pcsc_ctx_entry *pcsc_get_ctx(int id) { if (id <= 0) return NULL; int idx = id - 1; @@ -75,6 +93,12 @@ static pcsc_ctx_entry *pcsc_get_ctx(int id) { return &g_pcsc_ctx[idx]; } +/** + * @brief Lookup a card slot by id. + * + * @param id 1-based card id previously returned by pcsc_alloc_card_slot(). + * @return Pointer to the registry entry if valid and in use; NULL otherwise. + */ static pcsc_card_entry *pcsc_get_card(int id) { if (id <= 0) return NULL; int idx = id - 1; diff --git a/src/extensions/sqlite.c b/src/extensions/sqlite.c index 90dc332..961098e 100644 --- a/src/extensions/sqlite.c +++ b/src/extensions/sqlite.c @@ -1,29 +1,56 @@ -/* +/** * 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-11 (2025-12-11 migrated from src/vm/sqlite/common.c) */ /** - * SQLite handle registry and helpers + * @file sqlite.c + * @brief SQLite handle registry and helper utilities for the Fun VM extension. + * + * This module provides a very small registry for SQLite database handles when + * the project is compiled with FUN_WITH_SQLITE. It assigns small integer + * identifiers to opened sqlite3 connections and allows retrieval or removal of + * those entries. The registry itself does not open or close SQLite databases — + * it only stores pointers provided by the caller. + * + * Thread-safety: This registry is not thread-safe. Callers must ensure + * external synchronization if used from multiple threads. */ #ifdef FUN_WITH_SQLITE #include +/** + * @brief Node in a singly-linked list of registered SQLite handles. + */ typedef struct SqlHandle { int id; sqlite3 *db; struct SqlHandle *next; } SqlHandle; +/** Global head of the SQLite handle list. */ static SqlHandle *g_sql_handles = NULL; +/** Next positive identifier to assign to a newly added handle. */ static int g_sql_next_id = 1; +/** + * @brief Add a sqlite3 handle to the registry. + * + * The function allocates a new list node, assigns a fresh positive id, and + * prepends it to the internal registry list. + * + * @param db Valid pointer to an opened sqlite3 connection. + * @return Pointer to the newly created SqlHandle entry on success; NULL on + * allocation failure. The returned pointer remains owned by the + * registry; do not free it directly. + * @note This function does not take ownership of the sqlite3 connection in the + * sense of closing it; removal from the registry will not call + * sqlite3_close(). + */ static SqlHandle *sql_reg_add(sqlite3 *db) { SqlHandle *h = (SqlHandle *)calloc(1, sizeof(SqlHandle)); if (!h) return NULL; @@ -34,12 +61,27 @@ static SqlHandle *sql_reg_add(sqlite3 *db) { return h; } +/** + * @brief Look up a registered SQLite handle by id. + * + * @param id Positive identifier previously returned by sql_reg_add(). + * @return Pointer to the SqlHandle entry if found; NULL otherwise. + */ static SqlHandle *sql_reg_get(int id) { for (SqlHandle *p = g_sql_handles; p; p = p->next) if (p->id == id) return p; return NULL; } +/** + * @brief Remove a SQLite handle entry from the registry. + * + * Deletes the list node associated with the given id. + * + * @param id Positive identifier of the entry to remove. + * @note This function does not close the underlying sqlite3 connection; the + * caller is responsible for calling sqlite3_close() if appropriate. + */ static void sql_reg_del(int id) { SqlHandle **pp = &g_sql_handles; while (*pp) { diff --git a/src/extensions/xml2.c b/src/extensions/xml2.c index 34e9563..5229917 100644 --- a/src/extensions/xml2.c +++ b/src/extensions/xml2.c @@ -1,22 +1,48 @@ -/* +/** * 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-11 (2025-12-11 migrated from src/vm/libsql/common.c) */ +/** + * @file xml2.c + * @brief Lightweight libxml2 handle registry for Fun VM extension helpers. + * + * This module provides small fixed-size registries for libxml2 objects when + * compiled with FUN_WITH_XML2. Documents and nodes can be associated with + * small integer handles to make them easier to pass around inside the + * interpreter and its C API boundaries. The document registry owns the + * underlying xmlDoc, while the node registry does not own the xmlNode — nodes + * are freed when their owning document is freed. + * + * Limits: The registries are fixed-size (docs: 64, nodes: 256). Handle value 0 + * is reserved and indicates failure/invalid. + * + * Thread-safety: Not thread-safe. Coordinate access externally if needed. + */ #ifdef FUN_WITH_XML2 #include #include +/** + * @brief Slot describing a registered XML document. + * + * The registry owns the xmlDocPtr and will free it when the handle is + * released via xml_doc_free_handle(). + */ typedef struct { xmlDocPtr doc; int in_use; } XmlDocSlot; +/** + * @brief Slot describing a registered XML node. + * + * The registry does not own the node; it is managed by its document. Releasing + * a node handle does not free the node memory. + */ typedef struct { xmlNodePtr node; int in_use; @@ -25,6 +51,14 @@ typedef struct { static XmlDocSlot g_xml_docs[64]; static XmlNodeSlot g_xml_nodes[256]; +/** + * @brief Allocate a document handle for the given xmlDoc pointer. + * + * @param d Valid xmlDocPtr to register. Ownership is transferred to the + * registry, which will xmlFreeDoc() it when the handle is freed. + * @return Positive handle in the range [1, 63] on success; 0 if no slot is + * available. + */ static int xml_doc_alloc(xmlDocPtr d) { for (int i = 1; i < (int)(sizeof(g_xml_docs) / sizeof(g_xml_docs[0])); ++i) { if (!g_xml_docs[i].in_use) { @@ -35,10 +69,22 @@ static int xml_doc_alloc(xmlDocPtr d) { } return 0; } +/** + * @brief Retrieve a registered xmlDoc by handle. + * + * @param h Handle previously returned by xml_doc_alloc(). + * @return xmlDocPtr if the handle is valid and in use; NULL otherwise. + */ static xmlDocPtr xml_doc_get(int h) { if (h > 0 && h < (int)(sizeof(g_xml_docs) / sizeof(g_xml_docs[0])) && g_xml_docs[h].in_use) return g_xml_docs[h].doc; return NULL; } +/** + * @brief Free a document handle and the underlying xmlDoc. + * + * @param h Handle to release. + * @return 1 if the handle was valid and has been released; 0 otherwise. + */ static int xml_doc_free_handle(int h) { if (h <= 0 || h >= (int)(sizeof(g_xml_docs) / sizeof(g_xml_docs[0])) || !g_xml_docs[h].in_use) return 0; if (g_xml_docs[h].doc) xmlFreeDoc(g_xml_docs[h].doc); @@ -47,6 +93,14 @@ static int xml_doc_free_handle(int h) { return 1; } +/** + * @brief Allocate a node handle for the given xmlNode pointer. + * + * @param n Valid xmlNodePtr to register. Ownership is NOT transferred; + * nodes are managed by their owning document. + * @return Positive handle in the range [1, 255] on success; 0 if no slot is + * available. + */ static int xml_node_alloc(xmlNodePtr n) { for (int i = 1; i < (int)(sizeof(g_xml_nodes) / sizeof(g_xml_nodes[0])); ++i) { if (!g_xml_nodes[i].in_use) { @@ -57,10 +111,25 @@ static int xml_node_alloc(xmlNodePtr n) { } return 0; } +/** + * @brief Retrieve a registered xmlNode by handle. + * + * @param h Handle previously returned by xml_node_alloc(). + * @return xmlNodePtr if the handle is valid and in use; NULL otherwise. + */ static xmlNodePtr xml_node_get(int h) { if (h > 0 && h < (int)(sizeof(g_xml_nodes) / sizeof(g_xml_nodes[0])) && g_xml_nodes[h].in_use) return g_xml_nodes[h].node; return NULL; } +/** + * @brief Free a node handle without freeing the underlying node. + * + * Nodes are owned by their document; releasing the document invalidates any + * associated node handles. + * + * @param h Handle to release. + * @return 1 if the handle was valid and has been released; 0 otherwise. + */ static int xml_node_free_handle(int h) { if (h <= 0 || h >= (int)(sizeof(g_xml_nodes) / sizeof(g_xml_nodes[0])) || !g_xml_nodes[h].in_use) return 0; /* nodes are owned by their document; do not free here */ diff --git a/src/fun.c b/src/fun.c index 22f5587..a829a37 100644 --- a/src/fun.c +++ b/src/fun.c @@ -1,4 +1,4 @@ -/* +/** * This file is part of the Fun programming language. * https://fun-lang.xyz/ * @@ -7,7 +7,10 @@ * https://opensource.org/license/apache-2-0 */ -/* +/** + * @file fun.c + * @brief Command-line interface and entry point for the Fun interpreter. + * * Main entry point for the Fun language interpreter. * Builds a CLI that runs a script file if provided; otherwise starts the REPL * when compiled with FUN_WITH_REPL enabled. @@ -29,6 +32,14 @@ #define FUN_VERSION "0.0.0-dev" #endif +/** + * @brief Print command-line usage instructions to stdout. + * + * The usage varies depending on whether the binary was built with + * FUN_WITH_REPL enabled. + * + * @param prog Program name/path used in usage lines. May be NULL. + */ static void print_usage(const char *prog) { printf("Fun %s\n", FUN_VERSION); printf("Usage:\n"); @@ -51,6 +62,17 @@ static void print_usage(const char *prog) { #endif } +/** + * @brief Program entry point for the Fun interpreter. + * + * Parses CLI options, compiles and runs a script file if provided, or launches + * the REPL when enabled and no script is given. Exposes script arguments to + * the program via FUN_ARGC/FUN_ARGV_i/FUN_ARGS environment variables. + * + * @param argc Argument count. + * @param argv Argument vector; argv[0] is used to expose FUN_EXECUTABLE. + * @return Process exit code, typically taken from the VM after execution. + */ int main(int argc, char **argv) { /* Set FUN_EXECUTABLE environment variable to the path of this binary */ setenv("FUN_EXECUTABLE", argv[0], 1); diff --git a/src/fun_test.c b/src/fun_test.c index fbf8765..a721627 100644 --- a/src/fun_test.c +++ b/src/fun_test.c @@ -7,12 +7,23 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file fun_test.c + * @brief Minimal bytecode-level test harness for core VM operations. + */ + #include "bytecode.h" #include "value.h" #include "vm.h" #include #include +/** + * @brief Assert that a Value is an integer equal to the expected number. + * + * Prints a diagnostic and returns 1 from main on failure; used in quick + * sanity checks when extending this test harness. + */ #define ASSERT_EQ(val, expected) \ if ((val).type != VAL_INT || (val).i != (expected)) { \ fprintf(stderr, "Assertion failed: expected %lld, got ", (long long)(expected)); \ @@ -21,6 +32,15 @@ return 1; \ } +/** + * @brief Build and execute a demo bytecode program and print results. + * + * Exercises arithmetic, comparisons, logic, stack ops, rounding and + * transcendental functions, integer math helpers, and fmin/fmax semantics. + * Intended for manual inspection rather than strict unit testing. + * + * @return 0 on successful execution. + */ int main(void) { VM vm; vm_init(&vm); diff --git a/src/funstx.c b/src/funstx.c index bdfb122..9cd9910 100644 --- a/src/funstx.c +++ b/src/funstx.c @@ -1,4 +1,4 @@ -/* +/** * This file is part of the Fun programming language. * https://fun-lang.xyz/ * @@ -7,6 +7,11 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file funstx.c + * @brief Syntax checker and auto-fixer tool for Fun source files. + */ + #include #include #include @@ -15,11 +20,24 @@ #include "bytecode.h" #include "parser.h" +/** + * @brief Print command-line usage for the funstx tool to stderr. + * @param prog Program name (argv[0]). May be NULL. + */ static void usage(const char *prog) { fprintf(stderr, "Usage: %s [--fix] [--quiet] [file2.fun ...]\n", prog); } -/* Read entire file into a malloc'd buffer terminated with '\0'. Returns 1 on success. */ +/** + * @brief Read entire file into a newly allocated buffer. + * + * The returned buffer is NUL-terminated for convenience. Caller must free it. + * + * @param path Path to file to read. + * @param out_buf Output pointer to receive malloc'd buffer. + * @param out_len Output length of the file (without the terminating NUL). + * @return 1 on success, 0 on failure. + */ static int read_all(const char *path, char **out_buf, size_t *out_len) { *out_buf = NULL; *out_len = 0; @@ -51,7 +69,14 @@ static int read_all(const char *path, char **out_buf, size_t *out_len) { return 1; } -/* Write buffer to file atomically-ish (overwrite). */ +/** + * @brief Overwrite a file with the provided buffer. + * + * @param path Target file path. + * @param buf Buffer to write. + * @param len Number of bytes to write. + * @return 1 on success, 0 otherwise. + */ static int write_all(const char *path, const char *buf, size_t len) { FILE *f = fopen(path, "wb"); if (!f) return 0; @@ -61,14 +86,32 @@ static int write_all(const char *path, const char *buf, size_t len) { return ok; } -/* Check if c is a word constituent (identifier char) */ +/** + * @brief Determine whether a character is an identifier constituent. + * @param c Character code (unsigned char promoted to int). + * @return Non-zero if c is '_' or an alphanumeric character. + */ static int is_word(int c) { return (c == '_' || isalnum(c)); } -/* Apply auto-fixes to the given source text. Returns newly malloc'd buffer and new length. - * Idempotent, focuses on parser-related constraints: 2-space indents, no tab indents, - * CRLF->LF, trim trailing spaces, ensure final newline, normalize 'sint*' to 'int*'. +/** + * @brief Apply conservative style/syntax auto-fixes to a Fun source buffer. + * + * Fixes include: + * - Normalize indentation to 2 spaces (tabs become 2 spaces per tab) + * - Convert CRLF/CR line endings to LF + * - Trim trailing spaces on each line + * - Ensure the file ends with a single LF + * - Normalize identifiers 'sint8/16/32/64' to 'int8/16/32/64' at word boundaries + * + * Returns a newly allocated buffer containing the fixed content and writes the + * resulting length to out_len. Caller must free the returned buffer. + * + * @param src Input buffer. + * @param len Input length. + * @param out_len Output length of fixed buffer. + * @return Newly malloc'd fixed buffer on success, or NULL on OOM. */ static char *apply_fixes(const char *src, size_t len, size_t *out_len) { /* First pass: normalize line endings to LF and compute an upper bound size */ @@ -242,6 +285,17 @@ static char *apply_fixes(const char *src, size_t len, size_t *out_len) { return out; } +/** + * @brief Entry point for funstx. + * + * Parses flags, optionally applies in-place fixes (--fix), and validates each + * provided Fun source file by attempting to parse it. With --quiet, only + * errors are printed; otherwise, prints "OK" for valid files. + * + * @param argc Argument count. + * @param argv Argument vector. + * @return 0 if all files are valid (and fixes succeeded if requested), non-zero otherwise. + */ int main(int argc, char **argv) { int do_fix = 0; int quiet = 0; diff --git a/src/iter.c b/src/iter.c index dc20dfc..a888a02 100644 --- a/src/iter.c +++ b/src/iter.c @@ -7,10 +7,26 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file iter.c + * @brief Iterator-style helpers exposed as built-ins (enumerate, zip). + */ + #include "value.h" #include -/* enumerate(arr) -> [[0, v0], [1, v1], ...] */ +/** + * @brief Build an array of [index, value] pairs from an input array. + * + * For an input array [v0, v1, ...], returns [[0, v0], [1, v1], ...]. + * Non-array inputs are treated as empty arrays by array_length/array_get_copy. + * + * Ownership: The returned Value owns its internal array; caller must free it + * with free_value(). + * + * @param arr Input array Value. + * @return An array Value of length equal to the input's length. + */ Value bi_enumerate(const Value *arr) { int n = array_length(arr); if (n <= 0) return make_array_from_values(NULL, 0); @@ -34,7 +50,19 @@ Value bi_enumerate(const Value *arr) { return out; } -/* zip(a, b) -> [[a0,b0], [a1,b1], ...] up to min(len(a),len(b)) */ +/** + * @brief Zip two arrays into an array of pairs up to the shorter length. + * + * For inputs a=[a0,a1,...], b=[b0,b1,...], returns [[a0,b0],[a1,b1], ...] + * with length min(len(a), len(b)). + * + * Ownership: The returned Value owns its internal array; caller must free it + * with free_value(). + * + * @param a First input array Value. + * @param b Second input array Value. + * @return Array of pairs as a Value. + */ Value bi_zip(const Value *a, const Value *b) { int na = array_length(a); int nb = array_length(b); diff --git a/src/map.c b/src/map.c index b74b7d0..3b6b1fb 100644 --- a/src/map.c +++ b/src/map.c @@ -7,6 +7,11 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file map.c + * @brief Simple string-keyed map implementation backing VAL_MAP Values. + */ + #include "value.h" #include #include @@ -20,6 +25,13 @@ typedef struct Map { Value *vals; /* each value owned here */ } Map; +/** + * @brief Construct a new empty map Value. + * + * Allocates an internal Map structure with refcount=1 and zero capacity. + * + * @return A Value of type VAL_MAP on success, or VAL_NIL on allocation failure. + */ Value make_map_empty(void) { Map *m = (Map *)malloc(sizeof(Map)); if (!m) return make_nil(); @@ -34,6 +46,12 @@ Value make_map_empty(void) { return v; } +/** + * @brief Ensure the map has capacity for at least need elements. + * @param m Internal map pointer (must not be NULL). + * @param need Required capacity. + * @return 1 on success, 0 on allocation failure. + */ static int map_ensure_cap(Map *m, int need) { if (m->cap >= need) return 1; int ncap = m->cap == 0 ? 4 : m->cap * 2; @@ -48,6 +66,16 @@ static int map_ensure_cap(Map *m, int need) { return 1; } +/** + * @brief Insert or replace a key in the map. + * + * On success, ownership of v transfers into the map. On failure, v is freed. + * + * @param vm Target Value of type VAL_MAP. + * @param key NUL-terminated key string (copied into the map). + * @param v Value to store; consumed on success. + * @return 1 on success, 0 on error (type mismatch, OOM, or NULL params). + */ int map_set(Value *vm, const char *key, Value v) { if (!vm || vm->type != VAL_MAP || !vm->map || !key) { free_value(v); @@ -71,6 +99,16 @@ int map_set(Value *vm, const char *key, Value v) { return 1; } +/** + * @brief Look up a key and copy the stored value into out. + * + * The returned value is a deep copy; caller owns it and must free it. + * + * @param vm Source map Value (VAL_MAP). + * @param key Key to search for. + * @param out Output pointer to receive a copy; may be NULL to only test presence. + * @return 1 if found (and out filled if non-NULL), 0 otherwise. + */ int map_get_copy(const Value *vm, const char *key, Value *out) { if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0; Map *m = (Map *)vm->map; @@ -83,6 +121,12 @@ int map_get_copy(const Value *vm, const char *key, Value *out) { return 0; } +/** + * @brief Check whether the map contains the specified key. + * @param vm Map Value (VAL_MAP). + * @param key Key to search for. + * @return 1 if present, 0 if absent or on invalid input. + */ int map_has(const Value *vm, const char *key) { if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0; Map *m = (Map *)vm->map; @@ -92,6 +136,14 @@ int map_has(const Value *vm, const char *key) { return 0; } +/** + * @brief Return all map keys as an array of strings. + * + * Ownership: Caller must free the returned Value with free_value(). + * + * @param vm Map Value (VAL_MAP). + * @return Array Value of keys; empty array if vm is not a map or is empty. + */ Value map_keys_array(const Value *vm) { if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0); Map *m = (Map *)vm->map; @@ -108,6 +160,14 @@ Value map_keys_array(const Value *vm) { return arr; } +/** + * @brief Return all map values as an array (deep-copied). + * + * Ownership: Caller must free the returned Value with free_value(). + * + * @param vm Map Value (VAL_MAP). + * @return Array Value of values; empty array if vm is not a map or is empty. + */ Value map_values_array(const Value *vm) { if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0); Map *m = (Map *)vm->map; diff --git a/src/parser.c b/src/parser.c index 6ec6a9d..11fe86b 100644 --- a/src/parser.c +++ b/src/parser.c @@ -72,6 +72,15 @@ static int g_err_col = 0; static int g_temp_counter = 0; /* ---- runtime debug control (for suppressing noisy stdout in production/CGI) ---- */ +/** + * @brief Interpret an environment variable as a boolean flag. + * + * Recognizes common truthy forms: 1, true/TRUE, yes/YES, on/ON. + * Any other value (including unset) is considered false. + * + * @param name Environment variable name (must not be NULL). + * @return 1 if the variable is set to a recognized truthy value, 0 otherwise. + */ static int env_truthy(const char *name) { const char *v = getenv(name); if (!v) return 0; @@ -85,6 +94,14 @@ static int env_truthy(const char *name) { return 0; } +/** + * @brief Determine whether parser/compiler debug tracing is enabled. + * + * Debugging can be enabled by setting either FUN_TRACE or FUN_DEBUG to a + * truthy environment value (see env_truthy). + * + * @return 1 if debug is enabled, 0 otherwise. + */ static int fun_debug_enabled(void) { /* Allow enabling debug dumps at runtime via environment. * Default is OFF to avoid contaminating stdout (e.g., CGI responses). @@ -99,13 +116,29 @@ static int fun_debug_enabled(void) { positive/negative 8/16/32/64 = integers (negative means signed); TYPE_META_STRING/BOOLEAN/NIL mark non-integer enforced types; TYPE_META_CLASS marks class instances (Map with "__class"). */ +/** @brief Type metadata tag used for string enforcement in declared types. */ #define TYPE_META_STRING 10001 +/** @brief Type metadata tag used for boolean enforcement in declared types. */ #define TYPE_META_BOOLEAN 10002 +/** @brief Type metadata tag indicating explicit nil type. */ #define TYPE_META_NIL 10003 +/** @brief Type metadata tag marking class/instance values. */ #define TYPE_META_CLASS 10004 +/** @brief Type metadata tag marking floating point numbers. */ #define TYPE_META_FLOAT 10005 +/** @brief Type metadata tag marking array values. */ #define TYPE_META_ARRAY 10006 +/** + * @brief Record a parser/compiler error at a given source position. + * + * Formats an error message into the parser's global error buffer and stores + * the offending byte position. Line and column are derived later on demand. + * + * @param pos Byte offset in the current (preprocessed) source. + * @param fmt printf-style format string for the message. + * @param ... Arguments matching fmt. + */ static void parser_fail(size_t pos, const char *fmt, ...) { g_has_error = 1; g_err_pos = pos; @@ -115,6 +148,18 @@ static void parser_fail(size_t pos, const char *fmt, ...) { va_end(ap); } +/** + * @brief Compute one-based line and column from a byte position. + * + * Counts newlines up to the smaller of pos and len to derive a human-friendly + * (line, column) pair. Columns are one-based and reset after each newline. + * + * @param src Source buffer. + * @param len Source length in bytes. + * @param pos Target byte position within the source. + * @param out_line Optional out parameter for the computed line. + * @param out_col Optional out parameter for the computed column. + */ static void calc_line_col(const char *src, size_t len, size_t pos, int *out_line, int *out_col) { int line = 1, col = 1; size_t limit = pos < len ? pos : len; @@ -138,6 +183,9 @@ static void calc_line_col(const char *src, size_t len, size_t pos, int *out_line static char *g_ns_aliases[64]; static int g_ns_alias_count = 0; +/** + * @brief Reset and free all tracked namespace aliases. + */ static void ns_aliases_reset(void) { for (int i = 0; i < g_ns_alias_count; ++i) { free(g_ns_aliases[i]); @@ -146,6 +194,16 @@ static void ns_aliases_reset(void) { g_ns_alias_count = 0; } +/** + * @brief Scan preprocessed source for namespace alias markers. + * + * Looks for lines starting with "// __ns_alias__: " and stores + * so that member-style calls on that identifier are parsed as plain calls + * (no implicit receiver). + * + * @param src Preprocessed source buffer. + * @param len Length of src in bytes. + */ static void ns_aliases_scan(const char *src, size_t len) { const char *marker = "// __ns_alias__: "; size_t mlen = strlen(marker); @@ -184,6 +242,12 @@ static void ns_aliases_scan(const char *src, size_t len) { } } +/** + * @brief Check whether an identifier is a registered namespace alias. + * + * @param name Identifier to test. + * @return 1 if name is a known alias, 0 otherwise. + */ static int is_ns_alias(const char *name) { if (!name) return 0; for (int i = 0; i < g_ns_alias_count; ++i) { @@ -202,6 +266,12 @@ static struct { int count; } G = {{0}, {0}, {0}, 0}; +/** + * @brief Find a global symbol index by name. + * + * @param name Symbol name. + * @return Index in the global table, or -1 if not found. + */ static int sym_find(const char *name) { for (int i = 0; i < G.count; ++i) { if (strcmp(G.names[i], name) == 0) return i; @@ -209,6 +279,16 @@ static int sym_find(const char *name) { return -1; } +/** + * @brief Get or create a global symbol index for a name. + * + * Ensures the symbol exists in the global table, creating a new entry with + * default metadata when absent. + * + * @param name Symbol name. + * @return Index of the symbol (>=0). Returns 0 after reporting an error if + * the table is full. + */ static int sym_index(const char *name) { int existing = sym_find(name); if (existing >= 0) return existing; @@ -235,6 +315,14 @@ static LocalEnv *g_locals = NULL; static LocalEnv *g_func_env_stack[64]; static int g_func_env_depth = 0; /* number of valid outer env entries */ +/** + * @brief Check whether a local name exists in any outer function environment. + * + * Used to enforce no-capture semantics for nested functions. + * + * @param name Local identifier to search for. + * @return 1 if present in any outer environment, 0 otherwise. + */ static int name_in_outer_envs(const char *name) { if (g_func_env_depth <= 0) return 0; for (int d = g_func_env_depth - 1; d >= 0; --d) { @@ -258,6 +346,12 @@ typedef struct LoopCtx { static LoopCtx *g_loop_ctx = NULL; +/** + * @brief Find the index of a local variable in the current function. + * + * @param name Local identifier to look up. + * @return Zero-based local index, or -1 if not found or outside a function. + */ static int local_find(const char *name) { if (!g_locals) return -1; for (int i = 0; i < g_locals->count; ++i) { @@ -266,6 +360,12 @@ static int local_find(const char *name) { return -1; } +/** + * @brief Add a new local variable to the current function environment. + * + * @param name Local identifier to define. + * @return The assigned local index, or -1 if no function env exists or limit exceeded. + */ static int local_add(const char *name) { if (!g_locals) return -1; if (g_locals->count >= MAX_FRAME_LOCALS) { @@ -281,6 +381,18 @@ static int local_add(const char *name) { static int emit_expression(Bytecode *bc, const char *src, size_t len, size_t *pos); /* primary: (expr) | string | number | true/false | identifier */ +/** + * @brief Parse and emit bytecode for primary expressions. + * + * Handles literals, identifiers, parenthesized expressions, function literals, + * array/map literals, indexing, calls, member access, and related constructs. + * + * @param bc Target bytecode under construction. + * @param src Source buffer. + * @param len Source length in bytes. + * @param pos In/out byte position pointer; advanced past the parsed primary. + * @return 1 on success, 0 on parse error. + */ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) { skip_spaces(src, len, pos); @@ -4301,7 +4413,18 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos) return 0; } -/* unary: '!' unary | '-' unary | primary */ +/** + * @brief Parse and emit unary expressions. + * + * Supports logical not (!) and unary minus (-) with right associativity, + * falling back to primary expressions. + * + * @param bc Target bytecode. + * @param src Source buffer. + * @param len Source length. + * @param pos In/out byte position pointer. + * @return 1 on success, 0 on error. + */ static int emit_unary(Bytecode *bc, const char *src, size_t len, size_t *pos) { skip_spaces(src, len, pos); if (*pos < len && src[*pos] == '!') { @@ -4328,7 +4451,17 @@ static int emit_unary(Bytecode *bc, const char *src, size_t len, size_t *pos) { return emit_primary(bc, src, len, pos); } -/* multiplicative: unary (('*' | '/' | '%') unary)* */ +/** + * @brief Parse and emit multiplicative expressions. + * + * Grammar: unary (('*' | '/' | '%') unary)* + * + * @param bc Target bytecode. + * @param src Source buffer. + * @param len Source length. + * @param pos In/out byte position pointer. + * @return 1 on success, 0 on error. + */ static int emit_multiplicative(Bytecode *bc, const char *src, size_t len, size_t *pos) { if (!emit_unary(bc, src, len, pos)) return 0; for (;;) { @@ -4381,7 +4514,17 @@ static int emit_multiplicative(Bytecode *bc, const char *src, size_t len, size_t return 1; } -/* additive: multiplicative (('+' | '-') multiplicative)* */ +/** + * @brief Parse and emit additive expressions. + * + * Grammar: multiplicative (('+' | '-') multiplicative)* + * + * @param bc Target bytecode. + * @param src Source buffer. + * @param len Source length. + * @param pos In/out byte position pointer. + * @return 1 on success, 0 on error. + */ static int emit_additive(Bytecode *bc, const char *src, size_t len, size_t *pos) { if (!emit_multiplicative(bc, src, len, pos)) return 0; for (;;) { @@ -4409,7 +4552,17 @@ static int emit_additive(Bytecode *bc, const char *src, size_t len, size_t *pos) return 1; } -/* relational: additive (('<' | '<=' | '>' | '>=') additive)* */ +/** + * @brief Parse and emit relational expressions. + * + * Grammar: additive (('<' | '<=' | '>' | '>=') additive)* + * + * @param bc Target bytecode. + * @param src Source buffer. + * @param len Source length. + * @param pos In/out byte position pointer. + * @return 1 on success, 0 on error. + */ static int emit_relational(Bytecode *bc, const char *src, size_t len, size_t *pos) { if (!emit_additive(bc, src, len, pos)) return 0; for (;;) { @@ -4455,7 +4608,17 @@ static int emit_relational(Bytecode *bc, const char *src, size_t len, size_t *po return 1; } -/* equality: relational (('==' | '!=') relational)* */ +/** + * @brief Parse and emit equality/inequality expressions. + * + * Grammar: relational (('==' | '!=') relational)* + * + * @param bc Target bytecode. + * @param src Source buffer. + * @param len Source length. + * @param pos In/out byte position pointer. + * @return 1 on success, 0 on error. + */ static int emit_equality(Bytecode *bc, const char *src, size_t len, size_t *pos) { if (!emit_relational(bc, src, len, pos)) return 0; for (;;) { @@ -4483,7 +4646,18 @@ static int emit_equality(Bytecode *bc, const char *src, size_t len, size_t *pos) return 1; } -/* logical AND with short-circuit: equality ( '&&' equality )* */ +/** + * @brief Parse and emit logical AND (&&) with short-circuiting. + * + * Evaluates left-to-right, jumping around subsequent operands when a false + * operand is encountered. + * + * @param bc Target bytecode. + * @param src Source buffer. + * @param len Source length. + * @param pos In/out byte position pointer. + * @return 1 on success, 0 on error. + */ static int emit_and_expr(Bytecode *bc, const char *src, size_t len, size_t *pos) { int jf_idxs[64]; int jf_count = 0; @@ -4543,7 +4717,18 @@ static int emit_and_expr(Bytecode *bc, const char *src, size_t len, size_t *pos) return 1; } -/* logical OR with short-circuit: and_expr ( '||' and_expr )* */ +/** + * @brief Parse and emit logical OR (||) with short-circuiting. + * + * Evaluates left-to-right; when a true operand is encountered, remaining + * operands are skipped and the expression yields true. + * + * @param bc Target bytecode. + * @param src Source buffer. + * @param len Source length. + * @param pos In/out byte position pointer. + * @return 1 on success, 0 on error. + */ static int emit_or_expr(Bytecode *bc, const char *src, size_t len, size_t *pos) { int true_jumps[64]; int tj_count = 0; @@ -4604,8 +4789,17 @@ static int emit_or_expr(Bytecode *bc, const char *src, size_t len, size_t *pos) return 1; } -/* conditional operator (ternary) with right associativity: - Parses: logical_or ('?' conditional ':' conditional)? */ +/** + * @brief Parse and emit the ternary conditional operator. + * + * Grammar (right-associative): logical_or ('?' conditional ':' conditional)? + * + * @param bc Target bytecode. + * @param src Source buffer. + * @param len Source length. + * @param pos In/out byte position pointer. + * @return 1 on success, 0 on error. + */ static int emit_conditional(Bytecode *bc, const char *src, size_t len, size_t *pos) { /* parse condition (logical OR precedence or higher) */ if (!emit_or_expr(bc, src, len, pos)) return 0; @@ -4654,6 +4848,19 @@ static int emit_conditional(Bytecode *bc, const char *src, size_t len, size_t *p } /* top-level expression */ +/** + * @brief Parse and emit a full expression using precedence climbing. + * + * Delegates to emit_conditional which handles the highest-level precedence + * (ternary). This serves as the common entry for expression parsing at + * various grammar positions. + * + * @param bc Target bytecode. + * @param src Source buffer. + * @param len Source length in bytes. + * @param pos In/out byte position pointer. + * @return 1 on success, 0 on parse error. + */ static int emit_expression(Bytecode *bc, const char *src, size_t len, size_t *pos) { return emit_conditional(bc, src, len, pos); } @@ -4667,6 +4874,16 @@ static int emit_expression(Bytecode *bc, const char *src, size_t len, size_t *po */ /* line/indent utilities */ +/** + * @brief Advance position to the end of the current line, validating tail. + * + * Skips spaces and inline/block comments until CR/LF/CRLF or end-of-input. + * Reports an error if trailing non-space/comment characters are found. + * + * @param src Source buffer. + * @param len Source length. + * @param pos In/out byte position pointer; set to first char of next line. + */ static void skip_to_eol(const char *src, size_t len, size_t *pos) { /* Strict mode: only allow trailing spaces and comments until end-of-line. */ size_t p = *pos; @@ -4731,6 +4948,30 @@ static void skip_to_eol(const char *src, size_t len, size_t *pos) { } } +/** + * @brief Read the start of a logical line and compute indentation. + * + * Consumes blank lines and comment-only lines. Tabs are forbidden for + * indentation; only spaces are allowed, counted in units of 2. + * + * @param src Source buffer. + * @param len Source length. + * @param pos In/out byte position pointer; advanced to first non-space. + * @param out_indent Receives the number of leading spaces on the line. + * @return 1 if a non-empty logical line was found, 0 if end reached. + */ +/** + * @brief Read the start of a logical line and compute indentation. + * + * Consumes blank lines and comment-only lines. Tabs are forbidden for + * indentation; only spaces are allowed, counted in units of 2. + * + * @param src Source buffer. + * @param len Source length. + * @param pos In/out byte position pointer; advanced to first non-space. + * @param out_indent Receives the number of leading spaces on the line. + * @return 1 if a non-empty logical line was found, 0 if end reached. + */ static int read_line_start(const char *src, size_t len, size_t *pos, int *out_indent) { while (*pos < len) { size_t p = *pos; @@ -4813,6 +5054,17 @@ static int read_line_start(const char *src, size_t len, size_t *pos, int *out_in static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos, int current_indent); /* parse and emit a single simple (non-if) statement on the current line */ +/** + * @brief Parse a single statement on the current line and emit bytecode. + * + * Handles assignments, function calls, control flow starters, print/debug + * statements and other simple constructs that fit on one logical line. + * + * @param bc Target bytecode. + * @param src Source buffer. + * @param len Source length. + * @param pos In/out byte position pointer at start of the statement line. + */ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, size_t *pos) { size_t local_pos = *pos; char *name = NULL; @@ -5665,6 +5917,19 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si } /* parse a block with lines at indentation >= current_indent; stop at dedent */ +/** + * @brief Parse a block of statements at a given indentation level. + * + * Continues parsing lines while their indentation is >= current_indent. On + * dedent, returns control to the caller so upstream constructs (e.g., if/while) + * can close. Inserts OP_LINE markers to improve runtime diagnostics. + * + * @param bc Target bytecode. + * @param src Source buffer. + * @param len Source length. + * @param pos In/out byte position pointer. + * @param current_indent Indentation level (spaces) of the enclosing block. + */ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos, int current_indent) { while (*pos < len) { if (g_has_error) return; @@ -7219,6 +7484,17 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos, } } +/** + * @brief Compile a full source buffer into bytecode. + * + * Preprocesses namespace aliases, handles shebangs and top-level constructs, + * parses the indentation-aware block and appends a final HALT instruction. + * + * @param src Source buffer to compile (preprocessed when used via file API). + * @param len Length of the source buffer in bytes. + * @return Newly allocated Bytecode on success; never NULL here (errors are + * recorded globally and may lead to incomplete bytecode). + */ static Bytecode *compile_minimal(const char *src, size_t len) { Bytecode *bc = bytecode_new(); size_t pos = 0; @@ -7240,6 +7516,16 @@ static Bytecode *compile_minimal(const char *src, size_t len) { return bc; } +/** + * @brief Parse a .fun source file and return compiled bytecode. + * + * Reads the file, preprocesses includes (tracking original file paths), + * resets the global error state, and compiles to Bytecode while attaching + * source metadata (name, source_file). + * + * @param path Filesystem path to the source file. + * @return Bytecode pointer on success; NULL on I/O or parse error. + */ Bytecode *parse_file_to_bytecode(const char *path) { size_t len = 0; char *src = read_file_all(path, &len); @@ -7416,6 +7702,15 @@ Bytecode *parse_file_to_bytecode(const char *path) { return bc; } +/** + * @brief Parse a source string and return compiled bytecode. + * + * Suitable for REPL or tests. Performs include preprocessing with no base + * path, compiles, and attaches generic source metadata. + * + * @param source NUL-terminated source code string. + * @return Bytecode pointer on success; NULL on parse error. + */ Bytecode *parse_string_to_bytecode(const char *source) { if (!source) { fprintf(stderr, "Error: null source provided\n"); @@ -7459,6 +7754,19 @@ Bytecode *parse_string_to_bytecode(const char *source) { return bc; } +/** + * @brief Retrieve the last parser/compiler error information, if any. + * + * Copies the error message into msgBuf (truncated to msgCap-1), and returns + * the one-based line and column where available. If no error is pending, + * returns 0 and leaves outputs unchanged. + * + * @param msgBuf Destination buffer for the error message (may be NULL). + * @param msgCap Capacity of msgBuf in bytes. + * @param outLine Optional out param for one-based line number. + * @param outCol Optional out param for one-based column number. + * @return 1 if an error was available and copied, 0 otherwise. + */ int parser_last_error(char *msgBuf, unsigned long msgCap, int *outLine, int *outCol) { if (!g_has_error) return 0; if (msgBuf && msgCap > 0) { diff --git a/src/parser.h b/src/parser.h index 352372b..8c89a30 100644 --- a/src/parser.h +++ b/src/parser.h @@ -8,14 +8,8 @@ */ /** - * Parse a .fun source file and compile it into entry bytecode. - * Minimal support: - * - Optional shebang on the first line. - * - Optional single function wrapper: fun () { ... } - * - print("...") statements inside function or at top-level. - * Produces bytecode that executes all dApache-2.0overed print statements and halts. - * - * Returns: newly allocated Bytecode*, or NULL on error. + * @file parser.h + * @brief Public API for parsing Fun source into bytecode. */ #ifndef FUN_PARSER_H @@ -23,12 +17,37 @@ #include "bytecode.h" +/** + * @brief Parse a .fun source file and compile it into a bytecode chunk. + * + * The parser accepts an optional shebang on the first line and supports a + * minimal top-level or single-function program model. The returned bytecode + * will execute discovered statements (e.g., print) and then halt. + * + * @param path Filesystem path to the .fun source file. Must be a + * null-terminated UTF-8 string. + * @return Newly allocated Bytecode instance on success, or NULL on error. + */ Bytecode *parse_file_to_bytecode(const char *path); -/* Parse source provided as a single string buffer (for REPL, tests, etc.). */ +/** + * @brief Parse source from a provided string buffer (REPL/tests helper). + * + * @param source Null-terminated Fun program text. + * @return Newly allocated Bytecode instance on success, or NULL on error. + */ Bytecode *parse_string_to_bytecode(const char *source); -/* Query the last parser error (1 if present, 0 if none). */ +/** + * @brief Retrieve information about the last parser error, if any. + * + * @param msgBuf Output buffer to receive a human-readable error message. + * @param msgCap Capacity of msgBuf in bytes. + * @param outLine Optional output: 1-based line number where the error occurred. + * @param outCol Optional output: 1-based column number where the error occurred. + * @return 1 if an error was present and fields were populated, 0 if there is + * no recorded error. + */ int parser_last_error(char *msgBuf, unsigned long msgCap, int *outLine, int *outCol); #endif diff --git a/src/parser_utils.c b/src/parser_utils.c index d3e45b9..68bf2db 100644 --- a/src/parser_utils.c +++ b/src/parser_utils.c @@ -7,12 +7,29 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file parser_utils.c + * @brief Low-level parsing helpers and include preprocessor for the Fun parser. + * + * This module provides character scanners, token helpers, simple string/number + * literal readers, a lightweight include preprocessor that can expand + * `include` directives, and mapping utilities to translate expanded line + * numbers back to original files for diagnostics. + */ #include "parser.h" #include #include #include #include +/** + * @brief Read entire file into a newly allocated buffer. + * + * @param path Path to file. + * @param out_len Optional; receives number of bytes read (not including NUL). + * @return Newly allocated NUL-terminated buffer on success, or NULL on error. + * Caller must free() the returned buffer. + */ static char *read_file_all(const char *path, size_t *out_len) { FILE *f = fopen(path, "rb"); if (!f) return NULL; @@ -38,6 +55,12 @@ static char *read_file_all(const char *path, size_t *out_len) { return buf; } +/** + * @brief Skip spaces, tabs, carriage returns and newlines. + * @param src Source buffer. + * @param len Buffer length. + * @param pos In/out byte offset; advanced past whitespace. + */ static void skip_ws(const char *src, size_t len, size_t *pos) { while (*pos < len) { char c = src[*pos]; @@ -49,12 +72,19 @@ static void skip_ws(const char *src, size_t len, size_t *pos) { } } +/** + * @brief Advance @p pos to the next line, consuming the trailing '\n' if present. + */ static void skip_line(const char *src, size_t len, size_t *pos) { while (*pos < len && src[*pos] != '\n') (*pos)++; if (*pos < len && src[*pos] == '\n') (*pos)++; } +/** + * @brief Skip whitespace, then line (//) and block (/* ... */) comments. + * Continues until the next non-comment, non-whitespace character. + */ static void skip_comments(const char *src, size_t len, size_t *pos) { for (;;) { skip_ws(src, len, pos); @@ -74,18 +104,29 @@ static void skip_comments(const char *src, size_t len, size_t *pos) { } } +/** + * @brief Check if src starting at pos begins with kw and fits in len. + * @return 1 if matches, 0 otherwise. + */ static int starts_with(const char *src, size_t len, size_t pos, const char *kw) { size_t klen = strlen(kw); if (pos + klen > len) return 0; return strncmp(src + pos, kw, klen) == 0; } +/** + * @brief Skip a top-of-file shebang line that starts with "#!" if present. + */ static void skip_shebang_if_present(const char *src, size_t len, size_t *pos) { if (*pos == 0 && starts_with(src, len, *pos, "#!")) { skip_line(src, len, pos); } } +/** + * @brief If an identifier starts at pos, advance pos to its end. + * Recognizes [A-Za-z_][A-Za-z0-9_]*. + */ static void skip_identifier(const char *src, size_t len, size_t *pos) { size_t p = *pos; if (p < len && (isalpha((unsigned char)src[p]) || src[p] == '_')) { @@ -96,6 +137,10 @@ static void skip_identifier(const char *src, size_t len, size_t *pos) { *pos = p; } +/** + * @brief Consume expected character after skipping whitespace. + * @return 1 if consumed; 0 if not present. + */ static int consume_char(const char *src, size_t len, size_t *pos, char expected) { skip_ws(src, len, pos); if (*pos < len && src[*pos] == expected) { @@ -105,6 +150,13 @@ static int consume_char(const char *src, size_t len, size_t *pos, char expected) return 0; } +/** + * @brief Parse a single-quoted or double-quoted string literal. + * + * Supports common C-style escapes: \n, \r, \t, \\, \", \\' . Returns a newly + * allocated buffer on success and advances pos. On failure returns NULL and + * does not modify the source. Caller must free() returned buffer. + */ static char *parse_string_literal_any_quote(const char *src, size_t len, size_t *pos) { skip_ws(src, len, pos); if (*pos >= len) return NULL; @@ -174,6 +226,9 @@ static char *parse_string_literal_any_quote(const char *src, size_t len, size_t /* === Helpers for identifiers, numbers, booleans, and globals === */ +/** + * @brief Skip only spaces, tabs and carriage returns (not newlines). + */ static void skip_spaces(const char *src, size_t len, size_t *pos) { while (*pos < len) { char c = src[*pos]; @@ -185,6 +240,11 @@ static void skip_spaces(const char *src, size_t len, size_t *pos) { } } +/** + * @brief Read an identifier starting at pos and allocate its name. + * @param out_name Set to malloc'd NUL-terminated name on success. + * @return 1 on success (pos advanced), 0 otherwise. + */ static int read_identifier_into(const char *src, size_t len, size_t *pos, char **out_name) { size_t p = *pos; if (p < len && (isalpha((unsigned char)src[p]) || src[p] == '_')) { @@ -204,6 +264,11 @@ static int read_identifier_into(const char *src, size_t len, size_t *pos, char * return 0; } +/** + * @brief Parse an integer literal (decimal or 0x-hex) with optional sign. + * @param ok Set to 1 on success, 0 on failure. + * @return Parsed value (two's complement cast for negative inputs). + */ static uint64_t parse_int_literal_value(const char *src, size_t len, size_t *pos, int *ok) { size_t p = *pos; skip_spaces(src, len, &p); @@ -262,17 +327,26 @@ static uint64_t parse_int_literal_value(const char *src, size_t len, size_t *pos * Directives are recognized only when not inside strings or block comments. */ +/** + * @brief Thin wrapper over realloc used by local buffers. + */ static void *xrealloc(void *ptr, size_t newcap) { void *np = realloc(ptr, newcap); return np; } +/** + * @brief Simple growable string buffer. + */ typedef struct { char *buf; size_t len; size_t cap; } StrBuf; +/** + * @brief Initialize a StrBuf with a small starting capacity. + */ static void sb_init(StrBuf *sb) { sb->buf = (char *)malloc(256); sb->cap = sb->buf ? 256 : 0; @@ -280,6 +354,9 @@ static void sb_init(StrBuf *sb) { if (sb->buf) sb->buf[0] = '\0'; } +/** + * @brief Ensure buffer capacity for at least need bytes (including terminator). + */ static void sb_reserve(StrBuf *sb, size_t need) { if (need <= sb->cap) return; size_t nc = sb->cap ? sb->cap : 256; @@ -291,6 +368,9 @@ static void sb_reserve(StrBuf *sb, size_t need) { sb->cap = nc; } +/** + * @brief Append n bytes from s to the buffer. + */ static void sb_append_n(StrBuf *sb, const char *s, size_t n) { if (n == 0) return; sb_reserve(sb, sb->len + n + 1); @@ -300,10 +380,16 @@ static void sb_append_n(StrBuf *sb, const char *s, size_t n) { sb->buf[sb->len] = '\0'; } +/** + * @brief Append a NUL-terminated string to the buffer. + */ static void sb_append(StrBuf *sb, const char *s) { sb_append_n(sb, s, strlen(s)); } +/** + * @brief Append a single character to the buffer. + */ static void sb_append_ch(StrBuf *sb, char c) { sb_reserve(sb, sb->len + 2); if (!sb->buf) return; @@ -312,18 +398,27 @@ static void sb_append_ch(StrBuf *sb, char c) { } /* ---- Export collection for include-as namespaces ---- */ +/** + * @brief List of exported symbol names discovered at top level. + */ typedef struct { char **names; int count; int cap; } NameList; +/** + * @brief Initialize an empty NameList. + */ static void nl_init(NameList *nl) { nl->names = NULL; nl->count = 0; nl->cap = 0; } +/** + * @brief Add a copy of name to the list (ignores NULL/empty). + */ static void nl_add(NameList *nl, const char *name) { if (!name || !name[0]) return; if (nl->count >= nl->cap) { @@ -336,6 +431,9 @@ static void nl_add(NameList *nl, const char *name) { nl->names[nl->count++] = strdup(name); } +/** + * @brief Free all strings and internal storage in the list. + */ static void nl_free(NameList *nl) { if (!nl) return; for (int i = 0; i < nl->count; ++i) @@ -347,6 +445,10 @@ static void nl_free(NameList *nl) { /* Collect top-level (indent=0) exported symbols: function and class names. Ignores lines inside comments/strings and ignores nested indent. */ +/** + * @brief Collect top-level exported symbols (fun/class) from source text. + * Ignores strings and comments. Only lines with zero indentation count. + */ static void collect_exports_top_level(const char *text, NameList *out) { if (!text || !out) return; size_t len = strlen(text); @@ -499,6 +601,20 @@ static void collect_exports_top_level(const char *text, NameList *out) { } } +/** + * @brief Expand include directives in Fun source. + * + * Recognizes both `#include "..."` and `include "..."`/`include <...>` at + * the beginning of a line (after spaces/tabs). Angle-bracket includes search + * in FUN_LIB_DIR, then DEFAULT_LIB_DIR, then local lib/. Emits span markers of + * the form `// __include_begin__: [as alias] @line N` to enable later + * mapping back to original files. + * + * @param src Source code to preprocess. + * @param current_path Optional path of the current file for initial marker. + * @param depth Recursion depth guard. + * @return Newly allocated expanded text or NULL on OOM. + */ static char *preprocess_includes_internal(const char *src, const char *current_path, int depth) { if (!src) return NULL; if (depth > 64) { @@ -843,11 +959,17 @@ static char *preprocess_includes_internal(const char *src, const char *current_p return out.buf; } +/** + * @brief Public wrapper to preprocess includes without a current path. + */ char *preprocess_includes(const char *src) { return preprocess_includes_internal(src, NULL, 0); } /* Variant with known current file path to allow precise resume markers. */ +/** + * @brief Preprocess includes with a known file path to improve span markers. + */ char *preprocess_includes_with_path(const char *src, const char *current_path) { return preprocess_includes_internal(src, current_path, 0); } @@ -858,6 +980,19 @@ char *preprocess_includes_with_path(const char *src, const char *current_path) { * `// __include_begin__: [ as ]` markers injected by the * preprocessor. Returns 1 on success and fills out_path/out_line; 0 otherwise. */ +/** + * @brief Map a line number in expanded source back to original include path/line. + * + * Scans the expanded text for the nearest preceding `__include_begin__` marker + * and computes the corresponding inner line number. + * + * @param path Path to the original top-level file that was expanded. + * @param line 1-based line number in the expanded text. + * @param out_path Output buffer for the resolved file path. + * @param out_path_cap Capacity of out_path. + * @param out_line Receives 1-based line number within resolved file. + * @return 1 on success, 0 on failure. + */ int map_expanded_line_to_include_path(const char *path, int line, char *out_path, size_t out_path_cap, int *out_line) { @@ -968,6 +1103,11 @@ next_scan_back: } /* Float literal parser: supports decimal and scientific notation. Returns parsed double and advances pos on success. */ +/** + * @brief Parse a floating-point literal (supports . and scientific notation). + * @param ok Set to 1 on success, 0 otherwise. + * @return Parsed double value. + */ static double parse_float_literal_value(const char *src, size_t len, size_t *pos, int *ok) { size_t p = *pos; skip_spaces(src, len, &p); diff --git a/src/repl.c b/src/repl.c index c8732e3..da6ec09 100644 --- a/src/repl.c +++ b/src/repl.c @@ -5,13 +5,15 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2025-10-05 */ /** - * REPL implementation for the Fun programming language. - * Built only when FUN_WITH_REPL is defined. + * @file repl.c + * @brief Interactive Read–Eval–Print Loop (REPL) for the Fun language. + * + * Provides line editing, history, simple completion for :load paths and + * stdlib identifiers, multi-line input heuristics, and a command interface + * (e.g. :help, :env, :load). Built only when FUN_WITH_REPL is defined. */ #include "repl.h" @@ -49,12 +51,19 @@ static char *rl_hist[RL_HIST_MAX]; static int rl_count = 0; /* last history entry (or NULL) */ +/** + * @brief Return the last history line or NULL if history is empty. + */ static const char *rl_hist_last(void) { if (rl_count <= 0) return NULL; return rl_hist[rl_count - 1]; } /* add one line to in-memory history (without trailing newline), dedup consecutive */ +/** + * @brief Append a non-empty line to in-memory history, de-duplicating + * consecutive duplicates. Trailing newlines are stripped. + */ static void rl_hist_add(const char *s) { if (!s) return; size_t n = strlen(s); @@ -81,6 +90,9 @@ static void rl_hist_add(const char *s) { } /* preload history from file (one line per entry) */ +/** + * @brief Load history entries from a file (one line per entry). + */ static void rl_hist_load_file(const char *path) { if (!path) return; FILE *f = fopen(path, "r"); @@ -96,12 +108,19 @@ static void rl_hist_load_file(const char *path) { static struct termios g_orig_tios; static int g_raw_enabled = 0; +/** + * @brief Disable terminal raw mode if previously enabled. + */ static void repl_disable_raw(void) { if (g_raw_enabled) { tcsetattr(STDIN_FILENO, TCSAFLUSH, &g_orig_tios); g_raw_enabled = 0; } } +/** + * @brief Enable minimal terminal raw mode for interactive input. + * @return 1 on success, 0 otherwise. + */ static int repl_enable_raw(void) { if (!isatty(STDIN_FILENO)) return 0; if (g_raw_enabled) return 1; @@ -116,6 +135,9 @@ static int repl_enable_raw(void) { } /* return 1 if path is a directory, else 0 */ +/** + * @brief Return 1 if path refers to a directory, 0 otherwise. + */ static int is_dir_path(const char *path) { struct stat st; if (stat(path, &st) != 0) return 0; @@ -123,6 +145,9 @@ static int is_dir_path(const char *path) { } /* Compute longest common prefix of a set of strings (starting from offset base_len) */ +/** + * @brief Longest common suffix length among names[i] starting at base_len. + */ static size_t lcp_suffix(const char **names, int count, size_t base_len) { if (count <= 0) return base_len; size_t lcp = (size_t)-1; @@ -151,6 +176,9 @@ static size_t lcp_suffix(const char **names, int count, size_t base_len) { /* Word-jump helpers (used by Ctrl+Left/Right in the REPL editor). * Words are runs of non-space characters; separators are spaces. */ +/** + * @brief Move cursor left by one word. + */ static void rl_word_left(const char *out, size_t len, size_t *pos) { (void)len; if (!out || !pos) return; @@ -160,6 +188,9 @@ static void rl_word_left(const char *out, size_t len, size_t *pos) { while (*pos > 0 && out[*pos - 1] != ' ') (*pos)--; } +/** + * @brief Move cursor right by one word. + */ static void rl_word_right(const char *out, size_t len, size_t *pos) { if (!out || !pos) return; if (*pos >= len) return; @@ -170,6 +201,10 @@ static void rl_word_right(const char *out, size_t len, size_t *pos) { } /* Expand file path for path-taking REPL commands (e.g., :load, :run) in-place; returns 1 if buffer changed (redraw) */ +/** + * @brief Complete a :load file path in-place within the buffer. + * @return 1 if buffer changed, 2 if a menu was printed, 0 otherwise. + */ static int complete_load_path(char *buf, size_t *len_io) { size_t len = *len_io; if (len < 3) return 0; /* minimally ":x" */ @@ -322,13 +357,22 @@ static char **g_std_syms = NULL; static int g_std_syms_count = 0; static int g_std_syms_cap = 0; +/** + * @brief Return 1 if c can start an identifier. + */ static int is_ident_start(int c) { return (c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); } +/** + * @brief Return 1 if c is a valid identifier continuation character. + */ static int is_ident_char(int c) { return is_ident_start(c) || (c >= '0' && c <= '9'); } +/** + * @brief Add a stdlib symbol name to the completion table. + */ static void std_syms_add(const char *name) { if (!name || !*name) return; /* dedupe */ @@ -349,6 +393,9 @@ static void std_syms_add(const char *name) { g_std_syms[g_std_syms_count++] = cpy; } +/** + * @brief Scan a .fun file and collect top-level definitions for completion. + */ static void scan_symbols_from_file(const char *path) { FILE *f = fopen(path, "r"); if (!f) return; @@ -387,6 +434,9 @@ static void scan_symbols_from_file(const char *path) { fclose(f); } +/** + * @brief Recursively scan a directory tree for .fun files. + */ static void scan_dir_recursive(const char *dir) { DIR *dp = opendir(dir); if (!dp) return; @@ -410,11 +460,17 @@ static void scan_dir_recursive(const char *dir) { closedir(dp); } +/** + * @brief Populate stdlib completion symbols from a base directory. + */ static void load_stdlib_symbols(const char *libdir) { if (!libdir || !*libdir) return; scan_dir_recursive(libdir); } +/** + * @brief Free all memory held by stdlib completion symbols. + */ static void free_stdlib_symbols(void) { for (int i = 0; i < g_std_syms_count; ++i) free(g_std_syms[i]); @@ -425,6 +481,10 @@ static void free_stdlib_symbols(void) { /* Complete the trailing identifier in 'buf' using stdlib symbols. Returns 1 if buffer changed, 2 if a menu was printed, 0 otherwise. */ +/** + * @brief Complete the trailing identifier in buf from stdlib symbols. + * @return 1 if buffer changed, 2 if menu printed, 0 otherwise. + */ static int complete_stdlib_ident(char *buf, size_t *len_io, size_t *pos_io, size_t cap) { size_t len = *len_io; size_t pos = *pos_io; @@ -509,6 +569,13 @@ static int complete_stdlib_ident(char *buf, size_t *len_io, size_t *pos_io, size } /* Read one line with prompt, handling backspace, Up/Down history and :load path completion (now with multi-line editing via Ctrl+O) */ +/** + * @brief Read a line with basic line-editing and history support. + * @param out Output buffer to receive the line (NUL-terminated). + * @param out_cap Capacity of the output buffer. + * @param prompt Prompt string to display (may be NULL). + * @return 1 on success, 0 on EOF/error. + */ static int read_line_edit(char *out, size_t out_cap, const char *prompt) { #ifdef _WIN32 if (prompt) fputs(prompt, stdout), fflush(stdout); @@ -791,18 +858,27 @@ static int read_line_edit(char *out, size_t out_cap, const char *prompt) { /* ---------- Small utilities ---------- */ static int is_blank_line(const char *s) { + /** + * @brief Return 1 if the line consists only of whitespace. + */ for (const char *p = s; *p; ++p) { if (*p != ' ' && *p != '\t' && *p != '\r' && *p != '\n') return 0; } return 1; } +/** + * @brief Advance pointer past leading spaces and tabs. + */ static const char *lstrip(const char *s) { while (*s == ' ' || *s == '\t') s++; return s; } +/** + * @brief Heuristic: check if a line ends with an operator that suggests continuation. + */ static int ends_with_opener(const char *line) { size_t n = strlen(line); while (n > 0 && (line[n - 1] == ' ' || line[n - 1] == '\t' || line[n - 1] == '\r' || line[n - 1] == '\n')) @@ -817,6 +893,9 @@ static int ends_with_opener(const char *line) { } /* Compute how many indentation levels (2 spaces per level) are still open. */ +/** + * @brief Compute number of open indentation blocks (2 spaces per level). + */ static int compute_open_indent_blocks(const char *buf) { int in_block_comment = 0; int open = 0; @@ -887,6 +966,10 @@ static int compute_open_indent_blocks(const char *buf) { } /* Detect if current buffer looks incomplete. */ +/** + * @brief Heuristic to determine if the current buffer likely needs another line. + * Looks for open quotes/escapes or unmatched indentation. + */ static int buffer_looks_incomplete(const char *buf) { int in_single = 0, in_double = 0, escape = 0; int in_block_comment = 0, in_line_comment = 0; @@ -1018,6 +1101,9 @@ static int buffer_looks_incomplete(const char *buf) { return 0; } +/** + * @brief Print REPL help with available commands. + */ static void show_repl_help(void) { printf("Commands:\n"); printf(" :help | :h Show this help\n"); @@ -1057,6 +1143,10 @@ static void show_repl_help(void) { printf(" :finish | :fi Run until the current frame returns\n"); } +/** + * @brief Read a file fully into a newly allocated buffer. + * @return Buffer on success (caller frees), or NULL on error. + */ static char *read_entire_file(const char *path, size_t *out_len) { FILE *f = fopen(path, "rb"); if (!f) return NULL; @@ -1082,6 +1172,10 @@ static char *read_entire_file(const char *path, size_t *out_len) { return buf; } +/** + * @brief Write a buffer to a file path, replacing existing contents. + * @return 1 on success, 0 on error. + */ static int write_entire_file(const char *path, const char *data, size_t len) { FILE *f = fopen(path, "wb"); if (!f) return 0; @@ -1091,6 +1185,9 @@ static int write_entire_file(const char *path, const char *data, size_t len) { } /* ---------- REPL command matching helper ---------- */ +/** + * @brief Return 1 if cmd equals any non-NULL entry in names[] (terminated by NULL). + */ static int cmd_is_one_of(const char *cmd, const char *const names[]) { if (!cmd || !*cmd) return 0; for (int i = 0; names[i] != NULL; ++i) { @@ -1100,6 +1197,9 @@ static int cmd_is_one_of(const char *cmd, const char *const names[]) { } /* ---------- Hexdump helper ---------- */ +/** + * @brief Dump a byte buffer in hex to a FILE*, with offsets. + */ static void hexdump_to(FILE *out, const unsigned char *data, size_t len, size_t base_off) { if (!out || !data || len == 0) return; const size_t width = 16; @@ -1126,6 +1226,9 @@ static void hexdump_to(FILE *out, const unsigned char *data, size_t len, size_t } } +/** + * @brief Print the last n lines of a file to stdout (tail-like). + */ static void print_last_n_lines(const char *path, int n) { if (n <= 0) n = 50; size_t flen = 0; @@ -1150,6 +1253,9 @@ static void print_last_n_lines(const char *path, int n) { free(content); } +/** + * @brief Append multi-line buffer to history file, one line per entry. + */ static void append_history(FILE *hist, const char *buffer) { if (!hist || !buffer) return; fputs(buffer, hist); @@ -1158,6 +1264,9 @@ static void append_history(FILE *hist, const char *buffer) { } /* ---------- Env helpers ---------- */ +/** + * @brief Print usage for :env commands. + */ static void env_show_usage(void) { printf("Usage:\n"); printf(" :env NAME Show environment variable NAME\n"); @@ -1165,6 +1274,9 @@ static void env_show_usage(void) { printf(" :env Show this usage\n"); } +/** + * @brief Show the value of an environment variable. + */ static void env_get(const char *name) { const char *v = getenv(name); if (v) @@ -1173,6 +1285,9 @@ static void env_get(const char *name) { printf("%s is not set\n", name); } +/** + * @brief Set or unset (when value is NULL) an environment variable. + */ static void env_set(const char *name, const char *value) { #ifdef _WIN32 if (_putenv_s(name, value ? value : "") != 0) { @@ -1187,6 +1302,11 @@ static void env_set(const char *name, const char *value) { /* ---------- REPL Entry ---------- */ +/** + * @brief Run the interactive Fun REPL session. + * @param vm Initialized VM to execute user input within. + * @return Exit status code for the REPL. + */ int fun_run_repl(VM *vm) { int repl_timing = 0; int selected_frame = -1; /* -1 means use current top frame */ diff --git a/src/repl.h b/src/repl.h index 557e7d1..922c7dd 100644 --- a/src/repl.h +++ b/src/repl.h @@ -1,3 +1,16 @@ +/** + * 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 + */ + +/** + * @file repl.h + * @brief Interactive Read-Eval-Print Loop (REPL) entry point. + */ #ifndef FUN_REPL_H #define FUN_REPL_H @@ -8,8 +21,16 @@ extern "C" { #endif #ifdef FUN_WITH_REPL -// Runs the interactive REPL using the provided, already-initialized VM. -// Returns 0 on normal exit. +/** + * @brief Run the interactive REPL using an already-initialized VM. + * + * Reads lines from stdin, evaluates them in the provided VM context, and + * prints results/errors. The function returns on EOF or when the user issues + * a quit command supported by the REPL implementation. + * + * @param vm Pointer to an initialized VM; must not be NULL. + * @return 0 on normal exit, non-zero on fatal error. + */ int fun_run_repl(VM *vm); #endif diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs index b077762..a77bbd2 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -1,25 +1,58 @@ -/* +/** * 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-27 */ -// Use an FFI-safe opaque handle for the VM pointer +/** + * Rust FFI entry points and helpers used by the Fun VM (C). + * + * This crate builds a static library that exposes a handful of C-ABI + * functions callable by the C VM. The functions operate on an opaque VM + * pointer and follow the same conventions as native C opcode handlers: + * they return 0 on success and non‑zero on error, and they communicate + * values via VM stack helpers provided on the C side. + * + * Safety + * - All extern "C" functions are inherently unsafe due to raw pointers. + * - Callers must pass valid VM pointers provided by the C runtime. + * - Any strings returned across the FFI boundary are NUL‑terminated and + * must be freed using the dedicated free function documented below. + */ + +/// Use an FFI‑safe opaque handle for the VM pointer owned by the C VM. +/// +/// The Rust code treats this as an opaque blob and never dereferences it +/// directly, relying instead on C helper functions exposed via FFI. pub type Vm = core::ffi::c_void; extern "C" { + /// Pop a 64‑bit integer from the VM stack. + /// + /// Safety: `vm` must be a valid pointer to a VM instance. fn vm_pop_i64(vm: *mut Vm) -> i64; + /// Push a 64‑bit integer onto the VM stack. + /// + /// Safety: `vm` must be a valid pointer to a VM instance. fn vm_push_i64(vm: *mut Vm, v: i64); } -// Submodule with additional Rust VM math ops (exported via C ABI) +/// Submodule with additional Rust VM math ops (exported via C ABI). pub mod vm; +/// Add the top two integers on the VM stack. +/// +/// Stack effect +/// - Before: [..., a, b] +/// - After: [..., a+b] +/// +/// Return value +/// - 0 on success; non‑zero on error (never used here). +/// +/// Safety: `vm` must be a valid VM pointer. #[no_mangle] pub extern "C" fn fun_op_radd(vm: *mut Vm) -> i32 { unsafe { @@ -30,12 +63,26 @@ pub extern "C" fn fun_op_radd(vm: *mut Vm) -> i32 { 0 } +/// Return a static NUL‑terminated greeting string owned by Rust. +/// +/// The returned pointer remains valid for the duration of the process and +/// must NOT be freed by the caller. +/// +/// Return const char* to a constant "Hello from Rust ops!" string. #[no_mangle] pub extern "C" fn fun_rust_get_string() -> *const core::ffi::c_char { b"Hello from Rust ops!\0".as_ptr() as *const _ } -// Print a C string via libc printf to stdout +/// Print a C string to stdout using libc printf("%s\n"). +/// +/// - If `msg` is NULL, behavior is undefined (printf will likely crash). +/// - This is intended as a simple demo and not for performance‑critical use. +/// +/// Param msg NUL‑terminated UTF‑8/bytes C string. +/// Return 0 on success. +/// +/// Safety: `msg` must be a valid C string pointer when non‑NULL. #[no_mangle] pub extern "C" fn fun_rust_print_string(msg: *const core::ffi::c_char) -> i32 { unsafe { @@ -48,8 +95,17 @@ pub extern "C" fn fun_rust_print_string(msg: *const core::ffi::c_char) -> i32 { 0 } -// Return a newly allocated duplicate of the given C string. -// Caller (C side) must free using fun_rust_string_free. +/// Duplicate a C string and return an owned copy allocated by Rust. +/// +/// - On NULL input, returns an allocated empty string ("\0"). +/// - On invalid UTF‑8, the raw byte sequence is duplicated as‑is. +/// - Memory ownership is transferred to the caller, who must free it with +/// fun_rust_string_free(). +/// +/// Param input NUL‑terminated C string (may be NULL). +/// Return Newly allocated NUL‑terminated C string owned by the caller. +/// +/// Safety: `input` must be a valid pointer if non‑NULL. #[no_mangle] pub extern "C" fn fun_rust_echo_string(input: *const core::ffi::c_char) -> *mut core::ffi::c_char { use core::ffi::CStr; @@ -76,7 +132,13 @@ pub extern "C" fn fun_rust_echo_string(input: *const core::ffi::c_char) -> *mut } } -// Free a C string previously returned by fun_rust_echo_string +/// Free a C string allocated by fun_rust_echo_string(). +/// +/// The pointer may be NULL, in which case the function is a no‑op. +/// +/// Param ptr Pointer returned by fun_rust_echo_string(). +/// +/// Safety: `ptr` must have been allocated by fun_rust_echo_string(). #[no_mangle] pub extern "C" fn fun_rust_string_free(ptr: *mut core::ffi::c_char) { if ptr.is_null() { return; } @@ -90,4 +152,4 @@ pub extern "C" fn fun_rust_string_free(ptr: *mut core::ffi::c_char) { } } -// No custom panic handler; use std default +// No custom panic handler; use std default. diff --git a/src/rust/src/vm/mod.rs b/src/rust/src/vm/mod.rs index 2816307..1a68cf5 100644 --- a/src/rust/src/vm/mod.rs +++ b/src/rust/src/vm/mod.rs @@ -1,32 +1,45 @@ -/* +/** * 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-27 */ -//! Rust VM helpers and opcode handlers exposed via C ABI. -//! -//! Functions in this module are compiled into the Rust static library and can -//! be called from the C VM. They operate on the VM stack using the minimal C -//! ABI helpers declared on the C side (`vm_pop_i64`, `vm_push_i64`). +/** + * Rust VM helpers and opcode handlers exposed via C ABI. + * + * Functions in this module are compiled into the Rust static library and can + * be called from the C VM. They operate on the VM stack using the minimal C + * ABI helpers declared on the C side (e.g. `vm_pop_i64`, `vm_push_i64`). + * + * Safety + * - All extern "C" functions are unsafe; `vm` must be a valid pointer. + * - Raw field access helpers (offset reads/writes) assume the C `struct Vm` + * layout matches the offsets reported by the C side. + */ use super::Vm; //use core::mem::size_of; use core::ptr; extern "C" { + /// Pop a 64‑bit integer from the VM stack. fn vm_pop_i64(vm: *mut Vm) -> i64; + /// Push a 64‑bit integer onto the VM stack. fn vm_push_i64(vm: *mut Vm, v: i64); + /// Get a mutable pointer to the underlying C Vm as an opaque byte ptr. fn vm_as_mut_ptr(vm: *mut Vm) -> *mut core::ffi::c_void; + /// Size of the C Vm struct (bytes). fn vm_sizeof() -> usize; + /// Size of the C Value struct (bytes). fn vm_value_sizeof() -> usize; + /// Byte offset of the `exit_code` field inside C Vm. fn vm_offset_of_exit_code() -> usize; + /// Byte offset of the `sp` field inside C Vm. fn vm_offset_of_sp() -> usize; + /// Byte offset of the `stack` field inside C Vm. fn vm_offset_of_stack() -> usize; } diff --git a/src/str_utils.c b/src/str_utils.c index ca969c1..5fe10b4 100644 --- a/src/str_utils.c +++ b/src/str_utils.c @@ -7,12 +7,31 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file str_utils.c + * @brief Helpers for manipulating C strings and bridging with Value arrays. + * + * Functions here return newly allocated C strings or construct Value arrays + * from strings. Callers own returned allocations and must free them using + * free()/free_value() as appropriate. + */ #include "value.h" #include #include /* string helpers returning newly allocated C strings or arrays */ +/** + * @brief Create a newly allocated substring of s. + * + * Indices are clamped into valid range. If s is NULL, an empty string is + * returned. The caller owns the returned buffer and must free() it. + * + * @param s Source C string (may be NULL). + * @param start Zero-based start index; clamped to [0, strlen(s)]. + * @param len Maximum number of characters to copy; negative treated as 0. + * @return Newly allocated NUL-terminated substring; never NULL. + */ char *string_substr(const char *s, int start, int len) { if (!s) return strdup(""); int n = (int)strlen(s); @@ -27,6 +46,13 @@ char *string_substr(const char *s, int start, int len) { return out; } +/** + * @brief Find first occurrence of needle in hay. + * + * @param hay Haystack C string (may be NULL). + * @param needle Needle C string (may be NULL). + * @return Zero-based index or -1 if not found/invalid input. + */ int string_find(const char *hay, const char *needle) { if (!hay || !needle) return -1; const char *p = strstr(hay, needle); @@ -34,6 +60,17 @@ int string_find(const char *hay, const char *needle) { return (int)(p - hay); } +/** + * @brief Split a C string by separator into a Value array of strings. + * + * When sep is empty, splits into individual UTF-8 bytes (characters). Uses + * make_string/make_array_from_values; the returned Value owns internal memory + * per Value semantics. NULL inputs are treated as empty strings. + * + * @param s Source C string (may be NULL). + * @param sep Separator C string (may be NULL). Empty means split into chars. + * @return Value of type VAL_ARRAY with string elements. + */ Value string_split_to_array(const char *s, const char *sep) { if (!s) s = ""; if (!sep) sep = ""; @@ -90,6 +127,17 @@ Value string_split_to_array(const char *s, const char *sep) { return arr; } +/** + * @brief Join the elements of a Value array into a single newly allocated C string. + * + * Each array element is converted to a string via value_to_string_alloc. + * NULL/invalid inputs yield an empty string. The caller owns the returned + * buffer and must free() it. + * + * @param v Pointer to Value (expected VAL_ARRAY). + * @param sep Separator C string inserted between items (may be NULL). + * @return Newly allocated joined string; never NULL. + */ char *array_join_with_sep(const Value *v, const char *sep) { if (!v || v->type != VAL_ARRAY || !v->arr) return strdup(""); if (!sep) sep = ""; diff --git a/src/string.c b/src/string.c index ccaa3b7..503902f 100644 --- a/src/string.c +++ b/src/string.c @@ -7,17 +7,52 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file string.c + * @brief String built-ins wrappers used by VM opcodes. + * + * Provides small wrapper functions around core string utilities to operate on + * the Value type used by the VM (split, join, substr, find). + */ + #include "value.h" #include /* String built-ins wrappers used by VM opcodes */ +/** + * @brief Split a string by a separator into an array Value. + * + * Safely extracts C strings from the provided `Value` arguments. If either + * argument is NULL, not of type `VAL_STRING`, or has a NULL `s` pointer, + * an empty string ("") is used instead. + * + * @param str Input string (`VAL_STRING`) to split. May be NULL. + * @param sep Separator string (`VAL_STRING`). May be NULL. If empty, + * behavior is defined by `string_split_to_array`. + * @return A `Value` representing an array of substrings (each a `VAL_STRING`). + * The exact array layout and split semantics are delegated to + * `string_split_to_array`. + */ Value bi_split(const Value *str, const Value *sep) { const char *s = (str && str->type == VAL_STRING && str->s) ? str->s : ""; const char *p = (sep && sep->type == VAL_STRING && sep->s) ? sep->s : ""; return string_split_to_array(s, p); } +/** + * @brief Join an array of strings with a separator into a single string Value. + * + * Extracts the separator C string from `sep` if it is a `VAL_STRING`, + * otherwise uses an empty string (""). The join operation is performed by + * `array_join_with_sep`. + * + * @param arr Array `Value` expected to contain strings. Semantics for + * non-string elements are defined by `array_join_with_sep`. + * @param sep Separator string (`VAL_STRING`). May be NULL; defaults to empty. + * @return A `VAL_STRING` `Value` with the joined result. Never returns a NULL + * `Value`; if joining fails, an empty string is returned. + */ Value bi_join(const Value *arr, const Value *sep) { const char *p = (sep && sep->type == VAL_STRING && sep->s) ? sep->s : ""; char *s = array_join_with_sep(arr, p); @@ -26,6 +61,19 @@ Value bi_join(const Value *arr, const Value *sep) { return out; } +/** + * @brief Extract a substring from a string `Value`. + * + * If `str` is not a `VAL_STRING` or is NULL, an empty source string is used. + * The actual substring extraction semantics (e.g., handling of negative or + * out-of-range indices) are delegated to `string_substr`. + * + * @param str Source string (`VAL_STRING`). May be NULL. + * @param start Zero-based start index. + * @param len Maximum number of characters to include in the substring. + * @return A `VAL_STRING` `Value` containing the substring. Returns an empty + * string if extraction fails or inputs are treated as empty. + */ Value bi_substr(const Value *str, int start, int len) { const char *s = (str && str->type == VAL_STRING && str->s) ? str->s : ""; char *sub = string_substr(s, start, len); @@ -34,6 +82,17 @@ Value bi_substr(const Value *str, int start, int len) { return out; } +/** + * @brief Find the first occurrence of a needle inside a haystack string. + * + * If either argument is not a `VAL_STRING` or is NULL, it is treated as an + * empty string (""). The search is performed by `string_find`. + * + * @param hay Haystack string (`VAL_STRING`). May be NULL. + * @param needle Needle string (`VAL_STRING`). May be NULL. + * @return The zero-based index of the first occurrence of `needle` in `hay`, + * or -1 if not found. Exact semantics are delegated to `string_find`. + */ int bi_find(const Value *hay, const Value *needle) { const char *h = (hay && hay->type == VAL_STRING && hay->s) ? hay->s : ""; const char *n = (needle && needle->type == VAL_STRING && needle->s) ? needle->s : ""; diff --git a/src/test_opcodes.c b/src/test_opcodes.c index 45758d8..43958e3 100644 --- a/src/test_opcodes.c +++ b/src/test_opcodes.c @@ -7,11 +7,29 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file test_opcodes.c + * @brief Minimal executable exercising a subset of Fun VM opcodes. + * + * Builds a tiny bytecode program that loads constants, performs arithmetic, + * prints results, and demonstrates optional Rust FFI calls. Used for quick + * manual verification of the VM dispatch and I/O helpers. + */ + #include "bytecode.h" #include "value.h" #include "vm.h" #include +/** + * @brief Minimal executable to exercise a subset of VM opcodes. + * + * Builds a tiny bytecode chunk that loads two constants, adds them, and prints + * the result. Also demonstrates dumping bytecode and, optionally, calling Rust + * FFI examples when FUN_WITH_RUST is enabled. + * + * @return Zero on success; non-zero on fatal VM or allocation errors. + */ int main() { VM vm; diff --git a/src/value.c b/src/value.c index c6b2fa8..29196eb 100644 --- a/src/value.c +++ b/src/value.c @@ -7,6 +7,18 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file value.c + * @brief Implementation of the runtime Value type, including constructors, + * dynamic array/map utilities, copying, comparison, printing, and + * string conversion helpers. + * + * This translation unit provides the concrete operations for the Fun + * programming language's Value structure (ints, floats, bools, strings, + * arrays, maps, functions and nil). It is used by the VM and standard + * library to construct and manipulate runtime values. + */ + #include "value.h" #include #include @@ -30,6 +42,12 @@ typedef struct Map { Value *vals; /* each value owned here */ } Map; +/** + * @brief Construct a Value representing a 64-bit integer. + * + * @param v The integer payload. + * @return A Value with type VAL_INT holding v. + */ Value make_int(int64_t v) { Value val; val.type = VAL_INT; @@ -37,6 +55,12 @@ Value make_int(int64_t v) { return val; } +/** + * @brief Construct a Value representing a double-precision float. + * + * @param v The floating-point payload. + * @return A Value with type VAL_FLOAT holding v. + */ Value make_float(double v) { Value val; val.type = VAL_FLOAT; @@ -44,6 +68,14 @@ Value make_float(double v) { return val; } +/** + * @brief Construct a boolean Value. + * + * Any non-zero input is treated as true, zero as false. + * + * @param v Integer truthy/falsey indicator. + * @return A Value with type VAL_BOOL and normalized 0/1 payload. + */ Value make_bool(int v) { Value val; val.type = VAL_BOOL; @@ -51,6 +83,15 @@ Value make_bool(int v) { return val; } +/** + * @brief Construct a string Value by duplicating the given C string. + * + * If s is NULL, an empty string is used. The returned Value owns an allocated + * copy which must be released via free_value. + * + * @param s NUL-terminated C string (may be NULL). + * @return A Value with type VAL_STRING. + */ Value make_string(const char *s) { Value val; val.type = VAL_STRING; @@ -61,6 +102,15 @@ Value make_string(const char *s) { return val; } +/** + * @brief Construct a function Value referencing bytecode. + * + * The Bytecode pointer is stored as-is; ownership/lifetime is managed by the + * caller/VM and not freed by free_value. + * + * @param fn Pointer to function bytecode (may be NULL to represent an invalid function). + * @return A Value with type VAL_FUNCTION. + */ Value make_function(struct Bytecode *fn) { Value val; val.type = VAL_FUNCTION; @@ -68,12 +118,27 @@ Value make_function(struct Bytecode *fn) { return val; } +/** + * @brief Construct a nil Value. + * + * @return A Value with type VAL_NIL. + */ Value make_nil(void) { Value v; v.type = VAL_NIL; return v; } +/** + * @brief Create an array Value by copying items from an input span. + * + * Performs a shallow copy for scalars and reference-counted copy for arrays/maps + * via copy_value. On allocation failure, returns VAL_NIL. + * + * @param vals Pointer to input items; may be NULL when count == 0. + * @param count Number of items to copy (negative treated as 0). + * @return A Value with type VAL_ARRAY or VAL_NIL on failure. + */ Value make_array_from_values(const Value *vals, int count) { if (count < 0) count = 0; Array *arr = (Array *)malloc(sizeof(Array)); @@ -102,12 +167,28 @@ Value make_array_from_values(const Value *vals, int count) { return v; } +/** + * @brief Get the element count of an array Value. + * + * @param v Array Value. + * @return Number of elements, or -1 if v is not a valid array. + */ int array_length(const Value *v) { if (!v || v->type != VAL_ARRAY || !v->arr) return -1; const Array *a = (const Array *)v->arr; return a->count; } +/** + * @brief Copy an array element into out. + * + * The element is copied with copy_value; ownership of out remains with caller. + * + * @param v Array Value. + * @param index Zero-based index. + * @param out Destination pointer to receive the copied Value (may be NULL to only validate index). + * @return 1 on success, 0 on bounds/type error. + */ int array_get_copy(const Value *v, int index, Value *out) { if (!v || v->type != VAL_ARRAY || !v->arr) return 0; const Array *a = (const Array *)v->arr; @@ -116,6 +197,16 @@ int array_get_copy(const Value *v, int index, Value *out) { return 1; } +/** + * @brief Replace an element of an array with a new Value. + * + * Takes ownership of newElem and frees the old element. + * + * @param v Array Value to mutate. + * @param index Zero-based index to replace. + * @param newElem New element (ownership transferred to array). + * @return 1 on success, 0 on bounds/type error. + */ int array_set(Value *v, int index, Value newElem) { if (!v || v->type != VAL_ARRAY || !v->arr) return 0; Array *a = (Array *)v->arr; @@ -125,6 +216,15 @@ int array_set(Value *v, int index, Value newElem) { return 1; } +/** + * @brief Ensure the internal items buffer can hold at least newCount items. + * + * May grow the allocation exponentially; initializes new slots to nil. + * + * @param a Internal Array pointer. + * @param newCount Required minimum logical capacity. + * @return 1 on success, 0 on allocation failure. + */ static int ensure_array_capacity(Array *a, int newCount) { if (newCount <= a->count) return 1; /* grow to at least newCount; double strategy */ @@ -145,6 +245,15 @@ static int ensure_array_capacity(Array *a, int newCount) { return 1; } +/** + * @brief Append a Value to an array. + * + * On success, ownership of newElem is transferred to the array. + * + * @param v Array Value to append to. + * @param newElem Element to append. + * @return New array length on success (>=0), or -1 on failure/type error. + */ int array_push(Value *v, Value newElem) { if (!v || v->type != VAL_ARRAY || !v->arr) return -1; Array *a = (Array *)v->arr; @@ -160,6 +269,16 @@ int array_push(Value *v, Value newElem) { return a->count; } +/** + * @brief Remove the last element from an array. + * + * If out is provided, ownership of the removed element is transferred to *out; + * otherwise the element is freed. + * + * @param v Array Value to pop from. + * @param out Optional destination for removed element. + * @return 1 on success, 0 if array empty or invalid. + */ int array_pop(Value *v, Value *out) { if (!v || v->type != VAL_ARRAY || !v->arr) return 0; Array *a = (Array *)v->arr; @@ -173,6 +292,16 @@ int array_pop(Value *v, Value *out) { return 1; } +/** + * @brief Insert a new element at a specific position in an array. + * + * Index is clamped into [0, count]. Takes ownership of newElem. + * + * @param v Array Value to modify. + * @param index Insertion index. + * @param newElem Element to insert. + * @return New array length on success (>=0), or -1 on allocation/type error. + */ int array_insert(Value *v, int index, Value newElem) { if (!v || v->type != VAL_ARRAY || !v->arr) return -1; Array *a = (Array *)v->arr; @@ -193,6 +322,17 @@ int array_insert(Value *v, int index, Value newElem) { return a->count; } +/** + * @brief Remove an element at index from an array. + * + * If out is provided, ownership of the removed element is transferred; else it + * is freed. Remaining items are shifted left. + * + * @param v Array Value to modify. + * @param index Zero-based index to remove. + * @param out Optional destination for removed element. + * @return 1 on success, 0 on bounds/type error. + */ int array_remove(Value *v, int index, Value *out) { if (!v || v->type != VAL_ARRAY || !v->arr) return 0; Array *a = (Array *)v->arr; @@ -209,6 +349,16 @@ int array_remove(Value *v, int index, Value *out) { return 1; } +/** + * @brief Create a shallow-copied slice of an array Value. + * + * Start and end are clamped into valid bounds; end < start yields empty array. + * + * @param v Source array Value. + * @param start Inclusive zero-based start index (clamped to >= 0). + * @param end Exclusive end index (clamped to <= length; -1 means length). + * @return A new array Value (possibly empty) or VAL_NIL if v is not an array. + */ Value array_slice(const Value *v, int start, int end) { if (!v || v->type != VAL_ARRAY || !v->arr) return make_nil(); const Array *a = (const Array *)v->arr; @@ -223,6 +373,16 @@ Value array_slice(const Value *v, int start, int end) { return make_array_from_values(a->items + start, m); } +/** + * @brief Concatenate two array Values. + * + * Copies elements into a new array. If either input is not an array, returns + * VAL_NIL. + * + * @param av First array. + * @param bv Second array. + * @return A new concatenated array Value or VAL_NIL on type/alloc error. + */ Value array_concat(const Value *av, const Value *bv) { if (!av || !bv || av->type != VAL_ARRAY || bv->type != VAL_ARRAY) return make_nil(); const Array *a = (const Array *)av->arr; @@ -243,6 +403,15 @@ Value array_concat(const Value *av, const Value *bv) { return out; } +/** + * @brief Shallow copy a Value. + * + * Strings are duplicated, arrays/maps have their refcount incremented, and + * function pointers are copied as-is. + * + * @param v Source Value. + * @return A new Value with appropriate copy semantics. + */ Value copy_value(const Value *v) { Value out; out.type = v->type; @@ -282,6 +451,15 @@ Value copy_value(const Value *v) { } /* deep copy including arrays (recursively copies items) */ +/** + * @brief Deep copy a Value, recursively copying arrays and maps. + * + * Function Values are copied shallowly. On allocation failure, returns nil or + * an empty container as appropriate. + * + * @param v Source Value. + * @return A deep-copied Value. + */ Value deep_copy_value(const Value *v) { switch (v->type) { case VAL_INT: @@ -328,6 +506,14 @@ Value deep_copy_value(const Value *v) { } } +/** + * @brief Free dynamic storage owned by a Value. + * + * Strings are freed, arrays/maps are reference-counted and freed recursively + * when their refcount drops to zero. Functions are not freed here. + * + * @param v Value whose owned resources should be released. + */ void free_value(Value v) { if (v.type == VAL_STRING && v.s) { free(v.s); @@ -355,6 +541,14 @@ void free_value(Value v) { /* VAL_FUNCTION: we *do not* free the Bytecode here (caller frees it) */ } +/** + * @brief Print a human-readable representation of a Value to stdout. + * + * Numbers are printed in decimal; arrays/maps are formatted compactly; strings + * are printed without quotes. + * + * @param v Value to print. + */ void print_value(const Value *v) { switch (v->type) { case VAL_INT: @@ -404,6 +598,15 @@ void print_value(const Value *v) { } } +/** + * @brief Evaluate a Value's truthiness according to Fun language rules. + * + * Empty strings, zero numbers, nil and empty arrays are falsey; everything + * else is truthy. + * + * @param v Value to evaluate. + * @return 1 if truthy, 0 otherwise. + */ int value_is_truthy(const Value *v) { switch (v->type) { case VAL_INT: @@ -427,6 +630,14 @@ int value_is_truthy(const Value *v) { } /* allocate a printable C string for the value; caller must free */ +/** + * @brief Allocate a printable C string for a Value. + * + * The returned string must be freed by the caller with free(). + * + * @param v Value to convert. + * @return Newly allocated NUL-terminated string describing v. + */ char *value_to_string_alloc(const Value *v) { if (!v) return strdup("nil"); char buf[128]; @@ -470,6 +681,17 @@ char *value_to_string_alloc(const Value *v) { } } +/** + * @brief Compare two Values for equality. + * + * Supports numeric cross-type equality between ints and floats. Strings are + * compared by content. Other types default to pointer/type equality as + * implemented in the switch. + * + * @param a First Value. + * @param b Second Value. + * @return 1 if equal, 0 otherwise. + */ int value_equals(const Value *a, const Value *b) { // Numeric cross-type equality: int vs float compares numerically if ((a->type == VAL_INT || a->type == VAL_FLOAT) && (b->type == VAL_INT || b->type == VAL_FLOAT)) { diff --git a/src/value.h b/src/value.h index a0db138..9c61572 100644 --- a/src/value.h +++ b/src/value.h @@ -44,6 +44,9 @@ struct Bytecode; /* forward */ struct Array; /* forward */ struct Map; /* forward */ +/** + * @brief Enumeration of all runtime value types supported by Fun. + */ typedef enum { VAL_INT, VAL_BOOL, @@ -55,6 +58,13 @@ typedef enum { VAL_FLOAT } ValueType; +/** + * @brief Tagged union representing a Fun value. + * + * The active field in the anonymous union is determined by @ref ValueType in + * the @c type tag. Ownership/RC semantics for complex members (arrays/maps) + * are defined by the array/map APIs. + */ typedef struct { ValueType type; union { @@ -68,55 +78,91 @@ typedef struct { } Value; /* constructors / helpers */ +/** Create an integer Value. */ Value make_int(int64_t v); +/** Create a boolean Value (0=false, non-zero=true). */ Value make_bool(int v); +/** Create a string Value by copying @p s. */ Value make_string(const char *s); +/** Create a function Value from bytecode pointer (shallow). */ Value make_function(struct Bytecode *fn); +/** Create a nil Value. */ Value make_nil(void); +/** Create a floating-point Value. */ Value make_float(double v); /* arrays */ -Value make_array_from_values(const Value *vals, int count); /* deep-copies vals */ -int array_length(const Value *v); /* returns -1 if not array */ -int array_get_copy(const Value *v, int index, Value *out); /* returns 0 on error; out = copy_value(item) */ -int array_set(Value *v, int index, Value newElem); /* returns 0 on error; takes ownership of newElem */ -int array_push(Value *v, Value newElem); /* returns new length or -1 on error */ -int array_pop(Value *v, Value *out); /* returns 1 on success, out takes ownership */ -int array_insert(Value *v, int index, Value newElem); /* returns new length or -1 */ -int array_remove(Value *v, int index, Value *out); /* returns 1 on success */ -Value array_slice(const Value *v, int start, int end); /* negative end means till end */ -Value array_concat(const Value *a, const Value *b); /* returns new array */ +/** Build an array from a list of Values (deep-copies vals). */ +Value make_array_from_values(const Value *vals, int count); +/** Get array length or -1 if @p v is not an array. */ +int array_length(const Value *v); +/** Copy array item at index to out; returns 0 on error. */ +int array_get_copy(const Value *v, int index, Value *out); +/** Set element at index; takes ownership of newElem; 0 on error. */ +int array_set(Value *v, int index, Value newElem); +/** Push new element; returns new length or -1 on error. */ +int array_push(Value *v, Value newElem); +/** Pop last element into out; returns 1 on success. */ +int array_pop(Value *v, Value *out); +/** Insert at index; returns new length or -1 on error. */ +int array_insert(Value *v, int index, Value newElem); +/** Remove at index into out; returns 1 on success. */ +int array_remove(Value *v, int index, Value *out); +/** Return slice [start,end) (negative end means till end). */ +Value array_slice(const Value *v, int start, int end); +/** Concatenate arrays a and b into a new array. */ +Value array_concat(const Value *a, const Value *b); /* maps (string keys) */ -Value make_map_empty(void); /* new empty map */ -int map_set(Value *m, const char *key, Value v); /* 1 on ok (takes ownership of v) */ -int map_get_copy(const Value *m, const char *key, Value *out); /* 1 on found, out=copy */ -int map_has(const Value *m, const char *key); /* 1/0 */ -Value map_keys_array(const Value *m); /* array of strings */ -Value map_values_array(const Value *m); /* array of values (copies) */ +/** Create a new empty string-keyed map Value. */ +Value make_map_empty(void); +/** Set key to v (takes ownership); returns 1 on success. */ +int map_set(Value *m, const char *key, Value v); +/** Lookup key; returns 1 and copies value to out on success. */ +int map_get_copy(const Value *m, const char *key, Value *out); +/** Test if key exists; returns 1/0. */ +int map_has(const Value *m, const char *key); +/** Return array of string keys. */ +Value map_keys_array(const Value *m); +/** Return array of values (copies). */ +Value map_values_array(const Value *m); /* copy/free */ -Value copy_value(const Value *v); /* deep for strings, RC for arrays/maps, shallow fn */ -Value deep_copy_value(const Value *v); /* deep copy including arrays/maps */ -void free_value(Value v); /* frees owned resources */ +/** Shallow/deep copy depending on type (deep for strings, RC for arrays/maps). */ +Value copy_value(const Value *v); +/** Deep copy including arrays/maps. */ +Value deep_copy_value(const Value *v); +/** Free owned resources of v. */ +void free_value(Value v); /* utilities */ +/** Print value in a human-readable form to stdout. */ void print_value(const Value *v); +/** Truthiness predicate used by the language semantics. */ int value_is_truthy(const Value *v); -int value_equals(const Value *a, const Value *b); /* int/string equality */ +/** Equality for ints/strings; other types may be pointer/semantic based. */ +int value_equals(const Value *a, const Value *b); /* stringify into a newly-allocated C string; caller must free */ +/** Convert Value to a newly allocated C string; caller must free. */ char *value_to_string_alloc(const Value *v); /* array utils */ -int array_contains(const Value *arr, const Value *needle); /* 1/0 */ -int array_index_of(const Value *arr, const Value *needle); /* idx or -1 */ -void array_clear(Value *arr); /* free elements, count=0 */ +/** Return 1 if needle equals any element in arr. */ +int array_contains(const Value *arr, const Value *needle); +/** Return index of needle in arr or -1. */ +int array_index_of(const Value *arr, const Value *needle); +/** Free elements and reset count to 0. */ +void array_clear(Value *arr); /* string helpers returning newly allocated C strings or arrays */ -char *string_substr(const char *s, int start, int len); /* clamps bounds */ -int string_find(const char *hay, const char *needle); /* index or -1 */ -Value string_split_to_array(const char *s, const char *sep); /* array of strings */ -char *array_join_with_sep(const Value *arr, const char *sep); /* join items as strings */ +/** Create newly allocated substring (bounds clamped). */ +char *string_substr(const char *s, int start, int len); +/** Find first index of needle in hay or -1. */ +int string_find(const char *hay, const char *needle); +/** Split C string by sep into Value array of strings. */ +Value string_split_to_array(const char *s, const char *sep); +/** Join Value array items into a newly allocated C string with separator. */ +char *array_join_with_sep(const Value *arr, const char *sep); #endif diff --git a/src/vm.c b/src/vm.c index ee3b568..1a4abf7 100644 --- a/src/vm.c +++ b/src/vm.c @@ -7,6 +7,16 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file vm.c + * @brief Core virtual machine implementation and opcode dispatch for Fun. + * + * Defines the VM state, stack helpers, debugger/stepping support, built-in + * opcode handlers (included as amalgamated C files), platform I/O helpers, + * and the main interpreter loop that executes bytecode produced by the Fun + * compiler. This file is the central runtime of the language. + */ + /* Ensure POSIX/XSI prototypes (nanosleep, wcwidth, etc.) are available * before any system headers are included by amalgamated .c files. */ #ifndef _WIN32 @@ -85,7 +95,19 @@ static __declspec(thread) VM *g_active_vm = NULL; static __thread VM *g_active_vm = NULL; #endif -/* fprintf wrapper that appends source line info for stderr messages */ +/** + * @brief fprintf-like wrapper that annotates stderr messages with VM source context. + * + * When writing to stderr and a VM is active, this function appends file name, + * line number, function name, opcode and instruction pointer information to the + * message. It attempts to map expanded preprocessed line numbers back to the + * original included file for clearer diagnostics. + * + * @param stream Output stream (typically stderr or stdout). + * @param fmt printf-style format string. + * @param ap Variable argument list corresponding to fmt. + * @return Number of characters written, as returned by vfprintf. + */ static int fun_vm_vfprintf(FILE *stream, const char *fmt, va_list ap) { int written = vfprintf(stream, fmt, ap); if (stream == stderr && g_active_vm) { @@ -155,6 +177,16 @@ static int fun_vm_vfprintf(FILE *stream, const char *fmt, va_list ap) { return written; } +/** + * @brief fprintf wrapper forwarding to fun_vm_vfprintf. + * + * Convenience wrapper that collects varargs and calls fun_vm_vfprintf so that + * VM-aware diagnostics are consistently applied in this translation unit. + * + * @param stream Output stream. + * @param fmt printf-style format string. + * @return Number of characters written. + */ static int fun_vm_fprintf(FILE *stream, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -172,6 +204,15 @@ static int fun_vm_fprintf(FILE *stream, const char *fmt, ...) { static __thread jmp_buf g_vm_err_jmp; +/** + * @brief Replacement for exit() used within this translation unit. + * + * If REPL-on-error is enabled for the active VM, this performs a longjmp back + * to vm_run so the REPL can be entered with the current VM state intact. + * Otherwise, terminates the process immediately using _Exit/_exit. + * + * @param code Exit status code (non-zero values indicate error conditions). + */ static void fun_vm_exit(int code) { if (g_active_vm && g_active_vm->repl_on_error) { /* Jump back to vm_run to allow dropping into the REPL with intact VM state */ @@ -194,6 +235,16 @@ static void push_value(VM *vm, Value v); /* Raise a runtime error that respects try/catch/finally semantics. * If a handler is installed for the current frame, jump to it and push * an error string for the catch clause. Otherwise, print and stop VM. */ +/** + * @brief Raise a runtime error inside the VM, honoring try/catch/finally. + * + * If the current frame has a pending try handler, control is transferred to + * that handler and the error message is pushed onto the stack for the catch + * clause. If no handler exists, the error is printed and the VM is stopped. + * + * @param vm Pointer to the VM instance. + * @param msg Human-readable error message (may be NULL). + */ void vm_raise_error(VM *vm, const char *msg) { if (!vm || vm->fp < 0) { fprintf(stderr, "Runtime error: %s\n", msg ? msg : ""); @@ -258,6 +309,12 @@ Dev tips: - You can run scripts/run_examples.sh to sanity-check examples quickly. */ +/** + * @brief Get a human-readable name for a ValueType. + * + * @param t The value type enum. + * @return Constant string naming the type (e.g., "int", "string"). + */ static const char *value_type_name(ValueType t) { switch (t) { case VAL_FUNCTION: @@ -281,6 +338,14 @@ static const char *value_type_name(ValueType t) { } } +/** + * @brief Clear the VM's buffered output values and partial flags. + * + * Frees any dynamic storage held by buffered output Values and resets the + * output counters and partial line indicators. + * + * @param vm VM instance whose output buffer should be cleared. + */ void vm_clear_output(VM *vm) { for (int i = 0; i < vm->output_count; ++i) { free_value(vm->output[i]); @@ -291,6 +356,14 @@ void vm_clear_output(VM *vm) { vm->output_is_partial[i] = 0; } +/** + * @brief Free resources owned directly by the VM structure. + * + * Currently a no-op because the VM does not allocate persistent internal + * resources outside frames, globals and outputs, which are managed elsewhere. + * + * @param vm VM instance to free resources for. + */ void vm_free(VM *vm) { // currently nothing persistent allocated inside VM itself } @@ -298,6 +371,14 @@ void vm_free(VM *vm) { /* forward declaration for helper used in vm_reset */ static void vm_pop_frame(VM *vm); +/** + * @brief Reset the VM to a clean state. + * + * Pops all frames (releasing local variables), clears the operand stack and + * globals, resets the output buffer and debugger state, and zeros the exit code. + * + * @param vm VM instance to reset. + */ void vm_reset(VM *vm) { // Pop all frames (free locals) while (vm->fp >= 0) { @@ -319,6 +400,11 @@ void vm_reset(VM *vm) { vm_debug_reset(vm); } +/** + * @brief Print all non-nil global variables to stdout for debugging. + * + * @param vm VM whose globals should be dumped. + */ void vm_dump_globals(VM *vm) { printf("=== globals ===\n"); for (int i = 0; i < MAX_GLOBALS; ++i) { @@ -333,6 +419,13 @@ void vm_dump_globals(VM *vm) { /* --- Debugger API impl --- */ +/** + * @brief Reset debugger state: breakpoints and stepping controls. + * + * Clears all breakpoints, disables stepping modes, and resets counters. + * + * @param vm VM instance with debugger state to reset. + */ void vm_debug_reset(VM *vm) { for (int i = 0; i < vm->break_count; ++i) { if (vm->breakpoints[i].file) { @@ -349,6 +442,14 @@ void vm_debug_reset(VM *vm) { vm->debug_stop_requested = 0; } +/** + * @brief Add a source breakpoint. + * + * @param vm VM instance. + * @param file Source file path (must not be NULL). + * @param line One-based source line number (> 0). + * @return Breakpoint id (>=0) on success, -1 on failure (invalid args or full). + */ int vm_debug_add_breakpoint(VM *vm, const char *file, int line) { if (!file || line <= 0) return -1; if (vm->break_count >= (int)(sizeof(vm->breakpoints) / sizeof(vm->breakpoints[0]))) return -1; @@ -359,6 +460,15 @@ int vm_debug_add_breakpoint(VM *vm, const char *file, int line) { return id; } +/** + * @brief Delete a breakpoint by id. + * + * Compacts the internal breakpoint list to keep ids dense. + * + * @param vm VM instance. + * @param id Breakpoint identifier previously returned by add. + * @return 1 if deleted, 0 if id was invalid. + */ int vm_debug_delete_breakpoint(VM *vm, int id) { if (id < 0 || id >= vm->break_count) return 0; if (vm->breakpoints[id].file) free(vm->breakpoints[id].file); @@ -374,10 +484,20 @@ int vm_debug_delete_breakpoint(VM *vm, int id) { return 1; } +/** + * @brief Remove all breakpoints from the VM. + * + * @param vm VM instance. + */ void vm_debug_clear_breakpoints(VM *vm) { vm_debug_reset(vm); } +/** + * @brief Print active breakpoints to stdout. + * + * @param vm VM instance. + */ void vm_debug_list_breakpoints(VM *vm) { if (vm->break_count <= 0) { printf("(no breakpoints)\n"); @@ -389,12 +509,22 @@ void vm_debug_list_breakpoints(VM *vm) { } } +/** + * @brief Request single-step execution (stop after next instruction). + * + * @param vm VM instance. + */ void vm_debug_request_step(VM *vm) { vm->debug_step_mode = 1; // step vm->debug_step_start_ic = vm->instr_count; vm->debug_stop_requested = 0; } +/** + * @brief Request step-over (stop after next instruction in current frame). + * + * @param vm VM instance. + */ void vm_debug_request_next(VM *vm) { vm->debug_step_mode = 2; // next (step over) vm->debug_step_target_fp = vm->fp; @@ -402,17 +532,35 @@ void vm_debug_request_next(VM *vm) { vm->debug_stop_requested = 0; } +/** + * @brief Request finish (run until the current frame returns). + * + * @param vm VM instance. + */ void vm_debug_request_finish(VM *vm) { vm->debug_step_mode = 3; // finish (until return) vm->debug_step_target_fp = vm->fp; vm->debug_stop_requested = 0; } +/** + * @brief Resume normal execution (clear stepping state and stop flag). + * + * @param vm VM instance. + */ void vm_debug_request_continue(VM *vm) { vm->debug_step_mode = 0; vm->debug_stop_requested = 0; } +/** + * @brief Push a Value onto the VM operand stack. + * + * Takes ownership of the provided Value. Aborts execution on overflow. + * + * @param vm VM instance. + * @param v Value to push (ownership transferred). + */ static void push_value(VM *vm, Value v) { if (vm->sp >= STACK_SIZE - 1) { fprintf(stderr, "Runtime error: stack overflow\n"); @@ -421,6 +569,14 @@ static void push_value(VM *vm, Value v) { vm->stack[++vm->sp] = v; /* take ownership of v */ } +/** + * @brief Pop a Value from the VM operand stack. + * + * Caller takes ownership of the returned Value. Aborts execution on underflow. + * + * @param vm VM instance. + * @return The top Value from the stack. + */ static Value pop_value(VM *vm) { if (vm->sp < 0) { fprintf(stderr, "Runtime error: stack underflow\n"); @@ -430,6 +586,15 @@ static Value pop_value(VM *vm) { } /* --- C ABI helpers for Rust FFI --- */ +/** + * @brief Pop a numeric Value and convert it to a 64-bit integer (C ABI helper). + * + * Accepts int or float Values on the stack. Other types raise a runtime type + * error. The popped Value is freed. + * + * @param vm VM instance. + * @return The numeric value converted to int64_t. + */ int64_t vm_pop_i64(VM *vm) { Value v = pop_value(vm); int64_t out = 0; @@ -447,39 +612,89 @@ int64_t vm_pop_i64(VM *vm) { return out; } +/** + * @brief Push a 64-bit integer as a VM int Value (C ABI helper). + * + * @param vm VM instance. + * @param v Integer value to push. + */ void vm_push_i64(VM *vm, int64_t v) { push_value(vm, make_int(v)); } /* --- Extended C ABI for Rust to access VM internals (unsafe) --- */ +/** + * @brief Return sizeof(VM) for external FFI consumers. + * + * @return Size of the VM struct in bytes. + */ size_t vm_sizeof(void) { return sizeof(VM); } +/** + * @brief Return sizeof(Value) for external FFI consumers. + * + * @return Size of the Value struct in bytes. + */ size_t vm_value_sizeof(void) { return sizeof(Value); } +/** + * @brief Cast the VM pointer to an opaque mutable void* (unsafe FFI helper). + * + * @param vm VM instance pointer. + * @return The same pointer reinterpreted as void*. + */ void *vm_as_mut_ptr(VM *vm) { return (void *)vm; } +/** + * @brief Obtain offsetof(VM, exit_code) for FFI struct field access. + * + * @return Byte offset of the exit_code field within VM. + */ size_t vm_offset_of_exit_code(void) { return offsetof(VM, exit_code); } +/** + * @brief Obtain offsetof(VM, sp) for FFI struct field access. + * + * @return Byte offset of the sp field within VM. + */ size_t vm_offset_of_sp(void) { return offsetof(VM, sp); } +/** + * @brief Obtain offsetof(VM, stack) for FFI struct field access. + * + * @return Byte offset of the stack field within VM. + */ size_t vm_offset_of_stack(void) { return offsetof(VM, stack); } +/** + * @brief Obtain offsetof(VM, globals) for FFI struct field access. + * + * @return Byte offset of the globals field within VM. + */ size_t vm_offset_of_globals(void) { return offsetof(VM, globals); } +/** + * @brief Initialize a call frame to a clean state. + * + * Sets function pointer and instruction pointer, zeroes locals to nil and + * resets the try-stack pointer. + * + * @param f Frame to initialize. + */ static void frame_init(Frame *f) { f->fn = NULL; f->ip = 0; @@ -488,6 +703,14 @@ static void frame_init(Frame *f) { f->try_sp = -1; } +/** + * @brief Initialize a VM instance to its default state. + * + * Resets stack/frame pointers, output buffers, instruction counters, debugger + * state and globals. Does not allocate memory. + * + * @param vm VM instance to initialize. + */ void vm_init(VM *vm) { vm->sp = -1; vm->fp = -1; @@ -517,6 +740,17 @@ void vm_init(VM *vm) { } /* push a new frame, transferring ownership of args[] into frame->locals[0..argc-1] */ +/** + * @brief Push a new call frame for a function and transfer arguments. + * + * The first argc Values from args are moved (ownership transfer) into the new + * frame's local slots starting at index 0. Aborts on frame stack overflow. + * + * @param vm VM instance. + * @param fn Function bytecode to execute in the new frame. + * @param argc Number of arguments provided. + * @param args Array of argument Values (may be NULL if argc == 0). + */ static void vm_push_frame(VM *vm, Bytecode *fn, int argc, Value *args) { if (vm->fp >= MAX_FRAMES - 1) { fprintf(stderr, "Runtime error: too many frames\n"); @@ -533,6 +767,13 @@ static void vm_push_frame(VM *vm, Bytecode *fn, int argc, Value *args) { } /* pop current frame and free its locals */ +/** + * @brief Pop the current call frame and free its local variables. + * + * Aborts if there is no active frame. + * + * @param vm VM instance. + */ static void vm_pop_frame(VM *vm) { if (vm->fp < 0) { fprintf(stderr, "Runtime error: pop frame with empty frame stack\n"); @@ -546,6 +787,13 @@ static void vm_pop_frame(VM *vm) { vm->fp--; } +/** + * @brief Print the VM's buffered output values to stdout. + * + * Emits a newline after each value unless the corresponding partial flag is set. + * + * @param vm VM instance whose output should be printed. + */ void vm_print_output(VM *vm) { for (int i = 0; i < vm->output_count; ++i) { print_value(&vm->output[i]); @@ -555,6 +803,16 @@ void vm_print_output(VM *vm) { } } +/** + * @brief Execute a bytecode program starting from the given entry point. + * + * Sets up the initial frame and runs the main interpreter loop until there are + * no more frames. Honors debugger stepping/finish/continue requests and, when + * enabled, traps exit paths to enter a REPL via on_error_repl. + * + * @param vm VM instance to run. + * @param entry Entry function bytecode (must not be NULL). + */ void vm_run(VM *vm, Bytecode *entry) { /* reset instruction count for this run */ vm->instr_count = 0; diff --git a/src/vm.h b/src/vm.h index c1427ea..37a2585 100644 --- a/src/vm.h +++ b/src/vm.h @@ -7,6 +7,15 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file vm.h + * @brief Core virtual machine data structures and public VM API. + * + * Declares the execution stack/frame layout, global state of the Fun VM, + * human-readable opcode names for diagnostics, and the public functions for + * initializing, running, resetting, and debugging the VM. FFI helper + * declarations for Rust/C++ experiments are also exposed here. + */ #ifndef FUN_VM_H #define FUN_VM_H @@ -74,6 +83,13 @@ static const char *opcode_names[] = { /* C++ demo */ "CPP_ADD"}; +/** + * @brief Call frame representing one active function invocation. + * + * Each frame keeps a pointer to its function bytecode, the current + * instruction pointer within that bytecode, a fixed-size array of local + * variables, and a small try/catch stack for exception handling. + */ typedef struct { Bytecode *fn; int ip; @@ -83,6 +99,14 @@ typedef struct { int try_sp; /* -1 when empty */ } Frame; +/** + * @brief The Fun virtual machine state. + * + * Holds the operand stack, call frames, globals, standard output capture, + * runtime counters, and debugger state. Functions in this header operate on + * this structure; callers must ensure proper initialization with vm_init() + * before use and call vm_free()/vm_reset() as appropriate. + */ struct VM { Value stack[STACK_SIZE]; int sp; @@ -120,76 +144,125 @@ struct VM { int break_count; // number of active breakpoints }; +/** @brief Opaque VM alias for external users. */ typedef struct VM VM; -// initialize VM (zero state) +/** + * @brief Initialize a VM instance to zero/initial state. + * @param vm Non-NULL pointer to VM storage to initialize. + */ void vm_init(VM *vm); -// helper function to clear the output +/** + * @brief Clear the buffered output captured by the VM. + * @param vm VM instance. + */ void vm_clear_output(VM *vm); +/** + * @brief Print buffered output entries to stdout (debug aid). + * @param vm VM instance. + */ void vm_print_output(VM *vm); +/** + * @brief Free all resources owned by the VM (globals, frames, output buffers). + * The VM object itself is not freed when allocated on the stack. + * @param vm VM instance to dispose. + */ void vm_free(VM *vm); -// reset VM to initial state (free globals/locals/output; keep VM object) +/** + * @brief Reset VM to initial state, freeing globals/locals/output. + * The VM object remains valid for reuse after this call. + * @param vm VM instance to reset. + */ void vm_reset(VM *vm); -// print non-nil globals (index and value) to stdout +/** + * @brief Print non-nil globals (index and value) to stdout. + * @param vm VM instance. + */ void vm_dump_globals(VM *vm); -// run entry Bytecode (pushes first frame) +/** + * @brief Execute the provided entry bytecode in the VM. + * Pushes an initial frame and runs until HALT or an unrecoverable error. + * @param vm VM instance. + * @param entry Entry bytecode to execute; must outlive the call. + */ void vm_run(VM *vm, Bytecode *entry); -/* Raise a runtime error that respects try/catch/finally. - * If a try handler is active in the current frame, control jumps to it - * with an error string pushed on the stack. Otherwise, prints the error - * (annotated with location) and terminates execution. */ +/** + * @brief Raise a runtime error honoring active try/catch/finally handlers. + * If a try handler is active in the current frame, control jumps to it with + * an error string pushed on the stack. Otherwise, prints the error (with + * location) and terminates execution. + * @param vm VM instance. + * @param msg Null-terminated error message. + */ void vm_raise_error(VM *vm, const char *msg); /* --- Debugger API --- */ +/** Reset debugger state (clear step mode and breakpoints). */ void vm_debug_reset(VM *vm); -int vm_debug_add_breakpoint(VM *vm, const char *file, int line); // returns id >=0 or -1 -int vm_debug_delete_breakpoint(VM *vm, int id); // returns 1 on success +/** Add a breakpoint at file:line; returns non-negative id on success or -1. */ +int vm_debug_add_breakpoint(VM *vm, const char *file, int line); +/** Delete a breakpoint by id; returns 1 on success, 0 on failure. */ +int vm_debug_delete_breakpoint(VM *vm, int id); +/** Remove all breakpoints. */ void vm_debug_clear_breakpoints(VM *vm); +/** Print the current list of breakpoints to stdout. */ void vm_debug_list_breakpoints(VM *vm); +/** Request single-step execution mode. */ void vm_debug_request_step(VM *vm); +/** Step over (next) within the current frame. */ void vm_debug_request_next(VM *vm); +/** Run until the current frame returns (finish). */ void vm_debug_request_finish(VM *vm); +/** Continue execution until next breakpoint/stop. */ void vm_debug_request_continue(VM *vm); +/** + * @brief Check whether an integer value corresponds to a defined opcode. + * @param op Numeric opcode to validate. + * @return 1 if valid, 0 otherwise. + */ static inline int opcode_is_valid(int op) { return op >= OP_NOP && op <= OP_CPP_ADD; // all current opcodes } /* --- Minimal C ABI helpers for FFI (Rust opcode experiments) --- */ -/* Pop an int64 from VM stack (errors if not an int/float); returns integer-converted value. */ +/** Pop an int64 from the VM stack; accepts int/float; returns truncated value. */ int64_t vm_pop_i64(VM *vm); -/* Push an int64 onto VM stack. */ +/** Push an int64 onto the VM stack. */ void vm_push_i64(VM *vm, int64_t v); -/* Example Rust-implemented opcode (adds top two ints on stack) */ +/** Example Rust-implemented opcode (adds top two ints on stack). */ int fun_op_radd(VM *vm); -/* Example Rust function returning a demo C string (null-terminated). */ +/** Return a demo null-terminated C string owned by Rust. */ const char *fun_rust_get_string(void); -/* Rust function that prints a passed C string, returns 0 on success. */ +/** Print a C string via Rust; returns 0 on success. */ int fun_rust_print_string(const char *msg); -/* Rust function that returns a newly allocated duplicate of the input C string. */ +/** Return a newly allocated duplicate of the input C string (caller frees). */ char *fun_rust_echo_string(const char *input); -/* Free a C string previously returned by fun_rust_echo_string. */ +/** Free a C string previously returned by fun_rust_echo_string(). */ void fun_rust_string_free(char *ptr); -/* C++ demo opcode entry point (C ABI) */ +/** C++ demo opcode entry point (C ABI). */ int fun_op_cpp_add(struct VM *vm); /* --- Extended C ABI for Rust to access VM internals (unsafe) --- */ -/* Size helpers for Rust side to compute offsets and do pointer math */ +/** Size of struct VM in bytes. */ size_t vm_sizeof(void); +/** Size of struct Value in bytes. */ size_t vm_value_sizeof(void); -/* Get a mutable byte pointer to the VM object. Extremely unsafe; intended for - * low-level FFI where Rust wants parity access with C code. */ +/** + * @brief Get a mutable byte pointer to the VM object. + * Extremely unsafe; for low-level FFI use only. + */ void *vm_as_mut_ptr(VM *vm); -/* Offsets of commonly accessed VM fields to avoid re-declaring the struct layout in Rust */ +/** Offsets of commonly accessed VM fields (for Rust FFI). */ size_t vm_offset_of_exit_code(void); size_t vm_offset_of_sp(void); size_t vm_offset_of_stack(void); diff --git a/src/vm/arithmetic/div.c b/src/vm/arithmetic/div.c index 26f7fbd..bc6d025 100644 --- a/src/vm/arithmetic/div.c +++ b/src/vm/arithmetic/div.c @@ -9,24 +9,28 @@ /** * @file div.c - * @brief Implements the OP_DIV opcode for integer division in the VM. + * @brief Implements the OP_DIV opcode (numeric division) in the VM. * - * This file handles the OP_DIV instruction, which performs integer division - * on two integer values popped from the stack and pushes the result back onto the stack. + * Handles the OP_DIV instruction, dividing two numeric operands and pushing + * the result. If either operand is a float, division is performed in double + * precision and a VAL_FLOAT is produced; otherwise integer division is + * used and a VAL_INT is produced. * * Behavior: - * - Pops two integer values from the stack. - * - Performs integer division (`a / b`). - * - Pushes the result back onto the stack. + * - Pops two values from the stack. + * - If any operand is VAL_FLOAT, computes (double)a / (double)b and pushes a VAL_FLOAT. + * - Else computes a.i / b.i and pushes a VAL_INT. * * Error Handling: - * - Exits with an error if the operands are not integers. - * - Exits with an error if division by zero is attempted. + * - Raises a runtime error and aborts execution if operands are not numeric. + * - Raises a runtime error on division by zero (both integer and floating cases). * * Example: * // Bytecode: OP_DIV * // Stack before: [10, 2] * // Stack after: [5] + * // Stack before: [5.0, 2] + * // Stack after: [2.5] * * @author Johannes Findeisen * @date 2025-10-16 diff --git a/src/vm/arithmetic/mul.c b/src/vm/arithmetic/mul.c index b4eafec..3e63310 100644 --- a/src/vm/arithmetic/mul.c +++ b/src/vm/arithmetic/mul.c @@ -9,23 +9,27 @@ /** * @file mul.c - * @brief Implements the OP_MUL opcode for integer multiplication in the VM. + * @brief Implements the OP_MUL opcode (numeric multiplication) in the VM. * - * This file handles the OP_MUL instruction, which performs integer multiplication - * on two integer values popped from the stack and pushes the result back onto the stack. + * Handles the OP_MUL instruction, multiplying two numeric operands and + * pushing the result. If either operand is a float, multiplication is + * performed in double precision; otherwise it is 64-bit integer + * multiplication. * * Behavior: - * - Pops two integer values from the stack. - * - Performs integer multiplication (`a * b`). - * - Pushes the result back onto the stack. + * - Pops two values from the stack. + * - If any operand is VAL_FLOAT, computes (double)a * (double)b and pushes a VAL_FLOAT. + * - Else computes a.i * b.i and pushes a VAL_INT. * * Error Handling: - * - Exits with an error if the operands are not integers. + * - Raises a runtime error and aborts execution if operands are not numeric. * * Example: * // Bytecode: OP_MUL * // Stack before: [3, 4] * // Stack after: [12] + * // Stack before: [2.5, 4] + * // Stack after: [10.0] * * @author Johannes Findeisen * @date 2025-10-16 diff --git a/src/vm/arithmetic/sub.c b/src/vm/arithmetic/sub.c index e0939d1..7602620 100644 --- a/src/vm/arithmetic/sub.c +++ b/src/vm/arithmetic/sub.c @@ -9,23 +9,27 @@ /** * @file sub.c - * @brief Implements the OP_SUB opcode for integer subtraction in the VM. + * @brief Implements the OP_SUB opcode (numeric subtraction) in the VM. * - * This file handles the OP_SUB instruction, which performs integer subtraction - * on two integer values popped from the stack and pushes the result back onto the stack. + * Handles the OP_SUB instruction, subtracting two numeric operands and + * pushing the result. If either operand is a float, subtraction is + * performed in double precision; otherwise it is 64-bit integer + * subtraction. * * Behavior: - * - Pops two integer values from the stack. - * - Performs integer subtraction (`a - b`). - * - Pushes the result back onto the stack. + * - Pops two values from the stack. + * - If any operand is VAL_FLOAT, computes (double)a - (double)b and pushes a VAL_FLOAT. + * - Else computes a.i - b.i and pushes a VAL_INT. * * Error Handling: - * - Exits with an error if the operands are not integers. + * - Raises a runtime error and aborts execution if operands are not numeric. * * Example: * // Bytecode: OP_SUB * // Stack before: [10, 4] * // Stack after: [6] + * // Stack before: [10.0, 3] + * // Stack after: [7.0] * * @author Johannes Findeisen * @date 2025-10-16 diff --git a/src/vm/arrays/clear.c b/src/vm/arrays/clear.c index acaf1b7..ce7a8ae 100644 --- a/src/vm/arrays/clear.c +++ b/src/vm/arrays/clear.c @@ -8,26 +8,27 @@ */ /** -* @file clear.c + * @file clear.c * @brief Implements the OP_CLEAR opcode for clearing arrays in the VM. - * - * This file handles the OP_CLEAR instruction, which clears all elements from an array. - * The array is popped from the stack, and nothing is pushed back. - * + + * Handles the OP_CLEAR instruction, which removes all elements from an array. + * The array is popped from the stack; the opcode pushes an integer result + * (currently 0) as an acknowledgement. + * Behavior: * - Pops the array from the stack. - * - Clears all elements from the array. - * - * Error Handling: - * - Exits with an error if the array is of the wrong type. - * - * Example: + * - Clears all elements from the array (array becomes empty in place). + * - Pushes 0 (integer) to acknowledge success. + * Error Handling: + * - Exits with a runtime error if the operand is not an array. + + * Example: * // Bytecode: OP_CLEAR * // Stack before: [[10, 20, 30]] - * // Stack after: [] - * - * @author Johannes Findeise + * // Stack after: [0] + + * @author Johannes Findeisen * @date 2025-10-16 */ diff --git a/src/vm/arrays/make_array.c b/src/vm/arrays/make_array.c index b205b06..65c0ef2 100644 --- a/src/vm/arrays/make_array.c +++ b/src/vm/arrays/make_array.c @@ -28,7 +28,7 @@ * // Stack before: [1, 2, 3] * // Stack after: [[1, 2, 3]] * - * @author Johanes Findeisen + * @author Johannes Findeisen * @date 2025-10-16 */ diff --git a/src/vm/arrays/push.c b/src/vm/arrays/push.c index 68555cd..28f3f4c 100644 --- a/src/vm/arrays/push.c +++ b/src/vm/arrays/push.c @@ -8,11 +8,12 @@ */ /** - * @file arr_push.c - * @brief Implements the OP_ARR_PUSH opcode for appending elements to arrays in the VM. + * @file push.c + * @brief Implements the OP_PUSH opcode for appending elements to arrays in the VM. * - * This file handles the OP_ARR_PUSH instruction, which appends a value to the end of an array. - * The array and value are popped from the stack, and the new length of the array is pushed back onto the stack. + * Handles the OP_PUSH instruction, which appends a value to the end of an array. + * The array and value are popped from the stack; the opcode pushes the new array + * length (integer) as a result. * * Behavior: * - Pops the value and array from the stack. @@ -20,11 +21,11 @@ * - Pushes the new length of the array onto the stack. * * Error Handling: - * - Exits with an error if the array is of the wrong type. - * - Exits with an error if memory allocation fails during the append operation. + * - Exits with a runtime error if the first operand is not an array. + * - Exits with a runtime error if memory allocation fails. * * Example: - * // Bytecode: OP_ARR_PUSH + * // Bytecode: OP_PUSH * // Stack before: [42, [10, 20, 30]] * // Stack after: [4] * diff --git a/src/vm/arrays/remove.c b/src/vm/arrays/remove.c index 05e810f..ff1f43a 100644 --- a/src/vm/arrays/remove.c +++ b/src/vm/arrays/remove.c @@ -8,11 +8,11 @@ */ /** - * @file arr_remove.c - * @brief Implements the OP_ARR_REMOVE opcode for removing elements from arrays in the VM. + * @file remove.c + * @brief Implements the OP_REMOVE opcode for removing elements from arrays in the VM. * - * This file handles the OP_ARR_REMOVE instruction, which removes an element from an array - * at a specified index. The array and index are popped from the stack, and the removed + * Handles the OP_REMOVE instruction, which removes an element from an array at the + * specified index. The array and index are popped from the stack; the removed * element is pushed back onto the stack. * * Behavior: @@ -21,11 +21,11 @@ * - Pushes the removed element onto the stack. * * Error Handling: - * - Exits with an error if the array or index is of the wrong type. - * - Exits with an error if the index is out of bounds. + * - Exits with a runtime error if the container is not an array or index is not an int. + * - Exits with a runtime error if the index is out of bounds. * * Example: - * // Bytecode: OP_ARR_REMOVE + * // Bytecode: OP_REMOVE * // Stack before: [1, [10, 20, 30]] * // Stack after: [20] * diff --git a/src/vm/arrays/set.c b/src/vm/arrays/set.c index 4066741..70534c0 100644 --- a/src/vm/arrays/set.c +++ b/src/vm/arrays/set.c @@ -8,23 +8,24 @@ */ /** - * @file arr_set.c - * @brief Implements the OP_ARR_SET opcode for setting elements in arrays in the VM. + * @file set.c + * @brief Implements the OP_SET opcode for setting elements in arrays in the VM. * - * This file handles the OP_ARR_SET instruction, which sets a value at a specified index in an array. - * The value, index, and array are popped from the stack, and the value is pushed back onto the stack. + * Handles the OP_SET instruction, which sets a value at a specified index in an array. + * The value, index, and array are popped from the stack; the value is returned back + * on the stack (as a copy) to mirror expression semantics. * * Behavior: * - Pops the value, index, and array from the stack. * - Sets the value at the specified index in the array. - * - Pushes the value back onto the stack. + * - Pushes a copy of the value back onto the stack. * * Error Handling: - * - Exits with an error if the array or index is of the wrong type. - * - Exits with an error if the index is out of bounds. + * - Exits with a runtime error if the array or index is of the wrong type. + * - Exits with a runtime error if the index is out of bounds. * * Example: - * // Bytecode: OP_ARR_SET + * // Bytecode: OP_SET * // Stack before: [42, 1, [10, 20, 30]] * // Stack after: [42] * diff --git a/src/vm/bitwise/band.c b/src/vm/bitwise/band.c index 230225b..92e80da 100644 --- a/src/vm/bitwise/band.c +++ b/src/vm/bitwise/band.c @@ -5,14 +5,28 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2025-09-29 */ + /** + * @file band.c + * @brief Implements the OP_BAND opcode (bitwise AND). + * + * Opcode snippet included by vm.c. Performs a 32-bit unsigned bitwise AND + * on two integer operands from the VM stack. + */ + /** - * OP_BAND: bitwise AND (uint32 32-bit) - * pops: b, a - * pushes: (uint32_t)(a & b) + * OP_BAND: bitwise AND (uint32) + * + * Stack effects: + * - pops: b, a + * - pushes: (uint32_t)(a & b) + * + * Notes: + * - Operands are interpreted as 32-bit unsigned when of type VAL_INT; + * non-integer values are treated as 0. + * - The result is pushed as VAL_INT with the 32-bit value preserved in the + * low bits. */ case OP_BAND: { Value vb = pop_value(vm); diff --git a/src/vm/bitwise/bnot.c b/src/vm/bitwise/bnot.c index 29e2a9a..19ed68f 100644 --- a/src/vm/bitwise/bnot.c +++ b/src/vm/bitwise/bnot.c @@ -5,14 +5,28 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2025-09-29 */ + /** + * @file bnot.c + * @brief Implements the OP_BNOT opcode (bitwise NOT). + * + * Opcode snippet included by vm.c. Performs a 32-bit unsigned bitwise NOT + * on a single integer operand from the VM stack. + */ + /** - * OP_BNOT: bitwise NOT (uint32 32-bit) - * pops: a - * pushes: (uint32_t)(~a) + * OP_BNOT: bitwise NOT (uint32) + * + * Stack effects: + * - pops: a + * - pushes: (uint32_t)(~a) + * + * Notes: + * - Operand is interpreted as 32-bit unsigned when of type VAL_INT; + * non-integer values are treated as 0. + * - The result is pushed as VAL_INT with the 32-bit value preserved in the + * low bits. */ case OP_BNOT: { Value va = pop_value(vm); diff --git a/src/vm/bitwise/bor.c b/src/vm/bitwise/bor.c index 3e7605b..b9012f4 100644 --- a/src/vm/bitwise/bor.c +++ b/src/vm/bitwise/bor.c @@ -5,14 +5,28 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2025-09-29 */ + /** + * @file bor.c + * @brief Implements the OP_BOR opcode (bitwise OR). + * + * Opcode snippet included by vm.c. Performs a 32-bit unsigned bitwise OR + * on two integer operands from the VM stack. + */ + /** - * OP_BOR: bitwise OR (uint32 32-bit) - * pops: b, a - * pushes: (uint32_t)(a | b) + * OP_BOR: bitwise OR (uint32) + * + * Stack effects: + * - pops: b, a + * - pushes: (uint32_t)(a | b) + * + * Notes: + * - Operands are interpreted as 32-bit unsigned when of type VAL_INT; + * non-integer values are treated as 0. + * - The result is pushed as VAL_INT with the 32-bit value preserved in the + * low bits. */ case OP_BOR: { Value vb = pop_value(vm); diff --git a/src/vm/bitwise/bxor.c b/src/vm/bitwise/bxor.c index f886edc..95f9787 100644 --- a/src/vm/bitwise/bxor.c +++ b/src/vm/bitwise/bxor.c @@ -5,14 +5,28 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2025-09-29 */ /** - * OP_BXOR: bitwise XOR (uint32 32-bit) - * pops: b, a - * pushes: (uint32_t)(a ^ b) + * @file bxor.c + * @brief Implements the OP_BXOR opcode (bitwise XOR). + * + * This file is an opcode snippet included by vm.c. It implements a + * 32-bit unsigned bitwise XOR of two integer operands from the VM stack. + */ + +/** + * OP_BXOR: bitwise XOR (uint32) + * + * Stack effects: + * - pops: b, a + * - pushes: (uint32_t)(a ^ b) + * + * Notes: + * - Operands are taken as 32-bit unsigned integers when of type VAL_INT; + * non-integer values are treated as 0. + * - Result is pushed back as VAL_INT, preserving 32-bit value in the + * low bits of the 64-bit integer storage. */ case OP_BXOR: { Value vb = pop_value(vm); diff --git a/src/vm/bitwise/rol.c b/src/vm/bitwise/rol.c index 46ff010..5e32bed 100644 --- a/src/vm/bitwise/rol.c +++ b/src/vm/bitwise/rol.c @@ -5,14 +5,27 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file rol.c + * @brief Implements the OP_ROTL opcode (rotate-left). * - * Added: 2025-09-29 + * Opcode snippet included by vm.c. Performs a 32-bit unsigned rotate-left of + * an integer operand by a masked rotation count. */ /** * OP_ROTL: rotate left (uint32) - * pops: s, a - * pushes: rotl32(a, s) + * + * Stack effects: + * - pops: s, a + * - pushes: (a << s) | (a >> (32 - s)), with s masked to 0..31 + * + * Notes: + * - Both a (value) and s (count) are taken from VAL_INT; non-integers are 0. + * - The rotation count is masked to 0..31. A zero rotation returns a unchanged. + * - Result is pushed as VAL_INT with the 32-bit value preserved in the low bits. */ case OP_ROTL: { Value vs = pop_value(vm); diff --git a/src/vm/bitwise/ror.c b/src/vm/bitwise/ror.c index 26274b2..6b46aad 100644 --- a/src/vm/bitwise/ror.c +++ b/src/vm/bitwise/ror.c @@ -5,14 +5,27 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file ror.c + * @brief Implements the OP_ROTR opcode (rotate-right). * - * Added: 2025-09-29 + * Opcode snippet included by vm.c. Performs a 32-bit unsigned rotate-right of + * an integer operand by a masked rotation count. */ /** * OP_ROTR: rotate right (uint32) - * pops: s, a - * pushes: rotr32(a, s) + * + * Stack effects: + * - pops: s, a + * - pushes: (a >> s) | (a << (32 - s)), with s masked to 0..31 + * + * Notes: + * - Both a (value) and s (count) are taken from VAL_INT; non-integers are 0. + * - The rotation count is masked to 0..31. A zero rotation returns a unchanged. + * - Result is pushed as VAL_INT with the 32-bit value preserved in the low bits. */ case OP_ROTR: { Value vs = pop_value(vm); diff --git a/src/vm/bitwise/shl.c b/src/vm/bitwise/shl.c index 7ad1053..6ac9acc 100644 --- a/src/vm/bitwise/shl.c +++ b/src/vm/bitwise/shl.c @@ -5,14 +5,27 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file shl.c + * @brief Implements the OP_SHL opcode (logical left shift). * - * Added: 2025-09-29 + * Opcode snippet included by vm.c. Performs a 32-bit unsigned logical left + * shift of an integer operand by a masked shift count. */ /** * OP_SHL: logical left shift (uint32) - * pops: s, a - * pushes: (uint32_t)(a << (s&31)) + * + * Stack effects: + * - pops: s, a + * - pushes: (uint32_t)(a << (s & 31)) + * + * Notes: + * - Both a (value) and s (shift) are taken from VAL_INT; non-integers are 0. + * - The shift count is masked to 0..31. A zero shift returns a unchanged. + * - Result is pushed as VAL_INT with the 32-bit value preserved in the low bits. */ case OP_SHL: { Value vs = pop_value(vm); diff --git a/src/vm/bitwise/shr.c b/src/vm/bitwise/shr.c index 7a9bfc2..8daba6a 100644 --- a/src/vm/bitwise/shr.c +++ b/src/vm/bitwise/shr.c @@ -5,14 +5,28 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file shr.c + * @brief Implements the OP_SHR opcode (logical right shift). * - * Added: 2025-09-29 + * Opcode snippet included by vm.c. Performs a 32-bit unsigned logical right + * shift of an integer operand by a masked shift count. */ /** * OP_SHR: logical right shift (uint32) - * pops: s, a - * pushes: (uint32_t)(a >> (s&31)) using logical shift + * + * Stack effects: + * - pops: s, a + * - pushes: (uint32_t)(a >> (s & 31)) + * + * Notes: + * - Both a (value) and s (shift) are taken from VAL_INT; non-integers are 0. + * - The shift count is masked to 0..31. A zero shift returns a unchanged. + * - Logical (zero-filling) right shift is used (no sign extend). + * - Result is pushed as VAL_INT with the 32-bit value preserved in the low bits. */ case OP_SHR: { Value vs = pop_value(vm); diff --git a/src/vm/core/throw.c b/src/vm/core/throw.c index c4da59c..d292643 100644 --- a/src/vm/core/throw.c +++ b/src/vm/core/throw.c @@ -7,6 +7,32 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file throw.c + * @brief Implements the OP_THROW opcode for raising exceptions in the VM. + * + * This file handles the OP_THROW instruction, which raises an exception. If a + * matching TRY handler exists in the current frame (pushed via OP_TRY_PUSH), + * control flow jumps to the handler location and the error value is made + * available to the catch block via the stack. If no handler is present in the + * current frame, the error is printed and the VM terminates execution by + * clearing the frame stack. + * + * Behavior: + * - Pops the error `Value` from the stack. + * - If the current frame has a pending TRY handler (f->try_sp >= 0): + * - Retrieves the handler target IP from the TRY instruction's operand. + * - Pushes the error back on the stack for the catch block to consume. + * - Sets the instruction pointer (IP) to the handler target. + * - Otherwise (no handler): + * - Prints the error in a human-readable form. + * - Frees the error value and clears all frames (vm->fp = -1) to stop the VM. + * + * Errors: + * - None explicitly thrown here; if unhandled, the VM stops after printing the + * error message. + */ + case OP_THROW: { Value err = pop_value(vm); /* if there is a handler in this frame, jump to it and push err for catch */ diff --git a/src/vm/core/try_pop.c b/src/vm/core/try_pop.c index 3ac70ec..c2721ec 100644 --- a/src/vm/core/try_pop.c +++ b/src/vm/core/try_pop.c @@ -7,6 +7,22 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file try_pop.c + * @brief Implements the OP_TRY_POP opcode to end a try/catch region. + * + * This file handles the OP_TRY_POP instruction, which marks the end of the + * most recently started try/catch region in the current frame by popping its + * entry from the per-frame TRY stack. + * + * Behavior: + * - If f->try_sp >= 0, decrements f->try_sp (pops one TRY region). + * - Does not modify the value stack. + * + * Errors: + * - None; popping when no TRY is active is a no-op. + */ + case OP_TRY_POP: { if (f->try_sp >= 0) f->try_sp--; break; diff --git a/src/vm/core/try_push.c b/src/vm/core/try_push.c index f8a62f6..41aebd5 100644 --- a/src/vm/core/try_push.c +++ b/src/vm/core/try_push.c @@ -7,6 +7,25 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file try_push.c + * @brief Implements the OP_TRY_PUSH opcode to begin a try/catch region. + * + * This file handles the OP_TRY_PUSH instruction, which marks the start of a + * try/catch region in the current frame by pushing the index of the TRY + * instruction onto a small per-frame stack. The actual catch target IP is + * stored in the TRY instruction's operand and may be patched later by the + * compiler/linker. + * + * Behavior: + * - Pushes the index of this TRY instruction (f->ip - 1) onto f->try_stack. + * - Does not modify the value stack. + * + * Errors: + * - If the try depth exceeds the size of f->try_stack, prints a runtime error + * and terminates the process. + */ + case OP_TRY_PUSH: { /* push index of this TRY instruction; handler ip is in its operand (may be patched later) */ if (f->try_sp >= (int)(sizeof(f->try_stack) / sizeof(f->try_stack[0])) - 1) { diff --git a/src/vm/cpp/add.cpp b/src/vm/cpp/add.cpp index 49b503a..cfb3b06 100644 --- a/src/vm/cpp/add.cpp +++ b/src/vm/cpp/add.cpp @@ -1,17 +1,26 @@ -/* +/** * 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-29 */ -/* - * Minimal C++ opcode for Fun VM — cpp_add - * Build is gated by -DFUN_WITH_CPP=ON (see CMake). +/** + * @file add.cpp + * @brief Fun VM C++ opcode snippet: add two 64-bit integers (cpp_add). + * + * This opcode is compiled and linked only when the CMake option + * `-DFUN_WITH_CPP=ON` is enabled. It demonstrates how to implement a + * VM opcode in C++ while exposing a C ABI symbol for the VM dispatcher. + * + * Stack behavior: + * - Pops: b:int64, a:int64 + * - Pushes: (a + b):int64 + * + * Error handling and type conversions (e.g., from other numeric types to + * int64) are delegated to the VM helpers `vm_pop_i64` and `vm_push_i64`. */ #include @@ -20,6 +29,21 @@ extern "C" { #include "vm.h" // C header; provides VM, vm_pop_i64, vm_push_i64 } +/** + * @brief Add two 64-bit integers from the VM stack and push the sum. + * + * Pops two values from the VM stack using `vm_pop_i64`, adds them as + * 64-bit signed integers, and pushes the result via `vm_push_i64`. + * + * Stack effect: + * - Input: [..., a:int64, b:int64] + * - Output: [..., (a+b):int64] + * + * @param vm Pointer to the VM instance. Must not be NULL. + * @return 0 on success. Any stack underflow or conversion errors are + * handled by the VM helpers; non-zero may be used by future + * implementations to indicate a runtime error. + */ extern "C" int fun_op_cpp_add(VM *vm) { int64_t a = vm_pop_i64(vm); int64_t b = vm_pop_i64(vm); diff --git a/src/vm/curl/download.c b/src/vm/curl/download.c index 8c93f0b..f3af315 100644 --- a/src/vm/curl/download.c +++ b/src/vm/curl/download.c @@ -1,5 +1,41 @@ /** - * libcurl DOWNLOAD builtin + * 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 + */ + +/** + * @file download.c + * @brief Fun VM opcode snippet: HTTP download to file via libcurl (OP_CURL_DOWNLOAD). + * + * This snippet is included by vm.c and implements the OP_CURL_DOWNLOAD + * instruction. When FUN_WITH_CURL is enabled, it downloads the content at + * the given URL and writes it to the specified filesystem path. + * + * Stack behavior: + * - Pops: path:string, url:string (values are converted via value_to_string_alloc) + * - Pushes: int (1 on success, 0 on error or when CURL is disabled) + * + * Error handling: + * - Returns 0 if URL/path conversion fails, file open fails, CURL init + * or perform fails. + * - Follows redirects (CURLOPT_FOLLOWLOCATION = 1L). + * - Writes via fun_curl_file_write_cb directly into the opened FILE*. + * + * Notes: + * - All temporary allocations (URL, path) are freed; FILE* is closed. + * - On builds without FUN_WITH_CURL, consumes two values and pushes 0. + */ +/** + * @brief Opcode handler for OP_CURL_DOWNLOAD. + * + * Pops a destination path and URL, streams the HTTP response body + * into the file, and pushes 1 on success or 0 on any error. Without + * FUN_WITH_CURL, behaves as a no-op that consumes two values and + * pushes 0. */ case OP_CURL_DOWNLOAD: { #ifdef FUN_WITH_CURL diff --git a/src/vm/curl/get.c b/src/vm/curl/get.c index 729ef73..730abb5 100644 --- a/src/vm/curl/get.c +++ b/src/vm/curl/get.c @@ -1,5 +1,42 @@ /** - * libcurl GET builtin + * 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 + */ + +/** + * @file get.c + * @brief Fun VM opcode snippet: HTTP GET via libcurl (OP_CURL_GET). + * + * This snippet is included by vm.c and implements the OP_CURL_GET + * instruction. When FUN_WITH_CURL is enabled, it performs an HTTP GET + * request for the provided URL and pushes the response body as a string. + * If libcurl support is not built in, or an error occurs, an empty string + * is pushed instead. + * + * Stack behavior: + * - Pops: url:string (any value is converted via value_to_string_alloc) + * - Pushes: body:string ("" on error or when CURL is disabled) + * + * Error handling: + * - If URL conversion fails or CURL initialization/performance fails, + * the opcode pushes an empty string. + * - Follows HTTP redirects (CURLOPT_FOLLOWLOCATION = 1L). + * + * Notes: + * - Uses FunCurlBuf and fun_curl_write_cb from the curl extension helpers. + * - Memory allocated for temporary strings and buffers is freed before exit. + */ +/** + * @brief Opcode handler for OP_CURL_GET. + * + * Converts the top stack value to a URL string, issues a GET request + * using libcurl, and pushes the response body as a string. When + * compiled without FUN_WITH_CURL, the opcode becomes a no-op that + * consumes one value and pushes an empty string. */ case OP_CURL_GET: { #ifdef FUN_WITH_CURL diff --git a/src/vm/curl/post.c b/src/vm/curl/post.c index b8e1526..e6af2e8 100644 --- a/src/vm/curl/post.c +++ b/src/vm/curl/post.c @@ -1,5 +1,42 @@ /** - * libcurl POST builtin + * 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 + */ + +/** + * @file post.c + * @brief Fun VM opcode snippet: HTTP POST via libcurl (OP_CURL_POST). + * + * This snippet is included by vm.c and implements the OP_CURL_POST + * instruction. When FUN_WITH_CURL is enabled, it performs an HTTP POST + * to the given URL with the provided request body and pushes the response + * body as a string. If libcurl support is not built in, or an error occurs, + * an empty string is pushed instead. + * + * Stack behavior: + * - Pops: body:string, url:string (values are converted via value_to_string_alloc) + * - Pushes: body:string (server response; "" on error or when CURL is disabled) + * + * Error handling: + * - If URL conversion fails, the opcode pushes an empty string and discards + * any converted body. + * - Follows redirects (CURLOPT_FOLLOWLOCATION = 1L). + * - Sets CURLOPT_POST=1L and CURLOPT_POSTFIELDS to submit the body. + * + * Notes: + * - Uses FunCurlBuf and fun_curl_write_cb from the curl extension helpers. + * - All temporary allocations (URL, body, response buffer) are freed. + */ +/** + * @brief Opcode handler for OP_CURL_POST. + * + * Pops the POST body and URL, performs an HTTP POST, and pushes the + * response as a string. Without FUN_WITH_CURL, consumes two values and + * pushes an empty string. */ case OP_CURL_POST: { #ifdef FUN_WITH_CURL diff --git a/src/vm/echo.c b/src/vm/echo.c index 0d40dcc..c6c9b87 100644 --- a/src/vm/echo.c +++ b/src/vm/echo.c @@ -1,4 +1,4 @@ -/* +/** * This file is part of the Fun programming language. * https://fun-lang.xyz/ * @@ -8,11 +8,21 @@ */ /** - * Implements OP_ECHO: print top-of-stack value without trailing newline. - * Now stores the value into the VM's output buffer and marks it as partial, - * so the CLI can render echo output together with following print output. + * @file echo.c + * @brief Implements the OP_ECHO opcode for printing without a trailing newline. + * + * This snippet is included into the VM dispatch loop and handles OP_ECHO. + * It pops the top value from the stack and appends it to the VM's output buffer + * but marks the entry as partial so that subsequent OP_PRINT may continue the + * same line. + * + * Stack contract: + * - Pops: value (any) + * - Pushes: (none) */ +/* Implements OP_ECHO: print top-of-stack value without trailing newline. */ + case OP_ECHO: { Value v = pop_value(vm); Value snap = deep_copy_value(&v); diff --git a/src/vm/ini/free.c b/src/vm/ini/free.c index a385a38..41799ec 100644 --- a/src/vm/ini/free.c +++ b/src/vm/ini/free.c @@ -1,14 +1,34 @@ -/* +/** * 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-11-30 */ +/** + * @file free.c + * @brief VM opcode snippet for releasing an INI handle (OP_INI_FREE). + * + * This file is included into the main VM dispatch switch in vm.c. It is only + * compiled when FUN_WITH_INI is enabled and iniparser headers are available. + * + * Opcode: OP_INI_FREE + * Stack: [handle:int] -> [ok:int] + * + * Behavior + * - Pops an integer handle referring to an INI dictionary previously returned + * by OP_INI_LOAD. + * - Attempts to close the underlying dictionary and free the registry slot. + * - Pushes 1 on success, 0 if the handle was invalid or already freed. + * + * Errors + * - No VM error is thrown for invalid handles; the opcode simply returns 0. + * + * See also + * - ini_alloc_handle(), ini_free_handle() in src/vm/ini/handles.c + */ /* OP_INI_FREE: pops handle; pushes 1/0 */ #ifdef FUN_WITH_INI case OP_INI_FREE: { diff --git a/src/vm/ini/get_bool.c b/src/vm/ini/get_bool.c index c33233e..e9b9b5a 100644 --- a/src/vm/ini/get_bool.c +++ b/src/vm/ini/get_bool.c @@ -1,14 +1,33 @@ -/* +/** * 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-10 (split from getters.c) */ +/** + * @file get_bool.c + * @brief VM opcode snippet for reading a boolean from an INI dictionary (OP_INI_GET_BOOL). + * + * Included by vm.c when FUN_WITH_INI is enabled. + * + * Opcode: OP_INI_GET_BOOL + * Stack: [default:int|bool] [key:string] [section:string] [handle:int] -> [out:int] + * + * Behavior + * - Pops default value (0/1), key, section, and handle. + * - Looks up the entry "section:key" in the referenced dictionary. If not found, + * also tries a dotted variant "section.key" for compatibility. + * - Accepts textual booleans (true/false, yes/no, on/off; case-insensitive) and + * numeric values (non-zero => true). Falls back to the provided default when + * parsing fails or entry is missing. + * - Pushes 1 for true or 0 for false. + * + * Errors + * - Invalid handle or arguments simply yield the default value; no exception. + */ /* OP_INI_GET_BOOL */ #ifdef FUN_WITH_INI case OP_INI_GET_BOOL: { diff --git a/src/vm/ini/get_double.c b/src/vm/ini/get_double.c index 8f14e97..0533242 100644 --- a/src/vm/ini/get_double.c +++ b/src/vm/ini/get_double.c @@ -1,14 +1,27 @@ -/* +/** * 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-10 (split from getters.c) */ +/** + * @file get_double.c + * @brief VM opcode snippet for reading a floating-point value from INI (OP_INI_GET_DOUBLE). + * + * Opcode: OP_INI_GET_DOUBLE + * Stack: [default:float|int] [key:string] [section:string] [handle:int] -> [out:float] + * + * Behavior + * - Pops default, key, section, and handle; looks up "section:key" (and + * dotted fallback) and attempts to parse as double using strtod(). + * - If lookup or parsing fails, pushes the provided default. + * + * Errors + * - Invalid handle/args simply produce the default; no exception raised. + */ /* OP_INI_GET_DOUBLE */ #ifdef FUN_WITH_INI case OP_INI_GET_DOUBLE: { diff --git a/src/vm/ini/get_int.c b/src/vm/ini/get_int.c index ac564ad..4f04cf7 100644 --- a/src/vm/ini/get_int.c +++ b/src/vm/ini/get_int.c @@ -1,14 +1,27 @@ -/* +/** * 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-10 (split from getters.c) */ +/** + * @file get_int.c + * @brief VM opcode snippet for reading an integer from INI (OP_INI_GET_INT). + * + * Opcode: OP_INI_GET_INT + * Stack: [default:int] [key:string] [section:string] [handle:int] -> [out:int] + * + * Behavior + * - Pops default, key, section, and handle; looks up "section:key" (and dotted + * fallback) and attempts to parse as base-10 integer using strtol(). + * - If lookup or parsing fails, returns the provided default. + * + * Errors + * - Invalid handle/args produce the default; no VM exception is raised. + */ /* OP_INI_GET_INT */ #ifdef FUN_WITH_INI case OP_INI_GET_INT: { diff --git a/src/vm/ini/get_string.c b/src/vm/ini/get_string.c index 460ff76..a6f108f 100644 --- a/src/vm/ini/get_string.c +++ b/src/vm/ini/get_string.c @@ -1,14 +1,27 @@ -/* +/** * 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-10 (split from getters.c) */ +/** + * @file get_string.c + * @brief VM opcode snippet for reading a string from INI (OP_INI_GET_STRING). + * + * Opcode: OP_INI_GET_STRING + * Stack: [default:string] [key:string] [section:string] [handle:int] -> [out:string] + * + * Behavior + * - Pops default string, key, section, and handle; looks up "section:key" and + * a dotted fallback. If not found, uses the provided default. + * - Pushes the resulting string (copied into a VM Value). + * + * Errors + * - Invalid handle/args result in pushing the default (or empty string). + */ /* OP_INI_GET_STRING */ #ifdef FUN_WITH_INI case OP_INI_GET_STRING: { diff --git a/src/vm/ini/handles.c b/src/vm/ini/handles.c index 86cb13b..6bcbd99 100644 --- a/src/vm/ini/handles.c +++ b/src/vm/ini/handles.c @@ -1,4 +1,4 @@ -/* +/** * This file is part of the Fun programming language. * https://fun-lang.xyz/ * @@ -7,6 +7,15 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file handles.c + * @brief INI handle registry implementation used by VM INI opcodes. + * + * Provides a tiny fixed-size registry mapping small integer handles to + * iniparser dictionary pointers. Not thread-safe. Handles are positive + * integers in range [1, 63]. + */ + #ifdef FUN_WITH_INI #if defined(__has_include) #if __has_include() diff --git a/src/vm/ini/handles.h b/src/vm/ini/handles.h index c18ab62..a43eb00 100644 --- a/src/vm/ini/handles.h +++ b/src/vm/ini/handles.h @@ -5,11 +5,12 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2025-11-30 */ -/** INI handle registry for iniparser 4.2.6 */ +/** + * @file handles.h + * @brief INI handle registry for iniparser 4.2.6 used by INI VM opcodes. + */ #pragma once #ifdef FUN_WITH_INI @@ -29,19 +30,43 @@ #endif #include +/** + * @brief One slot in the global INI handle registry. + * @details Associates an iniparser dictionary pointer with an in-use flag. + */ typedef struct { dictionary *dict; int in_use; } IniSlot; -/* Single global registry (defined in handles.c) */ +/** Single global registry (defined in handles.c). */ extern IniSlot g_ini[64]; -/* Registry API (implemented in handles.c) */ +/** + * @brief Allocate a registry handle for a newly created dictionary. + * @param d Pointer to an iniparser dictionary. + * @return Handle id (>0) on success or 0 on failure. + */ int ini_alloc_handle(dictionary *d); +/** + * @brief Look up a dictionary pointer by registry handle. + * @param h Handle id previously returned by ini_alloc_handle(). + * @return Pointer to dictionary or NULL if not found. + */ dictionary *ini_get(int h); +/** + * @brief Free a previously allocated handle and close its dictionary. + * @param h Handle id to free. + * @return 1 on success, 0 on error (invalid handle or not in use). + */ int ini_free_handle(int h); -/* Helper to build section:key string safely into provided buffer (implemented in handles.c) */ +/** + * @brief Build a fully qualified key "section:key" into a caller-provided buffer. + * @param buf Destination buffer. + * @param cap Capacity of buf in bytes (including terminator). + * @param sec Section name (may be NULL for default section). + * @param key Key name (must not be NULL). + */ void ini_make_full_key(char *buf, size_t cap, const char *sec, const char *key); #endif /* FUN_WITH_INI */ diff --git a/src/vm/ini/load.c b/src/vm/ini/load.c index 37936e6..87f33e7 100644 --- a/src/vm/ini/load.c +++ b/src/vm/ini/load.c @@ -1,14 +1,28 @@ -/* +/** * 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-11-30 */ +/** + * @file load.c + * @brief VM opcode snippet for loading an INI file (OP_INI_LOAD). + * + * Opcode: OP_INI_LOAD + * Stack: [path:string] -> [handle:int] + * + * Behavior + * - Pops a filesystem path and attempts to parse it via iniparser_load(). + * - On success, registers the resulting dictionary and pushes a positive + * handle. On failure, pushes 0. + * + * Notes + * - The returned handle must later be released with OP_INI_FREE to avoid + * leaking dictionary objects. + */ /* OP_INI_LOAD: pops path string; pushes handle (>0) or 0 */ #ifdef FUN_WITH_INI case OP_INI_LOAD: { diff --git a/src/vm/ini/save.c b/src/vm/ini/save.c index 779a207..cdc9551 100644 --- a/src/vm/ini/save.c +++ b/src/vm/ini/save.c @@ -1,14 +1,27 @@ -/* +/** * 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-10 (split from set_unset_save.c) */ +/** + * @file save.c + * @brief VM opcode snippet for saving an INI dictionary to a file (OP_INI_SAVE). + * + * Opcode: OP_INI_SAVE + * Stack: [path:string] [handle:int] -> [ok:int] + * + * Behavior + * - Pops a path and a handle. If the handle is valid, opens the path for + * writing and dumps the dictionary in INI format. Pushes 1 on success, + * otherwise 0. + * + * Errors + * - Failing fopen() or invalid handle simply return 0; no exception is thrown. + */ /* OP_INI_SAVE */ #ifdef FUN_WITH_INI case OP_INI_SAVE: { diff --git a/src/vm/ini/set.c b/src/vm/ini/set.c index 04a756c..e759f1a 100644 --- a/src/vm/ini/set.c +++ b/src/vm/ini/set.c @@ -1,14 +1,28 @@ -/* +/** * 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-10 (split from set_unset_save.c) */ +/** + * @file set.c + * @brief VM opcode snippet for setting an INI value (OP_INI_SET). + * + * Opcode: OP_INI_SET + * Stack: [value:any] [key:string] [section:string] [handle:int] -> [ok:int] + * + * Behavior + * - Pops value, key, section, and handle. Converts the value to a string using + * value_to_string_alloc() and stores it under "section:key" (and a dotted + * fallback) via dictionary_set(). + * - Pushes 1 on success, 0 on failure (invalid args/handle or allocation fail). + * + * Errors + * - No VM exception is thrown; failures return 0. + */ /* OP_INI_SET */ #ifdef FUN_WITH_INI case OP_INI_SET: { diff --git a/src/vm/ini/stubs.c b/src/vm/ini/stubs.c index 2b53f2b..00b9164 100644 --- a/src/vm/ini/stubs.c +++ b/src/vm/ini/stubs.c @@ -1,6 +1,11 @@ -/* - * 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. +/** + * @file stubs.c + * @brief Stub opcode implementations for INI support when FUN_WITH_INI is disabled. + * + * These cases are compiled into the VM dispatch when the INI feature is not + * enabled. Each opcode prints a descriptive runtime error and pushes a safe + * default (0, 0.0, or empty string) to keep execution proceeding without + * crashing. */ /* OP_INI_LOAD: pops path string; pushes 0 (invalid handle) */ diff --git a/src/vm/ini/unset.c b/src/vm/ini/unset.c index 807aa23..58c8d7c 100644 --- a/src/vm/ini/unset.c +++ b/src/vm/ini/unset.c @@ -1,14 +1,28 @@ -/* +/** * 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-10 (split from set_unset_save.c) */ +/** + * @file unset.c + * @brief VM opcode snippet for removing an INI entry (OP_INI_UNSET). + * + * Opcode: OP_INI_UNSET + * Stack: [key:string] [section:string] [handle:int] -> [ok:int] + * + * Behavior + * - Pops key, section, and handle. Removes both "section:key" and a dotted + * fallback key from the dictionary. Pushes 1 if the operation was attempted + * (with a valid handle and arguments), otherwise 0. + * + * Notes + * - iniparser 4.2.6 dictionary_unset() returns void; we assume success when + * called with valid parameters. + */ /* OP_INI_UNSET */ #ifdef FUN_WITH_INI case OP_INI_UNSET: { diff --git a/src/vm/io/input_line.c b/src/vm/io/input_line.c index a8b34dd..5b52286 100644 --- a/src/vm/io/input_line.c +++ b/src/vm/io/input_line.c @@ -1,3 +1,53 @@ +/** + * 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 + */ + +/** + * @file input_line.c + * @brief Implements the OP_INPUT_LINE opcode for interactive console input. + * + * This snippet handles the OP_INPUT_LINE instruction in the VM dispatch. It can + * optionally print a prompt (taken from the stack) and can read input in a + * hidden mode (terminal echo disabled) suitable for passwords. + * + * Operand bits (inst.operand): + * - bit0 (1): Has prompt. When set, the top of the stack is popped and + * converted to string, printed without a trailing newline. + * - bit1 (2): Hidden input. When set, terminal echo is temporarily disabled + * while reading the line (best-effort, platform dependent). + * + * Stack effects: + * - If bit0 is set: pop(prompt) + * - Always: push(result_string) + * + * Behavior: + * - Converts an optional prompt Value to string using value_to_string_alloc, + * prints it to stdout without a newline, and flushes the stream. + * - If hidden is requested, disables terminal echo (POSIX termios or Win32 + * console modes) before reading. + * - Reads a single line from stdin, accepting both "\n" and "\r\n" endings. + * - Restores terminal echo if it was disabled and, when a prompt was printed, + * emits a newline for a better UX. + * - Pushes the captured line as a Fun string (never NULL; empty string on + * failure or EOF). + * + * Errors and corner cases: + * - Memory allocation failures are reported to stderr; an empty string is + * pushed in such cases to keep execution flowing. + * - If echo toggling fails, input still proceeds with echo enabled. + * - EOF before any character yields an empty string. + * + * Example: + * // Bytecode: [optional PUSH prompt], OP_INPUT_LINE(operand) + * // operand bit0=1 (has prompt), bit1=2 (hidden) can be combined + * // Stack before (bit0=1): ["Enter password: "] + * // Stack after: ["user-typed-line"] + */ case OP_INPUT_LINE: { /* operand bit flags: * bit0 (1): has prompt (string or any value convertible to string) — top of stack holds prompt when set diff --git a/src/vm/json/from_file.c b/src/vm/json/from_file.c index c3c8027..4352072 100644 --- a/src/vm/json/from_file.c +++ b/src/vm/json/from_file.c @@ -5,8 +5,30 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file from_file.c + * @brief VM opcode snippet for loading a JSON document from a file. * - * Added: 2025-11-24 + * This snippet is included by vm.c and implements the OP_JSON_FROM_FILE + * instruction. It expects a path on the VM stack, reads the file using + * json-c, converts the resulting json_object tree into a Fun Value, and + * pushes that Value back on the VM stack. + * + * Build gating: compiled only when FUN_WITH_JSON is enabled (json-c + * available). When disabled, the opcode consumes its argument (if any) + * and pushes Nil. + * + * Stack effect (with FUN_WITH_JSON): + * - Pops: path (any; converted to string) + * - Pushes: Value converted from JSON, or Nil on error + * + * Errors and edge cases: + * - If the path cannot be converted to a C string or json_object_from_file + * fails (e.g., missing file, invalid JSON), the opcode pushes Nil. + * - The created json_object is released after conversion; ownership of the + * pushed Fun Value follows normal VM semantics. */ /* JSON_FROM_FILE */ diff --git a/src/vm/json/parse.c b/src/vm/json/parse.c index 4083abb..6112218 100644 --- a/src/vm/json/parse.c +++ b/src/vm/json/parse.c @@ -5,8 +5,28 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file parse.c + * @brief VM opcode snippet for parsing a JSON string into a Fun Value. * - * Added: 2025-11-24 + * Implements the OP_JSON_PARSE instruction. Expects a string (or any value + * convertible to string) on the stack, parses it with json-c, converts the + * resulting json_object to a Fun Value and pushes it. + * + * Build gating: compiled only when FUN_WITH_JSON is enabled. Otherwise the + * opcode consumes its argument and pushes Nil. + * + * Stack effect (with FUN_WITH_JSON): + * - Pops: text (any; converted to string) + * - Pushes: Value converted from JSON, or Nil on error + * + * Errors and edge cases: + * - If allocation fails, tokenization fails, or the text is not valid JSON, + * the opcode pushes Nil. + * - The temporary json-c objects are released after conversion; ownership of + * the pushed Fun Value follows normal VM semantics. */ /* JSON_PARSE */ diff --git a/src/vm/json/stringify.c b/src/vm/json/stringify.c index a6347f6..c0583e5 100644 --- a/src/vm/json/stringify.c +++ b/src/vm/json/stringify.c @@ -5,8 +5,27 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file stringify.c + * @brief VM opcode snippet for converting a Fun Value to a JSON string. * - * Added: 2025-11-24 + * Implements the OP_JSON_STRINGIFY instruction. Expects a boolean/integer flag + * indicating pretty-printing and a Value to serialize. Uses json-c to build a + * json_object from the Value and then renders it to a string, which is pushed + * back to the stack. + * + * Build gating: compiled only when FUN_WITH_JSON is enabled. Otherwise the + * opcode consumes its two arguments and pushes the string "null". + * + * Stack effect (with FUN_WITH_JSON): + * - Pops: pretty (bool/int), value (any) + * - Pushes: string (JSON representation) + * + * Errors and edge cases: + * - If conversion to json_object fails, an empty string is pushed. + * - Pretty printing selects JSON_C_TO_STRING_PRETTY; otherwise plain output. */ /* JSON_STRINGIFY */ diff --git a/src/vm/json/to_file.c b/src/vm/json/to_file.c index c21610f..1b12809 100644 --- a/src/vm/json/to_file.c +++ b/src/vm/json/to_file.c @@ -5,8 +5,27 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file to_file.c + * @brief VM opcode snippet for writing a Fun Value as JSON to a file. * - * Added: 2025-11-24 + * Implements the OP_JSON_TO_FILE instruction. Expects a pretty-print flag, + * a Value to serialize, and a path. Serializes the Value via json-c and + * writes it to the specified file path. + * + * Build gating: compiled only when FUN_WITH_JSON is enabled. Otherwise the + * opcode consumes three arguments and pushes 0 (failure). + * + * Stack effect (with FUN_WITH_JSON): + * - Pops: pretty (bool/int), value (any), path (any; converted to string) + * - Pushes: int (1 on success, 0 on failure) + * + * Errors and edge cases: + * - If the path cannot be converted to a C string or file writing fails, + * the opcode pushes 0. + * - Pretty printing selects JSON_C_TO_STRING_PRETTY; otherwise plain output. */ /* JSON_TO_FILE */ diff --git a/src/vm/line.c b/src/vm/line.c index 540ce19..36f180b 100644 --- a/src/vm/line.c +++ b/src/vm/line.c @@ -7,6 +7,18 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file line.c + * @brief Implements the OP_LINE pseudo-opcode to update the current source line. + * + * This snippet is included into the VM dispatch loop and handles the OP_LINE + * instruction. It records the source line number carried in the instruction's + * operand so that runtime errors and debugger output can reference the correct + * line in the original program. + * + * Stack contract: none (does not read or write the VM value stack). + */ + case OP_LINE: { /* operand holds the source line number */ vm->current_line = inst.operand; diff --git a/src/vm/math/ceil.c b/src/vm/math/ceil.c index 04d71bc..2efbc01 100644 --- a/src/vm/math/ceil.c +++ b/src/vm/math/ceil.c @@ -5,13 +5,33 @@ * Copyright 2026 Johannes Findeisen * 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(). + * + * VM opcode snippet included by vm.c. Provides numeric ceiling operation. + * + * Behavior: + * - Pops one numeric operand (int or float) from the stack. + * - Applies ceil(x) in double precision. + * - If the result is an exact 64-bit integer, pushes VAL_INT; otherwise VAL_FLOAT. + * + * Stack effect: + * - Pop: x + * - Push: ceil(x) + * + * Types: + * - Accepts VAL_INT and VAL_FLOAT. + * - Other types cause a runtime error. + * + * Errors: + * - Exits with an error message if the operand is not a number. + * + * Example: + * - Input stack: [2.1] → Output stack: [3] + * - Input stack: [-2.1] → Output stack: [-2] */ #include diff --git a/src/vm/math/cos.c b/src/vm/math/cos.c index fc71582..01ce6b9 100644 --- a/src/vm/math/cos.c +++ b/src/vm/math/cos.c @@ -5,13 +5,29 @@ * Copyright 2026 Johannes Findeisen * 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(). + * + * VM opcode snippet included by vm.c. Provides cosine function. + * + * Behavior: + * - Pops one numeric operand (int or float) from the stack. + * - Computes cos(x) in double precision. + * - Always pushes a VAL_FLOAT result. + * + * Stack effect: + * - Pop: x + * - Push: cos(x) + * + * Types: + * - Accepts VAL_INT and VAL_FLOAT. + * - Other types cause a runtime error. + * + * Example: + * - Input [0] → Output [1.0] */ #include diff --git a/src/vm/math/exp.c b/src/vm/math/exp.c index e15d8c1..b354e6f 100644 --- a/src/vm/math/exp.c +++ b/src/vm/math/exp.c @@ -5,13 +5,25 @@ * Copyright 2026 Johannes Findeisen * 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(). + * + * VM opcode snippet included by vm.c. Provides the natural exponential function. + * + * Behavior: + * - Pops one numeric operand (int or float). + * - Computes e^x in double precision. + * - Pushes a VAL_FLOAT result. + * + * Stack effect: + * - Pop: x + * - Push: exp(x) + * + * Types: + * - Accepts VAL_INT and VAL_FLOAT; others raise a runtime error. */ #include diff --git a/src/vm/math/floor.c b/src/vm/math/floor.c index ceaec7d..f7a0480 100644 --- a/src/vm/math/floor.c +++ b/src/vm/math/floor.c @@ -5,13 +5,33 @@ * Copyright 2026 Johannes Findeisen * 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(). + * + * VM opcode snippet included by vm.c. Provides numeric floor operation. + * + * Behavior: + * - Pops one numeric operand (int or float) from the stack. + * - Applies floor(x) in double precision. + * - If the result is an exact 64-bit integer, pushes VAL_INT; otherwise VAL_FLOAT. + * + * Stack effect: + * - Pop: x + * - Push: floor(x) + * + * Types: + * - Accepts VAL_INT and VAL_FLOAT. + * - Other types cause a runtime error. + * + * Errors: + * - Exits with an error message if the operand is not a number. + * + * Example: + * - Input stack: [2.9] → Output stack: [2] + * - Input stack: [-2.1] → Output stack: [-3] */ #include diff --git a/src/vm/math/fmax.c b/src/vm/math/fmax.c index 0da3de4..08a5ad4 100644 --- a/src/vm/math/fmax.c +++ b/src/vm/math/fmax.c @@ -5,8 +5,6 @@ * Copyright 2026 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 */ /** diff --git a/src/vm/math/fmin.c b/src/vm/math/fmin.c index a407975..c6883cc 100644 --- a/src/vm/math/fmin.c +++ b/src/vm/math/fmin.c @@ -5,8 +5,6 @@ * Copyright 2026 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 */ /** diff --git a/src/vm/math/gcd.c b/src/vm/math/gcd.c index dbc1aa4..f9af580 100644 --- a/src/vm/math/gcd.c +++ b/src/vm/math/gcd.c @@ -5,13 +5,23 @@ * Copyright 2026 Johannes Findeisen * 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. + * + * Behavior: + * - Pops two numeric operands (a, b). Floats are truncated to int64. + * - Computes gcd(|a|, |b|) using Euclid's algorithm. + * - Pushes VAL_INT result. + * + * Stack effect: + * - Pop: b, a + * - Push: gcd(a, b) + * + * Types: + * - Accepts VAL_INT and VAL_FLOAT; others cause a runtime error. */ case OP_GCD: { diff --git a/src/vm/math/isqrt.c b/src/vm/math/isqrt.c index ba2b8e1..f3da284 100644 --- a/src/vm/math/isqrt.c +++ b/src/vm/math/isqrt.c @@ -5,13 +5,23 @@ * Copyright 2026 Johannes Findeisen * 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). + * + * Behavior: + * - Pops one numeric operand (int or float), converts to int64. + * - Computes floor(sqrt(max(0, x))) as an integer without floating point. + * - Pushes VAL_INT result. + * + * Stack effect: + * - Pop: x + * - Push: isqrt(x) + * + * Types: + * - Accepts VAL_INT and VAL_FLOAT; others cause a runtime error. */ case OP_ISQRT: { diff --git a/src/vm/math/lcm.c b/src/vm/math/lcm.c index b24e622..98498ac 100644 --- a/src/vm/math/lcm.c +++ b/src/vm/math/lcm.c @@ -5,13 +5,23 @@ * Copyright 2026 Johannes Findeisen * 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. + * + * Behavior: + * - Pops two numeric operands (a, b). Floats are truncated to int64. + * - Computes lcm(|a|, |b|) using gcd; returns 0 if either input is 0. + * - Pushes VAL_INT result. May overflow silently on extreme inputs. + * + * Stack effect: + * - Pop: b, a + * - Push: lcm(a, b) + * + * Types: + * - Accepts VAL_INT and VAL_FLOAT; others cause a runtime error. */ case OP_LCM: { diff --git a/src/vm/math/log.c b/src/vm/math/log.c index b8afcc6..0a788ad 100644 --- a/src/vm/math/log.c +++ b/src/vm/math/log.c @@ -5,13 +5,23 @@ * Copyright 2026 Johannes Findeisen * 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). + * + * Behavior: + * - Pops one numeric operand (int or float). + * - If x <= 0, pushes NaN to indicate domain error; otherwise pushes ln(x). + * - Result type is VAL_FLOAT. + * + * Stack effect: + * - Pop: x + * - Push: ln(x) | NaN + * + * Types: + * - Accepts VAL_INT and VAL_FLOAT; errors on others. */ #include diff --git a/src/vm/math/log10.c b/src/vm/math/log10.c index c40f7f0..52d8023 100644 --- a/src/vm/math/log10.c +++ b/src/vm/math/log10.c @@ -5,13 +5,19 @@ * Copyright 2026 Johannes Findeisen * 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(). + * + * Behavior: + * - Pops one numeric operand (int or float). + * - If x <= 0, pushes NaN; else pushes log10(x) as VAL_FLOAT. + * + * Stack effect: + * - Pop: x + * - Push: log10(x) | NaN */ #include diff --git a/src/vm/math/round.c b/src/vm/math/round.c index 42bbc76..0e44c48 100644 --- a/src/vm/math/round.c +++ b/src/vm/math/round.c @@ -5,8 +5,6 @@ * Copyright 2026 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 */ /** diff --git a/src/vm/math/sign.c b/src/vm/math/sign.c index 799abfc..58a1faf 100644 --- a/src/vm/math/sign.c +++ b/src/vm/math/sign.c @@ -5,8 +5,6 @@ * Copyright 2026 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 */ /** diff --git a/src/vm/math/sin.c b/src/vm/math/sin.c index 9dee63f..255e183 100644 --- a/src/vm/math/sin.c +++ b/src/vm/math/sin.c @@ -5,13 +5,28 @@ * Copyright 2026 Johannes Findeisen * 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(). + * + * VM opcode snippet included by vm.c. Provides sine function. + * + * Behavior: + * - Pops one numeric operand (int or float) from the stack. + * - Computes sin(x) in double precision. + * - Always pushes a VAL_FLOAT result. + * + * Stack effect: + * - Pop: x + * - Push: sin(x) + * + * Types: + * - Accepts VAL_INT and VAL_FLOAT; others cause a runtime error. + * + * Example: + * - Input [0] → Output [0.0] */ #include diff --git a/src/vm/math/sqrt.c b/src/vm/math/sqrt.c index ff773c7..983ff34 100644 --- a/src/vm/math/sqrt.c +++ b/src/vm/math/sqrt.c @@ -5,13 +5,20 @@ * Copyright 2026 Johannes Findeisen * 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(). + * + * Behavior: + * - Pops one numeric operand (int or float). + * - If x < 0, pushes NaN; else pushes sqrt(x). + * - Returns VAL_INT when the result fits exactly in int64, otherwise VAL_FLOAT. + * + * Stack effect: + * - Pop: x + * - Push: sqrt(x) | NaN */ #include diff --git a/src/vm/math/tan.c b/src/vm/math/tan.c index b64b5f4..8400415 100644 --- a/src/vm/math/tan.c +++ b/src/vm/math/tan.c @@ -5,13 +5,25 @@ * Copyright 2026 Johannes Findeisen * 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(). + * + * VM opcode snippet included by vm.c. Provides tangent function. + * + * Behavior: + * - Pops one numeric operand (int or float) from the stack. + * - Computes tan(x) in double precision. + * - Always pushes a VAL_FLOAT result. + * + * Stack effect: + * - Pop: x + * - Push: tan(x) + * + * Types: + * - Accepts VAL_INT and VAL_FLOAT; others cause a runtime error. */ #include diff --git a/src/vm/math/trunc.c b/src/vm/math/trunc.c index d353885..ac9b1d6 100644 --- a/src/vm/math/trunc.c +++ b/src/vm/math/trunc.c @@ -5,8 +5,6 @@ * Copyright 2026 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2026-01-03 */ /** diff --git a/src/vm/openssl/md5.c b/src/vm/openssl/md5.c index ede69cd..4c20d9d 100644 --- a/src/vm/openssl/md5.c +++ b/src/vm/openssl/md5.c @@ -5,10 +5,13 @@ * Copyright 2026 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2026-02-19 */ + /** + * @file md5.c + * @brief VM opcode snippet: compute MD5 hash (OP_MD5). Included by vm.c. + */ + /** * OpenSSL MD5 builtin */ diff --git a/src/vm/openssl/ripemd160.c b/src/vm/openssl/ripemd160.c index f974107..ef3f5f6 100644 --- a/src/vm/openssl/ripemd160.c +++ b/src/vm/openssl/ripemd160.c @@ -5,8 +5,6 @@ * Copyright 2026 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2026-02-19 */ /* diff --git a/src/vm/openssl/sha256.c b/src/vm/openssl/sha256.c index 964f9ad..e72bc5f 100644 --- a/src/vm/openssl/sha256.c +++ b/src/vm/openssl/sha256.c @@ -5,8 +5,6 @@ * Copyright 2026 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2026-02-19 */ /** diff --git a/src/vm/openssl/sha512.c b/src/vm/openssl/sha512.c index 805561d..c1ca5b1 100644 --- a/src/vm/openssl/sha512.c +++ b/src/vm/openssl/sha512.c @@ -5,8 +5,6 @@ * Copyright 2026 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2026-02-19 */ /** diff --git a/src/vm/os/clock_mono_ms.c b/src/vm/os/clock_mono_ms.c index 09d3c26..49eea55 100644 --- a/src/vm/os/clock_mono_ms.c +++ b/src/vm/os/clock_mono_ms.c @@ -5,8 +5,6 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2025-10-04 */ /** diff --git a/src/vm/os/date_format.c b/src/vm/os/date_format.c index e8b7b33..05c93f5 100644 --- a/src/vm/os/date_format.c +++ b/src/vm/os/date_format.c @@ -5,8 +5,6 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2025-10-04 */ /** diff --git a/src/vm/os/env.c b/src/vm/os/env.c index f48c759..272da09 100644 --- a/src/vm/os/env.c +++ b/src/vm/os/env.c @@ -7,7 +7,17 @@ * https://opensource.org/license/apache-2-0 */ -// Get environment variables of the operation system. +/** + * @file env.c + * @brief Implements OP_ENV to read an environment variable by name. + * + * Behavior: + * - Pops a string key from the stack and pushes the associated environment value as string. + * - If the variable is not set, pushes an empty string ("") rather than Nil. + * + * Errors: + * - If the key is not a string, prints an error and terminates the VM with exit(1). + */ case OP_ENV: { Value key = pop_value(vm); diff --git a/src/vm/os/env_all.c b/src/vm/os/env_all.c index f02ff0e..7d03bd6 100644 --- a/src/vm/os/env_all.c +++ b/src/vm/os/env_all.c @@ -5,11 +5,16 @@ * 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. +/** + * @file env_all.c + * @brief Implements OP_ENV_ALL to read the full environment into a map. + * + * Behavior: + * - Pushes a new map where each key is an environment variable and each value is its string value. + * - Keys and values are copied; the caller owns the returned map Value. + */ case OP_ENV_ALL: { extern char **environ; diff --git a/src/vm/os/fd_poll_read.c b/src/vm/os/fd_poll_read.c index 6c7d61a..8ae2248 100644 --- a/src/vm/os/fd_poll_read.c +++ b/src/vm/os/fd_poll_read.c @@ -5,8 +5,18 @@ * Copyright 2026 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file fd_poll_read.c + * @brief Implements OP_FD_POLL_READ to check if a file descriptor is readable. * - * Added: 2026-03-26 + * Behavior: + * - Pops timeout_ms (int) and fd (int); waits up to timeout for readability; pushes 1 if readable, 0 otherwise. + * - On non-UNIX platforms, returns 0 (unsupported). + * + * Errors: + * - If types are wrong, prints an error and returns 0. */ case OP_FD_POLL_READ: { diff --git a/src/vm/os/fd_poll_write.c b/src/vm/os/fd_poll_write.c index 508c42c..3b5c846 100644 --- a/src/vm/os/fd_poll_write.c +++ b/src/vm/os/fd_poll_write.c @@ -5,8 +5,18 @@ * Copyright 2026 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file fd_poll_write.c + * @brief Implements OP_FD_POLL_WRITE to check if a file descriptor is writable. * - * Added: 2026-03-26 + * Behavior: + * - Pops timeout_ms (int) and fd (int); waits up to timeout for writability; pushes 1 if writable, 0 otherwise. + * - On non-UNIX platforms, returns 0 (unsupported). + * + * Errors: + * - If types are wrong, prints an error and returns 0. */ case OP_FD_POLL_WRITE: { diff --git a/src/vm/os/fd_set_nonblock.c b/src/vm/os/fd_set_nonblock.c index 51e5135..f73dbd9 100644 --- a/src/vm/os/fd_set_nonblock.c +++ b/src/vm/os/fd_set_nonblock.c @@ -5,8 +5,18 @@ * Copyright 2026 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file fd_set_nonblock.c + * @brief Implements OP_FD_SET_NONBLOCK to toggle O_NONBLOCK on a file descriptor. * - * Added: 2026-03-26 + * Behavior: + * - Pops on (int, 0/1) and fd (int); sets or clears O_NONBLOCK via fcntl; pushes 1 on success, 0 otherwise. + * - On non-UNIX platforms, returns 0 (unsupported). + * + * Errors: + * - If types are wrong, prints an error and returns 0. */ case OP_FD_SET_NONBLOCK: { diff --git a/src/vm/os/fun_version.c b/src/vm/os/fun_version.c index 2f2cf5b..613350f 100644 --- a/src/vm/os/fun_version.c +++ b/src/vm/os/fun_version.c @@ -5,8 +5,14 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file fun_version.c + * @brief VM opcode snippet: push the current Fun version string (OP_FUN_VERSION). * - * Added: 2025-12-28 + * Included into vm.c dispatch. Pushes a string constant with the build's + * FUN_VERSION onto the VM stack. */ // Pushes the current Fun version string onto the stack. diff --git a/src/vm/os/list_dir.c b/src/vm/os/list_dir.c index b8c6727..4aa4d20 100644 --- a/src/vm/os/list_dir.c +++ b/src/vm/os/list_dir.c @@ -5,8 +5,18 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file list_dir.c + * @brief Implements OP_OS_LIST_DIR to list entries in a directory. * - * Added: 2025-12-23 + * Behavior: + * - Pops a path (string); pushes an array of file/directory names as strings. + * - Implementation may delegate to a platform command (e.g., ls -1) for portability. + * + * Errors: + * - Non-string path results in empty array; platform errors also yield an empty array. */ case OP_OS_LIST_DIR: { diff --git a/src/vm/os/proc_run.c b/src/vm/os/proc_run.c index 4e193ce..35c9ae9 100644 --- a/src/vm/os/proc_run.c +++ b/src/vm/os/proc_run.c @@ -5,8 +5,18 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file proc_run.c + * @brief Implements OP_PROC_RUN to execute a shell command and capture stdout. * - * Added: 2025-10-02 + * Behavior: + * - Pops command (string); runs it via the platform shell; pushes a map {"out": string, "code": int}. + * - "code" is the process exit status if available, otherwise -1. + * + * Errors: + * - On allocation failures or popen errors, returns {out: "", code: -1}. */ #include diff --git a/src/vm/os/proc_system.c b/src/vm/os/proc_system.c index a57ff4f..e16bdcd 100644 --- a/src/vm/os/proc_system.c +++ b/src/vm/os/proc_system.c @@ -5,8 +5,17 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file proc_system.c + * @brief Implements OP_PROC_SYSTEM to execute a shell command and return exit code. * - * Added: 2025-10-02 + * Behavior: + * - Pops command (string); executes it using system(3); pushes the process exit code (int) or -1 on failure. + * + * Errors: + * - If command is not a string or cannot be executed, returns -1. */ case OP_PROC_SYSTEM: { diff --git a/src/vm/os/random_number.c b/src/vm/os/random_number.c index 88e0612..18ba5fd 100644 --- a/src/vm/os/random_number.c +++ b/src/vm/os/random_number.c @@ -5,13 +5,20 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2025-12-12 */ -/* Generate OS-based random bytes and return them hex-encoded. - * Opcode: OP_RANDOM_NUMBER - * Stack: pops len (number of raw bytes), pushes hex string of length 2*len +/** + * @file random_number.c + * @brief Implements OP_RANDOM_NUMBER to generate cryptographically secure random bytes. + * + * Behavior: + * - Pops len (int, number of raw bytes to generate) and pushes a lowercase hex string of length 2*len. + * - Uses the platform-preferred CSPRNG (e.g., getrandom/arc4random/BCryptGenRandom); falls back to /dev/urandom on UNIX. + * - Caps requested length to prevent excessive allocations. + * + * Errors: + * - If len is not an int, or negative, prints an error and pushes an empty string. + * - If OS RNG is unavailable or allocation fails, prints an error and terminates the VM (exit(1)). */ #include diff --git a/src/vm/os/serial_close.c b/src/vm/os/serial_close.c index 19dfa5b..8229019 100644 --- a/src/vm/os/serial_close.c +++ b/src/vm/os/serial_close.c @@ -5,8 +5,18 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file serial_close.c + * @brief Implements OP_SERIAL_CLOSE to close an open serial port. * - * Added: 2025-12-28 + * Behavior: + * - Pops fd (int); attempts to close it; pushes 1 on success, 0 on failure. + * - Only supported on UNIX-like systems. + * + * Errors: + * - If argument type is wrong, prints an error and returns 0. */ #ifdef __unix__ diff --git a/src/vm/os/serial_config.c b/src/vm/os/serial_config.c index 811ad31..e0524c9 100644 --- a/src/vm/os/serial_config.c +++ b/src/vm/os/serial_config.c @@ -5,8 +5,18 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file serial_config.c + * @brief Implements OP_SERIAL_CONFIG to change serial port parameters. * - * Added: 2025-12-28 + * Behavior: + * - Pops config map and fd (int); applies selected settings (baud, parity, bits, stop) and pushes 1 on success, 0 on failure. + * - Only supported on UNIX-like systems. + * + * Errors: + * - On wrong types or OS errors, prints an error and returns 0. */ #ifdef __unix__ diff --git a/src/vm/os/serial_open.c b/src/vm/os/serial_open.c index e07401c..0c5de61 100644 --- a/src/vm/os/serial_open.c +++ b/src/vm/os/serial_open.c @@ -5,8 +5,18 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file serial_open.c + * @brief Implements OP_SERIAL_OPEN to open and configure a serial port. * - * Added: 2025-12-28 + * Behavior: + * - Pops baud_rate (int) and device path (string); opens and configures the port; pushes fd (>0) or 0 on failure. + * - Only available on UNIX-like systems; Windows is unsupported and returns 0. + * + * Errors: + * - On wrong types or OS errors, prints an error message and returns 0. */ #ifdef __unix__ diff --git a/src/vm/os/serial_recv.c b/src/vm/os/serial_recv.c index 71877b5..73359ef 100644 --- a/src/vm/os/serial_recv.c +++ b/src/vm/os/serial_recv.c @@ -5,8 +5,18 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file serial_recv.c + * @brief Implements OP_SERIAL_RECV to read bytes from a serial port. * - * Added: 2025-12-28 + * Behavior: + * - Pops max_len (int) and fd (int); reads up to max_len bytes; pushes a string with received data (possibly empty). + * - Only supported on UNIX-like systems; other platforms push empty string. + * + * Errors: + * - On wrong types, prints an error and pushes empty string. */ #ifdef __unix__ diff --git a/src/vm/os/serial_send.c b/src/vm/os/serial_send.c index 4e014be..ef32da0 100644 --- a/src/vm/os/serial_send.c +++ b/src/vm/os/serial_send.c @@ -5,8 +5,18 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file serial_send.c + * @brief Implements OP_SERIAL_SEND to write bytes to a serial port. * - * Added: 2025-12-28 + * Behavior: + * - Pops data (string) and fd (int); writes data to the serial port; pushes number of bytes written (>=0) or -1 on error. + * - Only supported on UNIX-like systems; other platforms push -1. + * + * Errors: + * - On wrong types, prints an error and pushes -1. */ #ifdef __unix__ diff --git a/src/vm/os/sleep_ms.c b/src/vm/os/sleep_ms.c index 3d599b3..5ba1489 100644 --- a/src/vm/os/sleep_ms.c +++ b/src/vm/os/sleep_ms.c @@ -5,8 +5,19 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file sleep_ms.c + * @brief Implements OP_SLEEP_MS to suspend execution for a number of milliseconds. * - * Added: 2025-09-30 + * Behavior: + * - Pops an integer value ms from the stack and sleeps for that many milliseconds. + * - Always pushes Nil after completion to keep stack discipline for statement POPs. + * + * Errors: + * - If the popped value is not an integer, prints an error, frees it, and pushes Nil. + * - Negative durations are treated as no-op; Nil is still pushed. */ case OP_SLEEP_MS: { diff --git a/src/vm/os/socket_close.c b/src/vm/os/socket_close.c index d1d6ca5..90fbb72 100644 --- a/src/vm/os/socket_close.c +++ b/src/vm/os/socket_close.c @@ -5,8 +5,18 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file socket_close.c + * @brief Implements OP_SOCK_CLOSE to close a socket file descriptor. * - * Added: 2025-10-04 + * Behavior: + * - Pops fd (int) and closes it; pushes 1 on success, 0 on failure. + * - On non-UNIX platforms, always pushes 0 (unsupported). + * + * Errors: + * - If argument type is wrong, prints an error and pushes 0. */ case OP_SOCK_CLOSE: { diff --git a/src/vm/os/socket_recv.c b/src/vm/os/socket_recv.c index b524123..9e02b1a 100644 --- a/src/vm/os/socket_recv.c +++ b/src/vm/os/socket_recv.c @@ -5,8 +5,18 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file socket_recv.c + * @brief Implements OP_SOCK_RECV to receive data from a socket into a string. * - * Added: 2025-10-04 + * Behavior: + * - Pops max_len (int) and fd (int); attempts to read up to max_len bytes; pushes a string with the bytes read. + * - On EOF or error, pushes empty string. Non-UNIX platforms return empty string (unsupported). + * + * Errors: + * - If argument types are wrong, prints an error and pushes empty string. */ case OP_SOCK_RECV: { diff --git a/src/vm/os/socket_send.c b/src/vm/os/socket_send.c index 6a8084d..10ff8d3 100644 --- a/src/vm/os/socket_send.c +++ b/src/vm/os/socket_send.c @@ -5,8 +5,18 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file socket_send.c + * @brief Implements OP_SOCK_SEND to transmit data over a connected socket. * - * Added: 2025-10-04 + * Behavior: + * - Pops data (string) and a socket file descriptor (int) and pushes the number of bytes sent (>=0) or -1 on error. + * - On non-UNIX platforms, pushes -1 (unsupported) without sending. + * + * Errors: + * - If argument types are wrong, prints an error, frees values, and pushes -1. */ case OP_SOCK_SEND: { diff --git a/src/vm/os/socket_tcp_accept.c b/src/vm/os/socket_tcp_accept.c index a93a3da..3265759 100644 --- a/src/vm/os/socket_tcp_accept.c +++ b/src/vm/os/socket_tcp_accept.c @@ -5,8 +5,17 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file socket_tcp_accept.c + * @brief Implements OP_SOCK_TCP_ACCEPT to accept an incoming TCP connection. * - * Added: 2025-10-04 + * Behavior: + * - Pops listening socket fd (int); accepts one connection; pushes new client fd (>0) or 0. + * + * Errors: + * - On wrong type or OS errors, prints an error and pushes 0. Non-UNIX platforms return 0. */ case OP_SOCK_TCP_ACCEPT: { diff --git a/src/vm/os/socket_tcp_connect.c b/src/vm/os/socket_tcp_connect.c index 1a229b2..58cfb39 100644 --- a/src/vm/os/socket_tcp_connect.c +++ b/src/vm/os/socket_tcp_connect.c @@ -5,8 +5,19 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file socket_tcp_connect.c + * @brief Implements OP_SOCK_TCP_CONNECT to open a TCP connection. * - * Added: 2025-10-04 + * Behavior: + * - Pops port (int) and host (string); attempts to connect and pushes fd (>0) on success or 0 on failure. + * - Name resolution is performed via getaddrinfo; first successful connect wins. + * + * Errors: + * - If argument types are wrong, prints an error and pushes 0. + * - On non-UNIX platforms, returns 0 (unsupported). */ case OP_SOCK_TCP_CONNECT: { diff --git a/src/vm/os/socket_tcp_listen.c b/src/vm/os/socket_tcp_listen.c index 09477d6..0c324f3 100644 --- a/src/vm/os/socket_tcp_listen.c +++ b/src/vm/os/socket_tcp_listen.c @@ -5,8 +5,17 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file socket_tcp_listen.c + * @brief Implements OP_SOCK_TCP_LISTEN to create a TCP listening socket. * - * Added: 2025-10-04 + * Behavior: + * - Pops port (int); creates a listening socket bound to INADDR_ANY; pushes fd (>0) on success or 0. + * + * Errors: + * - On wrong type or OS errors, prints an error and pushes 0. Non-UNIX platforms return 0. */ case OP_SOCK_TCP_LISTEN: { diff --git a/src/vm/os/socket_unix_connect.c b/src/vm/os/socket_unix_connect.c index d80b31b..4eee7b6 100644 --- a/src/vm/os/socket_unix_connect.c +++ b/src/vm/os/socket_unix_connect.c @@ -5,8 +5,17 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file socket_unix_connect.c + * @brief Implements OP_SOCK_UNIX_CONNECT to connect to a UNIX domain socket path. * - * Added: 2025-10-04 + * Behavior: + * - Pops path (string); creates an AF_UNIX SOCK_STREAM and connects to it; pushes fd (>0) or 0. + * + * Errors: + * - On wrong type or OS errors, prints an error and pushes 0. Non-UNIX platforms return 0. */ #include diff --git a/src/vm/os/socket_unix_listen.c b/src/vm/os/socket_unix_listen.c index 49739d1..2b176a7 100644 --- a/src/vm/os/socket_unix_listen.c +++ b/src/vm/os/socket_unix_listen.c @@ -5,8 +5,18 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file socket_unix_listen.c + * @brief Implements OP_SOCK_UNIX_LISTEN to create a UNIX domain listening socket. * - * Added: 2025-10-04 + * Behavior: + * - Pops path (string); creates/binds/listens on AF_UNIX socket; pushes fd (>0) or 0 on failure. + * - The path may be unlinked before bind to avoid EADDRINUSE. + * + * Errors: + * - On wrong type or OS errors, prints an error and pushes 0. Non-UNIX platforms return 0. */ case OP_SOCK_UNIX_LISTEN: { diff --git a/src/vm/os/thread_common.c b/src/vm/os/thread_common.c index 2ed7106..9a91482 100644 --- a/src/vm/os/thread_common.c +++ b/src/vm/os/thread_common.c @@ -5,8 +5,20 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file thread_common.c + * @brief Cross-platform thread helpers and registry used by OP_THREAD_SPAWN/OP_THREAD_JOIN. * - * Added: 2025-09-30 + * This file provides a tiny cross-platform threading layer (Windows/UNIX) embedded into the + * VM translation unit. It exposes utilities used by VM opcodes to spawn a function call in a + * background thread and later join it to retrieve the result. + * + * Notes: + * - Thread registry is bounded by FUN_MAX_THREADS; not intended for high concurrency. + * - Each task owns its argument Values and transfers ownership of the result to the caller + * on successful join. */ /* Cross-platform minimal threading support embedded into vm.c TU */ diff --git a/src/vm/os/thread_join.c b/src/vm/os/thread_join.c index a1c0d0c..6ad39b2 100644 --- a/src/vm/os/thread_join.c +++ b/src/vm/os/thread_join.c @@ -5,8 +5,18 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file thread_join.c + * @brief Implements OP_THREAD_JOIN to wait for a spawned thread and get its result. * - * Added: 2025-09-30 + * Behavior: + * - Pops thread id (int); waits for the thread to finish; pushes the result Value produced by the thread. + * - If the thread failed or id is invalid, pushes Nil. + * + * Errors: + * - If argument type is wrong, prints an error and pushes Nil. */ case OP_THREAD_JOIN: { diff --git a/src/vm/os/thread_spawn.c b/src/vm/os/thread_spawn.c index a115ed5..27085f8 100644 --- a/src/vm/os/thread_spawn.c +++ b/src/vm/os/thread_spawn.c @@ -5,8 +5,18 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file thread_spawn.c + * @brief Implements OP_THREAD_SPAWN to run a function in a background thread. * - * Added: 2025-09-30 + * Behavior: + * - Pops function and optionally one argument or an array of arguments (controlled by operand). + * - Spawns a new thread that invokes the function; pushes a thread id (int) or 0 on failure. + * + * Errors: + * - If types are wrong or spawning fails, returns 0. */ case OP_THREAD_SPAWN: { diff --git a/src/vm/os/time_now_ms.c b/src/vm/os/time_now_ms.c index d415f34..f9cded7 100644 --- a/src/vm/os/time_now_ms.c +++ b/src/vm/os/time_now_ms.c @@ -5,8 +5,6 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2025-10-04 */ /** diff --git a/src/vm/pcre2/findall.c b/src/vm/pcre2/findall.c index 7b1a65b..40786ce 100644 --- a/src/vm/pcre2/findall.c +++ b/src/vm/pcre2/findall.c @@ -5,8 +5,40 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file findall.c + * @brief Implements the OP_PCRE2_FINDALL opcode (conditional build). * - * Added: 2025-11-25 + * Finds all non-overlapping matches of a PCRE2 pattern in a subject string + * and returns an array of maps describing each match when FUN_WITH_PCRE2 is + * enabled. When PCRE2 support is disabled at build time, the opcode falls + * back to returning an empty array. + */ + +/** + * OP_PCRE2_FINDALL: (pattern:any, text:any, flags:int|bool=0) -> array(map) + * + * Behavior when FUN_WITH_PCRE2 is enabled: + * - Pops three arguments from the VM stack: pattern, text, flags. + * - pattern and text are converted to strings using value_to_string_alloc(). + * - flags bits map to PCRE2 options: + * - 1 = PCRE2_CASELESS (I) + * - 2 = PCRE2_MULTILINE (M) + * - 4 = PCRE2_DOTALL (S) + * - 8 = PCRE2_UTF (U) + * - 16 = PCRE2_EXTENDED (X) + * - Compiles the pattern and scans the subject for all non-overlapping + * matches. For each match, pushes into the result array a map with keys: + * - "full": matched substring (group 0) + * - "start": start index (int) + * - "end": end index (int, exclusive) + * - "groups": array of captured group strings (excluding group 0) + * - On compilation error or allocation failure, returns an empty array. + * + * Behavior when FUN_WITH_PCRE2 is disabled: + * - Pops three values and returns an empty array. */ /* PCRE2_FINDALL */ diff --git a/src/vm/pcre2/match.c b/src/vm/pcre2/match.c index 3fafa0f..c7a6db7 100644 --- a/src/vm/pcre2/match.c +++ b/src/vm/pcre2/match.c @@ -5,8 +5,38 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file match.c + * @brief Implements the OP_PCRE2_MATCH opcode (conditional build). * - * Added: 2025-11-25 + * Executes a single PCRE2 pattern match against a subject string and returns + * a map describing the first match and its capture groups when FUN_WITH_PCRE2 + * is enabled. Returns Nil when there is no match or on error. When PCRE2 + * support is disabled at build time, the opcode always returns Nil. + */ + +/** + * OP_PCRE2_MATCH: (pattern:any, text:any, flags:int|bool=0) -> map|Nil + * + * Behavior when FUN_WITH_PCRE2 is enabled: + * - Pops: pattern, text, flags. Converts pattern/text to strings. + * - Flags bits: + * - 1 = PCRE2_CASELESS (I) + * - 2 = PCRE2_MULTILINE (M) + * - 4 = PCRE2_DOTALL (S) + * - 8 = PCRE2_UTF (U) + * - 16 = PCRE2_EXTENDED (X) + * - On successful match, returns a map with keys: + * - "full": matched substring (group 0) + * - "start": start index (int) + * - "end": end index (int, exclusive) + * - "groups": array of captured group strings (excluding group 0) + * - On no match or compile error, returns Nil. + * + * Behavior when FUN_WITH_PCRE2 is disabled: + * - Pops three values and returns Nil. */ /* PCRE2_MATCH */ diff --git a/src/vm/pcre2/test.c b/src/vm/pcre2/test.c index e0500ca..9154b93 100644 --- a/src/vm/pcre2/test.c +++ b/src/vm/pcre2/test.c @@ -9,6 +9,33 @@ * Added: 2025-11-25 */ +/** + * @file test.c + * @brief Implements the OP_PCRE2_TEST opcode (conditional build). + * + * Tests whether a PCRE2 pattern matches a subject string and returns 1 on + * success or 0 otherwise when FUN_WITH_PCRE2 is enabled. When PCRE2 support + * is disabled at build time, the opcode always returns 0. + */ + +/** + * OP_PCRE2_TEST: (pattern:any, text:any, flags:int|bool=0) -> int + * + * Behavior when FUN_WITH_PCRE2 is enabled: + * - Pops: pattern, text, flags. Converts pattern/text to strings. + * - Flags bits: + * - 1 = PCRE2_CASELESS (I) + * - 2 = PCRE2_MULTILINE (M) + * - 4 = PCRE2_DOTALL (S) + * - 8 = PCRE2_UTF (U) + * - 16 = PCRE2_EXTENDED (X) + * - Returns 1 if pcre2_match() returns a non-negative result, else 0. + * - On compile error or allocation failure, returns 0. + * + * Behavior when FUN_WITH_PCRE2 is disabled: + * - Pops three values and returns 0. + */ + /* PCRE2_TEST */ case OP_PCRE2_TEST: { #ifdef FUN_WITH_PCRE2 diff --git a/src/vm/pcsc/connect.c b/src/vm/pcsc/connect.c index ce8b215..e53fc20 100644 --- a/src/vm/pcsc/connect.c +++ b/src/vm/pcsc/connect.c @@ -5,8 +5,24 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file connect.c + * @brief Implements the OP_PCSC_CONNECT opcode (conditional build). * - * Added: 2025-10-02 + * Connects to a smart card in the specified reader using an existing PC/SC + * context. On success, allocates/returns a card handle id from the internal + * registry. When PCSC support is disabled at build time, this opcode returns 0. + */ + +/** + * OP_PCSC_CONNECT: (ctx_id:int, reader_name:any) -> int + * + * - Pops: reader_name (converted to string), then ctx_id. + * - Pushes: card handle id (>0) on success; 0 on error/invalid inputs or when + * FUN_WITH_PCSC is disabled. + * - Notes: Uses SCARD_SHARE_SHARED and negotiates T0/T1 protocols. */ /* PCSC connect */ diff --git a/src/vm/pcsc/disconnect.c b/src/vm/pcsc/disconnect.c index aa7c160..4d86449 100644 --- a/src/vm/pcsc/disconnect.c +++ b/src/vm/pcsc/disconnect.c @@ -5,8 +5,23 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file disconnect.c + * @brief Implements the OP_PCSC_DISCONNECT opcode (conditional build). * - * Added: 2025-10-02 + * Disconnects a previously connected smart-card handle and frees the + * corresponding registry slot. When PCSC support is disabled at build time, + * this opcode returns 0 after consuming its argument. + */ + +/** + * OP_PCSC_DISCONNECT: (handle_id:int) -> int + * + * - Pops: handle_id. + * - Pushes: 1 on success; 0 on error (invalid id/not found) or when disabled. + * - Notes: Uses SCardDisconnect(..., SCARD_LEAVE_CARD). */ /* PCSC disconnect */ diff --git a/src/vm/pcsc/establish.c b/src/vm/pcsc/establish.c index 88067e3..4d2047f 100644 --- a/src/vm/pcsc/establish.c +++ b/src/vm/pcsc/establish.c @@ -5,8 +5,22 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file establish.c + * @brief Implements the OP_PCSC_ESTABLISH opcode (conditional build). * - * Added: 2025-10-02 + * Creates a new PC/SC context using SCardEstablishContext(SCARD_SCOPE_SYSTEM) + * and registers it in the internal PCSC context registry when FUN_WITH_PCSC + * is enabled. Returns the allocated context id on success, or 0 on failure. + * When PCSC support is disabled at build time, this opcode returns 0. + */ + +/** + * OP_PCSC_ESTABLISH: () -> int + * + * - Returns: context id (>0) on success; 0 on error or when disabled. */ /* PCSC establish */ diff --git a/src/vm/pcsc/list_readers.c b/src/vm/pcsc/list_readers.c index 4cdd995..4e583a0 100644 --- a/src/vm/pcsc/list_readers.c +++ b/src/vm/pcsc/list_readers.c @@ -5,8 +5,22 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file list_readers.c + * @brief Implements the OP_PCSC_LIST_READERS opcode (conditional build). * - * Added: 2025-10-02 + * Lists available PC/SC reader names for a given context. On success returns + * an array of strings. When PCSC support is disabled at build time, returns an + * empty array after consuming its argument to keep the stack balanced. + */ + +/** + * OP_PCSC_LIST_READERS: (ctx_id:int) -> array + * + * - Pops: ctx_id. + * - Pushes: array of reader names on success; empty array on error/disabled. */ /* PCSC list_readers */ diff --git a/src/vm/pcsc/release.c b/src/vm/pcsc/release.c index 070c4d7..909b00d 100644 --- a/src/vm/pcsc/release.c +++ b/src/vm/pcsc/release.c @@ -5,8 +5,22 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file release.c + * @brief Implements the OP_PCSC_RELEASE opcode (conditional build). * - * Added: 2025-10-02 + * Releases a previously established PC/SC context and frees its registry slot. + * When PCSC support is disabled at build time, this opcode returns 0 after + * consuming its argument. + */ + +/** + * OP_PCSC_RELEASE: (ctx_id:int) -> int + * + * - Pops: ctx_id. + * - Pushes: 1 on success; 0 on error (invalid id/not found) or when disabled. */ /* PCSC release */ diff --git a/src/vm/pcsc/transmit.c b/src/vm/pcsc/transmit.c index f4b18ba..e1fb8fb 100644 --- a/src/vm/pcsc/transmit.c +++ b/src/vm/pcsc/transmit.c @@ -5,8 +5,28 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 + */ + +/** + * @file transmit.c + * @brief Implements the OP_PCSC_TRANSMIT opcode (conditional build). * - * Added: 2025-10-02 + * Transmits an APDU to the connected smart card associated with a given + * handle. Returns a map with the response bytes and status words. When PCSC + * support is disabled at build time, consumes its arguments and returns a map + * with code = -2. + */ + +/** + * OP_PCSC_TRANSMIT: (handle_id:int, apdu:array) -> map + * + * - Pops: apdu array, then handle_id. + * - Pushes: map { data: array, sw1:int, sw2:int, code:int }. + * - On success (code == SCARD_S_SUCCESS), data contains response bytes (no + * status words) and sw1/sw2 are the trailing status bytes. + * - On error, data is [], sw1/sw2 remain -1, and code is the PCSC error. + * - Notes: Negotiates protocol from the connected handle (T0/T1) and uses + * SCardTransmit. APDU bytes are taken modulo 256 from array ints. */ /* PCSC transmit */ diff --git a/src/vm/rust/get_sp.c b/src/vm/rust/get_sp.c index 1b7c895..5e37f8c 100644 --- a/src/vm/rust/get_sp.c +++ b/src/vm/rust/get_sp.c @@ -1,17 +1,32 @@ -/* +/** * 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-29 */ -/* - * Rust FFI demo opcode: OP_RUST_GET_SP - * Calls into Rust to read vm.sp via raw pointer math and pushes it (int). +/** + * @file get_sp.c + * @brief Implements the OP_RUST_GET_SP opcode (conditional build). + * + * Delegates to a Rust helper to read the VM's current stack pointer (sp). + * When Rust support is disabled, this raises a runtime error and pushes -1 to + * signal unavailability. + */ + +/** + * OP_RUST_GET_SP: () -> int | Nil + * + * Behavior (FUN_WITH_RUST=ON): + * - Does not pop any values. + * - Invokes fun_op_rget_sp(vm). The Rust helper may push the result or + * otherwise communicate it. The C side does not directly push here. + * + * Behavior (FUN_WITH_RUST=OFF): + * - Raises a runtime error indicating missing Rust support. + * - Pushes integer -1 as a sentinel value. */ case OP_RUST_GET_SP: { #ifdef FUN_WITH_RUST diff --git a/src/vm/rust/hello.c b/src/vm/rust/hello.c index 86a279e..7de8c69 100644 --- a/src/vm/rust/hello.c +++ b/src/vm/rust/hello.c @@ -1,17 +1,34 @@ -/* +/** * 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-27 */ /** - * Rust FFI demo opcode: OP_RUST_HELLO - * When executed, it pushes a hello string returned by Rust onto the VM stack. + * @file hello.c + * @brief Implements the OP_RUST_HELLO opcode (conditional build). + * + * This opcode demonstrates calling into a Rust function from the VM. When + * FUN_WITH_RUST is enabled at build time, it retrieves a greeting string from + * Rust and pushes it as a VAL_STRING onto the VM stack. When Rust support is + * disabled, a runtime error is raised and Nil is pushed to keep stack + * consistency. + */ + +/** + * OP_RUST_HELLO: () -> string | Nil + * + * Behavior (FUN_WITH_RUST=ON): + * - Does not pop any values. + * - Calls fun_rust_get_string() and pushes the returned C string as a + * VAL_STRING. If Rust returns NULL, an empty string is used instead. + * + * Behavior (FUN_WITH_RUST=OFF): + * - Raises a runtime error indicating missing Rust support. + * - Pushes Nil to maintain stack discipline. */ case OP_RUST_HELLO: { diff --git a/src/vm/rust/hello_args.c b/src/vm/rust/hello_args.c index ce5de0a..f365def 100644 --- a/src/vm/rust/hello_args.c +++ b/src/vm/rust/hello_args.c @@ -1,17 +1,35 @@ -/* +/** * 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-29 */ -/* - * Rust FFI demo opcode with argument: OP_RUST_HELLO_ARGS - * Pops a string from the stack and asks Rust to print it. Pushes Nil. +/** + * @file hello_args.c + * @brief Implements the OP_RUST_HELLO_ARGS opcode (conditional build). + * + * Demonstrates passing a VM value to Rust. Converts the top stack value to a + * string, calls into Rust to print/log it, and pushes Nil. When Rust support is + * disabled, the argument is still popped and freed to keep the stack sane, + * then Nil is pushed after raising an error message. + */ + +/** + * OP_RUST_HELLO_ARGS: (msg:any) -> Nil + * + * Behavior (FUN_WITH_RUST=ON): + * - Pops one value from the stack and converts it to a newly allocated C + * string via value_to_string_alloc(). + * - Calls fun_rust_print_string(msg) and then frees the allocated buffer. + * - Pushes Nil. + * + * Behavior (FUN_WITH_RUST=OFF): + * - Pops and frees one value to preserve stack discipline. + * - Raises a runtime error indicating missing Rust support. + * - Pushes Nil. */ case OP_RUST_HELLO_ARGS: { #ifdef FUN_WITH_RUST diff --git a/src/vm/rust/hello_args_return.c b/src/vm/rust/hello_args_return.c index 3fed3c3..4c94425 100644 --- a/src/vm/rust/hello_args_return.c +++ b/src/vm/rust/hello_args_return.c @@ -1,18 +1,37 @@ -/* +/** * 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-29 */ -/* - * Rust FFI demo opcode with argument: OP_RUST_HELLO_ARGS_RETURN - * Pops a string from the stack, asks Rust to return it back (no printing). - * Pushes the returned string, or Nil on error. +/** + * @file hello_args_return.c + * @brief Implements the OP_RUST_HELLO_ARGS_RETURN opcode (conditional build). + * + * Demonstrates round-tripping a string value through Rust. Pops a value, + * converts it to a C string, passes it to Rust, and pushes back the string + * returned by Rust. If Rust returns NULL or Rust support is disabled, Nil is + * pushed. + */ + +/** + * OP_RUST_HELLO_ARGS_RETURN: (msg:any) -> string | Nil + * + * Behavior (FUN_WITH_RUST=ON): + * - Pops one value from the stack and converts it to a newly allocated C + * string via value_to_string_alloc(). + * - Calls fun_rust_echo_string(msg) which returns an owned C string pointer + * that must be released via fun_rust_string_free(). + * - Pushes the returned string as VAL_STRING on success; pushes Nil if Rust + * returns NULL. + * + * Behavior (FUN_WITH_RUST=OFF): + * - Pops and frees one value to preserve stack discipline. + * - Raises a runtime error indicating missing Rust support. + * - Pushes Nil. */ case OP_RUST_HELLO_ARGS_RETURN: { #ifdef FUN_WITH_RUST diff --git a/src/vm/rust/set_exit.c b/src/vm/rust/set_exit.c index ad3163c..66dd271 100644 --- a/src/vm/rust/set_exit.c +++ b/src/vm/rust/set_exit.c @@ -1,17 +1,34 @@ -/* +/** * 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-29 */ -/* - * Rust FFI demo opcode: OP_RUST_SET_EXIT - * Pops an int code and asks Rust to write it into vm.exit_code. +/** + * @file set_exit.c + * @brief Implements the OP_RUST_SET_EXIT opcode (conditional build). + * + * Pops an integer exit code and delegates to a Rust helper to store it into + * vm.exit_code. Pushes Nil afterwards. When Rust support is disabled, the + * argument is still popped/freed, a runtime error is raised, and Nil is + * pushed to maintain stack discipline. + */ + +/** + * OP_RUST_SET_EXIT: (code:int) -> Nil + * + * Behavior (FUN_WITH_RUST=ON): + * - Expects an integer on the stack (provided by previous opcodes). + * - Calls fun_op_rset_exit(vm) which pops the int and writes vm->exit_code. + * - Pushes Nil. + * + * Behavior (FUN_WITH_RUST=OFF): + * - Pops and frees one value to preserve stack discipline. + * - Raises a runtime error indicating missing Rust support. + * - Pushes Nil. */ case OP_RUST_SET_EXIT: { #ifdef FUN_WITH_RUST diff --git a/src/vm/sclamp.c b/src/vm/sclamp.c index 52066d6..10a7afb 100644 --- a/src/vm/sclamp.c +++ b/src/vm/sclamp.c @@ -7,6 +7,20 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file sclamp.c + * @brief Implements the OP_SCLAMP opcode for signed N-bit two's-complement wrapping. + * + * This VM opcode wraps an integer into the signed N-bit range using two's + * complement semantics. It first masks to N bits and then sign-extends if the + * sign bit is set, yielding a value in the range [-2^(N-1) .. 2^(N-1)-1]. + * N is provided by the instruction operand. + * + * Stack contract: + * - Pops: value (int) + * - Pushes: value (int) + */ + case OP_SCLAMP: { /* Two's complement wrap to signed N-bit range: - Mask to N bits diff --git a/src/vm/sqlite/close.c b/src/vm/sqlite/close.c index d8aa477..d2d4e2d 100644 --- a/src/vm/sqlite/close.c +++ b/src/vm/sqlite/close.c @@ -1,12 +1,18 @@ -/* +/** * 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 + */ + +/** + * @file close.c + * @brief Implements the OP_SQLITE_CLOSE opcode (conditional build). * - * Added: 2025-11-26 + * Closes a registered SQLite database handle and unregisters it when + * FUN_WITH_SQLITE is enabled. No-op when SQLite support is disabled. */ /** diff --git a/src/vm/sqlite/exec.c b/src/vm/sqlite/exec.c index 8bb99d1..8580d21 100644 --- a/src/vm/sqlite/exec.c +++ b/src/vm/sqlite/exec.c @@ -1,12 +1,18 @@ -/* +/** * 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 + */ + +/** + * @file exec.c + * @brief Implements the OP_SQLITE_EXEC opcode (conditional build). * - * Added: 2025-11-26 + * Executes a SQL statement against an open SQLite handle when + * FUN_WITH_SQLITE is enabled. Returns the SQLite result code. */ /** diff --git a/src/vm/sqlite/open.c b/src/vm/sqlite/open.c index 814937e..9b1f537 100644 --- a/src/vm/sqlite/open.c +++ b/src/vm/sqlite/open.c @@ -1,12 +1,18 @@ -/* +/** * 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 + */ + +/** + * @file open.c + * @brief Implements the OP_SQLITE_OPEN opcode (conditional build). * - * Added: 2025-11-26 + * Opens a SQLite database and registers a handle when FUN_WITH_SQLITE is + * enabled. On failure or when SQLite support is disabled, pushes 0. */ /** diff --git a/src/vm/sqlite/query.c b/src/vm/sqlite/query.c index e08424b..ac88673 100644 --- a/src/vm/sqlite/query.c +++ b/src/vm/sqlite/query.c @@ -1,12 +1,18 @@ -/* +/** * 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 + */ + +/** + * @file query.c + * @brief Implements the OP_SQLITE_QUERY opcode (conditional build). * - * Added: 2025-11-26 + * Prepares and steps through a SQLite statement to produce an array of row + * maps when FUN_WITH_SQLITE is enabled. Returns an empty array otherwise. */ /** diff --git a/src/vm/strings/regex_match.c b/src/vm/strings/regex_match.c index 857e1e2..d083ade 100644 --- a/src/vm/strings/regex_match.c +++ b/src/vm/strings/regex_match.c @@ -5,10 +5,32 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2025-10-04 */ +/** + * @file regex_match.c + * @brief VM opcode snippet for OP_REGEX_MATCH (POSIX full-match). + * + * This opcode checks whether a regular expression pattern matches the entire + * input string. It uses POSIX regex APIs on UNIX platforms and provides a + * graceful fallback elsewhere. + * + * Behavior (stack effects): + * - Pops: pattern (string), input (string) + * - Pushes: result (int) — 1 if the pattern matches the whole input string, + * 0 otherwise. On invalid regex, returns 0. + * + * Platform notes: + * - On non-UNIX platforms (no POSIX regex available), this opcode returns 0 + * without error. + * + * Errors: + * - If operands are not strings, the VM prints a runtime type error and exits. + * + * Example: + * - pattern = "[a-z]+", input = "hello" -> 1 + * - pattern = "[a-z]+", input = "hello!" -> 0 (not a full match) + */ /* Regex full-match opcode using POSIX regex */ #ifdef __unix__ #include diff --git a/src/vm/strings/regex_replace.c b/src/vm/strings/regex_replace.c index 37242b0..f0a92f8 100644 --- a/src/vm/strings/regex_replace.c +++ b/src/vm/strings/regex_replace.c @@ -5,10 +5,32 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2025-10-04 */ +/** + * @file regex_replace.c + * @brief VM opcode snippet for OP_REGEX_REPLACE (POSIX global replace). + * + * Performs a global search-and-replace on the input string using a POSIX + * regular expression pattern and a replacement string. Backreferences in the + * replacement are not expanded (simple literal replacement). + * + * Behavior (stack effects): + * - Pops: replacement (string), pattern (string), input (string) + * - Pushes: output (string) — the transformed string after replacing all + * matches; on invalid regex, the original input is returned unchanged. + * + * Platform notes: + * - On non-UNIX platforms (no POSIX regex), the original input string is + * returned unchanged. + * + * Errors: + * - If operands have invalid types, the VM prints a runtime type error and + * exits. + * + * Example: + * - pattern = "[0-9]+", repl = "#", input = "a1b22c" -> "a#b#c" + */ /* Regex global replace opcode using POSIX regex */ #ifdef __unix__ #include diff --git a/src/vm/strings/regex_search.c b/src/vm/strings/regex_search.c index 1d4fb82..192f2ca 100644 --- a/src/vm/strings/regex_search.c +++ b/src/vm/strings/regex_search.c @@ -5,10 +5,35 @@ * Copyright 2025 Johannes Findeisen * Licensed under the terms of the Apache-2.0 license. * https://opensource.org/license/apache-2-0 - * - * Added: 2025-10-04 */ +/** + * @file regex_search.c + * @brief VM opcode snippet for OP_REGEX_SEARCH (POSIX first-match search). + * + * Finds the first match of a POSIX regular expression within an input string + * and returns a map with details about the match and captured groups. + * + * Behavior (stack effects): + * - Pops: pattern (string), input (string) + * - Pushes: map with keys: + * - "match": string — the matched substring (empty if no match) + * - "start": int — start index of the match, or -1 + * - "end": int — end index (exclusive) of the match, or -1 + * - "groups": array — captured groups (may be empty) + * + * Platform notes: + * - On non-UNIX platforms (no POSIX regex available), returns a default map + * with empty match, start=-1, end=-1, and empty groups. + * + * Errors: + * - If operands are not strings, the VM prints a runtime type error and exits. + * - If the pattern is an invalid regex, returns the default empty map. + * + * Example: + * - pattern = "h(ell)o", input = "oh hello!" -> + * { match: "hello", start: 3, end: 8, groups: ["ell"] } + */ /* Regex search (first match) opcode using POSIX regex */ #ifdef __unix__ #include diff --git a/src/vm/typeof.c b/src/vm/typeof.c index bb1484d..8dcc09d 100644 --- a/src/vm/typeof.c +++ b/src/vm/typeof.c @@ -7,6 +7,20 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file typeof.c + * @brief Implements the OP_TYPEOF opcode for obtaining a human-readable type name. + * + * This snippet is included by the VM's opcode dispatch. It pops a single value + * from the stack, determines its runtime type, pushes a new string with a + * human-readable type name (e.g., "Number", "String", "Array"), and frees the + * original value. + * + * Stack contract: + * - Pops: value (any) + * - Pushes: string (type name) + */ + case OP_TYPEOF: { Value v = pop_value(vm); const char *tname = "Unknown"; diff --git a/src/vm/uclamp.c b/src/vm/uclamp.c index a40a602..2560a6f 100644 --- a/src/vm/uclamp.c +++ b/src/vm/uclamp.c @@ -7,6 +7,19 @@ * https://opensource.org/license/apache-2-0 */ +/** + * @file uclamp.c + * @brief Implements the OP_UCLAMP opcode for unsigned N-bit wrapping. + * + * This VM opcode masks an integer to the lower N bits (N taken from the + * instruction operand), effectively performing an unsigned wrap-around into + * the range [0 .. 2^N - 1]. + * + * Stack contract: + * - Pops: value (int) + * - Pushes: value (int) + */ + case OP_UCLAMP: { /* Unsigned wrap to N bits: mask lower N bits (operand = bits) */ Value v = pop_value(vm); diff --git a/src/vm/xml/name.c b/src/vm/xml/name.c index 2120610..a0d5c79 100644 --- a/src/vm/xml/name.c +++ b/src/vm/xml/name.c @@ -1,14 +1,38 @@ -/* +/** * 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-09 */ +/** + * @file + * @brief VM opcode snippet for XML node name retrieval. + * + * This file is included into the main VM dispatch switch in vm.c and + * implements the OP_XML_NAME instruction. + * + * Opcode: OP_XML_NAME + * - Stack effect: pop (int node_handle) → push (string name) + * - Success: pushes the element/node name as a string. If the node has no + * name, an empty string is pushed. + * - Errors/edge cases: if the handle is invalid or the node cannot be + * retrieved, an empty string is pushed. The instruction does not throw. + * - Gating: When FUN_WITH_XML2 is not enabled at build time, the operand is + * still popped and an empty string is pushed. + * + * Notes + * - Node handles are small positive integers managed by the XML handle + * registry defined in the xml2 extension (see extensions/xml2.c). + * - The pushed string is a new VM value; ownership is transferred to the VM + * stack as usual. + * + * Example + * // ... stack: [ node_handle ] + * OP_XML_NAME // → [ "book" ] + */ /* OP_XML_NAME: pops node handle; pushes string */ case OP_XML_NAME: { #ifdef FUN_WITH_XML2 diff --git a/src/vm/xml/parse.c b/src/vm/xml/parse.c index 226ef1e..c915752 100644 --- a/src/vm/xml/parse.c +++ b/src/vm/xml/parse.c @@ -1,14 +1,36 @@ -/* +/** * 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-09 */ +/** + * @file + * @brief VM opcode snippet for parsing XML text into a document handle. + * + * This snippet is included by vm.c and implements OP_XML_PARSE. + * + * Opcode: OP_XML_PARSE + * - Stack effect: pop (string xml_text) → push (int doc_handle | 0) + * - Behavior: Parses the given XML string into an in-memory xmlDoc. + * On success, a positive document handle is allocated via the XML handle + * registry and pushed. On failure (malformed XML, OOM), 0 is pushed. + * - Initialization: Ensures libxml2 is initialized once (xmlInitParser()). + * - Gating: If FUN_WITH_XML2 is disabled, the input is discarded and 0 is + * pushed. + * + * Notes + * - The handle must later be released via appropriate XML VM opcodes that + * free documents; leaking handles will leak memory. + * - The parser is invoked with XML_PARSE_NONET to disallow network access. + * + * Example + * // stack: [ "" ] + * OP_XML_PARSE // → [ 1 ] (example handle) + */ /* OP_XML_PARSE: pops text string; pushes doc handle (>0) or 0 */ case OP_XML_PARSE: { #ifdef FUN_WITH_XML2 diff --git a/src/vm/xml/root.c b/src/vm/xml/root.c index 49b82cf..fdcc61a 100644 --- a/src/vm/xml/root.c +++ b/src/vm/xml/root.c @@ -1,14 +1,34 @@ -/* +/** * 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-09 */ +/** + * @file + * @brief VM opcode snippet for retrieving the root node of an XML document. + * + * Included by vm.c; implements OP_XML_ROOT. + * + * Opcode: OP_XML_ROOT + * - Stack effect: pop (int doc_handle) → push (int node_handle | 0) + * - Behavior: For a valid document handle, obtains the document's root + * element and returns a positive node handle. If the document is invalid + * or has no root element, 0 is pushed. + * - Gating: If FUN_WITH_XML2 is disabled, the input is discarded and 0 is + * pushed. + * + * Notes + * - Returned node handles are managed by the XML node registry. They must be + * released by corresponding XML VM opcodes to avoid leaks. + * + * Example + * // stack: [ doc_handle ] + * OP_XML_ROOT // → [ node_handle ] or [ 0 ] + */ /* OP_XML_ROOT: pops doc handle; pushes node handle (>0) or 0 */ case OP_XML_ROOT: { #ifdef FUN_WITH_XML2 diff --git a/src/vm/xml/text.c b/src/vm/xml/text.c index 922d369..7acefad 100644 --- a/src/vm/xml/text.c +++ b/src/vm/xml/text.c @@ -1,14 +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-09 */ +/** + * @file + * @brief VM opcode snippet for retrieving the concatenated text of an XML node. + * + * Included by vm.c; implements OP_XML_TEXT. + * + * Opcode: OP_XML_TEXT + * - Stack effect: pop (int node_handle) → push (string text) + * - Behavior: Retrieves the node's textual content as produced by + * xmlNodeGetContent(), which concatenates all descendant text nodes. + * If the node is invalid or has no textual content, an empty string is + * pushed. + * - Gating: If FUN_WITH_XML2 is disabled, the input is discarded and an empty + * string is pushed. + * + * Notes + * - The returned string is copied into a VM string value; libxml2 buffers are + * freed immediately after use. + * + * Example + * // stack: [ node_handle ] + * OP_XML_TEXT // → [ "Hello world" ] + */ /* OP_XML_TEXT: pops node handle; pushes string (concatenate text node children) */ case OP_XML_TEXT: { #ifdef FUN_WITH_XML2