use keleusma::compiler::compile;
use keleusma::lexer::tokenize;
use keleusma::parser::parse;
use keleusma::vm::{DEFAULT_ARENA_CAPACITY, Vm, VmState};
use keleusma::{Arena, HostOpaque, Value, host_arc};
struct RustString(String);
impl HostOpaque for RustString {
fn type_name(&self) -> &'static str {
"RustString"
}
}
fn read_rust_string<'a>(
native: &'static str,
v: &'a Value,
) -> Result<&'a RustString, keleusma::VmError> {
let opaque = match v {
Value::Opaque(o) => o,
other => {
return Err(keleusma::VmError::TypeError(format!(
"{}: expected RustString, got {}",
native,
other.type_name()
)));
}
};
opaque.as_ref().downcast_ref::<RustString>().ok_or_else(|| {
keleusma::VmError::TypeError(format!(
"{}: expected RustString, got opaque {}",
native,
opaque.type_name()
))
})
}
fn main() {
let src = r#"
use make_string
use upper_case
use append_exclamation
fn main() -> RustString {
let s = make_string("hello, keleusma");
let s = upper_case(s);
append_exclamation(s)
}
"#;
let tokens = tokenize(src).expect("lex");
let program = parse(&tokens).expect("parse");
let module = compile(&program).expect("compile");
let arena = Arena::with_capacity(DEFAULT_ARENA_CAPACITY);
let mut vm = Vm::new(module, &arena).expect("verify");
vm.register_native_with_ctx("make_string", |ctx, args| {
if args.len() != 1 {
return Err(keleusma::VmError::NativeError(
"make_string: expected exactly one argument".into(),
));
}
let s: &str = args[0]
.as_str_with_arena(ctx.arena)
.map_err(|_| {
keleusma::VmError::NativeError(
"make_string: stale KStr (arena reset since allocation)".into(),
)
})?
.ok_or_else(|| {
keleusma::VmError::TypeError(format!(
"make_string: expected Text, got {}",
args[0].type_name()
))
})?;
Ok(Value::Opaque(host_arc(RustString(s.to_owned()))))
});
vm.register_native("upper_case", |args| {
if args.len() != 1 {
return Err(keleusma::VmError::NativeError(
"upper_case: expected exactly one argument".into(),
));
}
let s = read_rust_string("upper_case", &args[0])?;
Ok(Value::Opaque(host_arc(RustString(s.0.to_uppercase()))))
});
vm.register_native("append_exclamation", |args| {
if args.len() != 1 {
return Err(keleusma::VmError::NativeError(
"append_exclamation: expected exactly one argument".into(),
));
}
let s = read_rust_string("append_exclamation", &args[0])?;
let mut out = s.0.clone();
out.push('!');
Ok(Value::Opaque(host_arc(RustString(out))))
});
let result = match vm.call(&[]).expect("vm call") {
VmState::Finished(v) => v,
other => panic!("expected finished, got {:?}", other),
};
let opaque = match result {
Value::Opaque(o) => o,
other => panic!("expected opaque, got {:?}", other),
};
let typed = opaque
.as_ref()
.downcast_ref::<RustString>()
.expect("downcast RustString");
println!("script returned: {}", typed.0);
println!("byte length on host: {}", typed.0.len());
assert_eq!(typed.0, "HELLO, KELEUSMA!");
assert_eq!(typed.0.len(), 16);
}