1use std::io::{BufRead, Write};
2
3use sim_codec::{
4 DecodePosition, DecodedForm, Input, Output, decode_default_with_codec, encode_with_codec,
5};
6use sim_kernel::{Cx, EncodeOptions, Expr, ReadPolicy, Symbol};
7
8pub fn eval_line(cx: &mut Cx, codec: &Symbol, line: &str) -> Result<String, String> {
10 let decoded = match decode_default_with_codec(
11 cx,
12 codec,
13 Input::Text(line.to_owned()),
14 ReadPolicy::default(),
15 DecodePosition::Eval,
16 )
17 .map_err(|err| format!("{err:?}"))?
18 {
19 DecodedForm::Term(term) => Expr::from(term),
20 DecodedForm::Datum(datum) => Expr::from(datum),
21 };
22 let value = cx.eval_expr(decoded).map_err(|err| format!("{err:?}"))?;
23 let expr = value
24 .object()
25 .as_expr(cx)
26 .map_err(|err| format!("{err:?}"))?;
27 match encode_with_codec(cx, codec, &expr, EncodeOptions::default())
28 .map_err(|err| format!("{err:?}"))?
29 {
30 Output::Text(text) => Ok(text),
31 Output::Bytes(_) => Ok("<bytes>".to_owned()),
32 }
33}
34
35pub fn run_repl_lines<R, W>(
38 cx: &mut Cx,
39 codec: &Symbol,
40 reader: R,
41 writer: &mut W,
42) -> Result<(), String>
43where
44 R: BufRead,
45 W: Write,
46{
47 for line in reader.lines() {
48 let line = line.map_err(|err| format!("read stdin: {err}"))?;
49 let trimmed = line.trim();
50 if trimmed.is_empty() {
51 continue;
52 }
53 match eval_line(cx, codec, trimmed) {
54 Ok(result) => writeln!(writer, "{result}"),
55 Err(err) => writeln!(writer, "error: {err}"),
56 }
57 .map_err(|err| format!("write stdout: {err}"))?;
58 }
59 Ok(())
60}
61
62#[cfg(test)]
63mod tests {
64 use std::sync::Arc;
65
66 use sim::kernel::{Cx, DefaultFactory, EagerPolicy, Symbol};
67
68 use super::{eval_line, run_repl_lines};
69
70 fn boot() -> Cx {
71 let mut cx = Cx::new(Arc::new(EagerPolicy), Arc::new(DefaultFactory));
72 sim::runtime::install_core_runtime(&mut cx);
73 sim::numbers_prelude::NumbersPreludeLib::new()
74 .install_all(&mut cx)
75 .unwrap();
76 let lisp = sim::codec_lisp::LispCodecLib::new(cx.registry_mut().fresh_codec_id()).unwrap();
77 cx.load_lib(&lisp).unwrap();
78 cx
79 }
80
81 #[test]
82 fn eval_line_computes_value() {
83 let mut cx = boot();
84 let codec = Symbol::qualified("codec", "lisp");
85
86 let result = eval_line(&mut cx, &codec, "(math/add (math/mul 6 7) 0)").unwrap();
87
88 assert_eq!(result, "42");
89 }
90
91 #[test]
92 fn run_repl_lines_writes_non_empty_results() {
93 let mut cx = boot();
94 let codec = Symbol::qualified("codec", "lisp");
95 let mut output = Vec::new();
96
97 run_repl_lines(
98 &mut cx,
99 &codec,
100 "\n(math/add 1 2)\n".as_bytes(),
101 &mut output,
102 )
103 .unwrap();
104
105 assert_eq!(String::from_utf8(output).unwrap(), "3\n");
106 }
107}