diff --git a/bro.py b/bro.py index 0ebd357..55a1275 100644 --- a/bro.py +++ b/bro.py @@ -1,4 +1,3 @@ -def hello(): - print("Hello, bro!") - -hello() \ No newline at end of file +def hello(name: str) -> str: + print("Hello, bro! " + name) + return "Idk, return value i guess" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ad7e296..aacc8dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,21 +1,29 @@ -use std::{ffi::CString, fs, path::Path}; - -#[link(name = "python3.13")] -extern "C" { - fn Py_Initialize(); - fn PyRun_SimpleString(command: *const i8) -> i32; - fn Py_Finalize(); +pub enum FluxValue { + Int(i64), + Float(f64), + Str(String), + Null, + // later: List(Vec), Dict(HashMap) } -pub fn run_py(path: &Path) { - let code = fs::read_to_string(path).expect("Failed to read python script"); +pub trait Serialize { + fn serialize(&self) -> FluxValue; +} - unsafe { - Py_Initialize(); +pub trait Deserialize { + fn deserialize(v: FluxValue) -> Self; +} - let code = CString::new(code.as_str()).unwrap(); - PyRun_SimpleString(code.as_ptr()); +pub trait FluxModule { + fn init(&self); + fn end(&self); + fn call(&self, func: &str, args: Vec<&dyn Serialize>) -> FluxValue; +} + +pub mod values; +pub mod runtime; + +pub mod prelude { + pub use crate::{FluxModule, Serialize, Deserialize}; +} - Py_Finalize(); - } -} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e1ac8bd..8fff5b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ -use std::path::Path; -use flux_rt::run_py; +use flux_rt::{runtime::python::PythonModule, prelude::*}; fn main() { - run_py(Path::new("bro.py")); + let bro = PythonModule("bro.py".to_string()); + bro.init(); + + bro.call("hello", vec![]); } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs new file mode 100644 index 0000000..568b414 --- /dev/null +++ b/src/runtime/mod.rs @@ -0,0 +1 @@ +pub mod python; \ No newline at end of file diff --git a/src/runtime/python.rs b/src/runtime/python.rs new file mode 100644 index 0000000..17c548a --- /dev/null +++ b/src/runtime/python.rs @@ -0,0 +1,113 @@ +use std::{ffi::{CString, c_char}, fs}; + + use crate::{FluxModule, FluxValue, Serialize}; + + #[link(name = "python3.13")] + extern "C" { + fn Py_Initialize(); + fn Py_Finalize(); + + fn PyRun_SimpleString(command: *const i8) -> i32; + + fn PyImport_AddModule(name: *const c_char) -> *mut PyObject; + fn PyModule_GetDict(module: *mut PyObject) -> *mut PyObject; + fn PyDict_GetItemString(dict: *mut PyObject, key: *const c_char) -> *mut PyObject; + + fn PyObject_CallObject(callable: *mut PyObject, args: *mut PyObject) -> *mut PyObject; + fn Py_BuildValue(format: *const c_char, ...) -> *mut PyObject; + + fn PyLong_AsLong(obj: *mut PyObject) -> i64; + } + + #[repr(C)] + pub struct PyObject; + + pub struct PythonModule(pub String); + + impl FluxModule for PythonModule { + fn init(&self) { + let code = fs::read_to_string(&self.0).expect("Failed to read python script"); + let code = CString::new(code.as_str()).unwrap(); + + unsafe { + Py_Initialize(); + PyRun_SimpleString(code.as_ptr()); + } + } + + fn call(&self, func: &str, args: Vec<&dyn Serialize>) -> FluxValue { + use std::ffi::CString; + + unsafe { + // 1. Get __main__ module dict + let main_str = CString::new("__main__").unwrap(); + let module = PyImport_AddModule(main_str.as_ptr()); + let dict = PyModule_GetDict(module); + + // 2. Get function by name + let func_name = CString::new(func).unwrap(); + let func_obj = PyDict_GetItemString(dict, func_name.as_ptr()); + + if func_obj.is_null() { + panic!("Python function '{}' not found", func); + } + + let mut fmt = String::from("("); + let values: Vec = args.iter().map(|a| a.serialize()).collect(); + + for v in &values { + match v { + FluxValue::Int(_) => fmt.push('l'), // long + FluxValue::Float(_) => fmt.push('d'), // double + FluxValue::Str(_) => fmt.push('s'), // string + FluxValue::Null => fmt.push('O'), // PyObject* + } + } + fmt.push(')'); + + let fmt_c = CString::new(fmt).unwrap(); + + // Call Py_BuildValue with the right arguments + let args_obj: *mut PyObject = match values.as_slice() { + [FluxValue::Int(a), FluxValue::Int(b)] => { + Py_BuildValue(fmt_c.as_ptr(), *a, *b) + } + [FluxValue::Float(a), FluxValue::Float(b)] => { + Py_BuildValue(fmt_c.as_ptr(), *a, *b) + } + [FluxValue::Str(a), FluxValue::Str(b)] => { + let a_c = CString::new(a.as_str()).unwrap(); + let b_c = CString::new(b.as_str()).unwrap(); + Py_BuildValue(fmt_c.as_ptr(), a_c.as_ptr(), b_c.as_ptr()) + } + [FluxValue::Int(a)] => { + Py_BuildValue(fmt_c.as_ptr(), *a) + } + [FluxValue::Float(a)] => { + Py_BuildValue(fmt_c.as_ptr(), *a) + } + [FluxValue::Str(a)] => { + let a_c = CString::new(a.as_str()).unwrap(); + Py_BuildValue(fmt_c.as_ptr(), a_c.as_ptr()) + } + _ => std::ptr::null_mut(), + }; + + let result_obj = PyObject_CallObject(func_obj, args_obj); + + if result_obj.is_null() { + FluxValue::Null + } else { + let value = PyLong_AsLong(result_obj); + FluxValue::Int(value) + } + } + } + + + fn end(&self) { + unsafe { + Py_Finalize(); + } + } + } \ No newline at end of file diff --git a/src/values.rs b/src/values.rs new file mode 100644 index 0000000..611e5b0 --- /dev/null +++ b/src/values.rs @@ -0,0 +1,25 @@ +use crate::{Deserialize, FluxValue, Serialize}; + +impl Serialize for i64 { + fn serialize(&self) -> FluxValue { + FluxValue::Int(*self) + } +} +impl Serialize for f64 { + fn serialize(&self) -> FluxValue { + FluxValue::Float(*self) + } +} +impl Serialize for &str { + fn serialize(&self) -> FluxValue { + FluxValue::Str(self.to_string()) + } +} +impl Deserialize for i64 { + fn deserialize(v: FluxValue) -> Self { + match v { + FluxValue::Int(i) => i, + _ => panic!("Expected Int"), + } + } +}