package ppxlib
Standard infrastructure for ppx rewriters
Install
Dune Dependency
Authors
Maintainers
Sources
ppxlib-0.36.0.tbz
sha256=5aba1bce14c53108614130110c843d004bf93bd2cf3a0778fd7086b85390a434
sha512=1e3e8fee42fe74bffc178dbcbb2db8ec38dd23e71f6fed3c4c92618cf93892f5847787e6e9abb322f5c85d29a76afde28ce840b42e10fedc14cd82ba578ad06a
doc/src/ppxlib/utils.ml.html
Source file utils.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
open Import (* We want to make sure we only convert actual odoc comments [(** ... *)] and not actual instances of [@@ocaml.doc "..."]. When parsed, both get translated as an attribute. To differentiate them, we can take advantage of the fact that the location attached to the attribute node for (** ... *) comments is equal to the location of the string itself, while for [@@ocaml.doc "..."] they are different. The same is true for [@@@ocaml.text]. *) let get_odoc_contents_if_comment = function | { attr_loc; attr_name = { txt = "doc" | "ocaml.doc" | "text" | "ocaml.text"; _ }; attr_payload = PStr [ { pstr_desc = Pstr_eval ( { pexp_desc = Pexp_constant (Pconst_string (text, loc, _)); _; }, _ ); _; }; ]; } when Location.compare attr_loc loc = 0 -> Some text | _ -> None let prettify_odoc_attributes = object inherit Ast_traverse.map as super method! attribute attr = let attr = super#attribute attr in match get_odoc_contents_if_comment attr with | Some txt -> let open Ast_builder.Default in let loc = Location.none in let delim = Some (Common.valid_string_constant_delimiter txt) in let expr = pexp_constant ~loc (Pconst_string (txt, loc, delim)) in { attr with attr_payload = PStr [ pstr_eval ~loc expr [] ] } | None -> attr end let with_output fn ~binary ~f = match fn with | None | Some "-" -> (* Flipping back and forth from binary to text is not a good idea, so we'll make two simplifying assumptions: 1. Assume that nothing is buffered on stdout before entering [with_output]. That means we don't need to flush the stdout on entry. 2. Assume that nothing else is sent to stdout after [with_output]. That means it is safe to leave stdout channel in binary mode (or text mode if [binary=true]) after the function is done. *) set_binary_mode_out stdout binary; f stdout | Some fn -> Out_channel.with_file fn ~binary ~f module Kind = struct type t = Intf | Impl let of_filename fn : t option = if Stdlib.Filename.check_suffix fn ".ml" then Some Impl else if Stdlib.Filename.check_suffix fn ".mli" then Some Intf else None let describe = function Impl -> "implementation" | Intf -> "interface" let equal : t -> t -> bool = Poly.equal end module Intf_or_impl = struct type t = Intf of signature | Impl of structure let map t (map : Ast_traverse.map) = match t with | Impl x -> Impl (map#structure x) | Intf x -> Intf (map#signature x) let map_with_context t (map : _ Ast_traverse.map_with_context) ctx = match t with | Impl x -> Impl (map#structure ctx x) | Intf x -> Intf (map#signature ctx x) let kind : _ -> Kind.t = function Intf _ -> Intf | Impl _ -> Impl end module Ast_io = struct type input_version = (module OCaml_version) let fall_back_input_version = (module Compiler_version : OCaml_version) (* This should only be used when the input version can't be determined due to loading or preprocessing errors *) type t = { input_name : string; input_version : input_version; ast : Intf_or_impl.t; } type read_error = | Not_a_binary_ast | Unknown_version of string * input_version | Source_parse_error of Location.Error.t * input_version | System_error of Location.Error.t * input_version type input_source = Stdin | File of string type input_kind = Possibly_source of Kind.t * string | Necessarily_binary let read_error_to_string (error : read_error) = match error with | Not_a_binary_ast -> "Error: Not a binary ast" | Unknown_version (s, _) -> "Error: Unknown version " ^ s | Source_parse_error (loc, _) -> "Source parse error:" ^ Location.Error.message loc | System_error (loc, _) -> "System error: " ^ Location.Error.message loc let parse_source_code ~(kind : Kind.t) ~input_name ~prefix_read_from_source ic = (* The input version is determined by the fact that the input will get parsed by the current compiler Parse module *) let input_version = (module Compiler_version : OCaml_version) in try (* To test if a file is an AST file, we have to read the first few bytes of the file. If it is not, we have to parse these bytes and the rest of the file as source code. The compiler just does [seek_on 0] in this case, however this doesn't work when the input is a pipe. What we do is we build a string of the whole source, append the prefix and built a lexing buffer from that. We have to put all the source into the lexing buffer at once this way for source quotation to work in error messages. See ocaml#12238 and ocaml/driver/pparse.ml. *) let all_source = prefix_read_from_source ^ In_channel.input_all ic in let lexbuf = Lexing.from_string all_source in lexbuf.lex_curr_p <- { lexbuf.lex_curr_p with pos_fname = input_name }; Astlib.Location.set_input_lexbuf (Some lexbuf); Skip_hash_bang.skip_hash_bang lexbuf; let ast : Intf_or_impl.t = match kind with | Intf -> Intf (Parse.interface lexbuf) | Impl -> Impl (Parse.implementation lexbuf) in Ok { input_name; input_version; ast } with exn -> ( match Location.Error.of_exn exn with | None -> raise exn | Some error -> Error (Source_parse_error (error, input_version))) let magic_length = String.length Astlib.Config.ast_impl_magic_number let read_magic ic = let buf = Bytes.create magic_length in let len = input ic buf 0 magic_length in let s = Bytes.sub_string buf ~pos:0 ~len in if len = magic_length then Ok s else Error s let set_input_lexbuf input_name = let set_input_lexbuf ic = (* set input lexbuf for error messages. *) let source = In_channel.input_all ic in let lexbuf = Lexing.from_string source in Astlib.Location.set_input_lexbuf (Some lexbuf); lexbuf in match In_channel.with_file ~binary:true input_name ~f:set_input_lexbuf with | (_ : Lexing.lexbuf) -> () | exception Sys_error _ -> () let from_channel ch ~input_kind = let handle_non_binary prefix_read_from_source = match input_kind with | Possibly_source (kind, input_name) -> parse_source_code ~kind ~input_name ~prefix_read_from_source ch | Necessarily_binary -> Error Not_a_binary_ast in (* Marshalled AST must be read in binary mode. Even though we don't know before reading the magic number when the file has a marshalled AST, it is safe to read source files in binary mode. *) set_binary_mode_in ch true; match read_magic ch with | Error s -> handle_non_binary s | Ok s -> ( match Find_version.from_magic s with | Intf (module Input_version : OCaml_version) -> let input_name : string = input_value ch in let ast = input_value ch in let module Input_to_ppxlib = Convert (Input_version) (Js) in set_input_lexbuf input_name; let ast = Intf_or_impl.Intf (Input_to_ppxlib.copy_signature ast) in Ok { input_name; input_version = (module Input_version : OCaml_version); ast; } | Impl (module Input_version : OCaml_version) -> let input_name : string = input_value ch in let ast = input_value ch in let module Input_to_ppxlib = Convert (Input_version) (Js) in set_input_lexbuf input_name; let ast = Intf_or_impl.Impl (Input_to_ppxlib.copy_structure ast) in Ok { input_name; input_version = (module Input_version : OCaml_version); ast; } | Unknown -> if String.equal (String.sub s ~pos:0 ~len:9) (String.sub Astlib.Config.ast_impl_magic_number ~pos:0 ~len:9) || String.equal (String.sub s ~pos:0 ~len:9) (String.sub Astlib.Config.ast_intf_magic_number ~pos:0 ~len:9) then Error (Unknown_version (s, fall_back_input_version)) else handle_non_binary s) let read input_source ~input_kind = try match input_source with | Stdin -> set_binary_mode_in stdin true; from_channel stdin ~input_kind | File fn -> In_channel.with_file fn ~f:(from_channel ~input_kind) with exn -> ( match Location.Error.of_exn exn with | None -> raise exn | Some error -> Error (System_error (error, fall_back_input_version))) let write oc { input_name; input_version = (module Input_version); ast } ~add_ppx_context = let module Ppxlib_to_input = Convert (Js) (Input_version) in let module Ocaml_to_input = Convert (Compiler_version) (Input_version) in match ast with | Intf sg -> let sg = if add_ppx_context then Selected_ast.To_ocaml.copy_signature sg |> Astlib.Ast_metadata.add_ppx_context_sig ~tool_name:"ppx_driver" |> Ocaml_to_input.copy_signature else Ppxlib_to_input.copy_signature sg in output_string oc Input_version.Ast.Config.ast_intf_magic_number; output_value oc input_name; output_value oc sg | Impl st -> let st = if add_ppx_context then Selected_ast.To_ocaml.copy_structure st |> Astlib.Ast_metadata.add_ppx_context_str ~tool_name:"ppx_driver" |> Ocaml_to_input.copy_structure else Ppxlib_to_input.copy_structure st in output_string oc Input_version.Ast.Config.ast_impl_magic_number; output_value oc input_name; output_value oc st module Read_bin = struct type ast = Intf of signature | Impl of structure type t = { ast : ast; input_name : string } let read_binary fn = match In_channel.with_file fn ~f:(from_channel ~input_kind:Necessarily_binary) with | Ok { ast; input_name; _ } -> let ast = match ast with | Impl structure -> Impl structure | Intf signature -> Intf signature in Ok { ast; input_name } | Error e -> Error (read_error_to_string e) let get_ast t = t.ast let get_input_name t = t.input_name end end module System = struct let run_preprocessor ~pp ~input ~output = let command = Printf.sprintf "%s %s > %s" pp (if String.equal input "-" then "" else Stdlib.Filename.quote input) (Stdlib.Filename.quote output) in if Stdlib.Sys.command command = 0 then Ok () else Error (command, Ast_io.fall_back_input_version) end let print_as_compiler_source ppf ast = let module Ppxlib_to_compiler = Convert (Js) (Compiler_version) in match (ast : Intf_or_impl.t) with | Intf sg -> let sg = Ppxlib_to_compiler.copy_signature sg in Astlib.Compiler_pprintast.signature ppf sg | Impl st -> let st = Ppxlib_to_compiler.copy_structure st in Astlib.Compiler_pprintast.structure ppf st
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>