use std::fmt::Display; use std::io::{stdin, stdout, Read, Write}; use rustc_hash::FxHashMap; use crate::error::RuntimeError; use super::lox_std::init_std; use super::Value; pub struct Runtime { globals: FxHashMap, in_stream: Box, out_stream: Box, } impl Runtime { pub fn new(in_stream: Box, out_stream: Box) -> Self { let in_stream = Box::new(std::io::BufReader::new(in_stream)); let mut runtime = Runtime { globals: FxHashMap::default(), in_stream, out_stream, }; init_std(&mut runtime); runtime } pub fn globals(&self) -> &FxHashMap { &self.globals } pub fn get_global(&self, name: &str) -> Result { self.globals .get(name) .cloned() .ok_or_else(|| RuntimeError::GlobalNotDefined { name: name.to_owned() }) } pub fn define_global(&mut self, name: impl Into, value: Value) { let name = name.into(); self.globals.insert(name, value); } pub fn assign_global(&mut self, name: &str, value: Value) -> Result<(), RuntimeError> { if let Some(old_value) = self.globals.get_mut(name) { *old_value = value; Ok(()) } else { Err(RuntimeError::GlobalNotDefined { name: name.to_owned() }) } } } impl Default for Runtime { fn default() -> Self { Runtime::new(Box::new(std::io::BufReader::new(stdin())), Box::new(stdout())) } } impl std::fmt::Debug for Runtime { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Runtime").field("globals", &self.globals).finish() } } impl Display for Runtime { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Globals:")?; for (name, value) in self.globals.iter() { write!(f, "\n\t{name} = {value}")?; } Ok(()) } } impl Drop for Runtime { fn drop(&mut self) { self.out_stream.flush().unwrap(); } } impl Read for Runtime { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.in_stream.read(buf) } } impl Write for Runtime { fn write(&mut self, buf: &[u8]) -> std::io::Result { self.out_stream.write(buf) } fn flush(&mut self) -> std::io::Result<()> { self.out_stream.flush() } }