I am trying to read a file, line by line in OCaml. Each line in the file represents a string I want to parse, in the correct format expected by the Parsing tool. I am saving each line in a list structure.
I an finding an issue parsing the string contained in each element of the list. I am using OCamllex and Menhir as parsing tools.
If I try to use print_string to print the contents of the list at every element, I get the correct file contents.
If I try to pass a string that I defined within the program to the function, then I get the desired output.
However, if I try to parse the string which I have just read from the file, I get an error: Fatal error: exception Failure ("lexing empty token")
Note: that all of this has been tested against the same string.
Here is a snippet of the code:
let parse_mon m = Parser.monitor Lexer.token (from_string m)
let parse_and_print (mon: string)=
print_endline (print_monitor (parse_mon mon) 0)
let get_file_contents file =
let m_list = ref [] in
let read_contents = open_in file in
try
while true; do
m_list := input_line read_contents :: !m_list
done; !m_list
with End_of_file -> close_in read_contents; List.rev !m_list
let rec print_file_contents cont_list = match cont_list with
| [] -> ()
| m::ms -> parse_and_print m
let pt = print_file_contents (get_file_contents filename)
Ocamllex throws an exception Failure "lexing: empty token" when a text in the stream doesn't match any scanner pattern. Therefore, you will need to match with "catch-all" patterns such as ., _, or eof.
{ }
rule scan = parse
| "hello" as w { print_string w; scan lexbuf }
(* need these two for catch-all *)
| _ as c { print_char c; scan lexbuf }
| eof { exit 0 }
Without seeing your grammar and file I can only offer a wild guess: Could it be that the file contains an empty line at the end? Depending on the .mll that might result in the error you see. The reason being that get_file appends new lines to the front of the list and print_file_contents only looks at the head of that list.
I agree with kne, hard to say without seeing the file, but what you can do is trying to isolate the line that causes the trouble by doing :
let rec print_file_contents cont_list =
match cont_list with
| [] -> ()
| m::ms ->
try parse_and_print m
with Failure _ -> print_string m
Related
I have a function that uses a mutable variable that takes strings and returns strings. (its a read eval print loop interpreter)
I tried exporting it as such:
let () =
Js.export_all
(object%js
method js_run_repl = Js.wrap_callback js_run_repl
end)
Heres a snippet of the function im exporting
let js_run_repl str =
match String.(compare str "quit") with
| 0 -> "bye"
| _ -> ...
regardless of my input it always returns bye, calling the function directly in ocaml produced the expected behaviour. Heres the output from node:
> var mod = require('./main.bc');
undefined
> mod.js_run("constant P : Prop");
MlBytes { t: 0, c: 'bye', l: 3 }
>
Its also peculiar why the function is called js_run instead of js_run_repl. the latter is undefined according to node.
let () =
Js.export_all
(object%js
method js_run_repl str =
str
|> Js.to_string
|> js_run_repl
|> Js.string
end)
I had to convert the strings explicitly to ocaml strings and back to js
I am trying to find if some string is really in list. There is my code:
comparing() ->
FileName = "msg-0001",
{ok,[NumLine],_} = io_lib:fread("msg-~d",FileName),
io:format("Numline:~p~n", [NumLine]),
{ok, Pars} = file:read_file("parsing.txt"),
{ok, Dump} = file:read_file("msg-0001"),
StringNumline = lists:flatten(io_lib:format("~p", [NumLine])),
io:format("StringNumline:~p~n", [StringNumline]),
StringDump = lists:flatten(io_lib:format("~p", [Dump])),
io:format("StringDump:~p~n", [StringDump]),
SubStringDump = string:substr(StringDump, 4),
io:format("SubStringDump:~p~n", [SubStringDump]),
Ndump = concat(StringNumline, SubStringDump),
io:format("Ndump:~p~n", [Ndump]),
FineDump = Ndump--"\">>",
io:format("FineDump:~p~n", [FineDump]),
L1 = binary:split(Pars, <<"\r\n">>, [global]),
io:format("L1=~p~n", [L1]),
Check = lists:member(FineDump, L1),
io:format("Check=~p~n", [Check]),
if
Check ->
file:write_file("check.txt", "true\n", [append]);
true ->
file:write_file("check.txt", "false\n", [append])
end.
Here is output of the code:
10> c(compare).
{ok,compare}
11> compare:comparing().
Numline:1
StringNumline:"1"
StringDump:"<<\"hello\">>"
SubStringDump:"hello\">>"
Ndump:"1hello\">>"
FineDump:"1hello"
L1=[<<"0hello">>,<<"something">>,<<"anyword">>,<<"1hello">>,<<"2exercise">>,
<<"2solution">>,<<"3test">>,<<"new">>,<<"4check">>,<<"4grade">>]
Check=false
ok
I have a problem in line Check = lists:member(FineDump, L1). It's always false although 1hello is member of the list. I don't know where is the mistake. Is it function lists:member fine for this operation? Or does exist some other way to find if string is a member of a list? I'm new at Erlang.
L1 is a list of binaries while FineDump is a string (a list of integers in Erlang). You need to convert FineDump into a binary to make the lists:member/2 call work.
This should work:
Check = lists:member(list_to_binary(FineDump), L1),
You also seem to be doing this in a way too convoluted way than necessary. If I understood the logic fine, you don't need all that code. You can concatenate NumLine and Dump into a binary using just:
X = <<(integer_to_binary(NumLine))/binary, Dump/binary>>
and then use that directly in lists:member:
lists:member(X, L1)
1> NumLine = 1.
1
2> Dump = <<"hello">>.
<<"hello">>
3> <<(integer_to_binary(NumLine))/binary, Dump/binary>>.
<<"1hello">>
I want to read a line from a file, initialize an array from that line and then display the integers.
Why is is not reading the five integers in the line? I want to get output 1 2 3 4 5, i have 1 1 1 1 1
open Array;;
open Scanf;;
let print_ints file_name =
let file = open_in file_name in
let s = input_line(file) in
let n = ref 5 in
let arr = Array.init !n (fun i -> if i < !n then sscanf s "%d" (fun a -> a) else 0) in
let i = ref 0 in
while !i < !n do
print_int (Array.get arr !i);
print_string " ";
i := !i + 1;
done;;
print_ints "string_ints.txt";;
My file is just: 1 2 3 4 5
You might want to try the following approach. Split your string into a list of substrings representing numbers. This answer describes one way of doing so. Then use the resulting function in your print_ints function.
let ints_of_string s =
List.map int_of_string (Str.split (Str.regexp " +") s)
let print_ints file_name =
let file = open_in file_name in
let s = input_line file in
let ints = ints_of_string s in
List.iter (fun i -> print_int i; print_char ' ') ints;
close_in file
let _ = print_ints "string_ints.txt"
When compiling, pass str.cma or str.cmxa as an argument (see this answer for details on compilation):
$ ocamlc str.cma print_ints.ml
Another alternative would be using the Scanf.bscanf function -- this question, contains an example (use with caution).
The Scanf.sscanf function may not be particularly suitable for this task.
An excerpt from the OCaml manual:
the scanf facility is not intended for heavy duty lexical analysis and parsing. If it appears not expressive enough for your needs, several alternative exists: regular expressions (module Str), stream parsers, ocamllex-generated lexers, ocamlyacc-generated parsers
There is though a way to parse a string of ints using Scanf.sscanf (which I wouldn't recommend):
let rec int_list_of_string s =
try
Scanf.sscanf s
"%d %[0-9-+ ]"
(fun n rest_str -> n :: int_list_of_string rest_str)
with
| End_of_file | Scanf.Scan_failure _ -> []
The trick here is to represent the input string s as a part which is going to be parsed into a an integer (%d) and the rest of the string using the range format: %[0-9-+ ]", which will match the rest of the string, containing only decimal digits 0-9, the - and + signs, and whitespace .
function escape_sqli(source)
to_replace = {"'", '"'}
replace_with = {"\'", '\"'}
output = source
for i = 1, table.getn(to_replace) do
output = string.gsub(output, to_replace[i], replace_with[i])
end
return output
end
I tried the code above to Escape SQLis but I get the following error when I try to compile it:
Unfinished String near '"}'
As it currently is, there is no syntactical error in the code.
A suggestion though; From string.gsub documentation:
string.gsub (s, pattern, repl [, n])
[...]
If repl is a table, then the table is queried for every match, using
the first capture as the key.
You can simply recreate replacement tables as follows:
local replacements = { ['"'] = '\\"', ["'"] = "\\'" }
and use it in a single gsub call:
function escape_sqli(source)
local replacements = { ['"'] = '\\"', ["'"] = "\\'" }
return source:gsub( "['\"]", replacements ) -- or string.gsub( source, "['\"]", replacements )
end
I'm writing a simple ini file parser and I'm having a little problem with the initialization of the object in the "do" clause. It wants me to return a unit but i can't get the blankity function to do the side effects if I try to pipe into an "ignore" or if i return "()" directly.
This code works as a separate function because I can ignore the results.
#light
module Utilities.Config
open System
open System.IO
open System.Text.RegularExpressions
open System.Collections.Generic
type Config(?fileName : string) =
let fileName = defaultArg fileName #"C:\path\myConfigs.ini"
static let defaultSettings =
dict[ "Setting1", "1";
"Setting2", "2";
"Debug", "0";
"State", "Disarray";]
let settingRegex = new Regex(#"\s*(?<key>([^;#=]*[^;#= ]))\s*=\s*(?<value>([^;#]*[^;# ]))")
let fileSettings = new Dictionary<string, string>()
let addFileSetting (groups : GroupCollection) =
fileSettings.Add(groups.Item("key").Value, groups.Item("value").Value)
do File.ReadAllLines(fileName)
|> Seq.map(fun line -> settingRegex.Match(line))
|> Seq.filter(fun mtch -> mtch.Success)
|> Seq.map(fun mtch -> addFileSetting(mtch.Groups) // Does not have the correct return type
//|> ignore //#1 Does not init the dictionary
//() //#2 Does not init the dictionary
//The extra step will work
member c.ReadFile =
File.ReadAllLines(fileName)
|> Seq.map(fun line -> settingRegex.Match(line))
|> Seq.filter(fun mtch -> mtch.Success)
|> Seq.map(fun mtch -> addFileSetting(mtch.Groups))
Use Seq.iter (executing an action for each element - returning unit) instead of Seq.map (transforming elements).
The code doesn't work with ignore because Seq's are evaluated lazily and when you ignore the result, there is no need to run any code at all. Read this article