aboutsummaryrefslogtreecommitdiff
path: root/discocaml/discocaml.ml
blob: 676cd67302d8295a4fea5f9383b5e235d7aed8fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
open Ast

type command = [ `Parse | `DrawTree | `RunCBN | `RunCBV | `StepCBN | `StepCBV ]

let command_of_yojson = function
  | `String "Parse" -> Ok `Parse
  | `String "DrawTree" -> Ok `DrawTree
  | `String "RunCBN" -> Ok `RunCBN
  | `String "RunCBV" -> Ok `RunCBV
  | `String "StepCBN" -> Ok `StepCBN
  | `String "StepCBV" -> Ok `StepCBV
  | _ -> Error "invalid command"

type request = { expr : string; command : command }
[@@deriving of_yojson { exn = true }]

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
  | `Exprs of string option * string array
  | `Graphviz of string ]
[@@deriving to_yojson { exn = true }]

let%expect_test _ =
  Yojson.Safe.to_channel stdout (response_to_yojson (`Error "foo"));
  [%expect {| ["Error","foo"] |}]

let%expect_test _ =
  Yojson.Safe.to_channel stdout
    (response_to_yojson
       (`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
    let expr = Parse.expression buf in
    let expr = expr_of_parsetree expr in

    let expr_response ?(note : string option) (expr : expr ast) : response =
      `Expr
        {
          expr = show_expr expr;
          has_redex = Option.is_some (Eval.find_redex_cbn expr);
          note;
        }
    in

    let step_with (find_redex : expr ast -> expr index option) (expr : expr ast)
        : expr ast =
      match find_redex expr with
      | 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
    | `StepCBN ->
        expr_response ~note:"After one CBN step:"
          (step_with Eval.find_redex_cbn expr)
    | `StepCBV ->
        expr_response ~note:"After one CBV step:"
          (step_with Eval.find_redex_cbv expr)
    | `DrawTree -> `Graphviz (Draw_tree.draw_tree expr)
    | `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
  | exn -> `Error ("uncaught exception: " ^ Printexc.to_string exn)