aboutsummaryrefslogtreecommitdiffstats
path: root/lib/nix/printer.ml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/nix/printer.ml')
-rw-r--r--lib/nix/printer.ml176
1 files changed, 176 insertions, 0 deletions
diff --git a/lib/nix/printer.ml b/lib/nix/printer.ml
new file mode 100644
index 0000000..57e81f4
--- /dev/null
+++ b/lib/nix/printer.ml
@@ -0,0 +1,176 @@
1open Core
2open Types
3open PPrint
4
5let rec escape_chlist = function
6 | [] -> []
7 | '$' :: '{' :: l' -> '\\' :: '$' :: '{' :: escape_chlist l'
8 | '\n' :: l' -> '\\' :: 'n' :: escape_chlist l'
9 | '\r' :: l' -> '\\' :: 'r' :: escape_chlist l'
10 | '\t' :: l' -> '\\' :: 't' :: escape_chlist l'
11 | '\\' :: l' -> '\\' :: '\\' :: escape_chlist l'
12 | '"' :: l' -> '\\' :: '"' :: escape_chlist l'
13 | c :: l' -> c :: escape_chlist l'
14
15let escape_string s = s |> String.to_list |> escape_chlist |> String.of_list
16let out_width = ref 80
17let set_width i = out_width := i
18let indent = ref 2
19let set_indent i = indent := i
20
21let rec doc_of_expr = function
22 | BinaryOp (op, lhs, rhs) ->
23 let lhs_doc = maybe_parens_bop op `Left lhs
24 and rhs_doc = maybe_parens_bop op `Right rhs in
25 infix !indent 1 (doc_of_bop op) lhs_doc rhs_doc
26 | UnaryOp (op, e) -> precede (doc_of_uop op) (maybe_parens (prec_of_uop op) e)
27 | Cond (e1, e2, e3) ->
28 surround !indent 1
29 (soft_surround !indent 1 (string "if") (doc_of_expr e1) (string "then"))
30 (doc_of_expr e2)
31 (string "else" ^^ nest !indent (break 1 ^^ doc_of_expr e3))
32 | With (e1, e2) ->
33 flow (break 1) [ string "with"; doc_of_expr e1 ^^ semi; doc_of_expr e2 ]
34 | Assert (e1, e2) ->
35 flow (break 1) [ string "assert"; doc_of_expr e1 ^^ semi; doc_of_expr e2 ]
36 | Test (e, path) ->
37 maybe_parens 4 e ^^ space ^^ string "?"
38 ^^ group (break 1 ^^ separate_map dot doc_of_expr path)
39 | SetLet bs ->
40 surround !indent 1
41 (string "let " ^^ lbrace)
42 (group (separate_map (break 1) doc_of_binding bs))
43 rbrace
44 | Let (bs, e) ->
45 surround !indent 1 (string "let")
46 (separate_map (break 1) doc_of_binding bs)
47 (prefix !indent 1 (string "in") (doc_of_expr e))
48 | Val v -> doc_of_val v
49 | Id id -> string id
50 | Select (e, path, oe) ->
51 maybe_parens 1 e ^^ dot ^^ doc_of_attpath path
52 ^^ optional
53 (fun e ->
54 space ^^ string "or" ^^ nest !indent (break 1 ^^ maybe_parens 1 e))
55 oe
56 | Apply (e1, e2) -> prefix !indent 1 (maybe_parens 2 e1) (maybe_parens 2 e2)
57 | Aquote e -> surround !indent 0 (string "${") (doc_of_expr e) (string "}")
58
59and maybe_parens lvl e =
60 if prec_of_expr e >= lvl then surround !indent 0 lparen (doc_of_expr e) rparen
61 else doc_of_expr e
62
63and maybe_parens_bop op (loc : [ `Left | `Right ]) e =
64 match (loc, assoc_of_bop op) with
65 | (`Left, Some Left | `Right, Some Right)
66 when prec_of_expr e >= prec_of_bop op ->
67 doc_of_expr e
68 | _, _ -> maybe_parens (prec_of_bop op) e
69
70and doc_of_attpath path = separate_map dot doc_of_expr path
71
72and doc_of_paramset (params, kind) =
73 let ps =
74 List.map ~f:doc_of_param params
75 @ if Poly.(kind = Loose) then [ string "..." ] else []
76 in
77 surround !indent 0 lbrace (separate (comma ^^ break 1) ps) rbrace
78
79and doc_of_param (id, oe) =
80 string id ^^ optional (fun e -> qmark ^^ space ^^ doc_of_expr e) oe
81
82and doc_of_binding = function
83 | AttrPath (path, e) ->
84 doc_of_attpath path ^^ space ^^ equals ^^ space ^^ doc_of_expr e ^^ semi
85 | Inherit (oe, ids) ->
86 let id_docs =
87 List.map
88 ~f:(function
89 | Id x | Val (Str (x, [])) -> string x | _ -> assert false)
90 ids
91 in
92 let xs =
93 flow (break 1)
94 (match oe with
95 | Some e -> parens (doc_of_expr e) :: id_docs
96 | None -> id_docs)
97 in
98 soft_surround !indent 0 (string "inherit" ^^ space) xs semi
99
100and doc_of_bop = function
101 | Plus -> plus
102 | Minus -> minus
103 | Mult -> star
104 | Div -> slash
105 | Gt -> rangle
106 | Lt -> langle
107 | Lte -> string "<="
108 | Gte -> string ">="
109 | Eq -> string "=="
110 | Neq -> string "!="
111 | Or -> string "||"
112 | And -> string "&&"
113 | Impl -> string "->"
114 | Merge -> string "//"
115 | Concat -> string "++"
116
117and doc_of_uop = function Negate -> minus | Not -> bang
118
119and doc_of_val = function
120 | Str (start, xs) ->
121 dquotes
122 (string (escape_string start)
123 ^^ concat
124 (List.map
125 ~f:(fun (e, s) ->
126 surround !indent 0 (string "${") (doc_of_expr e)
127 (string "}" ^^ string (escape_string s)))
128 xs))
129 | IStr (i, start, xs) ->
130 let qq = string "''" in
131 let str s =
132 String.split ~on:'\n' s
133 |> List.map ~f:(fun s ->
134 let len = String.length s in
135 let s' =
136 if len >= i then String.sub s ~pos:i ~len:(len - i) else s
137 in
138 string s')
139 |> separate hardline
140 in
141 enclose qq qq
142 (str start
143 ^^ concat
144 (List.map
145 ~f:(fun (e, s) ->
146 enclose (string "${") rbrace (doc_of_expr e) ^^ str s)
147 xs))
148 | Int x | Float x | Path x | SPath x | HPath x | Uri x -> string x
149 | Lambda (pattern, body) ->
150 let pat =
151 match pattern with
152 | Alias id -> string id
153 | ParamSet (None, ps) -> doc_of_paramset ps
154 | ParamSet (Some id, ps) ->
155 doc_of_paramset ps ^^ group (break 1 ^^ at ^^ break 1 ^^ string id)
156 in
157 flow (break 1) [ pat ^^ colon; doc_of_expr body ]
158 | List [] -> lbracket ^^ space ^^ rbracket
159 | List es ->
160 surround !indent 1 lbracket
161 (separate_map (break 1) (maybe_parens 2) es)
162 rbracket
163 | AttSet (Nonrec, []) -> lbrace ^^ space ^^ rbrace
164 | AttSet (Nonrec, bs) ->
165 surround !indent 1 lbrace
166 (group (separate_map (break 1) doc_of_binding bs))
167 rbrace
168 | AttSet (Rec, bs) ->
169 string "rec" ^^ space ^^ doc_of_val (AttSet (Nonrec, bs))
170
171let print chan expr = ToChannel.pretty 0.7 !out_width chan (doc_of_expr expr)
172
173let to_string expr =
174 let buf = Stdlib.Buffer.create 0 in
175 ToBuffer.pretty 0.7 !out_width buf (doc_of_expr expr);
176 Stdlib.Buffer.contents buf