Flowlog

41: * Timeout
Having a nice answer description that effectively realizes a specific timeout would be nice. The question is rather how to call it? Maybe takes_less_than(4s), needs_less_than.
40: * quads: sto annotation
reads:
?- X = f(X).
   sto, X = f(X) % With occurs-check: fails or error
|  sto, loops.   % Without occurs-check: may loop
This case is particularly ambiguous, as there is not a single system that loops here. Many systems loop when writing the answer, though. More explicitly:
?- X = f(X), Y = f(Y), X = Y.
   sto, false   % occurs-check
|  sto, representation_error(term) % occurs-check error
|  sto, loops
|  sto, resource_error(...) % effectively implied by loops
|  sto, X = f(X), X = Y   % rational trees
|  sto, X = f(f(Y)), Y = f(f(Y)) % literal substitution, Tau
|  sto, ... .
39: * quads: Malformed answer description accepted
?- X = 1.
   1 = X. % not an answer substitution
v4.11.0
38: * multiple answer descriptions not supported
Currently, only a single answer description is possible. This is in particular of relevance when reporting bugs. So a succession of bug reports can be compactly described.
"✳54·43" ?- X is 1+1.
   X = 3, unexpected. % almost
   X = 1, unexpected. % too low
   X = 2.0, unexpected.
% and after checking PM:
   X = 2.
37: * (?-)/2 not supported
Same as (?-)/1 but with the first argument a ground term to identify the test case. Like in number_chars_quad.
36: * quads: wrong documentation
In QUAD_TESTS
...
# Run a single quad file (two equivalent forms)
make quad tests/predicates/assertz_quad.pl
make quad QUAD_FILE=tests/predicates/assertz_quad.pl
The first version does not work, the second does.
35: * Unclear quad
at_end_of_stream_quad.pl:
% Verify predicates exist
?- predicate_property(at_end_of_stream, built_in).
   true
|  predicate_property(at_end_of_stream, _).
This is not an answer description!
34: * Some more specific test quads
arith_eq_quad.pl:
?- X =:= foo.
   instantiation_error
|  type_error(evaluable,foo/0).
?- foo =:= X.
   idem.
Because in 7.12:
When more than one error condition is satisfied, the error
that is reported by the Prolog processor is implementation
dependent.
33: * quads: arg/3
arg_quad.pl:
% Negative argument fails
?- arg(-1, foo(a,b), X).
   false
|  error(domain_error(not_less_than_zero, -1), _).
Should read just
?- arg(-1, foo(a,b), X).
   domain_error(not_less_than_zero, -1).
32: * quads: Minor naming
% Can't abolish static predicates
% ?- abolish(append/3).
%    permission_error(modify, static_procedure, append/3).
Why not:
?- abolish(abolish/1)).
   permission_error(modify, static_procedure, abolish/1)
|  permission_error(modify, static_procedure, ...).
(It's not clear how you indicate modules, in case they appear ...)
31: * quads: Impossible outcome
abolish_quad.pl:
% Abolish non-existent predicate (may succeed or error)
?- abolish(abolish_nonexistent_xyz/3).
   true
|  error(existence_error(procedure, abolish_nonexistent_xyz/3), _).
This can never happen, however
|  permission_error(modify, static_procedure, ...).
might be possible. But since you implicitly imply qua the name that abolish_nonexistent_xyz/3 does not exist, it cannot happen. (But then note that we have again some implicit assumption, and testing should avoid such nonchalant implications.)
30: * quads: Unclear answer description
abolish_quad.pl:
?- assertz(abolish_test(1)), assertz(abolish_test(2)),
   abolish(abolish_test/1),
   \+ current_predicate(abolish_test/1).
   true
|  \+ abolish_test(_).
How can this alternative exist? The only alternative imaginable is
|  permission_error(modify, static_procedure, ...).
29: * quads: Missing error formats
asserta_quad.pl:
?- asserta(X).
   error(instantiation_error, _).
This insists on the second argument being a variable. Instead:
?- asserta(X).
   error(instantiation_error, ...).
   instantiation_error.  % compact like in 13211-1
   throw(error(instantiation_error, ...)).
That is, all three are valid outcomes, and all three have to be tested. In answer descriptions variables represent variables (or some rational, infinite trees, but only when sto ...).
Will continue testing number_chars/2 extensively after #28, #27, #19, #17,
28: * top level: disjunctive hickup
With larger computations, the semicolon is written multiple times and thus the output is no longer valid Prolog text.
% ugly hack because of unaddressed flowlog-prolog#19

glength([],0).
glength([_|L],N1) :-
   glength(L,N2),
   N1 is N2+1.

abs([]).
abs([C|Cs]) :-
   ( C = a ; C = b ),
   abs(Cs).

?- length(L,16),(X=1;X=2),( abs(L), fail ; true ).
   L = [_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15], X = 1
; % unexpected output
;  L = [_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15], X = 2.
?- glength(L,N), N > 14, ( abs(L), fail ; true ).
   L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28], N = 15
; % unexpected output
;  L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28,_30], N = 16
; % unexpected output
;  L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28,_30,_32], N = 17
; % unexpected output
;  L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28,_30,_32,_34], N = 18
; % unexpected output
;  L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28,_30,_32,_34,_36], N = 19
;  ... .
?- glength(L,N), N > 14, countall(abs(L),Cnt).
   L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28], N = 15, Cnt = 32768
; % unexpected output
;  L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28,_30], N = 16, Cnt = 65536
; % unexpected output
;  L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28,_30,_32], N = 17, Cnt = 131072
; % unexpected output
;  L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28,_30,_32,_34], N = 18, Cnt = 262144
; % unexpected output
;  L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28,_30,_32,_34,_36], N = 19, Cnt = 524288
;  ... .
?- glength(L,N), N > 13, countall(abs(L),Cnt).
   L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26], N = 14, Cnt = 16384
;  L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28], N = 15, Cnt = 32768
; % unexpected output
;  L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28,_30], N = 16, Cnt = 65536
; % unexpected output
;  L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28,_30,_32], N = 17, Cnt = 131072
; % unexpected output
;  L = [_0,_2,_4,_6,_8,_10,_12,_14,_16,_18,_20,_22,_24,_26,_28,_30,_32,_34], N = 18, Cnt = 262144
;  ... .
v4.10.2
27: * segv
l(0).
l(N0) :-
   l(N1),
   N0 is N1+1.

?- l(N).
   N = 0
;  N = 1
;  N = 2
;  N = 3
;  N = 4Segmentation fault (core dumped)
26: * number_chars/2
nc#71
25: * $-ambiguities
s#366
24: *

Valid term rejected
nihil --> [].
This is a valid term, even if you do not have any DCG support. It should be at least a fact.
v4.10.1 number_chars/2 passes all current tests
23: * Operator '|' needed for DCGs
Definite Clause Grammars are defined in 13211-3, they have the bar operator defined. Including them by default does make a lot of sense. Also quads need this operator defined in that way.
v4.9.18 no progress for number_chars/2
22: * Minor big integer problem
#7, #8.
?- N is 2^63, functor(_,f,N).
   error(type_error(integer,9223372036854775808),functor/3), unexpected.
   type_error(...,...), unexpected. % any type error is bad
?- N is 2^63, arg(N,f(1),_).
   error(type_error(integer,9223372036854775808),arg/3), unexpected.
   false. % expected, but not found
21: * Output in quads
For simple output there is outputs/1 with a grammar rule body as argument. To start with, a list of characters will do.
?- call((write(3), call(1)).
   outputs("3"),
   type_error(callable, 1).
Compare this to the original example in 7.8.3.4:
call((write(3), call(1)).
   Outputs '3', then
   type_error(callable, 1).
So it's outputs not output. The comma has here the meaning and then. Answer constraints and answer substitutions come last in a leaf-answer. (Unclear, if there are situations to put them first. Maybe if constraints and side effects are involved. But this gets way too complex.)
?- Ch = t, put_char(Ch).
   outputs("t"),
   Ch = t.
20: * sub_string/5 unusual
Why the same as sub_atom/5? If (at all), it might be defined over strings (lists of chars).
19: * length/2
l#1, l#4 ... And the actual quads are here. In case of difficulties, please refer to the sources of Scryer.
?- length(L,N).   % #1
error(existence_error(procedure,length/2),length/2), unexpected.
   L = [], N = 0
;  ... . % expected, but not found
?- length(2,0).   % #4
error(type_error(list,2),length/2), unexpected.
   false. % expected, but not found
v4.9.13
18: * Unexpected operator
?- current_op(Pri,Fix,'|').
   Pri = 1150, Fix = xfy
;  Pri = 1150, Fix = fy, unexpected.
There must not be any such operator. Only the infix '|' is possible. See Cor.2#C2 and 8.14.3.4. Otherwise Flowlog is correct.
?- op(1150,fy,'|').
   error(permission_error(create,operator,'|'),op/3). % nice
?- op(0,fy,'|').
   true. % nice
v4.9.8
v4.9.6
v4.9.3
v4.9.0
For the moment, I will stop testing Flowlog, since the most elementary items are ignored. I will look at s#1 only and update other issues only after this has been addressed.
17: * append/3 and double quotes entirely disfunctional
?- append("ab","cd",L).
   false, unexpected.
   L = "abcd". % expected, but not found
?- append([],Ys,Zs).
Segmentation fault (core dumped)
   Ys = Zs. % expected, but not found
?- append("a",Ys,Zs).
   false, unexpected.
?- member(X,"a").
   false, unexpected.
?- member(X,[a]).
   X = a.
?- [X] = "a".
   X = a.

cons([_|_]).
?- cons("a").
   false, unexpected.
v4.8.0
v4.7.2
16: * Elementary read syntax still not supported
like s#28 (all these tables should be machine readable by anyone including LLMs)
15: * No -0.0
ISO Prolog does not contain -0.0 as a number distinct from 0.0. Therefore, they must be identical and print identically.
?- 0.0 == -0.0.
   true. % good
?- writeq(-0.0).
   outputs("-0.0"), unexpected.
   outputs("0.0"). % expected, but not found
14: * number_chars/2 different to read-syntax
?- writeq(010).
10   true. % nice
?- number_chars(N,"010").
   N = 8, unexpected.
?- number_chars(N,"08").
   N = 8. % expected, but not found
   syntax_error(...), unexpected.
v4.7.1
13: * random answer order
Flowlog (v4.6.14)
Type 'help.' for help, 'halt.' to exit.
?- select(X,[1,2,3,4,5,6,7,8],_).
   X = 1
   X = 3, unexpected % too early
   X = 6, unexpected
   X = 2
   X = 7, unexpected
   ...
?- select(X,[1,2,3,4,5,6,7,8],_).
   X = 2, unexpected
   ...
?- select(X,[1,2,3,4,5,6,7,8],_),!.
   X = 2, unexpected. % sometimes, retry to see
v4.6.14
v4.6.13
v4.6.10
v4.6.8
v4.6.6
12: * infinite loop does not work
inf :-
   inf.

?- inf.
Segmentation fault (core dumped), unexpected.
   loops % expected
|  resource_error(...). % if you insist, ... but a WAM should loop
11: * append/3 does not work
(Example from the Prolog prologue)
?- append([a], Ys, Zs).
Segmentation fault (core dumped), unexpected.
   Zs = [a|Ys]. % expected, but not found
   Ys = _1, Zs = [a|_2], unexpected.
(above with default. Does work with interp. With wam:)
?- append([a], Ys, Zs).
   Ys = Xs, Zs = [a|Xs], unexpected. % no longer
   Ys = _1, Zs = [a|_2], unexpected. % new
Where does Xs come from?
v4.6.5
10: * Default of double_quotes
Consider chars as default. Like all new systems like Scryer, 1ban, Trealla, Tau. And now also Flowlog!
9: * git clone problem with old system
ulrich@p0:/tmp$ git clone https://git.liminal.cafe/byakuren/flowlog.git
Cloning into 'flowlog'...
fatal: unable to access 'https://git.liminal.cafe/byakuren/flowlog.git/': gnutls_handshake() failed: Handshake failed
ulrich@p0:/tmp$ cat /etc/issue
Ubuntu 14.04.6 LTS \n \l
This does not happen with github. There I follow various systems like Scryer, Trealla.
8: * select/3 not conforming
Since 2011 select/3 is defined without any errors. Just with two clauses. (Actually, it was commonly defined like that since at least 1983. C&M:1981 did not use it, nor in the 5th ed. 2003) Thus the following is not conforming:
?- select(X, Xs, Ys).
false, unexpected.
?- select(X, Xs, Xs).
false, unexpected. % see p.p.5.4
?- select(X,Xs,[1,2,3]).
false, unexpected.
% this is the expected answer:
   Xs = [X,1,2,3]
;  Xs = [1,X,2,3]
;  Xs = [1,2,X,3]
;  Xs = [1,2,3,X].
?- select(X, [Y|nonlist], Xs).
error(type_error(list,[_G0|nonlist]),/(select1,3)), unexpected.
This type error would make sense, if the template and modes subclause would indicate ?list for the second argument, but it reads in p.p.5.2
select(?term, ?term, ?term)
?- Xs = [a|Xs], ( true ; catch(select(X, Xs, Ys), Err, true ) ).
   Xs = [a,a,a,a,a|...]
;  Xs = [a,a,a,a,a|...], error(representation_error(term),/(select1,3)), unexpected.
% expected behaviour:
   sto,
   false % occurs-check
|  sto,
   Xs = [a|Xs] % rational trees
;  Xs = [a|Xs], X = a, Ys = [a|Ys]
;  Xs = [a|Xs], X = a, Ys = [a|Ys]
;  ..., ad_infinitum
|  sto,
   representation_error(term).
A representation error can only be issued at a point in time when a goal attempts to create some term that cannot be represented. So this error prevents the creation of something. Thus the goal Xs = [a|Xs] may issue a representation error, if unification is defined in such a manner (e.g., set_prolog_flag(occurs_check, error) in Scryer or Trealla). But from above answer it is clear that this goal succeeds, and thus the infinite tree is created. (Do not call it an infinite term since terms are defined such that there are no cyclic structures in them).
7: * integer overflow incorrect
?- 62 = E, X is 2^E-1+2^E.
X = 9223372036854775807 % good
?- 62 = E, X is 2^E-1+2^E+1.
X = -9223372036854775808, unexpected.
?- X is 2^63.
error(evaluation_error(undefined),/(is,2)), unexpected.
   evaluation_error(int_overflow) % expected exceptional value (3.66, 7.9.2 e, 7.12.2 g, 9.1.2)
|  X = 9223372036854775808. % current_prolog_flag(bounded,false)
current_prolog_flag(bounded,false) in SICStus, Scryer, Trealla, YAP, B, IF, IV, SWI, ECLiPSe, Ciao. Internally, these implementations have two versions of integers, the small immediate ones and the large one which reside on the heap. In this manner the immediate integers may use less than 60..63 bits, which frees tagging space for other uses.

Only GNU, 1ban with current_prolog_flag(bounded,true) and correct overflows.

GNU:

| ?- 59 = E, X is 2^E-1+2^E.

X = 1152921504606846975

yes
| ?- 59 = E, X is 2^E-1+2^E+1.
uncaught exception: error(evaluation_error(int_overflow),(is)/2)
1ban:
?- 62 = E, X is 2^E-1+2^E.
X = 9223372036854775807.
?- 62 = E, X is 2^E-1+2^E+1.
2026/01/02 11:43:26 error(evaluation_error(int_overflow),is/2)
XSB, Cx, Minerva are also bounded, but miss some overflows.

In general, it is much easier to be conforming with unbounded integers, because this overflow evaluation_error never occurs which limits arithmetical optimizations. And one has to test for overflows anyway.

6: * writeq ignores operators
Before improving read-ing, consider writeq-ing. Such that you always write valid syntax. So the first to address would be:
  1. s#1,
  2. s#13,
  3. s#27,
  4. s#35,
  5. s#36
5: * testing number_chars/2
Because of #3 and #4, I cannot Now I use the current test table, so I resorted to like the old one. Please go through it. (Numbers are the same) Problems start with #9, #17, #21.
ulrich@gupu:/opt/gupu/flowlog$ ./flowlog 
Flowlog (pthreads)
Type 'help.' for help, 'halt.' to exit.
?- number_chars(1,[_,[]]).
error(instantiation_error,/(number_chars,2)), unexpected.
   type_error(character,[]). % expected, but not found
?- number_chars(N,['3',.]).
N = 3.0, unexpected.
   syntax_error(...). % expected, but not found
?- number_chars(N,[-,' ','1']).
flowlog: fatal error (240): flowlog: unexpected token while parsing term
4: * read/1 strangely buffered
Sometimes, read/1 succeeds immediately after entering a valid term (and return/newline), and sometimes it needs a C-d (control-d) to flush out the buffer, but then this C-d is interpreted as an end-of-file.
ulrich@gupu:/opt/gupu/flowlog$ ./flowlog 
Flowlog (pthreads)
Type 'help.' for help, 'halt.' to exit.
?- read(X),catch(X,Err,true),writeq(X+Err),nl,fail.
number_chars(N,[X]).
number_chars(_1,[_2])+error(instantiation_error,number_chars/2) % good
false
?- repeat,read(X),catch(X,Err,true),(X==end_of_file,!;writeq(X+Err),nl,fail).
number_chars(N,[X]).


   % unexpected. Only C-d causes a reaction, newlines alone do not work here
number_chars(_1,[_2])+error(instantiation_error,number_chars/2)
X = end_of_file, Err = error(existence_error(procedure,/(end_of_file,0)),/(end_of_file,0))
Also entering SPACE C-d instead does not work. This should flush the line and since it ends with ". ", the end-token is present already.

So how is it possible to write my own top level for the purpose of testing?

3: * top level ignores Prolog flag
The goal set_prolog_flag(double_quotes, chars) is ignored by the top level loop.
ulrich@gupu:/opt/gupu/flowlog$ ./flowlog 
Flowlog (pthreads)
Type 'help.' for help, 'halt.' to exit.
?- set_prolog_flag(double_quotes,chars).
true
?- read(X).
"abc".
X = [a,b,c] % good
?- writeq("abc").
[97,98,99]true % unexpected
2: * compilation on older system does not work
ulrich@p0:/opt/gupu/flowlog$ make
cc -O2 -Wall -Wextra -pthread flowlog.c -lm  -o flowlog
flowlog.c:41:23: fatal error: stdatomic.h: No such file or directory
 #include 
                       ^
compilation terminated.
make: *** [flowlog] Error 1
ulrich@p0:/opt/gupu/flowlog$ cc --version
cc (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1: * Consider github
or at least redo the color scheme, the dark colors are very hard to read. Solved with git repository.
Installation:
wget https://web.liminal.cafe/~byakuren/flowlog/flowlog.tar.gz
git clone https://git.liminal.cafe/byakuren/flowlog.git
make