How to append to string in ocaml? - ocaml

I don't know how to add some string to itself in loop.
let parameters = [| [| ("name", "fdjks"); ("value", "dsf") |]; [| ("name", "&^%"); ("value", "helo") |] |] ;;
let boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";;
let body = "";;
for x = 0 to (Array.length(parameters) : int)-1 do
let (_, paramName) = parameters.(x).(0) in
let (_, paramValue) = parameters.(x).(1) in
body = body ^ "--" ^ boundary ^ "\r\n" ^ "Content-Disposition:form-data; name=\"" ^ paramName ^ "\"\r\n\r\n" ^ paramValue ;
print_endline(body)
done;;
but this gives error..
Any way to do this......?

The (^) operator concatenates two strings, e.g.,
# "hello" ^ ", " ^ "world!";;
- : string = "hello, world!"
If you have a list of strings, then you can use the String.concat function, that takes a separator, and a list of strings and produces there concatentation in an effective way:
# String.concat ", " ["hello"; "world"];;
- : string = "hello, world"
Why is using the (^) operator in a cycle is a bad idea? Every concatentation creates a new string and then copies the contents of both strings into the new string. So appending N strings will end up in approximately n^2 copying (where n is the length of a string). The same is true in Java and other languages/libraries where concatenation returns a new string, instead of mutating one of its arguments. A usual solution is to use the StringBuilder pattern, which in OCaml is represented with the Buffer module. So suppose, you don't have the String.concat function, and you would like to build your own efficient concatenation function (this could also be useful, since Buffer is a more general solution than String.concat, and will work when, for example, you input is not a list). Here is our implementation,
let concat xs =
let buf = Buffer.create 16 in
List.iter (Buffer.add_string buf) xs;
Buffer.contents buf
This function will create a buffer that will automatically resize itself. The 16 is just an initial guess and could be any number. On the second line we just iterate over all strings and push the to the buffer, and finally, we ask the buffer to build the resulting string. Here is how we use this function:
# concat ["hello"; ", "; "world"];;
- : string = "hello, world"

In OCaml variables are immutable by default. You need to use a reference if you want to change the value of a variable.
let parameters = [| [| ("name", "fdjks"); ("value", "dsf") |]; [| ("name", "&^%"); ("value", "helo") |] |] ;;
let boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";;
let body = ref "";;
for x = 0 to (Array.length(parameters) : int)-1 do
let (_, paramName) = parameters.(x).(0) in
let (_, paramValue) = parameters.(x).(1) in
body := !body ^ "--" ^ boundary ^ "\r\n" ^ "Content-Disposition:form-data; name=\"" ^ paramName ^ "\"\r\n\r\n" ^ paramValue ;
print_endline(!body)
done;;
See also add elements to list in a loop in OCaml.
Please note that adding two strings in OCaml is an O(n) operation (n = number of characters), making it rather expensive for long strings. You can use the Buffer module to concatenate strings efficiently. Using ^, however, is much more readable.
A good compromise between readability and efficiency is probably, using the Buffer module for long and using ^ for short strings. Hence, using a Buffer for your outer iteration would be a good idea. The code looks as follows.
let parameters = [| [| ("name", "fdjks"); ("value", "dsf") |];
[| ("name", "&^%"); ("value", "helo") |] |] ;;
let boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";;
let body = Buffer.create 256;;
for x = 0 to (Array.length(parameters) : int)-1 do
let (_, paramName) = parameters.(x).(0) in
let (_, paramValue) = parameters.(x).(1) in
Buffer.add_string body
("--" ^ boundary ^ "\r\n"
^ "Content-Disposition:form-data; name=\""
^ paramName ^ "\"\r\n\r\n" ^ paramValue);
print_endline(Buffer.contents body)
done;;

Iterating recursively with an accumulator is the idiomatic way to do what you want in ocaml:
let parameters = [| [| ("name", "fdjks"); ("value", "dsf") |]; [| ("name", "&^%"); "value", "helo") |] |] ;;
let boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW" ;;
let len = Array.length(parameters) ;;
let rec loop accum index =
if index < len then
let (_, paramName) = parameters.(index).(0) in
let (_, paramValue) = parameters.(index).(1) in
loop (accum ^ "--" ^ boundary ^ "\r\n"
^ "Content-Disposition:form-data; name=\""
^ paramName ^ "\"\r\n\r\n" ^ paramValue)
(index + 1)
else print_endline accum
in
loop "" 0 ;;
Or better still look at the Array.fold_left function.
let parameters = [| [| ("name", "fdjks"); ("value", "dsf") |]; [| ("name", "&^%"); ("value", "helo") |] |] ;;
let boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";;
let () = print_endline
(Array.fold_left
(fun acc elt ->
let (_, paramName) = elt.(0) in
let (_, paramValue) = elt.(1) in
acc ^ "--" ^ boundary ^ "\r\n"
^ "Content-Disposition:form-data; name=\""
^ paramName ^ "\"\r\n\r\n" ^ paramValue)
""
parameters)

Related

Incomplete expression in ppx extension

I want to try to write my own ppx to allow named arguments in formatting strings:
From Format.printf [%fmt "!(abc) !(qsd)"] to Format.printf "%s %s" abc qsd
When dumping with ppx_tools I want to go from:
{pexp_desc =
Pexp_apply
({pexp_desc = Pexp_ident {txt = Ldot (Lident "Format", "printf")};
pexp_loc_stack = []},
[(Nolabel,
{pexp_desc =
Pexp_extension
({txt = "fmt"},
PStr
[{pstr_desc =
Pstr_eval
({pexp_desc =
Pexp_constant (Pconst_string ("!(abc) !(qsd)", ...));
pexp_loc_stack = []},
...)}]);
pexp_loc_stack = []})]);
pexp_loc_stack = []}
To
{pexp_desc =
Pexp_apply
({pexp_desc = Pexp_ident {txt = Ldot (Lident "Format", "printf")};
pexp_loc_stack = []},
[(Nolabel,
{pexp_desc = Pexp_constant (Pconst_string ("%s %s", ...));
pexp_loc_stack = []});
(Nolabel,
{pexp_desc = Pexp_ident {txt = Lident "abc"}; pexp_loc_stack = []});
(Nolabel,
{pexp_desc = Pexp_ident {txt = Lident "qsd"}; pexp_loc_stack = []})]);
pexp_loc_stack = []}
The ppx extension starts inside a function application so I would just want to specify that what I'm about to create are applications arguments but so far I've not been able to do so:
I get the formatting string (in my example it would be "%s %s") and the arguments to it (e.g. abc and qsd) and try to produce "%s %s" abc qsd but if I use Ast_build.Default.elist fmt args I get ["%s %s"; abc; qsd] and with eapply I get ("%s %s" abc qsd) (almost there but the parenthesis make it wrong).
let expand ~ctxt fmt =
let loc = Expansion_context.Extension.extension_point_loc ctxt in
let fmt, args = parse loc fmt in
Ast_builder.Default.eapply ~loc (* <- Here is where I don't know what to do *)
(Ast_builder.Default.estring ~loc fmt)
(List.map (Ast_builder.Default.evar ~loc) args)
Since it's heavily recommended to use ppxlib to do this kind of things, is there an easy way to achieve what I want? I tried looking for some documentation for it but it's still a work in progress and the few examples I could find transform an expression in another expression while I'm transforming an expression (a string) in an incomplete one.
FULL CODE:
open Ppxlib
(* A format string is a normal string with the special construct !(...) *)
let parse loc string =
let length = String.length string in
let buffer = Buffer.create length in
let rec parse args index =
if index = length then (Buffer.contents buffer, args)
else
match String.unsafe_get string index with
| '!' as c ->
if index = length - 1 || String.unsafe_get string (index + 1) <> '('
then (
(* Simple ! not starting a named argument *)
Buffer.add_char buffer c;
parse args (index + 1))
else
(* We saw !( and need to parse the rest as a named argument *)
let index, var = parse_named_arg (index + 2) in
Buffer.add_string buffer "%s";
parse (var :: args) index
| c ->
Buffer.add_char buffer c;
parse args (index + 1)
and parse_named_arg index =
let var = Buffer.create 8 in
let rec parse_var index =
if index = length then
Location.raise_errorf ~loc
"Reached end of formatting string with !(...) construct not ended"
else
match String.unsafe_get string index with
| ')' -> (index + 1, Buffer.contents var)
| c ->
Buffer.add_char var c;
parse_var (index + 1)
in
parse_var index
in
parse [] 0
let expand ~ctxt fmt =
let loc = Expansion_context.Extension.extension_point_loc ctxt in
let fmt, args = parse loc fmt in
Ast_builder.Default.eapply ~loc
(Ast_builder.Default.estring ~loc fmt)
(List.map (Ast_builder.Default.evar ~loc) args)
let my_extension =
Extension.V3.declare "fmt" Extension.Context.expression
Ast_pattern.(single_expr_payload (estring __))
expand
let rule = Ppxlib.Context_free.Rule.extension my_extension
let () = Driver.register_transformation ~rules:[ rule ] "ppx_fmt_string"

Syntax error : operator expected (Closing parenthesis not handling)

I have this error showing up when i tried to test my functions, would anyone know where this error came from ?
let () =
let t = Sys.time() in
let args_n = Array.length Sys.argv - 1 in
let args_list = Array.to_list (Array.sub Sys.argv 1 args_n) in
List.iter (fun element ->
let length_of_element = String.length element in
let text = check_if_file(List.nth args_list 1) in
let int_ls = search (to_list_ch element) text length_of_element) (check_if_file(List.nth args_list 0 )) in
if (List.length int_ls)> 1 then print_string "pattern found at characters "
else if (List.length int_ls) = 1 then print_string "Pattern found at character "
else print_string "No patterns found."
;
print_ls int_ls;
Printf.printf "Execution time: %fs\n" (Sys.time() -. t);;
Ocaml is telling that it came from the closing parenthesis after length_of_element but the things is if i remove it, the open parenthesis at the List.iter line won't have any closing parenthesis matching with him.
let int_ls = search (to_list_ch element) text length_of_element) (check_if_file(List.nth args_list 0 )) in
Before trying to make this functions iterating on a list of string it was like that :
let () =
let t = Sys.time() in
let args_n = Array.length Sys.argv - 1 in
let args_list = Array.to_list (Array.sub Sys.argv 1 args_n) in
let pattern =check_if_file(List.nth args_list 0 )in
let lpattern = String.length pattern - 1 in
let text = check_if_file(List.nth args_list 1) in
let int_ls = search (to_list_ch pattern) text lpattern in
if (List.length int_ls)> 1 then print_string "pattern found at characters "
else if (List.length int_ls) = 1 then print_string "Pattern found at character "
else print_string "No patterns found."
;
print_ls int_ls;
Printf.printf "Execution time: %fs\n" (Sys.time() -. t);;
But it was work only for one string and not multiple string, so i trie to iterate in a list to make it work not for only one string but a list of string
The let x = e1 in e2 construct evaluates e1, and then makes its result available in e2. In your case, you have no in e2, so there is not much point in having let x =.
What you have written is List.iter (fun -> ... let x = e1) in e2. But what do you expect x to mean inside e2? Should it be the result of the first evaluation of e1 in the loop? The last one? What if the body of the loop is never executed because the list you iterate over is empty? I suggest to step back and think a bit more about what you are actually trying to compute.

How to use ocaml-re

I am currently trying to use ocaml-re. Documentation is sparse. I was wondering how I would, for instance, do the equivalent:
Str.regexp "example \\([A-Za-z]+\\)" using Re.Perl? I think it would help me to naturally get the rest of the documentation on my own. Thank you!
Bonus points if you convert this code from Str to Re.Perl:
let read_filename = "example.ts"
let filename = "example2.ts"
let () =
CCIO.(
let modify_file ~chunks =
let r = Str.regexp "example \\([A-Za-z]+\\)" in
match chunks () with
None -> chunks (* is the same as (fun () -> None) *)
| Some chunks ->
let test_chunks = Str.replace_first r "\\1" chunks in (* compute once *)
(fun () -> Some test_chunks) in
with_in read_filename
(fun ic ->
let chunks = read_chunks ic in
let new_chunks = modify_file ~chunks in
with_out ~flags:[Open_binary] ~mode:0o644 filename
(fun oc ->
write_gen oc new_chunks
)
)
)
Don't use Re.Perl, Re's API is much simpler. You can constructor your re with:
let re =
let open Re in
alt [rg 'A' 'Z'; rg 'a' 'z'] (* [A-Za-z] *)
|> rep1a (* [A-Za-z]+ *)
|> group (* ([A-Za-z]+) *)
|> compile

Trying to get first word from character list

I have a character list [#"h", #"i", #" ", #"h", #"i"] which I want to get the first word from this (the first character sequence before each space).
I've written a function which gives me this warning:
stdIn:13.1-13.42 Warning: type vars not generalized because of value
restriction are instantiated to dummy types (X1,X2,...)
Here is my code:
fun next [] = ([], [])
| next (hd::tl) = if(not(ord(hd) >= 97 andalso ord(hd) <= 122)) then ([], (hd::tl))
else
let
fun getword [] = [] | getword (hd::tl) = if(ord(hd) >= 97 andalso ord(hd) <= 122) then [hd]#getword tl else [];
in
next (getword (hd::tl))
end;
EDIT:
Expected input and output
next [#"h", #"i", #" ", #"h", #"i"] => ([#"h", #"i"], [#" ", #"h", #"i"])
Can anybody help me with this solution? Thanks!
This functionality already exists within the standard library:
val nexts = String.tokens Char.isSpace
val nexts_test = nexts "hi hi hi" = ["hi", "hi", "hi"]
But if you were to build such a function anyway, it seems that you return ([], []) sometimes and a single list at other times. Normally in a recursive function, you can build the result by doing e.g. c :: recursive_f cs, but this is assuming your function returns a single list. If, instead, it returns a tuple, you suddenly have to unpack this tuple using e.g. pattern matching in a let-expression:
let val (x, y) = recursive_f cs
in (c :: x, y + ...) end
Or you could use an extra argument inside a helper function (since the extra argument would change the type of the function) to store the word you're extracting, instead. A consequence of doing that is that you end up with the word in reverse and have to reverse it back when you're done recursing.
fun isLegal c = ord c >= 97 andalso ord c <= 122 (* Only lowercase ASCII letters *)
(* But why not use one of the following:
fun isLegal c = Char.isAlpha c
fun isLegal c = not (Char.isSpace c) *)
fun next input =
let fun extract (c::cs) word =
if isLegal c
then extract cs (c::word)
else (rev word, c::cs)
| extract [] word = (rev word, [])
in extract input [] end
val next_test_1 =
let val (w, r) = next (explode "hello world")
in (implode w, implode r) = ("hello", " world")
end
val next_test_2 = next [] = ([], [])

ocaml stringOfList using concat

The code I currently have is this where I'm testing stringOfList function
let rec sepConcat sep sl = match sl with
| [] -> ""
| h :: t ->
let f a x = a^sep^x in
let base = h in
let l = t in
List.fold_left f base l
let stringOfList f l = sepConcat "; " (List.map f l)
The output I'm supposed to get is
# stringOfList string_of_int [1;2;3;4;5;6];;
- : string = "[1; 2; 3; 4; 5; 6]"
but I'm getting
# stringOfList string_of_int [1;2;3;4;5;6];;
- : string = "1; 2; 3; 4; 5; 6"
What am I doing wrong? How can i add the extra [] inside the parentheses. Sepconcat is supposed to do this
# sepConcat ", " ["foo";"bar";"baz"];;
- : string = "foo, bar, baz"
# sepConcat "---" [];;
- : string = ""
# sepConcat "" ["a";"b";"c";"d";"e"];;
- : string = "abcde"
# sepConcat "X" ["hello"];;
- : string = "hello"
You're not doing anything wrong, there's just no place in the code that adds the square bracket characters.
I'd say that the square brackets are another punctuation thing like your sep parameter. You might want to pass them in as additional parameters at some level. Seems like it would be OK to add them as parameters to sepConcat--then it would handle all the punctuation.
Edit: it's not going to help to pass different values as sep. The separator goes between the strings. From your description, you want the extra brackets to go around the outside of the result. Not a separator.
Edit: The " characters are not part of the string. They are OCaml's way of showing you that the value is a string.
# let x = "a";;
val x : string = "a"
# x;;
- : string = "a"
# String.length x;;
- : int = 1
#
Any string value will have "" around it when printed by the interpreter. So there's no way the square brackets can be outside them!
Change your stringOfList to
let stringOfList f l =
let str = sepConcat "; " (List.map f l) in
"["^str^"]"
# stringOfList string_of_int [1;2;3;4;5];;
- : string = "[1; 2; 3; 4; 5]"