aboutsummaryrefslogtreecommitdiffstats
path: root/bin/run.ml
blob: f39997c19338eeef7b16ccbe8d8ffda1a00a5688 (about) (plain) (blame)
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
open Core

let opts = Settings.opts

module Origin = struct
  type t = Filename of string | Stdin | Interactive

  let to_string = function
    | Filename name -> name
    | Stdin -> "<stdin>"
    | Interactive -> "<interactive>"
end

(* [dir] must be an absolute path *)
let rec find_imports_file dir : (string, string) result =
  let def_filename = Filename.concat dir "importdef.sexp" in
  match Core_unix.access def_filename [ `Read ] with
  | Ok () -> Ok def_filename
  | Error (Core_unix.Unix_error (ENOENT, _, _))
  | Error (Core_unix.Unix_error (EACCES, _, _)) ->
      let parent = Filename.dirname dir in
      if String.(parent = dir) then Error "Could not find importdef.sexp file"
      else find_imports_file (Filename.dirname dir)
  | Error _ -> Error "Could not find importdef.sexp file"

let load_imports ~for_ =
  let cwd = Core_unix.getcwd () in
  let filename =
    match !(opts.imports_def_file) with
    | None -> (
        let dir =
          match for_ with
          | Origin.Filename filename ->
              Filename.to_absolute_exn
                (Filename.dirname filename)
                ~relative_to:cwd
          | Origin.Stdin | Origin.Interactive -> cwd
        in
        match find_imports_file dir with
        | Error _ ->
            printf
              "Note: no importdef.sexp was found / could be accessed; imports \
               will not work\n\
               %!";
            None
        | Ok filename ->
            let relative =
              if Filename.is_absolute filename then
                Filename.of_absolute_exn filename ~relative_to:cwd
              else filename
            in
            printf "Imports definition found at %s\n%!" relative;
            Some filename)
    | Some filename -> Some filename
  in
  match filename with
  | None -> Ok []
  | Some filename -> (
      (* User-provided filenames may not be absolute *)
      let filename_abs = Filename.to_absolute_exn filename ~relative_to:cwd in
      try
        Ok
          (In_channel.read_all filename
          |> Sexp.of_string |> Mininix.Sexp.import_forest_of_sexp
          |> Mininix.Import.materialize
               ~relative_to:(Filename.dirname filename_abs))
      with Sys_error err -> Error ("Failed to read imports definition: " ^ err))

let eval_expr_with_imports ~origin ~imports data =
  let cwd = Core_unix.getcwd () in
  let config = Sexp_pretty.Config.default
  and formatter = Stdlib.Format.formatter_of_out_channel stdout in
  try
    if !(opts.print_input) then printf "==> Input Nix:\n%s\n\n%!" data;
    let nexp = Nix.parse ~filename:(Origin.to_string origin) data in
    if !(opts.print_parsed) then (
      print_string "==> Parsed Nix:\n";
      Nix.Printer.print stdout nexp;
      printf "\n\n%!");
    let nnexp =
      Nix.elaborate
        ~dir:
          (Some
             (match origin with
             | Filename name ->
                 Filename.to_absolute_exn ~relative_to:cwd
                   (Filename.dirname name)
             | Stdin | Interactive -> cwd))
        nexp
    in
    if !(opts.print_elaborated) then (
      print_string "==> Parsed, elaborated Nix:\n";
      Nix.Printer.print stdout nnexp;
      printf "\n\n%!");
    if !(opts.print_nix_sexp) then (
      let nsexp = Nix.Ast.sexp_of_expr nnexp in
      print_string "==> Nix S-expr:\n";
      Sexp_pretty.pp_formatter config formatter nsexp;
      printf "\n%!");
    let mnexp = Mininix.Nix2mininix.from_nix nnexp in
    if !(opts.print_mininix_sexp) then (
      let mnsexp = Mininix.Sexp.expr_to_sexp mnexp in
      print_string "==> Mininix S-expr:\n";
      Sexp_pretty.pp_formatter config formatter mnsexp;
      printf "\n%!");
    let mnwpexp = Mininix.apply_prelude mnexp in
    if !(opts.print_mininix_sexp_w_prelude) then (
      let mnwpsexp = Mininix.Sexp.expr_to_sexp mnwpexp in
      print_string "==> Mininix S-expr (+ prelude):\n";
      Sexp_pretty.pp_formatter config formatter mnwpsexp;
      printf "\n%!");
    let res =
      Mininix.interp_tl ~fuel:!(opts.fuel_amount) ~mode:!(opts.eval_strategy)
        ~imports mnwpexp
    in
    if !(opts.print_result_mininix_sexp) then (
      let ressexp = Mininix.Sexp.val_res_to_sexp res in
      print_string "==> Evaluation result (Mininix S-exp):\n";
      Sexp_pretty.pp_formatter config formatter ressexp;
      printf "\n%!");
    match res with
    | Res (Some v) ->
        let nixv = Mininix.Mininix2nix.from_val v in
        if !(opts.print_result_nix_sexp) then (
          let nixvsexp = Nix.Ast.sexp_of_expr nixv in
          print_string "==> Evaluation result (Nix S-exp):\n";
          Sexp_pretty.pp_formatter config formatter nixvsexp;
          printf "\n%!");
        print_string "==> Evaluation result (Nix):\n";
        Nix.Printer.print stdout nixv;
        printf "\n%!";
        true
    | Res None ->
        printf "Failed to evaluate\n%!";
        false
    | _ ->
        printf "Ran out of fuel\n%!";
        false
  with
  | Nix.ParseError msg ->
      printf "Failed to parse: %s\n%!" msg;
      false
  | Nix.ElaborateError msg ->
      printf "Elaboration failed: %s\n%!" msg;
      false
  | Mininix.Nix2mininix.FromNixError msg ->
      printf "Failed to convert Nix to Mininix: %s\n%!" msg;
      false

let eval_expr ~origin data =
  match load_imports ~for_:origin with
  | Ok imports -> eval_expr_with_imports ~origin ~imports data
  | Error msg ->
      print_endline msg;
      false

let eval_ch ~origin ch = In_channel.input_all ch |> eval_expr ~origin

let eval_file filename =
  In_channel.with_file filename ~binary:true
    ~f:(eval_ch ~origin:(Filename filename))

let eval_stdin () = eval_ch In_channel.stdin ~origin:Stdin