package visitors
An OCaml syntax extension for generating visitor classes
Install
Dune Dependency
Authors
Maintainers
Sources
archive.tar.gz
md5=80fc467552d944dcae0c5d7895cfba64
sha512=42522af2845fab409cdf0766cce83ac1345e0169248252ad74da2d72eefdb5d846dff2ece566667b9d80a8db57dabdbf333c32c50fef9c39f7837e78b3476b5b
doc/src/ppx_deriving_visitors/VisitorsSettings.ml.html
Source file VisitorsSettings.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 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
open Result open VisitorsString open List let sprintf = Printf.sprintf open Ppxlib open Parsetree open Ppx_deriving open VisitorsPlugin open VisitorsAnalysis open VisitorsGeneration (* -------------------------------------------------------------------------- *) (* We can generate classes that adhere to several distinct schemes, listed below. These schemes differ only in the re-building code that is executed after the recursive calls. In [iter], this code does nothing. In [map], it reconstructs a data structure. In [endo], it also reconstructs a data structure, but attempts to preserve sharing. In [reduce], it combines the results of the recursive calls using a monoid operation. In [fold], this code is missing; it is represented by a virtual method. *) type scheme = | Iter | Map | Endo | Reduce | MapReduce | Fold (* -------------------------------------------------------------------------- *) (* The parameters that can be set by the user. *) module type SETTINGS = sig (* The type declarations that we are processing. *) val decls: type_declaration list (* The name of the generated class. *) val name: classe (* The arity of the generated code, e.g., 1 if one wishes to generate [iter] and [map], 2 if one wishes to generate [iter2] and [map2], and so on. *) val arity: int (* The scheme of visitor that we wish to generate (see the definition of the type [scheme] above). *) val scheme: scheme (* [variety] combines the information in [scheme] and [arity]. It is just the string provided by the user. *) val variety: string (* [visit_prefix] is the common prefix used to name the descending visitor methods. It must be nonempty and a valid identifier by itself. Its default value is "visit_". *) val visit_prefix: string (* [build_prefix] is the common prefix used to name the ascending visitor methods. It must be nonempty and a valid identifier by itself. Its default value is "build_". *) val build_prefix: string (* [fail_prefix] is the common prefix used to name the failure methods. It must be nonempty and a valid identifier by itself. Its default value is "fail_". *) val fail_prefix: string (* The classes that the visitor should inherit. If [nude] is [false], the class [VisitorsRuntime.<scheme>] is implicitly prepended to this list. If [nude] is [true], it is not. *) val ancestors: Longident.t list (* [concrete] controls whether the generated class should be concrete or virtual. By default, it is virtual. *) val concrete: bool (* If [irregular] is [true], the regularity check is suppressed; this allows a local parameterized type to be instantiated. The definition of ['a t] can then refer to [int t]. However, in most situations, this will lead to ill-typed generated code. The generated code should be well-typed if [t] is always instantiated in the same manner, e.g., if there are references to [int t] but not to other instances of [t]. *) val irregular: bool (* If [public] is present, then every method is declared private, except the methods whose name appears in the list [public]. *) val public: string list option (* If [polymorphic] is [true], then (possibly polymorphic) type annotations for methods are generated. The function [poly], applied to the name of a type variable (without its quote), tells whether this type variable should receive monomorphic or polymorphic treatment. In the former case, this type variable is dealt with via a visitor method; in the latter case, it is dealt with via a visitor function. *) val polymorphic: bool val poly: tyvar -> bool (* If [data] is [true], then descending visitor methods for data constructors are generated. This allows the user to request per-data-constructor custom behavior by overriding these methods. If [data] is [false], then these methods are not generated. This yields simpler and faster code with fewer customization opportunities. *) val data: bool end (* -------------------------------------------------------------------------- *) (* The supported varieties. *) (* Note that [mapreduce] must appear in this list before [map], as shorter prefixes must be tested last. *) let supported = [ "mapreduce", MapReduce; "map", Map; "iter", Iter; "endo", Endo; "reduce", Reduce; "fold", Fold; ] let valid_varieties = "iter, map, endo, reduce, mapreduce, fold,\n\ iter2, map2, reduce2, mapreduce2, fold2" let invalid_variety loc = raise_errorf ~loc "%s: invalid variety. The valid varieties are\n\ %s." plugin valid_varieties (* [parse_variety] takes a variety, which could be "iter", "map2", etc. and returns a pair of a scheme and an arity. *) let parse_variety loc (s : string) : scheme * int = (* A loop over [supported] tries each supported variety in turn. *) let rec loop supported s = match supported with | (p, scheme) :: supported -> if prefix p s then let s = remainder p s in let i = if s = "" then 1 else int_of_string s in if i <= 0 then failwith "negative integer" else scheme, i else loop supported s | [] -> failwith "unexpected prefix" in (* Start the loop and handle errors. *) try loop supported s with Failure _ -> invalid_variety loc (* -------------------------------------------------------------------------- *) let must_be_valid_method_name_prefix loc p = if not (is_valid_method_name_prefix p) then raise_errorf ~loc "%s: %S is not a valid method name prefix." plugin p let must_be_valid_mod_longident loc m = if not (is_valid_mod_longident m) then raise_errorf ~loc "%s: %S is not a valid module identifier." plugin m let must_be_valid_class_longident loc c = if not (is_valid_class_longident c) then raise_errorf ~loc "%s: %S is not a valid class identifier." plugin c (* -------------------------------------------------------------------------- *) type bool_or_strings = | Bool of bool | Strings of string list let bool_or_strings : bool_or_strings Arg.conv = fun e -> match Arg.bool e, Arg.list Arg.string e with | Ok b, Error _ -> Ok (Bool b) | Error _, Ok alphas -> Ok (Strings alphas) | Error _, Error _ -> Error "Boolean or string list" | Ok _, Ok _ -> assert false (* -------------------------------------------------------------------------- *) (* The option processing code constructs a module of type [SETTINGS]. *) module Parse (O : sig val loc: Location.t val decls: type_declaration list val options: (string * expression) list end) : SETTINGS = struct open O (* Set up a few parsers. *) let bool = Arg.get_expr ~deriver:plugin Arg.bool let string = Arg.get_expr ~deriver:plugin Arg.string let strings = Arg.get_expr ~deriver:plugin (Arg.list Arg.string) let bool_or_strings = Arg.get_expr ~deriver:plugin bool_or_strings (* Default values. *) let name = ref None let arity = ref 1 (* dummy: [variety] is mandatory; see below *) let scheme = ref Iter (* dummy: [variety] is mandatory; see below *) let variety = ref None let visit_prefix = ref "visit_" let build_prefix = ref "build_" let fail_prefix = ref "fail_" let ancestors = ref [] let concrete = ref false let data = ref true let irregular = ref false let nude = ref false let polymorphic = ref false let poly = ref (fun _ -> false) let public = ref None (* Parse every option. *) let () = iter (fun (o, e) -> let loc = e.pexp_loc in match o with | "visit_prefix" -> visit_prefix := string e; must_be_valid_method_name_prefix loc !visit_prefix | "build_prefix" -> build_prefix := string e; must_be_valid_method_name_prefix loc !build_prefix | "fail_prefix" -> fail_prefix := string e; must_be_valid_method_name_prefix loc !fail_prefix | "ancestors" -> ancestors := strings e | "concrete" -> concrete := bool e | "data" -> data := bool e | "irregular" -> irregular := bool e | "name" -> name := Some (string e) | "nude" -> nude := bool e | "polymorphic" -> (* The [polymorphic] parameter can be a Boolean constant or a list of type variable names. If [true], then all type variables are considered polymorphic. If a list of type variables, then only the variables in the list are considered polymorphic. *) begin match bool_or_strings e with | Bool b -> polymorphic := b; poly := (fun _ -> b) | Strings alphas -> let alphas = List.map unquote alphas in polymorphic := true; poly := (fun alpha -> List.mem alpha alphas) end | "monomorphic" -> (* The [monomorphic] parameter is provided as a facility for the user. It means the reverse of [polymorphic]. This is particularly useful when the parameter is a list of type variables: then, only the variables *not* in the list are considered polymorphic. *) begin match bool_or_strings e with | Bool b -> polymorphic := not b; poly := (fun _ -> not b) | Strings alphas -> let alphas = List.map unquote alphas in polymorphic := true; (* yes, [true] *) poly := (fun alpha -> not (List.mem alpha alphas)) end | "public" -> public := Some (strings e) | "variety" -> let v = string e in variety := Some v; let s, a = parse_variety loc v in scheme := s; arity := a; (* [endo] is supported only at arity 1. *) if s = Endo && a > 1 then invalid_variety loc | _ -> (* We could emit a warning, instead of an error, if we find an unsupported option. That might be preferable for forward compatibility. That said, I am not sure that ignoring unknown options is a good idea; it might cause us to generate code that does not work as expected by the user. *) raise_errorf ~loc "%s: option %s is not supported." plugin o ) options (* Export the results. *) let decls = decls let arity = !arity let scheme = !scheme let visit_prefix = !visit_prefix let build_prefix = !build_prefix let fail_prefix = !fail_prefix let ancestors = !ancestors let concrete = !concrete let data = !data let irregular = !irregular let nude = !nude let polymorphic = !polymorphic let poly = !poly let public = !public (* Perform sanity checking. *) (* The parameter [variety] is not optional. *) let variety = match !variety with | None -> raise_errorf ~loc "%s: please specify the variety of the generated class.\n\ e.g. [@@deriving visitors { variety = \"iter\" }]" plugin | Some variety -> variety (* The parameter [name] is optional. If it is absent, then [variety] is used as its default value. *) let name = match !name with | Some name -> (* We expect [name] to be a valid class name. *) if classify name <> LIDENT then raise_errorf ~loc "%s: %s is not a valid class name." plugin name; name | None -> variety (* Every string in the list [ancestors] must be a valid (long) class identifier. *) let () = iter (must_be_valid_class_longident loc) ancestors (* When the variety is [iter], the class [VisitorsRuntime.iter] is an implicit ancestor, and similarly for every variety. *) let ancestors = if nude then ancestors else ("VisitorsRuntime." ^ variety) :: ancestors let ancestors = map parse ancestors (* If [scheme] is [Fold], then [polymorphic] must be [false]. Indeed, we currently cannot generate polymorphic type annotations in that case, as we cannot guess the return types of the visitor methods. *) let () = if scheme = Fold && polymorphic then raise_errorf ~loc "%s: cannot generate polymorphic\n\ type annotations for fold visitors." plugin end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>