sim_lib_server/repl/
runtime.rs1use sim_codec::{Input, encode_with_codec};
2use sim_kernel::{
3 Cx, EncodeOptions, Expr, ReadPolicy, Result, Symbol, Value, eval_fabric_capability,
4};
5
6use crate::{Connection, ensure_installed_codec};
7
8use super::spec::DriverSpec;
9
10#[derive(Clone)]
12pub enum ReplOutput {
13 Driver,
15 String,
17}
18
19pub struct ReplOptions {
21 pub connection: Connection,
23 pub codec: Symbol,
25 pub prompt: String,
27 pub driver: DriverSpec,
29 pub input: Option<String>,
31 pub output: ReplOutput,
33}
34
35pub fn run_repl(cx: &mut Cx, options: ReplOptions) -> Result<Value> {
39 cx.require(&eval_fabric_capability())?;
40 for capability in options.driver.required_capabilities() {
41 cx.require(&capability)?;
42 }
43 ensure_installed_codec(cx, &options.codec)?;
44 if let Some(input) = options.input {
45 let rendered = run_once(cx, &options.connection, &options.codec, &input)?;
46 return match options.output {
47 ReplOutput::String => cx.factory().string(rendered),
48 ReplOutput::Driver => cx.factory().nil(),
49 };
50 }
51
52 let mut driver = options.driver.create_driver(cx)?;
53 while let Some(input) = driver.read_line(cx, &options.prompt)? {
54 if input.trim().is_empty() {
55 continue;
56 }
57 match run_once(cx, &options.connection, &options.codec, &input) {
58 Ok(rendered) => {
59 driver.write_output(cx, &(rendered + "\n"))?;
60 }
61 Err(err) => {
62 driver.write_output(cx, &format!("error: {err}\n"))?;
63 }
64 }
65 }
66 cx.factory().nil()
67}
68
69fn run_once(cx: &mut Cx, connection: &Connection, codec: &Symbol, input: &str) -> Result<String> {
70 let expr = lower_repl_expr(sim_codec::decode_with_codec(
71 cx,
72 codec,
73 Input::Text(input.to_owned()),
74 ReadPolicy::default(),
75 )?);
76 let value = connection.request(cx, expr, None, Vec::new())?;
77 render_value(cx, codec, &value)
78}
79
80pub(crate) fn lower_repl_expr(expr: Expr) -> Expr {
81 match expr {
82 Expr::List(items) => {
83 let mut items = items.into_iter();
84 let Some(operator) = items.next() else {
85 return Expr::List(Vec::new());
86 };
87 Expr::Call {
88 operator: Box::new(lower_repl_operator(operator)),
89 args: items.map(lower_repl_expr).collect(),
90 }
91 }
92 Expr::Vector(items) => Expr::Vector(items.into_iter().map(lower_repl_expr).collect()),
93 Expr::Map(entries) => Expr::Map(
94 entries
95 .into_iter()
96 .map(|(key, value)| (key, lower_repl_expr(value)))
97 .collect(),
98 ),
99 Expr::Set(items) => Expr::Set(items.into_iter().map(lower_repl_expr).collect()),
100 Expr::Block(items) => Expr::Block(items.into_iter().map(lower_repl_expr).collect()),
101 Expr::Annotated { expr, annotations } => Expr::Annotated {
102 expr: Box::new(lower_repl_expr(*expr)),
103 annotations,
104 },
105 other => other,
106 }
107}
108
109fn lower_repl_operator(expr: Expr) -> Expr {
110 match expr {
111 Expr::Symbol(symbol) => Expr::Symbol(match symbol.name.as_ref() {
112 "+" if symbol.namespace.is_none() => Symbol::qualified("math", "add"),
113 "-" if symbol.namespace.is_none() => Symbol::qualified("math", "sub"),
114 "*" if symbol.namespace.is_none() => Symbol::qualified("math", "mul"),
115 "/" if symbol.namespace.is_none() => Symbol::qualified("math", "div"),
116 _ => symbol,
117 }),
118 other => lower_repl_expr(other),
119 }
120}
121
122fn render_value(cx: &mut Cx, codec: &Symbol, value: &Value) -> Result<String> {
123 let expr = value.object().as_expr(cx)?;
124 encode_with_codec(cx, codec, &expr, EncodeOptions::default())?.into_text()
125}