Some more Fun VM and Rust interaction. (0.38.2)
This commit is contained in:
parent
543483041b
commit
c2e7f3869e
17 changed files with 251 additions and 46 deletions
|
|
@ -208,6 +208,8 @@ 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_GET_SP: return "RUST_GET_SP";
|
||||
case OP_RUST_SET_EXIT: return "RUST_SET_EXIT";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,6 +266,8 @@ 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_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)
|
||||
|
||||
/* Notcurses TUI (optional) */
|
||||
OP_NC_INIT, // initializes Notcurses; returns 1 on success, 0 on failure
|
||||
|
|
|
|||
15
src/parser.c
15
src/parser.c
|
|
@ -801,6 +801,21 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
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; }
|
||||
bytecode_add_instruction(bc, OP_RUST_GET_SP, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "rust_set_exit") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "rust_set_exit expects (code:int)"); free(name); return 0; }
|
||||
if (!consume_char(src, len, pos, ')')) { parser_fail(*pos, "Expected ')' after rust_set_exit arg"); free(name); return 0; }
|
||||
bytecode_add_instruction(bc, OP_RUST_SET_EXIT, 0);
|
||||
free(name);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(name, "os_list_dir") == 0) {
|
||||
(*pos)++; /* '(' */
|
||||
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "os_list_dir expects (path)"); free(name); return 0; }
|
||||
|
|
|
|||
|
|
@ -2,15 +2,13 @@
|
|||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* 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
|
||||
*/
|
||||
|
||||
#![no_std]
|
||||
|
||||
// Use an FFI-safe opaque handle for the VM pointer
|
||||
pub type Vm = core::ffi::c_void;
|
||||
|
||||
|
|
@ -37,7 +35,7 @@ pub extern "C" fn fun_rust_get_string() -> *const core::ffi::c_char {
|
|||
b"Hello from Rust ops!\0".as_ptr() as *const _
|
||||
}
|
||||
|
||||
// Print a C string via libc printf to stdout (acceptable under no_std via extern)
|
||||
// Print a C string via libc printf to stdout
|
||||
#[no_mangle]
|
||||
pub extern "C" fn fun_rust_print_string(msg: *const core::ffi::c_char) -> i32 {
|
||||
unsafe {
|
||||
|
|
@ -50,12 +48,4 @@ pub extern "C" fn fun_rust_print_string(msg: *const core::ffi::c_char) -> i32 {
|
|||
0
|
||||
}
|
||||
|
||||
// Provide personality symbol to satisfy some linkers in no_std builds
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_eh_personality() {}
|
||||
|
||||
// Minimal panic handler for no_std; abort behavior requested via Cargo profile
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
||||
// No custom panic handler; use std default
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
|
|
@ -16,10 +16,18 @@
|
|||
//! ABI helpers declared on the C side (`vm_pop_i64`, `vm_push_i64`).
|
||||
|
||||
use super::Vm;
|
||||
//use core::mem::size_of;
|
||||
use core::ptr;
|
||||
|
||||
extern "C" {
|
||||
fn vm_pop_i64(vm: *mut Vm) -> i64;
|
||||
fn vm_push_i64(vm: *mut Vm, v: i64);
|
||||
fn vm_as_mut_ptr(vm: *mut Vm) -> *mut core::ffi::c_void;
|
||||
fn vm_sizeof() -> usize;
|
||||
fn vm_value_sizeof() -> usize;
|
||||
fn vm_offset_of_exit_code() -> usize;
|
||||
fn vm_offset_of_sp() -> usize;
|
||||
fn vm_offset_of_stack() -> usize;
|
||||
}
|
||||
|
||||
/// Multiply the top two integers on the VM stack.
|
||||
|
|
@ -68,3 +76,29 @@ pub extern "C" fn fun_op_rdiv(vm: *mut Vm) -> i32 {
|
|||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// Demo: push current stack pointer (vm.sp) using raw offset access.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn fun_op_rget_sp(vm: *mut Vm) -> i32 {
|
||||
unsafe {
|
||||
let base = vm_as_mut_ptr(vm) as *mut u8;
|
||||
let off_sp = vm_offset_of_sp();
|
||||
let sp_ptr = base.add(off_sp) as *const i32;
|
||||
let sp = ptr::read(sp_ptr);
|
||||
vm_push_i64(vm, sp as i64);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// Demo: set vm.exit_code to a value popped from the stack (i64 -> i32 cast).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn fun_op_rset_exit(vm: *mut Vm) -> i32 {
|
||||
unsafe {
|
||||
let code = vm_pop_i64(vm) as i32;
|
||||
let base = vm_as_mut_ptr(vm) as *mut u8;
|
||||
let off_ec = vm_offset_of_exit_code();
|
||||
let ec_ptr = base.add(off_ec) as *mut i32;
|
||||
ptr::write(ec_ptr, code);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
|
|
|||
33
src/vm.c
33
src/vm.c
|
|
@ -22,6 +22,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
|
||||
|
|
@ -488,6 +489,35 @@ void vm_push_i64(VM *vm, int64_t v) {
|
|||
push_value(vm, make_int(v));
|
||||
}
|
||||
|
||||
/* --- Extended C ABI for Rust to access VM internals (unsafe) --- */
|
||||
size_t vm_sizeof(void) {
|
||||
return sizeof(VM);
|
||||
}
|
||||
|
||||
size_t vm_value_sizeof(void) {
|
||||
return sizeof(Value);
|
||||
}
|
||||
|
||||
void *vm_as_mut_ptr(VM *vm) {
|
||||
return (void*)vm;
|
||||
}
|
||||
|
||||
size_t vm_offset_of_exit_code(void) {
|
||||
return offsetof(VM, exit_code);
|
||||
}
|
||||
|
||||
size_t vm_offset_of_sp(void) {
|
||||
return offsetof(VM, sp);
|
||||
}
|
||||
|
||||
size_t vm_offset_of_stack(void) {
|
||||
return offsetof(VM, stack);
|
||||
}
|
||||
|
||||
size_t vm_offset_of_globals(void) {
|
||||
return offsetof(VM, globals);
|
||||
}
|
||||
|
||||
static void frame_init(Frame *f) {
|
||||
f->fn = NULL;
|
||||
f->ip = 0;
|
||||
|
|
@ -758,6 +788,9 @@ 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/get_sp.c"
|
||||
#include "vm/rust/set_exit.c"
|
||||
|
||||
#include "vm/os/env.c"
|
||||
#include "vm/os/env_all.c"
|
||||
|
|
|
|||
22
src/vm.h
22
src/vm.h
|
|
@ -11,6 +11,7 @@
|
|||
#define FUN_VM_H
|
||||
|
||||
#include "bytecode.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#define MAX_FRAMES 128
|
||||
#define MAX_FRAME_LOCALS 64
|
||||
|
|
@ -56,7 +57,7 @@ static const char *opcode_names[] = {
|
|||
"TRY_PUSH","TRY_POP","THROW",
|
||||
"FMIN","FMAX",
|
||||
/* Rust FFI demo */
|
||||
"RUST_HELLO","RUST_HELLO_ARGS",
|
||||
"RUST_HELLO","RUST_HELLO_ARGS","RUST_GET_SP","RUST_SET_EXIT",
|
||||
/* Notcurses TUI (optional) */
|
||||
"NC_INIT","NC_SHUTDOWN","NC_CLEAR","NC_DRAW_TEXT","NC_GETCH"
|
||||
};
|
||||
|
|
@ -160,4 +161,23 @@ 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);
|
||||
|
||||
/* --- Extended C ABI for Rust to access VM internals (unsafe) --- */
|
||||
/* Size helpers for Rust side to compute offsets and do pointer math */
|
||||
size_t vm_sizeof(void);
|
||||
size_t vm_value_sizeof(void);
|
||||
|
||||
/* Get a mutable byte pointer to the VM object. Extremely unsafe; intended for
|
||||
* low-level FFI where Rust wants parity access with C code. */
|
||||
void *vm_as_mut_ptr(VM *vm);
|
||||
|
||||
/* Offsets of commonly accessed VM fields to avoid re-declaring the struct layout in Rust */
|
||||
size_t vm_offset_of_exit_code(void);
|
||||
size_t vm_offset_of_sp(void);
|
||||
size_t vm_offset_of_stack(void);
|
||||
size_t vm_offset_of_globals(void);
|
||||
|
||||
/* Demo Rust ops using the extended ABI */
|
||||
int fun_op_rget_sp(VM *vm);
|
||||
int fun_op_rset_exit(VM *vm);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
26
src/vm/rust/get_sp.c
Normal file
26
src/vm/rust/get_sp.c
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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: OP_RUST_GET_SP
|
||||
* Calls into Rust to read vm.sp via raw pointer math and pushes it (int).
|
||||
*/
|
||||
case OP_RUST_GET_SP: {
|
||||
#ifdef FUN_WITH_RUST
|
||||
extern int fun_op_rget_sp(VM *vm);
|
||||
int rc = fun_op_rget_sp(vm);
|
||||
(void)rc; /* rc currently unused; 0 means OK */
|
||||
#else
|
||||
vm_raise_error(vm, "RUST_GET_SP requires FUN_WITH_RUST=ON at build time");
|
||||
push_value(vm, make_int(-1));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
|
|
@ -25,29 +25,3 @@ case OP_RUST_HELLO: {
|
|||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Rust FFI demo opcode with argument: OP_RUST_HELLO_ARGS
|
||||
* Pops a string from the stack and asks Rust to print it. Pushes Nil.
|
||||
*/
|
||||
case OP_RUST_HELLO_ARGS: {
|
||||
#ifdef FUN_WITH_RUST
|
||||
Value vmsg = pop_value(vm);
|
||||
char *msg = value_to_string_alloc(&vmsg);
|
||||
free_value(vmsg);
|
||||
if (msg) {
|
||||
fun_rust_print_string(msg);
|
||||
free(msg);
|
||||
} else {
|
||||
fun_rust_print_string("");
|
||||
}
|
||||
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 requires FUN_WITH_RUST=ON at build time");
|
||||
push_value(vm, make_nil());
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
36
src/vm/rust/hello_args.c
Normal file
36
src/vm/rust/hello_args.c
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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
|
||||
* Pops a string from the stack and asks Rust to print it. Pushes Nil.
|
||||
*/
|
||||
case OP_RUST_HELLO_ARGS: {
|
||||
#ifdef FUN_WITH_RUST
|
||||
Value vmsg = pop_value(vm);
|
||||
char *msg = value_to_string_alloc(&vmsg);
|
||||
free_value(vmsg);
|
||||
if (msg) {
|
||||
fun_rust_print_string(msg);
|
||||
free(msg);
|
||||
} else {
|
||||
fun_rust_print_string("");
|
||||
}
|
||||
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 requires FUN_WITH_RUST=ON at build time");
|
||||
push_value(vm, make_nil());
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
33
src/vm/rust/set_exit.c
Normal file
33
src/vm/rust/set_exit.c
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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: OP_RUST_SET_EXIT
|
||||
* Pops an int code and asks Rust to write it into vm.exit_code.
|
||||
*/
|
||||
case OP_RUST_SET_EXIT: {
|
||||
#ifdef FUN_WITH_RUST
|
||||
extern int fun_op_rset_exit(VM *vm);
|
||||
/* Expect an integer on the stack already (produced by prior ops) */
|
||||
/* fun_op_rset_exit will pop it and store into vm.exit_code */
|
||||
int rc = fun_op_rset_exit(vm);
|
||||
(void)rc;
|
||||
/* push Nil as a conventional result */
|
||||
push_value(vm, make_nil());
|
||||
#else
|
||||
/* pop and ignore to keep stack sane */
|
||||
Value v = pop_value(vm);
|
||||
free_value(v);
|
||||
vm_raise_error(vm, "RUST_SET_EXIT requires FUN_WITH_RUST=ON at build time");
|
||||
push_value(vm, make_nil());
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue