impl(pyvm/llvmlite): - add tools/parity.sh; tools/pyvm_runner.py; src/llvm_py/pyvm/* - emit string const as handle type in MIR JSON; add dst_type hints - unify '+' to concat_hh with from_i64/from_i8_string bridges; console print via to_i8p_h - add runtime bridges: nyash.box.from_i64, nyash.string.to_i8p_h tests: - add apps/tests/min_str_cat_loop (minimal repro for string cat loop)
155 lines
5.1 KiB
Rust
155 lines
5.1 KiB
Rust
// ---- String helpers for LLVM lowering ----
|
|
// Exported as: nyash_string_new(i8* ptr, i32 len) -> i8*
|
|
#[no_mangle]
|
|
pub extern "C" fn nyash_string_new(ptr: *const u8, len: i32) -> *mut i8 {
|
|
use std::ptr;
|
|
if ptr.is_null() || len < 0 {
|
|
return std::ptr::null_mut();
|
|
}
|
|
let n = len as usize;
|
|
// Allocate n+1 and null-terminate for C interop (puts, etc.)
|
|
let mut buf = Vec::<u8>::with_capacity(n + 1);
|
|
unsafe {
|
|
ptr::copy_nonoverlapping(ptr, buf.as_mut_ptr(), n);
|
|
buf.set_len(n);
|
|
}
|
|
buf.push(0);
|
|
let boxed = buf.into_boxed_slice();
|
|
let raw = Box::into_raw(boxed) as *mut u8;
|
|
raw as *mut i8
|
|
}
|
|
|
|
// ---- String concat helpers for LLVM lowering ----
|
|
// Exported as: nyash.string.concat_ss(i8* a, i8* b) -> i8*
|
|
#[export_name = "nyash.string.concat_ss"]
|
|
pub extern "C" fn nyash_string_concat_ss(a: *const i8, b: *const i8) -> *mut i8 {
|
|
let mut s = String::new();
|
|
unsafe {
|
|
if !a.is_null() {
|
|
if let Ok(sa) = std::ffi::CStr::from_ptr(a).to_str() {
|
|
s.push_str(sa);
|
|
}
|
|
}
|
|
if !b.is_null() {
|
|
if let Ok(sb) = std::ffi::CStr::from_ptr(b).to_str() {
|
|
s.push_str(sb);
|
|
}
|
|
}
|
|
}
|
|
let mut bytes = s.into_bytes();
|
|
bytes.push(0);
|
|
let boxed = bytes.into_boxed_slice();
|
|
let raw = Box::into_raw(boxed) as *mut u8;
|
|
raw as *mut i8
|
|
}
|
|
|
|
// Exported as: nyash.string.concat_si(i8* a, i64 b) -> i8*
|
|
#[export_name = "nyash.string.concat_si"]
|
|
pub extern "C" fn nyash_string_concat_si(a: *const i8, b: i64) -> *mut i8 {
|
|
let mut s = String::new();
|
|
unsafe {
|
|
if !a.is_null() {
|
|
if let Ok(sa) = std::ffi::CStr::from_ptr(a).to_str() {
|
|
s.push_str(sa);
|
|
}
|
|
}
|
|
}
|
|
s.push_str(&b.to_string());
|
|
let mut bytes = s.into_bytes();
|
|
bytes.push(0);
|
|
let boxed = bytes.into_boxed_slice();
|
|
let raw = Box::into_raw(boxed) as *mut u8;
|
|
raw as *mut i8
|
|
}
|
|
|
|
// Exported as: nyash.string.concat_is(i64 a, i8* b) -> i8*
|
|
#[export_name = "nyash.string.concat_is"]
|
|
pub extern "C" fn nyash_string_concat_is(a: i64, b: *const i8) -> *mut i8 {
|
|
let mut s = a.to_string();
|
|
unsafe {
|
|
if !b.is_null() {
|
|
if let Ok(sb) = std::ffi::CStr::from_ptr(b).to_str() {
|
|
s.push_str(sb);
|
|
}
|
|
}
|
|
}
|
|
let mut bytes = s.into_bytes();
|
|
bytes.push(0);
|
|
let boxed = bytes.into_boxed_slice();
|
|
let raw = Box::into_raw(boxed) as *mut u8;
|
|
raw as *mut i8
|
|
}
|
|
|
|
// Exported as: nyash.string.substring_sii(i8* s, i64 start, i64 end) -> i8*
|
|
#[export_name = "nyash.string.substring_sii"]
|
|
pub extern "C" fn nyash_string_substring_sii(s: *const i8, start: i64, end: i64) -> *mut i8 {
|
|
use std::ffi::CStr;
|
|
if s.is_null() {
|
|
return std::ptr::null_mut();
|
|
}
|
|
let src = unsafe { CStr::from_ptr(s) };
|
|
let src = match src.to_str() {
|
|
Ok(v) => v,
|
|
Err(_) => return std::ptr::null_mut(),
|
|
};
|
|
let n = src.len() as i64;
|
|
let mut st = if start < 0 { 0 } else { start };
|
|
let mut en = if end < 0 { 0 } else { end };
|
|
if st > n { st = n; }
|
|
if en > n { en = n; }
|
|
if en < st { std::mem::swap(&mut st, &mut en); }
|
|
let (st_u, en_u) = (st as usize, en as usize);
|
|
let sub = &src[st_u.min(src.len())..en_u.min(src.len())];
|
|
let mut bytes = sub.as_bytes().to_vec();
|
|
bytes.push(0);
|
|
let boxed = bytes.into_boxed_slice();
|
|
let raw = Box::into_raw(boxed) as *mut u8;
|
|
raw as *mut i8
|
|
}
|
|
|
|
// Exported as: nyash.string.lastIndexOf_ss(i8* s, i8* needle) -> i64
|
|
#[export_name = "nyash.string.lastIndexOf_ss"]
|
|
pub extern "C" fn nyash_string_lastindexof_ss(s: *const i8, needle: *const i8) -> i64 {
|
|
use std::ffi::CStr;
|
|
if s.is_null() || needle.is_null() { return -1; }
|
|
let hs = unsafe { CStr::from_ptr(s) };
|
|
let ns = unsafe { CStr::from_ptr(needle) };
|
|
let h = match hs.to_str() { Ok(v) => v, Err(_) => return -1 };
|
|
let n = match ns.to_str() { Ok(v) => v, Err(_) => return -1 };
|
|
if n.is_empty() { return h.len() as i64; }
|
|
if let Some(pos) = h.rfind(n) {
|
|
pos as i64
|
|
} else { -1 }
|
|
}
|
|
|
|
// Exported as: nyash.string.to_i8p_h(i64 handle) -> i8*
|
|
#[export_name = "nyash.string.to_i8p_h"]
|
|
pub extern "C" fn nyash_string_to_i8p_h(handle: i64) -> *mut i8 {
|
|
use nyash_rust::jit::rt::handles;
|
|
if handle <= 0 {
|
|
// return "0" for consistency with existing fallback behavior
|
|
let s = handle.to_string();
|
|
let mut bytes = s.into_bytes();
|
|
bytes.push(0);
|
|
let boxed = bytes.into_boxed_slice();
|
|
let raw = Box::into_raw(boxed) as *mut u8;
|
|
return raw as *mut i8;
|
|
}
|
|
if let Some(obj) = handles::get(handle as u64) {
|
|
let s = obj.to_string_box().value;
|
|
let mut bytes = s.into_bytes();
|
|
bytes.push(0);
|
|
let boxed = bytes.into_boxed_slice();
|
|
let raw = Box::into_raw(boxed) as *mut u8;
|
|
raw as *mut i8
|
|
} else {
|
|
// not found -> print numeric handle string
|
|
let s = handle.to_string();
|
|
let mut bytes = s.into_bytes();
|
|
bytes.push(0);
|
|
let boxed = bytes.into_boxed_slice();
|
|
let raw = Box::into_raw(boxed) as *mut u8;
|
|
raw as *mut i8
|
|
}
|
|
}
|