How to run program in OCaml toplevel with input from file? - ocaml

I know that in order to load a program in OCaml one has to type #use "source_code_file.ml" in toplevel where source_code_file.ml is the file we want to use.
My program reads input from stdin. In the command line i have a txt file that with redirection is used to act as stdin. Can i do this in toplevel? I would like to this because in toplevel i can easily see what type variables have and if things are initialized with the correct values.

If you're on a Unix-like system you can use Unix.dup2 to do almost any kind of input redirection. Here is a function with_stdin that takes an input file name, a function, and a value. It calls the function with standard input redirected from the named file.
let with_stdin fname f x =
let oldstdin = Unix.dup Unix.stdin in
let newstdin = Unix.openfile fname [Unix.O_RDONLY] 0 in
Unix.dup2 newstdin Unix.stdin;
Unix.close newstdin;
let res = f x in
Unix.dup2 oldstdin Unix.stdin;
Unix.close oldstdin;
res
If your function doesn't consume the entire input the leftover input will confuse the toplevel. Here's an example that does consume its entire input:
# let rec linecount c =
try ignore (read_line ()); linecount (c + 1)
with End_of_file -> c;;
val linecount : int -> int = <fun>
# with_stdin "/etc/passwd" linecount 0;;
- : int = 86
#
This technique is too simple if you wanted to interleave interactions with the toplevel with calls to your function to consume just part of its input. I suspect that would make things too complicated to be worth the effort. It would be much easier (and perhaps better overall) to rewrite your code to work with an explicitly specified input channel.

Related

noob question on reading integer/string inputs in OCaml

I was trying some coding challenge in Hackerank, and I was wondering how to read a line of space-separated integers using OCaml.
I know if the input integers are newline separated,
then we could do
let recread_lines () =
try line = read_line ()
in line:: read_lines()
with End_of_file->[]
and then convert the list to list of int using
let ()=
let input = read_lines ()
in List.map int_of_string input.
I also know that we can read the first integer from a line of integers by doing:
let n1 =Scanf.scanf " %d" (fun x -> x)
From C, I know scanf method scans the line looking for structure defined in the parameter and then store the scanned values into the variables. I don't see the use of (fun x->x) at the end of the scanf method in OCaml.
I don't know how to read a line of integers though, so if you can point me in the right direction, I would be able to work on those challenges!
Here's a function to read a line of space-separated integers from stdin:
let read_ints () =
let line = read_line () in
let ints = Str.split (Str.regexp " *") line in
List.map int_of_string ints
This function will raise End_of_file if there are no more lines in the input.
You can adapt the regular expression if there are actually tabs mixed with the spaces (say).
(FWIW I have found over the years that the scanf family of functions is prone to behaving badly in the face of unexpected input. This isn't a problem with OCaml in particular, it's the same for all languages that have scanf. So I haven't used scanf for quite a few decades.)

OCAML Taking multiple arguments from stdin and operating on them one by one

I have written an interpreter using ocamllex and ocamlyacc, the lexer and the parser work correctly but currently they only parse the last .txt argument it receives as oppose to all of them in turn. For example, ./interpret one.txt two.txt three.txt only parses three.txt as oppose to parsing one.txt and then two.txt and then three.txt which is what I want. So for example the parse results are as follows:
one.txt -> "1"
two.txt -> "2"
three.txt -> "3"
On calling ./interpret one.txt two.txt three.txt the current output is: 3 but I want it to be 123
Here is my main class which deals with the stdin and stdout
open Lexer
open Parser
open Arg
open Printf
let toParse c =
try let lexbuf = Lexing.from_channel c in
parser_main lexer_main lexbuf
with Parsing.Parse_error -> failwith "Parse failure!" ;;
let argument = ref stdin in
let prog p = argument := open_in p in
let usage = "./interpreter FILE" in
parse [] prog usage ;
let parsed = toParse !argument in
let result = eval parsed in
let _ = parsed in
flush stdout;
Thanks for your time
There's not really enough code here to be able to help.
If I assume that the output is written by eval, then I see only one call to eval. But there's nothing here that deals with filenames from the command line, so it's hard to say more.
If you are planning to read input from files, then there's no reason to be using stdin for anything as far as I can tell.
(I know this is a very minor point, but this code doesn't constitute a class. Other languages use classes for everything, but this is a module.)
Update
Here's a module that works something like the Unix cat command; it writes out the contents of all the files from the command line one after the next.
let cat () =
for i = 1 to Array.length Sys.argv - 1 do
let ic = open_in Sys.argv.(i) in
let rec loop () =
match input_line ic with
| line -> output_string stdout (line ^ "\n"); loop ()
| exception End_of_file -> ()
in
loop ();
close_in ic
done
let () = cat ()
Here's how it looks when you compile and run it.
$ ocamlc -o mycat mycat.ml
$ echo test line 1 > file1
$ echo test line 2 > file2
$ ./mycat file1 file2
test line 1
test line 2

F# - Creating program to read a file and print in reverse, how do I remove all the new line quotations?

I have a program where if I type "tac Text.txt" (or any file path in quotations, but this is the test text file I made/used), it will print what is in the txt file, but in reverse. But it doesn't work properly. Where it should appear as
No it is not.
Yes-it is.
No it is not.
Hello my name is Jim.
it appears as
["No it is not."; ""; ""; "Yes-it is."; "No it is not."; "Hello my
name is Jim."].
My code currently is
open System
open System.IO
let countLines path =
File.ReadAllLines(path) |> Seq.toList|> List.rev
// File.ReadAllLines reads the file, turns it into a list then reverses the list.
let printFunction lines =
printfn "%A" lines
// This, when called, will print the file in reverse.
let tac path =
if File.Exists(path) then
let lines = countLines path
printFunction lines
else
printfn "File not found."
[<EntryPoint>]
let main argv =
if argv.Length > 0 then
tac argv.[0]
else
printfn "Error - Please enter file path."
0
I'm assuming it's due to converting to a list, is there a way I can print it normally? I'm hoping it's just a small rookie mistake I've made.
UPDATE: I just changed
let countLines path =
File.ReadAllLines(path) |> Seq.toList|> List.rev
to
let countLines path =
File.ReadAllLines(path) |> Array.rev
Same thing happens, but I'm hoping it leads me closer to the result I want.
It's writing out your entire data type, which is a string list.
You need to iterate through the list:
let printFunction lines =
for line in lines do printfn "%s" line
Print an array, list or seq of lines individually:
let printFunction = Seq.iter (printfn "%s")

Printing promt before user input

I am new to OCaml, and now I am trying to make a simple REPL.
let rec repl () =
print_prompt () ;
let input = Scanf.scanf "%s" (fun x -> x) in
if input = "" then repl ()
else print_endline input
let print_prompt () = print_string "> "
The problem now i am having is: when program starts, it does not display prompt immediately. It waits for my input and prints prompt along with my input.
What I want is:
> "user_input"
"user_input"
But i am getting :
"user_input"
> "user_input"
How can I fix this?
Using readline instead of Scanf :
val read_line : unit -> string
Flush standard output, then read characters from standard input until a newline character is encountered. Return the string of all characters read, without the newline character at the end.
Well, you didn't show the print_promt implementation, but I can guess, that it uses some buffered io function like print_string or printf. They print into an intermediate buffer and data will not be displayed unless flush is called. You can use flush or flush_all functions to do this manually. Also you can use a special specificator %! in printf formats string:
open Printf
let print_prompt () = printf "> %!"
This is almost certainly a buffering problem. In your print_prompt function, flush the standard output:
flush stdout

building a lexical analyser using ml-lex

I need to create a new instance of a lexer tied to the standard input stream.
However, when I type in
val lexer = makeLexer( fn n => inputLine( stdIn ) );
I get an error that I don't understand:
stdIn:1.5-11.13 Error: operator and operand don't agree [tycon mismatch]
operator domain: int -> string
operand: int -> string option
in expression:
(makeLexer is a function name present in my source code)
inputLine returns a string option, and my guess is a string is expected.
What you want to do is either have makeLexer take a string option, like so:
fun makeLexer NONE = <whatever you want to do when stream is empty>
| makeLexer (SOME s) = <the normal body makeLexer, working on the string s>
or change your line to:
val lexer = makeLexer( fn n => valOf ( inputLine( stdIn ) ) );
valOf takes an option type and unpacks it.
Note that, since inputLine returns NONE when the stream is empty, it's probably a better idea to use the first approach, rather than the second.
An example of how to make an interactive stream is given on page 38 (or 32 in the paper) of the User's Guide to ML-Lex and ML-Yacc
The example code could be simpler by using inputLine.
So I would use the example given by Sebastian, keeping in mind that inputLine might return NONE using stdIn atleast if the user presses CTRL-D.
val lexer =
let
fun input f =
case TextIO.inputLine f of
SOME s => s
| NONE => raise Fail "Implement proper error handling."
in
Mlex.makeLexer (fn (n:int) => input TextIO.stdIn)
end
Also the calculator example on page 40 (34 in the paper) shows how to use this in a whole
In general the user guide contains some nice examples and explanations.