diff options
author | Nathan Ringo <nathan@remexre.com> | 2024-01-19 23:06:59 -0600 |
---|---|---|
committer | Nathan Ringo <nathan@remexre.com> | 2024-01-19 23:06:59 -0600 |
commit | 6bea013cc69b8d11c24fd1e6041677e8e9310f66 (patch) | |
tree | 36587659c57a21a2ded67a8f3cb28e4decc9d709 | |
parent | e9ce0f5ca752f044716c17384bd7ef2486d74805 (diff) |
"Run mode" done.
-rw-r--r-- | discocaml/discocaml.ml | 31 | ||||
-rw-r--r-- | src/commands/discocaml.rs | 63 |
2 files changed, 90 insertions, 4 deletions
diff --git a/discocaml/discocaml.ml b/discocaml/discocaml.ml index e244e26..676cd67 100644 --- a/discocaml/discocaml.ml +++ b/discocaml/discocaml.ml @@ -18,7 +18,10 @@ type response_expr = { expr : string; has_redex : bool; note : string option } [@@deriving to_yojson { exn = true }] type response = - [ `Error of string | `Expr of response_expr | `Graphviz of string ] + [ `Error of string + | `Expr of response_expr + | `Exprs of string option * string array + | `Graphviz of string ] [@@deriving to_yojson { exn = true }] let%expect_test _ = @@ -31,6 +34,11 @@ let%expect_test _ = (`Expr { expr = "foo"; has_redex = false; note = None })); [%expect {| ["Expr",{"expr":"foo","has_redex":false,"note":null}] |}] +let%expect_test _ = + Yojson.Safe.to_channel stdout + (response_to_yojson (`Exprs (Some "A", [| "B"; "C" |]))); + [%expect {| ["Exprs","A",["B","C"]] |}] + let handle_request { expr; command } : response = try let buf = Lexing.from_string expr in @@ -52,6 +60,16 @@ let handle_request { expr; command } : response = | Some i -> Eval.reduce expr i | None -> failwith "no redex" in + let run_with (find_redex : expr ast -> expr index option) : + expr ast -> expr ast Seq.t = + let rec loop (expr : expr ast) : expr ast Seq.t = + Seq.cons expr (fun () -> + match find_redex expr with + | Some i -> loop (Eval.reduce expr i) () + | None -> Nil) + in + loop + in match command with | `Parse -> expr_response expr @@ -62,7 +80,16 @@ let handle_request { expr; command } : response = expr_response ~note:"After one CBV step:" (step_with Eval.find_redex_cbv expr) | `DrawTree -> `Graphviz (Draw_tree.draw_tree expr) - | `RunCBN | `RunCBV -> failwith "not implemented" + | `RunCBN -> + `Exprs + ( Some "Evaluating with CBN:", + run_with Eval.find_redex_cbn expr + |> Seq.take 100 |> Seq.map show_expr |> Array.of_seq ) + | `RunCBV -> + `Exprs + ( Some "Evaluating with CBV:", + run_with Eval.find_redex_cbv expr + |> Seq.take 100 |> Seq.map show_expr |> Array.of_seq ) with | Eval.NotARedex expr -> `Error ("not a redex: " ^ show_expr expr) | Failure msg -> `Error msg diff --git a/src/commands/discocaml.rs b/src/commands/discocaml.rs index ed00185..6defac0 100644 --- a/src/commands/discocaml.rs +++ b/src/commands/discocaml.rs @@ -72,10 +72,32 @@ pub enum DiscocamlCommand { /// /// assert_eq!(out, expected); /// ``` +/// +/// ``` +/// # use lambo::{commands::discocaml::*, utils::EnumAsArray}; +/// # use serde::Deserialize; +/// # use serde_json::Deserializer; +/// +/// let example = r#"["Exprs", "A", ["B", "C"]]"#; +/// let expected = DiscocamlResponse::Exprs( +/// "A".to_string(), +/// vec![ +/// "B".to_string(), +/// "C".to_string(), +/// ], +/// ); +/// +/// let mut de = Deserializer::from_str(&example); +/// let out = DiscocamlResponse::deserialize(EnumAsArray(&mut de)).unwrap(); +/// de.end().unwrap(); +/// +/// assert_eq!(out, expected); +/// ``` #[derive(Debug, Deserialize, PartialEq)] #[serde(deny_unknown_fields)] pub enum DiscocamlResponse { Expr(DiscocamlResponseExpr), + Exprs(Option<String>, Vec<String>), Error(String), Graphviz(String), } @@ -196,6 +218,39 @@ where send(CreateInteractionResponse::Message(res)).await?; Ok(()) } + DiscocamlResponse::Exprs(note, exprs) => { + let mut msg = String::new(); + if let Some(note) = ¬e { + msg.push_str(note); + msg.push('\n'); + } + + let mut msg_chars = msg.chars().count(); + for expr in exprs { + let mut bullet = String::new(); + bullet.push_str("1. ```ocaml\n"); + for line in escape_code(&expr).lines() { + bullet.push_str(" "); + bullet.push_str(line); + bullet.push('\n'); + } + bullet.push_str(" ```\n"); + let bullet_chars = bullet.chars().count(); + if msg_chars + bullet_chars > 1995 { + msg.push_str("1. …\n"); + msg_chars += 5; + assert!(msg_chars < 2000); + break; + } + msg.push_str(&bullet); + msg_chars += bullet_chars; + } + assert_eq!(msg.chars().count(), msg_chars); + + let msg = CreateInteractionResponseMessage::new().content(msg); + send(CreateInteractionResponse::Message(msg)).await?; + Ok(()) + } DiscocamlResponse::Graphviz(dot) => { let png = match run_dot(&dot).await { Ok(png) => png, @@ -290,6 +345,11 @@ async fn run_dot(dot: &str) -> Result<BString> { Ok(BString::new(output.stdout)) } +fn escape_code(s: &str) -> String { + // TODO + s.to_string() +} + fn expr_response_message( expr: &DiscocamlResponseExpr, rowid: i64, @@ -300,8 +360,7 @@ fn expr_response_message( msg.push('\n'); } msg.push_str("```ocaml\n"); - // TODO: Escaping - msg.push_str(&expr.expr); + msg.push_str(&escape_code(&expr.expr)); msg.push_str("```\n"); CreateInteractionResponseMessage::new() |