|
| 1 | +module Ot = Pb_codegen_ocaml_type |
| 2 | +module F = Pb_codegen_formatting |
| 3 | + |
| 4 | +let sp = Pb_codegen_util.sp |
| 5 | + |
| 6 | +let field_pattern_match ~r_name ~rf_label field_type = |
| 7 | + match field_type with |
| 8 | + | Ot.Ft_basic_type bt -> |
| 9 | + let decode runtime_f = |
| 10 | + sp "Pbrt_pb_options.%s pb_options_value \"%s\" \"%s\"" runtime_f r_name |
| 11 | + rf_label |
| 12 | + in |
| 13 | + let exp = |
| 14 | + match bt with |
| 15 | + | Ot.Bt_string -> decode "string" |
| 16 | + | Ot.Bt_float -> decode "float" |
| 17 | + | Ot.Bt_int -> decode "int" |
| 18 | + | Ot.Bt_int32 -> decode "int32" |
| 19 | + | Ot.Bt_int64 -> decode "int64" |
| 20 | + | Ot.Bt_uint32 -> sp "`unsigned (%s)" (decode "int32") |
| 21 | + | Ot.Bt_uint64 -> sp "`unsigned (%s)" (decode "int64") |
| 22 | + | Ot.Bt_bool -> decode "bool" |
| 23 | + | Ot.Bt_bytes -> decode "bytes" |
| 24 | + in |
| 25 | + "pb_options_value", exp |
| 26 | + | Ot.Ft_unit -> |
| 27 | + ( "pb_options_value", |
| 28 | + sp "Pbrt_pb_options.unit pb_options_value \"%s\" \"%s\"" r_name rf_label ) |
| 29 | + | Ot.Ft_user_defined_type udt -> |
| 30 | + let f_name = |
| 31 | + let function_prefix = "decode_pb_options" in |
| 32 | + Pb_codegen_util.function_name_of_user_defined ~function_prefix udt |
| 33 | + in |
| 34 | + let value_expression = "(" ^ f_name ^ " pb_options_value)" in |
| 35 | + "pb_options_value", value_expression |
| 36 | + | _ -> assert false |
| 37 | + |
| 38 | +let pb_options_label_of_field_label rf_label = |
| 39 | + match rf_label with |
| 40 | + | "and_" | "as_" | "assert_" | "begin_" | "class_" | "constraint_" | "do_" |
| 41 | + | "done_" | "downto_" | "else_" | "end_" | "exception_" | "external_" |
| 42 | + | "false_" | "for_" | "fun_" | "function_" | "functor_" | "if_" | "in_" |
| 43 | + | "include_" | "inherit_" | "initializer_" | "lazy_" | "let_" | "match_" |
| 44 | + | "method_" | "module_" | "mutable_" | "new_" | "nonrec_" | "object_" | "of_" |
| 45 | + | "open_" | "or_" | "private_" | "rec_" | "sig_" | "struct_" | "then_" | "to_" |
| 46 | + | "true_" | "try_" | "type_" | "unit_" | "val_" | "virtual_" | "when_" |
| 47 | + | "while_" | "with_" | "mod_" | "land_" | "lor_" | "lxor_" | "lsl_" | "lsr_" |
| 48 | + | "asr_" -> |
| 49 | + String.sub rf_label 0 (String.length rf_label - 1) |
| 50 | + | _ -> rf_label |
| 51 | + |
| 52 | +(* Generate all the pattern matches for a record field *) |
| 53 | +let gen_rft_nolabel sc ~r_name ~rf_label (field_type, _, _) = |
| 54 | + let pb_options_label = pb_options_label_of_field_label rf_label in |
| 55 | + |
| 56 | + let match_variable_name, exp = |
| 57 | + field_pattern_match ~r_name ~rf_label field_type |
| 58 | + in |
| 59 | + F.linep sc "| (\"%s\", %s) -> " pb_options_label match_variable_name; |
| 60 | + F.linep sc " v.%s <- %s" rf_label exp |
| 61 | + |
| 62 | +(* Generate all the pattern matches for a repeated field *) |
| 63 | +let gen_rft_repeated_field sc ~r_name ~rf_label repeated_field = |
| 64 | + let _, field_type, _, _, _ = repeated_field in |
| 65 | + |
| 66 | + let pb_options_label = pb_options_label_of_field_label rf_label in |
| 67 | + |
| 68 | + F.linep sc |
| 69 | + "| (\"%s\", Ocaml_protoc_compiler_lib.Pb_option.List_literal l) -> begin" |
| 70 | + pb_options_label; |
| 71 | + |
| 72 | + F.sub_scope sc (fun sc -> |
| 73 | + F.linep sc "v.%s <- List.map (function" rf_label; |
| 74 | + let match_variable_name, exp = |
| 75 | + field_pattern_match ~r_name ~rf_label field_type |
| 76 | + in |
| 77 | + F.linep sc " | %s -> %s" match_variable_name exp; |
| 78 | + F.line sc ") l;"); |
| 79 | + |
| 80 | + F.line sc "end" |
| 81 | + |
| 82 | +let gen_rft_optional_field sc ~r_name ~rf_label optional_field = |
| 83 | + let field_type, _, _, _ = optional_field in |
| 84 | + |
| 85 | + let pb_options_label = pb_options_label_of_field_label rf_label in |
| 86 | + |
| 87 | + let match_variable_name, exp = |
| 88 | + field_pattern_match ~r_name ~rf_label field_type |
| 89 | + in |
| 90 | + |
| 91 | + F.linep sc "| (\"%s\", %s) -> " pb_options_label match_variable_name; |
| 92 | + F.linep sc " v.%s <- Some (%s)" rf_label exp |
| 93 | + |
| 94 | +(* Generate pattern match for a variant field *) |
| 95 | +let gen_rft_variant_field sc ~r_name ~rf_label { Ot.v_constructors; _ } = |
| 96 | + List.iter |
| 97 | + (fun { Ot.vc_constructor; vc_field_type; _ } -> |
| 98 | + let pb_options_label = |
| 99 | + Pb_codegen_util.camel_case_of_constructor vc_constructor |
| 100 | + in |
| 101 | + |
| 102 | + match vc_field_type with |
| 103 | + | Ot.Vct_nullary -> |
| 104 | + F.linep sc "| (\"%s\", _) -> v.%s <- Some %s" pb_options_label rf_label |
| 105 | + vc_constructor |
| 106 | + | Ot.Vct_non_nullary_constructor field_type -> |
| 107 | + let match_variable_name, exp = |
| 108 | + field_pattern_match ~r_name ~rf_label field_type |
| 109 | + in |
| 110 | + F.linep sc "| (\"%s\", %s) -> " pb_options_label match_variable_name; |
| 111 | + F.linep sc " v.%s <- Some (%s (%s))" rf_label vc_constructor exp) |
| 112 | + v_constructors |
| 113 | + |
| 114 | +let gen_rft_assoc_field sc ~r_name ~rf_label ~assoc_type ~key_type ~value_type = |
| 115 | + let pb_options_label = pb_options_label_of_field_label rf_label in |
| 116 | + F.linep sc |
| 117 | + "| (\"%s\", Ocaml_protoc_compiler_lib.Pb_option.Message_literal assoc) ->" |
| 118 | + pb_options_label; |
| 119 | + F.sub_scope sc (fun sc -> |
| 120 | + let value_name, value_exp = |
| 121 | + field_pattern_match ~r_name ~rf_label value_type |
| 122 | + in |
| 123 | + let key_name = "key" in |
| 124 | + let key_exp = |
| 125 | + match key_type with |
| 126 | + | Ot.Bt_string -> "key" |
| 127 | + | Ot.Bt_int -> "(Int.of_string key)" |
| 128 | + | Ot.Bt_int32 -> "(Int32.of_string key)" |
| 129 | + | Ot.Bt_int64 -> "(Int64.of_string key)" |
| 130 | + | Ot.Bt_uint32 -> "(`unsigned (Int32.of_string key))" |
| 131 | + | Ot.Bt_uint64 -> "(`unsigned (Int64.of_string key))" |
| 132 | + | Ot.Bt_bool -> "(Bool.of_string key)" |
| 133 | + | Ot.Bt_float -> |
| 134 | + Printf.eprintf "float cannot be used as a map key type"; |
| 135 | + exit 1 |
| 136 | + | Ot.Bt_bytes -> |
| 137 | + Printf.eprintf "bytes cannot be used as a map key type"; |
| 138 | + exit 1 |
| 139 | + in |
| 140 | + F.line sc "let assoc ="; |
| 141 | + F.sub_scope sc (fun sc -> |
| 142 | + F.line sc "assoc"; |
| 143 | + F.linep sc "|> List.map (fun (%s, %s) -> (%s, %s)) " key_name |
| 144 | + value_name key_exp value_exp; |
| 145 | + F.line sc "|> List.to_seq"; |
| 146 | + (* Passing through [Hashtbl.of_seq] even in the [At_list] case ensures that if there |
| 147 | + is a repeated key we take the last value associated with it. *) |
| 148 | + F.line sc "|> Hashtbl.of_seq"); |
| 149 | + F.line sc "in"; |
| 150 | + let assoc_exp = |
| 151 | + match assoc_type with |
| 152 | + | Ot.At_hashtable -> "assoc" |
| 153 | + | Ot.At_list -> "assoc |> Hashtbl.to_seq |> List.of_seq" |
| 154 | + in |
| 155 | + F.linep sc "v.%s <- %s" rf_label assoc_exp) |
| 156 | + |
| 157 | +(* Generate decode function for a record *) |
| 158 | +let gen_record ?and_ { Ot.r_name; r_fields } sc = |
| 159 | + let mutable_record_name = Pb_codegen_util.mutable_record_name r_name in |
| 160 | + |
| 161 | + F.line sc |
| 162 | + @@ sp "%s decode_pb_options_%s d =" |
| 163 | + (Pb_codegen_util.let_decl_of_and and_) |
| 164 | + r_name; |
| 165 | + |
| 166 | + F.sub_scope sc (fun sc -> |
| 167 | + F.linep sc "let v = default_%s () in" mutable_record_name; |
| 168 | + F.line sc @@ "let assoc = match d with"; |
| 169 | + F.line sc |
| 170 | + @@ " | Ocaml_protoc_compiler_lib.Pb_option.Message_literal assoc -> \ |
| 171 | + assoc"; |
| 172 | + F.line sc @@ " | _ -> assert(false)"; |
| 173 | + (* TODO raise E *) |
| 174 | + F.line sc @@ "in"; |
| 175 | + |
| 176 | + F.line sc "List.iter (function "; |
| 177 | + F.sub_scope sc (fun sc -> |
| 178 | + (* Generate pattern match for all the possible message field *) |
| 179 | + List.iter |
| 180 | + (fun { Ot.rf_label; rf_field_type; _ } -> |
| 181 | + match rf_field_type with |
| 182 | + | Ot.Rft_nolabel nolabel_field -> |
| 183 | + gen_rft_nolabel sc ~r_name ~rf_label nolabel_field |
| 184 | + | Ot.Rft_repeated repeated_field -> |
| 185 | + gen_rft_repeated_field sc ~r_name ~rf_label repeated_field |
| 186 | + | Ot.Rft_variant variant_field -> |
| 187 | + gen_rft_variant_field sc ~r_name ~rf_label variant_field |
| 188 | + | Ot.Rft_optional optional_field -> |
| 189 | + gen_rft_optional_field sc ~r_name ~rf_label optional_field |
| 190 | + | Ot.Rft_required _ -> |
| 191 | + Printf.eprintf |
| 192 | + "Only proto3 syntax supported in pb_options encoding"; |
| 193 | + exit 1 |
| 194 | + | Ot.Rft_associative |
| 195 | + (assoc_type, _, (key_type, _), (value_type, _)) -> |
| 196 | + gen_rft_assoc_field sc ~r_name ~rf_label ~assoc_type ~key_type |
| 197 | + ~value_type) |
| 198 | + r_fields; |
| 199 | + |
| 200 | + (* Unknown fields are simply ignored *) |
| 201 | + F.empty_line sc; |
| 202 | + F.line sc "| (_, _) -> () (*Unknown fields are ignored*)"); |
| 203 | + F.line sc ") assoc;"; |
| 204 | + |
| 205 | + (* Transform the mutable record in an immutable one *) |
| 206 | + F.line sc "({"; |
| 207 | + F.sub_scope sc (fun sc -> |
| 208 | + List.iter |
| 209 | + (fun { Ot.rf_label; _ } -> |
| 210 | + F.linep sc "%s = v.%s;" rf_label rf_label) |
| 211 | + r_fields); |
| 212 | + F.linep sc "} : %s)" r_name) |
| 213 | + |
| 214 | +(* Generate decode function for an empty record *) |
| 215 | +let gen_unit ?and_ { Ot.er_name } sc = |
| 216 | + F.line sc |
| 217 | + @@ sp "%s decode_pb_options_%s d =" |
| 218 | + (Pb_codegen_util.let_decl_of_and and_) |
| 219 | + er_name; |
| 220 | + F.line sc (sp "Pbrt_pb_options.unit d \"%s\" \"%s\"" er_name "empty record") |
| 221 | + |
| 222 | +(* Generate decode function for a variant type *) |
| 223 | +let gen_variant ?and_ { Ot.v_name; v_constructors } sc = |
| 224 | + (* helper function for each constructor case *) |
| 225 | + let process_v_constructor sc { Ot.vc_constructor; vc_field_type; _ } = |
| 226 | + let pb_options_label = |
| 227 | + Pb_codegen_util.camel_case_of_constructor vc_constructor |
| 228 | + in |
| 229 | + |
| 230 | + match vc_field_type with |
| 231 | + | Ot.Vct_nullary -> |
| 232 | + F.linep sc "| (\"%s\", _)::_-> (%s : %s)" pb_options_label vc_constructor |
| 233 | + v_name |
| 234 | + | Ot.Vct_non_nullary_constructor field_type -> |
| 235 | + let match_, exp = |
| 236 | + let r_name = v_name and rf_label = vc_constructor in |
| 237 | + field_pattern_match ~r_name ~rf_label field_type |
| 238 | + in |
| 239 | + |
| 240 | + F.linep sc "| (\"%s\", %s)::_ -> " pb_options_label match_; |
| 241 | + F.linep sc " (%s (%s) : %s)" vc_constructor exp v_name |
| 242 | + in |
| 243 | + |
| 244 | + F.linep sc "%s decode_pb_options_%s pb_options =" |
| 245 | + (Pb_codegen_util.let_decl_of_and and_) |
| 246 | + v_name; |
| 247 | + |
| 248 | + F.sub_scope sc (fun sc -> |
| 249 | + (* even though a variant should be an object with a single field, |
| 250 | + * it is possible other fields are present in the pb_options object. Therefore |
| 251 | + * we still need a loop to iterate over the key/value, even if in 99.99% |
| 252 | + * of the cases it will be a single iteration *) |
| 253 | + F.line sc "let assoc = match pb_options with"; |
| 254 | + F.line sc |
| 255 | + " | Ocaml_protoc_compiler_lib.Pb_option.Message_literal assoc -> assoc"; |
| 256 | + F.line sc " | _ -> assert(false)"; |
| 257 | + (* TODO raise E *) |
| 258 | + F.line sc "in"; |
| 259 | + |
| 260 | + F.line sc "let rec loop = function"; |
| 261 | + F.sub_scope sc (fun sc -> |
| 262 | + (* termination condition *) |
| 263 | + F.linep sc "| [] -> Pbrt_pb_options.E.malformed_variant \"%s\"" v_name; |
| 264 | + |
| 265 | + List.iter (process_v_constructor sc) v_constructors; |
| 266 | + |
| 267 | + F.empty_line sc; |
| 268 | + F.line sc "| _ :: tl -> loop tl"); |
| 269 | + F.line sc "in"; |
| 270 | + F.line sc "loop assoc") |
| 271 | + |
| 272 | +let gen_const_variant ?and_ { Ot.cv_name; cv_constructors } sc = |
| 273 | + F.linep sc "%s decode_pb_options_%s pb_options =" |
| 274 | + (Pb_codegen_util.let_decl_of_and and_) |
| 275 | + cv_name; |
| 276 | + |
| 277 | + F.sub_scope sc (fun sc -> |
| 278 | + F.line sc "match pb_options with"; |
| 279 | + List.iter |
| 280 | + (fun { Ot.cvc_name; cvc_string_value; _ } -> |
| 281 | + F.linep sc |
| 282 | + "| Ocaml_protoc_compiler_lib.Pb_option.Scalar_value \ |
| 283 | + (Constant_literal \"%s\") -> (%s : %s)" |
| 284 | + cvc_string_value cvc_name cv_name) |
| 285 | + cv_constructors; |
| 286 | + F.linep sc "| _ -> Pbrt_pb_options.E.malformed_variant \"%s\"" cv_name) |
| 287 | + |
| 288 | +let gen_struct ?and_ t sc = |
| 289 | + let { Ot.spec; _ } = t in |
| 290 | + let has_encoded = |
| 291 | + match spec with |
| 292 | + | Ot.Record r -> |
| 293 | + gen_record ?and_ r sc; |
| 294 | + true |
| 295 | + | Ot.Variant v -> |
| 296 | + gen_variant ?and_ v sc; |
| 297 | + true |
| 298 | + | Ot.Const_variant v -> |
| 299 | + gen_const_variant ?and_ v sc; |
| 300 | + true |
| 301 | + | Ot.Unit u -> |
| 302 | + gen_unit ?and_ u sc; |
| 303 | + true |
| 304 | + in |
| 305 | + has_encoded |
| 306 | + |
| 307 | +let gen_sig ?and_ t sc = |
| 308 | + let _ = and_ in |
| 309 | + let { Ot.spec; _ } = t in |
| 310 | + |
| 311 | + let f type_name = |
| 312 | + F.linep sc |
| 313 | + "val decode_pb_options_%s : Ocaml_protoc_compiler_lib.Pb_option.value -> \ |
| 314 | + %s" |
| 315 | + type_name type_name; |
| 316 | + F.linep sc |
| 317 | + ("(** [decode_pb_options_%s decoder] decodes a " |
| 318 | + ^^ "[%s] value from [decoder] *)") |
| 319 | + type_name type_name |
| 320 | + in |
| 321 | + |
| 322 | + match spec with |
| 323 | + | Ot.Record { Ot.r_name; _ } -> |
| 324 | + f r_name; |
| 325 | + true |
| 326 | + | Ot.Variant { Ot.v_name; _ } -> |
| 327 | + f v_name; |
| 328 | + true |
| 329 | + | Ot.Const_variant { Ot.cv_name; _ } -> |
| 330 | + f cv_name; |
| 331 | + true |
| 332 | + | Ot.Unit { Ot.er_name; _ } -> |
| 333 | + f er_name; |
| 334 | + true |
| 335 | + |
| 336 | +let ocamldoc_title = "Pb_option.set Decoding" |
| 337 | +let requires_mutable_records = true |
| 338 | + |
| 339 | +let plugin : Pb_codegen_plugin.t = |
| 340 | + let module P = struct |
| 341 | + let gen_sig = gen_sig |
| 342 | + let gen_struct = gen_struct |
| 343 | + let ocamldoc_title = ocamldoc_title |
| 344 | + let requires_mutable_records = requires_mutable_records |
| 345 | + end in |
| 346 | + (module P) |
0 commit comments