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.
Related
I am trying to write the following code in OCaml:
let a = 0
let b = 1
if a > b then
{
print_endline "a";
print_endline "a";
}
print_endline "b"
And then I encountered the following error:
File "[21]", line 4, characters 0-2:
4 | if a > b then
^^
Error: Syntax error
I have tried using the begin and end keywords.
If you're writing a program (rather than mucking about in a REPL), then there are only certain constructs which can exist at the top level of your program.
One of those is a binding. So the following is fine:
let a = 0
let b = 1
But a conditional expression (if/else) is not permitted. We can get around this by binding that expression to a pattern. Since print_endline will just return (), we can write:
let () =
...
Your use of { and } is incorrect in this situation, but you can grouped multiple expressions with ; and ( and ). Remember that ; is not a "statement terminator" but rather a separator.
let () =
if a > b then (
print_endline "a";
print_endline "a"
);
print_endline "b"
Note that if can only exist without a matching else if the entire expression returns unit. This meets that criteria.
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 ....
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 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)
I am learning Python.
For code:
def main():
fileName = raw_input("file name ")
infile = open(fileName, "r")
sm = 0.0
ct = 0
line = infile.readline()
while line != "":
sm = sm + eval(line)
ct = ct + 1
line = infile.readline()
print "\nAverage is ", sm/ct
main()
it results the following error:
Traceback (most recent call last):
File "/home/sorin/avg6.py", line 13, in <module>
main()
File "/home/sorin/avg6.py", line 8, in main
sm = sm + eval(line)
File "<string>", line 1
^
SyntaxError: unexpected EOF while parsing
I don't understand way. Please help. Thank you.
The eval function expects the string you pass it to be a valid Python expression, but it got an empty string (or, given the while loop's condition, perhaps a string with only whitespace). That doesn't have a value, so it raises an error.
You may want to look in the data file to see if there any blank lines (and remove them, if you can). Or you could modify the code to ignore invalid strings:
while line != "":
try:
sm = sm + eval(line)
ct = ct + 1
except SyntaxError:
pass
line = infile.readline()
You might also want to catch other kinds of errors, if you go that route.
Another option is to explicitly check for specific invalid strings that might come up (like just a bare newline):
while line != "":
if line != "\n": # or maybe us "if line.strip()", to reject whitespace lines
sm = sm + eval(line)
ct = ct + 1
line = infile.readline()
One final suggestion is to use a for loop on the file object rather than a while loop with calls to readline. This won't prevent the kinds of error you're getting, but it generally results in nicer looking code, which might be easier to debug.
What does your input file look like? If your input file has a blank line, it would explain your error.