use oxide_compiler::compiler::Compiler; use oxide_parser::Allocator; use oxide_types::value::JsValue; use oxide_vm::vm::Vm; fn eval(source: &str) -> String { let allocator = Allocator::default(); let program = match oxide_parser::parse(&allocator, source) { Ok(p) => p, Err(e) => return format!("parse error: {}", e[0].message), }; let module = match Compiler::new().compile(&program) { Ok(m) => m, Err(e) => return format!("compile error: {}", e), }; let mut vm = Vm::new(); match vm.run(&module) { Ok(result) => format!("{}", result), Err(e) => format!("vm error: {}", e), } } fn eval_val(source: &str) -> (Vm, Result) { let allocator = Allocator::default(); let program = match oxide_parser::parse(&allocator, source) { Ok(p) => p, Err(e) => return (Vm::new(), Err(format!("parse error: {}", e[0].message))), }; let module = match Compiler::new().compile(&program) { Ok(m) => m, Err(e) => return (Vm::new(), Err(format!("compile error: {}", e))), }; let mut vm = Vm::new(); let result = vm.run(&module); (vm, result) } fn to_str(vm: &Vm, val: JsValue) -> String { if val.is_string() { vm.kernel_core().string_forge().lookup(val.as_string_index()).unwrap_or_default() } else { format!("{}", val) } } // -- Template literal tests -- #[test] fn template_no_expressions() { let (vm, result) = eval_val("`hello`"); assert_eq!(to_str(&vm, result.unwrap()), "hello"); } #[test] fn template_single_expression() { let (vm, result) = eval_val("const name = 'world'; `hello ${name}`"); assert_eq!(to_str(&vm, result.unwrap()), "hello world"); } #[test] fn template_multiple_expressions() { let (vm, result) = eval_val("`a${1}b${2}c`"); assert_eq!(to_str(&vm, result.unwrap()), "a1b2c"); } #[test] fn template_expression_only() { let (vm, result) = eval_val("const x = 'foo', y = 'bar'; `${x}${y}`"); assert_eq!(to_str(&vm, result.unwrap()), "foobar"); } #[test] fn template_empty() { let (vm, result) = eval_val("``"); assert_eq!(to_str(&vm, result.unwrap()), ""); } #[test] fn template_with_numbers() { let (vm, result) = eval_val("`value is ${42}`"); assert_eq!(to_str(&vm, result.unwrap()), "value is 42"); } #[test] fn template_numeric_expression() { let (vm, result) = eval_val("`${1 + 1}`"); assert_eq!(to_str(&vm, result.unwrap()), "2"); } // -- Template tagging tests (basic) -- #[test] fn tagged_template_basic() { // Use Math.max as a simple native tag function to verify the CALL dispatch works. // Math.max(cooked_array, raw_array, 42) should return 42 (the max of the args). // This avoids bytecode function call complexity. let result = eval("Math.max`hello ${42} world`"); // Math.max on the args should work - just verify it doesn't crash assert!(!result.starts_with("vm error:"), "Tagged template should not crash, got: {}", result); } #[test] fn tagged_template_compiles_no_error() { // Verify that tagged templates compile without error (even if the tag // function behavior isn't fully tested) let result = eval("function t(s,v){ return s[0]+v; } t`x${1}`"); assert!(!result.starts_with("compile error:"), "Tagged template should compile"); }