I think that if you test for a delimiter, it is deterministic. string_without//2
will take all codes up to the delimiter deterministically, then you optimistically give those codes to read_term_from_atom/3
(or why not term_string/2
?) Here is the full example:
$ cat parse_log.pl
:- module(parse_log, [delimited_term//2, nondet_term//1]).
:- use_module(library(dcg/basics)).
delimited_term(D, T) -->
string_without([D], S),
{ read_term_from_atom(S, T, []) },
[D].
nondet_term(T) -->
string(S),
/* this doesn't even work! */
{ read_term_from_atom(S, T, []) }.
You might want to hardcode your delimiter but anyway.
$ swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.11-8-gbf5ff3dae)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
For online help and background, visit http://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).
?- use_module(parse_log).
true.
?- phrase(delimited_term(0';, T), `foo(bar);`).
T = foo(bar).
I used a semicolon for the delimiter just to make it more obvious on the top-level.
The other one I tried to code by naively following your description, and it even seems to work at first sight:
?- phrase(nondet_term(T), `foo`).
T = foo ;
false.
… but of course you need to catch exceptions. Try parsing a compound with this.
I guess another option you have is to work directly on the stream, without a DCG, and use read_term/3
when you expect a term.
Yet another option would be to use library(csv).
I personally would anyway try to use any common command line tool to clean up (“massage”) my input, to make it more Prolog friendly, and write simple obvious Prolog code to parse it or just read it.
EDIT: for example, why not use awk
or something to make your input look like this:
log_line('2019-08-13', 123456, my_term(foo)).
log_line('2019-08-14', 234566, my_other_term(bar)).
Again, this depends on how well defined your actual data is. You might end up getting caught up in quoting problems, for example.