use crate::compiler::BytecodeCompiler;
use crate::executor::{VMConfig, VirtualMachine};
use crate::type_tracking::NativeKind;
use shape_value::{KindedSlot, VMError};
pub fn eval_raw(source: &str) -> (u64, Option<NativeKind>) {
let program = shape_ast::parser::parse_program(source).expect("parse failed");
let compiler = BytecodeCompiler::new();
let bytecode = compiler.compile(&program).expect("compile failed");
let kind = top_level_return_kind(&bytecode);
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(bytecode);
let bits = vm.execute_raw(None).expect("execution failed");
(bits, kind)
}
#[inline]
fn top_level_return_kind(program: &crate::bytecode::BytecodeProgram) -> Option<NativeKind> {
program.top_level_frame.as_ref()?.return_kind
}
pub fn eval(source: &str) -> KindedSlot {
let program = shape_ast::parser::parse_program(source).expect("parse failed");
let compiler = BytecodeCompiler::new();
let bytecode = compiler.compile(&program).expect("compile failed");
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(bytecode);
vm.execute(None).expect("execution failed")
}
pub fn eval_result(source: &str) -> Result<KindedSlot, VMError> {
let program = shape_ast::parser::parse_program(source)
.map_err(|e| VMError::RuntimeError(format!("{:?}", e)))?;
let compiler = BytecodeCompiler::new();
let bytecode = compiler
.compile(&program)
.map_err(|e| VMError::RuntimeError(format!("{:?}", e)))?;
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(bytecode);
vm.execute(None)
}
pub fn eval_with_kind(source: &str, expected: NativeKind) -> KindedSlot {
let (bits, _) = eval_raw(source);
KindedSlot::new(shape_value::ValueSlot::from_raw(bits), expected)
}
pub fn compile(source: &str) -> crate::bytecode::BytecodeProgram {
let program = shape_ast::parser::parse_program(source).expect("parse failed");
let compiler = BytecodeCompiler::new();
compiler.compile(&program).expect("compile failed")
}
pub fn eval_with_prelude(source: &str) -> KindedSlot {
let program = shape_ast::parser::parse_program(source).expect("parse failed");
let mut loader = shape_runtime::module_loader::ModuleLoader::new();
let (graph, stdlib_names, prelude_imports) =
crate::module_resolution::build_graph_and_stdlib_names(&program, &mut loader, &[])
.expect("graph build failed");
let mut compiler = BytecodeCompiler::new();
compiler.stdlib_function_names = stdlib_names;
let bytecode = compiler
.compile_with_graph_and_prelude(&program, graph, &prelude_imports)
.expect("compile failed");
let mut vm = VirtualMachine::new(VMConfig::default());
vm.load_program(bytecode);
vm.execute(None).expect("execution failed")
}
pub fn compile_with_prelude(
source: &str,
) -> Result<crate::bytecode::BytecodeProgram, VMError> {
let program = shape_ast::parser::parse_program(source)
.map_err(|e| VMError::RuntimeError(format!("{:?}", e)))?;
let mut loader = shape_runtime::module_loader::ModuleLoader::new();
let (graph, stdlib_names, prelude_imports) =
crate::module_resolution::build_graph_and_stdlib_names(&program, &mut loader, &[])
.map_err(|e| VMError::RuntimeError(format!("{:?}", e)))?;
let mut compiler = BytecodeCompiler::new();
compiler.stdlib_function_names = stdlib_names;
compiler
.compile_with_graph_and_prelude(&program, graph, &prelude_imports)
.map_err(|e| VMError::RuntimeError(format!("{:?}", e)))
}
pub fn eval_typed_i64(source: &str) -> i64 {
eval_with_kind(source, NativeKind::Int64)
.as_i64()
.expect("eval_typed_i64: result is not an integer")
}
pub fn eval_typed_f64(source: &str) -> f64 {
eval_with_kind(source, NativeKind::Float64)
.as_f64()
.expect("eval_typed_f64: result is not a float")
}
pub fn eval_typed_bool(source: &str) -> bool {
eval_with_kind(source, NativeKind::Bool)
.as_bool()
.expect("eval_typed_bool: result is not a boolean")
}
#[cfg(test)]
mod kind_hint_api_tests {
use super::*;
#[test]
fn eval_returns_int_kindedslot_today() {
let v = eval("42");
assert_eq!(v.as_i64(), Some(42));
}
#[test]
fn eval_raw_returns_bits_and_legacy_none_kind() {
let (bits, kind) = eval_raw("42");
assert_eq!(
kind,
Some(NativeKind::Int64),
"Int literal at top-level should now stamp Int64 return kind",
);
assert_eq!(bits as i64, 42, "raw bits decode to 42 as native i64");
}
#[test]
fn eval_with_kind_forces_int64_decode_on_raw_bits() {
let raw_bits = 42i64 as u64;
let v = KindedSlot::new(
shape_value::ValueSlot::from_raw(raw_bits),
NativeKind::Int64,
);
assert_eq!(v.as_i64(), Some(42));
}
#[test]
fn eval_typed_helpers_work_when_program_emits_raw_bits() {
let raw = 100i64 as u64;
assert_eq!(
KindedSlot::new(
shape_value::ValueSlot::from_raw(raw),
NativeKind::Int64
)
.as_i64()
.unwrap(),
100
);
let raw = 2.5f64.to_bits();
assert_eq!(
KindedSlot::new(
shape_value::ValueSlot::from_raw(raw),
NativeKind::Float64
)
.as_f64()
.unwrap(),
2.5
);
assert_eq!(
KindedSlot::new(shape_value::ValueSlot::from_raw(1), NativeKind::Bool)
.as_bool()
.unwrap(),
true
);
assert_eq!(
KindedSlot::new(shape_value::ValueSlot::from_raw(0), NativeKind::Bool)
.as_bool()
.unwrap(),
false
);
}
#[test]
fn eval_result_propagates_runtime_errors() {
let r = eval_result("1 / 0");
assert!(r.is_err(), "division by zero should be a runtime error");
}
}