For example, the -g Goal switch/option suggests itself to specify Goal on the command line.
It should be handled as if the directive :- initialization(Goal). were present in a file that is being loaded.
For command line parameters in general, the following order may be useful:
$ scryer-prolog Option1 ... Optioni File1 ... Filej Switch1 ... Switchk
With the meaning that Option is an option specified to Scryer Prolog itself. Then, a number of Prolog source files are specified that need to be loaded. And then, a number of switches (additional options) is specified which are only meaningful from the perspective of the source files that are being loaded, and are made accessible to them for example via a Prolog flag.
This could be a good opportunity to revisit #303. Instead of printing the version from Rust, the passed flag could translate to a goal fed into the machine to output the version (which would have to be compiled into a fact, where it could be useful for other interpreted programs, too).
Not an experienced prologer, please bear with me 馃槄
Yes, absolutely! Please see #203.
It is always preferable to make things available for reasoning within Prolog. I mean that instead of emitting the version, it is preferable to make it accessible to Prolog code, for example via a fact or Prolog flag. This way, it can not only be printed, but also compared etc.
This feature can be useful for Prolog programs that depend on specific versions of Scryer.
Related: #84.
@pmoura 馃槂Thanks for making we are of this (d'oh I've seen that branch before, of course...) -- it made me reconsider #306 once more; let's do this instead: #311. It could also give a clue for what's missing in #84, I suppose.
Hmm I've come to realize that we can't call that env macro in builtin.pl. I suppose we could do a string replace or something?
@triska (comment) , from the Rust side I'm stuck for now. Changing toplevel.pl in this way:
'$repl'([_|Args]) :-
run(Args, []),
false.
'$repl'(_) :- repl.
run([], []) :- !.
run([A0|Args0], Gs) :-
atom_chars(AG, A0),
( member(AG, ['-g', '--goal']) ->
[A1|Args] = Args0,
atom_chars(AGoal, A1),
run(Args, [AGoal|Gs])
; catch(use_module(AG), E, print_exception(E)),
run(Args0, Gs)
).
run([], [G|Gs]) :-
(G ; true),
run([], Gs).
First the modules are loaded, then the goals are executed in reverse. The goal must be a predicate of arity 0.
Arbitrary Prolog code can't be executed unless the code is parsed first. The code allows:
scryer-prolog -g run0 file0.pl -g run1 file1.pl and scryer-prolog -g run0 -g run1 file0.pl file1.pl. If all predicates are the same atom then the order matters.
This test file shows how the predicate should look:
fac(0, 1).
fac(N, F) :-
N1 is N - 1,
fac(N1, F1),
F is N * F1.
run0 :-
fac(5, F),
write(F), nl,
!, % Cut is required here.
true.
run1 :-
fac(6, F),
write(F), nl,
!, % Cut is required here.
false.
Is there predicate for parsing like chars_goal("( run -> true ; write(failed), nl ), halt", G) where G is the predicate?
Please use read_term_from_chars/2 from library(charsio) to read a Prolog term from a list of characters. For example:
?- use_module(library(charsio)). true. ?- use_module(library(lists)). true. ?- Cs0 = "( run -> true ; write(failed), nl ), halt", append(Cs0, ".", Cs), read_term_from_chars(Cs, T).
Yielding the Prolog term T that can be called with call/1:
..., T = ((run->true;write(failed),nl),halt).
Note that '.' must be appended to the list of characters unless it is already there (possibly followed by layout characters).
What should happen when the goal fails? Does that failure halt scryer-prolog?
If no then does that failure stop the processing of the remaining arguments?
And should it be ignored or print a warning?
I think throwing an exception, or emitting a warning, would both be fine.
When a goal fails (and also when it succeeds), Scryer should start the toplevel interaction.
Ideally, there should be an additional command line option, say -t Goal that states that Goal shall be used as the toplevel. By default, if this flag is omitted, Goal is toplevel:repl. But it could be useful to specify for example -t halt on the command line. It means that after the given goal terminates, whether it fails, succeeds or throws an exception, Scryer will halt and return to the shell.
As regards the implementation: !/0 is not needed if argument indexing can distinguish the cases, which is the ideal situation that's worth striving for. For example, given:
l([]). l([_|Ls]) :- l(Ls).
The following is deterministic, because the clauses can be told apart by the principal functor of their first (and only) argument:
?- l([_,_,_]). true.
In addition, if strings are already available, then they can simply be used. For example, it is perfectly admissible to use strings here:
?- member(AG, ["-g", "--goal"]). AG = "-g" ; AG = "--goal" ; false.
The advantage is that no atoms are created, and therefore the atom table remains intact. The more temporary the data is, the better it is to use strings, because they can be allocated and deallocated quickly. Since arguments are processed only once, during initialization of Scryer, strings are a very good representation.
An important advantage if strings is that they are amenable to processing via DCGs. For instance, to detect whether a string ends with ., possibly followed by layout characters, we can use:
:- use_module(library(lists)).
:- use_module(library(dcgs)).
:- use_module(library(charsio)).
ends_with_dot(Ls0) :-
reverse(Ls0, Ls),
phrase(layout_and_dot, Ls, _).
layout_and_dot --> ".".
layout_and_dot --> [C], { char_type(C, layout) }, layout_and_dot.
Example:
?- ends_with_dot("hello. ").
true
The brilliance of Scryer's string representation makes this very memory-efficient.
Your implementation of char_type/2 is very useful for this!
Made a PR, will incorporate the changes. A lot is missing.
It is possible to do this: ?- true. /* comment */. If the idea is to run a program then it could fail with syntax error.
Does the order matter? Like scryer-prolog -g run file1.pl -g run file2.pl depends on the order of execution.
In the case of scryer-prolog -t run file1.pl if the predicate is in file1.pl then the order for -t doesn't matter. But it's also possible to do scryer-prolog file1.pl -t run1 -t run2 which of the -t is going to be executed first?
It seems that adding dcgs in toplevel.pl creates an infinite loop.
term_from_chars/2 will read a term from the string, much like read/1:
?- read_term_from_chars("true. /* comment */.", Term).
Term = true.
So, it can be expected that the first term (which is the goal) is run.
Ideally, the goals should be executed in the order they appear. However, even supporting only a single "-g" flag would already be tremendously useful, because users can always write G1,G2 to invoke both G1 and G2, without needing separate -g switches for it.
Regarding scryer-prolog file1.pl -t run1 -t run2: Once a concrete file appears, then Scryer must stop processing the command line flags, because the remaining flags need to be parsed so that file1.pl can process it. Only what appears before the first file should affect Scryer itself. This includes the -v, -g and -t switches. Everything after that should ideally be available via the flag argv, so that Prolog programs can inspect the remaining arguments.
So something like scryer-prolog [options] file [data], where the available options are -v, -h, -t, -g, -argv and it loads one module _file_, the remaining is data (a list of chars) available if -argv is set for _file_.
For the flags argv, is it like scryer-prolog -argv run file.pl data1 data2 where run/1 takes [data1, data2]? Is argv like -g or like -t?
[data] is anything on the command line, each argument represented as a list of chars.
A typical use case, once Scryer provides library(sockets) will be for example flexibly starting a web server via:
$ scryer-prolog server.pl --port=5054 --user=you
where argv is ["--port=5054","--user=you"].
And argv is accessed via current_prolog_flag/2, where we currently already have a few flags available:
?- current_prolog_flag(Flag, Value). Flag = bounded, Value = false ; Flag = integer_rounding_function, Value = toward_zero ; Flag = double_quotes, Value = chars ; false.
The pull request is now already filed at https://github.com/mthom/scryer-prolog/pull/439.
I am closing this issue, thank you so much @srenatus and @notoria for this very nice feature!
The only thing that remains is to make the remaining arguments available in a flag, if that is somehow possible. Already having -g available is extremely nice!
馃帀 Makes me happy to see this resolved! Nicely done :)
Most helpful comment
Please use
read_term_from_chars/2fromlibrary(charsio)to read a Prolog term from a list of characters. For example:Yielding the Prolog term
Tthat can be called withcall/1:Note that
'.'must be appended to the list of characters unless it is already there (possibly followed by layout characters).