rustscript_embedded/
host.rs1use std::fmt;
2use std::path::Path;
3
4pub use vm::Value;
5use vm::{
6 JitConfig, SourceMap, Vm, VmStatus, compile_source, compile_source_file,
7 compile_source_for_repl, render_source_error,
8};
9
10#[derive(Debug, Clone, PartialEq)]
11pub enum RunOutcome {
12 Halted { stack: Vec<Value> },
13}
14
15#[derive(Debug)]
16pub enum EmbeddedError {
17 Compile(String),
18 Runtime(String),
19 Io(std::io::Error),
20}
21
22impl fmt::Display for EmbeddedError {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 match self {
25 Self::Compile(message) | Self::Runtime(message) => f.write_str(message),
26 Self::Io(err) => write!(f, "{err}"),
27 }
28 }
29}
30
31impl std::error::Error for EmbeddedError {}
32
33impl From<std::io::Error> for EmbeddedError {
34 fn from(err: std::io::Error) -> Self {
35 Self::Io(err)
36 }
37}
38
39pub type EmbeddedResult<T> = Result<T, EmbeddedError>;
40
41pub fn run_source(source: &str) -> EmbeddedResult<RunOutcome> {
42 let compiled = compile_source(source)
43 .map_err(|err| render_source_compile_error("<memory>", source, err))?;
44 run_program(compiled.program.with_local_count(compiled.locals))
45}
46
47pub fn run_source_file(path: impl AsRef<Path>) -> EmbeddedResult<RunOutcome> {
48 let path = path.as_ref();
49 let compiled =
50 compile_source_file(path).map_err(|err| EmbeddedError::Compile(err.to_string()))?;
51 run_program(compiled.program.with_local_count(compiled.locals))
52}
53
54pub fn eval_repl_entry(source: &str) -> EmbeddedResult<RunOutcome> {
55 let compiled = compile_source_for_repl(source)
56 .map_err(|err| render_source_compile_error("<repl>", source, err))?;
57 run_program(compiled.program.with_local_count(compiled.locals))
58}
59
60pub fn embedded_jit_config() -> JitConfig {
61 JitConfig {
62 enabled: false,
63 ..JitConfig::default()
64 }
65}
66
67pub fn new_vm(program: vm::Program) -> Vm {
68 let mut vm = Vm::new_with_jit_config(program, embedded_jit_config());
69 vm.set_runtime_print_sink(|rendered| {
70 print!("{rendered}");
71 });
72 vm
73}
74
75pub fn render_value(value: &Value) -> String {
76 vm::format_value(value)
77}
78
79fn run_program(program: vm::Program) -> EmbeddedResult<RunOutcome> {
80 let mut vm = new_vm(program);
81 loop {
82 match vm.run() {
83 Ok(VmStatus::Halted) => {
84 return Ok(RunOutcome::Halted {
85 stack: vm.stack().to_vec(),
86 });
87 }
88 Ok(VmStatus::Yielded) => continue,
89 Ok(VmStatus::Waiting(_)) => vm
90 .wait_for_host_op_blocking()
91 .map_err(|err| EmbeddedError::Runtime(vm::render_vm_error(&vm, &err)))?,
92 Err(err) => return Err(EmbeddedError::Runtime(vm::render_vm_error(&vm, &err))),
93 }
94 }
95}
96
97fn render_source_compile_error(path: &str, source: &str, err: vm::SourceError) -> EmbeddedError {
98 match err {
99 vm::SourceError::Parse(parse) => {
100 let mut source_map = SourceMap::new();
101 let source_id = source_map.add_source(path.to_string(), source.to_string());
102 let parse = parse.with_line_span_from_source(&source_map, source_id);
103 EmbeddedError::Compile(render_source_error(&source_map, &parse, true))
104 }
105 vm::SourceError::Compile(compile) => {
106 let mut source_map = SourceMap::new();
107 source_map.add_source(path.to_string(), source.to_string());
108 EmbeddedError::Compile(vm::render_compile_error(&source_map, &compile, true))
109 }
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn jit_is_disabled() {
119 assert!(!embedded_jit_config().enabled);
120 }
121
122 #[test]
123 fn runs_basic_program() {
124 let _ = eval_repl_entry("print(1 + 2);").expect("program should run");
125 }
126}