ppx_string-v0.16.0/0000775005353701000030000000000014421750670016247 5ustar dkalinichenkodomain_usersppx_string-v0.16.0/.gitignore0000664005353701000030000000004114421750670020232 0ustar dkalinichenkodomain_users_build *.install *.merlin _opam ppx_string-v0.16.0/CONTRIBUTING.md0000664005353701000030000000441014421750670020477 0ustar dkalinichenkodomain_usersThis repository contains open source software that is developed and maintained by [Jane Street][js]. Contributions to this project are welcome and should be submitted via GitHub pull requests. Signing contributions --------------------- We require that you sign your contributions. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify the below (from [developercertificate.org][dco]): ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 1 Letterman Drive Suite D4700 San Francisco, CA, 94129 Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` Then you just add a line to every git commit message: ``` Signed-off-by: Joe Smith ``` Use your real name (sorry, no pseudonyms or anonymous contributions.) If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with git commit -s. [dco]: http://developercertificate.org/ [js]: https://opensource.janestreet.com/ ppx_string-v0.16.0/LICENSE.md0000664005353701000030000000214614421750670017656 0ustar dkalinichenkodomain_usersThe MIT License Copyright (c) 2020--2023 Jane Street Group, LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ppx_string-v0.16.0/Makefile0000664005353701000030000000040314421750670017704 0ustar dkalinichenkodomain_usersINSTALL_ARGS := $(if $(PREFIX),--prefix $(PREFIX),) default: dune build install: dune install $(INSTALL_ARGS) uninstall: dune uninstall $(INSTALL_ARGS) reinstall: uninstall install clean: dune clean .PHONY: default install uninstall reinstall clean ppx_string-v0.16.0/README.org0000664005353701000030000000411614421750670017717 0ustar dkalinichenkodomain_users This extension provides a syntax for string interpolation. Here is an example of its features: #+begin_src ocaml let script_remotely (user : string) (host : string) (port : int) (script : string) = [%string "ssh %{user}@%{host} -p %{port#Int} %{Sys.quote script}"] #+end_src The above expression is equivalent to: #+begin_src ocaml let script_remotely (user : string) (host : string) (port : int) (script : string) = String.concat "" [ "ssh " ; user ; "@" ; host ; " -p " ; Int.to_string port ; " " ; Sys.quote script ] #+end_src Compared to =Printf.sprintf=: #+begin_src ocaml let script_remotely (user : string) (host : string) (port : int) (script : string) = sprintf "ssh %s@%s -p %d %s" user host port (Sys.quote script) #+end_src having the values inline instead of after the format string can make it easier to understand the resulting string, and avoids the potential mistake of passing arguments in the wrong order. This is truer the more format arguments there are. On the other hand, some things are much easier with printf: pad numbers with zeroes, pad strings on the right, display floats in a specific formats, etc. Compared to manually writing something like =String.concat= version above, ppx_string is shorter and can oftentimes be less error-prone (it's really easy to forget whitespace after =ssh= or around =-p= in the explicit =String.concat= version). To emit the literal sequence =%{=, you can escape it as follows: #+begin_src ocaml [%string {|%{"%{"}|}] #+end_src To pad strings with spaces on the left, add an integer expression after a colon: #+begin_src ocaml [%string "%{col1#Int:term_width / 2}%{col2#:term_width/4}%{col3#:8}%{col4}"] #+end_src is equivalent to: #+begin_src ocaml let pad str len = let pad_len = max 0 (len - String.length str) in let padding = String.make pad_len ' ' in padding ^ str in String.concat "" [ pad (Int.to_string col1) (term_width / 2) ; pad col2 (term_width / 4) ; pad col3 8 ; col4 ] #+end_src (note that the pad length can be dynamic, as with the format string "%*s") ppx_string-v0.16.0/dune0000664005353701000030000000000014421750670017113 0ustar dkalinichenkodomain_usersppx_string-v0.16.0/dune-project0000664005353701000030000000002014421750670020561 0ustar dkalinichenkodomain_users(lang dune 1.10)ppx_string-v0.16.0/ppx_string.opam0000664005353701000030000000140114421750670021316 0ustar dkalinichenkodomain_usersopam-version: "2.0" version: "v0.16.0" maintainer: "Jane Street developers" authors: ["Jane Street Group, LLC"] homepage: "https://github.com/janestreet/ppx_string" bug-reports: "https://github.com/janestreet/ppx_string/issues" dev-repo: "git+https://github.com/janestreet/ppx_string.git" doc: "https://ocaml.janestreet.com/ocaml-core/latest/doc/ppx_string/index.html" license: "MIT" build: [ ["dune" "build" "-p" name "-j" jobs] ] depends: [ "ocaml" {>= "4.14.0"} "base" {>= "v0.16" & < "v0.17"} "ppx_base" {>= "v0.16" & < "v0.17"} "dune" {>= "2.0.0"} "ppxlib" {>= "0.28.0"} ] available: arch != "arm32" & arch != "x86_32" synopsis: "Ppx extension for string interpolation" description: " Part of the Jane Street's PPX rewriters collection. " ppx_string-v0.16.0/src/0000775005353701000030000000000014421750670017036 5ustar dkalinichenkodomain_usersppx_string-v0.16.0/src/dune0000664005353701000030000000026314421750670017715 0ustar dkalinichenkodomain_users(library (name ppx_string) (public_name ppx_string) (kind ppx_rewriter) (libraries base compiler-libs.common ppxlib) (preprocess (pps ppx_base ppxlib.metaquot ppxlib.traverse)))ppx_string-v0.16.0/src/ppx_string.ml0000664005353701000030000001766214421750670021601 0ustar dkalinichenkodomain_usersopen Base open Ppxlib open Ast_builder.Default module Where = struct type t = | Imprecise of Location.t | Precise of { mutable position : position } let is_precise = function | Imprecise _ -> false | Precise _ -> true ;; let advance position char = let pos_cnum = position.pos_cnum + 1 in match char with | '\n' -> { position with pos_lnum = position.pos_lnum + 1; pos_bol = pos_cnum; pos_cnum } | _ -> { position with pos_cnum } ;; let skip t string = match t with | Imprecise _ -> () | Precise at -> for pos = 0 to String.length string - 1 do at.position <- advance at.position string.[pos] done ;; let loc_start = function | Imprecise loc -> loc.loc_start | Precise { position } -> position ;; let loc_end = function | Imprecise loc -> loc.loc_end | Precise { position } -> position ;; let skip_with_loc t string = let loc_start = loc_start t in skip t string; let loc_end = loc_end t in { loc_ghost = true; loc_start; loc_end } ;; let has_escapes ~loc ~string ~delimiter = match delimiter with | Some _ -> false | None -> let unescaped_len = 1 + String.length string + 1 in let actual_len = loc.loc_end.pos_cnum - loc.loc_start.pos_cnum in unescaped_len <> actual_len ;; let literal_prefix ~delimiter = match delimiter with | None -> "\"" | Some id -> Printf.sprintf "{%s|" id ;; let create ~loc ~string ~delimiter = if has_escapes ~loc ~string ~delimiter then Imprecise { loc with loc_ghost = true } else ( let t = Precise { position = loc.loc_start } in skip t (literal_prefix ~delimiter); t) ;; end module Part = struct type t = | Literal of label loc | Interpreted of { loc_start : position ; value : expression ; module_path : longident_loc option ; pad_length : expression option ; loc_end : position } end module Parse_result = struct type t = { parts : Part.t list ; locations_are_precise : bool } end let parse_literal string ~where ~start ~until ~acc = if start >= until then acc else ( let literal = String.sub string ~pos:start ~len:(until - start) in let loc = Where.skip_with_loc where literal in Part.Literal { txt = literal; loc } :: acc) ;; let set_locs loc = object inherit Ast_traverse.map method! location _ = loc end ;; let parse_error ~loc ~name string = Location.raise_errorf ~loc "invalid %s: %S" name string ;; let parse_expression ~where ~loc ~name string = let lexbuf = Lexing.from_string string in lexbuf.lex_abs_pos <- loc.loc_start.pos_cnum; lexbuf.lex_curr_p <- loc.loc_start; match Parse.expression lexbuf with | exception _ -> parse_error ~loc ~name string | expr -> if Where.is_precise where then expr else (set_locs loc)#expression expr ;; let parse_ident ~where ~loc ~name module_path = match parse_expression ~where ~loc ~name module_path with | { pexp_desc = Pexp_construct (ident, None); _ } -> ident | _ -> parse_error ~loc ~name module_path ;; let parse_body ~where string = let loc = Where.skip_with_loc where string in parse_expression ~where ~loc ~name:"%{...} expression" string ;; let parse_module_path ~where string = let loc = Where.skip_with_loc where string in parse_ident ~where ~loc ~name:"%{...} module path" string ;; let parse_pad_length ~where string = let loc = Where.skip_with_loc where string in parse_expression ~where ~loc ~name:"%{...} pad length" string ;; let parse_interpreted string ~where ~start ~until ~acc = Where.skip where "%{"; let loc_start = Where.loc_start where in let string = String.sub string ~pos:start ~len:(until - start) in let value, module_path, pad_length = match String.rsplit2 string ~on:'#' with | None -> let value = parse_body ~where string in value, None, None | Some (body, formatting) -> let body = parse_body ~where body in Where.skip where "#"; let module_path, pad_length = match String.rsplit2 formatting ~on:':' with | None -> let fn = parse_module_path ~where formatting in Some fn, None | Some (module_path, pad_length) -> let fn = if String.is_empty module_path then None else Some (parse_module_path ~where module_path) in Where.skip where ":"; let len = parse_pad_length ~where pad_length in fn, Some len in body, module_path, pad_length in let loc_end = Where.loc_end where in Where.skip where "}"; Part.Interpreted { loc_start; value; module_path; pad_length; loc_end } :: acc ;; type interpreted = { percent : int ; lbrace : int ; rbrace : int } let find_interpreted string ~where ~pos = String.substr_index string ~pos ~pattern:"%{" |> Option.map ~f:(fun percent -> let lbrace = percent + 1 in match String.substr_index string ~pos:(lbrace + 1) ~pattern:"}" with | None -> Where.skip where (String.sub string ~pos ~len:(percent - pos)); let loc = Where.skip_with_loc where "%{" in Location.raise_errorf ~loc "unterminated %%{" | Some rbrace -> { percent; lbrace; rbrace }) ;; let rec parse_parts_from string ~where ~pos ~acc = match find_interpreted string ~where ~pos with | None -> let len = String.length string in let acc = parse_literal string ~where ~start:pos ~until:len ~acc in List.rev acc | Some { percent; lbrace; rbrace } -> let acc = parse_literal string ~where ~start:pos ~until:percent ~acc in let acc = parse_interpreted string ~where ~start:(lbrace + 1) ~until:rbrace ~acc in parse_parts_from string ~where ~pos:(rbrace + 1) ~acc ;; let parse_parts ~string_loc ~delimiter string = let where = Where.create ~loc:string_loc ~delimiter ~string in let parts = parse_parts_from string ~where ~pos:0 ~acc:[] in let locations_are_precise = Where.is_precise where in ({ parts; locations_are_precise } : Parse_result.t) ;; let expand_part_to_expression part = match (part : Part.t) with | Literal { txt; loc } -> estring txt ~loc | Interpreted { loc_start; value; module_path; pad_length; loc_end } -> let expression = let unpadded = match module_path with | None -> fun ~loc:_ -> value | Some fn -> fun ~loc -> pexp_apply ~loc (pexp_ident ~loc:fn.loc { fn with txt = Ldot (fn.txt, "to_string") }) [ Nolabel, value ] in match pad_length with | None -> unpadded | Some len -> fun ~loc -> let ex_var = gen_symbol ~prefix:"__string_exp" () in let ex = evar ~loc ex_var in let lenvar = gen_symbol ~prefix:"__string_len" () in [%expr let [%p pvar ~loc ex_var] = [%e unpadded ~loc] in let [%p pvar ~loc lenvar] = Stdlib.String.length [%e ex] in Stdlib.( ^ ) (Stdlib.String.make (Stdlib.max 0 ([%e len] - [%e evar ~loc lenvar])) ' ') [%e ex]] in expression ~loc:{ loc_ghost = true; loc_start; loc_end } ;; let concatenate ~loc expressions = match expressions with | [] -> [%expr ""] | [ expr ] -> [%expr ([%e expr] : Stdlib.String.t)] | multiple -> [%expr Stdlib.String.concat "" [%e elist ~loc multiple]] ;; let expand ~expr_loc ~string_loc ~string ~delimiter = (parse_parts ~string_loc ~delimiter string).parts |> List.map ~f:expand_part_to_expression |> concatenate ~loc:expr_loc ;; let () = Ppxlib.Driver.register_transformation "ppx_string" ~extensions: [ Extension.declare "ppx_string.string" Extension.Context.expression Ast_pattern.( pstr (pstr_eval (pexp_constant (pconst_string __' __ __)) nil ^:: nil)) (fun ~loc:expr_loc ~path:_ { loc = string_loc; txt = string } _ delimiter -> Merlin_helpers.hide_expression (expand ~expr_loc ~string_loc ~string ~delimiter)) ] ;; ppx_string-v0.16.0/src/ppx_string.mli0000664005353701000030000000114514421750670021737 0ustar dkalinichenkodomain_usersopen! Base open Ppxlib module Part : sig type t = | Literal of label loc | Interpreted of { loc_start : position ; value : expression ; module_path : longident_loc option ; pad_length : expression option ; loc_end : position } end module Parse_result : sig type t = { parts : Part.t list ; locations_are_precise : bool } end val parse_parts : string_loc:location -> delimiter:label option -> label -> Parse_result.t val expand : expr_loc:location -> string_loc:location -> string:label -> delimiter:label option -> expression