diff --git a/src/builtin_function/assign.rs b/src/builtin_function/assign.rs index 0b049cd..cb7d5c0 100644 --- a/src/builtin_function/assign.rs +++ b/src/builtin_function/assign.rs @@ -34,7 +34,7 @@ mod tests { #[test] fn assign_value_and_variable() { - let mut runtime = Runtime::new(|_| {}, || "".to_string()); + let mut runtime = Runtime::new(Box::new(|_| {}), Box::new(|| "".to_string())); assign( &mut runtime, diff --git a/src/builtin_function/control_flow/new_tag.rs b/src/builtin_function/control_flow/new_tag.rs index 31ebd02..9bac30d 100644 --- a/src/builtin_function/control_flow/new_tag.rs +++ b/src/builtin_function/control_flow/new_tag.rs @@ -28,7 +28,7 @@ mod tests { #[test] fn new_tag_test() { - let mut runtime = Runtime::new(|_| {}, || "".to_string()); + let mut runtime = Runtime::new(Box::new(|_| {}), Box::new(|| "".to_string())); new_tag( &mut runtime, diff --git a/src/builtin_function/debugger/dump.rs b/src/builtin_function/debugger/dump.rs index 6dacc35..49d9493 100644 --- a/src/builtin_function/debugger/dump.rs +++ b/src/builtin_function/debugger/dump.rs @@ -4,13 +4,22 @@ use crate::{ }; pub fn dump(runtime: &mut Runtime, executable: &ExecutableLine) -> Result<()> { - runtime.std_out(format!("------- Memory dump line: {}", executable.line_number).as_str()); + let mut result = String::new(); for variable in runtime.variables.iter() { let name = variable.0; let value = variable.1; - runtime.std_out(format!("{} -> ${}", value.to_string(), name).as_str()) + result.push_str(format!("{} -> ${}\n", value.to_string(), name).as_str()) } - runtime.std_out("------- Memory dump ends"); + result.pop(); + runtime.std_out( + format!( + "------- Memory dump line: {} -------", + executable.line_number + ) + .as_str(), + ); + runtime.std_out(&result); + runtime.std_out("------- Memory dump ends -------"); Ok(()) } diff --git a/src/builtin_function/std_io/input.rs b/src/builtin_function/std_io/input.rs index 265d7cf..fa0aa74 100644 --- a/src/builtin_function/std_io/input.rs +++ b/src/builtin_function/std_io/input.rs @@ -1,6 +1,6 @@ +use crate::builtin_function::utils::returns; use crate::common::data_type::DataType; use crate::common::errors::Result; -use crate::builtin_function::utils::returns; use crate::{common::executable::ExecutableLine, runtime::Runtime}; pub fn input(runtime: &mut Runtime, executable: &ExecutableLine) -> Result<()> { @@ -19,7 +19,7 @@ mod tests { #[test] fn input_test() { - let mut runtime = Runtime::new(|_| {}, || "test".to_string()); + let mut runtime = Runtime::new(Box::new(|_| {}), Box::new(|| "test".to_string())); input( &mut runtime, diff --git a/src/builtin_function/std_io/println.rs b/src/builtin_function/std_io/println.rs index 75635ba..81be322 100644 --- a/src/builtin_function/std_io/println.rs +++ b/src/builtin_function/std_io/println.rs @@ -1,5 +1,5 @@ -use crate::common::errors::Result; use crate::builtin_function::utils::param_to_datatype; +use crate::common::errors::Result; use crate::{common::executable::ExecutableLine, runtime::Runtime}; pub fn println(runtime: &mut Runtime, executable: &ExecutableLine) -> Result<()> { @@ -23,10 +23,10 @@ mod tests { #[test] fn print_string() { let mut runtime = Runtime::new( - |x| { + Box::new(|x| { assert_eq!(x, "test"); - }, - || "".to_string(), + }), + Box::new(|| "".to_string()), ); println( @@ -44,10 +44,10 @@ mod tests { #[test] fn print_int() { let mut runtime = Runtime::new( - |x| { + Box::new(|x| { assert_eq!(x, "2"); - }, - || "".to_string(), + }), + Box::new(|| "".to_string()), ); println( @@ -65,10 +65,10 @@ mod tests { #[test] fn print_many() { let mut runtime = Runtime::new( - |x| { + Box::new(|x| { assert_eq!(x, "2, false"); - }, - || "".to_string(), + }), + Box::new(|| "".to_string()), ); println( diff --git a/src/runners/eval.rs b/src/runners/eval.rs index 8ad010e..f2ac05c 100644 --- a/src/runners/eval.rs +++ b/src/runners/eval.rs @@ -4,7 +4,12 @@ use crate::compile::parser::Parser; use crate::compile::preprocessor::Preprocessor; use crate::runtime::Runtime; -pub fn eval(code: String, std_out: fn(&str), std_in: fn() -> String, on_error: fn(ChapError)) { +pub fn eval<'a>( + code: String, + std_out: Box, + std_in: Box String + 'a>, + mut on_error: impl FnMut(ChapError), +) { let mut runtime = match make_runtime(code, std_out, std_in) { Ok(rt) => rt, Err(e) => { @@ -30,7 +35,11 @@ pub fn eval(code: String, std_out: fn(&str), std_in: fn() -> String, on_error: f } } -fn make_runtime(code: String, std_out: fn(&str), std_in: fn() -> String) -> Result { +fn make_runtime<'a>( + code: String, + std_out: Box, + std_in: Box String + 'a>, +) -> Result> { let mut preprocessor = Preprocessor::default(); let mut parser = Parser::default(); let mut runtime = Runtime::new(std_out, std_in); @@ -51,10 +60,54 @@ fn make_runtime(code: String, std_out: fn(&str), std_in: fn() -> String) -> Resu #[cfg(test)] mod tests { + use crate::common::errors::ChapError; + use super::eval; #[test] fn test_eval() { - eval("3".to_string(), |_| {}, || "".to_string(), |_| {}); + eval( + "3".to_string(), + Box::new(|_| {}), + Box::new(|| "".to_string()), + |_| {}, + ); + } + + #[test] + fn test_eval_closure() { + let input = "INPUT".to_string(); + let mut output = "".to_string(); + + eval( + "input -> $a; $a -> print".to_string(), + Box::new(|out| { + output = out.to_string(); + }), + Box::new(|| { + return input.clone(); + }), + |_| {}, + ); + assert_eq!(input, output); + } + + #[test] + fn test_eval_error() { + let mut error: Option = None; + eval( + "a".to_string(), + Box::new(|_| {}), + Box::new(|| { + return "".to_string(); + }), + |e| { + error = Some(e); + }, + ); + + if let None = error { + assert!(false, "error not happened"); + } } } diff --git a/src/runners/file_executor.rs b/src/runners/file_executor.rs index be7a78a..4adafde 100644 --- a/src/runners/file_executor.rs +++ b/src/runners/file_executor.rs @@ -7,16 +7,16 @@ pub fn file_executor(file_name: &str) { let code = read_to_string(file_name).unwrap(); eval( code, - |msg| { + Box::new(|msg| { println!("{}", msg); - }, - || { + }), + Box::new(|| { let mut buffer = String::new(); let stdin = io::stdin(); // We get `Stdin` here. stdin.read_line(&mut buffer).unwrap(); buffer = buffer.replace('\n', "").trim().to_string(); buffer - }, + }), |e| { e.exit_with_error(); }, diff --git a/src/runners/repl.rs b/src/runners/repl.rs index 792a629..da5613a 100644 --- a/src/runners/repl.rs +++ b/src/runners/repl.rs @@ -16,10 +16,10 @@ pub fn start_repl() { let mut reader = DefaultEditor::new().unwrap(); // TODO: handle error let mut runtime = Runtime::new( - |msg| { + Box::new(|msg| { println!("{}", msg); - }, - || String::from(""), + }), + Box::new(|| String::from("")), ); loop { diff --git a/src/runtime.rs b/src/runtime.rs index 5dfb6de..bbc159b 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -3,17 +3,20 @@ use crate::common::errors::Result; use crate::common::executable::ExecutableLine; use std::collections::HashMap; -pub struct Runtime { +pub struct Runtime<'a> { pub executables: Vec, pub variables: HashMap, // pub tags: HashMap, // pub current_line: usize, - pub std_out: fn(&str), - pub std_in: fn() -> String, + pub std_out: Box, + pub std_in: Box String + 'a>, } -impl Runtime { - pub fn new(std_out: fn(&str), std_in: fn() -> String) -> Self { +impl<'a> Runtime<'a> { + pub fn new( + std_out: Box, + std_in: Box String + 'a>, + ) -> Self { Self { executables: vec![], variables: HashMap::new(), @@ -43,11 +46,11 @@ impl Runtime { Ok(()) } - pub fn std_out(&self, msg: &str) { + pub fn std_out(&mut self, msg: &str) { (self.std_out)(msg); } - pub fn std_in(&self) -> String { + pub fn std_in(&mut self) -> String { (self.std_in)() } } @@ -61,7 +64,7 @@ mod tests { #[test] fn simple_execution_test() { - let mut rt = Runtime::new(|_| {}, || "".to_string()); + let mut rt = Runtime::new(Box::new(|_| {}), Box::new(|| "".to_string())); assert_eq!(rt.current_line, 0); rt.on_new_line(ExecutableLine::new( @@ -82,10 +85,10 @@ mod tests { #[test] fn runtime_std_test() { let mut rt = Runtime::new( - |x| { + Box::new(|x| { assert_eq!(x, "the text"); - }, - || "the text".to_string(), + }), + Box::new(|| "the text".to_string()), ); rt.on_new_line(ExecutableLine::new(