What is CPP? Holy shit... (0.38.4)
This commit is contained in:
parent
d39ef1a58e
commit
d79815e366
10 changed files with 158 additions and 4 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(fun VERSION 0.38.3 LANGUAGES C)
|
project(fun VERSION 0.38.4 LANGUAGES C)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
|
@ -19,14 +19,21 @@ else()
|
||||||
set(_FUN_DEBUG_STATE "DISABLED")
|
set(_FUN_DEBUG_STATE "DISABLED")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(FUN_WITH_RUST)
|
if(FUN_WITH_CPP)
|
||||||
set(_FUN_WITH_RUST_STATE "ENABLED")
|
set(_FUN_WITH_CPP_STATE "ENABLED")
|
||||||
else()
|
else()
|
||||||
set(_FUN_WITH_RUST_STATE "DISABLED")
|
set(_FUN_WITH_CPP_STATE "DISABLED")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(FUN_WITH_RUST)
|
||||||
|
set(_FUN_WITH_RUST_STATE "ENABLED")
|
||||||
|
else()
|
||||||
|
set(_FUN_WITH_RUST_STATE "DISABLED")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS "==== Fun build options ====")
|
message(STATUS "==== Fun build options ====")
|
||||||
message(STATUS " FUN_DEBUG: ${_FUN_DEBUG_STATE}")
|
message(STATUS " FUN_DEBUG: ${_FUN_DEBUG_STATE}")
|
||||||
|
message(STATUS " FUN_WITH_CPP: ${_FUN_WITH_CPP_STATE}")
|
||||||
message(STATUS " FUN_WITH_RUST: ${_FUN_WITH_RUST_STATE}")
|
message(STATUS " FUN_WITH_RUST: ${_FUN_WITH_RUST_STATE}")
|
||||||
message(STATUS "===========================")
|
message(STATUS "===========================")
|
||||||
|
|
||||||
|
|
@ -98,6 +105,36 @@ if(FUN_WITH_RUST)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# --- Optional C++ opcode demo ---
|
||||||
|
option(FUN_WITH_CPP "Build and link C++ opcode demo" OFF)
|
||||||
|
if(FUN_WITH_CPP)
|
||||||
|
enable_language(CXX)
|
||||||
|
|
||||||
|
add_library(fun_cpp_ops STATIC
|
||||||
|
src/vm/cpp/add.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Compile definitions propagate to C targets that dispatch the opcode
|
||||||
|
target_compile_definitions(fun_cpp_ops PUBLIC FUN_WITH_CPP)
|
||||||
|
target_include_directories(fun_cpp_ops PUBLIC ${CMAKE_SOURCE_DIR}/src)
|
||||||
|
|
||||||
|
# Link the C++ ops into the core so executables can call them
|
||||||
|
if(TARGET fun_core)
|
||||||
|
target_link_libraries(fun_core PRIVATE fun_cpp_ops)
|
||||||
|
target_compile_definitions(fun_core PRIVATE FUN_WITH_CPP)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Also propagate to test targets (if they might use it)
|
||||||
|
if(TARGET test_opcodes)
|
||||||
|
target_link_libraries(test_opcodes PRIVATE fun_cpp_ops)
|
||||||
|
target_compile_definitions(test_opcodes PRIVATE FUN_WITH_CPP)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(TARGET fun)
|
||||||
|
target_compile_definitions(fun PRIVATE FUN_WITH_CPP)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# --- Size optimization for final binaries (Release) ---
|
# --- Size optimization for final binaries (Release) ---
|
||||||
# Enable function/data sectioning for better GC; safe for all configs.
|
# Enable function/data sectioning for better GC; safe for all configs.
|
||||||
add_compile_options(-ffunction-sections -fdata-sections)
|
add_compile_options(-ffunction-sections -fdata-sections)
|
||||||
|
|
|
||||||
28
examples/cpp_add.fun
Executable file
28
examples/cpp_add.fun
Executable file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Build:
|
||||||
|
cmake -S . -B build_debug -DFUN_WITH_CPP=ON
|
||||||
|
cmake --build build_debug --target fun
|
||||||
|
Run:
|
||||||
|
build_debug/fun examples/cpp_add.fun
|
||||||
|
*/
|
||||||
|
|
||||||
|
number x = cpp_add(2, 40)
|
||||||
|
//print("typeof(x): " + typeof(x))
|
||||||
|
print(x)
|
||||||
|
|
||||||
|
/* Expected output:
|
||||||
|
42
|
||||||
|
*/
|
||||||
0
examples/rust_hello_args_return.fun
Normal file → Executable file
0
examples/rust_hello_args_return.fun
Normal file → Executable file
2
examples/rust_vm_access.fun
Normal file → Executable file
2
examples/rust_vm_access.fun
Normal file → Executable file
|
|
@ -23,7 +23,7 @@ print("[Rust VM access demo]")
|
||||||
print("vm.sp (from Rust) = " + to_string(rust_get_sp()))
|
print("vm.sp (from Rust) = " + to_string(rust_get_sp()))
|
||||||
|
|
||||||
/* Write vm.exit_code via Rust (not directly observable without VM inspection) */
|
/* Write vm.exit_code via Rust (not directly observable without VM inspection) */
|
||||||
print("Setting vm.exit_code to 5 via Rust...")
|
print("Setting vm.exit_code to 42 via Rust...")
|
||||||
rust_set_exit(42)
|
rust_set_exit(42)
|
||||||
|
|
||||||
print("Now exiting...")
|
print("Now exiting...")
|
||||||
|
|
|
||||||
27
make
27
make
|
|
@ -31,6 +31,7 @@ if [ "$target" = "all" ]; then
|
||||||
-DFUN_WITH_TCLTK=ON \
|
-DFUN_WITH_TCLTK=ON \
|
||||||
-DFUN_WITH_INI=ON \
|
-DFUN_WITH_INI=ON \
|
||||||
-DFUN_WITH_NOTCURSES=ON \
|
-DFUN_WITH_NOTCURSES=ON \
|
||||||
|
-DFUN_WITH_CPP=ON \
|
||||||
&& cmake --build build --target fun
|
&& cmake --build build --target fun
|
||||||
elif [ "$target" = "all_debug" ]; then
|
elif [ "$target" = "all_debug" ]; then
|
||||||
rm -rf build \
|
rm -rf build \
|
||||||
|
|
@ -46,6 +47,7 @@ elif [ "$target" = "all_debug" ]; then
|
||||||
-DFUN_WITH_TCLTK=ON \
|
-DFUN_WITH_TCLTK=ON \
|
||||||
-DFUN_WITH_INI=ON \
|
-DFUN_WITH_INI=ON \
|
||||||
-DFUN_WITH_NOTCURSES=ON \
|
-DFUN_WITH_NOTCURSES=ON \
|
||||||
|
-DFUN_WITH_CPP=ON \
|
||||||
-DFUN_DEBUG=ON \
|
-DFUN_DEBUG=ON \
|
||||||
&& cmake --build build --target fun
|
&& cmake --build build --target fun
|
||||||
elif [ "$target" = "alpine" ]; then
|
elif [ "$target" = "alpine" ]; then
|
||||||
|
|
@ -59,7 +61,29 @@ elif [ "$target" = "alpine" ]; then
|
||||||
-DFUN_WITH_XML2=ON \
|
-DFUN_WITH_XML2=ON \
|
||||||
-DFUN_WITH_JSON=ON \
|
-DFUN_WITH_JSON=ON \
|
||||||
-DFUN_WITH_INI=ON \
|
-DFUN_WITH_INI=ON \
|
||||||
|
-DFUN_WITH_CPP=ON \
|
||||||
&& cmake --build build --target fun
|
&& cmake --build build --target fun
|
||||||
|
elif [ "$target" = "cpp_all" ]; then
|
||||||
|
rm -rf build \
|
||||||
|
&& cmake -S . -B build \
|
||||||
|
-DFUN_WITH_PCSC=ON \
|
||||||
|
-DFUN_WITH_REPL=ON \
|
||||||
|
-DFUN_WITH_LIBSQL=ON \
|
||||||
|
-DFUN_WITH_SQLITE=ON \
|
||||||
|
-DFUN_WITH_CURL=ON \
|
||||||
|
-DFUN_WITH_PCRE2=ON \
|
||||||
|
-DFUN_WITH_XML2=ON \
|
||||||
|
-DFUN_WITH_JSON=ON \
|
||||||
|
-DFUN_WITH_TCLTK=ON \
|
||||||
|
-DFUN_WITH_INI=ON \
|
||||||
|
-DFUN_WITH_NOTCURSES=ON \
|
||||||
|
-DFUN_WITH_CPP=ON \
|
||||||
|
&& cmake --build build --target fun
|
||||||
|
elif [ "$target" = "cpp_minimal" ]; then
|
||||||
|
rm -rf build \
|
||||||
|
&& cmake -S . -B build \
|
||||||
|
-DFUN_WITH_CPP=ON \
|
||||||
|
&& cmake --build build --target fun
|
||||||
elif [ "$target" = "debug" ]; then
|
elif [ "$target" = "debug" ]; then
|
||||||
rm -rf build \
|
rm -rf build \
|
||||||
&& cmake -S . -B build \
|
&& cmake -S . -B build \
|
||||||
|
|
@ -102,6 +126,7 @@ elif [ "$target" = "rust_all" ]; then
|
||||||
-DFUN_WITH_TCLTK=ON \
|
-DFUN_WITH_TCLTK=ON \
|
||||||
-DFUN_WITH_INI=ON \
|
-DFUN_WITH_INI=ON \
|
||||||
-DFUN_WITH_RUST=ON \
|
-DFUN_WITH_RUST=ON \
|
||||||
|
-DFUN_WITH_CPP=ON \
|
||||||
-DFUN_DEBUG=OFF \
|
-DFUN_DEBUG=OFF \
|
||||||
&& cmake --build build --target fun
|
&& cmake --build build --target fun
|
||||||
elif [ "$target" = "rust_minimal" ]; then
|
elif [ "$target" = "rust_minimal" ]; then
|
||||||
|
|
@ -115,6 +140,8 @@ else
|
||||||
echo " - all";
|
echo " - all";
|
||||||
echo " - all_debug";
|
echo " - all_debug";
|
||||||
echo " - alpine";
|
echo " - alpine";
|
||||||
|
echo " - cpp_all";
|
||||||
|
echo " - cpp_minimal";
|
||||||
echo " - debug";
|
echo " - debug";
|
||||||
echo " - freebsd";
|
echo " - freebsd";
|
||||||
echo " - install";
|
echo " - install";
|
||||||
|
|
|
||||||
|
|
@ -270,6 +270,9 @@ typedef enum {
|
||||||
OP_RUST_GET_SP, // pushes current VM stack pointer (via Rust reading VM memory)
|
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)
|
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)
|
||||||
|
|
||||||
/* Notcurses TUI (optional) */
|
/* Notcurses TUI (optional) */
|
||||||
OP_NC_INIT, // initializes Notcurses; returns 1 on success, 0 on failure
|
OP_NC_INIT, // initializes Notcurses; returns 1 on success, 0 on failure
|
||||||
OP_NC_SHUTDOWN, // shuts down Notcurses; returns 0
|
OP_NC_SHUTDOWN, // shuts down Notcurses; returns 0
|
||||||
|
|
|
||||||
10
src/parser.c
10
src/parser.c
|
|
@ -824,6 +824,16 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
free(name);
|
free(name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (strcmp(name, "cpp_add") == 0) {
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "cpp_add expects (a:int, b:int)"); free(name); return 0; }
|
||||||
|
if (!consume_char(src, len, pos, ',')) { parser_fail(*pos, "cpp_add expects two arguments"); free(name); return 0; }
|
||||||
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "cpp_add expects (a:int, b:int)"); free(name); return 0; }
|
||||||
|
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after cpp_add args"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_CPP_ADD, 0);
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
if (strcmp(name, "os_list_dir") == 0) {
|
if (strcmp(name, "os_list_dir") == 0) {
|
||||||
(*pos)++; /* '(' */
|
(*pos)++; /* '(' */
|
||||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "os_list_dir expects (path)"); free(name); return 0; }
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "os_list_dir expects (path)"); free(name); return 0; }
|
||||||
|
|
|
||||||
16
src/vm.c
16
src/vm.c
|
|
@ -897,6 +897,22 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
#include "vm/sqlite/query.c"
|
#include "vm/sqlite/query.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* C++ demo opcodes (guarded) */
|
||||||
|
#if defined(FUN_WITH_CPP)
|
||||||
|
case OP_CPP_ADD: {
|
||||||
|
int rc = fun_op_cpp_add(vm);
|
||||||
|
if (rc != 0) {
|
||||||
|
vm_raise_error(vm, "cpp_add failed");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
case OP_CPP_ADD: {
|
||||||
|
vm_raise_error(vm, "CPP support is not enabled (build with -DFUN_WITH_CPP=ON)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* libsql ops (independent) */
|
/* libsql ops (independent) */
|
||||||
#ifdef FUN_WITH_LIBSQL
|
#ifdef FUN_WITH_LIBSQL
|
||||||
#include "vm/libsql/open.c"
|
#include "vm/libsql/open.c"
|
||||||
|
|
|
||||||
5
src/vm.h
5
src/vm.h
|
|
@ -58,6 +58,8 @@ static const char *opcode_names[] = {
|
||||||
"FMIN","FMAX",
|
"FMIN","FMAX",
|
||||||
/* Rust FFI demo */
|
/* Rust FFI demo */
|
||||||
"RUST_HELLO","RUST_HELLO_ARGS","RUST_HELLO_ARGS_RETURN","RUST_GET_SP","RUST_SET_EXIT",
|
"RUST_HELLO","RUST_HELLO_ARGS","RUST_HELLO_ARGS_RETURN","RUST_GET_SP","RUST_SET_EXIT",
|
||||||
|
/* C++ demo */
|
||||||
|
"CPP_ADD",
|
||||||
/* Notcurses TUI (optional) */
|
/* Notcurses TUI (optional) */
|
||||||
"NC_INIT","NC_SHUTDOWN","NC_CLEAR","NC_DRAW_TEXT","NC_GETCH"
|
"NC_INIT","NC_SHUTDOWN","NC_CLEAR","NC_DRAW_TEXT","NC_GETCH"
|
||||||
};
|
};
|
||||||
|
|
@ -165,6 +167,9 @@ char *fun_rust_echo_string(const char *input);
|
||||||
/* Free a C string previously returned by fun_rust_echo_string. */
|
/* Free a C string previously returned by fun_rust_echo_string. */
|
||||||
void fun_rust_string_free(char *ptr);
|
void fun_rust_string_free(char *ptr);
|
||||||
|
|
||||||
|
/* C++ demo opcode entry point (C ABI) */
|
||||||
|
int fun_op_cpp_add(struct VM *vm);
|
||||||
|
|
||||||
/* --- Extended C ABI for Rust to access VM internals (unsafe) --- */
|
/* --- Extended C ABI for Rust to access VM internals (unsafe) --- */
|
||||||
/* Size helpers for Rust side to compute offsets and do pointer math */
|
/* Size helpers for Rust side to compute offsets and do pointer math */
|
||||||
size_t vm_sizeof(void);
|
size_t vm_sizeof(void);
|
||||||
|
|
|
||||||
28
src/vm/cpp/add.cpp
Normal file
28
src/vm/cpp/add.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Minimal C++ opcode for Fun VM — cpp_add
|
||||||
|
* Build is gated by -DFUN_WITH_CPP=ON (see CMake).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "vm.h" // C header; provides VM, vm_pop_i64, vm_push_i64
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int fun_op_cpp_add(VM *vm) {
|
||||||
|
int64_t b = vm_pop_i64(vm);
|
||||||
|
int64_t a = vm_pop_i64(vm);
|
||||||
|
vm_push_i64(vm, a + b);
|
||||||
|
return 0; // success
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue