1
0
Fork 0
forked from fun/fun

Some more Rusty Fun... :) (0.38.3)

This commit is contained in:
Johannes Findeisen 2026-01-29 03:29:14 +01:00
commit d39ef1a58e
10 changed files with 157 additions and 113 deletions

View file

@ -208,6 +208,7 @@ static const char *opcode_name(OpCode op) {
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 "???";

View file

@ -266,6 +266,7 @@ typedef enum {
// 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)

View file

@ -801,6 +801,14 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
free(name);
return 1;
}
if (strcmp(name, "rust_hello_args_return") == 0) {
(*pos)++; /* '(' */
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "rust_hello_args_return expects (message:string)"); free(name); return 0; }
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after rust_hello_args_return arg"); free(name); return 0; }
bytecode_add_instruction(bc, OP_RUST_HELLO_ARGS_RETURN, 0);
free(name);
return 1;
}
if (strcmp(name, "rust_get_sp") == 0) {
(*pos)++; /* '(' */
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "rust_get_sp expects ()"); free(name); return 0; }

View file

@ -48,4 +48,46 @@ pub extern "C" fn fun_rust_print_string(msg: *const core::ffi::c_char) -> i32 {
0
}
// Return a newly allocated duplicate of the given C string.
// Caller (C side) must free using fun_rust_string_free.
#[no_mangle]
pub extern "C" fn fun_rust_echo_string(input: *const core::ffi::c_char) -> *mut core::ffi::c_char {
use core::ffi::CStr;
unsafe {
if input.is_null() {
// allocate empty string
let v = Vec::<u8>::from([0u8]);
return v.leak().as_mut_ptr() as *mut core::ffi::c_char;
}
match CStr::from_ptr(input).to_str() {
Ok(s) => {
let mut v = s.as_bytes().to_vec();
v.push(0); // NUL-terminate
v.leak().as_mut_ptr() as *mut core::ffi::c_char
}
Err(_) => {
// On invalid UTF-8, still duplicate bytes as-is
let c = CStr::from_ptr(input);
let mut v = c.to_bytes().to_vec();
v.push(0);
v.leak().as_mut_ptr() as *mut core::ffi::c_char
}
}
}
}
// Free a C string previously returned by fun_rust_echo_string
#[no_mangle]
pub extern "C" fn fun_rust_string_free(ptr: *mut core::ffi::c_char) {
if ptr.is_null() { return; }
unsafe {
// Reconstruct a Vec<u8> so Rust will free it when it drops
// Determine length by scanning for NUL
let mut len: usize = 0;
while *ptr.add(len) != 0 { len += 1; }
let slice = core::slice::from_raw_parts_mut(ptr as *mut u8, len + 1);
let _ = Vec::from_raw_parts(slice.as_mut_ptr(), len + 1, len + 1);
}
}
// No custom panic handler; use std default

View file

@ -789,6 +789,7 @@ void vm_run(VM *vm, Bytecode *entry) {
/* Rust FFI demo opcode(s) */
#include "vm/rust/hello.c"
#include "vm/rust/hello_args.c"
#include "vm/rust/hello_args_return.c"
#include "vm/rust/get_sp.c"
#include "vm/rust/set_exit.c"

View file

@ -57,7 +57,7 @@ static const char *opcode_names[] = {
"TRY_PUSH","TRY_POP","THROW",
"FMIN","FMAX",
/* Rust FFI demo */
"RUST_HELLO","RUST_HELLO_ARGS","RUST_GET_SP","RUST_SET_EXIT",
"RUST_HELLO","RUST_HELLO_ARGS","RUST_HELLO_ARGS_RETURN","RUST_GET_SP","RUST_SET_EXIT",
/* Notcurses TUI (optional) */
"NC_INIT","NC_SHUTDOWN","NC_CLEAR","NC_DRAW_TEXT","NC_GETCH"
};
@ -160,6 +160,10 @@ int fun_op_radd(VM *vm);
const char *fun_rust_get_string(void);
/* Rust function that prints a passed C string, returns 0 on success. */
int fun_rust_print_string(const char *msg);
/* Rust function that returns a newly allocated duplicate of the input C string. */
char *fun_rust_echo_string(const char *input);
/* Free a C string previously returned by fun_rust_echo_string. */
void fun_rust_string_free(char *ptr);
/* --- Extended C ABI for Rust to access VM internals (unsafe) --- */
/* Size helpers for Rust side to compute offsets and do pointer math */

View file

@ -0,0 +1,48 @@
/*
* 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
*/
/*
* Rust FFI demo opcode with argument: OP_RUST_HELLO_ARGS_RETURN
* Pops a string from the stack, asks Rust to return it back (no printing).
* Pushes the returned string, or Nil on error.
*/
case OP_RUST_HELLO_ARGS_RETURN: {
#ifdef FUN_WITH_RUST
Value vmsg = pop_value(vm);
char *msg = value_to_string_alloc(&vmsg);
free_value(vmsg);
if (msg) {
char *ret = fun_rust_echo_string(msg);
free(msg);
if (ret) {
push_value(vm, make_string(ret));
fun_rust_string_free(ret);
} else {
push_value(vm, make_nil());
}
} else {
char *ret = fun_rust_echo_string("");
if (ret) {
push_value(vm, make_string(ret));
fun_rust_string_free(ret);
} else {
push_value(vm, make_nil());
}
}
#else
/* Still pop and free the arg to keep stack sane */
Value vmsg = pop_value(vm);
free_value(vmsg);
vm_raise_error(vm, "RUST_HELLO_ARGS_RETURN requires FUN_WITH_RUST=ON at build time");
push_value(vm, make_nil());
#endif
break;
}