1
0
Fork 0
forked from fun/fun

Fixed the code style in *.c files to two spaces indentation and add a linter named funstx to Fun. (0.39.0)

This commit is contained in:
Johannes Findeisen 2026-03-18 20:52:00 +01:00
commit 03b2532474
237 changed files with 17550 additions and 13911 deletions

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.10)
project(fun VERSION 0.38.16 LANGUAGES C)
project(fun VERSION 0.39.0 LANGUAGES C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
@ -46,7 +46,16 @@ message(STATUS "===========================")
# Convenience aggregate target (like 'build' in Makefile)
add_custom_target(build
DEPENDS fun fun_test test_opcodes
DEPENDS fun funstx fun_test test_opcodes
)
# Formatting helper target: run clang-format over C/C++ sources using .clang-format
add_custom_target(format
COMMAND ${CMAKE_COMMAND} -E echo "Running clang-format on sources..."
COMMAND /bin/sh -c "command -v clang-format >/dev/null 2>&1 || { echo 'clang-format not found in PATH' >&2; exit 1; }"
COMMAND /bin/sh -c "set -e; for pat in '*.c' '*.h' '*.cpp' '*.hpp'; do find ${CMAKE_SOURCE_DIR}/src -type f -name \"\$pat\" -print0; done | xargs -0 -n 50 clang-format -i"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Apply .clang-format (IndentWidth=2) to all source files"
)
# --- Rust (Cargo) integration: optionally build and link a staticlib with opcode examples ---
@ -400,6 +409,11 @@ install(TARGETS fun
RUNTIME DESTINATION /usr/bin
)
# Also install the syntax-checker CLI by default
install(TARGETS funstx
RUNTIME DESTINATION /usr/bin
)
# Libs
install(DIRECTORY lib/
DESTINATION /usr/share/fun/lib

View file

@ -139,6 +139,12 @@ if(FUN_WITH_REPL)
endif()
target_link_libraries(fun PRIVATE fun_core)
# Executable: funstx (syntax checker CLI)
add_executable(funstx
${CMAKE_SOURCE_DIR}/src/funstx.c
)
target_link_libraries(funstx PRIVATE fun_core)
# Internal test programs
add_executable(fun_test
${CMAKE_SOURCE_DIR}/src/fun_test.c)

View file

@ -1,24 +1,24 @@
#!/usr/bin/env fun
/*
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*/
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*/
/*
* Arrays Example
*
* Demonstrates basic array operations including:
* - Array declaration and initialization
* - Accessing elements via indexing
* - Iterating through arrays with `for` loops
* - Creating subarrays (slices)
*- Length calculation
*/
* Arrays Example
*
* Demonstrates basic array operations including:
* - Array declaration and initialization
* - Accessing elements via indexing
* - Iterating through arrays with `for` loops
* - Creating subarrays (slices)
*- Length calculation
*/
// Arrays basics
arr = [1, 2, 3]

View file

@ -1,24 +1,24 @@
#!/usr/bin/env fun
/*
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*/
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*/
/*
* Array Iteration Examples
*
* Focuses specifically on:
* - Various iteration methods (`for`, `while`)
* - Using array mapping functions
* - Filtering arrays based on conditions
* - Reducing arrays to computed values
*- Converting between different collection types during iteration
*/
* Array Iteration Examples
*
* Focuses specifically on:
* - Various iteration methods (`for`, `while`)
* - Using array mapping functions
* - Filtering arrays based on conditions
* - Reducing arrays to computed values
*- Converting between different collection types during iteration
*/
// for-in over an array literal
for x in [1, 2, 3]

View file

@ -1,26 +1,26 @@
#!/usr/bin/env fun
/*
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2026-01-13
*/
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2026-01-13
*/
/*
* Base64 usage demo (RFC 4648, standard alphabet)
*
* Run without installing:
* FUN_LIB_DIR="$(pwd)/lib" ./build/fun examples/base64_demo.fun
*/
* Base64 usage demo (RFC 4648, standard alphabet)
*
* Run without installing:
* FUN_LIB_DIR="$(pwd)/lib" ./build/fun examples/base64_demo.fun
*/
#include <encoding/base64.fun>
print("=== Base64 demo ===")
print("=== Base64 demo ===")
// Bytes for the ASCII string "Hello"
bytes = [0x48, 0x65, 0x6c, 0x6c, 0x6f]

View file

@ -1,15 +1,15 @@
#!/usr/bin/env fun
/*
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2026-01-29
*/
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2026-01-29
*/
/*
Build:

View file

@ -1,15 +1,15 @@
#!/usr/bin/env fun
/*
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-12-09
*/
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-12-09
*/
// Simple stopwatch using DateTime helpers
#include <utils/datetime.fun>

View file

@ -1,15 +1,15 @@
#!/usr/bin/env fun
/*
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-12-11
*/
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-12-11
*/
// Demonstrates usage of RANDOM_SEED and RANDOM_INT opcodes via
// the built-ins: random_seed(seed) and random_int(lo, hiExclusive).

View file

@ -1,33 +1,33 @@
#!/usr/bin/env fun
/*
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2026-01-27
*/
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2026-01-27
*/
/*
* Rust-backed opcode demo: rust_hello()
*
* Build instructions:
* - Default builds disable Rust integration.
* - Enable it via: cmake -S . -B build_debug -DFUN_WITH_RUST=ON
* - Then: cmake --build build_debug --target fun
*
* Run:
* build_debug/fun examples/rust_hello.fun
*
* Expected output (with FUN_WITH_RUST=ON):
* Hello from Rust ops!
*
* If built without Rust, calling rust_hello() will raise a runtime error
* explaining that Rust integration is disabled.
*/
* Rust-backed opcode demo: rust_hello()
*
* Build instructions:
* - Default builds disable Rust integration.
* - Enable it via: cmake -S . -B build_debug -DFUN_WITH_RUST=ON
* - Then: cmake --build build_debug --target fun
*
* Run:
* build_debug/fun examples/rust_hello.fun
*
* Expected output (with FUN_WITH_RUST=ON):
* Hello from Rust ops!
*
* If built without Rust, calling rust_hello() will raise a runtime error
* explaining that Rust integration is disabled.
*/
print(rust_hello())

View file

@ -1,13 +1,13 @@
#!/usr/bin/env fun
/*
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*/
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*/
// Short-circuit demo for || and &&

View file

@ -1,15 +1,15 @@
#!/usr/bin/env fun
/*
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-12-27
*/
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-12-27
*/
print(bxor(0x80000000, 0x00000001))
print(bor(0x80000000, 0x00000001))

View file

@ -1,15 +1,15 @@
#!/usr/bin/env fun
/*
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-09-30
*/
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
* Licensed under the terms of the Apache-2.0 license.
* https://opensource.org/license/apache-2-0
*
* Added: 2025-09-30
*/
// Threading demo for Fun
// Run without installing:

View file

@ -12,46 +12,46 @@
/* array utilities */
int array_contains(const Value *v, const Value *needle) {
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
int n = array_length(v);
for (int i = 0; i < n; ++i) {
Value item;
if (array_get_copy(v, i, &item)) {
int eq = value_equals(&item, needle);
free_value(item);
if (eq) return 1;
}
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
int n = array_length(v);
for (int i = 0; i < n; ++i) {
Value item;
if (array_get_copy(v, i, &item)) {
int eq = value_equals(&item, needle);
free_value(item);
if (eq) return 1;
}
return 0;
}
return 0;
}
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);
for (int i = 0; i < n; ++i) {
Value item;
if (array_get_copy(v, i, &item)) {
int eq = value_equals(&item, needle);
free_value(item);
if (eq) return i;
}
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
int n = array_length(v);
for (int i = 0; i < n; ++i) {
Value item;
if (array_get_copy(v, i, &item)) {
int eq = value_equals(&item, needle);
free_value(item);
if (eq) return i;
}
return -1;
}
return -1;
}
void array_clear(Value *v) {
if (!v || v->type != VAL_ARRAY || !v->arr) return;
int n = array_length(v);
for (int i = 0; i < n; ++i) {
Value item;
if (array_get_copy(v, i, &item)) {
free_value(item);
}
}
/* internal clear uses public API to reset to zero length */
/* Since we don't expose capacity, emulate by popping elements */
Value out;
while (array_pop(v, &out)) {
free_value(out);
if (!v || v->type != VAL_ARRAY || !v->arr) return;
int n = array_length(v);
for (int i = 0; i < n; ++i) {
Value item;
if (array_get_copy(v, i, &item)) {
free_value(item);
}
}
/* internal clear uses public API to reset to zero length */
/* Since we don't expose capacity, emulate by popping elements */
Value out;
while (array_pop(v, &out)) {
free_value(out);
}
}

View file

@ -8,235 +8,400 @@
*/
#include "bytecode.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
Bytecode *bytecode_new(void) {
Bytecode *bc = (Bytecode*)malloc(sizeof(Bytecode));
bc->instructions = NULL;
bc->instr_count = 0;
bc->constants = NULL;
bc->const_count = 0;
bc->name = NULL;
bc->source_file = NULL;
return bc;
Bytecode *bc = (Bytecode *)malloc(sizeof(Bytecode));
bc->instructions = NULL;
bc->instr_count = 0;
bc->constants = NULL;
bc->const_count = 0;
bc->name = NULL;
bc->source_file = NULL;
return bc;
}
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++;
bc->constants = (Value *)realloc(bc->constants, sizeof(Value) * (bc->const_count + 1));
bc->constants[bc->const_count] = copy_value(&v);
return bc->const_count++;
}
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;
bc->instructions[bc->instr_count].operand = operand;
return bc->instr_count++;
bc->instructions = (Instruction *)realloc(bc->instructions, sizeof(Instruction) * (bc->instr_count + 1));
bc->instructions[bc->instr_count].op = op;
bc->instructions[bc->instr_count].operand = operand;
return bc->instr_count++;
}
void bytecode_set_operand(Bytecode *bc, int idx, int32_t operand) {
if (idx >= 0 && idx < bc->instr_count) {
bc->instructions[idx].operand = operand;
}
if (idx >= 0 && idx < bc->instr_count) {
bc->instructions[idx].operand = operand;
}
}
void bytecode_free(Bytecode *bc) {
if (!bc) return;
for (int i = 0; i < bc->const_count; ++i) {
free_value(bc->constants[i]);
}
free(bc->constants);
free(bc->instructions);
if (bc->name) free((void*)bc->name);
if (bc->source_file) free((void*)bc->source_file);
free(bc);
if (!bc) return;
for (int i = 0; i < bc->const_count; ++i) {
free_value(bc->constants[i]);
}
free(bc->constants);
free(bc->instructions);
if (bc->name) free((void *)bc->name);
if (bc->source_file) free((void *)bc->source_file);
free(bc);
}
static const char *opcode_name(OpCode op) {
switch (op) {
case OP_NOP: return "NOP";
case OP_LOAD_CONST: return "LOAD_CONST";
case OP_LOAD_LOCAL: return "LOAD_LOCAL";
case OP_STORE_LOCAL: return "STORE_LOCAL";
case OP_LOAD_GLOBAL: return "LOAD_GLOBAL";
case OP_STORE_GLOBAL: return "STORE_GLOBAL";
case OP_ADD: return "ADD";
case OP_SUB: return "SUB";
case OP_MUL: return "MUL";
case OP_DIV: return "DIV";
case OP_LT: return "LT";
case OP_LTE: return "LTE";
case OP_GT: return "GT";
case OP_GTE: return "GTE";
case OP_EQ: return "EQ";
case OP_NEQ: return "NEQ";
case OP_POP: return "POP";
case OP_JUMP: return "JUMP";
case OP_JUMP_IF_FALSE: return "JUMP_IF_FALSE";
case OP_CALL: return "CALL";
case OP_RETURN: return "RETURN";
case OP_PRINT: return "PRINT";
case OP_ECHO: return "ECHO";
case OP_HALT: return "HALT";
case OP_MOD: return "MOD";
case OP_AND: return "AND";
case OP_OR: return "OR";
case OP_NOT: return "NOT";
case OP_DUP: return "DUP";
case OP_SWAP: return "SWAP";
case OP_MAKE_ARRAY: return "MAKE_ARRAY";
case OP_INDEX_GET: return "INDEX_GET";
case OP_INDEX_SET: return "INDEX_SET";
case OP_LEN: return "LEN";
case OP_PUSH: return "ARR_PUSH";
case OP_APOP: return "ARR_POP";
case OP_SET: return "ARR_SET";
case OP_INSERT: return "ARR_INSERT";
case OP_REMOVE: return "ARR_REMOVE";
case OP_SLICE: return "SLICE";
case OP_TO_NUMBER: return "TO_NUMBER";
case OP_TO_STRING: return "TO_STRING";
case OP_TYPEOF: return "TYPEOF";
case OP_CAST: return "CAST";
case OP_SPLIT: return "SPLIT";
case OP_JOIN: return "JOIN";
case OP_SUBSTR: return "SUBSTR";
case OP_FIND: return "FIND";
case OP_REGEX_MATCH: return "REGEX_MATCH";
case OP_REGEX_SEARCH: return "REGEX_SEARCH";
case OP_REGEX_REPLACE: return "REGEX_REPLACE";
case OP_CONTAINS: return "CONTAINS";
case OP_INDEX_OF: return "INDEX_OF";
case OP_CLEAR: return "CLEAR";
case OP_ENUMERATE: return "ENUMERATE";
case OP_ZIP: return "ZIP";
case OP_MIN: return "MIN";
case OP_MAX: return "MAX";
case OP_CLAMP: return "CLAMP";
case OP_ABS: return "ABS";
case OP_POW: return "POW";
case OP_RANDOM_SEED: return "RANDOM_SEED";
case OP_RANDOM_INT: return "RANDOM_INT";
case OP_MAKE_MAP: return "MAKE_MAP";
case OP_KEYS: return "KEYS";
case OP_VALUES: return "VALUES";
case OP_HAS_KEY: return "HAS_KEY";
case OP_READ_FILE: return "READ_FILE";
case OP_WRITE_FILE: return "WRITE_FILE";
case OP_ENV: return "ENV";
case OP_INPUT_LINE: return "INPUT_LINE";
case OP_PROC_RUN: return "PROC_RUN";
case OP_PROC_SYSTEM: return "PROC_SYSTEM";
case OP_TIME_NOW_MS: return "TIME_NOW_MS";
case OP_CLOCK_MONO_MS: return "CLOCK_MONO_MS";
case OP_DATE_FORMAT: return "DATE_FORMAT";
case OP_ENV_ALL: return "ENV_ALL";
case OP_FUN_VERSION: return "FUN_VERSION";
case OP_THREAD_SPAWN: return "THREAD_SPAWN";
case OP_THREAD_JOIN: return "THREAD_JOIN";
case OP_SLEEP_MS: return "SLEEP_MS";
case OP_RANDOM_NUMBER: return "RANDOM_NUMBER";
case OP_BAND: return "BAND";
case OP_BOR: return "BOR";
case OP_BXOR: return "BXOR";
case OP_BNOT: return "BNOT";
case OP_SHL: return "SHL";
case OP_SHR: return "SHR";
case OP_ROTL: return "ROTL";
case OP_ROTR: return "ROTR";
case OP_JSON_PARSE: return "JSON_PARSE";
case OP_JSON_STRINGIFY: return "JSON_STRINGIFY";
case OP_JSON_FROM_FILE: return "JSON_FROM_FILE";
case OP_JSON_TO_FILE: return "JSON_TO_FILE";
case OP_CURL_GET: return "CURL_GET";
case OP_CURL_POST: return "CURL_POST";
case OP_CURL_DOWNLOAD: return "CURL_DOWNLOAD";
case OP_SQLITE_OPEN: return "SQLITE_OPEN";
case OP_SQLITE_CLOSE: return "SQLITE_CLOSE";
case OP_SQLITE_EXEC: return "SQLITE_EXEC";
case OP_SQLITE_QUERY: return "SQLITE_QUERY";
case OP_LIBSQL_OPEN: return "LIBSQL_OPEN";
case OP_LIBSQL_CLOSE: return "LIBSQL_CLOSE";
case OP_LIBSQL_EXEC: return "LIBSQL_EXEC";
case OP_LIBSQL_QUERY: return "LIBSQL_QUERY";
case OP_PCSC_ESTABLISH: return "PCSC_ESTABLISH";
case OP_PCSC_RELEASE: return "PCSC_RELEASE";
case OP_PCSC_LIST_READERS: return "PCSC_LIST_READERS";
case OP_PCSC_CONNECT: return "PCSC_CONNECT";
case OP_PCSC_DISCONNECT: return "PCSC_DISCONNECT";
case OP_PCSC_TRANSMIT: return "PCSC_TRANSMIT";
case OP_PCRE2_TEST: return "PCRE2_TEST";
case OP_PCRE2_MATCH: return "PCRE2_MATCH";
case OP_PCRE2_FINDALL: return "PCRE2_FINDALL";
case OP_OPENSSL_MD5: return "OPENSSL_MD5";
case OP_OPENSSL_SHA256: return "OPENSSL_SHA256";
case OP_OPENSSL_SHA512: return "OPENSSL_SHA512";
case OP_OPENSSL_RIPEMD160: return "OPENSSL_RIPEMD160";
case OP_LIBRESSL_MD5: return "LIBRESSL_MD5";
case OP_LIBRESSL_SHA256: return "LIBRESSL_SHA256";
case OP_LIBRESSL_SHA512: return "LIBRESSL_SHA512";
case OP_LIBRESSL_RIPEMD160: return "LIBRESSL_RIPEMD160";
case OP_INI_LOAD: return "INI_LOAD";
case OP_INI_FREE: return "INI_FREE";
case OP_INI_GET_STRING: return "INI_GET_STRING";
case OP_INI_GET_INT: return "INI_GET_INT";
case OP_INI_GET_DOUBLE: return "INI_GET_DOUBLE";
case OP_INI_GET_BOOL: return "INI_GET_BOOL";
case OP_INI_SET: return "INI_SET";
case OP_INI_UNSET: return "INI_UNSET";
case OP_INI_SAVE: return "INI_SAVE";
case OP_XML_PARSE: return "XML_PARSE";
case OP_XML_ROOT: return "XML_ROOT";
case OP_XML_NAME: return "XML_NAME";
case OP_XML_TEXT: return "XML_TEXT";
case OP_TK_EVAL: return "TK_EVAL";
case OP_TK_RESULT: return "TK_RESULT";
case OP_TK_LOOP: return "TK_LOOP";
case OP_TK_WM_TITLE: return "TK_WM_TITLE";
case OP_TK_LABEL: return "TK_LABEL";
case OP_TK_BUTTON: return "TK_BUTTON";
case OP_TK_PACK: return "TK_PACK";
case OP_FLOOR: return "FLOOR";
case OP_CEIL: return "CEIL";
case OP_TRUNC: return "TRUNC";
case OP_ROUND: return "ROUND";
case OP_SIN: return "SIN";
case OP_COS: return "COS";
case OP_TAN: return "TAN";
case OP_EXP: return "EXP";
case OP_LOG: return "LOG";
case OP_LOG10: return "LOG10";
case OP_SQRT: return "SQRT";
case OP_GCD: return "GCD";
case OP_LCM: return "LCM";
case OP_ISQRT: return "ISQRT";
case OP_SIGN: return "SIGN";
case OP_FMIN: return "FMIN";
case OP_FMAX: return "FMAX";
case OP_RUST_HELLO: return "RUST_HELLO";
case OP_RUST_HELLO_ARGS: return "RUST_HELLO_ARGS";
case OP_RUST_HELLO_ARGS_RETURN: return "RUST_HELLO_ARGS_RETURN";
case OP_RUST_GET_SP: return "RUST_GET_SP";
case OP_RUST_SET_EXIT: return "RUST_SET_EXIT";
default: return "???";
}
switch (op) {
case OP_NOP:
return "NOP";
case OP_LOAD_CONST:
return "LOAD_CONST";
case OP_LOAD_LOCAL:
return "LOAD_LOCAL";
case OP_STORE_LOCAL:
return "STORE_LOCAL";
case OP_LOAD_GLOBAL:
return "LOAD_GLOBAL";
case OP_STORE_GLOBAL:
return "STORE_GLOBAL";
case OP_ADD:
return "ADD";
case OP_SUB:
return "SUB";
case OP_MUL:
return "MUL";
case OP_DIV:
return "DIV";
case OP_LT:
return "LT";
case OP_LTE:
return "LTE";
case OP_GT:
return "GT";
case OP_GTE:
return "GTE";
case OP_EQ:
return "EQ";
case OP_NEQ:
return "NEQ";
case OP_POP:
return "POP";
case OP_JUMP:
return "JUMP";
case OP_JUMP_IF_FALSE:
return "JUMP_IF_FALSE";
case OP_CALL:
return "CALL";
case OP_RETURN:
return "RETURN";
case OP_PRINT:
return "PRINT";
case OP_ECHO:
return "ECHO";
case OP_HALT:
return "HALT";
case OP_MOD:
return "MOD";
case OP_AND:
return "AND";
case OP_OR:
return "OR";
case OP_NOT:
return "NOT";
case OP_DUP:
return "DUP";
case OP_SWAP:
return "SWAP";
case OP_MAKE_ARRAY:
return "MAKE_ARRAY";
case OP_INDEX_GET:
return "INDEX_GET";
case OP_INDEX_SET:
return "INDEX_SET";
case OP_LEN:
return "LEN";
case OP_PUSH:
return "ARR_PUSH";
case OP_APOP:
return "ARR_POP";
case OP_SET:
return "ARR_SET";
case OP_INSERT:
return "ARR_INSERT";
case OP_REMOVE:
return "ARR_REMOVE";
case OP_SLICE:
return "SLICE";
case OP_TO_NUMBER:
return "TO_NUMBER";
case OP_TO_STRING:
return "TO_STRING";
case OP_TYPEOF:
return "TYPEOF";
case OP_CAST:
return "CAST";
case OP_SPLIT:
return "SPLIT";
case OP_JOIN:
return "JOIN";
case OP_SUBSTR:
return "SUBSTR";
case OP_FIND:
return "FIND";
case OP_REGEX_MATCH:
return "REGEX_MATCH";
case OP_REGEX_SEARCH:
return "REGEX_SEARCH";
case OP_REGEX_REPLACE:
return "REGEX_REPLACE";
case OP_CONTAINS:
return "CONTAINS";
case OP_INDEX_OF:
return "INDEX_OF";
case OP_CLEAR:
return "CLEAR";
case OP_ENUMERATE:
return "ENUMERATE";
case OP_ZIP:
return "ZIP";
case OP_MIN:
return "MIN";
case OP_MAX:
return "MAX";
case OP_CLAMP:
return "CLAMP";
case OP_ABS:
return "ABS";
case OP_POW:
return "POW";
case OP_RANDOM_SEED:
return "RANDOM_SEED";
case OP_RANDOM_INT:
return "RANDOM_INT";
case OP_MAKE_MAP:
return "MAKE_MAP";
case OP_KEYS:
return "KEYS";
case OP_VALUES:
return "VALUES";
case OP_HAS_KEY:
return "HAS_KEY";
case OP_READ_FILE:
return "READ_FILE";
case OP_WRITE_FILE:
return "WRITE_FILE";
case OP_ENV:
return "ENV";
case OP_INPUT_LINE:
return "INPUT_LINE";
case OP_PROC_RUN:
return "PROC_RUN";
case OP_PROC_SYSTEM:
return "PROC_SYSTEM";
case OP_TIME_NOW_MS:
return "TIME_NOW_MS";
case OP_CLOCK_MONO_MS:
return "CLOCK_MONO_MS";
case OP_DATE_FORMAT:
return "DATE_FORMAT";
case OP_ENV_ALL:
return "ENV_ALL";
case OP_FUN_VERSION:
return "FUN_VERSION";
case OP_THREAD_SPAWN:
return "THREAD_SPAWN";
case OP_THREAD_JOIN:
return "THREAD_JOIN";
case OP_SLEEP_MS:
return "SLEEP_MS";
case OP_RANDOM_NUMBER:
return "RANDOM_NUMBER";
case OP_BAND:
return "BAND";
case OP_BOR:
return "BOR";
case OP_BXOR:
return "BXOR";
case OP_BNOT:
return "BNOT";
case OP_SHL:
return "SHL";
case OP_SHR:
return "SHR";
case OP_ROTL:
return "ROTL";
case OP_ROTR:
return "ROTR";
case OP_JSON_PARSE:
return "JSON_PARSE";
case OP_JSON_STRINGIFY:
return "JSON_STRINGIFY";
case OP_JSON_FROM_FILE:
return "JSON_FROM_FILE";
case OP_JSON_TO_FILE:
return "JSON_TO_FILE";
case OP_CURL_GET:
return "CURL_GET";
case OP_CURL_POST:
return "CURL_POST";
case OP_CURL_DOWNLOAD:
return "CURL_DOWNLOAD";
case OP_SQLITE_OPEN:
return "SQLITE_OPEN";
case OP_SQLITE_CLOSE:
return "SQLITE_CLOSE";
case OP_SQLITE_EXEC:
return "SQLITE_EXEC";
case OP_SQLITE_QUERY:
return "SQLITE_QUERY";
case OP_LIBSQL_OPEN:
return "LIBSQL_OPEN";
case OP_LIBSQL_CLOSE:
return "LIBSQL_CLOSE";
case OP_LIBSQL_EXEC:
return "LIBSQL_EXEC";
case OP_LIBSQL_QUERY:
return "LIBSQL_QUERY";
case OP_PCSC_ESTABLISH:
return "PCSC_ESTABLISH";
case OP_PCSC_RELEASE:
return "PCSC_RELEASE";
case OP_PCSC_LIST_READERS:
return "PCSC_LIST_READERS";
case OP_PCSC_CONNECT:
return "PCSC_CONNECT";
case OP_PCSC_DISCONNECT:
return "PCSC_DISCONNECT";
case OP_PCSC_TRANSMIT:
return "PCSC_TRANSMIT";
case OP_PCRE2_TEST:
return "PCRE2_TEST";
case OP_PCRE2_MATCH:
return "PCRE2_MATCH";
case OP_PCRE2_FINDALL:
return "PCRE2_FINDALL";
case OP_OPENSSL_MD5:
return "OPENSSL_MD5";
case OP_OPENSSL_SHA256:
return "OPENSSL_SHA256";
case OP_OPENSSL_SHA512:
return "OPENSSL_SHA512";
case OP_OPENSSL_RIPEMD160:
return "OPENSSL_RIPEMD160";
case OP_LIBRESSL_MD5:
return "LIBRESSL_MD5";
case OP_LIBRESSL_SHA256:
return "LIBRESSL_SHA256";
case OP_LIBRESSL_SHA512:
return "LIBRESSL_SHA512";
case OP_LIBRESSL_RIPEMD160:
return "LIBRESSL_RIPEMD160";
case OP_INI_LOAD:
return "INI_LOAD";
case OP_INI_FREE:
return "INI_FREE";
case OP_INI_GET_STRING:
return "INI_GET_STRING";
case OP_INI_GET_INT:
return "INI_GET_INT";
case OP_INI_GET_DOUBLE:
return "INI_GET_DOUBLE";
case OP_INI_GET_BOOL:
return "INI_GET_BOOL";
case OP_INI_SET:
return "INI_SET";
case OP_INI_UNSET:
return "INI_UNSET";
case OP_INI_SAVE:
return "INI_SAVE";
case OP_XML_PARSE:
return "XML_PARSE";
case OP_XML_ROOT:
return "XML_ROOT";
case OP_XML_NAME:
return "XML_NAME";
case OP_XML_TEXT:
return "XML_TEXT";
case OP_TK_EVAL:
return "TK_EVAL";
case OP_TK_RESULT:
return "TK_RESULT";
case OP_TK_LOOP:
return "TK_LOOP";
case OP_TK_WM_TITLE:
return "TK_WM_TITLE";
case OP_TK_LABEL:
return "TK_LABEL";
case OP_TK_BUTTON:
return "TK_BUTTON";
case OP_TK_PACK:
return "TK_PACK";
case OP_FLOOR:
return "FLOOR";
case OP_CEIL:
return "CEIL";
case OP_TRUNC:
return "TRUNC";
case OP_ROUND:
return "ROUND";
case OP_SIN:
return "SIN";
case OP_COS:
return "COS";
case OP_TAN:
return "TAN";
case OP_EXP:
return "EXP";
case OP_LOG:
return "LOG";
case OP_LOG10:
return "LOG10";
case OP_SQRT:
return "SQRT";
case OP_GCD:
return "GCD";
case OP_LCM:
return "LCM";
case OP_ISQRT:
return "ISQRT";
case OP_SIGN:
return "SIGN";
case OP_FMIN:
return "FMIN";
case OP_FMAX:
return "FMAX";
case OP_RUST_HELLO:
return "RUST_HELLO";
case OP_RUST_HELLO_ARGS:
return "RUST_HELLO_ARGS";
case OP_RUST_HELLO_ARGS_RETURN:
return "RUST_HELLO_ARGS_RETURN";
case OP_RUST_GET_SP:
return "RUST_GET_SP";
case OP_RUST_SET_EXIT:
return "RUST_SET_EXIT";
default:
return "???";
}
}
void bytecode_dump(const Bytecode *bc) {
if (!bc) {
printf("<null bytecode>\n");
return;
}
printf("Constants (%d):\n", bc->const_count);
for (int i = 0; i < bc->const_count; ++i) {
printf(" [%d] ", i);
print_value(&bc->constants[i]);
printf("\n");
}
printf("Instructions (%d):\n", bc->instr_count);
for (int i = 0; i < bc->instr_count; ++i) {
const Instruction *ins = &bc->instructions[i];
printf(" %3d: %-15s %d\n", i, opcode_name(ins->op), ins->operand);
}
if (!bc) {
printf("<null bytecode>\n");
return;
}
printf("Constants (%d):\n", bc->const_count);
for (int i = 0; i < bc->const_count; ++i) {
printf(" [%d] ", i);
print_value(&bc->constants[i]);
printf("\n");
}
printf("Instructions (%d):\n", bc->instr_count);
for (int i = 0; i < bc->instr_count; ++i) {
const Instruction *ins = &bc->instructions[i];
printf(" %3d: %-15s %d\n", i, opcode_name(ins->op), ins->operand);
}
}

View file

@ -10,304 +10,304 @@
#ifndef FUN_BYTECODE_H
#define FUN_BYTECODE_H
#include <stdint.h>
#include "value.h"
#include <stdint.h>
// VM opcodes
typedef enum {
OP_NOP,
OP_LOAD_CONST, // operand = constant index
OP_LOAD_LOCAL, // operand = local slot index
OP_STORE_LOCAL, // operand = local slot index
OP_NOP,
OP_LOAD_CONST, // operand = constant index
OP_LOAD_LOCAL, // operand = local slot index
OP_STORE_LOCAL, // operand = local slot index
OP_LOAD_GLOBAL, //
OP_STORE_GLOBAL, //
OP_LOAD_GLOBAL, //
OP_STORE_GLOBAL, //
OP_ADD, //
OP_SUB, //
OP_MUL, //
OP_DIV, //
OP_ADD, //
OP_SUB, //
OP_MUL, //
OP_DIV, //
OP_LT, // a < b -> push 1/0
OP_LTE, // a <= b -> push 1/0
OP_GT, // a > b -> push 1/0
OP_GTE, // a >= b -> push 1/0
OP_EQ, // a == b -> push 1/0
OP_NEQ, // a != b -> push 1/0
OP_LT, // a < b -> push 1/0
OP_LTE, // a <= b -> push 1/0
OP_GT, // a > b -> push 1/0
OP_GTE, // a >= b -> push 1/0
OP_EQ, // a == b -> push 1/0
OP_NEQ, // a != b -> push 1/0
OP_POP, // discard top of stack
OP_JUMP, // unconditional jump
OP_JUMP_IF_FALSE, // jump if top of stack is false (0)
OP_POP, // discard top of stack
OP_JUMP, // unconditional jump
OP_JUMP_IF_FALSE, // jump if top of stack is false (0)
OP_CALL, // operand = arg count; pops fn + args and enters fn
OP_RETURN, // pop optional return value and return to caller
OP_CALL, // operand = arg count; pops fn + args and enters fn
OP_RETURN, // pop optional return value and return to caller
OP_PRINT,
OP_ECHO, // like print but does not append a newline; prints immediately
OP_HALT,
OP_PRINT,
OP_ECHO, // like print but does not append a newline; prints immediately
OP_HALT,
OP_LINE, // operand = source line number (debug marker)
OP_LINE, // operand = source line number (debug marker)
// add after existing opcodes
OP_MOD, // a % b
OP_AND, // logical AND
OP_OR, // logical OR
OP_NOT, // logical NOT
// add after existing opcodes
OP_MOD, // a % b
OP_AND, // logical AND
OP_OR, // logical OR
OP_NOT, // logical NOT
OP_DUP, // duplicate top of stack
OP_SWAP, // swap top two stack values
OP_DUP, // duplicate top of stack
OP_SWAP, // swap top two stack values
// arrays
OP_MAKE_ARRAY, // operand = element count; pops N values, pushes array
OP_INDEX_GET, // pops index, array; pushes element copy
OP_INDEX_SET, // pops value, index, array; sets in place
// arrays
OP_MAKE_ARRAY, // operand = element count; pops N values, pushes array
OP_INDEX_GET, // pops index, array; pushes element copy
OP_INDEX_SET, // pops value, index, array; sets in place
// array and builtin helpers
OP_LEN, // pops array or string; pushes length
OP_PUSH, // pops value, array; pushes new length
OP_APOP, // pops array; pushes removed element
OP_SET, // pops value, index, array; pushes value
OP_INSERT, // pops value, index, array; pushes new length
OP_REMOVE, // pops index, array; pushes removed element
OP_SLICE, // pops end, start, array; pushes new array
// array and builtin helpers
OP_LEN, // pops array or string; pushes length
OP_PUSH, // pops value, array; pushes new length
OP_APOP, // pops array; pushes removed element
OP_SET, // pops value, index, array; pushes value
OP_INSERT, // pops value, index, array; pushes new length
OP_REMOVE, // pops index, array; pushes removed element
OP_SLICE, // pops end, start, array; pushes new array
// conversions
OP_TO_NUMBER, // pops any; pushes int (parse strings)
OP_TO_STRING, // pops any; pushes string
OP_CAST, // pops typeName, value; pushes casted value (see vm/cast.c)
OP_TYPEOF, // pops any; pushes string name of type
OP_UCLAMP, // pops number; pushes number masked to N bits (operand = bits)
OP_SCLAMP, // pops number; pushes number clamped to signed N-bit range (operand = bits)
// conversions
OP_TO_NUMBER, // pops any; pushes int (parse strings)
OP_TO_STRING, // pops any; pushes string
OP_CAST, // pops typeName, value; pushes casted value (see vm/cast.c)
OP_TYPEOF, // pops any; pushes string name of type
OP_UCLAMP, // pops number; pushes number masked to N bits (operand = bits)
OP_SCLAMP, // pops number; pushes number clamped to signed N-bit range (operand = bits)
// string ops
OP_SPLIT, // pops sep, string; pushes array of strings
OP_JOIN, // pops sep, array; pushes string
OP_SUBSTR, // pops len, start, string; pushes string
OP_FIND, // pops needle, haystack; pushes int index or -1
// string ops
OP_SPLIT, // pops sep, string; pushes array of strings
OP_JOIN, // pops sep, array; pushes string
OP_SUBSTR, // pops len, start, string; pushes string
OP_FIND, // pops needle, haystack; pushes int index or -1
// regex ops (POSIX)
OP_REGEX_MATCH, // pops pattern, string; pushes 1/0 for full match
OP_REGEX_SEARCH, // pops pattern, string; pushes map {"match":str, "start":int, "end":int, "groups":array}
OP_REGEX_REPLACE, // pops repl, pattern, string; pushes string with global replacements
// regex ops (POSIX)
OP_REGEX_MATCH, // pops pattern, string; pushes 1/0 for full match
OP_REGEX_SEARCH, // pops pattern, string; pushes map {"match":str, "start":int, "end":int, "groups":array}
OP_REGEX_REPLACE, // pops repl, pattern, string; pushes string with global replacements
// array utils
OP_CONTAINS, // pops value, array; pushes 1/0
OP_INDEX_OF, // pops value, array; pushes index or -1
OP_CLEAR, // pops array; clears it; pushes nothing (we'll push 0)
// array utils
OP_CONTAINS, // pops value, array; pushes 1/0
OP_INDEX_OF, // pops value, array; pushes index or -1
OP_CLEAR, // pops array; clears it; pushes nothing (we'll push 0)
// iteration helpers
OP_ENUMERATE, // pops array; pushes array of [index, value]
OP_ZIP, // pops b, a; pushes array of [a[i], b[i]]
// iteration helpers
OP_ENUMERATE, // pops array; pushes array of [index, value]
OP_ZIP, // pops b, a; pushes array of [a[i], b[i]]
// math
OP_MIN, // pops b, a; pushes min(a,b)
OP_MAX, // pops b, a; pushes max(a,b)
OP_CLAMP, // pops hi, lo, x; pushes clamped
OP_ABS, // pops x; pushes |x|
OP_POW, // pops b, a; pushes a^b
OP_RANDOM_SEED, // pops seed; sets RNG seed; pushes nothing (we'll push 0)
OP_RANDOM_INT, // pops hi, lo; pushes random int in [lo, hi)
// math
OP_MIN, // pops b, a; pushes min(a,b)
OP_MAX, // pops b, a; pushes max(a,b)
OP_CLAMP, // pops hi, lo, x; pushes clamped
OP_ABS, // pops x; pushes |x|
OP_POW, // pops b, a; pushes a^b
OP_RANDOM_SEED, // pops seed; sets RNG seed; pushes nothing (we'll push 0)
OP_RANDOM_INT, // pops hi, lo; pushes random int in [lo, hi)
// maps
OP_MAKE_MAP, // operand = pair count; pops 2*n (key,value)..., pushes map
OP_KEYS, // pops map; pushes array of keys
OP_VALUES, // pops map; pushes array of values
OP_HAS_KEY, // pops key, map; pushes 1/0
// maps
OP_MAKE_MAP, // operand = pair count; pops 2*n (key,value)..., pushes map
OP_KEYS, // pops map; pushes array of keys
OP_VALUES, // pops map; pushes array of values
OP_HAS_KEY, // pops key, map; pushes 1/0
// file I/O
OP_READ_FILE, // pops path string; pushes content string (or "")
OP_WRITE_FILE, // pops data string, path string; pushes 1/0
// file I/O
OP_READ_FILE, // pops path string; pushes content string (or "")
OP_WRITE_FILE, // pops data string, path string; pushes 1/0
// OS
OP_ENV, // pops name string; pushes value string (or "")
OP_INPUT_LINE, // operand: 0=no prompt; 1=has prompt. Pops [prompt?]; pushes input string (no trailing newline)
OP_PROC_RUN, // pops command string; pushes map {"out": string, "code": int}
OP_PROC_SYSTEM, // pops command string; pushes exit code number
OP_TIME_NOW_MS, // pushes current wall-clock time in milliseconds since Unix epoch
OP_CLOCK_MONO_MS, // pushes monotonic clock in milliseconds (not wall time)
OP_DATE_FORMAT, // pops fmt string, ms epoch (int); pushes formatted date string using strftime
OP_ENV_ALL, // pushes map of all environment variables
OP_FUN_VERSION, // pushes version string
// OS
OP_ENV, // pops name string; pushes value string (or "")
OP_INPUT_LINE, // operand: 0=no prompt; 1=has prompt. Pops [prompt?]; pushes input string (no trailing newline)
OP_PROC_RUN, // pops command string; pushes map {"out": string, "code": int}
OP_PROC_SYSTEM, // pops command string; pushes exit code number
OP_TIME_NOW_MS, // pushes current wall-clock time in milliseconds since Unix epoch
OP_CLOCK_MONO_MS, // pushes monotonic clock in milliseconds (not wall time)
OP_DATE_FORMAT, // pops fmt string, ms epoch (int); pushes formatted date string using strftime
OP_ENV_ALL, // pushes map of all environment variables
OP_FUN_VERSION, // pushes version string
// Threads
OP_THREAD_SPAWN, // operand: 0=no args, 1=has args; pops [args?], fn; pushes thread id (int>0)
OP_THREAD_JOIN, // pops thread id; waits; pushes result value (or Nil)
OP_SLEEP_MS, // pops milliseconds; sleeps; pushes Nil (for statement POP safety)
OP_RANDOM_NUMBER, // pops length; pushes hex string of that length from OS RNG (hex-encoded)
// Threads
OP_THREAD_SPAWN, // operand: 0=no args, 1=has args; pops [args?], fn; pushes thread id (int>0)
OP_THREAD_JOIN, // pops thread id; waits; pushes result value (or Nil)
OP_SLEEP_MS, // pops milliseconds; sleeps; pushes Nil (for statement POP safety)
OP_RANDOM_NUMBER, // pops length; pushes hex string of that length from OS RNG (hex-encoded)
// Bitwise (32-bit) and shifts/rotates
OP_BAND, // pops b, a; pushes (uint32_t)(a & b)
OP_BOR, // pops b, a; pushes (uint32_t)(a | b)
OP_BXOR, // pops b, a; pushes (uint32_t)(a ^ b)
OP_BNOT, // pops a; pushes (uint32_t)(~a)
OP_SHL, // pops s, a; pushes (uint32_t)(a << (s&31))
OP_SHR, // pops s, a; pushes (uint32_t)(a >> (s&31)) logical
OP_ROTL, // pops s, a; pushes rotl32(a, s)
OP_ROTR, // pops s, a; pushes rotr32(a, s)
// Bitwise (32-bit) and shifts/rotates
OP_BAND, // pops b, a; pushes (uint32_t)(a & b)
OP_BOR, // pops b, a; pushes (uint32_t)(a | b)
OP_BXOR, // pops b, a; pushes (uint32_t)(a ^ b)
OP_BNOT, // pops a; pushes (uint32_t)(~a)
OP_SHL, // pops s, a; pushes (uint32_t)(a << (s&31))
OP_SHR, // pops s, a; pushes (uint32_t)(a >> (s&31)) logical
OP_ROTL, // pops s, a; pushes rotl32(a, s)
OP_ROTR, // pops s, a; pushes rotr32(a, s)
// JSON (json-c)
OP_JSON_PARSE, // pops text string; pushes value (or Nil on error)
OP_JSON_STRINGIFY, // pops pretty(bool), value; pushes string
OP_JSON_FROM_FILE, // pops path string; pushes value (or Nil)
OP_JSON_TO_FILE, // pops pretty(bool), value, path; pushes 1/0
// JSON (json-c)
OP_JSON_PARSE, // pops text string; pushes value (or Nil on error)
OP_JSON_STRINGIFY, // pops pretty(bool), value; pushes string
OP_JSON_FROM_FILE, // pops path string; pushes value (or Nil)
OP_JSON_TO_FILE, // pops pretty(bool), value, path; pushes 1/0
// CURL (libcurl)
OP_CURL_GET, // pops [headers map?], url; pushes response string (or "")
OP_CURL_POST, // pops [headers map?], body string, url; pushes response string (or "")
OP_CURL_DOWNLOAD, // pops [headers map?], path, url; pushes 1/0
// CURL (libcurl)
OP_CURL_GET, // pops [headers map?], url; pushes response string (or "")
OP_CURL_POST, // pops [headers map?], body string, url; pushes response string (or "")
OP_CURL_DOWNLOAD, // pops [headers map?], path, url; pushes 1/0
// SQLite (optional)
OP_SQLITE_OPEN, // pops path; pushes handle (>0) or 0
OP_SQLITE_CLOSE, // pops handle; pushes Nil
OP_SQLITE_EXEC, // pops sql, handle; pushes sqlite rc (0=OK)
OP_SQLITE_QUERY, // pops sql, handle; pushes array<map>
// SQLite (optional)
OP_SQLITE_OPEN, // pops path; pushes handle (>0) or 0
OP_SQLITE_CLOSE, // pops handle; pushes Nil
OP_SQLITE_EXEC, // pops sql, handle; pushes sqlite rc (0=OK)
OP_SQLITE_QUERY, // pops sql, handle; pushes array<map>
// libsql (optional, independent)
OP_LIBSQL_OPEN, // pops url/path; pushes handle (>0) or 0
OP_LIBSQL_CLOSE, // pops handle; pushes Nil
OP_LIBSQL_EXEC, // pops sql, handle; pushes rc (0=OK)
OP_LIBSQL_QUERY, // pops sql, handle; pushes array<map>
// libsql (optional, independent)
OP_LIBSQL_OPEN, // pops url/path; pushes handle (>0) or 0
OP_LIBSQL_CLOSE, // pops handle; pushes Nil
OP_LIBSQL_EXEC, // pops sql, handle; pushes rc (0=OK)
OP_LIBSQL_QUERY, // pops sql, handle; pushes array<map>
// PCSC (smart card) opcodes
OP_PCSC_ESTABLISH, // returns context id (>0) or 0
OP_PCSC_RELEASE, // pops ctx id; returns 1/0
OP_PCSC_LIST_READERS, // pops ctx id; returns array of reader names (possibly empty)
OP_PCSC_CONNECT, // pops reader, ctx id; returns handle id (>0) or 0
OP_PCSC_DISCONNECT, // pops handle id; returns 1/0
OP_PCSC_TRANSMIT, // pops apdu array, handle id; returns map {"data":[],"sw1":n,"sw2":n,"code":n}
// PCSC (smart card) opcodes
OP_PCSC_ESTABLISH, // returns context id (>0) or 0
OP_PCSC_RELEASE, // pops ctx id; returns 1/0
OP_PCSC_LIST_READERS, // pops ctx id; returns array of reader names (possibly empty)
OP_PCSC_CONNECT, // pops reader, ctx id; returns handle id (>0) or 0
OP_PCSC_DISCONNECT, // pops handle id; returns 1/0
OP_PCSC_TRANSMIT, // pops apdu array, handle id; returns map {"data":[],"sw1":n,"sw2":n,"code":n}
// PCRE2 regex ops (optional)
OP_PCRE2_TEST, // pops flags, text, pattern; pushes 1/0
OP_PCRE2_MATCH, // pops flags, text, pattern; pushes match map or Nil
OP_PCRE2_FINDALL, // pops flags, text, pattern; pushes array of match maps
// PCRE2 regex ops (optional)
OP_PCRE2_TEST, // pops flags, text, pattern; pushes 1/0
OP_PCRE2_MATCH, // pops flags, text, pattern; pushes match map or Nil
OP_PCRE2_FINDALL, // pops flags, text, pattern; pushes array of match maps
// OpenSSL (optional)
OP_OPENSSL_MD5, // pops data string; pushes md5 hex string
OP_OPENSSL_SHA256, // pops data string; pushes sha256 hex string
OP_OPENSSL_SHA512, // pops data string; pushes sha512 hex string
OP_OPENSSL_RIPEMD160, // pops data string; pushes ripemd160 hex string
// OpenSSL (optional)
OP_OPENSSL_MD5, // pops data string; pushes md5 hex string
OP_OPENSSL_SHA256, // pops data string; pushes sha256 hex string
OP_OPENSSL_SHA512, // pops data string; pushes sha512 hex string
OP_OPENSSL_RIPEMD160, // pops data string; pushes ripemd160 hex string
// LibreSSL (optional; same API as OpenSSL but different toggle)
OP_LIBRESSL_MD5, // pops data string; pushes md5 hex string
OP_LIBRESSL_SHA256, // pops data string; pushes sha256 hex string
OP_LIBRESSL_SHA512, // pops data string; pushes sha512 hex string
OP_LIBRESSL_RIPEMD160, // pops data string; pushes ripemd160 hex string
// LibreSSL (optional; same API as OpenSSL but different toggle)
OP_LIBRESSL_MD5, // pops data string; pushes md5 hex string
OP_LIBRESSL_SHA256, // pops data string; pushes sha256 hex string
OP_LIBRESSL_SHA512, // pops data string; pushes sha512 hex string
OP_LIBRESSL_RIPEMD160, // pops data string; pushes ripemd160 hex string
// INI (iniparser 4.2.6) optional
OP_INI_LOAD, // pops path; pushes handle (>0) or 0
OP_INI_FREE, // pops handle; pushes 1/0
OP_INI_GET_STRING, // pops def, key, section, handle; pushes string
OP_INI_GET_INT, // pops def, key, section, handle; pushes int
OP_INI_GET_DOUBLE, // pops def, key, section, handle; pushes float
OP_INI_GET_BOOL, // pops def, key, section, handle; pushes int (0/1)
OP_INI_SET, // pops value, key, section, handle; pushes 1/0
OP_INI_UNSET, // pops key, section, handle; pushes 1/0
OP_INI_SAVE, // pops path, handle; pushes 1/0
// INI (iniparser 4.2.6) optional
OP_INI_LOAD, // pops path; pushes handle (>0) or 0
OP_INI_FREE, // pops handle; pushes 1/0
OP_INI_GET_STRING, // pops def, key, section, handle; pushes string
OP_INI_GET_INT, // pops def, key, section, handle; pushes int
OP_INI_GET_DOUBLE, // pops def, key, section, handle; pushes float
OP_INI_GET_BOOL, // pops def, key, section, handle; pushes int (0/1)
OP_INI_SET, // pops value, key, section, handle; pushes 1/0
OP_INI_UNSET, // pops key, section, handle; pushes 1/0
OP_INI_SAVE, // pops path, handle; pushes 1/0
// XML (libxml2) optional minimal API
OP_XML_PARSE, // pops text string; pushes doc handle (>0) or 0
OP_XML_ROOT, // pops doc handle; pushes node handle (>0) or 0
OP_XML_NAME, // pops node handle; pushes string (node name)
OP_XML_TEXT, // pops node handle; pushes string (node text)
// XML (libxml2) optional minimal API
OP_XML_PARSE, // pops text string; pushes doc handle (>0) or 0
OP_XML_ROOT, // pops doc handle; pushes node handle (>0) or 0
OP_XML_NAME, // pops node handle; pushes string (node name)
OP_XML_TEXT, // pops node handle; pushes string (node text)
// Sockets (UNIX platforms)
OP_SOCK_TCP_LISTEN, // pops backlog, port; returns listen fd (>0) or 0
OP_SOCK_TCP_ACCEPT, // pops listen fd; returns client fd (>0) or 0
OP_SOCK_TCP_CONNECT, // pops port, host; returns fd (>0) or 0
OP_SOCK_SEND, // pops data string, fd; returns bytes sent (>=0) or -1
OP_SOCK_RECV, // pops maxlen, fd; returns data string ("" on EOF/error)
OP_SOCK_CLOSE, // pops fd; returns 1/0
OP_SOCK_UNIX_LISTEN, // pops backlog, path; returns listen fd (>0) or 0
OP_SOCK_UNIX_CONNECT, // pops path; returns fd (>0) or 0
// Sockets (UNIX platforms)
OP_SOCK_TCP_LISTEN, // pops backlog, port; returns listen fd (>0) or 0
OP_SOCK_TCP_ACCEPT, // pops listen fd; returns client fd (>0) or 0
OP_SOCK_TCP_CONNECT, // pops port, host; returns fd (>0) or 0
OP_SOCK_SEND, // pops data string, fd; returns bytes sent (>=0) or -1
OP_SOCK_RECV, // pops maxlen, fd; returns data string ("" on EOF/error)
OP_SOCK_CLOSE, // pops fd; returns 1/0
OP_SOCK_UNIX_LISTEN, // pops backlog, path; returns listen fd (>0) or 0
OP_SOCK_UNIX_CONNECT, // pops path; returns fd (>0) or 0
// process control
OP_EXIT, // pops code (or uses operand) and terminates script with exit code
// process control
OP_EXIT, // pops code (or uses operand) and terminates script with exit code
// OS additions
OP_OS_LIST_DIR, // pops path string; pushes array of strings
// OS additions
OP_OS_LIST_DIR, // pops path string; pushes array of strings
// Tk additions
OP_TK_BIND, // pops command, event, id; binds event to command
// Tk additions
OP_TK_BIND, // pops command, event, id; binds event to command
// Serial communication (termios)
OP_SERIAL_OPEN, // pops baud_rate (int), path (string); returns fd (int) or 0
OP_SERIAL_CONFIG, // pops flow_control, stop_bits, parity, data_bits, fd; returns 1/0
OP_SERIAL_SEND, // pops data (string), fd; returns bytes sent (int)
OP_SERIAL_RECV, // pops maxlen (int), fd; returns data (string)
OP_SERIAL_CLOSE, // pops fd; returns 1/0
// Serial communication (termios)
OP_SERIAL_OPEN, // pops baud_rate (int), path (string); returns fd (int) or 0
OP_SERIAL_CONFIG, // pops flow_control, stop_bits, parity, data_bits, fd; returns 1/0
OP_SERIAL_SEND, // pops data (string), fd; returns bytes sent (int)
OP_SERIAL_RECV, // pops maxlen (int), fd; returns data (string)
OP_SERIAL_CLOSE, // pops fd; returns 1/0
// Tk (Tcl/Tk) optional minimal API
OP_TK_EVAL, // pops script string; pushes int rc (0 = OK)
OP_TK_RESULT, // pushes string: last Tcl result
OP_TK_LOOP, // enters Tk event loop; pushes Nil when done
OP_TK_WM_TITLE, // pops title string; sets window title; pushes rc
OP_TK_LABEL, // pops text, id; creates/updates label .id; pushes rc
OP_TK_BUTTON, // pops text, id; creates/updates button .id; pushes rc
OP_TK_PACK, // pops id; packs .id; pushes rc
// Tk (Tcl/Tk) optional minimal API
OP_TK_EVAL, // pops script string; pushes int rc (0 = OK)
OP_TK_RESULT, // pushes string: last Tcl result
OP_TK_LOOP, // enters Tk event loop; pushes Nil when done
OP_TK_WM_TITLE, // pops title string; sets window title; pushes rc
OP_TK_LABEL, // pops text, id; creates/updates label .id; pushes rc
OP_TK_BUTTON, // pops text, id; creates/updates button .id; pushes rc
OP_TK_PACK, // pops id; packs .id; pushes rc
// exceptions (minimal)
OP_TRY_PUSH, // operand = handler ip; push handler onto try-stack
OP_TRY_POP, // pop current handler
OP_THROW, // pops error value; if handler -> jump to it (push err), else print and terminate
// exceptions (minimal)
OP_TRY_PUSH, // operand = handler ip; push handler onto try-stack
OP_TRY_POP, // pop current handler
OP_THROW, // pops error value; if handler -> jump to it (push err), else print and terminate
// C99 math.h rounding family (float-aware)
OP_FLOOR, // pops x (int/float); pushes floor(x) (int if integral else float)
OP_CEIL, // pops x (int/float); pushes ceil(x) (int if integral else float)
OP_TRUNC, // pops x (int/float); pushes trunc(x) (int if integral else float)
OP_ROUND, // pops x (int/float); pushes round(x) (half away from zero)
// C99 math.h rounding family (float-aware)
OP_FLOOR, // pops x (int/float); pushes floor(x) (int if integral else float)
OP_CEIL, // pops x (int/float); pushes ceil(x) (int if integral else float)
OP_TRUNC, // pops x (int/float); pushes trunc(x) (int if integral else float)
OP_ROUND, // pops x (int/float); pushes round(x) (half away from zero)
// C99 math.h transcendentals (float-aware)
OP_SIN, // pops x (int/float); pushes sin(x)
OP_COS, // pops x (int/float); pushes cos(x)
OP_TAN, // pops x (int/float); pushes tan(x)
OP_EXP, // pops x (int/float); pushes exp(x)
OP_LOG, // pops x (int/float); pushes natural log ln(x)
OP_LOG10, // pops x (int/float); pushes log10(x)
OP_SQRT, // pops x (int/float); pushes sqrt(x)
// C99 math.h transcendentals (float-aware)
OP_SIN, // pops x (int/float); pushes sin(x)
OP_COS, // pops x (int/float); pushes cos(x)
OP_TAN, // pops x (int/float); pushes tan(x)
OP_EXP, // pops x (int/float); pushes exp(x)
OP_LOG, // pops x (int/float); pushes natural log ln(x)
OP_LOG10, // pops x (int/float); pushes log10(x)
OP_SQRT, // pops x (int/float); pushes sqrt(x)
// Integer math helpers
OP_GCD, // pops b, a; pushes gcd(|a|,|b|)
OP_LCM, // pops b, a; pushes lcm(|a|,|b|) (0 if either is 0)
OP_ISQRT, // pops x; pushes floor(sqrt(max(0,x))) for integers
OP_SIGN, // pops x; pushes -1, 0, or 1 depending on the sign
// Integer math helpers
OP_GCD, // pops b, a; pushes gcd(|a|,|b|)
OP_LCM, // pops b, a; pushes lcm(|a|,|b|) (0 if either is 0)
OP_ISQRT, // pops x; pushes floor(sqrt(max(0,x))) for integers
OP_SIGN, // pops x; pushes -1, 0, or 1 depending on the sign
// Min/Max variants (float-aware, C99 semantics)
OP_FMIN, // pops b, a (int/float); pushes fmin(a,b) (NaN handling per C99)
OP_FMAX, // pops b, a (int/float); pushes fmax(a,b) (NaN handling per C99)
// Min/Max variants (float-aware, C99 semantics)
OP_FMIN, // pops b, a (int/float); pushes fmin(a,b) (NaN handling per C99)
OP_FMAX, // pops b, a (int/float); pushes fmax(a,b) (NaN handling per C99)
// Rust FFI demo opcode(s)
OP_RUST_HELLO, // pushes string returned from Rust (hello world)
OP_RUST_HELLO_ARGS, // pops message string; prints it via Rust; pushes Nil
OP_RUST_HELLO_ARGS_RETURN, // pops message string; returns it from Rust without printing; pushes returned string
OP_RUST_GET_SP, // pushes current VM stack pointer (via Rust reading VM memory)
OP_RUST_SET_EXIT, // pops int and sets VM exit_code (via Rust writing VM memory)
// Rust FFI demo opcode(s)
OP_RUST_HELLO, // pushes string returned from Rust (hello world)
OP_RUST_HELLO_ARGS, // pops message string; prints it via Rust; pushes Nil
OP_RUST_HELLO_ARGS_RETURN, // pops message string; returns it from Rust without printing; pushes returned string
OP_RUST_GET_SP, // pushes current VM stack pointer (via Rust reading VM memory)
OP_RUST_SET_EXIT, // pops int and sets VM exit_code (via Rust writing VM memory)
// C++ demo opcode(s)
OP_CPP_ADD, // pops b, a; pushes (a + b)
// C++ demo opcode(s)
OP_CPP_ADD, // pops b, a; pushes (a + b)
/* Notcurses TUI (optional) */
OP_NC_INIT, // initializes Notcurses; returns 1 on success, 0 on failure
OP_NC_SHUTDOWN, // shuts down Notcurses; returns 0
OP_NC_CLEAR, // clears screen/plane; returns 0
OP_NC_DRAW_TEXT, // pops text, x, y; draws; returns 0
OP_NC_GETCH // pops timeout_ms; returns codepoint or -1 on timeout/error
/* Notcurses TUI (optional) */
OP_NC_INIT, // initializes Notcurses; returns 1 on success, 0 on failure
OP_NC_SHUTDOWN, // shuts down Notcurses; returns 0
OP_NC_CLEAR, // clears screen/plane; returns 0
OP_NC_DRAW_TEXT, // pops text, x, y; draws; returns 0
OP_NC_GETCH // pops timeout_ms; returns codepoint or -1 on timeout/error
} OpCode;
typedef struct {
OpCode op;
int32_t operand;
OpCode op;
int32_t operand;
} Instruction;
typedef struct Bytecode {
Instruction *instructions;
int instr_count;
Instruction *instructions;
int instr_count;
Value *constants;
int const_count;
Value *constants;
int const_count;
/* debug metadata */
const char *name; /* function or module name (optional) */
const char *source_file; /* originating source filename (optional) */
/* debug metadata */
const char *name; /* function or module name (optional) */
const char *source_file; /* originating source filename (optional) */
} Bytecode;
// constructors / manipulation

15
src/external/curl.c vendored
View file

@ -12,18 +12,23 @@
/* Ensure libcurl headers and helpers are defined at file scope (not inside vm_run) */
#ifdef FUN_WITH_CURL
#include <curl/curl.h>
typedef struct { char *d; size_t n; } FunCurlBuf;
typedef struct {
char *d;
size_t n;
} FunCurlBuf;
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;
char *p = (char*)realloc(b->d, b->n + add + 1);
FunCurlBuf *b = (FunCurlBuf *)ud;
char *p = (char *)realloc(b->d, b->n + add + 1);
if (!p) return 0;
memcpy(p + b->n, ptr, add);
b->d = p; b->n += add; b->d[b->n] = '\0';
b->d = p;
b->n += add;
b->d[b->n] = '\0';
return add;
}
static size_t fun_curl_file_write_cb(void *ptr, size_t sz, size_t nm, void *ud) {
FILE *f = (FILE*)ud;
FILE *f = (FILE *)ud;
return fwrite(ptr, sz, nm, f);
}
#endif

22
src/external/ini.c vendored
View file

@ -11,18 +11,18 @@
#ifdef FUN_WITH_INI
#if defined(__has_include)
# if __has_include(<iniparser/iniparser.h>)
# include <iniparser/iniparser.h>
# include <iniparser/dictionary.h>
# elif __has_include(<iniparser.h>)
# include <iniparser.h>
# include <dictionary.h>
# else
# error "iniparser headers not found"
# endif
#if __has_include(<iniparser/iniparser.h>)
#include <iniparser/dictionary.h>
#include <iniparser/iniparser.h>
#elif __has_include(<iniparser.h>)
#include <dictionary.h>
#include <iniparser.h>
#else
# include <iniparser/iniparser.h>
# include <iniparser/dictionary.h>
#error "iniparser headers not found"
#endif
#else
#include <iniparser/dictionary.h>
#include <iniparser/iniparser.h>
#endif
#include "vm/ini/handles.h"
#endif

171
src/external/json.c vendored
View file

@ -9,7 +9,7 @@
* Added: 2025-12-11 (2025-12-11 migrated from src/vm.c)
*/
/* json-c helpers and VM opcode cases (included from vm.c) */
/* json-c helpers and VM opcode cases (included from vm.c) */
#ifdef FUN_WITH_JSON
#include "value.h"
@ -20,88 +20,99 @@
/* --- Conversion helpers between json-c and Fun Value --- */
static Value json_to_fun(json_object *j) {
if (!j) return make_nil();
enum json_type t = json_object_get_type(j);
switch (t) {
case json_type_null: return make_nil();
case json_type_boolean: return make_bool(json_object_get_boolean(j));
case json_type_double: return make_float(json_object_get_double(j));
case json_type_int: return make_int((int64_t)json_object_get_int64(j));
case json_type_string: return make_string(json_object_get_string(j));
case json_type_array: {
size_t n = json_object_array_length(j);
if (n == 0) {
return make_array_from_values(NULL, 0);
}
Value *vals = (Value*)malloc(sizeof(Value) * n);
if (!vals) return make_array_from_values(NULL, 0);
for (size_t i = 0; i < n; ++i) {
json_object *item = json_object_array_get_idx(j, (int)i);
vals[i] = json_to_fun(item);
}
Value arr = make_array_from_values(vals, (int)n);
for (size_t i = 0; i < n; ++i) free_value(vals[i]);
free(vals);
return arr;
}
case json_type_object: {
Value map = make_map_empty();
json_object_object_foreach(j, key, val) {
(void)map_set(&map, key, json_to_fun(val));
}
return map;
}
default:
return make_nil();
if (!j) return make_nil();
enum json_type t = json_object_get_type(j);
switch (t) {
case json_type_null:
return make_nil();
case json_type_boolean:
return make_bool(json_object_get_boolean(j));
case json_type_double:
return make_float(json_object_get_double(j));
case json_type_int:
return make_int((int64_t)json_object_get_int64(j));
case json_type_string:
return make_string(json_object_get_string(j));
case json_type_array: {
size_t n = json_object_array_length(j);
if (n == 0) {
return make_array_from_values(NULL, 0);
}
Value *vals = (Value *)malloc(sizeof(Value) * n);
if (!vals) return make_array_from_values(NULL, 0);
for (size_t i = 0; i < n; ++i) {
json_object *item = json_object_array_get_idx(j, (int)i);
vals[i] = json_to_fun(item);
}
Value arr = make_array_from_values(vals, (int)n);
for (size_t i = 0; i < n; ++i)
free_value(vals[i]);
free(vals);
return arr;
}
case json_type_object: {
Value map = make_map_empty();
json_object_object_foreach(j, key, val) {
(void)map_set(&map, key, json_to_fun(val));
}
return map;
}
default:
return make_nil();
}
}
static json_object* fun_to_json(const Value *v) {
switch (v->type) {
case VAL_NIL: return json_object_new_null();
case VAL_BOOL: return json_object_new_boolean(v->i ? 1 : 0);
case VAL_INT: return json_object_new_int64(v->i);
case VAL_FLOAT: return json_object_new_double(v->d);
case VAL_STRING: return json_object_new_string(v->s ? v->s : "");
case VAL_ARRAY: {
json_object *arr = json_object_new_array();
int n = array_length(v);
for (int i = 0; i < n; ++i) {
Value item;
if (array_get_copy(v, i, &item)) {
json_object_array_add(arr, fun_to_json(&item));
free_value(item);
} else {
json_object_array_add(arr, json_object_new_null());
}
}
return arr;
}
case VAL_MAP: {
json_object *obj = json_object_new_object();
/* We don't have an iterator API; use keys() helper */
Value keys = map_keys_array(v);
int kn = array_length(&keys);
for (int i = 0; i < kn; ++i) {
Value k;
if (!array_get_copy(&keys, i, &k)) continue;
if (k.type == VAL_STRING && k.s) {
Value val;
if (map_get_copy(v, k.s, &val)) {
json_object_object_add(obj, k.s, fun_to_json(&val));
free_value(val);
} else {
json_object_object_add(obj, k.s, json_object_new_null());
}
}
free_value(k);
}
free_value(keys);
return obj;
}
default:
/* Fallback: stringify unsupported types */
return json_object_new_string("<unsupported>");
static json_object *fun_to_json(const Value *v) {
switch (v->type) {
case VAL_NIL:
return json_object_new_null();
case VAL_BOOL:
return json_object_new_boolean(v->i ? 1 : 0);
case VAL_INT:
return json_object_new_int64(v->i);
case VAL_FLOAT:
return json_object_new_double(v->d);
case VAL_STRING:
return json_object_new_string(v->s ? v->s : "");
case VAL_ARRAY: {
json_object *arr = json_object_new_array();
int n = array_length(v);
for (int i = 0; i < n; ++i) {
Value item;
if (array_get_copy(v, i, &item)) {
json_object_array_add(arr, fun_to_json(&item));
free_value(item);
} else {
json_object_array_add(arr, json_object_new_null());
}
}
return arr;
}
case VAL_MAP: {
json_object *obj = json_object_new_object();
/* We don't have an iterator API; use keys() helper */
Value keys = map_keys_array(v);
int kn = array_length(&keys);
for (int i = 0; i < kn; ++i) {
Value k;
if (!array_get_copy(&keys, i, &k)) continue;
if (k.type == VAL_STRING && k.s) {
Value val;
if (map_get_copy(v, k.s, &val)) {
json_object_object_add(obj, k.s, fun_to_json(&val));
free_value(val);
} else {
json_object_object_add(obj, k.s, json_object_new_null());
}
}
free_value(k);
}
free_value(keys);
return obj;
}
default:
/* Fallback: stringify unsupported types */
return json_object_new_string("<unsupported>");
}
}
#endif

View file

@ -1,5 +1,5 @@
/*
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
@ -33,140 +33,164 @@ int EVP_MD_get_size(const EVP_MD *md);
/* Compute MD5 hex of input buffer; returns malloc'ed C string (lowercase hex). */
static char *fun_libressl_md5_hex(const unsigned char *data, size_t len) {
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
#ifdef FUN_WITH_LIBRESSL
const EVP_MD *md = EVP_md5();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; }
for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
const EVP_MD *md = EVP_md5();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) {
free(digest);
return hex;
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) {
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
free(digest);
return hex;
#else
char *hex = (char*)malloc(1);
if (hex) hex[0] = '\0';
return hex;
char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0';
return hex;
#endif
}
/* Compute SHA-256 hex of input buffer; returns malloc'ed C string (lowercase hex). */
static char *fun_libressl_sha256_hex(const unsigned char *data, size_t len) {
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
#ifdef FUN_WITH_LIBRESSL
const EVP_MD *md = EVP_sha256();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; }
for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
const EVP_MD *md = EVP_sha256();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) {
free(digest);
return hex;
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) {
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
free(digest);
return hex;
#else
char *hex = (char*)malloc(1);
if (hex) hex[0] = '\0';
return hex;
char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0';
return hex;
#endif
}
/* Compute SHA-512 hex of input buffer; returns malloc'ed C string (lowercase hex). */
static char *fun_libressl_sha512_hex(const unsigned char *data, size_t len) {
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
#ifdef FUN_WITH_LIBRESSL
const EVP_MD *md = EVP_sha512();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; }
for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
const EVP_MD *md = EVP_sha512();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) {
free(digest);
return hex;
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) {
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
free(digest);
return hex;
#else
char *hex = (char*)malloc(1);
if (hex) hex[0] = '\0';
return hex;
char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0';
return hex;
#endif
}
/* Compute RIPEMD-160 hex of input buffer; returns malloc'ed C string (lowercase hex). */
static char *fun_libressl_ripemd160_hex(const unsigned char *data, size_t len) {
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
#ifdef FUN_WITH_LIBRESSL
const EVP_MD *md = EVP_ripemd160();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; }
for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
const EVP_MD *md = EVP_ripemd160();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) {
free(digest);
return hex;
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) {
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
free(digest);
return hex;
#else
char *hex = (char*)malloc(1);
if (hex) hex[0] = '\0';
return hex;
char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0';
return hex;
#endif
}

51
src/external/libsql.c vendored
View file

@ -10,46 +10,49 @@
*/
#ifdef FUN_WITH_LIBSQL
#include <sqlite3.h> /* libsql provides a sqlite3-compatible C API */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sqlite3.h> /* libsql provides a sqlite3-compatible C API */
typedef struct LibSqlHandle {
int id;
sqlite3 *db;
struct LibSqlHandle *next;
int id;
sqlite3 *db;
struct LibSqlHandle *next;
} LibSqlHandle;
static LibSqlHandle *g_libsql_handles = NULL;
static int g_libsql_next_id = 1;
static LibSqlHandle *libsql_reg_add(sqlite3 *db) {
LibSqlHandle *h = (LibSqlHandle*)malloc(sizeof(LibSqlHandle));
if (!h) return NULL;
h->id = g_libsql_next_id++;
h->db = db;
h->next = g_libsql_handles;
g_libsql_handles = h;
return h;
LibSqlHandle *h = (LibSqlHandle *)malloc(sizeof(LibSqlHandle));
if (!h) return NULL;
h->id = g_libsql_next_id++;
h->db = db;
h->next = g_libsql_handles;
g_libsql_handles = h;
return h;
}
static LibSqlHandle *libsql_reg_get(int id) {
LibSqlHandle *p = g_libsql_handles;
while (p) { if (p->id == id) return p; p = p->next; }
return NULL;
LibSqlHandle *p = g_libsql_handles;
while (p) {
if (p->id == id) return p;
p = p->next;
}
return NULL;
}
static void libsql_reg_del(int id) {
LibSqlHandle **pp = &g_libsql_handles;
while (*pp) {
if ((*pp)->id == id) {
LibSqlHandle *dead = *pp;
*pp = (*pp)->next;
free(dead);
return;
}
pp = &((*pp)->next);
LibSqlHandle **pp = &g_libsql_handles;
while (*pp) {
if ((*pp)->id == id) {
LibSqlHandle *dead = *pp;
*pp = (*pp)->next;
free(dead);
return;
}
pp = &((*pp)->next);
}
}
#endif

246
src/external/openssl.c vendored
View file

@ -1,5 +1,5 @@
/*
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
@ -9,7 +9,7 @@
* Added: 2026-02-19
*/
/*
/*
* OpenSSL integration helpers (MD5, RIPEMD-160, SHA-256, SHA-512)
*/
@ -28,109 +28,127 @@ int EVP_MD_get_size(const EVP_MD *md);
* returns an allocated empty string ("") to keep behavior consistent with
* other optional extensions. */
static char *fun_openssl_md5_hex(const unsigned char *data, size_t len) {
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
#ifdef FUN_WITH_OPENSSL
const EVP_MD *md = EVP_md5();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
// EVP_Digest handles zero-length fine as well
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; }
for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
const EVP_MD *md = EVP_md5();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
// EVP_Digest handles zero-length fine as well
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) {
free(digest);
return hex;
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) {
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
free(digest);
return hex;
#else
char *hex = (char*)malloc(1);
if (hex) hex[0] = '\0';
return hex;
char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0';
return hex;
#endif
}
/* Compute SHA-256 hex of input buffer; returns malloc'ed C string (lowercase hex).
* Fallback when OpenSSL disabled: empty string. */
static char *fun_openssl_sha256_hex(const unsigned char *data, size_t len) {
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
#ifdef FUN_WITH_OPENSSL
const EVP_MD *md = EVP_sha256();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; }
for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
const EVP_MD *md = EVP_sha256();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) {
free(digest);
return hex;
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) {
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
free(digest);
return hex;
#else
char *hex = (char*)malloc(1);
if (hex) hex[0] = '\0';
return hex;
char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0';
return hex;
#endif
}
/* Compute SHA-512 hex of input buffer; returns malloc'ed C string (lowercase hex).
* Fallback when OpenSSL disabled: empty string. */
static char *fun_openssl_sha512_hex(const unsigned char *data, size_t len) {
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
#ifdef FUN_WITH_OPENSSL
const EVP_MD *md = EVP_sha512();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; }
for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
const EVP_MD *md = EVP_sha512();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) {
free(digest);
return hex;
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) {
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
free(digest);
return hex;
#else
char *hex = (char*)malloc(1);
if (hex) hex[0] = '\0';
return hex;
char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0';
return hex;
#endif
}
@ -139,35 +157,41 @@ static char *fun_openssl_sha512_hex(const unsigned char *data, size_t len) {
* 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. */
static char *fun_openssl_ripemd160_hex(const unsigned char *data, size_t len) {
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
static const char hexdig[] = "0123456789abcdef";
if (!data && len != 0) return NULL;
#ifdef FUN_WITH_OPENSSL
const EVP_MD *md = EVP_ripemd160();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; }
for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
const EVP_MD *md = EVP_ripemd160();
if (!md) return NULL;
int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL;
unsigned int out_len = 0;
int ok;
if (len == 0) {
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
} else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
}
if (ok != 1 || (int)out_len != dlen) {
free(digest);
return hex;
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) {
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
}
hex[dlen * 2] = '\0';
free(digest);
return hex;
#else
char *hex = (char*)malloc(1);
if (hex) hex[0] = '\0';
return hex;
char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0';
return hex;
#endif
}

View file

@ -9,7 +9,7 @@
* Added: 2025-12-11 (2025-12-11 migrated from src/vm/libsql/common.c)
*/
/* Ensure PCRE2 is configured consistently across the whole translation unit.
/* Ensure PCRE2 is configured consistently across the whole translation unit.
* vm.c includes many opcode implementation .c files; some use PCRE2. For PCRE2
* headers to expose the correct typedefs (e.g., pcre2_code, PCRE2_SPTR), the
* PCRE2_CODE_UNIT_WIDTH macro must be defined before the first inclusion of

87
src/external/pcsc.c vendored
View file

@ -16,61 +16,70 @@ Included the file scope from vm.c.
#ifdef FUN_WITH_PCSC
#if defined(__has_include)
#if __has_include(<PCSC/winscard.h>)
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
#elif __has_include(<winscard.h>)
#include <winscard.h>
#else
#error "FUN_WITH_PCSC is enabled but PCSC headers were not found"
#endif
#else
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
#endif
#include <string.h>
typedef struct {
SCARDCONTEXT ctx;
int in_use;
} pcsc_ctx_entry;
#if __has_include(<PCSC/winscard.h>)
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
#elif __has_include(<winscard.h>)
#include <winscard.h>
#else
#error "FUN_WITH_PCSC is enabled but PCSC headers were not found"
#endif
#else
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
#endif
#include <string.h>
typedef struct {
SCARDHANDLE h;
DWORD proto;
int in_use;
SCARDCONTEXT ctx;
int in_use;
} pcsc_ctx_entry;
typedef struct {
SCARDHANDLE h;
DWORD proto;
int in_use;
} pcsc_card_entry;
static pcsc_ctx_entry g_pcsc_ctx[8];
static pcsc_card_entry g_pcsc_card[32];
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) { g_pcsc_ctx[i].in_use = 1; g_pcsc_ctx[i].ctx = 0; return i + 1; }
for (int i = 0; i < (int)(sizeof(g_pcsc_ctx) / sizeof(g_pcsc_ctx[0])); ++i) {
if (!g_pcsc_ctx[i].in_use) {
g_pcsc_ctx[i].in_use = 1;
g_pcsc_ctx[i].ctx = 0;
return i + 1;
}
return 0;
}
return 0;
}
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) { g_pcsc_card[i].in_use = 1; g_pcsc_card[i].h = 0; g_pcsc_card[i].proto = 0; return i + 1; }
for (int i = 0; i < (int)(sizeof(g_pcsc_card) / sizeof(g_pcsc_card[0])); ++i) {
if (!g_pcsc_card[i].in_use) {
g_pcsc_card[i].in_use = 1;
g_pcsc_card[i].h = 0;
g_pcsc_card[i].proto = 0;
return i + 1;
}
return 0;
}
return 0;
}
static pcsc_ctx_entry* pcsc_get_ctx(int id) {
if (id <= 0) return NULL;
int idx = id - 1;
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_ctx)/sizeof(g_pcsc_ctx[0]))) return NULL;
if (!g_pcsc_ctx[idx].in_use) return NULL;
return &g_pcsc_ctx[idx];
static pcsc_ctx_entry *pcsc_get_ctx(int id) {
if (id <= 0) return NULL;
int idx = id - 1;
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_ctx) / sizeof(g_pcsc_ctx[0]))) return NULL;
if (!g_pcsc_ctx[idx].in_use) return NULL;
return &g_pcsc_ctx[idx];
}
static pcsc_card_entry* pcsc_get_card(int id) {
if (id <= 0) return NULL;
int idx = id - 1;
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_card)/sizeof(g_pcsc_card[0]))) return NULL;
if (!g_pcsc_card[idx].in_use) return NULL;
return &g_pcsc_card[idx];
static pcsc_card_entry *pcsc_get_card(int id) {
if (id <= 0) return NULL;
int idx = id - 1;
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_card) / sizeof(g_pcsc_card[0]))) return NULL;
if (!g_pcsc_card[idx].in_use) return NULL;
return &g_pcsc_card[idx];
}
#endif

42
src/external/sqlite.c vendored
View file

@ -16,34 +16,40 @@
#include <sqlite3.h>
typedef struct SqlHandle {
int id;
sqlite3 *db;
struct SqlHandle *next;
int id;
sqlite3 *db;
struct SqlHandle *next;
} SqlHandle;
static SqlHandle *g_sql_handles = NULL;
static int g_sql_next_id = 1;
static SqlHandle* sql_reg_add(sqlite3 *db) {
SqlHandle *h = (SqlHandle*)calloc(1, sizeof(SqlHandle));
if (!h) return NULL;
h->id = g_sql_next_id++;
h->db = db;
h->next = g_sql_handles;
g_sql_handles = h;
return h;
static SqlHandle *sql_reg_add(sqlite3 *db) {
SqlHandle *h = (SqlHandle *)calloc(1, sizeof(SqlHandle));
if (!h) return NULL;
h->id = g_sql_next_id++;
h->db = db;
h->next = g_sql_handles;
g_sql_handles = h;
return h;
}
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;
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;
}
static void sql_reg_del(int id) {
SqlHandle **pp = &g_sql_handles;
while (*pp) {
if ((*pp)->id == id) { SqlHandle *d = *pp; *pp = d->next; free(d); return; }
pp = &(*pp)->next;
SqlHandle **pp = &g_sql_handles;
while (*pp) {
if ((*pp)->id == id) {
SqlHandle *d = *pp;
*pp = d->next;
free(d);
return;
}
pp = &(*pp)->next;
}
}
#endif

82
src/external/tcltk.c vendored
View file

@ -12,58 +12,68 @@
#ifdef FUN_WITH_TCLTK
#include <tcl.h>
#include <tk.h>
static Tcl_Interp* g_fun_tcl_interp = NULL;
static Tcl_Interp *g_fun_tcl_interp = NULL;
static void fun_tk_init_once(void) {
if (g_fun_tcl_interp) return;
Tcl_FindExecutable(NULL);
g_fun_tcl_interp = Tcl_CreateInterp();
if (!g_fun_tcl_interp) return;
if (Tcl_Init(g_fun_tcl_interp) != TCL_OK) {
fprintf(stderr, "Tcl_Init failed: %s\n", Tcl_GetStringResult(g_fun_tcl_interp));
}
if (Tk_Init(g_fun_tcl_interp) != TCL_OK) {
fprintf(stderr, "Tk_Init failed: %s\n", Tcl_GetStringResult(g_fun_tcl_interp));
}
/* Ensure the app terminates if the main window is closed via window manager */
/* Best-effort: set WM_DELETE_WINDOW handler to exit the process. */
Tcl_Eval(g_fun_tcl_interp, "wm protocol . WM_DELETE_WINDOW {exit 0}");
if (g_fun_tcl_interp) return;
Tcl_FindExecutable(NULL);
g_fun_tcl_interp = Tcl_CreateInterp();
if (!g_fun_tcl_interp) return;
if (Tcl_Init(g_fun_tcl_interp) != TCL_OK) {
fprintf(stderr, "Tcl_Init failed: %s\n", Tcl_GetStringResult(g_fun_tcl_interp));
}
if (Tk_Init(g_fun_tcl_interp) != TCL_OK) {
fprintf(stderr, "Tk_Init failed: %s\n", Tcl_GetStringResult(g_fun_tcl_interp));
}
/* Ensure the app terminates if the main window is closed via window manager */
/* Best-effort: set WM_DELETE_WINDOW handler to exit the process. */
Tcl_Eval(g_fun_tcl_interp, "wm protocol . WM_DELETE_WINDOW {exit 0}");
}
static int fun_tk_eval_script(const char *script) {
fun_tk_init_once();
if (!g_fun_tcl_interp) return -1;
int rc = Tcl_Eval(g_fun_tcl_interp, script ? script : "");
return rc; /* TCL_OK = 0 */
fun_tk_init_once();
if (!g_fun_tcl_interp) return -1;
int rc = Tcl_Eval(g_fun_tcl_interp, script ? script : "");
return rc; /* TCL_OK = 0 */
}
static const char* fun_tk_get_result(void) {
fun_tk_init_once();
if (!g_fun_tcl_interp) return "";
return Tcl_GetStringResult(g_fun_tcl_interp);
static const char *fun_tk_get_result(void) {
fun_tk_init_once();
if (!g_fun_tcl_interp) return "";
return Tcl_GetStringResult(g_fun_tcl_interp);
}
static void fun_tk_loop(void) {
fun_tk_init_once();
if (!g_fun_tcl_interp) return;
/* Drive Tk event loop until all main windows are closed */
while (Tk_GetNumMainWindows() > 0) {
while (Tcl_DoOneEvent(0)) {}
/* tiny sleep to avoid busy spin */
fun_tk_init_once();
if (!g_fun_tcl_interp) return;
/* Drive Tk event loop until all main windows are closed */
while (Tk_GetNumMainWindows() > 0) {
while (Tcl_DoOneEvent(0)) {
}
/* tiny sleep to avoid busy spin */
#ifdef _WIN32
#include <windows.h>
Sleep(1);
Sleep(1);
#else
#include <time.h>
struct timespec ts = {0, 1000000}; /* 1 ms */
nanosleep(&ts, NULL);
struct timespec ts = {0, 1000000}; /* 1 ms */
nanosleep(&ts, NULL);
#endif
}
}
}
#else
/* Stubs when Tcl/Tk is disabled */
static void fun_tk_init_once(void) { (void)0; }
static int fun_tk_eval_script(const char *script) { (void)script; return -1; }
static const char* fun_tk_get_result(void) { return ""; }
static void fun_tk_loop(void) { (void)0; }
static void fun_tk_init_once(void) {
(void)0;
}
static int fun_tk_eval_script(const char *script) {
(void)script;
return -1;
}
static const char *fun_tk_get_result(void) {
return "";
}
static void fun_tk_loop(void) {
(void)0;
}
#endif

34
src/external/xml2.c vendored
View file

@ -13,24 +13,34 @@
#include <libxml/parser.h>
#include <libxml/tree.h>
typedef struct { xmlDocPtr doc; int in_use; } XmlDocSlot;
typedef struct { xmlNodePtr node; int in_use; } XmlNodeSlot;
typedef struct {
xmlDocPtr doc;
int in_use;
} XmlDocSlot;
typedef struct {
xmlNodePtr node;
int in_use;
} XmlNodeSlot;
static XmlDocSlot g_xml_docs[64];
static XmlNodeSlot g_xml_nodes[256];
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) { g_xml_docs[i].in_use = 1; g_xml_docs[i].doc = d; return i; }
for (int i = 1; i < (int)(sizeof(g_xml_docs) / sizeof(g_xml_docs[0])); ++i) {
if (!g_xml_docs[i].in_use) {
g_xml_docs[i].in_use = 1;
g_xml_docs[i].doc = d;
return i;
}
}
return 0;
}
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;
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;
}
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 (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);
g_xml_docs[h].doc = NULL;
g_xml_docs[h].in_use = 0;
@ -38,17 +48,21 @@ static int xml_doc_free_handle(int h) {
}
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) { g_xml_nodes[i].in_use = 1; g_xml_nodes[i].node = n; return i; }
for (int i = 1; i < (int)(sizeof(g_xml_nodes) / sizeof(g_xml_nodes[0])); ++i) {
if (!g_xml_nodes[i].in_use) {
g_xml_nodes[i].in_use = 1;
g_xml_nodes[i].node = n;
return i;
}
}
return 0;
}
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;
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;
}
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;
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 */
g_xml_nodes[h].node = NULL;
g_xml_nodes[h].in_use = 0;

212
src/fun.c
View file

@ -1,5 +1,5 @@
/*
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -7,15 +7,15 @@
* https://opensource.org/license/apache-2-0
*/
/*
/*
* 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.
*/
#include "bytecode.h"
#include "vm.h"
#include "parser.h"
#include "vm.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@ -30,127 +30,127 @@
#endif
static void print_usage(const char *prog) {
printf("Fun %s\n", FUN_VERSION);
printf("Usage:\n");
printf("Fun %s\n", FUN_VERSION);
printf("Usage:\n");
#ifdef FUN_WITH_REPL
printf(" %s [--trace|-t] [--repl-on-error] [script.fun]\n", prog ? prog : "fun");
printf(" %s --help | -h\n", prog ? prog : "fun");
printf(" %s --version | -V\n", prog ? prog : "fun");
printf("\n");
printf("Options:\n");
printf(" --trace, -t Print executed ops and stack tops during run\n");
printf(" --repl-on-error Enter interactive REPL on runtime error with stack preserved\n\n");
printf("When no script is provided, a REPL starts. Submit an empty line to execute the buffer.\n");
printf(" %s [--trace|-t] [--repl-on-error] [script.fun]\n", prog ? prog : "fun");
printf(" %s --help | -h\n", prog ? prog : "fun");
printf(" %s --version | -V\n", prog ? prog : "fun");
printf("\n");
printf("Options:\n");
printf(" --trace, -t Print executed ops and stack tops during run\n");
printf(" --repl-on-error Enter interactive REPL on runtime error with stack preserved\n\n");
printf("When no script is provided, a REPL starts. Submit an empty line to execute the buffer.\n");
#else
printf(" %s [--trace|-t] <script.fun>\n", prog ? prog : "fun");
printf(" %s --help | -h\n", prog ? prog : "fun");
printf(" %s --version | -V\n", prog ? prog : "fun");
printf("\n");
printf("Options:\n --trace, -t Print executed ops and stack tops during run\n\n");
printf("REPL is disabled in this build. Please provide a script file to run.\n");
printf(" %s [--trace|-t] <script.fun>\n", prog ? prog : "fun");
printf(" %s --help | -h\n", prog ? prog : "fun");
printf(" %s --version | -V\n", prog ? prog : "fun");
printf("\n");
printf("Options:\n --trace, -t Print executed ops and stack tops during run\n\n");
printf("REPL is disabled in this build. Please provide a script file to run.\n");
#endif
}
int main(int argc, char **argv) {
/* Set FUN_EXECUTABLE environment variable to the path of this binary */
setenv("FUN_EXECUTABLE", argv[0], 1);
/* Set FUN_EXECUTABLE environment variable to the path of this binary */
setenv("FUN_EXECUTABLE", argv[0], 1);
VM vm;
vm_init(&vm);
VM vm;
vm_init(&vm);
int argi = 1;
for (; argi < argc; ++argi) {
const char *arg = argv[argi];
if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
print_usage(argv[0]);
return 0;
}
if (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0) {
printf("Fun %s\n", FUN_VERSION);
return 0;
}
if (strcmp(arg, "--trace") == 0 || strcmp(arg, "-t") == 0) {
vm.trace_enabled = 1;
continue;
}
#ifdef FUN_WITH_REPL
if (strcmp(arg, "--repl-on-error") == 0) {
vm.repl_on_error = 1;
vm.on_error_repl = fun_run_repl; /* provide REPL entry to core VM */
continue;
}
#endif
/* first non-option assumed to be script path */
break;
int argi = 1;
for (; argi < argc; ++argi) {
const char *arg = argv[argi];
if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
print_usage(argv[0]);
return 0;
}
if (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0) {
printf("Fun %s\n", FUN_VERSION);
return 0;
}
if (strcmp(arg, "--trace") == 0 || strcmp(arg, "-t") == 0) {
vm.trace_enabled = 1;
continue;
}
#ifdef FUN_WITH_REPL
if (strcmp(arg, "--repl-on-error") == 0) {
vm.repl_on_error = 1;
vm.on_error_repl = fun_run_repl; /* provide REPL entry to core VM */
continue;
}
#endif
/* first non-option assumed to be script path */
break;
}
#ifndef FUN_WITH_REPL
if (argi >= argc) {
fprintf(stderr, "Error: REPL is disabled. Please provide a script to run.\n");
print_usage(argv[0]);
return 2;
}
if (argi >= argc) {
fprintf(stderr, "Error: REPL is disabled. Please provide a script to run.\n");
print_usage(argv[0]);
return 2;
}
#endif
if (argi < argc) {
const char *path = argv[argi];
if (argi < argc) {
const char *path = argv[argi];
/* Collect script arguments (everything after script path) and expose via env vars */
int sargi = argi + 1; /* first script arg following the script path */
int sargc = (sargi < argc) ? (argc - sargi) : 0;
/* Collect script arguments (everything after script path) and expose via env vars */
int sargi = argi + 1; /* first script arg following the script path */
int sargc = (sargi < argc) ? (argc - sargi) : 0;
/* Export FUN_ARGC */
{
char buf[32];
snprintf(buf, sizeof(buf), "%d", sargc);
setenv("FUN_ARGC", buf, 1);
}
/* Export FUN_ARGV_i */
for (int i = 0; i < sargc; ++i) {
char key[32];
snprintf(key, sizeof(key), "FUN_ARGV_%d", i);
setenv(key, argv[sargi + i], 1);
}
/* Optional: space-joined convenience string FUN_ARGS */
if (sargc > 0) {
size_t total = 0;
for (int i = 0; i < sargc; ++i) {
total += strlen(argv[sargi + i]) + 1; /* +1 for space or NUL */
}
char *joined = (char*)malloc(total);
if (joined) {
joined[0] = '\0';
for (int i = 0; i < sargc; ++i) {
strcat(joined, argv[sargi + i]);
if (i + 1 < sargc) strcat(joined, " ");
}
setenv("FUN_ARGS", joined, 1);
free(joined);
}
} else {
/* Ensure FUN_ARGS is at least cleared for consistency */
setenv("FUN_ARGS", "", 1);
}
Bytecode *bc = parse_file_to_bytecode(path);
if (!bc) {
fprintf(stderr, "Failed to compile script: %s\n", path);
return 1;
}
vm_run(&vm, bc);
vm_print_output(&vm);
vm_clear_output(&vm);
bytecode_free(bc);
return vm.exit_code;
/* Export FUN_ARGC */
{
char buf[32];
snprintf(buf, sizeof(buf), "%d", sargc);
setenv("FUN_ARGC", buf, 1);
}
/* Export FUN_ARGV_i */
for (int i = 0; i < sargc; ++i) {
char key[32];
snprintf(key, sizeof(key), "FUN_ARGV_%d", i);
setenv(key, argv[sargi + i], 1);
}
/* Optional: space-joined convenience string FUN_ARGS */
if (sargc > 0) {
size_t total = 0;
for (int i = 0; i < sargc; ++i) {
total += strlen(argv[sargi + i]) + 1; /* +1 for space or NUL */
}
char *joined = (char *)malloc(total);
if (joined) {
joined[0] = '\0';
for (int i = 0; i < sargc; ++i) {
strcat(joined, argv[sargi + i]);
if (i + 1 < sargc) strcat(joined, " ");
}
setenv("FUN_ARGS", joined, 1);
free(joined);
}
} else {
/* Ensure FUN_ARGS is at least cleared for consistency */
setenv("FUN_ARGS", "", 1);
}
Bytecode *bc = parse_file_to_bytecode(path);
if (!bc) {
fprintf(stderr, "Failed to compile script: %s\n", path);
return 1;
}
vm_run(&vm, bc);
vm_print_output(&vm);
vm_clear_output(&vm);
bytecode_free(bc);
return vm.exit_code;
}
#ifdef FUN_WITH_REPL
return fun_run_repl(&vm);
return fun_run_repl(&vm);
#else
fprintf(stderr, "Internal error: REPL not available in this build.\n");
return 2;
fprintf(stderr, "Internal error: REPL not available in this build.\n");
return 2;
#endif
}

View file

@ -10,302 +10,302 @@
#include "bytecode.h"
#include "value.h"
#include "vm.h"
#include <stdio.h>
#include <math.h>
#include <stdio.h>
#define ASSERT_EQ(val, expected) \
if ((val).type != VAL_INT || (val).i != (expected)) { \
fprintf(stderr, "Assertion failed: expected %lld, got ", (long long)(expected)); \
print_value(&(val)); \
printf("\n"); \
return 1; \
}
#define ASSERT_EQ(val, expected) \
if ((val).type != VAL_INT || (val).i != (expected)) { \
fprintf(stderr, "Assertion failed: expected %lld, got ", (long long)(expected)); \
print_value(&(val)); \
printf("\n"); \
return 1; \
}
int main(void) {
VM vm;
vm_init(&vm);
VM vm;
vm_init(&vm);
Bytecode *bc = bytecode_new();
Bytecode *bc = bytecode_new();
// constants
int c0 = bytecode_add_constant(bc, make_int(0));
int c1 = bytecode_add_constant(bc, make_int(1));
int c2 = bytecode_add_constant(bc, make_int(2));
int c3 = bytecode_add_constant(bc, make_int(3));
int c10 = bytecode_add_constant(bc, make_int(10));
int c42 = bytecode_add_constant(bc, make_int(42));
int cf3_2 = bytecode_add_constant(bc, make_float(3.2));
int cf3_5 = bytecode_add_constant(bc, make_float(3.5));
int cf3_8 = bytecode_add_constant(bc, make_float(3.8));
int cfn3_2 = bytecode_add_constant(bc, make_float(-3.2));
int cfn3_5 = bytecode_add_constant(bc, make_float(-3.5));
int cfn3_8 = bytecode_add_constant(bc, make_float(-3.8));
int cf0 = bytecode_add_constant(bc, make_float(0.0));
int cf1 = bytecode_add_constant(bc, make_float(1.0));
int cf5 = bytecode_add_constant(bc, make_float(5.0));
int c4 = bytecode_add_constant(bc, make_int(4));
int c9 = bytecode_add_constant(bc, make_int(9));
int c48 = bytecode_add_constant(bc, make_int(48));
int c18 = bytecode_add_constant(bc, make_int(18));
int c21 = bytecode_add_constant(bc, make_int(21));
int c6 = bytecode_add_constant(bc, make_int(6));
int c15 = bytecode_add_constant(bc, make_int(15));
int c16 = bytecode_add_constant(bc, make_int(16));
int cneg5 = bytecode_add_constant(bc, make_int(-5));
int c7 = bytecode_add_constant(bc, make_int(7));
// constants
int c0 = bytecode_add_constant(bc, make_int(0));
int c1 = bytecode_add_constant(bc, make_int(1));
int c2 = bytecode_add_constant(bc, make_int(2));
int c3 = bytecode_add_constant(bc, make_int(3));
int c10 = bytecode_add_constant(bc, make_int(10));
int c42 = bytecode_add_constant(bc, make_int(42));
int cf3_2 = bytecode_add_constant(bc, make_float(3.2));
int cf3_5 = bytecode_add_constant(bc, make_float(3.5));
int cf3_8 = bytecode_add_constant(bc, make_float(3.8));
int cfn3_2 = bytecode_add_constant(bc, make_float(-3.2));
int cfn3_5 = bytecode_add_constant(bc, make_float(-3.5));
int cfn3_8 = bytecode_add_constant(bc, make_float(-3.8));
int cf0 = bytecode_add_constant(bc, make_float(0.0));
int cf1 = bytecode_add_constant(bc, make_float(1.0));
int cf5 = bytecode_add_constant(bc, make_float(5.0));
int c4 = bytecode_add_constant(bc, make_int(4));
int c9 = bytecode_add_constant(bc, make_int(9));
int c48 = bytecode_add_constant(bc, make_int(48));
int c18 = bytecode_add_constant(bc, make_int(18));
int c21 = bytecode_add_constant(bc, make_int(21));
int c6 = bytecode_add_constant(bc, make_int(6));
int c15 = bytecode_add_constant(bc, make_int(15));
int c16 = bytecode_add_constant(bc, make_int(16));
int cneg5 = bytecode_add_constant(bc, make_int(-5));
int c7 = bytecode_add_constant(bc, make_int(7));
// ---------- Arithmetic ----------
bytecode_add_instruction(bc, OP_LOAD_CONST, c42);
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_ADD, 0); // 42+1=43
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- Arithmetic ----------
bytecode_add_instruction(bc, OP_LOAD_CONST, c42);
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_ADD, 0); // 42+1=43
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
bytecode_add_instruction(bc, OP_SUB, 0); // 10-3=7
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
bytecode_add_instruction(bc, OP_SUB, 0); // 10-3=7
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
bytecode_add_instruction(bc, OP_MUL, 0); // 2*3=6
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
bytecode_add_instruction(bc, OP_MUL, 0); // 2*3=6
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_DIV, 0); // 10/2=5
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_DIV, 0); // 10/2=5
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
bytecode_add_instruction(bc, OP_MOD, 0); // 10%3=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
bytecode_add_instruction(bc, OP_MOD, 0); // 10%3=1
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- Comparisons ----------
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LT, 0); // 1<2=1
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- Comparisons ----------
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LT, 0); // 1<2=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LTE, 0); // 2<=2=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LTE, 0); // 2<=2=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_GT, 0); // 3>2=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_GT, 0); // 3>2=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_GTE, 0); // 2>=2=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_GTE, 0); // 2>=2=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_EQ, 0); // 2==2=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_EQ, 0); // 2==2=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
bytecode_add_instruction(bc, OP_NEQ, 0); // 2!=3=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
bytecode_add_instruction(bc, OP_NEQ, 0); // 2!=3=1
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- Logical ----------
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
bytecode_add_instruction(bc, OP_AND, 0); // 1&&0=0
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- Logical ----------
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
bytecode_add_instruction(bc, OP_AND, 0); // 1&&0=0
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
bytecode_add_instruction(bc, OP_OR, 0); // 1||0=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
bytecode_add_instruction(bc, OP_OR, 0); // 1||0=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
bytecode_add_instruction(bc, OP_NOT, 0); // !0=1
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
bytecode_add_instruction(bc, OP_NOT, 0); // !0=1
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- Stack ----------
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_DUP, 0); // duplicate 1
bytecode_add_instruction(bc, OP_ADD, 0); // 1+1=2
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- Stack ----------
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_DUP, 0); // duplicate 1
bytecode_add_instruction(bc, OP_ADD, 0); // 1+1=2
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_SWAP, 0); // swap top two
bytecode_add_instruction(bc, OP_PRINT, 0); // top=1
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_SWAP, 0); // swap top two
bytecode_add_instruction(bc, OP_PRINT, 0); // top=1
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_POP, 0); // dApache-2.0ard 1 (stack now empty)
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_POP, 0); // dApache-2.0ard 1 (stack now empty)
// ---------- Rounding (math.h) demo ----------
// floor/ceil/trunc/round on representative values
int start_round_demo = bc->instr_count;
(void)start_round_demo;
// ---------- Rounding (math.h) demo ----------
// floor/ceil/trunc/round on representative values
int start_round_demo = bc->instr_count;
(void)start_round_demo;
// +3.2
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
bytecode_add_instruction(bc, OP_FLOOR, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
bytecode_add_instruction(bc, OP_CEIL, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
bytecode_add_instruction(bc, OP_TRUNC, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
bytecode_add_instruction(bc, OP_ROUND, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// +3.2
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
bytecode_add_instruction(bc, OP_FLOOR, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
bytecode_add_instruction(bc, OP_CEIL, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
bytecode_add_instruction(bc, OP_TRUNC, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
bytecode_add_instruction(bc, OP_ROUND, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// +3.5
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_5);
bytecode_add_instruction(bc, OP_ROUND, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// +3.5
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_5);
bytecode_add_instruction(bc, OP_ROUND, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// -3.5
bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_5);
bytecode_add_instruction(bc, OP_ROUND, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// -3.5
bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_5);
bytecode_add_instruction(bc, OP_ROUND, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// -3.2
bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_2);
bytecode_add_instruction(bc, OP_FLOOR, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_2);
bytecode_add_instruction(bc, OP_CEIL, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// -3.2
bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_2);
bytecode_add_instruction(bc, OP_FLOOR, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_2);
bytecode_add_instruction(bc, OP_CEIL, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// integers should be unchanged
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
bytecode_add_instruction(bc, OP_FLOOR, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// integers should be unchanged
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
bytecode_add_instruction(bc, OP_FLOOR, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- Transcendentals demo ----------
// sin(0)=0
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
bytecode_add_instruction(bc, OP_SIN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// cos(0)=1
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
bytecode_add_instruction(bc, OP_COS, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// tan(0)=0
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
bytecode_add_instruction(bc, OP_TAN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// exp(0)=1
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
bytecode_add_instruction(bc, OP_EXP, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// log(1)=0
bytecode_add_instruction(bc, OP_LOAD_CONST, cf1);
bytecode_add_instruction(bc, OP_LOG, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// log10(1)=0
bytecode_add_instruction(bc, OP_LOAD_CONST, cf1);
bytecode_add_instruction(bc, OP_LOG10, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// sqrt(9)=3
bytecode_add_instruction(bc, OP_LOAD_CONST, c9);
bytecode_add_instruction(bc, OP_SQRT, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- Transcendentals demo ----------
// sin(0)=0
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
bytecode_add_instruction(bc, OP_SIN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// cos(0)=1
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
bytecode_add_instruction(bc, OP_COS, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// tan(0)=0
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
bytecode_add_instruction(bc, OP_TAN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// exp(0)=1
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
bytecode_add_instruction(bc, OP_EXP, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// log(1)=0
bytecode_add_instruction(bc, OP_LOAD_CONST, cf1);
bytecode_add_instruction(bc, OP_LOG, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// log10(1)=0
bytecode_add_instruction(bc, OP_LOAD_CONST, cf1);
bytecode_add_instruction(bc, OP_LOG10, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// sqrt(9)=3
bytecode_add_instruction(bc, OP_LOAD_CONST, c9);
bytecode_add_instruction(bc, OP_SQRT, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- Integer math (gcd/lcm/isqrt/sign) demo ----------
// gcd(48,18)=6
bytecode_add_instruction(bc, OP_LOAD_CONST, c48);
bytecode_add_instruction(bc, OP_LOAD_CONST, c18);
bytecode_add_instruction(bc, OP_GCD, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- Integer math (gcd/lcm/isqrt/sign) demo ----------
// gcd(48,18)=6
bytecode_add_instruction(bc, OP_LOAD_CONST, c48);
bytecode_add_instruction(bc, OP_LOAD_CONST, c18);
bytecode_add_instruction(bc, OP_GCD, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// lcm(21,6)=42
bytecode_add_instruction(bc, OP_LOAD_CONST, c21);
bytecode_add_instruction(bc, OP_LOAD_CONST, c6);
bytecode_add_instruction(bc, OP_LCM, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// lcm(21,6)=42
bytecode_add_instruction(bc, OP_LOAD_CONST, c21);
bytecode_add_instruction(bc, OP_LOAD_CONST, c6);
bytecode_add_instruction(bc, OP_LCM, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// isqrt cases: 0, 1, 15->3, 16->4
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
bytecode_add_instruction(bc, OP_ISQRT, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_ISQRT, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c15);
bytecode_add_instruction(bc, OP_ISQRT, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c16);
bytecode_add_instruction(bc, OP_ISQRT, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// isqrt cases: 0, 1, 15->3, 16->4
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
bytecode_add_instruction(bc, OP_ISQRT, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_ISQRT, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c15);
bytecode_add_instruction(bc, OP_ISQRT, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c16);
bytecode_add_instruction(bc, OP_ISQRT, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// sign(-5)=-1, sign(0)=0, sign(7)=1
bytecode_add_instruction(bc, OP_LOAD_CONST, cneg5);
bytecode_add_instruction(bc, OP_SIGN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
bytecode_add_instruction(bc, OP_SIGN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c7);
bytecode_add_instruction(bc, OP_SIGN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// sign(-5)=-1, sign(0)=0, sign(7)=1
bytecode_add_instruction(bc, OP_LOAD_CONST, cneg5);
bytecode_add_instruction(bc, OP_SIGN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
bytecode_add_instruction(bc, OP_SIGN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
bytecode_add_instruction(bc, OP_LOAD_CONST, c7);
bytecode_add_instruction(bc, OP_SIGN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- fmin/fmax demo ----------
// fmin(3.2, 4) -> 3.2
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c4);
bytecode_add_instruction(bc, OP_FMIN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// fmax(3.2, 4) -> 4
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c4);
bytecode_add_instruction(bc, OP_FMAX, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// NaN cases
double nanv = NAN;
int cNaN = bytecode_add_constant(bc, make_float(nanv));
// fmin(NaN, 5.0) -> 5.0
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
bytecode_add_instruction(bc, OP_FMIN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// fmax(NaN, 5.0) -> 5.0
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
bytecode_add_instruction(bc, OP_FMAX, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// fmin(5.0, NaN) -> 5.0
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
bytecode_add_instruction(bc, OP_FMIN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// fmax(5.0, NaN) -> 5.0
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
bytecode_add_instruction(bc, OP_FMAX, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// fmin(NaN, NaN) -> NaN (prints as nan)
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
bytecode_add_instruction(bc, OP_FMIN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- fmin/fmax demo ----------
// fmin(3.2, 4) -> 3.2
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c4);
bytecode_add_instruction(bc, OP_FMIN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// fmax(3.2, 4) -> 4
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
bytecode_add_instruction(bc, OP_LOAD_CONST, c4);
bytecode_add_instruction(bc, OP_FMAX, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// NaN cases
double nanv = NAN;
int cNaN = bytecode_add_constant(bc, make_float(nanv));
// fmin(NaN, 5.0) -> 5.0
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
bytecode_add_instruction(bc, OP_FMIN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// fmax(NaN, 5.0) -> 5.0
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
bytecode_add_instruction(bc, OP_FMAX, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// fmin(5.0, NaN) -> 5.0
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
bytecode_add_instruction(bc, OP_FMIN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// fmax(5.0, NaN) -> 5.0
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
bytecode_add_instruction(bc, OP_FMAX, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// fmin(NaN, NaN) -> NaN (prints as nan)
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
bytecode_add_instruction(bc, OP_FMIN, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// ---------- HALT ----------
bytecode_add_instruction(bc, OP_HALT, 0);
// ---------- HALT ----------
bytecode_add_instruction(bc, OP_HALT, 0);
printf("=== Bytecode dump ===\n");
for (int i = 0; i < bc->instr_count; ++i) {
Instruction instr = bc->instructions[i];
printf("instr %3d: opcode=%2d operand=%d\n", i, instr.op, instr.operand);
}
printf("=====================\n");
printf("=== Bytecode dump ===\n");
for (int i = 0; i < bc->instr_count; ++i) {
Instruction instr = bc->instructions[i];
printf("instr %3d: opcode=%2d operand=%d\n", i, instr.op, instr.operand);
}
printf("=====================\n");
// run VM
vm_run(&vm, bc);
// run VM
vm_run(&vm, bc);
printf("All tests executed. Output count: %d\n", vm.output_count);
printf("All tests executed. Output count: %d\n", vm.output_count);
vm_clear_output(&vm);
bytecode_free(bc);
return 0;
vm_clear_output(&vm);
bytecode_free(bc);
return 0;
}

View file

@ -12,49 +12,51 @@
/* enumerate(arr) -> [[0, v0], [1, v1], ...] */
Value bi_enumerate(const Value *arr) {
int n = array_length(arr);
if (n <= 0) return make_array_from_values(NULL, 0);
Value *pairs = (Value*)malloc(sizeof(Value) * n);
if (!pairs) return make_array_from_values(NULL, 0);
for (int i = 0; i < n; ++i) {
Value elem;
array_get_copy(arr, i, &elem);
Value kv_vals[2];
kv_vals[0] = make_int(i);
kv_vals[1] = elem;
Value kv = make_array_from_values(kv_vals, 2);
free_value(kv_vals[0]);
free_value(kv_vals[1]);
pairs[i] = kv;
}
Value out = make_array_from_values(pairs, n);
for (int i = 0; i < n; ++i) free_value(pairs[i]);
free(pairs);
return out;
int n = array_length(arr);
if (n <= 0) return make_array_from_values(NULL, 0);
Value *pairs = (Value *)malloc(sizeof(Value) * n);
if (!pairs) return make_array_from_values(NULL, 0);
for (int i = 0; i < n; ++i) {
Value elem;
array_get_copy(arr, i, &elem);
Value kv_vals[2];
kv_vals[0] = make_int(i);
kv_vals[1] = elem;
Value kv = make_array_from_values(kv_vals, 2);
free_value(kv_vals[0]);
free_value(kv_vals[1]);
pairs[i] = kv;
}
Value out = make_array_from_values(pairs, n);
for (int i = 0; i < n; ++i)
free_value(pairs[i]);
free(pairs);
return out;
}
/* zip(a, b) -> [[a0,b0], [a1,b1], ...] up to min(len(a),len(b)) */
Value bi_zip(const Value *a, const Value *b) {
int na = array_length(a);
int nb = array_length(b);
int n = na < nb ? na : nb;
if (n <= 0) return make_array_from_values(NULL, 0);
Value *pairs = (Value*)malloc(sizeof(Value) * n);
if (!pairs) return make_array_from_values(NULL, 0);
for (int i = 0; i < n; ++i) {
Value av, bv;
array_get_copy(a, i, &av);
array_get_copy(b, i, &bv);
Value kv_vals[2];
kv_vals[0] = av;
kv_vals[1] = bv;
Value kv = make_array_from_values(kv_vals, 2);
free_value(kv_vals[0]);
free_value(kv_vals[1]);
pairs[i] = kv;
}
Value out = make_array_from_values(pairs, n);
for (int i = 0; i < n; ++i) free_value(pairs[i]);
free(pairs);
return out;
int na = array_length(a);
int nb = array_length(b);
int n = na < nb ? na : nb;
if (n <= 0) return make_array_from_values(NULL, 0);
Value *pairs = (Value *)malloc(sizeof(Value) * n);
if (!pairs) return make_array_from_values(NULL, 0);
for (int i = 0; i < n; ++i) {
Value av, bv;
array_get_copy(a, i, &av);
array_get_copy(b, i, &bv);
Value kv_vals[2];
kv_vals[0] = av;
kv_vals[1] = bv;
Value kv = make_array_from_values(kv_vals, 2);
free_value(kv_vals[0]);
free_value(kv_vals[1]);
pairs[i] = kv;
}
Value out = make_array_from_values(pairs, n);
for (int i = 0; i < n; ++i)
free_value(pairs[i]);
free(pairs);
return out;
}

163
src/map.c
View file

@ -13,104 +13,113 @@
/* Internal Map definition; Value holds struct Map* */
typedef struct Map {
int refcount;
int count;
int cap;
char **keys; /* each key owned here */
Value *vals; /* each value owned here */
int refcount;
int count;
int cap;
char **keys; /* each key owned here */
Value *vals; /* each value owned here */
} Map;
Value make_map_empty(void) {
Map *m = (Map*)malloc(sizeof(Map));
if (!m) return make_nil();
m->refcount = 1;
m->count = 0;
m->cap = 0;
m->keys = NULL;
m->vals = NULL;
Value v;
v.type = VAL_MAP;
v.map = (struct Map*)m;
return v;
Map *m = (Map *)malloc(sizeof(Map));
if (!m) return make_nil();
m->refcount = 1;
m->count = 0;
m->cap = 0;
m->keys = NULL;
m->vals = NULL;
Value v;
v.type = VAL_MAP;
v.map = (struct Map *)m;
return v;
}
static int map_ensure_cap(Map *m, int need) {
if (m->cap >= need) return 1;
int ncap = m->cap == 0 ? 4 : m->cap * 2;
while (ncap < need) ncap *= 2;
char **nkeys = (char**)realloc(m->keys, sizeof(char*) * ncap);
Value *nvals = (Value*)realloc(m->vals, sizeof(Value) * ncap);
if (!nkeys || !nvals) return 0;
m->keys = nkeys;
m->vals = nvals;
m->cap = ncap;
return 1;
if (m->cap >= need) return 1;
int ncap = m->cap == 0 ? 4 : m->cap * 2;
while (ncap < need)
ncap *= 2;
char **nkeys = (char **)realloc(m->keys, sizeof(char *) * ncap);
Value *nvals = (Value *)realloc(m->vals, sizeof(Value) * ncap);
if (!nkeys || !nvals) return 0;
m->keys = nkeys;
m->vals = nvals;
m->cap = ncap;
return 1;
}
int map_set(Value *vm, const char *key, Value v) {
if (!vm || vm->type != VAL_MAP || !vm->map || !key) { free_value(v); return 0; }
Map *m = (Map*)vm->map;
for (int i = 0; i < m->count; ++i) {
if (strcmp(m->keys[i], key) == 0) {
free_value(m->vals[i]);
m->vals[i] = v;
return 1;
}
if (!vm || vm->type != VAL_MAP || !vm->map || !key) {
free_value(v);
return 0;
}
Map *m = (Map *)vm->map;
for (int i = 0; i < m->count; ++i) {
if (strcmp(m->keys[i], key) == 0) {
free_value(m->vals[i]);
m->vals[i] = v;
return 1;
}
if (!map_ensure_cap(m, m->count + 1)) { free_value(v); return 0; }
m->keys[m->count] = strdup(key);
m->vals[m->count] = v;
m->count++;
return 1;
}
if (!map_ensure_cap(m, m->count + 1)) {
free_value(v);
return 0;
}
m->keys[m->count] = strdup(key);
m->vals[m->count] = v;
m->count++;
return 1;
}
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;
for (int i = 0; i < m->count; ++i) {
if (strcmp(m->keys[i], key) == 0) {
if (out) *out = copy_value(&m->vals[i]);
return 1;
}
if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0;
Map *m = (Map *)vm->map;
for (int i = 0; i < m->count; ++i) {
if (strcmp(m->keys[i], key) == 0) {
if (out) *out = copy_value(&m->vals[i]);
return 1;
}
return 0;
}
return 0;
}
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;
for (int i = 0; i < m->count; ++i) {
if (strcmp(m->keys[i], key) == 0) return 1;
}
return 0;
if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0;
Map *m = (Map *)vm->map;
for (int i = 0; i < m->count; ++i) {
if (strcmp(m->keys[i], key) == 0) return 1;
}
return 0;
}
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;
if (m->count <= 0) return make_array_from_values(NULL, 0);
Value *tmp = (Value*)malloc(sizeof(Value) * m->count);
if (!tmp) return make_array_from_values(NULL, 0);
for (int i = 0; i < m->count; ++i) {
tmp[i] = make_string(m->keys[i]);
}
Value arr = make_array_from_values(tmp, m->count);
for (int i = 0; i < m->count; ++i) free_value(tmp[i]);
free(tmp);
return arr;
if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0);
Map *m = (Map *)vm->map;
if (m->count <= 0) return make_array_from_values(NULL, 0);
Value *tmp = (Value *)malloc(sizeof(Value) * m->count);
if (!tmp) return make_array_from_values(NULL, 0);
for (int i = 0; i < m->count; ++i) {
tmp[i] = make_string(m->keys[i]);
}
Value arr = make_array_from_values(tmp, m->count);
for (int i = 0; i < m->count; ++i)
free_value(tmp[i]);
free(tmp);
return arr;
}
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;
if (m->count <= 0) return make_array_from_values(NULL, 0);
Value *tmp = (Value*)malloc(sizeof(Value) * m->count);
if (!tmp) return make_array_from_values(NULL, 0);
for (int i = 0; i < m->count; ++i) {
tmp[i] = copy_value(&m->vals[i]);
}
Value arr = make_array_from_values(tmp, m->count);
for (int i = 0; i < m->count; ++i) free_value(tmp[i]);
free(tmp);
return arr;
if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0);
Map *m = (Map *)vm->map;
if (m->count <= 0) return make_array_from_values(NULL, 0);
Value *tmp = (Value *)malloc(sizeof(Value) * m->count);
if (!tmp) return make_array_from_values(NULL, 0);
for (int i = 0; i < m->count; ++i) {
tmp[i] = copy_value(&m->vals[i]);
}
Value arr = make_array_from_values(tmp, m->count);
for (int i = 0; i < m->count; ++i)
free_value(tmp[i]);
free(tmp);
return arr;
}

11547
src/parser.c

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

3480
src/repl.c

File diff suppressed because it is too large Load diff

View file

@ -14,121 +14,126 @@
/* string helpers returning newly allocated C strings or arrays */
char *string_substr(const char *s, int start, int len) {
if (!s) return strdup("");
int n = (int)strlen(s);
if (start < 0) start = 0;
if (start > n) start = n;
if (len < 0) len = 0;
if (start + len > n) len = n - start;
char *out = (char*)malloc((size_t)len + 1);
if (!out) return strdup("");
memcpy(out, s + start, (size_t)len);
out[len] = '\0';
return out;
if (!s) return strdup("");
int n = (int)strlen(s);
if (start < 0) start = 0;
if (start > n) start = n;
if (len < 0) len = 0;
if (start + len > n) len = n - start;
char *out = (char *)malloc((size_t)len + 1);
if (!out) return strdup("");
memcpy(out, s + start, (size_t)len);
out[len] = '\0';
return out;
}
int string_find(const char *hay, const char *needle) {
if (!hay || !needle) return -1;
const char *p = strstr(hay, needle);
if (!p) return -1;
return (int)(p - hay);
if (!hay || !needle) return -1;
const char *p = strstr(hay, needle);
if (!p) return -1;
return (int)(p - hay);
}
Value string_split_to_array(const char *s, const char *sep) {
if (!s) s = "";
if (!sep) sep = "";
int seplen = (int)strlen(sep);
if (seplen == 0) {
/* split into characters */
int n = (int)strlen(s);
if (n <= 0) return make_array_from_values(NULL, 0);
Value *tmp = (Value*)malloc(sizeof(Value) * n);
if (!tmp) return make_array_from_values(NULL, 0);
for (int i = 0; i < n; ++i) {
char ch[2] = { s[i], 0 };
tmp[i] = make_string(ch);
}
Value arr = make_array_from_values(tmp, n);
for (int i = 0; i < n; ++i) free_value(tmp[i]);
free(tmp);
return arr;
if (!s) s = "";
if (!sep) sep = "";
int seplen = (int)strlen(sep);
if (seplen == 0) {
/* split into characters */
int n = (int)strlen(s);
if (n <= 0) return make_array_from_values(NULL, 0);
Value *tmp = (Value *)malloc(sizeof(Value) * n);
if (!tmp) return make_array_from_values(NULL, 0);
for (int i = 0; i < n; ++i) {
char ch[2] = {s[i], 0};
tmp[i] = make_string(ch);
}
/* split by separator */
Value *parts = NULL;
int count = 0;
int cap = 0;
const char *cur = s;
const char *pos = NULL;
while ((pos = strstr(cur, sep)) != NULL) {
int len = (int)(pos - cur);
char *piece = (char*)malloc((size_t)len + 1);
if (!piece) break;
memcpy(piece, cur, (size_t)len);
piece[len] = '\0';
if (count >= cap) {
cap = cap == 0 ? 4 : cap * 2;
parts = (Value*)realloc(parts, sizeof(Value) * cap);
}
parts[count++] = make_string(piece);
free(piece);
cur = pos + seplen;
}
/* tail */
char *tail = strdup(cur ? cur : "");
if (count >= cap) {
cap = cap == 0 ? 1 : cap + 1;
parts = (Value*)realloc(parts, sizeof(Value) * cap);
}
parts[count++] = make_string(tail ? tail : "");
free(tail);
Value arr = make_array_from_values(parts, count);
for (int i = 0; i < count; ++i) free_value(parts[i]);
free(parts);
Value arr = make_array_from_values(tmp, n);
for (int i = 0; i < n; ++i)
free_value(tmp[i]);
free(tmp);
return arr;
}
/* split by separator */
Value *parts = NULL;
int count = 0;
int cap = 0;
const char *cur = s;
const char *pos = NULL;
while ((pos = strstr(cur, sep)) != NULL) {
int len = (int)(pos - cur);
char *piece = (char *)malloc((size_t)len + 1);
if (!piece) break;
memcpy(piece, cur, (size_t)len);
piece[len] = '\0';
if (count >= cap) {
cap = cap == 0 ? 4 : cap * 2;
parts = (Value *)realloc(parts, sizeof(Value) * cap);
}
parts[count++] = make_string(piece);
free(piece);
cur = pos + seplen;
}
/* tail */
char *tail = strdup(cur ? cur : "");
if (count >= cap) {
cap = cap == 0 ? 1 : cap + 1;
parts = (Value *)realloc(parts, sizeof(Value) * cap);
}
parts[count++] = make_string(tail ? tail : "");
free(tail);
Value arr = make_array_from_values(parts, count);
for (int i = 0; i < count; ++i)
free_value(parts[i]);
free(parts);
return arr;
}
char *array_join_with_sep(const Value *v, const char *sep) {
if (!v || v->type != VAL_ARRAY || !v->arr) return strdup("");
if (!sep) sep = "";
/* Array is defined in value.c; we only need safe public access. */
const int n = array_length(v);
if (n <= 0) return strdup("");
if (!v || v->type != VAL_ARRAY || !v->arr) return strdup("");
if (!sep) sep = "";
/* Array is defined in value.c; we only need safe public access. */
const int n = array_length(v);
if (n <= 0) return strdup("");
char **parts = (char**)malloc(sizeof(char*) * n);
if (!parts) return strdup("");
char **parts = (char **)malloc(sizeof(char *) * n);
if (!parts) return strdup("");
size_t total = 0;
for (int i = 0; i < n; ++i) {
Value item;
if (!array_get_copy(v, i, &item)) {
parts[i] = strdup("");
} else {
parts[i] = value_to_string_alloc(&item);
free_value(item);
}
total += strlen(parts[i]);
if (i + 1 < n) total += strlen(sep);
size_t total = 0;
for (int i = 0; i < n; ++i) {
Value item;
if (!array_get_copy(v, i, &item)) {
parts[i] = strdup("");
} else {
parts[i] = value_to_string_alloc(&item);
free_value(item);
}
total += strlen(parts[i]);
if (i + 1 < n) total += strlen(sep);
}
char *out = (char*)malloc(total + 1);
if (!out) {
for (int i = 0; i < n; ++i) free(parts[i]);
free(parts);
return strdup("");
}
size_t off = 0;
for (int i = 0; i < n; ++i) {
size_t li = strlen(parts[i]);
memcpy(out + off, parts[i], li); off += li;
if (i + 1 < n) {
size_t ls = strlen(sep);
memcpy(out + off, sep, ls); off += ls;
}
free(parts[i]);
}
char *out = (char *)malloc(total + 1);
if (!out) {
for (int i = 0; i < n; ++i)
free(parts[i]);
free(parts);
out[off] = '\0';
return out;
return strdup("");
}
size_t off = 0;
for (int i = 0; i < n; ++i) {
size_t li = strlen(parts[i]);
memcpy(out + off, parts[i], li);
off += li;
if (i + 1 < n) {
size_t ls = strlen(sep);
memcpy(out + off, sep, ls);
off += ls;
}
free(parts[i]);
}
free(parts);
out[off] = '\0';
return out;
}

View file

@ -13,29 +13,29 @@
/* String built-ins wrappers used by VM opcodes */
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);
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);
}
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);
Value out = make_string(s ? s : "");
if (s) free(s);
return out;
const char *p = (sep && sep->type == VAL_STRING && sep->s) ? sep->s : "";
char *s = array_join_with_sep(arr, p);
Value out = make_string(s ? s : "");
if (s) free(s);
return out;
}
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);
Value out = make_string(sub ? sub : "");
if (sub) free(sub);
return out;
const char *s = (str && str->type == VAL_STRING && str->s) ? str->s : "";
char *sub = string_substr(s, start, len);
Value out = make_string(sub ? sub : "");
if (sub) free(sub);
return out;
}
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 : "";
return string_find(h, n);
const char *h = (hay && hay->type == VAL_STRING && hay->s) ? hay->s : "";
const char *n = (needle && needle->type == VAL_STRING && needle->s) ? needle->s : "";
return string_find(h, n);
}

View file

@ -7,69 +7,69 @@
* https://opensource.org/license/apache-2-0
*/
#include "vm.h"
#include "bytecode.h"
#include "value.h"
#include "vm.h"
#include <stdio.h>
int main() {
VM vm;
vm_init(&vm);
VM vm;
vm_init(&vm);
Bytecode *bc = bytecode_new();
Bytecode *bc = bytecode_new();
// Example: test OP_ADD
int c1 = bytecode_add_constant(bc, make_int(5));
int c2 = bytecode_add_constant(bc, make_int(3));
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_ADD, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
// Example: test OP_ADD
int c1 = bytecode_add_constant(bc, make_int(5));
int c2 = bytecode_add_constant(bc, make_int(3));
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
bytecode_add_instruction(bc, OP_ADD, 0);
bytecode_add_instruction(bc, OP_PRINT, 0);
printf("=== Bytecode dump ===\n");
for (int i = 0; i < bc->instr_count; ++i) {
Instruction instr = bc->instructions[i];
printf("instr %3d: opcode=%2d operand=%d\n", i, instr.op, instr.operand);
}
printf("=====================\n");
bytecode_dump(bc);
printf("=====================\n");
printf("=== Bytecode dump ===\n");
for (int i = 0; i < bc->instr_count; ++i) {
Instruction instr = bc->instructions[i];
printf("instr %3d: opcode=%2d operand=%d\n", i, instr.op, instr.operand);
}
printf("=====================\n");
bytecode_dump(bc);
printf("=====================\n");
vm_run(&vm, bc);
vm_run(&vm, bc);
printf("Output count: %d\n", vm.output_count);
for (int i = 0; i < vm.output_count; i++) {
printf("Output[%d] = ", i);
print_value(&vm.output[i]);
printf("\n");
}
printf("Output count: %d\n", vm.output_count);
for (int i = 0; i < vm.output_count; i++) {
printf("Output[%d] = ", i);
print_value(&vm.output[i]);
printf("\n");
}
vm_clear_output(&vm);
vm_clear_output(&vm);
/* --- Rust FFI demo: call a Rust opcode and string function --- */
/* --- Rust FFI demo: call a Rust opcode and string function --- */
#ifdef FUN_WITH_RUST
extern int fun_op_radd(VM *vm);
extern const char *fun_rust_get_string(void);
extern int fun_op_radd(VM * vm);
extern const char *fun_rust_get_string(void);
printf("=== Rust FFI demo ===\n");
const char *rs = fun_rust_get_string();
if (rs) {
printf("Rust says: %s\n", rs);
}
printf("=== Rust FFI demo ===\n");
const char *rs = fun_rust_get_string();
if (rs) {
printf("Rust says: %s\n", rs);
}
/* prepare stack: push 10 and 32, then call Rust add -> expect 42 */
vm_push_i64(&vm, 10);
vm_push_i64(&vm, 32);
int rc = fun_op_radd(&vm);
printf("fun_op_radd rc=%d\n", rc);
long long sum = (long long)vm_pop_i64(&vm);
printf("Rust op result: %lld\n", sum);
/* prepare stack: push 10 and 32, then call Rust add -> expect 42 */
vm_push_i64(&vm, 10);
vm_push_i64(&vm, 32);
int rc = fun_op_radd(&vm);
printf("fun_op_radd rc=%d\n", rc);
long long sum = (long long)vm_pop_i64(&vm);
printf("Rust op result: %lld\n", sum);
#else
printf("=== Rust FFI demo (disabled; build with -DFUN_WITH_RUST=ON) ===\n");
printf("=== Rust FFI demo (disabled; build with -DFUN_WITH_RUST=ON) ===\n");
#endif
vm_free(&vm);
bytecode_free(bc);
return 0;
vm_free(&vm);
bytecode_free(bc);
return 0;
}

View file

@ -8,470 +8,487 @@
*/
#include "value.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* Compile helper implementations into this TU to avoid build system changes */
#include "str_utils.c"
#include "array_utils.c"
#include "str_utils.c"
typedef struct Array {
int refcount;
int count;
Value *items; /* owns items; each item owned by array */
int refcount;
int count;
Value *items; /* owns items; each item owned by array */
} Array;
typedef struct Map {
int refcount;
int count;
int cap;
char **keys; /* each key owned here */
Value *vals; /* each value owned here */
int refcount;
int count;
int cap;
char **keys; /* each key owned here */
Value *vals; /* each value owned here */
} Map;
Value make_int(int64_t v) {
Value val;
val.type = VAL_INT;
val.i = v;
return val;
Value val;
val.type = VAL_INT;
val.i = v;
return val;
}
Value make_float(double v) {
Value val;
val.type = VAL_FLOAT;
val.d = v;
return val;
Value val;
val.type = VAL_FLOAT;
val.d = v;
return val;
}
Value make_bool(int v) {
Value val;
val.type = VAL_BOOL;
val.i = v ? 1 : 0;
return val;
Value val;
val.type = VAL_BOOL;
val.i = v ? 1 : 0;
return val;
}
Value make_string(const char *s) {
Value val;
val.type = VAL_STRING;
if (s) val.s = strdup(s);
else val.s = strdup("");
return val;
Value val;
val.type = VAL_STRING;
if (s)
val.s = strdup(s);
else
val.s = strdup("");
return val;
}
Value make_function(struct Bytecode *fn) {
Value val;
val.type = VAL_FUNCTION;
val.fn = fn;
return val;
Value val;
val.type = VAL_FUNCTION;
val.fn = fn;
return val;
}
Value make_nil(void) {
Value v;
v.type = VAL_NIL;
return v;
Value v;
v.type = VAL_NIL;
return v;
}
Value make_array_from_values(const Value *vals, int count) {
if (count < 0) count = 0;
Array *arr = (Array*)malloc(sizeof(Array));
if (!arr) {
Value nil = make_nil();
return nil;
if (count < 0) count = 0;
Array *arr = (Array *)malloc(sizeof(Array));
if (!arr) {
Value nil = make_nil();
return nil;
}
arr->refcount = 1;
arr->count = count;
if (count > 0) {
arr->items = (Value *)malloc(sizeof(Value) * count);
if (!arr->items) {
free(arr);
Value nil = make_nil();
return nil;
}
arr->refcount = 1;
arr->count = count;
if (count > 0) {
arr->items = (Value*)malloc(sizeof(Value) * count);
if (!arr->items) {
free(arr);
Value nil = make_nil();
return nil;
}
for (int i = 0; i < count; ++i) {
arr->items[i] = copy_value(&vals[i]);
}
} else {
arr->items = NULL;
for (int i = 0; i < count; ++i) {
arr->items[i] = copy_value(&vals[i]);
}
Value v;
v.type = VAL_ARRAY;
v.arr = (struct Array*)arr;
return v;
} else {
arr->items = NULL;
}
Value v;
v.type = VAL_ARRAY;
v.arr = (struct Array *)arr;
return v;
}
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;
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
const Array *a = (const Array *)v->arr;
return a->count;
}
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;
if (index < 0 || index >= a->count) return 0;
if (out) *out = copy_value(&a->items[index]);
return 1;
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
const Array *a = (const Array *)v->arr;
if (index < 0 || index >= a->count) return 0;
if (out) *out = copy_value(&a->items[index]);
return 1;
}
int array_set(Value *v, int index, Value newElem) {
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
Array *a = (Array*)v->arr;
if (index < 0 || index >= a->count) return 0;
free_value(a->items[index]);
a->items[index] = newElem; /* take ownership */
return 1;
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
Array *a = (Array *)v->arr;
if (index < 0 || index >= a->count) return 0;
free_value(a->items[index]);
a->items[index] = newElem; /* take ownership */
return 1;
}
static int ensure_array_capacity(Array *a, int newCount) {
if (newCount <= a->count) return 1;
/* grow to at least newCount; double strategy */
int curr = a->count;
int cap = curr;
if (cap < 4) cap = 4;
while (cap < newCount) cap *= 2;
Value *newItems = (Value*)realloc(a->items, sizeof(Value) * cap);
if (!newItems) return 0;
/* if growing beyond current count, initialize new slots to nil */
if (cap > a->count) {
for (int i = a->count; i < cap; ++i) {
newItems[i] = make_nil();
}
if (newCount <= a->count) return 1;
/* grow to at least newCount; double strategy */
int curr = a->count;
int cap = curr;
if (cap < 4) cap = 4;
while (cap < newCount)
cap *= 2;
Value *newItems = (Value *)realloc(a->items, sizeof(Value) * cap);
if (!newItems) return 0;
/* if growing beyond current count, initialize new slots to nil */
if (cap > a->count) {
for (int i = a->count; i < cap; ++i) {
newItems[i] = make_nil();
}
a->items = newItems;
return 1;
}
a->items = newItems;
return 1;
}
int array_push(Value *v, Value newElem) {
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
Array *a = (Array*)v->arr;
/* ensure capacity for count+1 by reallocating items array to at least count+1 elements */
Value *newItems = (Value*)realloc(a->items, sizeof(Value) * (a->count + 1));
if (!newItems) { free_value(newElem); return -1; }
a->items = newItems;
a->items[a->count] = newElem; /* take ownership */
a->count += 1;
return a->count;
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
Array *a = (Array *)v->arr;
/* ensure capacity for count+1 by reallocating items array to at least count+1 elements */
Value *newItems = (Value *)realloc(a->items, sizeof(Value) * (a->count + 1));
if (!newItems) {
free_value(newElem);
return -1;
}
a->items = newItems;
a->items[a->count] = newElem; /* take ownership */
a->count += 1;
return a->count;
}
int array_pop(Value *v, Value *out) {
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
Array *a = (Array*)v->arr;
if (a->count <= 0) return 0;
int idx = a->count - 1;
if (out) *out = a->items[idx]; /* transfer ownership */
else free_value(a->items[idx]);
a->count -= 1;
return 1;
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
Array *a = (Array *)v->arr;
if (a->count <= 0) return 0;
int idx = a->count - 1;
if (out)
*out = a->items[idx]; /* transfer ownership */
else
free_value(a->items[idx]);
a->count -= 1;
return 1;
}
int array_insert(Value *v, int index, Value newElem) {
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
Array *a = (Array*)v->arr;
if (index < 0) index = 0;
if (index > a->count) index = a->count;
Value *newItems = (Value*)realloc(a->items, sizeof(Value) * (a->count + 1));
if (!newItems) { free_value(newElem); return -1; }
a->items = newItems;
/* shift right */
for (int i = a->count; i > index; --i) {
a->items[i] = a->items[i - 1];
}
a->items[index] = newElem; /* take ownership */
a->count += 1;
return a->count;
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
Array *a = (Array *)v->arr;
if (index < 0) index = 0;
if (index > a->count) index = a->count;
Value *newItems = (Value *)realloc(a->items, sizeof(Value) * (a->count + 1));
if (!newItems) {
free_value(newElem);
return -1;
}
a->items = newItems;
/* shift right */
for (int i = a->count; i > index; --i) {
a->items[i] = a->items[i - 1];
}
a->items[index] = newElem; /* take ownership */
a->count += 1;
return a->count;
}
int array_remove(Value *v, int index, Value *out) {
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
Array *a = (Array*)v->arr;
if (index < 0 || index >= a->count) return 0;
if (out) *out = a->items[index]; /* transfer ownership */
else free_value(a->items[index]);
/* shift left */
for (int i = index; i < a->count - 1; ++i) {
a->items[i] = a->items[i + 1];
}
a->count -= 1;
return 1;
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
Array *a = (Array *)v->arr;
if (index < 0 || index >= a->count) return 0;
if (out)
*out = a->items[index]; /* transfer ownership */
else
free_value(a->items[index]);
/* shift left */
for (int i = index; i < a->count - 1; ++i) {
a->items[i] = a->items[i + 1];
}
a->count -= 1;
return 1;
}
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;
int n = a->count;
if (start < 0) start = 0;
if (end < 0 || end > n) end = n;
if (start > end) start = end;
int m = end - start;
if (m <= 0) {
return make_array_from_values(NULL, 0);
}
return make_array_from_values(a->items + start, m);
if (!v || v->type != VAL_ARRAY || !v->arr) return make_nil();
const Array *a = (const Array *)v->arr;
int n = a->count;
if (start < 0) start = 0;
if (end < 0 || end > n) end = n;
if (start > end) start = end;
int m = end - start;
if (m <= 0) {
return make_array_from_values(NULL, 0);
}
return make_array_from_values(a->items + start, m);
}
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;
const Array *b = (const Array*)bv->arr;
int na = a ? a->count : 0;
int nb = b ? b->count : 0;
int total = na + nb;
if (total <= 0) return make_array_from_values(NULL, 0);
Value *tmp = (Value*)malloc(sizeof(Value) * total);
if (!tmp) return make_nil();
for (int i = 0; i < na; ++i) tmp[i] = a->items[i];
for (int j = 0; j < nb; ++j) tmp[na + j] = b->items[j];
Value out = make_array_from_values(tmp, total);
/* free temporaries we copied from (deep copy in make_array_from_values) */
free(tmp);
return out;
if (!av || !bv || av->type != VAL_ARRAY || bv->type != VAL_ARRAY) return make_nil();
const Array *a = (const Array *)av->arr;
const Array *b = (const Array *)bv->arr;
int na = a ? a->count : 0;
int nb = b ? b->count : 0;
int total = na + nb;
if (total <= 0) return make_array_from_values(NULL, 0);
Value *tmp = (Value *)malloc(sizeof(Value) * total);
if (!tmp) return make_nil();
for (int i = 0; i < na; ++i)
tmp[i] = a->items[i];
for (int j = 0; j < nb; ++j)
tmp[na + j] = b->items[j];
Value out = make_array_from_values(tmp, total);
/* free temporaries we copied from (deep copy in make_array_from_values) */
free(tmp);
return out;
}
Value copy_value(const Value *v) {
Value out;
out.type = v->type;
switch (v->type) {
case VAL_INT:
out.i = v->i;
break;
case VAL_FLOAT:
out.d = v->d;
break;
case VAL_BOOL:
out.i = v->i ? 1 : 0;
break;
case VAL_STRING:
out.s = v->s ? strdup(v->s) : strdup("");
break;
case VAL_FUNCTION:
out.fn = v->fn; /* shallow copy pointer */
break;
case VAL_ARRAY: {
Array *a = (Array*)v->arr;
out.arr = (struct Array*)a;
if (a) a->refcount++;
break;
}
case VAL_MAP: {
Map *m = (Map*)v->map;
out.map = (struct Map*)m;
if (m) m->refcount++;
break;
}
case VAL_NIL:
default:
break;
}
return out;
Value out;
out.type = v->type;
switch (v->type) {
case VAL_INT:
out.i = v->i;
break;
case VAL_FLOAT:
out.d = v->d;
break;
case VAL_BOOL:
out.i = v->i ? 1 : 0;
break;
case VAL_STRING:
out.s = v->s ? strdup(v->s) : strdup("");
break;
case VAL_FUNCTION:
out.fn = v->fn; /* shallow copy pointer */
break;
case VAL_ARRAY: {
Array *a = (Array *)v->arr;
out.arr = (struct Array *)a;
if (a) a->refcount++;
break;
}
case VAL_MAP: {
Map *m = (Map *)v->map;
out.map = (struct Map *)m;
if (m) m->refcount++;
break;
}
case VAL_NIL:
default:
break;
}
return out;
}
/* deep copy including arrays (recursively copies items) */
Value deep_copy_value(const Value *v) {
switch (v->type) {
case VAL_INT:
return make_int(v->i);
case VAL_FLOAT:
return make_float(v->d);
case VAL_BOOL:
return make_bool(v->i);
case VAL_STRING:
return make_string(v->s ? v->s : "");
case VAL_FUNCTION:
return make_function(v->fn); /* shallow pointer for function bytecode */
case VAL_ARRAY: {
const Array *a = (const Array*)v->arr;
if (!a || a->count <= 0) {
return make_array_from_values(NULL, 0);
}
/* copy items deeply */
Value *tmp = (Value*)malloc(sizeof(Value) * a->count);
if (!tmp) return make_nil();
for (int i = 0; i < a->count; ++i) {
tmp[i] = deep_copy_value(&a->items[i]);
}
Value out = make_array_from_values(tmp, a->count);
for (int i = 0; i < a->count; ++i) {
free_value(tmp[i]);
}
free(tmp);
return out;
}
case VAL_MAP: {
const Map *m = (const Map*)v->map;
if (!m || m->count <= 0) return make_map_empty();
Value out = make_map_empty();
for (int i = 0; i < m->count; ++i) {
Value dv = deep_copy_value(&m->vals[i]);
map_set(&out, m->keys[i], dv);
}
return out;
}
case VAL_NIL:
default:
return make_nil();
switch (v->type) {
case VAL_INT:
return make_int(v->i);
case VAL_FLOAT:
return make_float(v->d);
case VAL_BOOL:
return make_bool(v->i);
case VAL_STRING:
return make_string(v->s ? v->s : "");
case VAL_FUNCTION:
return make_function(v->fn); /* shallow pointer for function bytecode */
case VAL_ARRAY: {
const Array *a = (const Array *)v->arr;
if (!a || a->count <= 0) {
return make_array_from_values(NULL, 0);
}
/* copy items deeply */
Value *tmp = (Value *)malloc(sizeof(Value) * a->count);
if (!tmp) return make_nil();
for (int i = 0; i < a->count; ++i) {
tmp[i] = deep_copy_value(&a->items[i]);
}
Value out = make_array_from_values(tmp, a->count);
for (int i = 0; i < a->count; ++i) {
free_value(tmp[i]);
}
free(tmp);
return out;
}
case VAL_MAP: {
const Map *m = (const Map *)v->map;
if (!m || m->count <= 0) return make_map_empty();
Value out = make_map_empty();
for (int i = 0; i < m->count; ++i) {
Value dv = deep_copy_value(&m->vals[i]);
map_set(&out, m->keys[i], dv);
}
return out;
}
case VAL_NIL:
default:
return make_nil();
}
}
void free_value(Value v) {
if (v.type == VAL_STRING && v.s) {
free(v.s);
} else if (v.type == VAL_ARRAY && v.arr) {
Array *a = (Array*)v.arr;
if (--a->refcount == 0) {
for (int i = 0; i < a->count; ++i) {
free_value(a->items[i]);
}
free(a->items);
free(a);
}
} else if (v.type == VAL_MAP && v.map) {
Map *m = (Map*)v.map;
if (--m->refcount == 0) {
for (int i = 0; i < m->count; ++i) {
if (m->keys[i]) free(m->keys[i]);
free_value(m->vals[i]);
}
free(m->keys);
free(m->vals);
free(m);
}
if (v.type == VAL_STRING && v.s) {
free(v.s);
} else if (v.type == VAL_ARRAY && v.arr) {
Array *a = (Array *)v.arr;
if (--a->refcount == 0) {
for (int i = 0; i < a->count; ++i) {
free_value(a->items[i]);
}
free(a->items);
free(a);
}
/* VAL_FUNCTION: we *do not* free the Bytecode here (caller frees it) */
} else if (v.type == VAL_MAP && v.map) {
Map *m = (Map *)v.map;
if (--m->refcount == 0) {
for (int i = 0; i < m->count; ++i) {
if (m->keys[i]) free(m->keys[i]);
free_value(m->vals[i]);
}
free(m->keys);
free(m->vals);
free(m);
}
}
/* VAL_FUNCTION: we *do not* free the Bytecode here (caller frees it) */
}
void print_value(const Value *v) {
switch (v->type) {
case VAL_INT:
printf("%" PRId64, v->i);
break;
case VAL_FLOAT:
printf("%.17g", v->d);
break;
case VAL_STRING:
printf("%s", v->s ? v->s : "");
break;
case VAL_BOOL:
printf("%s", v->i ? "true" : "false");
break;
case VAL_FUNCTION:
printf("<function@%p>", (void*)v->fn);
break;
case VAL_ARRAY: {
const Array *a = (const Array*)v->arr;
printf("[");
if (a) {
for (int i = 0; i < a->count; ++i) {
if (i > 0) printf(", ");
print_value(&a->items[i]);
}
}
printf("]");
break;
}
case VAL_MAP: {
const Map *m = (const Map*)v->map;
printf("{");
if (m) {
for (int i = 0; i < m->count; ++i) {
if (i > 0) printf(", ");
printf("\"%s\": ", m->keys[i] ? m->keys[i] : "");
print_value(&m->vals[i]);
}
}
printf("}");
break;
}
case VAL_NIL:
default:
printf("nil");
break;
switch (v->type) {
case VAL_INT:
printf("%" PRId64, v->i);
break;
case VAL_FLOAT:
printf("%.17g", v->d);
break;
case VAL_STRING:
printf("%s", v->s ? v->s : "");
break;
case VAL_BOOL:
printf("%s", v->i ? "true" : "false");
break;
case VAL_FUNCTION:
printf("<function@%p>", (void *)v->fn);
break;
case VAL_ARRAY: {
const Array *a = (const Array *)v->arr;
printf("[");
if (a) {
for (int i = 0; i < a->count; ++i) {
if (i > 0) printf(", ");
print_value(&a->items[i]);
}
}
printf("]");
break;
}
case VAL_MAP: {
const Map *m = (const Map *)v->map;
printf("{");
if (m) {
for (int i = 0; i < m->count; ++i) {
if (i > 0) printf(", ");
printf("\"%s\": ", m->keys[i] ? m->keys[i] : "");
print_value(&m->vals[i]);
}
}
printf("}");
break;
}
case VAL_NIL:
default:
printf("nil");
break;
}
}
int value_is_truthy(const Value *v) {
switch (v->type) {
case VAL_INT:
return v->i != 0;
case VAL_FLOAT:
return v->d != 0.0;
case VAL_BOOL:
return v->i != 0;
case VAL_STRING:
return v->s && v->s[0] != '\0';
case VAL_FUNCTION:
return 1;
case VAL_ARRAY: {
const Array *a = (const Array*)v->arr;
return a && a->count > 0;
}
case VAL_NIL:
default:
return 0;
}
switch (v->type) {
case VAL_INT:
return v->i != 0;
case VAL_FLOAT:
return v->d != 0.0;
case VAL_BOOL:
return v->i != 0;
case VAL_STRING:
return v->s && v->s[0] != '\0';
case VAL_FUNCTION:
return 1;
case VAL_ARRAY: {
const Array *a = (const Array *)v->arr;
return a && a->count > 0;
}
case VAL_NIL:
default:
return 0;
}
}
/* allocate a printable C string for the value; caller must free */
char *value_to_string_alloc(const Value *v) {
if (!v) return strdup("nil");
char buf[128];
switch (v->type) {
case VAL_INT: {
char tmp[64];
snprintf(tmp, sizeof(tmp), "%" PRId64, v->i);
return strdup(tmp);
}
case VAL_FLOAT: {
char tmp[64];
snprintf(tmp, sizeof(tmp), "%.17g", v->d);
return strdup(tmp);
}
case VAL_STRING:
return strdup(v->s ? v->s : "");
case VAL_BOOL:
return strdup(v->i ? "true" : "false");
case VAL_FUNCTION: {
snprintf(buf, sizeof(buf), "<function@%p>", (void*)v->fn);
return strdup(buf);
}
case VAL_ARRAY: {
int n = array_length(v);
if (n < 0) n = 0;
snprintf(buf, sizeof(buf), "[array n=%d]", n);
return strdup(buf);
}
case VAL_MAP: {
int n = 0;
if (v->type == VAL_MAP && v->map) {
const Map *m = (const Map*)v->map;
n = m ? m->count : 0;
}
snprintf(buf, sizeof(buf), "{map n=%d}", n);
return strdup(buf);
}
case VAL_NIL:
default:
return strdup("nil");
if (!v) return strdup("nil");
char buf[128];
switch (v->type) {
case VAL_INT: {
char tmp[64];
snprintf(tmp, sizeof(tmp), "%" PRId64, v->i);
return strdup(tmp);
}
case VAL_FLOAT: {
char tmp[64];
snprintf(tmp, sizeof(tmp), "%.17g", v->d);
return strdup(tmp);
}
case VAL_STRING:
return strdup(v->s ? v->s : "");
case VAL_BOOL:
return strdup(v->i ? "true" : "false");
case VAL_FUNCTION: {
snprintf(buf, sizeof(buf), "<function@%p>", (void *)v->fn);
return strdup(buf);
}
case VAL_ARRAY: {
int n = array_length(v);
if (n < 0) n = 0;
snprintf(buf, sizeof(buf), "[array n=%d]", n);
return strdup(buf);
}
case VAL_MAP: {
int n = 0;
if (v->type == VAL_MAP && v->map) {
const Map *m = (const Map *)v->map;
n = m ? m->count : 0;
}
snprintf(buf, sizeof(buf), "{map n=%d}", n);
return strdup(buf);
}
case VAL_NIL:
default:
return strdup("nil");
}
}
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)) {
double da = (a->type == VAL_INT) ? (double)a->i : a->d;
double db = (b->type == VAL_INT) ? (double)b->i : b->d;
return da == db;
}
if (a->type != b->type) return 0;
switch (a->type) {
case VAL_INT: return a->i == b->i;
case VAL_BOOL: return (a->i != 0) == (b->i != 0);
case VAL_STRING: {
const char *sa = a->s ? a->s : "";
const char *sb = b->s ? b->s : "";
return strcmp(sa, sb) == 0;
}
default: return 0;
}
// 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)) {
double da = (a->type == VAL_INT) ? (double)a->i : a->d;
double db = (b->type == VAL_INT) ? (double)b->i : b->d;
return da == db;
}
if (a->type != b->type) return 0;
switch (a->type) {
case VAL_INT:
return a->i == b->i;
case VAL_BOOL:
return (a->i != 0) == (b->i != 0);
case VAL_STRING: {
const char *sa = a->s ? a->s : "";
const char *sb = b->s ? b->s : "";
return strcmp(sa, sb) == 0;
}
default:
return 0;
}
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file value.h
* @file value.h
* @brief Defines the Value type and associated functions for the Fun VM.
*
* This file defines the `Value` type, which represents all possible data types
@ -45,26 +45,26 @@ struct Array; /* forward */
struct Map; /* forward */
typedef enum {
VAL_INT,
VAL_BOOL,
VAL_STRING,
VAL_FUNCTION,
VAL_ARRAY,
VAL_MAP,
VAL_NIL,
VAL_FLOAT
VAL_INT,
VAL_BOOL,
VAL_STRING,
VAL_FUNCTION,
VAL_ARRAY,
VAL_MAP,
VAL_NIL,
VAL_FLOAT
} ValueType;
typedef struct {
ValueType type;
union {
int64_t i;
double d;
char *s;
struct Bytecode *fn;
struct Array *arr;
struct Map *map;
};
ValueType type;
union {
int64_t i;
double d;
char *s;
struct Bytecode *fn;
struct Array *arr;
struct Map *map;
};
} Value;
/* constructors / helpers */
@ -77,46 +77,46 @@ 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 */
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 */
/* 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) */
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) */
/* copy/free */
Value copy_value(const Value *v); /* deep for strings, RC for arrays/maps, shallow fn */
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 */
void free_value(Value v); /* frees owned resources */
/* utilities */
void print_value(const Value *v);
int value_is_truthy(const Value *v);
int value_equals(const Value *a, const Value *b); /* int/string equality */
int value_equals(const Value *a, const Value *b); /* int/string equality */
/* stringify into 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 */
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 */
/* 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 */
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 */
#endif

1481
src/vm.c

File diff suppressed because it is too large Load diff

153
src/vm.h
View file

@ -20,94 +20,93 @@
#define STACK_SIZE 1024
static const char *opcode_names[] = {
"NOP","LOAD_CONST","LOAD_LOCAL","STORE_LOCAL",
"LOAD_GLOBAL","STORE_GLOBAL","ADD","SUB","MUL","DIV",
"LT","LTE","GT","GTE","EQ","NEQ","POP","JUMP",
"JUMP_IF_FALSE","CALL","RETURN","PRINT","ECHO","HALT",
"LINE",
"MOD","AND","OR","NOT","DUP","SWAP",
"MAKE_ARRAY","INDEX_GET","INDEX_SET",
"LEN","PUSH","APOP","SET","INSERT","REMOVE","SLICE",
"TO_NUMBER","TO_STRING","CAST","TYPEOF",
"SPLIT","JOIN","SUBSTR","FIND",
"REGEX_MATCH","REGEX_SEARCH","REGEX_REPLACE",
"CONTAINS","INDEX_OF","CLEAR",
"ENUMERATE","ZIP",
"MIN","MAX","CLAMP","ABS","POW","RANDOM_SEED","RANDOM_INT",
"MAKE_MAP","KEYS","VALUES","HAS_KEY",
"READ_FILE","WRITE_FILE","ENV","INPUT_LINE","PROC_RUN","PROC_SYSTEM",
"TIME_NOW_MS","CLOCK_MONO_MS","DATE_FORMAT",
"THREAD_SPAWN","THREAD_JOIN","SLEEP_MS",
"RANDOM_NUMBER",
"BAND","BOR","BXOR","BNOT","SHL","SHR","ROTL","ROTR",
"JSON_PARSE","JSON_STRINGIFY","JSON_FROM_FILE","JSON_TO_FILE",
"CURL_GET","CURL_POST","CURL_DOWNLOAD",
"SQLITE_OPEN","SQLITE_CLOSE","SQLITE_EXEC","SQLITE_QUERY",
"LIBSQL_OPEN","LIBSQL_CLOSE","LIBSQL_EXEC","LIBSQL_QUERY",
"PCSC_ESTABLISH","PCSC_RELEASE","PCSC_LIST_READERS","PCSC_CONNECT","PCSC_DISCONNECT","PCSC_TRANSMIT",
"PCRE2_TEST","PCRE2_MATCH","PCRE2_FINDALL",
"INI_LOAD","INI_FREE","INI_GET_STRING","INI_GET_INT","INI_GET_DOUBLE","INI_GET_BOOL","INI_SET","INI_UNSET","INI_SAVE",
"XML_PARSE","XML_ROOT","XML_NAME","XML_TEXT",
"SOCK_TCP_LISTEN","SOCK_TCP_ACCEPT","SOCK_TCP_CONNECT","SOCK_SEND","SOCK_RECV","SOCK_CLOSE","SOCK_UNIX_LISTEN","SOCK_UNIX_CONNECT",
"EXIT",
"OS_LIST_DIR",
"TK_BIND",
"SERIAL_OPEN","SERIAL_CONFIG","SERIAL_SEND","SERIAL_RECV","SERIAL_CLOSE",
"TK_EVAL","TK_RESULT","TK_LOOP","TK_WM_TITLE","TK_LABEL","TK_BUTTON","TK_PACK",
"TRY_PUSH","TRY_POP","THROW",
"FMIN","FMAX",
/* Rust FFI demo */
"RUST_HELLO","RUST_HELLO_ARGS","RUST_HELLO_ARGS_RETURN","RUST_GET_SP","RUST_SET_EXIT",
/* C++ demo */
"CPP_ADD",
/* Notcurses TUI (optional) */
"NC_INIT","NC_SHUTDOWN","NC_CLEAR","NC_DRAW_TEXT","NC_GETCH"
};
"NOP", "LOAD_CONST", "LOAD_LOCAL", "STORE_LOCAL",
"LOAD_GLOBAL", "STORE_GLOBAL", "ADD", "SUB", "MUL", "DIV",
"LT", "LTE", "GT", "GTE", "EQ", "NEQ", "POP", "JUMP",
"JUMP_IF_FALSE", "CALL", "RETURN", "PRINT", "ECHO", "HALT",
"LINE",
"MOD", "AND", "OR", "NOT", "DUP", "SWAP",
"MAKE_ARRAY", "INDEX_GET", "INDEX_SET",
"LEN", "PUSH", "APOP", "SET", "INSERT", "REMOVE", "SLICE",
"TO_NUMBER", "TO_STRING", "CAST", "TYPEOF",
"SPLIT", "JOIN", "SUBSTR", "FIND",
"REGEX_MATCH", "REGEX_SEARCH", "REGEX_REPLACE",
"CONTAINS", "INDEX_OF", "CLEAR",
"ENUMERATE", "ZIP",
"MIN", "MAX", "CLAMP", "ABS", "POW", "RANDOM_SEED", "RANDOM_INT",
"MAKE_MAP", "KEYS", "VALUES", "HAS_KEY",
"READ_FILE", "WRITE_FILE", "ENV", "INPUT_LINE", "PROC_RUN", "PROC_SYSTEM",
"TIME_NOW_MS", "CLOCK_MONO_MS", "DATE_FORMAT",
"THREAD_SPAWN", "THREAD_JOIN", "SLEEP_MS",
"RANDOM_NUMBER",
"BAND", "BOR", "BXOR", "BNOT", "SHL", "SHR", "ROTL", "ROTR",
"JSON_PARSE", "JSON_STRINGIFY", "JSON_FROM_FILE", "JSON_TO_FILE",
"CURL_GET", "CURL_POST", "CURL_DOWNLOAD",
"SQLITE_OPEN", "SQLITE_CLOSE", "SQLITE_EXEC", "SQLITE_QUERY",
"LIBSQL_OPEN", "LIBSQL_CLOSE", "LIBSQL_EXEC", "LIBSQL_QUERY",
"PCSC_ESTABLISH", "PCSC_RELEASE", "PCSC_LIST_READERS", "PCSC_CONNECT", "PCSC_DISCONNECT", "PCSC_TRANSMIT",
"PCRE2_TEST", "PCRE2_MATCH", "PCRE2_FINDALL",
"INI_LOAD", "INI_FREE", "INI_GET_STRING", "INI_GET_INT", "INI_GET_DOUBLE", "INI_GET_BOOL", "INI_SET", "INI_UNSET", "INI_SAVE",
"XML_PARSE", "XML_ROOT", "XML_NAME", "XML_TEXT",
"SOCK_TCP_LISTEN", "SOCK_TCP_ACCEPT", "SOCK_TCP_CONNECT", "SOCK_SEND", "SOCK_RECV", "SOCK_CLOSE", "SOCK_UNIX_LISTEN", "SOCK_UNIX_CONNECT",
"EXIT",
"OS_LIST_DIR",
"TK_BIND",
"SERIAL_OPEN", "SERIAL_CONFIG", "SERIAL_SEND", "SERIAL_RECV", "SERIAL_CLOSE",
"TK_EVAL", "TK_RESULT", "TK_LOOP", "TK_WM_TITLE", "TK_LABEL", "TK_BUTTON", "TK_PACK",
"TRY_PUSH", "TRY_POP", "THROW",
"FMIN", "FMAX",
/* Rust FFI demo */
"RUST_HELLO", "RUST_HELLO_ARGS", "RUST_HELLO_ARGS_RETURN", "RUST_GET_SP", "RUST_SET_EXIT",
/* C++ demo */
"CPP_ADD",
/* Notcurses TUI (optional) */
"NC_INIT", "NC_SHUTDOWN", "NC_CLEAR", "NC_DRAW_TEXT", "NC_GETCH"};
typedef struct {
Bytecode *fn;
int ip;
Value locals[MAX_FRAME_LOCALS];
/* exception handling (per-frame) */
int try_stack[16];
int try_sp; /* -1 when empty */
Bytecode *fn;
int ip;
Value locals[MAX_FRAME_LOCALS];
/* exception handling (per-frame) */
int try_stack[16];
int try_sp; /* -1 when empty */
} Frame;
struct VM {
Value stack[STACK_SIZE];
int sp;
Value stack[STACK_SIZE];
int sp;
Frame frames[MAX_FRAMES];
int fp; // frame pointer, -1 when no frame
Frame frames[MAX_FRAMES];
int fp; // frame pointer, -1 when no frame
Value globals[MAX_GLOBALS];
Value globals[MAX_GLOBALS];
Value output[OUTPUT_SIZE]; // store printed values
int output_count;
int output_is_partial[OUTPUT_SIZE]; // 1 when the corresponding output entry should not end with newline (echo)
Value output[OUTPUT_SIZE]; // store printed values
int output_count;
int output_is_partial[OUTPUT_SIZE]; // 1 when the corresponding output entry should not end with newline (echo)
long long instr_count; // executed instructions in the last vm_run
long long instr_count; // executed instructions in the last vm_run
int current_line; // last executed source line (debug)
int current_line; // last executed source line (debug)
int exit_code; // process exit code set by OP_EXIT
int exit_code; // process exit code set by OP_EXIT
int trace_enabled; // when non-zero, print executed ops and stack
int repl_on_error; // when non-zero, enter REPL on runtime error (preserve stack)
int (*on_error_repl)(struct VM *vm); // optional hook to run REPL on error
int trace_enabled; // when non-zero, print executed ops and stack
int repl_on_error; // when non-zero, enter REPL on runtime error (preserve stack)
int (*on_error_repl)(struct VM *vm); // optional hook to run REPL on error
/* --- Debugger state --- */
int debug_step_mode; // 0 none, 1 step, 2 next, 3 finish
int debug_step_target_fp; // target frame pointer for next/finish
long long debug_step_start_ic; // instruction count snapshot when step/next requested
int debug_stop_requested; // force a pause at loop top
/* --- Debugger state --- */
int debug_step_mode; // 0 none, 1 step, 2 next, 3 finish
int debug_step_target_fp; // target frame pointer for next/finish
long long debug_step_start_ic; // instruction count snapshot when step/next requested
int debug_stop_requested; // force a pause at loop top
struct {
char *file; // strdup'ed file path
int line; // 1-based line
int active; // 1 if active
} breakpoints[64];
int break_count; // number of active breakpoints
struct {
char *file; // strdup'ed file path
int line; // 1-based line
int active; // 1 if active
} breakpoints[64];
int break_count; // number of active breakpoints
};
typedef struct VM VM;
@ -136,8 +135,8 @@ void vm_raise_error(VM *vm, const char *msg);
/* --- Debugger API --- */
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
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
void vm_debug_clear_breakpoints(VM *vm);
void vm_debug_list_breakpoints(VM *vm);
void vm_debug_request_step(VM *vm);
@ -146,7 +145,7 @@ void vm_debug_request_finish(VM *vm);
void vm_debug_request_continue(VM *vm);
static inline int opcode_is_valid(int op) {
return op >= OP_NOP && op <= OP_NC_GETCH; // all current opcodes (including optional NC_*)
return op >= OP_NOP && op <= OP_NC_GETCH; // all current opcodes (including optional NC_*)
}
/* --- Minimal C ABI helpers for FFI (Rust opcode experiments) --- */

View file

@ -8,7 +8,7 @@
*/
/**
* @file add.c
* @file add.c
* @brief Implements the OP_ADD opcode for arithmetic and string concatenation in the VM.
*
* This file handles the OP_ADD instruction, which performs addition or concatenation
@ -36,50 +36,50 @@
*/
case OP_ADD: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da + db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
Value res = make_int(a.i + b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
} else if (a.type == VAL_STRING && b.type == VAL_STRING) {
const char *sa = a.s ? a.s : "";
const char *sb = b.s ? b.s : "";
size_t la = strlen(sa);
size_t lb = strlen(sb);
char *buf = (char*)malloc(la + lb + 1);
if (!buf) {
fprintf(stderr, "Runtime error: out of memory during string concatenation\n");
exit(1);
}
memcpy(buf, sa, la);
memcpy(buf + la, sb, lb);
buf[la + lb] = '\0';
Value res;
res.type = VAL_STRING;
res.s = buf;
free_value(a);
free_value(b);
push_value(vm, res);
} else if (a.type == VAL_ARRAY && b.type == VAL_ARRAY) {
Value res = array_concat(&a, &b);
free_value(a);
free_value(b);
push_value(vm, res);
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da + db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
fprintf(stderr, "Runtime type error: ADD expects both numbers, both strings, or both arrays, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
Value res = make_int(a.i + b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
break;
} else if (a.type == VAL_STRING && b.type == VAL_STRING) {
const char *sa = a.s ? a.s : "";
const char *sb = b.s ? b.s : "";
size_t la = strlen(sa);
size_t lb = strlen(sb);
char *buf = (char *)malloc(la + lb + 1);
if (!buf) {
fprintf(stderr, "Runtime error: out of memory during string concatenation\n");
exit(1);
}
memcpy(buf, sa, la);
memcpy(buf + la, sb, lb);
buf[la + lb] = '\0';
Value res;
res.type = VAL_STRING;
res.s = buf;
free_value(a);
free_value(b);
push_value(vm, res);
} else if (a.type == VAL_ARRAY && b.type == VAL_ARRAY) {
Value res = array_concat(&a, &b);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
fprintf(stderr, "Runtime type error: ADD expects both numbers, both strings, or both arrays, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file div.c
* @file div.c
* @brief Implements the OP_DIV opcode for integer division in the VM.
*
* This file handles the OP_DIV instruction, which performs integer division
@ -33,34 +33,34 @@
*/
case OP_DIV: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
if (db == 0.0) {
vm_raise_error(vm, "division by zero");
break;
}
Value res = make_float(da / db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
if (b.i == 0) {
vm_raise_error(vm, "division by zero");
break;
}
Value res = make_int(a.i / b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
if (db == 0.0) {
vm_raise_error(vm, "division by zero");
break;
}
Value res = make_float(da / db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
fprintf(stderr, "Runtime type error: DIV expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
if (b.i == 0) {
vm_raise_error(vm, "division by zero");
break;
}
Value res = make_int(a.i / b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
break;
} else {
fprintf(stderr, "Runtime type error: DIV expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file mul.c
* @file mul.c
* @brief Implements the OP_MUL opcode for integer multiplication in the VM.
*
* This file handles the OP_MUL instruction, which performs integer multiplication
@ -32,26 +32,26 @@
*/
case OP_MUL: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da * db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
Value res = make_int(a.i * b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da * db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
fprintf(stderr, "Runtime type error: MUL expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
Value res = make_int(a.i * b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
break;
} else {
fprintf(stderr, "Runtime type error: MUL expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file sub.c
* @file sub.c
* @brief Implements the OP_SUB opcode for integer subtraction in the VM.
*
* This file handles the OP_SUB instruction, which performs integer subtraction
@ -32,26 +32,26 @@
*/
case OP_SUB: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da - db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
Value res = make_int(a.i - b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da - db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
fprintf(stderr, "Runtime type error: SUB expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
Value res = make_int(a.i - b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
break;
} else {
fprintf(stderr, "Runtime type error: SUB expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file apop.c
* @file apop.c
* @brief Implements the OP_APOP opcode for removing elements from arrays in the VM.
*
* This file handles the OP_APOP instruction, which removes the last element from an array
@ -33,17 +33,17 @@
*/
case OP_APOP: {
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ARR_APOP expects array\n");
exit(1);
}
Value out;
if (!array_pop(&arr, &out)) {
fprintf(stderr, "Runtime error: pop from empty array\n");
exit(1);
}
free_value(arr);
push_value(vm, out);
break;
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ARR_APOP expects array\n");
exit(1);
}
Value out;
if (!array_pop(&arr, &out)) {
fprintf(stderr, "Runtime error: pop from empty array\n");
exit(1);
}
free_value(arr);
push_value(vm, out);
break;
}

View file

@ -32,13 +32,13 @@
*/
case OP_CLEAR: {
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: CLEAR expects array\n");
exit(1);
}
array_clear(&arr);
free_value(arr);
push_value(vm, make_int(0));
break;
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: CLEAR expects array\n");
exit(1);
}
array_clear(&arr);
free_value(arr);
push_value(vm, make_int(0));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file contains.c
* @file contains.c
* @brief Implements the OP_CONTAINS opcode for checking array membership in the VM.
*
* This file handles the OP_CONTAINS instruction, which checks if a value is present in an array.
@ -32,15 +32,15 @@
*/
case OP_CONTAINS: {
Value needle = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: CONTAINS expects (array, value)\n");
exit(1);
}
int ok = array_contains(&arr, &needle);
free_value(arr);
free_value(needle);
push_value(vm, make_int(ok ? 1 : 0));
break;
Value needle = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: CONTAINS expects (array, value)\n");
exit(1);
}
int ok = array_contains(&arr, &needle);
free_value(arr);
free_value(needle);
push_value(vm, make_int(ok ? 1 : 0));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file enumerate.c
* @file enumerate.c
* @brief Implements the OP_ENUMERATE opcode for enumerating arrays in the VM.
*
* This file handles the OP_ENUMERATE instruction, which creates an array of [index, value] pairs
@ -32,13 +32,13 @@
*/
case OP_ENUMERATE: {
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ENUMERATE expects array\n");
exit(1);
}
Value out = bi_enumerate(&arr);
free_value(arr);
push_value(vm, out);
break;
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ENUMERATE expects array\n");
exit(1);
}
Value out = bi_enumerate(&arr);
free_value(arr);
push_value(vm, out);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file index_get.c
* @file index_get.c
* @brief Implements the OP_INDEX_GET opcode for array and map indexing in the VM.
*
* This file handles the OP_INDEX_GET instruction, which retrieves an element from
@ -34,34 +34,41 @@
*/
case OP_INDEX_GET: {
Value idx = pop_value(vm);
Value container = pop_value(vm);
Value idx = pop_value(vm);
Value container = pop_value(vm);
#ifdef FUN_DEBUG
fprintf(stderr, "DEBUG INDEX_GET: container.type=%d idx.type=%d\n",
container.type, idx.type);
fprintf(stderr, "DEBUG INDEX_GET: container.type=%d idx.type=%d\n",
container.type, idx.type);
#endif
if (container.type == VAL_ARRAY) {
if (idx.type != VAL_INT) { fprintf(stderr, "INDEX_GET index must be int for array\n"); exit(1); }
Value elem;
if (!array_get_copy(&container, (int)idx.i, &elem)) {
fprintf(stderr, "Runtime error: index out of range\n"); exit(1);
}
free_value(container);
free_value(idx);
push_value(vm, elem);
} else if (container.type == VAL_MAP) {
if (idx.type != VAL_STRING) { fprintf(stderr, "INDEX_GET key must be string for map\n"); exit(1); }
Value out;
if (!map_get_copy(&container, idx.s ? idx.s : "", &out)) {
out = make_nil();
}
free_value(container);
free_value(idx);
push_value(vm, out);
} else {
fprintf(stderr, "Runtime type error: INDEX_GET expects array or map (got container=%s, index=%s)\n",
value_type_name(container.type), value_type_name(idx.type));
exit(1);
if (container.type == VAL_ARRAY) {
if (idx.type != VAL_INT) {
fprintf(stderr, "INDEX_GET index must be int for array\n");
exit(1);
}
break;
Value elem;
if (!array_get_copy(&container, (int)idx.i, &elem)) {
fprintf(stderr, "Runtime error: index out of range\n");
exit(1);
}
free_value(container);
free_value(idx);
push_value(vm, elem);
} else if (container.type == VAL_MAP) {
if (idx.type != VAL_STRING) {
fprintf(stderr, "INDEX_GET key must be string for map\n");
exit(1);
}
Value out;
if (!map_get_copy(&container, idx.s ? idx.s : "", &out)) {
out = make_nil();
}
free_value(container);
free_value(idx);
push_value(vm, out);
} else {
fprintf(stderr, "Runtime type error: INDEX_GET expects array or map (got container=%s, index=%s)\n",
value_type_name(container.type), value_type_name(idx.type));
exit(1);
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file index_of.c
* @file index_of.c
* @brief Implements the OP_INDEX_OF opcode for finding the index of a value in an array in the VM.
*
* This file handles the OP_INDEX_OF instruction, which finds the index of a value in an array.
@ -32,15 +32,15 @@
*/
case OP_INDEX_OF: {
Value needle = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: INDEX_OF expects (array, value)\n");
exit(1);
}
int idx = array_index_of(&arr, &needle);
free_value(arr);
free_value(needle);
push_value(vm, make_int(idx));
break;
Value needle = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: INDEX_OF expects (array, value)\n");
exit(1);
}
int idx = array_index_of(&arr, &needle);
free_value(arr);
free_value(needle);
push_value(vm, make_int(idx));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file index_set.c
* @file index_set.c
* @brief Implements the OP_INDEX_SET opcode for array and map assignment in the VM.
*
* This file handles the OP_INDEX_SET instruction, which assigns a value to an
@ -32,32 +32,39 @@
* @date 2025-10-16
*/
case OP_INDEX_SET: {
Value v = pop_value(vm);
Value idx = pop_value(vm);
Value container = pop_value(vm);
Value v = pop_value(vm);
Value idx = pop_value(vm);
Value container = pop_value(vm);
#ifdef FUN_DEBUG
fprintf(stderr, "DEBUG INDEX_SET: container.type=%d idx.type=%d value.type=%d\n",
container.type, idx.type, v.type);
fprintf(stderr, "DEBUG INDEX_SET: container.type=%d idx.type=%d value.type=%d\n",
container.type, idx.type, v.type);
#endif
if (container.type == VAL_ARRAY) {
if (idx.type != VAL_INT) { fprintf(stderr, "INDEX_SET index must be int for array\n"); exit(1); }
if (!array_set(&container, (int)idx.i, v)) {
fprintf(stderr, "Runtime error: index out of range\n"); exit(1);
}
free_value(container);
free_value(idx);
} else if (container.type == VAL_MAP) {
if (idx.type != VAL_STRING) { fprintf(stderr, "INDEX_SET key must be string for map\n"); exit(1); }
if (!map_set(&container, idx.s ? idx.s : "", v)) {
fprintf(stderr, "Runtime error: map set failed\n"); exit(1);
}
free_value(container);
free_value(idx);
} else {
fprintf(stderr, "Runtime type error: INDEX_SET expects array or map\n");
exit(1);
if (container.type == VAL_ARRAY) {
if (idx.type != VAL_INT) {
fprintf(stderr, "INDEX_SET index must be int for array\n");
exit(1);
}
break;
if (!array_set(&container, (int)idx.i, v)) {
fprintf(stderr, "Runtime error: index out of range\n");
exit(1);
}
free_value(container);
free_value(idx);
} else if (container.type == VAL_MAP) {
if (idx.type != VAL_STRING) {
fprintf(stderr, "INDEX_SET key must be string for map\n");
exit(1);
}
if (!map_set(&container, idx.s ? idx.s : "", v)) {
fprintf(stderr, "Runtime error: map set failed\n");
exit(1);
}
free_value(container);
free_value(idx);
} else {
fprintf(stderr, "Runtime type error: INDEX_SET expects array or map\n");
exit(1);
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file insert.c
* @file insert.c
* @brief Implements the OP_INSERT opcode for inserting elements into arrays in the VM.
*
* This file handles the OP_INSERT instruction, which inserts a value into an array
@ -34,20 +34,20 @@
*/
case OP_INSERT: {
Value v = pop_value(vm);
Value idx = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || idx.type != VAL_INT) {
fprintf(stderr, "Runtime type error: ARR_INSERT expects (array, int, value)\n");
exit(1);
}
int n = array_insert(&arr, (int)idx.i, v);
if (n < 0) {
fprintf(stderr, "Runtime error: insert failed (OOM?)\n");
exit(1);
}
free_value(arr);
free_value(idx);
push_value(vm, make_int(n));
break;
Value v = pop_value(vm);
Value idx = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || idx.type != VAL_INT) {
fprintf(stderr, "Runtime type error: ARR_INSERT expects (array, int, value)\n");
exit(1);
}
int n = array_insert(&arr, (int)idx.i, v);
if (n < 0) {
fprintf(stderr, "Runtime error: insert failed (OOM?)\n");
exit(1);
}
free_value(arr);
free_value(idx);
push_value(vm, make_int(n));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file join.c
* @file join.c
* @brief Implements the OP_JOIN opcode for joining array elements into a string in the VM.
*
* This file handles the OP_JOIN instruction, which joins the elements of an array into a string
@ -33,15 +33,15 @@
*/
case OP_JOIN: {
Value sep = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || sep.type != VAL_STRING) {
fprintf(stderr, "Runtime type error: JOIN expects (array, string)\n");
exit(1);
}
Value out = bi_join(&arr, &sep);
free_value(arr);
free_value(sep);
push_value(vm, out);
break;
Value sep = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || sep.type != VAL_STRING) {
fprintf(stderr, "Runtime type error: JOIN expects (array, string)\n");
exit(1);
}
Value out = bi_join(&arr, &sep);
free_value(arr);
free_value(sep);
push_value(vm, out);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file make_array.c
* @file make_array.c
* @brief Implements the OP_MAKE_ARRAY opcode for creating arrays in the VM.
*
* This file handles the OP_MAKE_ARRAY instruction, which pops `n` values from the stack,
@ -33,23 +33,26 @@
*/
case OP_MAKE_ARRAY: {
int n = inst.operand;
if (n < 0 || vm->sp + 1 < n) {
fprintf(stderr, "Runtime error: invalid element count for MAKE_ARRAY\n");
exit(1);
}
/* pop n values into temp array preserving original order */
Value *vals = (Value*)malloc(sizeof(Value) * n);
if (!vals) { fprintf(stderr, "Runtime error: OOM in MAKE_ARRAY\n"); exit(1); }
for (int i = n - 1; i >= 0; --i) {
vals[i] = pop_value(vm); /* take ownership */
}
/* build array by copying values, then free originals */
Value arr = make_array_from_values(vals, n);
for (int i = 0; i < n; ++i) {
free_value(vals[i]);
}
free(vals);
push_value(vm, arr);
break;
int n = inst.operand;
if (n < 0 || vm->sp + 1 < n) {
fprintf(stderr, "Runtime error: invalid element count for MAKE_ARRAY\n");
exit(1);
}
/* pop n values into temp array preserving original order */
Value *vals = (Value *)malloc(sizeof(Value) * n);
if (!vals) {
fprintf(stderr, "Runtime error: OOM in MAKE_ARRAY\n");
exit(1);
}
for (int i = n - 1; i >= 0; --i) {
vals[i] = pop_value(vm); /* take ownership */
}
/* build array by copying values, then free originals */
Value arr = make_array_from_values(vals, n);
for (int i = 0; i < n; ++i) {
free_value(vals[i]);
}
free(vals);
push_value(vm, arr);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file arr_push.c
* @file arr_push.c
* @brief Implements the OP_ARR_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.
@ -33,18 +33,18 @@
*/
case OP_PUSH: {
Value v = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ARR_PUSH expects array\n");
exit(1);
}
int n = array_push(&arr, v);
if (n < 0) {
fprintf(stderr, "Runtime error: push failed (OOM?)\n");
exit(1);
}
free_value(arr);
push_value(vm, make_int(n));
break;
Value v = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ARR_PUSH expects array\n");
exit(1);
}
int n = array_push(&arr, v);
if (n < 0) {
fprintf(stderr, "Runtime error: push failed (OOM?)\n");
exit(1);
}
free_value(arr);
push_value(vm, make_int(n));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file arr_remove.c
* @file arr_remove.c
* @brief Implements the OP_ARR_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
@ -34,19 +34,19 @@
*/
case OP_REMOVE: {
Value idx = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || idx.type != VAL_INT) {
fprintf(stderr, "Runtime type error: ARR_REMOVE expects (array, int)\n");
exit(1);
}
Value out;
if (!array_remove(&arr, (int)idx.i, &out)) {
fprintf(stderr, "Runtime error: remove index out of range\n");
exit(1);
}
free_value(arr);
free_value(idx);
push_value(vm, out);
break;
Value idx = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || idx.type != VAL_INT) {
fprintf(stderr, "Runtime type error: ARR_REMOVE expects (array, int)\n");
exit(1);
}
Value out;
if (!array_remove(&arr, (int)idx.i, &out)) {
fprintf(stderr, "Runtime error: remove index out of range\n");
exit(1);
}
free_value(arr);
free_value(idx);
push_value(vm, out);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file arr_set.c
* @file arr_set.c
* @brief Implements the OP_ARR_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.
@ -33,21 +33,21 @@
*/
case OP_SET: {
Value v = pop_value(vm);
Value idx = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || idx.type != VAL_INT) {
fprintf(stderr, "Runtime type error: ARR_SET expects (array, int, value)\n");
exit(1);
}
if (!array_set(&arr, (int)idx.i, v)) {
fprintf(stderr, "Runtime error: set index out of range\n");
exit(1);
}
free_value(arr);
free_value(idx);
/* v already owned by array; push copy for return value */
push_value(vm, copy_value(&v));
free_value(v);
break;
Value v = pop_value(vm);
Value idx = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || idx.type != VAL_INT) {
fprintf(stderr, "Runtime type error: ARR_SET expects (array, int, value)\n");
exit(1);
}
if (!array_set(&arr, (int)idx.i, v)) {
fprintf(stderr, "Runtime error: set index out of range\n");
exit(1);
}
free_value(arr);
free_value(idx);
/* v already owned by array; push copy for return value */
push_value(vm, copy_value(&v));
free_value(v);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file slice.c
* @file slice.c
* @brief Implements the OP_SLICE opcode for array slicing in the VM.
*
* This file handles the OP_SLICE instruction, which creates a new array containing
@ -33,17 +33,17 @@
*/
case OP_SLICE: {
Value end = pop_value(vm);
Value start = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || start.type != VAL_INT || end.type != VAL_INT) {
fprintf(stderr, "Runtime type error: SLICE expects (array, int, int)\n");
exit(1);
}
Value out = array_slice(&arr, (int)start.i, (int)end.i);
free_value(arr);
free_value(start);
free_value(end);
push_value(vm, out);
break;
Value end = pop_value(vm);
Value start = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || start.type != VAL_INT || end.type != VAL_INT) {
fprintf(stderr, "Runtime type error: SLICE expects (array, int, int)\n");
exit(1);
}
Value out = array_slice(&arr, (int)start.i, (int)end.i);
free_value(arr);
free_value(start);
free_value(end);
push_value(vm, out);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file zip.c
* @file zip.c
* @brief Implements the OP_ZIP opcode for array zipping in the VM.
*
* This file handles the OP_ZIP instruction, which combines two arrays into
@ -33,15 +33,15 @@
*/
case OP_ZIP: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_ARRAY || b.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ZIP expects (array, array)\n");
exit(1);
}
Value out = bi_zip(&a, &b);
free_value(a);
free_value(b);
push_value(vm, out);
break;
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_ARRAY || b.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ZIP expects (array, array)\n");
exit(1);
}
Value out = bi_zip(&a, &b);
free_value(a);
free_value(b);
push_value(vm, out);
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,13 +15,13 @@
* pushes: (uint32_t)(a & b)
*/
case OP_BAND: {
Value vb = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
uint32_t r = a & b;
free_value(vb);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vb = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
uint32_t r = a & b;
free_value(vb);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,10 +15,10 @@
* pushes: (uint32_t)(~a)
*/
case OP_BNOT: {
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t r = ~a;
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t r = ~a;
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,13 +15,13 @@
* pushes: (uint32_t)(a | b)
*/
case OP_BOR: {
Value vb = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
uint32_t r = a | b;
free_value(vb);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vb = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
uint32_t r = a | b;
free_value(vb);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,13 +15,13 @@
* pushes: (uint32_t)(a ^ b)
*/
case OP_BXOR: {
Value vb = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
uint32_t r = a ^ b;
free_value(vb);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vb = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
uint32_t r = a ^ b;
free_value(vb);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,14 +15,14 @@
* pushes: rotl32(a, s)
*/
case OP_ROTL: {
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : ((a << s) | (a >> (32u - s)));
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : ((a << s) | (a >> (32u - s)));
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,14 +15,14 @@
* pushes: rotr32(a, s)
*/
case OP_ROTR: {
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : ((a >> s) | (a << (32u - s)));
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : ((a >> s) | (a << (32u - s)));
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,14 +15,14 @@
* pushes: (uint32_t)(a << (s&31))
*/
case OP_SHL: {
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : (a << s);
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : (a << s);
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,14 +15,14 @@
* pushes: (uint32_t)(a >> (s&31)) using logical shift
*/
case OP_SHR: {
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : (a >> s);
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : (a >> s);
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -17,73 +17,77 @@
*/
case OP_CAST: {
/* pop type then value (args pushed in this order: value, typeName) */
Value t = pop_value(vm);
Value v = pop_value(vm);
/* pop type then value (args pushed in this order: value, typeName) */
Value t = pop_value(vm);
Value v = pop_value(vm);
const char *tn = (t.type == VAL_STRING && t.s) ? t.s : NULL;
Value out = make_nil();
const char *tn = (t.type == VAL_STRING && t.s) ? t.s : NULL;
Value out = make_nil();
/* Normalize target name to lowercase into a small buffer */
char target[32];
int k = 0;
if (tn) {
const char *p = tn;
while (*p && k < (int)sizeof(target) - 1) {
char c = *p++;
if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a');
target[k++] = c;
}
/* Normalize target name to lowercase into a small buffer */
char target[32];
int k = 0;
if (tn) {
const char *p = tn;
while (*p && k < (int)sizeof(target) - 1) {
char c = *p++;
if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a');
target[k++] = c;
}
target[k] = '\0';
}
target[k] = '\0';
if (!tn) {
out = make_nil();
} else if (strcmp(target, "number") == 0) {
if (v.type == VAL_INT) {
out = make_int(v.i);
} else if (v.type == VAL_STRING) {
const char *s = v.s ? v.s : "";
const char *p = s;
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++;
char *endp = NULL;
long long parsed = strtoll(p, &endp, 10);
while (endp && (*endp == ' ' || *endp == '\t' || *endp == '\r' || *endp == '\n')) endp++;
if (endp && *endp != '\0') out = make_int(0);
else out = make_int((int64_t)parsed);
} else {
out = make_int(0);
}
} else if (strcmp(target, "string") == 0) {
char *s = value_to_string_alloc(&v);
out = make_string(s ? s : "");
if (s) free(s);
} else if (strcmp(target, "array") == 0) {
if (v.type == VAL_ARRAY) {
out = copy_value(&v);
} else {
Value tmp = deep_copy_value(&v);
out = make_array_from_values(&tmp, 1);
free_value(tmp);
}
} else if (strcmp(target, "map") == 0) {
if (v.type == VAL_MAP) {
out = copy_value(&v);
} else {
out = make_map_empty();
}
} else if (strcmp(target, "nil") == 0) {
out = make_nil();
} else if (strcmp(target, "function") == 0) {
out = (v.type == VAL_FUNCTION) ? copy_value(&v) : make_nil();
} else if (strcmp(target, "boolean") == 0) {
out = make_int(value_is_truthy(&v) ? 1 : 0);
if (!tn) {
out = make_nil();
} else if (strcmp(target, "number") == 0) {
if (v.type == VAL_INT) {
out = make_int(v.i);
} else if (v.type == VAL_STRING) {
const char *s = v.s ? v.s : "";
const char *p = s;
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
p++;
char *endp = NULL;
long long parsed = strtoll(p, &endp, 10);
while (endp && (*endp == ' ' || *endp == '\t' || *endp == '\r' || *endp == '\n'))
endp++;
if (endp && *endp != '\0')
out = make_int(0);
else
out = make_int((int64_t)parsed);
} else {
out = make_nil();
out = make_int(0);
}
} else if (strcmp(target, "string") == 0) {
char *s = value_to_string_alloc(&v);
out = make_string(s ? s : "");
if (s) free(s);
} else if (strcmp(target, "array") == 0) {
if (v.type == VAL_ARRAY) {
out = copy_value(&v);
} else {
Value tmp = deep_copy_value(&v);
out = make_array_from_values(&tmp, 1);
free_value(tmp);
}
} else if (strcmp(target, "map") == 0) {
if (v.type == VAL_MAP) {
out = copy_value(&v);
} else {
out = make_map_empty();
}
} else if (strcmp(target, "nil") == 0) {
out = make_nil();
} else if (strcmp(target, "function") == 0) {
out = (v.type == VAL_FUNCTION) ? copy_value(&v) : make_nil();
} else if (strcmp(target, "boolean") == 0) {
out = make_int(value_is_truthy(&v) ? 1 : 0);
} else {
out = make_nil();
}
free_value(t);
free_value(v);
push_value(vm, out);
break;
free_value(t);
free_value(v);
push_value(vm, out);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file call.c
* @file call.c
* @brief Implements the OP_CALL opcode for function calls in the VM.
*
* This file handles the OP_CALL instruction, which calls a function with arguments.
@ -28,27 +28,27 @@
*/
case OP_CALL: {
int argc = inst.operand;
if (argc < 0) argc = 0;
/* collect args in reverse (preserve order) */
Value *args = NULL;
if (argc > 0) {
args = (Value*)malloc(sizeof(Value) * argc);
/* pop args into array in reverse */
for (int i = argc - 1; i >= 0; --i) {
args[i] = pop_value(vm);
}
int argc = inst.operand;
if (argc < 0) argc = 0;
/* collect args in reverse (preserve order) */
Value *args = NULL;
if (argc > 0) {
args = (Value *)malloc(sizeof(Value) * argc);
/* pop args into array in reverse */
for (int i = argc - 1; i >= 0; --i) {
args[i] = pop_value(vm);
}
/* pop function value */
Value fnv = pop_value(vm);
if (fnv.type != VAL_FUNCTION) {
fprintf(stderr, "Runtime type error: CALL expects function\n");
exit(1);
}
/* push new frame and transfer args */
vm_push_frame(vm, fnv.fn, argc, args);
/* free args array (locals moved), free fnv (no-op for function) */
free(args);
/* note: fnv contains a pointer to the Bytecode, don't free here */
break;
}
/* pop function value */
Value fnv = pop_value(vm);
if (fnv.type != VAL_FUNCTION) {
fprintf(stderr, "Runtime type error: CALL expects function\n");
exit(1);
}
/* push new frame and transfer args */
vm_push_frame(vm, fnv.fn, argc, args);
/* free args array (locals moved), free fnv (no-op for function) */
free(args);
/* note: fnv contains a pointer to the Bytecode, don't free here */
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file dup.c
* @file dup.c
* @brief Implements the OP_DUP opcode for duplicating the top stack value in the VM.
*
* This file handles the OP_DUP instruction, which duplicates the top value on the stack.
@ -25,17 +25,17 @@
* // Bytecode: OP_DUP
* // Stack before: [42]
* // Stack after: [42, 42]
*
*
* @author Johannes Findeisen
* @date 2025-10-16
*/
case OP_DUP: {
if (vm->sp < 0) {
fprintf(stderr, "Runtime error: stack underflow for DUP\n");
exit(1);
}
Value top = vm->stack[vm->sp];
push_value(vm, copy_value(&top));
break;
if (vm->sp < 0) {
fprintf(stderr, "Runtime error: stack underflow for DUP\n");
exit(1);
}
Value top = vm->stack[vm->sp];
push_value(vm, copy_value(&top));
break;
}

View file

@ -10,7 +10,7 @@
*/
/**
* @file exit.c
* @file exit.c
* @brief Implements the OP_EXIT opcode to terminate the script with an exit code.
*
* Behavior:
@ -20,22 +20,22 @@
*/
case OP_EXIT: {
int code = 0;
if (vm->sp >= 0) {
Value v = pop_value(vm);
if (v.type == VAL_INT) {
code = (int)v.i;
} else if (v.type == VAL_STRING) {
/* best-effort parse number from string */
code = (int)strtoll(v.s, NULL, 10);
} else if (v.type == VAL_NIL) {
code = 0;
} else {
/* unsupported type for exit; default to 0 */
code = 0;
}
free_value(v);
int code = 0;
if (vm->sp >= 0) {
Value v = pop_value(vm);
if (v.type == VAL_INT) {
code = (int)v.i;
} else if (v.type == VAL_STRING) {
/* best-effort parse number from string */
code = (int)strtoll(v.s, NULL, 10);
} else if (v.type == VAL_NIL) {
code = 0;
} else {
/* unsupported type for exit; default to 0 */
code = 0;
}
vm->exit_code = code;
return;
free_value(v);
}
vm->exit_code = code;
return;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file halt.c
* @file halt.c
* @brief Implements the OP_HALT opcode for stopping VM execution.
*
* This file handles the OP_HALT instruction, which stops the execution of the VM.
@ -27,4 +27,4 @@
*/
case OP_HALT:
return;
return;

View file

@ -8,7 +8,7 @@
*/
/**
* @file jump.c
* @file jump.c
* @brief Implements the OP_JUMP opcode for unconditional jumps in the VM.
*
* This file handles the OP_JUMP instruction, which performs an unconditional
@ -28,6 +28,6 @@
*/
case OP_JUMP: {
f->ip = inst.operand;
break;
f->ip = inst.operand;
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file jump_if_false.c
* @file jump_if_false.c
* @brief Implements the OP_JUMP_IF_FALSE opcode for conditional jumps in the VM.
*
* This file handles the OP_JUMP_IF_FALSE instruction, which jumps if the top
@ -29,11 +29,11 @@
*/
case OP_JUMP_IF_FALSE: {
Value cond = pop_value(vm);
int truthy = value_is_truthy(&cond);
free_value(cond);
if (!truthy) {
f->ip = inst.operand;
}
break;
Value cond = pop_value(vm);
int truthy = value_is_truthy(&cond);
free_value(cond);
if (!truthy) {
f->ip = inst.operand;
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file load_const.c
* @file load_const.c
* @brief Implements the OP_LOAD_CONST opcode for loading constants in the VM.
*
* This file handles the OP_LOAD_CONST instruction, which loads a constant value
@ -26,12 +26,12 @@
*/
case OP_LOAD_CONST: {
int idx = inst.operand;
if (idx < 0 || idx >= f->fn->const_count) {
fprintf(stderr, "Runtime error: constant index out of range\n");
exit(1);
}
Value c = copy_value(&f->fn->constants[idx]);
push_value(vm, c);
break;
int idx = inst.operand;
if (idx < 0 || idx >= f->fn->const_count) {
fprintf(stderr, "Runtime error: constant index out of range\n");
exit(1);
}
Value c = copy_value(&f->fn->constants[idx]);
push_value(vm, c);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file load_global.c
* @file load_global.c
* @brief Implements the OP_LOAD_GLOBAL opcode for loading global variables in the VM.
*
* This file handles the OP_LOAD_GLOBAL instruction, which loads a global variable
@ -31,14 +31,14 @@
*/
case OP_LOAD_GLOBAL: {
int idx = inst.operand;
if (idx < 0 || idx >= MAX_GLOBALS) {
fprintf(stderr, "Runtime error: global index out of range\n");
exit(1);
}
int idx = inst.operand;
if (idx < 0 || idx >= MAX_GLOBALS) {
fprintf(stderr, "Runtime error: global index out of range\n");
exit(1);
}
#ifdef FUN_DEBUG
fprintf(stderr, "DEBUG LOAD_GLOBAL[%d]: type=%d\n", idx, vm->globals[idx].type);
fprintf(stderr, "DEBUG LOAD_GLOBAL[%d]: type=%d\n", idx, vm->globals[idx].type);
#endif
push_value(vm, copy_value(&vm->globals[idx]));
break;
push_value(vm, copy_value(&vm->globals[idx]));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file load_local.c
* @file load_local.c
* @brief Implements the OP_LOAD_LOCAL opcode for loading local variables in the VM.
*
* This file handles the OP_LOAD_LOCAL instruction, which loads a local variable
@ -31,12 +31,12 @@
*/
case OP_LOAD_LOCAL: {
int slot = inst.operand;
if (slot < 0 || slot >= MAX_FRAME_LOCALS) {
fprintf(stderr, "Runtime error: local slot out of range\n");
exit(1);
}
Value val = copy_value(&f->locals[slot]);
push_value(vm, val);
break;
int slot = inst.operand;
if (slot < 0 || slot >= MAX_FRAME_LOCALS) {
fprintf(stderr, "Runtime error: local slot out of range\n");
exit(1);
}
Value val = copy_value(&f->locals[slot]);
push_value(vm, val);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file nop.c
* @file nop.c
* @brief Implements the OP_NOP opcode for no operation in the VM.
*
* This file handles the OP_NOP instruction, which performs no operation.
@ -27,4 +27,4 @@
*/
case OP_NOP:
break;
break;

View file

@ -8,7 +8,7 @@
*/
/**
* @file pop.c
* @file pop.c
* @brief Implements the OP_POP opcode for removing the top stack value in the VM.
*
* This file handles the OP_POP instruction, which removes the top value from the stack.
@ -29,11 +29,11 @@
*/
case OP_POP: {
if (vm->sp < 0) {
fprintf(stderr, "Runtime error: stack underflow for POP\n");
exit(1);
}
Value v = pop_value(vm);
free_value(v);
break;
if (vm->sp < 0) {
fprintf(stderr, "Runtime error: stack underflow for POP\n");
exit(1);
}
Value v = pop_value(vm);
free_value(v);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file return.c
* @file return.c
* @brief Implements the OP_RETURN opcode for returning from a function in the VM.
*
* This file handles the OP_RETURN instruction, which returns from the current function
@ -32,10 +32,12 @@
*/
case OP_RETURN: {
Value retv;
if (vm->sp >= 0) retv = pop_value(vm);
else retv = make_nil();
vm_pop_frame(vm);
push_value(vm, retv);
break;
Value retv;
if (vm->sp >= 0)
retv = pop_value(vm);
else
retv = make_nil();
vm_pop_frame(vm);
push_value(vm, retv);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file store_global.c
* @file store_global.c
* @brief Implements the OP_STORE_GLOBAL opcode for storing global variables in the VM.
*
* This file handles the OP_STORE_GLOBAL instruction, which stores a value into a global variable
@ -31,16 +31,16 @@
*/
case OP_STORE_GLOBAL: {
int idx = inst.operand;
if (idx < 0 || idx >= MAX_GLOBALS) {
fprintf(stderr, "Runtime error: global index out of range\n");
exit(1);
}
Value v = pop_value(vm);
int idx = inst.operand;
if (idx < 0 || idx >= MAX_GLOBALS) {
fprintf(stderr, "Runtime error: global index out of range\n");
exit(1);
}
Value v = pop_value(vm);
#ifdef FUN_DEBUG
fprintf(stderr, "DEBUG STORE_GLOBAL[%d]: new.type=%d\n", idx, v.type);
fprintf(stderr, "DEBUG STORE_GLOBAL[%d]: new.type=%d\n", idx, v.type);
#endif
free_value(vm->globals[idx]);
vm->globals[idx] = v;
break;
free_value(vm->globals[idx]);
vm->globals[idx] = v;
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file store_local.c
* @file store_local.c
* @brief Implements the OP_STORE_LOCAL opcode for storing local variables in the VM.
*
* This file handles the OP_STORE_LOCAL instruction, which stores a value into a local variable
@ -31,14 +31,14 @@
*/
case OP_STORE_LOCAL: {
int slot = inst.operand;
if (slot < 0 || slot >= MAX_FRAME_LOCALS) {
fprintf(stderr, "Runtime error: local slot out of range\n");
exit(1);
}
Value v = pop_value(vm);
/* free previous local then move v into it */
free_value(f->locals[slot]);
f->locals[slot] = v;
break;
int slot = inst.operand;
if (slot < 0 || slot >= MAX_FRAME_LOCALS) {
fprintf(stderr, "Runtime error: local slot out of range\n");
exit(1);
}
Value v = pop_value(vm);
/* free previous local then move v into it */
free_value(f->locals[slot]);
f->locals[slot] = v;
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file swap.c
* @file swap.c
* @brief Implements the OP_SWAP opcode for stack manipulation in the VM.
*
* This file handles the OP_SWAP instruction, which swaps the top two values
@ -26,13 +26,13 @@
*/
case OP_SWAP: {
if (vm->sp < 1) {
fprintf(stderr, "Runtime error: stack underflow for SWAP\n");
exit(1);
}
Value a = vm->stack[vm->sp];
Value b = vm->stack[vm->sp - 1];
vm->stack[vm->sp] = b;
vm->stack[vm->sp - 1] = a;
break;
if (vm->sp < 1) {
fprintf(stderr, "Runtime error: stack underflow for SWAP\n");
exit(1);
}
Value a = vm->stack[vm->sp];
Value b = vm->stack[vm->sp - 1];
vm->stack[vm->sp] = b;
vm->stack[vm->sp - 1] = a;
break;
}

View file

@ -8,26 +8,26 @@
*/
case OP_THROW: {
Value err = pop_value(vm);
/* if there is a handler in this frame, jump to it and push err for catch */
if (f->try_sp >= 0) {
int try_idx = f->try_stack[f->try_sp--];
int target = f->fn->instructions[try_idx].operand;
/* push error for catch block */
push_value(vm, err); /* transfer ownership to stack */
f->ip = target;
break;
}
/* Unhandled: print error and terminate */
char *s = value_to_string_alloc(&err);
if (s) {
fprintf(stdout, "%s\n", s);
free(s);
} else {
fprintf(stdout, "<error>\n");
}
free_value(err);
/* clear frames to stop execution */
vm->fp = -1;
Value err = pop_value(vm);
/* if there is a handler in this frame, jump to it and push err for catch */
if (f->try_sp >= 0) {
int try_idx = f->try_stack[f->try_sp--];
int target = f->fn->instructions[try_idx].operand;
/* push error for catch block */
push_value(vm, err); /* transfer ownership to stack */
f->ip = target;
break;
}
/* Unhandled: print error and terminate */
char *s = value_to_string_alloc(&err);
if (s) {
fprintf(stdout, "%s\n", s);
free(s);
} else {
fprintf(stdout, "<error>\n");
}
free_value(err);
/* clear frames to stop execution */
vm->fp = -1;
break;
}

View file

@ -8,6 +8,6 @@
*/
case OP_TRY_POP: {
if (f->try_sp >= 0) f->try_sp--;
break;
if (f->try_sp >= 0) f->try_sp--;
break;
}

View file

@ -8,11 +8,11 @@
*/
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) {
fprintf(stderr, "Runtime error: try depth exceeded\n");
exit(1);
}
f->try_stack[++f->try_sp] = f->ip - 1; /* index of TRY_PUSH instruction */
break;
/* 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) {
fprintf(stderr, "Runtime error: try depth exceeded\n");
exit(1);
}
f->try_stack[++f->try_sp] = f->ip - 1; /* index of TRY_PUSH instruction */
break;
}

View file

@ -1,5 +1,5 @@
/*
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
@ -21,8 +21,8 @@ extern "C" {
}
extern "C" int fun_op_cpp_add(VM *vm) {
int64_t a = vm_pop_i64(vm);
int64_t b = vm_pop_i64(vm);
vm_push_i64(vm, a + b);
return 0; // success
int64_t a = vm_pop_i64(vm);
int64_t b = vm_pop_i64(vm);
vm_push_i64(vm, a + b);
return 0; // success
}

View file

@ -3,45 +3,53 @@
*/
case OP_CURL_DOWNLOAD: {
#ifdef FUN_WITH_CURL
Value vpath = pop_value(vm);
Value vurl = pop_value(vm);
char *url = value_to_string_alloc(&vurl);
char *path = value_to_string_alloc(&vpath);
free_value(vurl);
free_value(vpath);
if (!url || !path) {
if (url) free(url);
if (path) free(path);
push_value(vm, make_int(0));
break;
}
FILE *fp = fopen(path, "wb");
if (!fp) {
free(url); free(path);
push_value(vm, make_int(0));
break;
}
CURL *h = curl_easy_init();
if (!h) {
fclose(fp);
free(url); free(path);
push_value(vm, make_int(0));
break;
}
curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_file_write_cb);
curl_easy_setopt(h, CURLOPT_WRITEDATA, fp);
CURLcode rc = curl_easy_perform(h);
curl_easy_cleanup(h);
fclose(fp);
free(url); free(path);
if (rc != CURLE_OK) { push_value(vm, make_int(0)); break; }
push_value(vm, make_int(1));
#else
Value a = pop_value(vm); free_value(a);
Value b = pop_value(vm); free_value(b);
Value vpath = pop_value(vm);
Value vurl = pop_value(vm);
char *url = value_to_string_alloc(&vurl);
char *path = value_to_string_alloc(&vpath);
free_value(vurl);
free_value(vpath);
if (!url || !path) {
if (url) free(url);
if (path) free(path);
push_value(vm, make_int(0));
#endif
break;
}
FILE *fp = fopen(path, "wb");
if (!fp) {
free(url);
free(path);
push_value(vm, make_int(0));
break;
}
CURL *h = curl_easy_init();
if (!h) {
fclose(fp);
free(url);
free(path);
push_value(vm, make_int(0));
break;
}
curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_file_write_cb);
curl_easy_setopt(h, CURLOPT_WRITEDATA, fp);
CURLcode rc = curl_easy_perform(h);
curl_easy_cleanup(h);
fclose(fp);
free(url);
free(path);
if (rc != CURLE_OK) {
push_value(vm, make_int(0));
break;
}
push_value(vm, make_int(1));
#else
Value a = pop_value(vm);
free_value(a);
Value b = pop_value(vm);
free_value(b);
push_value(vm, make_int(0));
#endif
break;
}

View file

@ -3,31 +3,39 @@
*/
case OP_CURL_GET: {
#ifdef FUN_WITH_CURL
Value vurl = pop_value(vm);
char *url = value_to_string_alloc(&vurl);
free_value(vurl);
if (!url) { push_value(vm, make_string("")); break; }
FunCurlBuf buf = { NULL, 0 };
CURL *h = curl_easy_init();
if (!h) { free(url); push_value(vm, make_string("")); break; }
curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_write_cb);
curl_easy_setopt(h, CURLOPT_WRITEDATA, &buf);
CURLcode rc = curl_easy_perform(h);
curl_easy_cleanup(h);
free(url);
if (rc != CURLE_OK) {
if (buf.d) free(buf.d);
push_value(vm, make_string(""));
break;
}
Value s = make_string(buf.d ? buf.d : "");
if (buf.d) free(buf.d);
push_value(vm, s);
#else
Value v = pop_value(vm); free_value(v);
Value vurl = pop_value(vm);
char *url = value_to_string_alloc(&vurl);
free_value(vurl);
if (!url) {
push_value(vm, make_string(""));
#endif
break;
}
FunCurlBuf buf = {NULL, 0};
CURL *h = curl_easy_init();
if (!h) {
free(url);
push_value(vm, make_string(""));
break;
}
curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_write_cb);
curl_easy_setopt(h, CURLOPT_WRITEDATA, &buf);
CURLcode rc = curl_easy_perform(h);
curl_easy_cleanup(h);
free(url);
if (rc != CURLE_OK) {
if (buf.d) free(buf.d);
push_value(vm, make_string(""));
break;
}
Value s = make_string(buf.d ? buf.d : "");
if (buf.d) free(buf.d);
push_value(vm, s);
#else
Value v = pop_value(vm);
free_value(v);
push_value(vm, make_string(""));
#endif
break;
}

View file

@ -3,39 +3,50 @@
*/
case OP_CURL_POST: {
#ifdef FUN_WITH_CURL
Value vbody = pop_value(vm);
Value vurl = pop_value(vm);
char *url = value_to_string_alloc(&vurl);
char *body = value_to_string_alloc(&vbody);
free_value(vurl);
free_value(vbody);
if (!url) { if (body) free(body); push_value(vm, make_string("")); break; }
if (!body) body = strdup("");
FunCurlBuf buf = { NULL, 0 };
CURL *h = curl_easy_init();
if (!h) { free(url); free(body); push_value(vm, make_string("")); break; }
curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_POST, 1L);
curl_easy_setopt(h, CURLOPT_POSTFIELDS, body);
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_write_cb);
curl_easy_setopt(h, CURLOPT_WRITEDATA, &buf);
CURLcode rc = curl_easy_perform(h);
curl_easy_cleanup(h);
Value vbody = pop_value(vm);
Value vurl = pop_value(vm);
char *url = value_to_string_alloc(&vurl);
char *body = value_to_string_alloc(&vbody);
free_value(vurl);
free_value(vbody);
if (!url) {
if (body) free(body);
push_value(vm, make_string(""));
break;
}
if (!body) body = strdup("");
FunCurlBuf buf = {NULL, 0};
CURL *h = curl_easy_init();
if (!h) {
free(url);
free(body);
if (rc != CURLE_OK) {
if (buf.d) free(buf.d);
push_value(vm, make_string(""));
break;
}
Value s = make_string(buf.d ? buf.d : "");
if (buf.d) free(buf.d);
push_value(vm, s);
#else
Value a = pop_value(vm); free_value(a);
Value b = pop_value(vm); free_value(b);
push_value(vm, make_string(""));
#endif
break;
}
curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_POST, 1L);
curl_easy_setopt(h, CURLOPT_POSTFIELDS, body);
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_write_cb);
curl_easy_setopt(h, CURLOPT_WRITEDATA, &buf);
CURLcode rc = curl_easy_perform(h);
curl_easy_cleanup(h);
free(url);
free(body);
if (rc != CURLE_OK) {
if (buf.d) free(buf.d);
push_value(vm, make_string(""));
break;
}
Value s = make_string(buf.d ? buf.d : "");
if (buf.d) free(buf.d);
push_value(vm, s);
#else
Value a = pop_value(vm);
free_value(a);
Value b = pop_value(vm);
free_value(b);
push_value(vm, make_string(""));
#endif
break;
}

View file

@ -1,5 +1,5 @@
/*
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -7,25 +7,25 @@
* https://opensource.org/license/apache-2-0
*/
/**
/**
* 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.
*/
case OP_ECHO: {
Value v = pop_value(vm);
Value snap = deep_copy_value(&v);
free_value(v);
if (vm->output_count < OUTPUT_SIZE) {
int idx = vm->output_count;
vm->output[idx] = snap;
vm->output_is_partial[idx] = 1; // ECHO does not end the line
vm->output_count++;
} else {
free_value(snap);
fprintf(stderr, "Runtime error: output buffer overflow\n");
exit(1);
}
break;
Value v = pop_value(vm);
Value snap = deep_copy_value(&v);
free_value(v);
if (vm->output_count < OUTPUT_SIZE) {
int idx = vm->output_count;
vm->output[idx] = snap;
vm->output_is_partial[idx] = 1; // ECHO does not end the line
vm->output_count++;
} else {
free_value(snap);
fprintf(stderr, "Runtime error: output buffer overflow\n");
exit(1);
}
break;
}

View file

@ -12,11 +12,11 @@
/* OP_INI_FREE: pops handle; pushes 1/0 */
#ifdef FUN_WITH_INI
case OP_INI_FREE: {
Value vh = pop_value(vm);
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
free_value(vh);
int ok = ini_free_handle(h);
push_value(vm, make_int(ok));
break;
Value vh = pop_value(vm);
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
free_value(vh);
int ok = ini_free_handle(h);
push_value(vm, make_int(ok));
break;
}
#endif

View file

@ -12,51 +12,67 @@
/* OP_INI_GET_BOOL */
#ifdef FUN_WITH_INI
case OP_INI_GET_BOOL: {
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
int def = (vdef.type==VAL_INT||vdef.type==VAL_BOOL) ? (int)vdef.i : 0;
const char *key = (vkey.type==VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type==VAL_STRING) ? vsec.s : NULL;
int h = (vh.type==VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
int outb = def;
if (d && sec && key) {
char full[1024]; char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } }
const char *s = iniparser_getstring(d, full, NULL);
if (!s) s = iniparser_getstring(d, alt, NULL);
if (s) {
/* normalize and parse boolean */
char buf[256];
size_t n = strlen(s);
if (n >= 2 && ((s[0]=='"' && s[n-1]=='"') || (s[0]=='\'' && s[n-1]=='\''))) {
size_t copy = (n-2) < sizeof(buf)-1 ? (n-2) : sizeof(buf)-1;
memcpy(buf, s+1, copy); buf[copy] = '\0';
s = buf;
}
/* trim spaces */
while (*s && (unsigned char)*s <= ' ') s++;
/* lower copy for textual booleans */
char lb[256]; size_t li=0; for (; s[li] && li < sizeof(lb)-1; ++li) lb[li] = (char)tolower((unsigned char)s[li]); lb[li]='\0';
if (strcmp(lb, "true")==0 || strcmp(lb, "yes")==0 || strcmp(lb, "on")==0) {
outb = 1;
} else if (strcmp(lb, "false")==0 || strcmp(lb, "no")==0 || strcmp(lb, "off")==0) {
outb = 0;
} else {
/* numeric */
char *endp=NULL; long v = strtol(lb, &endp, 10);
outb = (endp && endp!=lb) ? (v!=0) : def;
}
} else {
outb = def;
}
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
int def = (vdef.type == VAL_INT || vdef.type == VAL_BOOL) ? (int)vdef.i : 0;
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
int outb = def;
if (d && sec && key) {
char full[1024];
char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) {
if (alt[i] == ':') {
alt[i] = '.';
break;
}
}
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
push_value(vm, make_int(outb ? 1 : 0));
break;
const char *s = iniparser_getstring(d, full, NULL);
if (!s) s = iniparser_getstring(d, alt, NULL);
if (s) {
/* normalize and parse boolean */
char buf[256];
size_t n = strlen(s);
if (n >= 2 && ((s[0] == '"' && s[n - 1] == '"') || (s[0] == '\'' && s[n - 1] == '\''))) {
size_t copy = (n - 2) < sizeof(buf) - 1 ? (n - 2) : sizeof(buf) - 1;
memcpy(buf, s + 1, copy);
buf[copy] = '\0';
s = buf;
}
/* trim spaces */
while (*s && (unsigned char)*s <= ' ')
s++;
/* lower copy for textual booleans */
char lb[256];
size_t li = 0;
for (; s[li] && li < sizeof(lb) - 1; ++li)
lb[li] = (char)tolower((unsigned char)s[li]);
lb[li] = '\0';
if (strcmp(lb, "true") == 0 || strcmp(lb, "yes") == 0 || strcmp(lb, "on") == 0) {
outb = 1;
} else if (strcmp(lb, "false") == 0 || strcmp(lb, "no") == 0 || strcmp(lb, "off") == 0) {
outb = 0;
} else {
/* numeric */
char *endp = NULL;
long v = strtol(lb, &endp, 10);
outb = (endp && endp != lb) ? (v != 0) : def;
}
} else {
outb = def;
}
}
free_value(vdef);
free_value(vkey);
free_value(vsec);
free_value(vh);
push_value(vm, make_int(outb ? 1 : 0));
break;
}
#endif

View file

@ -12,41 +12,55 @@
/* OP_INI_GET_DOUBLE */
#ifdef FUN_WITH_INI
case OP_INI_GET_DOUBLE: {
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
double def = (vdef.type==VAL_FLOAT) ? vdef.d : (vdef.type==VAL_INT ? (double)vdef.i : 0.0);
const char *key = (vkey.type==VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type==VAL_STRING) ? vsec.s : NULL;
int h = (vh.type==VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
double outd = def;
if (d && sec && key) {
char full[1024]; char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } }
const char *s = iniparser_getstring(d, full, NULL);
if (!s) s = iniparser_getstring(d, alt, NULL);
if (s) {
char buf[256];
size_t n = strlen(s);
if (n >= 2 && ((s[0]=='"' && s[n-1]=='"') || (s[0]=='\'' && s[n-1]=='\''))) {
size_t copy = (n-2) < sizeof(buf)-1 ? (n-2) : sizeof(buf)-1;
memcpy(buf, s+1, copy); buf[copy] = '\0';
s = buf;
}
while (*s && (unsigned char)*s <= ' ') s++;
char *endp = NULL;
double v = strtod(s, &endp);
if (endp && endp != s) outd = v; else outd = def;
} else {
outd = def;
}
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
double def = (vdef.type == VAL_FLOAT) ? vdef.d : (vdef.type == VAL_INT ? (double)vdef.i : 0.0);
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
double outd = def;
if (d && sec && key) {
char full[1024];
char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) {
if (alt[i] == ':') {
alt[i] = '.';
break;
}
}
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
push_value(vm, make_float(outd));
break;
const char *s = iniparser_getstring(d, full, NULL);
if (!s) s = iniparser_getstring(d, alt, NULL);
if (s) {
char buf[256];
size_t n = strlen(s);
if (n >= 2 && ((s[0] == '"' && s[n - 1] == '"') || (s[0] == '\'' && s[n - 1] == '\''))) {
size_t copy = (n - 2) < sizeof(buf) - 1 ? (n - 2) : sizeof(buf) - 1;
memcpy(buf, s + 1, copy);
buf[copy] = '\0';
s = buf;
}
while (*s && (unsigned char)*s <= ' ')
s++;
char *endp = NULL;
double v = strtod(s, &endp);
if (endp && endp != s)
outd = v;
else
outd = def;
} else {
outd = def;
}
}
free_value(vdef);
free_value(vkey);
free_value(vsec);
free_value(vh);
push_value(vm, make_float(outd));
break;
}
#endif

View file

@ -12,43 +12,57 @@
/* OP_INI_GET_INT */
#ifdef FUN_WITH_INI
case OP_INI_GET_INT: {
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
int def = (vdef.type==VAL_INT) ? (int)vdef.i : 0;
const char *key = (vkey.type==VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type==VAL_STRING) ? vsec.s : NULL;
int h = (vh.type==VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
int outi = def;
if (d && sec && key) {
char full[1024]; char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } }
const char *s = iniparser_getstring(d, full, NULL);
if (!s) s = iniparser_getstring(d, alt, NULL);
if (s) {
/* strip optional quotes and parse */
char buf[256];
size_t n = strlen(s);
if (n >= 2 && ((s[0]=='"' && s[n-1]=='"') || (s[0]=='\'' && s[n-1]=='\''))) {
size_t copy = (n-2) < sizeof(buf)-1 ? (n-2) : sizeof(buf)-1;
memcpy(buf, s+1, copy); buf[copy] = '\0';
s = buf;
}
/* skip leading spaces */
while (*s && (unsigned char)*s <= ' ') s++;
char *endp = NULL;
long v = strtol(s, &endp, 10);
if (endp && endp != s) outi = (int)v; else outi = def;
} else {
outi = def;
}
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
int def = (vdef.type == VAL_INT) ? (int)vdef.i : 0;
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
int outi = def;
if (d && sec && key) {
char full[1024];
char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) {
if (alt[i] == ':') {
alt[i] = '.';
break;
}
}
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
push_value(vm, make_int(outi));
break;
const char *s = iniparser_getstring(d, full, NULL);
if (!s) s = iniparser_getstring(d, alt, NULL);
if (s) {
/* strip optional quotes and parse */
char buf[256];
size_t n = strlen(s);
if (n >= 2 && ((s[0] == '"' && s[n - 1] == '"') || (s[0] == '\'' && s[n - 1] == '\''))) {
size_t copy = (n - 2) < sizeof(buf) - 1 ? (n - 2) : sizeof(buf) - 1;
memcpy(buf, s + 1, copy);
buf[copy] = '\0';
s = buf;
}
/* skip leading spaces */
while (*s && (unsigned char)*s <= ' ')
s++;
char *endp = NULL;
long v = strtol(s, &endp, 10);
if (endp && endp != s)
outi = (int)v;
else
outi = def;
} else {
outi = def;
}
}
free_value(vdef);
free_value(vkey);
free_value(vsec);
free_value(vh);
push_value(vm, make_int(outi));
break;
}
#endif

View file

@ -12,34 +12,42 @@
/* OP_INI_GET_STRING */
#ifdef FUN_WITH_INI
case OP_INI_GET_STRING: {
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
const char *def = (vdef.type==VAL_STRING && vdef.s) ? vdef.s : "";
const char *key = (vkey.type==VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type==VAL_STRING) ? vsec.s : NULL;
int h = (vh.type==VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
const char *res = def;
if (d && sec && key) {
char full[1024]; char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
/* Build alternate with dot separator for robustness */
ini_make_full_key(alt, sizeof(alt), sec, key);
size_t flen = strlen(full);
if (flen < sizeof(alt) && flen > 0) { /* create dot version in alt */
memcpy(alt, full, flen + 1);
for (size_t i = 0; i < flen; ++i) if (alt[i] == ':') { alt[i] = '.'; break; }
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
const char *def = (vdef.type == VAL_STRING && vdef.s) ? vdef.s : "";
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
const char *res = def;
if (d && sec && key) {
char full[1024];
char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
/* Build alternate with dot separator for robustness */
ini_make_full_key(alt, sizeof(alt), sec, key);
size_t flen = strlen(full);
if (flen < sizeof(alt) && flen > 0) { /* create dot version in alt */
memcpy(alt, full, flen + 1);
for (size_t i = 0; i < flen; ++i)
if (alt[i] == ':') {
alt[i] = '.';
break;
}
const char *s = iniparser_getstring(d, full, def);
if (s == def) { /* not found, try alternate dot form */
s = iniparser_getstring(d, alt, def);
}
res = s ? s : "";
}
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
push_value(vm, make_string(res));
break;
const char *s = iniparser_getstring(d, full, def);
if (s == def) { /* not found, try alternate dot form */
s = iniparser_getstring(d, alt, def);
}
res = s ? s : "";
}
free_value(vdef);
free_value(vkey);
free_value(vsec);
free_value(vh);
push_value(vm, make_string(res));
break;
}
#endif

View file

@ -9,55 +9,59 @@
#ifdef FUN_WITH_INI
#if defined(__has_include)
# if __has_include(<iniparser/iniparser.h>)
# include <iniparser/iniparser.h>
# include <iniparser/dictionary.h>
# elif __has_include(<iniparser.h>)
# include <iniparser.h>
# include <dictionary.h>
# else
# error "iniparser headers not found"
# endif
#if __has_include(<iniparser/iniparser.h>)
#include <iniparser/dictionary.h>
#include <iniparser/iniparser.h>
#elif __has_include(<iniparser.h>)
#include <dictionary.h>
#include <iniparser.h>
#else
# include <iniparser/iniparser.h>
# include <iniparser/dictionary.h>
#error "iniparser headers not found"
#endif
#else
#include <iniparser/dictionary.h>
#include <iniparser/iniparser.h>
#endif
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <string.h>
#include "handles.h"
IniSlot g_ini[64];
int ini_alloc_handle(dictionary *d) {
if (!d) return 0;
for (int i = 1; i < (int)(sizeof(g_ini)/sizeof(g_ini[0])); ++i) {
if (!g_ini[i].in_use) { g_ini[i].in_use = 1; g_ini[i].dict = d; return i; }
if (!d) return 0;
for (int i = 1; i < (int)(sizeof(g_ini) / sizeof(g_ini[0])); ++i) {
if (!g_ini[i].in_use) {
g_ini[i].in_use = 1;
g_ini[i].dict = d;
return i;
}
return 0;
}
return 0;
}
dictionary* ini_get(int h) {
if (h > 0 && h < (int)(sizeof(g_ini)/sizeof(g_ini[0])) && g_ini[h].in_use) return g_ini[h].dict;
return NULL;
dictionary *ini_get(int h) {
if (h > 0 && h < (int)(sizeof(g_ini) / sizeof(g_ini[0])) && g_ini[h].in_use) return g_ini[h].dict;
return NULL;
}
int ini_free_handle(int h) {
if (h <= 0 || h >= (int)(sizeof(g_ini)/sizeof(g_ini[0])) || !g_ini[h].in_use) return 0;
if (g_ini[h].dict) iniparser_freedict(g_ini[h].dict);
g_ini[h].dict = NULL;
g_ini[h].in_use = 0;
return 1;
if (h <= 0 || h >= (int)(sizeof(g_ini) / sizeof(g_ini[0])) || !g_ini[h].in_use) return 0;
if (g_ini[h].dict) iniparser_freedict(g_ini[h].dict);
g_ini[h].dict = NULL;
g_ini[h].in_use = 0;
return 1;
}
void ini_make_full_key(char *buf, size_t cap, const char *sec, const char *key) {
if (!buf || cap == 0) return;
if (!sec) sec = "";
if (!key) key = "";
/* iniparser expects section:key; lookup is case-insensitive internally */
snprintf(buf, cap, "%s:%s", sec, key);
if (!buf || cap == 0) return;
if (!sec) sec = "";
if (!key) key = "";
/* iniparser expects section:key; lookup is case-insensitive internally */
snprintf(buf, cap, "%s:%s", sec, key);
}
#endif /* FUN_WITH_INI */

View file

@ -14,29 +14,32 @@
#ifdef FUN_WITH_INI
#if defined(__has_include)
# if __has_include(<iniparser/iniparser.h>)
# include <iniparser/iniparser.h>
# include <iniparser/dictionary.h>
# elif __has_include(<iniparser.h>)
# include <iniparser.h>
# include <dictionary.h>
# else
# error "iniparser headers not found"
# endif
#if __has_include(<iniparser/iniparser.h>)
#include <iniparser/dictionary.h>
#include <iniparser/iniparser.h>
#elif __has_include(<iniparser.h>)
#include <dictionary.h>
#include <iniparser.h>
#else
# include <iniparser/iniparser.h>
# include <iniparser/dictionary.h>
#error "iniparser headers not found"
#endif
#else
#include <iniparser/dictionary.h>
#include <iniparser/iniparser.h>
#endif
#include <stddef.h>
typedef struct { dictionary *dict; int in_use; } IniSlot;
typedef struct {
dictionary *dict;
int in_use;
} IniSlot;
/* Single global registry (defined in handles.c) */
extern IniSlot g_ini[64];
/* Registry API (implemented in handles.c) */
int ini_alloc_handle(dictionary *d);
dictionary* ini_get(int h);
dictionary *ini_get(int h);
int ini_free_handle(int h);
/* Helper to build section:key string safely into provided buffer (implemented in handles.c) */

View file

@ -12,18 +12,20 @@
/* OP_INI_LOAD: pops path string; pushes handle (>0) or 0 */
#ifdef FUN_WITH_INI
case OP_INI_LOAD: {
Value vpath = pop_value(vm);
const char *path = (vpath.type == VAL_STRING && vpath.s) ? vpath.s : NULL;
int h = 0;
if (path) {
dictionary *d = iniparser_load(path);
if (d) {
h = ini_alloc_handle(d);
if (!h) { iniparser_freedict(d); }
}
Value vpath = pop_value(vm);
const char *path = (vpath.type == VAL_STRING && vpath.s) ? vpath.s : NULL;
int h = 0;
if (path) {
dictionary *d = iniparser_load(path);
if (d) {
h = ini_alloc_handle(d);
if (!h) {
iniparser_freedict(d);
}
}
free_value(vpath);
push_value(vm, make_int(h));
break;
}
free_value(vpath);
push_value(vm, make_int(h));
break;
}
#endif

View file

@ -12,17 +12,22 @@
/* OP_INI_SAVE */
#ifdef FUN_WITH_INI
case OP_INI_SAVE: {
Value vpath = pop_value(vm);
Value vh = pop_value(vm);
const char *path = (vpath.type==VAL_STRING)?vpath.s:NULL;
dictionary *d = ini_get((vh.type==VAL_INT)?(int)vh.i:0);
int ok = 0;
if (d && path) {
FILE *f = fopen(path, "w");
if (f) { iniparser_dump_ini(d, f); fclose(f); ok = 1; }
Value vpath = pop_value(vm);
Value vh = pop_value(vm);
const char *path = (vpath.type == VAL_STRING) ? vpath.s : NULL;
dictionary *d = ini_get((vh.type == VAL_INT) ? (int)vh.i : 0);
int ok = 0;
if (d && path) {
FILE *f = fopen(path, "w");
if (f) {
iniparser_dump_ini(d, f);
fclose(f);
ok = 1;
}
free_value(vpath); free_value(vh);
push_value(vm, make_int(ok));
break;
}
free_value(vpath);
free_value(vh);
push_value(vm, make_int(ok));
break;
}
#endif

Some files were not shown because too many files have changed in this diff Show more