I am writing a short script to read each file in folder of script files and print last word its first line - indicating executable file that is run- ocaml e.g. in usr/bin/ocaml
#use "topfind"
#require "str"
(* fn to print binary file being called from hashbang line of script file *)
let rec myfn (afile) =
print_string (afile^": ");
if (Sys.is_directory afile) then print_endline("This is a directory.");
let ic = (open_in afile) in
if in_channel_length(ic)==0 then print_endline("Zero length file."); exit 0
let line = input_line(ic) in (* error here *)
if (Str.first_chars line 0 = "#") then
let linelist = (Str.split (Str.regexp "/") line) in
let lastword = List.nth linelist ((List.length linelist) - 1) in
print_endline(lastword)
else
print_endline("Not a script file.") ;;
(* to check all files in directory *)
let dir = "." in
let files = Sys.readdir dir in
Array.iter myfn files;;
However, it is giving following error:
File "firstline3.ml", line 13, characters 27-29:
Error: Syntax error
I have tried to replace in with ; or no terminator in this line but it does not help. What is the problem and how can it be solved. Thanks for your help.
You are missing parentheses around the then expression:
if in_channel_length ic =0 then ( print_endline "Zero length file."; exit 0 );
otherwise the expression is read by the parser as
(if in_channel_length ic = 0 then print_endline "Zero length file.");
exit 0;
and the exit is thus unconditional.
Note, you should avoid the physical equality operator, ==, (especially on non-mutable values) and use = instead.
EDIT: If you want to return, you just need to add an else branch
if in_channel_length ic = 0 then print_endline "Zero length file."
else ....
Related
I want to write a function that reads a text file line by line. ie, read the first line until EOL and then treat this as a string, and repeat this until the text ends.
I have managed only this much till now:
fun read file =
let val is = TextIO.openIn file
in
end
You can use TextIO.inputLine : instream -> string option for this. Using refs you can do something like
let
val stream = TextIO.openIn file
val done = ref false
in
while not (!done) do
case TextIO.inputLine stream of
SOME s => print s
| NONE => done := true
end
You can also use the functions in TextIO.StreamIO.
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
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")
When I have a file foo.ml:
let foo = [];;
let bar =
match [] with
| [] -> [];;
and I try to have the missing match case on line 4 be flagged as an error:
#warn_error "+8";;
#use "foo.ml";;
I get a spurious line reference to line 1 just before the end of the ouput of my #use:
val foo : 'a list = []
File "foo.ml", line 4, characters 2-26:
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
_::_
File "foo.ml", line 1:
Error: Some fatal warnings were triggered (1 occurrences)
This is of course not a real problem, but I still find the spurious line reference irritating. Is there some way to convince OCaml not to print that irrelevant File "foo.ml", line 1: line in the output?
Unfortunately, this is just how the compiler works. You can find the source code of this feature in parsing/location.ml. The way fatal warnings are implemented is by building an error message relative to the file itself:
| Warnings.Errors n ->
Some
(errorf ~loc:(in_file !input_name)
"Some fatal warnings were triggered (%d occurrences)" n)
The in_file function is why you are seeing "line 1":
let in_file name =
let loc = {
pos_fname = name;
pos_lnum = 1;
pos_bol = 0;
pos_cnum = -1;
} in
{ loc_start = loc; loc_end = loc; loc_ghost = true }
This data type lacks a way to represent a location which as a filename but no line number.
I don't think it's complettely spurious. It's referring to a fact about the file as a whole. They had to pick some line to represent the whole file.
I have a frontend written in menhir which tries to parse an expression: from a string to an expression AST. The entry point of the frontend Parser_e.main is called in several different places in my OCaml code. So I would like to be able to catch possible errors inside the frontend rather than outside. When catching an error, a particular important information I want to show is the entire input string that the frontend cannot parse. (Errors from the lexer are very rare, because the frontend can almost read everything).
So I tried to follow this thread, and to print more information when there is an error. In parser_e.mly, I have added
exception LexErr of string
exception ParseErr of string
let error msg start finish =
Printf.sprintf "(line %d: char %d..%d): %s" start.pos_lnum
(start.pos_cnum - start.pos_bol) (finish.pos_cnum - finish.pos_bol) msg
let parse_error msg nterm =
raise (ParseErr (error msg (rhs_start_pos nterm) (rhs_end_pos nterm)))
e_expression:
/* empty */ { EE_empty }
| INTEGER { EE_integer $1 }
| DOUBLE { EE_double $1 }
...
| error { parse_error "e_expression" 1; ERR "" }
But it still does not have the input string as information. Does anyone if there is any function I am missing to get that?
In the context of an error you can extract a location of failed lexeme in a format of two positions, using Parsing.symbol_start_pos and Parsing.symbol_end_pos functions. Unfortunately Parsing module doesn't really provide an access to the lexeme as a string, but if the input was stored in file then it is possible to extract it manually or print an error in a compiler style, that a descent IDE will understand and highlight it manually. A module Parser_error is below. It defines function Parser_error.throw that will raise an Parser_error.T exception. The exception caries a diagnostic message and a position of a failed lexeme. Several handy functions are provided to extract this lexeme from a file, or to generate a fileposition message. If your input is not stored in a file, then you can use string_of_exn function that accepts the input as a string and the Parser_error.T exception, and extracts the offending substring from it. This is an example of a parser that uses this exception for error reporting.
open Lexing
(** T(message,start,finish) parser failed with a [message] on an
input specified by [start] and [finish] position.*)
exception T of (string * position * position)
(** [throw msg] raise a [Parser_error.T] exception with corresponding
message. Must be called in a semantic action of a production rule *)
let throw my_unique_msg =
let check_pos f = try f () with _ -> dummy_pos in
Printexc.(print_raw_backtrace stderr (get_raw_backtrace ()));
let sp = check_pos Parsing.symbol_start_pos in
let ep = check_pos Parsing.symbol_end_pos in
raise (T (my_unique_msg,sp,ep))
(** [fileposition start finish] creates a string describing a position
of an lexeme specified by [start] and [finish] file positions. The
message has the same format as OCaml and GNU compilers, so it is
recognized by most IDE, e.g., Emacs. *)
let fileposition err_s err_e =
Printf.sprintf
"\nFile \"%s\", line %d, at character %d-%d\n"
err_s.pos_fname err_s.pos_lnum err_s.pos_cnum err_e.pos_cnum
(** [string_of_exn line exn] given a [line] in a file, extract a failed
lexeme form the exception [exn] and create a string denoting the
parsing error in a format similar to the format used by OCaml
compiler, i.e., with fancy underlying. *)
let string_of_exn line (msg,err_s,err_e) =
let b = Buffer.create 42 in
if err_s.pos_fname <> "" then
Buffer.add_string b (fileposition err_s err_e);
Buffer.add_string b
(Printf.sprintf "Parse error: %s\n%s\n" msg line);
let start = max 0 (err_s.pos_cnum - err_s.pos_bol) in
for i=1 to start do
Buffer.add_char b ' '
done;
let diff = max 1 (err_e.pos_cnum - err_s.pos_cnum) in
for i=1 to diff do
Buffer.add_char b '^'
done;
Buffer.contents b
(** [extract_line err] a helper function that will extract a line from
a file designated by the parsing error exception *)
let extract_line err =
let line = ref "" in
try
let ic = open_in err.pos_fname in
for i=0 to max 0 (err.pos_lnum - 1) do
line := input_line ic
done;
close_in ic;
!line
with exn -> !line
(** [to_string exn] converts an exception to a string *)
let to_string ((msg,err,_) as exn) =
let line = extract_line err in
string_of_exn line exn
Here is an example, that shows how to use in case if there is no file, and input is from a stream or interactive (shell-like) source:
let parse_command line =
try
let lbuf = Lexing.from_string line in
`Ok Parser.statement Lexer.tokens lbuf
with
| Parsing.Parse_error -> `Fail "Parse error"
| Parser_error.T exn -> `Fail (Parser_error.string_of_exn line exn)