Source file element_gen.ml
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
open Types
module P = Ppxlib
module Ast_builder = Ppxlib.Ast_builder.Default
module Ast_helper = Ppxlib.Ast_helper
let expr_of_runtime_fun ~loc fun_name =
let lid = Longident.Ldot (Lident "Ppx_pyformat_runtime", fun_name) in
Ast_helper.Exp.ident ~loc P.{ txt = lid; loc }
let expr_of_ids ~loc ids : P.expression =
match ids with
| [] -> P.Location.raise_errorf ~loc "the identifier list cannot be empty"
| hd :: tl ->
List.fold_left
(fun acc cur -> Longident.Ldot (acc, cur))
(Longident.Lident hd) tl
|> fun lid -> P.{ txt = lid; loc } |> Ast_helper.Exp.ident ~loc
let padding_of_fill (fill : fill option) =
match fill with
| Some { align = Pad; char_; width } -> Some (char_, width)
| _ -> None
let arg_opt_of_padding ~loc padding : (P.arg_label * P.expression) option =
padding
|> Option.map (fun (char_, width) ->
let char_expr = Ast_builder.echar ~loc char_ in
let width_expr = Ast_builder.eint ~loc width in
let open P in
(Labelled "padding", [%expr [%e char_expr], [%e width_expr]]))
let arg_opt_of_sign ~loc sign : (P.arg_label * P.expression) option =
sign
|> Option.map (fun sign ->
let sign_expr =
let open P in
match sign with
| Plus -> [%expr Ppx_pyformat_runtime.Plus]
| Minus -> [%expr Ppx_pyformat_runtime.Minus]
| Space -> [%expr Ppx_pyformat_runtime.Space]
in
(P.Labelled "sign", sign_expr))
let arg_opt_of_alternate_form ~loc alternate_form :
(P.arg_label * P.expression) option =
alternate_form
|> Option.map (fun alternate_form ->
(P.Labelled "alternate_form", Ast_builder.ebool ~loc alternate_form))
let arg_opt_of_grouping_option ~loc grouping_option :
(P.arg_label * P.expression) option =
grouping_option
|> Option.map (fun grouping_option ->
let open P in
let label = Labelled "grouping_option" in
match grouping_option with
| Comma -> (label, [%expr Ppx_pyformat_runtime.Comma])
| Underscore -> (label, [%expr Ppx_pyformat_runtime.Underscore]))
let grouping_of_grouping_option = function
| Some Underscore -> true
| _ -> false
let arg_opt_of_grouping ~loc grouping : (P.arg_label * P.expression) option =
grouping
|> Option.map (fun grouping ->
(P.Labelled "grouping", Ast_builder.ebool ~loc grouping))
let arg_opt_of_precision ~loc precision : (P.arg_label * P.expression) option =
precision
|> Option.map (fun precision ->
(P.Labelled "precision", Ast_builder.eint ~loc precision))
let arg_opt_of_upper ~loc upper : (P.arg_label * P.expression) option =
upper
|> Option.map (fun upper ->
(P.Labelled "upper", Ast_builder.ebool ~loc upper))
let apply_index ~loc index (expr : P.expression) : P.expression =
match index with
| None -> expr
| Some (List_index idx) ->
let idx_expr = Ast_builder.eint ~loc idx in
let open P in
[%expr List.nth [%e expr] [%e idx_expr]]
let apply_conversion ~loc conversion (expr : P.expression) : P.expression =
match conversion with
| None -> expr
| Some ids ->
let fun_expr = expr_of_ids ~loc ids in
let open P in
[%expr [%e fun_expr] [%e expr]]
let apply_fill ~loc fun_name char_ width (expr : P.expression) : P.expression =
let func_expr = expr_of_runtime_fun ~loc fun_name in
let char_expr = Ast_builder.echar ~loc char_ in
let width_expr = Ast_builder.eint ~loc width in
let open P in
[%expr [%e func_expr] [%e char_expr] [%e width_expr] [%e expr]]
let apply_string_format ~loc ~(fill : fill option) (expr : P.expression) :
P.expression =
match fill with
| Some { align = Left; char_; width } ->
apply_fill ~loc "align_left" char_ width expr
| Some { align = Right; char_; width } ->
apply_fill ~loc "align_right" char_ width expr
| Some { align = Center; char_; width } ->
apply_fill ~loc "align_center" char_ width expr
| _ -> expr
(** apply functions for binary format *)
let apply_format_function
~loc
?fill
?padding
?sign
?alternate_form
?grouping_option
?grouping
?precision
?upper
func_name
(expr : P.expression) : P.expression =
let fun_expr = expr_of_runtime_fun ~loc func_name in
let padding_arg = arg_opt_of_padding ~loc padding in
let sign_arg = arg_opt_of_sign ~loc sign in
let alternate_form_arg = arg_opt_of_alternate_form ~loc alternate_form in
let grouping_option_arg = arg_opt_of_grouping_option ~loc grouping_option in
let grouping_arg = arg_opt_of_grouping ~loc grouping in
let precision_arg = arg_opt_of_precision ~loc precision in
let upper_arg = arg_opt_of_upper ~loc upper in
[
padding_arg;
sign_arg;
alternate_form_arg;
grouping_option_arg;
grouping_arg;
precision_arg;
upper_arg;
Some (P.Nolabel, expr);
]
|> List.filter Option.is_some
|> List.map Option.get
|> Ast_helper.Exp.apply ~loc fun_expr
|> apply_string_format ~loc ~fill
(** apply functions for format *)
let apply_format_spec ~loc format_spec (expr : P.expression) : P.expression =
match format_spec with
| String_format { fill } -> apply_string_format ~loc ~fill expr
| Int_format
{ type_ = Binary; fill; sign; alternate_form; grouping_option; _ } ->
let padding = padding_of_fill fill in
let grouping = grouping_of_grouping_option grouping_option in
apply_format_function ~loc ?fill ?padding ~sign ~alternate_form ~grouping
"int_to_binary" expr
| Int_format { type_ = Char; fill; _ } ->
apply_format_function ~loc ?fill "int_to_char" expr
| Int_format { type_ = Decimal; fill; sign; grouping_option; _ } ->
let padding = padding_of_fill fill in
apply_format_function ~loc ?fill ?padding ~sign ?grouping_option
"int_to_decimal" expr
| Int_format { type_ = Octal; fill; sign; alternate_form; grouping_option; _ }
->
let padding = padding_of_fill fill in
let grouping = grouping_of_grouping_option grouping_option in
apply_format_function ~loc ?fill ?padding ~sign ~alternate_form ~grouping
"int_to_octal" expr
| Int_format
{ type_ = Hex; fill; sign; alternate_form; grouping_option; upper } ->
let padding = padding_of_fill fill in
let grouping = grouping_of_grouping_option grouping_option in
apply_format_function ~loc ?fill ?padding ~sign ~alternate_form ~grouping
~upper "int_to_hexadecimal" expr
| Float_format
{ type_; fill; sign; alternate_form; grouping_option; precision; upper }
->
let func_name =
match type_ with
| Scientific -> "float_to_scientific"
| Fixed -> "float_to_fixed_point"
| General -> "float_to_general"
| Percentage -> "float_to_percentage"
in
let padding = padding_of_fill fill in
apply_format_function ~loc ?fill ?padding ~sign ~alternate_form
?grouping_option ~precision ~upper func_name expr
(** generate string expression according to replacement field *)
let string_expr_of_rfield ~loc (rfield : replacement_field) : P.expression =
(match rfield.arg with
| Digit idx -> [ Utils.get_arg_name idx ]
| Identifier ids -> ids)
|> expr_of_ids ~loc
|> apply_index ~loc rfield.index
|> apply_conversion ~loc rfield.conversion
|> apply_format_spec ~loc rfield.format_spec
(** generate string expression according to element *)
let string_expr_of_element ~loc (element : element) : P.expression =
match element with
| Text str -> Ast_builder.estring ~loc str
| Field rfield -> string_expr_of_rfield ~loc rfield