1use crate::error::Result;
7use crate::eval::Interpreter;
8use crate::value::Value;
9
10pub struct ReplSession<'i, H> {
16 interp: &'i mut Interpreter<H>,
17}
18
19impl<'i, H: 'static> ReplSession<'i, H> {
20 pub fn new(interp: &'i mut Interpreter<H>) -> Self {
21 Self { interp }
22 }
23
24 pub fn eval_str(&mut self, input: &str, host: &mut H) -> Result<Value> {
27 let forms = tatara_lisp::read_spanned(input)?;
28 let mut last = Value::Nil;
29 for form in &forms {
30 last = self.interp.eval_spanned(form, host)?;
31 }
32 Ok(last)
33 }
34
35 pub fn is_complete(input: &str) -> bool {
42 let mut depth: i32 = 0;
43 let mut in_string = false;
44 let mut chars = input.chars().peekable();
45 while let Some(c) = chars.next() {
46 if in_string {
47 if c == '\\' {
48 chars.next();
49 continue;
50 }
51 if c == '"' {
52 in_string = false;
53 }
54 continue;
55 }
56 match c {
57 '"' => in_string = true,
58 '(' => depth += 1,
59 ')' => depth -= 1,
60 ';' => {
61 for nc in chars.by_ref() {
62 if nc == '\n' {
63 break;
64 }
65 }
66 }
67 _ => {}
68 }
69 if depth < 0 {
70 return true; }
72 }
73 depth == 0 && !in_string
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 struct NoHost;
82
83 #[test]
84 fn session_evaluates_last_form() {
85 let mut i: Interpreter<NoHost> = Interpreter::new();
86 let mut s = ReplSession::new(&mut i);
87 let mut host = NoHost;
88 let v = s.eval_str("42", &mut host).unwrap();
89 assert!(matches!(v, Value::Int(42)));
90 }
91
92 #[test]
93 fn session_returns_last_of_multiple() {
94 let mut i: Interpreter<NoHost> = Interpreter::new();
95 let mut s = ReplSession::new(&mut i);
96 let mut host = NoHost;
97 let v = s.eval_str("1 2 3", &mut host).unwrap();
98 assert!(matches!(v, Value::Int(3)));
99 }
100
101 #[test]
102 fn is_complete_balanced() {
103 assert!(ReplSession::<()>::is_complete("42"));
104 assert!(ReplSession::<()>::is_complete("(foo bar)"));
105 assert!(ReplSession::<()>::is_complete("(a (b c) d)"));
106 assert!(ReplSession::<()>::is_complete("()"));
107 }
108
109 #[test]
110 fn is_complete_unbalanced_open() {
111 assert!(!ReplSession::<()>::is_complete("(foo"));
112 assert!(!ReplSession::<()>::is_complete("(a (b"));
113 }
114
115 #[test]
116 fn is_complete_ignores_parens_in_strings() {
117 assert!(ReplSession::<()>::is_complete("\"(\""));
118 assert!(ReplSession::<()>::is_complete("(foo \"a (b c)\")"));
119 }
120
121 #[test]
122 fn is_complete_ignores_comments() {
123 assert!(ReplSession::<()>::is_complete("42 ; a ( comment"));
124 }
125}