Git Product home page Git Product logo

trealla's Issues

findall/3 bagof/3 performance

$ cat ffbb.pl 

isl({L},N) :- L = (_ is _+N).

test1 :- time(findall(Z,(between(1,5,N), isl({X}, N),
                         X = (Y is B+_), findall(Y,(between(1,3,B),X),Z)),
	              M)), write(M).

test2 :- time(bagof(Z,N^X^W^(between(1,5,N), isl({X}, N),
                             X = (Y is B+W), bagof(Y,X^(between(1,3,B),X),Z)),
	            M)), write(M).

testff :- time(findall(Z,(between(1,5_000_00,N), isl({X}, N),
                          X = (Y is B+_), findall(Y,(between(1,3,B),X),Z)
		         ),
	               M)), length(M,L), L1 is L*3, write(L1), write(' findall/findall Elements').

testbb :- time(bagof(Z,N^X^W^(between(1,5_000_00,N), isl({X}, N),
                              X = (Y is B+W), bagof(Y,X^(between(1,3,B),X),Z)
	                     ),
	             M)), length(M,L), L1 is L*3, write(L1), write(' bagof/bagof Elements').


test1, test2 are just there to show what is processed.(Of course the same data sets).

v2.1.8

$ tpl ffbb.pl 
?- nl,test1,nl,nl,test2,nl,nl,testff,nl,nl,testbb,nl.

Time elapsed 0.000116s
[[2,3,4],[3,4,5],[4,5,6],[5,6,7],[6,7,8]]

Time elapsed 0.000542s
[[2,3,4],[3,4,5],[4,5,6],[5,6,7],[6,7,8]]

Time elapsed 2.71s
1500000 findall/findall Elements

Time elapsed 26.2s
1500000 bagof/bagof Elements  % Performance loss
   true.
?- 

Can define operator '.', but it doesn't parse

Tried to run this:

{"quote":"Winning isn’t everything, but wanting to win is.","author":"Vince Lombardi"}.
{"quote":"I am not a product of my circumstances. I am a product of my decisions.","author":"Stephen Covey"}.
{"quote":"You miss 100% of the shots you don’t take.","author":"Wayne Gretzky"}.
{"quote":"Every strike brings me closer to the next home run.","author":"Babe Ruth"}.
{"quote":"Whether you think you can or you think you can’t, you’re right.","author":"Henry Ford"}.

member_comma(Z, (X,_)) :- member_comma(Z, X).
member_comma(Z, (_,Y)) :- !, member_comma(Z, Y).
member_comma(X, X).

:- op(100,yfx,'.').
:- op(800,xfx,':=').
X := A.B :- member_comma(B:X, A).

main :- statistics(wall, W), N is W mod 5+1, call_nth({ X }, N), Q := X."quote", A := X."author",
   write(Q-A), nl.

But I got errors:

?- ['quotes.pl'].
Warning: singleton: X, near line 13, file 'quotes.pl'
Warning: singleton: A, near line 13, file 'quotes.pl'
Warning: singleton: X, near line 13, file 'quotes.pl'
Warning: singleton: A, near line 13, file 'quotes.pl'
Error: '.', line 13, '
'
   true.

If Trealla cannot parser operator '.', wouldn't it make more sense to
throw already an error during op/3.

Dropping cyclic-terms, consideration

I am considering dropping support in Trealla for working with cyclic-terms/rational-trees. Trealla would just detect cycles and error, like GNU Prolog does.

#1: Recreation of old issues

As of now, the old issues are still gone. These issues are pretty valuable, as they contain many useful test cases.

garbage collection

Hi! Is there any documentation about the GC algorithm implemented in Trealla?

I'm curious about this: "Dynamic atoms are automatically garbage collected"

Upstreaming WASM changes

Thanks for merging my WASM PRs. I'm making this issue to help me keep track of what hasn't been upstreamed yet.
All of this stuff is available at: https://github.com/guregu/trealla

  • WASM build action: #57
    • This should help us know when we break the WASM build.
    • I'll keep doing releases to WAPM from guregu/trealla until the fork no longer needs to exist.
  • Use Wizer to pre-initialize a global interpreter for WASM
    • This brings the startup time of new interpreters from ~180ms to ~5ms in browsers.
    • Includes one Apache 2.0 licensed header file. It's a very simple macro so I might just rewrite it to keep us all MIT.
  • library(js) JSON-based programmatic toplevel & javascript native predicates
    • Need to split this into 2 modules: JSON toplevel (js_ask/1) and Javsascript-specific helpers like js_fetch/3 (trealla-prolog/go needs the toplevel but not the JS helpers)
  • JSON related hacks: guregu#5
    • Solved in #92
  • Host call stuff: '$host_call'/2 and '$host_resume'/1. These are two native predicates for passing data between guest → host → guest, used for calling JS from Prolog.
    • Includes Makefile changes to build the pure WASI version and the 'libtpl.wasm' version with host call predicates.
    • If we use better WASI libraries for trealla-js, we might not need these at all, and we can use pipes/sockets/whatever instead.
  • Toplevel formatting hacks for WASM/pl_query.
    • In pl_query mode, don't print spaces before var dumps.
    • In pl_query mode, always include the dot for terms and don't print "; " on redo, etc. This gives us uniform results in the WASM library query iterators.
    • For WASM, print warnings to stderr instead of stdout (keeps stdout clean and easy to parse).
  • Export all of trealla.h and add a query_did_yield API in trealla.h

Wow, that was a lot of stuff. Anyway, instead of dumping one huge PR on you I will make small ones that add things as they are stabilized.
Of course, I'd be happy to change how things work so feel free to reject anything. My aim is to be minimally invasive :-)

bagof/3 issue

v2.1.4

$ cat xyz.pl

isl({L},N) :- L = (_ is _+N).

testf :- findall(Z,(between(1,5,N),
                    (isl({X}, N), X = (Y is B+_), bagof(Y,X^B^(between(1,3,B),X),Z))
		   ),
	         M), write(M).

testb :- bagof(Z,N^X^Y^B^(between(1,5,N),
                          (isl({X}, N), X = (Y is B+_), bagof(Y,X^B^(between(1,3,B),X),Z))
	                 ),
	       M), write(M).
$ tpl xyz.pl
?- testf.
[[2,3,4],[3,4,5],[4,5,6],[5,6,7],[6,7,8]]   true.

?- testb.
[[2,3,4]]   true  % unexpected? expecting the whole list(see testf).
; [[3,4,5]] true
; [[4,5,6]] true
;  ... .
?-

testf: findall/3 works as expected and gathers up Z of (sub-)bagof/3, which works as expected, into M.

testb: bagof/3 does not collecting Z as findall/3 does but it should, shouldn't it?

Can't read from sockets

I'm having some trouble getting the HTTP server example to run:

tpl samples/http_server.pl -g main

It seems to accept connections fine, but getline/3 repeatedly fails with EAGAIN, leaving the client hanging forever. I get this behavior with threads enabled and disabled.

Not sure if this is actually a bug or an issue with my environment. Could someone else give this a try? I'm on Ubuntu 20.04 under WSL.

must_be/2 "a collection of fragments?"

v2.0.10

?- must_be(positive_int, +-).
   true.
?- must_be(positive_int, \0 + - 1).
   true.

?- must_be(flix,flux).
   true.
%  may_be
?- must_be(flux,flix).
   true.

Ludwig van B's Symphony No. 10 in E♭ major is a hypothetical work (1827) ... In 2019, artificial intelligence technology was used to reconstruct ... (wikipedia).
Not all hope is lost.

feature request ¤ should behave like $ during writing

Trealla gives me:

$ ./tpl -v
Trealla Prolog (c) Infradig 2020-2022, v2.0.16
$ ./tpl
?- X = $$$ .
   X = $$$ .
?- X = $$$6 .
Error: syntax error, near '6', operator expected
?- X = '$$$6' .
   X = '$$$6'.

But then it gives me:

$ ./tpl
?- X = ¤¤¤ .
   X = '¤¤¤'.
?- X = ¤¤¤6 .
Error: syntax error, near '6', operator expected
?- X = '¤¤¤6' .
   X = '¤¤¤6'.

So the reading seems no problem. But the writing, I am not sure about,
but if ¤ is in the same graphic char Prolog classification as $, there wouldn't
be a need to quote a sequence ¤ during output.

At least SWI-Prolog does the same, see also:

mthom/scryer-prolog#1515

Dropping attributed variables, consideration

I am considering dropping support for attributed variables, such as they are, in Trealla. Trealla used to do freeze/2 natively and could do so again. Ditto eventually for dif/2 & when/2. Maybe revisit it later.

Syntax error in json library

?- use_module(library(json)).
Error: syntax error, mismatched parens/brackets/braces, line 153
   error(existence_error(procedure,dcg_translate/0),top_level/0).

From one of the recent changes. I'll try and figure out what the issue is.

findall/3 member/2 core dump

$ cat testmem.pl

isl({L},N) :- L = (_ is _+N).

testmem(Lim) :- findall(Z,(between(1,Lim,N), isl({X}, N),
                           X = (Y is B+_), findall(Y,(between(1,3,B),X),Z)),
	                M),
	        M1 =[[2,3,3],[3,4,4],[4,5,5],[5,6,6],[6,7,7]],
	        !,
	        member(Q,M1), \+member(Q,M),
	        findall([_,_,Q3], member([_,_,Q3],M),F),
                length(F,Lf), write(Lf), nl, false.
v2.1.8 (main)
$ tpl testmem.pl

?- testmem(1000).
1000
1000
1000
1000
1000
   false.

?- testmem(2000).
2000
2000
malloc(): invalid size (unsorted) 
Aborted (core dumped)

% I see the malloc() error when running testmem(1000) first
% Standalone I get the realloc() error as below

v2.1.8-3-g994c (devel) 
?- testmem(2000).
2000
2000
realloc(): invalid next size
Aborted (core dumped)

Memory streams

For the JS integration, it'd be nice if I could set_output/1 to a memory buffer. This would make it a lot easier to wrangle the specially formatted output for the programmatic toplevel vs. user code writing to stdout. Currently there's some hairy logic using ASCII control codes to work around it.

% kind of like this in SWI but without the implicit once/1?
with_output_to(chars(Cs), call(Goal)),
write({"stdout": Cs}).

I'd like to avoid the OS filesystem as some WASM runtimes may not have a FS at all.

Reset history for version 2

Time for a clean slate after two years of code-cruft. A gigabyte of git commit history was just ridiculous.

#12: nth0/3 incorrect

?- nth0(N, [[]|Es], Es), ground(Es).
   N = 0, Es = []
;  N = 2, Es = [2|2], unexpected
;  ... .

freeze/2 uninstantiation_error

v2.0.16

?- member(A,Z), freeze(B,B), B=!, writeq(A/Z/B).
_0/[_0|_4]/ !   Z = [A|_A], B = !
;   error(uninstantiation_error(frozen),'$put_attributes'/2).
?- 

At present I can't interpret the error message: uninstantiation_error(frozen)?

And if freeze freezes together it unfreezes:

?- member(A,Z), freeze(B,B), freeze(B,B), B=!, writeq(A/Z/B).
_0/[_0|_4]/ !   Z = [A|_A], B = !
; _0/[_5,_0|_7]/ ! Z = [_A,A|_B], B = !
; _0/[_5,_8,_0|_10]/ ! Z = [_A,_B,A|_C], B = !
;  ... .
?-

freeze/2 spooky backtracking

Correct...

$ tpl
?- freeze(Y,write(ok)),(X=Y;true),Y=123.
ok   Y = 123, X = Y
; ok Y = 123.
?- $ tpl

Incorrect...

?- _=[X,Y],freeze(Y,write(ok)),(X=Y;true),Y=123.
ok   X = 123, Y = X
; ok Y = 123, freeze:freeze(X,write(ok)).
?- 

Leak in term writing?

The following program aborts with a malloc error (likely from a buffer overflow):

:- op(201, fy, '@').

test :-
	Result = dict('.'(:(valid,@(true)),'.'(:(fields,dict('.'(:(revision,dict('.'(:(valid,@(true)),'.'(:(ignored,@(true)),[])))),'.'(:(creator,dict('.'(:(valid,@(true)),'.'(:(ignored,@(true)),[])))),'.'(:(updated_at,dict('.'(:(valid,@(true)),'.'(:(ignored,@(true)),[])))),'.'(:(created_at,dict('.'(:(valid,@(true)),'.'(:(ignored,@(true)),[])))),'.'(:(qux_abbreviation,dict('.'(:(valid,@(true)),[]))),'.'(:(qux_name,dict('.'(:(valid,@(false)),'.'(:(error,'256文字以下の値を入力してください'),[])))),'.'(:(baz_id,dict('.'(:(valid,@(false)),'.'(:(error,'256文字以下の値を入力してください'),[])))),'.'(:(owner_id,dict('.'(:(valid,@(true)),'.'(:(ignored,@(true)),[])))),'.'(:(qux_type_code,dict('.'(:(valid,@(true)),[]))),'.'(:(foo,dict('.'(:(fields,dict('.'(:('foo/bar_groups',dict('.'(:(valid,@(true)),[]))),'.'(:('foo/acme_notification_url',dict('.'(:(valid,@(false)),'.'(:(error,'「https://」で始まらなければなりません'),[])))),'.'(:('foo/qux_api_version',dict('.'(:(valid,@(true)),[]))),'.'(:('foo/xyz_unit_id',dict('.'(:(valid,@(false)),'.'(:(error,'256文字以下の値を入力してください'),[])))),'.'(:('foo/abc_label',dict('.'(:(fields,dict('.'(:('foo/abc_label/print_abc_list',dict('.'(:(valid,@(true)),[]))),'.'(:('foo/abc_label/report_id',dict('.'(:(valid,@(true)),[]))),'.'(:('foo/abc_label/report_setting_key',dict('.'(:(valid,@(true)),[]))),[]))))),'.'(:(valid,@(true)),[])))),'.'(:('foo/xyz_notes',dict('.'(:(valid,@(true)),[]))),[])))))))),'.'(:(valid,@(true)),[])))),'.'(:(system_id,dict('.'(:(valid,@(false)),'.'(:(error,'256文字以下の値を入力してください'),[])))),'.'(:(qux_status,dict('.'(:(valid,@(true)),[]))),[])))))))))))))),'.'(:(spec,dict('.'(:(type,model),'.'(:(model,qux_config),[])))),[])))),
	write(Result).
$ tpl leak.pl -g test,halt
malloc(): invalid size (unsorted)
Aborted

Writing with ignore_ops(true) seems to avoid it, so my guess would be it has to do with space insertion for the @ operator.

Valgrind log

==2116== Memcheck, a memory error detector
==2116== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2116== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==2116== Command: tpl leak.pl -g test,halt
==2116==
==2116== Invalid write of size 2
==2116==    at 0x4842B33: memmove (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1AA12D: plain (print.c:232)
==2116==    by 0x1AEF4F: print_term_to_buf (print.c:844)
==2116==    by 0x1B1344: print_term_to_buf (print.c:1033)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==  Address 0x59f1930 is 1,200 bytes inside a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4842B63: memmove (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1AA12D: plain (print.c:232)
==2116==    by 0x1AEF4F: print_term_to_buf (print.c:844)
==2116==    by 0x1B1344: print_term_to_buf (print.c:1033)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==  Address 0x59f1932 is 1 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x1AA13B: plain (print.c:233)
==2116==    by 0x1AEF4F: print_term_to_buf (print.c:844)
==2116==    by 0x1B1344: print_term_to_buf (print.c:1033)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==  Address 0x59f1933 is 2 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4E05F81: __vsnprintf_internal (vsnprintf.c:112)
==2116==    by 0x4DDBDF5: snprintf (snprintf.c:31)
==2116==    by 0x1AEFD2: print_term_to_buf (print.c:848)
==2116==    by 0x1B1344: print_term_to_buf (print.c:1033)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==  Address 0x59f1933 is 2 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4E05FAB: __vsnprintf_internal (vsnprintf.c:117)
==2116==    by 0x4DDBDF5: snprintf (snprintf.c:31)
==2116==    by 0x1AEFD2: print_term_to_buf (print.c:848)
==2116==    by 0x1B1344: print_term_to_buf (print.c:1033)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==  Address 0x59f1933 is 2 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4842B63: memmove (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1AA12D: plain (print.c:232)
==2116==    by 0x1B19DE: print_term_to_buf (print.c:1084)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==  Address 0x59f1933 is 2 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x1AA13B: plain (print.c:233)
==2116==    by 0x1B19DE: print_term_to_buf (print.c:1084)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==  Address 0x59f1934 is 3 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4E05F81: __vsnprintf_internal (vsnprintf.c:112)
==2116==    by 0x4DDBDF5: snprintf (snprintf.c:31)
==2116==    by 0x1AEB60: print_term_to_buf (print.c:806)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==  Address 0x59f1934 is 3 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4E05FAB: __vsnprintf_internal (vsnprintf.c:117)
==2116==    by 0x4DDBDF5: snprintf (snprintf.c:31)
==2116==    by 0x1AEB60: print_term_to_buf (print.c:806)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==  Address 0x59f1934 is 3 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 2
==2116==    at 0x4842B33: memmove (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1AA12D: plain (print.c:232)
==2116==    by 0x1AEF4F: print_term_to_buf (print.c:844)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==  Address 0x59f1934 is 3 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x1AA13B: plain (print.c:233)
==2116==    by 0x1AEF4F: print_term_to_buf (print.c:844)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==  Address 0x59f193e is 13 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4E05F81: __vsnprintf_internal (vsnprintf.c:112)
==2116==    by 0x4DDBDF5: snprintf (snprintf.c:31)
==2116==    by 0x1AEFD2: print_term_to_buf (print.c:848)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==  Address 0x59f193e is 13 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4E05FAB: __vsnprintf_internal (vsnprintf.c:117)
==2116==    by 0x4DDBDF5: snprintf (snprintf.c:31)
==2116==    by 0x1AEFD2: print_term_to_buf (print.c:848)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==  Address 0x59f193e is 13 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4E05F81: __vsnprintf_internal (vsnprintf.c:112)
==2116==    by 0x4DDBDF5: snprintf (snprintf.c:31)
==2116==    by 0x1AC98D: print_iso_list (print.c:586)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==  Address 0x59f193e is 13 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4E0C134: _IO_default_xsputn (genops.c:394)
==2116==    by 0x4E0C134: _IO_default_xsputn (genops.c:370)
==2116==    by 0x4DF10FB: __vfprintf_internal (vfprintf-internal.c:1688)
==2116==    by 0x4E05F99: __vsnprintf_internal (vsnprintf.c:114)
==2116==    by 0x4DDBDF5: snprintf (snprintf.c:31)
==2116==    by 0x1AC98D: print_iso_list (print.c:586)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==  Address 0x59f193e is 13 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4E05FAB: __vsnprintf_internal (vsnprintf.c:117)
==2116==    by 0x4DDBDF5: snprintf (snprintf.c:31)
==2116==    by 0x1AC98D: print_iso_list (print.c:586)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==  Address 0x59f193f is 14 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4E05F81: __vsnprintf_internal (vsnprintf.c:112)
==2116==    by 0x4DDBDF5: snprintf (snprintf.c:31)
==2116==    by 0x1AF5E0: print_term_to_buf (print.c:909)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==  Address 0x59f193f is 14 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4E05FAB: __vsnprintf_internal (vsnprintf.c:117)
==2116==    by 0x4DDBDF5: snprintf (snprintf.c:31)
==2116==    by 0x1AF5E0: print_term_to_buf (print.c:909)
==2116==    by 0x1B21D8: print_term_to_buf (print.c:1127)
==2116==    by 0x1ABDA3: print_iso_list (print.c:520)
==2116==    by 0x1AE067: print_term_to_buf (print.c:741)
==2116==    by 0x1AF47E: print_term_to_buf (print.c:895)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==  Address 0x59f1940 is 15 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4E05F81: __vsnprintf_internal (vsnprintf.c:112)
==2116==    by 0x4DDBDF5: snprintf (snprintf.c:31)
==2116==    by 0x1AF5E0: print_term_to_buf (print.c:909)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==  Address 0x59f1941 is 16 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
==2116== Invalid write of size 1
==2116==    at 0x4E05FAB: __vsnprintf_internal (vsnprintf.c:117)
==2116==    by 0x4DDBDF5: snprintf (snprintf.c:31)
==2116==    by 0x1AF5E0: print_term_to_buf (print.c:909)
==2116==    by 0x1B2EFE: print_term_to_stream (print.c:1317)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==  Address 0x59f1942 is 17 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
dict([valid: @true,fields:dict([revision:dict([valid: @true,ignored: @true]),creator:dict([valid: @true,ignored: @true]),updated_at:dict([valid: @true,ignored: @true]),created_at:dict([valid: @true,ignored: @true]),qux_abbreviation:dict([valid: @true]),qux_name:dict([valid: @false,error:256文字以下の値を入力してください]),baz_id:dict([valid: @false,error:256文字以下の値を入力してください]),owner_id:dict([valid: @true,ignored: @true]),qux_type_code:dict([valid: @true]),foo:dict([fields:dict([foo/bar_groups:dict([valid: @true]),foo/acme_notification_url:dict([valid: @false,error:「https://」で始まらなければなりません]),foo/qux_api_version:dict([valid: @true]),foo/xyz_unit_id:dict([valid: @false,error:256文字以下 の値を入力してください]),foo/abc_label:dict([fields:dict([foo/abc_label/print_abc_list:dict([valid: @true]),foo/abc_label/report_id:dict([valid: @true]),foo/abc_label/report_setting_key:dict([valid: @true])]),valid: @true]),foo/xyz_notes:dict([v==2116== Invalid read of size 1
==2116==    at 0x4E0C01E: _IO_default_xsputn (genops.c:399)
==2116==    by 0x4E0C01E: _IO_default_xsputn (genops.c:370)
==2116==    by 0x4E096F9: _IO_new_file_xsputn (fileops.c:1265)
==2116==    by 0x4E096F9: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1197)
==2116==    by 0x4DFD3C0: fwrite (iofwrite.c:39)
==2116==    by 0x15286D: net_write (network.c:330)
==2116==    by 0x1B2F27: print_term_to_stream (print.c:1321)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==  Address 0x59f1931 is 0 bytes after a block of size 1,201 alloc'd
==2116==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x1B2E8C: print_term_to_stream (print.c:1313)
==2116==    by 0x1D4E88: fn_iso_write_1 (streams.c:1998)
==2116==    by 0x1BC369: start (query.c:1749)
==2116==    by 0x1BCB84: execute (query.c:1949)
==2116==    by 0x1654CA: run (parser.c:3615)
==2116==    by 0x1B393B: pl_eval (prolog.c:122)
==2116==    by 0x1164F2: main (tpl.c:274)
==2116==
alid: @true])]),valid: @true]),system_id:dict([valid: @false,error:256文字以下の値を入力してください]),qux_status:dict([valid: @true])]),spec:dict([type:model,model:qux_config])])==2116==
==2116== HEAP SUMMARY:
==2116==     in use at exit: 11,526 bytes in 68 blocks
==2116==   total heap usage: 5,701 allocs, 5,633 frees, 56,428,199 bytes allocated
==2116==
==2116== 3,299 (2,960 direct, 339 indirect) bytes in 10 blocks are definitely lost in loss record 3 of 4
==2116==    at 0x483DD99: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x154DC9: directives (parser.c:397)
==2116==    by 0x161B9A: process_term (parser.c:2887)
==2116==    by 0x16283E: tokenize (parser.c:3046)
==2116==    by 0x14FF6A: load_text (module.c:1276)
==2116==    by 0x156E29: directives (parser.c:678)
==2116==    by 0x161B9A: process_term (parser.c:2887)
==2116==    by 0x16283E: tokenize (parser.c:3046)
==2116==    by 0x14FF6A: load_text (module.c:1276)
==2116==    by 0x156E29: directives (parser.c:678)
==2116==    by 0x161B9A: process_term (parser.c:2887)
==2116==    by 0x16283E: tokenize (parser.c:3046)
==2116==
==2116== 8,227 (7,104 direct, 1,123 indirect) bytes in 24 blocks are definitely lost in loss record 4 of 4
==2116==    at 0x483DD99: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2116==    by 0x154DC9: directives (parser.c:397)
==2116==    by 0x161B9A: process_term (parser.c:2887)
==2116==    by 0x16283E: tokenize (parser.c:3046)
==2116==    by 0x14FF6A: load_text (module.c:1276)
==2116==    by 0x1B553E: pl_create (prolog.c:533)
==2116==    by 0x115C14: main (tpl.c:165)
==2116==
==2116== LEAK SUMMARY:
==2116==    definitely lost: 10,064 bytes in 34 blocks
==2116==    indirectly lost: 1,462 bytes in 34 blocks
==2116==      possibly lost: 0 bytes in 0 blocks
==2116==    still reachable: 0 bytes in 0 blocks
==2116==         suppressed: 0 bytes in 0 blocks
==2116==
==2116== For lists of detected and suppressed errors, rerun with: -s
==2116== ERROR SUMMARY: 54 errors from 23 contexts (suppressed: 0 from 0)

Trailing command line options are unexpectedly processed

For example, we get:

$ touch test.pl
$ ./tpl test.pl -g halt
$ 

However, the only way to make the system interpret the -g command line option in this way should be if it is specified before any source files, such as:

$ ./tpl -g halt test.pl 

The reason is that command line options that come after the source file(s) should not be processed by the interpreter itself, but rather passed in some way to the program so that the program (in this case: test.pl) can process them in any way it wants.

I noticed this issue due to #34, which uses:

$ tpl samples/http_server.pl -g main

This should not cause Trealla to actually run main. It should run main only if this is specified as:

$ tpl -g main samples/http_server.pl 

O(retract/1) is O(N^2) in simple test case

Keep up the good work and fix this issue:

stefankral@Stefans-MacBook-Air trealla-main % ./tpl
?- time((between(1,100000,X),assertz(f(X)),false;true)).
Time elapsed 0.125s
   true.
?- time((retract(f(X)),false;true)).
Time elapsed 1.81s
   true.
?- time((between(1,200000,X),assertz(f(X)),false;true)).
Time elapsed 0.217s
   true.
?- time((retract(f(X)),false;true)).
Time elapsed 7.12s
   true.
?- time((between(1,400000,X),assertz(f(X)),false;true)).
Time elapsed 0.395s
   true.
?- time((retract(f(X)),false;true)).
Time elapsed 28.4s
   true.

Segmentation fault (core dumped)

v2.3.11

?- [user].
add3(A,B,C,D) :- D is A+B+C.

?- add3(1,2,3,X).
   X = 6.

?- add3(1+(2;3),4,5,X).
Segmentation fault (core dumped)

Undefined posix_spawn_file_actions_addchdir_np

While trying to build this project on Ubuntu 18.04 64bits I'm getting this error:

make
...
src/streams.c: In function ‘fn_process_create_3’:
src/streams.c:793:5: warning: implicit declaration of function ‘posix_spawn_file_actions_addchdir_np’; did you mean ‘posix_spawn_file_actions_adddup2’? [-Wimplicit-function-declaration]
  793 |     posix_spawn_file_actions_addchdir_np(&file_actions, cwd);
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |     posix_spawn_file_actions_adddup2
...
src/streams.o: In function `fn_process_create_3':
streams.c:(.text+0x183d4): undefined reference to `posix_spawn_file_actions_addchdir_np'
collect2: error: ld returned 1 exit status
Makefile:114: recipe for target 'tpl' failed
make: *** [tpl] Error 1
...

call_nth/2 leaves a spurious choice point

I just tried to verify this issue:
#23

And I see a spurious choice point:

?- main.
"Every strike brings me closer to the next home run."-"Babe Ruth"
   true
;  ... .

But there is not much in main/0 that would leave a choice point, or does it?
Maybe call_nth/2 is the problem?

not precise enough float printing

Currently I get:

$ ./tpl -v
Trealla Prolog (c) Infradig 2020-2022, v2.2.10
$ ./tpl
?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890.
   N9 = 3.0.
?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890 - 3.
   N9 = 4.440892098500626e-16.
?-

Because the number is not 3.0, I expect something else than 3.0 is printed.

Like here:

/* Jekejeke Prolog 1.5.4 */
?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890.
N9 = 3.0000000000000004.

?- N9 is 370370367037037036703703703670 / 123456789012345678901234567890 - 3.
N9 = 4.440892098500626E-16.

core benchmark gives segmentation fault

Hi,

Was doing some regression testing:

$ ../tpl -v
Trealla Prolog (c) Infradig 2020-2022, v2.1.11
$ ../tpl
?- ['suite.p'].
   true.
?- suite, nl, suite.
nrev Time elapsed 0.716s
crypt Time elapsed 0.611s
deriv Time elapsed 0.708s
poly Time elapsed 0.607s
sortq Time elapsed 0.825s
tictac Time elapsed 0.786s
queens Time elapsed 0.769s
query Time elapsed 1.52s
mtak Segmentation fault

CLP(Z) - all_distinct/1

Really just a progress note to self here:

~/trealla (devel) $ tpl ~/scryer-prolog/src/lib/clpz.pl
?- all_different([A,B]).
   clpz:all_different([A,B]),clpz:all_different([A,B]).
?- all_different([A,B]), A=1, B=1.
   false.

Ok, that's good.

?- all_distinct([A,B]).
   true.

Oops, not good, where are the residuals?

?- all_distinct([A,B]), A=1, B=1.
   A = 1, B = A.

They are really not there it seems. But freezing them first then they now appear...

?- freeze(A,true),freeze(B,true),all_distinct([A,B]).
   clpz:all_distinct([A,B]),freeze:freeze(A,true),clpz:all_distinct([A,B]),freeze:freeze(B,true).
?- freeze(A,true),freeze(B,true),all_distinct([A,B]),A=1,B=1.
   false.
?- freeze(A,true),freeze(B,true),all_distinct([A,B]),A=1,B=2.
   A = 1, B = 2.

Yes, that works. Very odd!

Trealla in the browser

Hello, I finally got around to getting Trealla running in the browser. Repo is here: https://github.com/guregu/trealla-js
Quick and dirty demo here: https://php.energy/trealla.html

It has the same flaw as the Go+WASM port in that it can't finely control the toplevel so it starts the interpreter fresh for each query and findalls everything. I was hoping this JS WASI library would give us better stdin control and use that, but alas it does not. I think I'll have to end up exporting "tell me the query results" functions from the native hosts to WASM and call that from Trealla somehow.

Anyway, just wanted to post this here because it's pretty cool. The discussions section is missing so I made this an issue. There are no particular problems with Trealla, just need to figure out a way to wrangle I/O in WASM better.

Missing rounding in float/1 for bigint argument

I get these results (note the 119042423827613008 is wrong):

/* Trealla Prolog 2.2.10 */
?- X is 51^10, Z is float(X).
   X = 119042423827613008, Z = 1.19042423827613e+17.
?- Z is float(51^10).
   Z = 1.19042423827613e+17.

But one would rather expect that float/1 performs HALF_EVEN rounding.
The results should then be (1.1904242382761301E17 instead of 1.19042423827613e+17):

?- X is 51^10, Z is float(X).
X = 119042423827613001, Z = 1.1904242382761301E17.
?- Z is float(51^10).
Z = 1.1904242382761301E17.

That the (^)/2 result is wrong, is rather irrelevant, it is also:

?- Z is float(119042423827613008).
Z = 1.1904242382761301E17.

Reading multiple terms from a string

Is it possible to read multiple terms from a string? read_term_from_chars/3 only returns one.
Maybe it could backtrack and return multiple terms.
Or maybe position(Start,End) could work for strings too?

% maybe like this?
?- read_term_from_chars(X, [], "foo. bar.").
   X = foo
;  X = bar.
% or this?
?- read_term_from_chars(X, [position(Start,End)], "foo. bar.").
   X = foo, Start = 0, End = 3.

Use-case: trying to execute Prolog code inside webpage templates. Currently look for . in a DCG but that breaks when a float has a dot in it.

CLP(Z) - top-level first time failure

Note to self:

?- ~/trealla (devel) $ tpl ~/scryer-prolog/src/lib/clpz.pl f1.pl
?- X #> 3, X #< 20.
   error(type_error(integer,clpz_attr(no,no,no,from_to(n(4),sup),fd_props([],[],[]),queue(_579,_580,_581,_582))),unknown(clpz_attr(no,no,no,from_to(n(4),sup),fd_props([],[],[]),queue(_579,_580,_581,_582)))-1).
?- X #> 3, X #< 20.
   clpz:(X in 4..19).
?- X #> 3, X #< 20.
   clpz:(X in 4..19).

findall/3 builtin serious accident

I'm sorry, tried but did not find the issue in a different way.

v2.0.1-2-g8cbe

#
# Simple Fraud Detection 
#
# Short Docu:
#
# Outside there exist a sqlite3 database containing 4 tables
#
# 3 Values(columns) are recorded: Name, Months(1,2,3), Score.
# Entries allowed: One Score Value per Person and Month.
# Table 'data' contains manipulated figures(impermissible entries)

# SQL:
# Strictly prohibited: Conventional SQL
# Only Advanced Single Statement Solutions are allowed.
# Reduces to efficient one pass table scan.
# No transaction control(Commit/Rollback logic) needed.
# The (advanced) SQL-Programmer has to be an advanced C-Programmer, too.
# In some circumstances the single statement of a business processing
# solution demands enhancement of sqlite3 (to write in C).

# The example uses sql to transform tables back and forth
# table data -> pivoting -> table result
# table result -> folding -> table atad
#
# Folding is the inverse of pivoting and
# table 'atad' should have the same content as table 'data'
#
# PROLOG:
# As Trealla does collect 'select * from ..' into a list of lists
# 'atad' and 'data' are checked:
# A member of atad must be a member of 'data'.
# If not, something is suspect.
#
#
# Save this all as a Shell-Script(copy/paste) test.sh
# Make sure 'tpl' is invocable.
# Run: sh test.sh

rm -f test.db test.db-wal test.db-shm test.pl


sqlite3 test.db <<-"EOFDB"
create table data(name varchar(255), month int, score int);
insert into data(name,month,score) values('Jack', 1, 5500);
insert into data(name,month,score) values('Jones', 1, 5000);
insert into data(name,month,score) values('Jack', 2, 6500);
insert into data(name,month,score) values('Jones', 2, 4500);
insert into data(name,month,score) values('Jack', 3, 6000);
insert into data(name,month,score) values('Jones', 3, 4750);

/* These are valid but impermissible entries */
insert into data(name,month,score) values('Jack', 1, -500);
insert into data(name,month,score) values('Jones', 1, 500);
insert into data(name,month,score) values('Jack', 2, -2000);
insert into data(name,month,score) values('Jones', 2, 2000);
insert into data(name,month,score) values('Jack', 3, -1250);
insert into data(name,month,score) values('Jones', 3, 1250);

create table months(month int);
insert into months(month) values(1);
insert into months(month) values(2);
insert into months(month) values(3);

/* Used for pivoting */
create table result(name varchar(255), jan int, feb int, mar int);

/* Used for folding, with helper table months */
create table atad(name varchar(255), month int, score int);

EOFDB

cat <<-"EOFPL" > test.pl
:- use_module(library(sqlite3)).

run :-
        test('./test.db').

test(Database) :-
        flag('SQLITE_OK', SQLITE_OK),
        sqlite3_open(Database, Connection, Ret_open),
        Ret_open =:= SQLITE_OK,

        QueryData = 'select * from data',
        findall(Row1, sqlite3_query(Connection, QueryData, Row1, _), Data),
        nl, write('Data:   '), writeq(Data), nl,

        PrepResult = 'delete from result;vacuum',
        sqlite3_exec(Connection,PrepResult, 0, 0, _, Ret_prep),
        Ret_prep =:= SQLITE_OK,

        GenResult = '\
insert into result(name,jan,feb,mar) \
select \
name,  \
SUM(score*(1-abs(sign(month-1)))), \
SUM(score*(1-abs(sign(month-2)))), \
SUM(score*(1-abs(sign(month-3))))  \
from data group by name',

        sqlite3_exec(Connection,GenResult, 0, 0, _, Ret_gen),
        Ret_gen =:= SQLITE_OK,
        QueryResult = 'select * from result',
        findall(Row2, sqlite3_query(Connection, QueryResult, Row2, _), Result),
        write('Result: '), writeq(Result), nl,

        QueryMonths = 'select * from months',
        findall(Row3, sqlite3_query(Connection, QueryMonths, Row3, _), Months),
        write('Months: '), writeq(Months), nl,

        PrepAtad = 'delete from atad;vacuum',
        sqlite3_exec(Connection,PrepAtad, 0, 0, _, Ret_atad),
        Ret_atad =:= SQLITE_OK,
        
	GenAtad = '\
insert into atad(name,month,score) \
select \
result.name, \
1*(1-abs(sign(months.month-1)))+ \
2*(1-abs(sign(months.month-2)))+ \
3*(1-abs(sign(months.month-3))),\
result.jan*(1-abs(sign(months.month-1)))+ \
result.feb*(1-abs(sign(months.month-2)))+ \
result.mar*(1-abs(sign(months.month-3))) \
from result cross join months',

        sqlite3_exec(Connection,GenAtad, 0, 0, _, Ret_genatad),
        Ret_genatad =:= SQLITE_OK,

        QueryAtad = 'select * from atad',
        findall(Row4, sqlite3_query(Connection, QueryAtad, Row4, _), Atad),
        write('Atad:   '), writeq(Atad), nl, nl,

        !, /* SQL done*/

	member(X,Atad), \+member(X,Data),

	[A,B,_] = X, /* get Name,Month and findall Fraud in Data (Var C) */

	findall([C,A,B],member([A,B,C],Data),Fraud),

        /* These findall(s) show a kind of Information-Hopping, Content-Jumping */
        /* Please edit findall above, just change the first parameter and run again (sh test.sh)
	/*findall([C,B,_], ..., ... Problem! */
	/*findall([A,_,_], ..., ... Problem! */

        write('Fraud detected: '), writeq(X/Fraud), nl,
       
        fail.

EOFPL

tpl -g run ./test.pl

# Output SQL processing, No problem!
#
# Data:   [['Jack',1,5500],['Jones',1,5000],['Jack',2,6500],['Jones',2,4500],['Jack',3,6000],['Jones',3,4750],['Jack',1,-500],['Jones',1,500],['Jack',2,-2000],['Jones',2,2000],['Jack',3,-1250],['Jones',3,1250]]
# Result: [['Jack',5000,4500,4750],['Jones',5500,6500,6000]]
# Months: [[1],[2],[3]]
# Atad:   [['Jack',1,5000],['Jack',2,4500],['Jack',3,4750],['Jones',1,5500],['Jones',2,6500],['Jones',3,6000]]

# Output Prolog processing
#
# This findall generates the correct answer!
#
# findall([C,A,B],member([A,B,C],Data),Fraud),
#
# Fraud detected: ['Jack',1,5000]/[[5500,'Jack',1],[-500,'Jack',1]]
# Fraud detected: ['Jack',2,4500]/[[6500,'Jack',2],[-2000,'Jack',2]]
# Fraud detected: ['Jack',3,4750]/[[6000,'Jack',3],[-1250,'Jack',3]]
# Fraud detected: ['Jones',1,5500]/[[5000,'Jones',1],[500,'Jones',1]]
# Fraud detected: ['Jones',2,6500]/[[4500,'Jones',2],[2000,'Jones',2]]
# Fraud detected: ['Jones',3,6000]/[[4750,'Jones',3],[1250,'Jones',3]]

# Perhaps Jones has manipulated the recording, Jack/Jones scores are swapped.

# Figures displayed nicely(...,5000]/[[5500, ...)
# ['Jack' ,1,5000]/[[5500,'Jack',1],[-500,'Jack',1]]
# ['Jones',1,5500]/[[5000,'Jones',1],[500,'Jones',1]]


# Now a report must be passed on (anonymized somehow):

# These findall(s) have a problem

# findall([C,B,_],member([A,B,C],Data),Fraud)
#
# Fraud detected: ['Jack',1,5000]/[[5500,1,'insert into atad(name,month,score) select result.name, 1*(1-abs(sign(months.month-1)))+ 2*(1-abs(sign(months.month-2)))+ 3*(1-abs(sign(months.month-3))),result.jan*(1-abs(sign(months.month-1)))+ result.feb*(1-abs(sign(months.month-2)))+ result.mar*(1-abs(sign(months.month-3))) from result cross join months'],[-500,1,'insert into atad(name,month,score) select result.name, 1*(1-abs(sign(months.month-1)))+ 2*(1-abs(sign(months.month-2)))+ 3*(1-abs(sign(months.month-3))),result.jan*(1-abs(sign(months.month-1)))+ result.feb*(1-abs(sign(months.month-2)))+ result.mar*(1-abs(sign(months.month-3))) from result cross join months']]
# 
# As you can see, SQL Statements are in the list
#
# Expecting: ['Jack',1,5000]/[[5500,1,_111],[-500,1,_112]]


# findall([A,_,_],member([A,B,C],Data),Fraud)
#
# Fraud detected: ['Jack',1,5000]/[['Jack',0,'select * from atad'],['Jack',0,'select * from atad']]
# Fraud detected: ['Jack',2,4500]/[['Jack',0,'select * from atad'],['Jack',0,'select * from atad']]
#
# Expecting:      ['Jack',1,5000]/[['Jack',_111,_112],['Jack',_113,_114]]

# Use bagof/3 instead of findall/3 to see expected results
# bagof([C,B,_],A^member([A,B,C],Data),Fraud)
# bagof([A,_,_],B^C^member([A,B,C],Data),Fraud)

bagof/3 setof/3 findall/3 syntax analysis

bagof/3, setof/3

GNU
| ?- B^bagof(Y,(between(1,3,B)),X).
uncaught exception: error(existence_error(procedure,(^)/2),top_level/0)

Swipl
?- B^bagof(Y,(between(1,3,B)),X).
ERROR: Unknown procedure: (^)/2
ERROR:   ^/2 can only appear as the 2nd argument of setof/3 and bagof/3


Trealla
?- B^bagof(Y,(between(1,3,B)),X).
   B = 1, X = [_A]
;  ... .
?-

findall/3

| ?- findall(Y,B^(between(1,3,B)),X).
uncaught exception: error(existence_error(procedure,(^)/2),findall/3)

?- findall(Y,B^(between(1,3,B)),X).
ERROR: Unknown procedure: (^)/2
ERROR:   ^/2 can only appear as the 2nd argument of setof/3 and bagof/3

?- findall(Y,B^(between(1,3,B)),X).
   X = [_A,_B,_C].
?-

Logtalk port of FCube exposes a parsing bug

Trying to load the Logtalk port of FCube using the main branch (520add9) exposes a parsing bug:

?- {fcube(loader)}.
...
!     Syntax error: operator_expected
!       in term
!       in file /Users/pmoura/logtalk/ports/fcube/fcube.lgt at or above line 1559
...

The clause at that position is:

pruning(swff(t, (X & Y)), PRUNED) :-
	/*calcola il contesto congiuntivo della
	formula	*/ ccTAnd((X & Y), CCX), !,
		list_to_set(CCX, CCXSET),
		pruningSetSwff(CCXSET, CCXSetPruned),
		simplification(CCXSetPruned, CCXSetPruned, RES), !,
		list_to_set(RES, SET),
	/*
	La formula di input e' di segno T,
	quindi in SET ci devono essere solo
	sottoformule con segno T
	*/ fromSetTotSwff(SET, H),
		/*esegue semplificazioni booleane*/ valSWFF(H, PRUNED).

findall/3 perf test

$ cat isl.pl

isl({L},N) :- L = (_ is _+N).

test1 :- time(findall(Z,(between(1,5,N), isl({X}, N),
                         X = (Y is B+_), findall(Y,(between(1,3,B),X),Z)),
	              M)), write(M).

test2 :- time(findall(Z,(between(1,5_000_000,N), isl({X}, N),
                         X = (Y is B+_), findall(Y,(between(1,3,B),X),Z)),
	              M)), length(M,L), L1 is L*3, write(L1), write(' Elements processed').
$ tpl

?- "isl".
   true.

?- nl,test1,nl,nl,test2.

Time elapsed 9.2e-05s
[[2,3,4],[3,4,5],[4,5,6],[5,6,7],[6,7,8]]   true.

Time elapsed 26.9s
15000000 Elements processed   true.
?-

Is this adequate? I've no idea.

Wrong result in (^)/2 bigint computation

According to Corrigendum 2 I guess the idea of the evaluable predicate
(^)/2 is to be exact for bigint arguments. But I find for example:

$ ./tpl -v
Trealla Prolog (c) Infradig 2020-2022, v2.2.10
$ ./tpl
?- X is 51^10, Y is 51*51*51*51*51*51*51*51*51*51.
   X = 119042423827613008, Y = 119042423827613001.

Two different results! :-(

API for controlling queries

I'm working on the last major feature for the WASM ports: returning one query result at a time instead of findall'ing the query.
Ideally we could just use stdin and wait for some input to continue the query, but unfortunately the majority of WASM runtimes don't implement stdin as a stream, so this approach doesn't work. I was considering doing something clever with history_getch as an exported host function but I think that'll just lead it to block forever in the browser.

Is there a way to control the redo process for queries with the current API?

I am imagining an API kind of like this:

// works like pl_eval, except returns a handle to the query instead of waiting for toplevel input
*query pl_query(*prolog, const char *expr);
// redo a given query, returning false if there are no more choice points
bool pl_redo(*prolog, *query);

And the WASM host would call it like so:

class Prolog {
    ptr; // *prolog ptr

    // example: javascript generator function that returns one result at a tinme
    *query(expr) {
        // eval once and grab ptr to query handle
        const query = this.pl_query(this.ptr, expr);
        do {
            const answer = /* read from stdout */;
            yield answer;
        } while (this.pl_redo(this.ptr, query))
    }
}

// usage
const query = pl.query("member(X, [1,2,3]).");
for (const answer of query) {
    console.log(answer) // X=1, then X=2 next iteration, etc
}

Ideally these would be silent, not dumping vars or printing prompts so it's easy to parse the output. We can use Prolog code to marshal the answers to an easy-to-parse format, so we don't need a C results API, just a way to run redos.

Odd behavior on backtracking

Backtracking here seems to use the wrong variable substitutions (as mentioned here: #46 (comment)).
Here's the smallest way to reproduce it I could find.
See comment below for smaller version.

/* Old version of the WASM toplevel stripped down. */
:- module(js_toplevel, [js_ask/1]).

:- use_module(library(lists)).

js_ask(Input) :-
	read_term_from_chars(Query, [variable_names(Vars)], Input),
	query(Query, Vars, Status, Solution),
	write_result(Status, Solution),
	flush_output.

write_result(Status, Solution) :-
	write(Status), write(' '), write(Solution), nl.

query(Query, Vars, Status, Solution) :-
	(   catch(call(Query), Error, true)
	*-> OK = true
	;   OK = false
	),
	query_status(OK, Error, Status),
	(  nonvar(Error)
	-> Solution = Error
	;  Solution = Vars
	).

query_status(_OK, Error, error) :- nonvar(Error), !.
query_status(true, _, success).
query_status(false, _, failure).

person(socrates).
person(plato).
mortal(X) :- person(X).
?- use_module(js_toplevel).
   true.

% bug?
?- js_ask("mortal(X)").
success [X=socrates]
   true
;  false. % expected plato

% this is ok:
?- js_ask("person(X)").
success [X=socrates]
   true
; success [X=plato]
 true.

% this is ok:
?- js_ask("findall(X, mortal(X), Xs)").
success [X=_5,Xs=[socrates,plato]]
   true.

Trace shows odd backtracking:

?- trace, js_ask("mortal(X)").
[js_toplevel:0:f0:fp:1:cp0:sp0:hp0:tp0] EXIT trace
[js_toplevel:1:f0:fp:1:cp0:sp0:hp0:tp0] CALL js_ask("mortal(X)")
[js_toplevel:2:f1:fp:2:cp0:sp5:hp0:tp0] CALL read_term_from_chars(_0,[variable_names(_1)],"mortal(X)")
[js_toplevel:3:f1:fp:2:cp0:sp6:hp7:tp2] EXIT read_term_from_chars(mortal(_5),[variable_names(['X'=_5])],"mortal(X)")
[js_toplevel:4:f1:fp:2:cp0:sp6:hp7:tp2] CALL query(mortal(_5),['X'=_5],_3,_4)
[js_toplevel:5:f2:fp:3:cp1:sp12:hp18:tp0] CALL catch(call(mortal(_5)),_7,true)
[js_toplevel:6:f3:fp:4:cp1:sp15:hp18:tp0] CALL '$catch'(call(call(mortal(_5))),_7,call(true))
[js_toplevel:7:f3:fp:4:cp2:sp15:hp24:tp0] EXIT '$catch'(call(call(mortal(_5))),_7,call(true))
[js_toplevel:8:f3:fp:4:cp2:sp15:hp24:tp0] CALL call(call(mortal(_5)))
[js_toplevel:9:f3:fp:4:cp3:sp15:hp30:tp0] EXIT call(call(mortal(_5)))
[js_toplevel:10:f3:fp:4:cp3:sp15:hp30:tp0] CALL call(mortal(_5))
[js_toplevel:11:f3:fp:4:cp4:sp15:hp35:tp0] EXIT call(mortal(_5))
[js_toplevel:12:f3:fp:4:cp4:sp15:hp35:tp0] CALL mortal(_5)
[js_toplevel:13:f4:fp:5:cp4:sp16:hp35:tp0] CALL person(_5)
[js_toplevel:14:f5:fp:6:cp5:sp16:hp35:tp1] EXIT person(socrates)
[js_toplevel:15:f3:fp:5:cp5:sp16:hp35:tp1] CALL '$drop_barrier'
[js_toplevel:16:f3:fp:5:cp5:sp16:hp35:tp1] EXIT '$drop_barrier'
[js_toplevel:17:f3:fp:5:cp5:sp16:hp35:tp1] CALL '$drop_barrier'
[js_toplevel:18:f3:fp:5:cp5:sp16:hp35:tp1] EXIT '$drop_barrier'
[js_toplevel:19:f3:fp:5:cp5:sp16:hp35:tp1] CALL '$block_catcher'(1)
[js_toplevel:20:f3:fp:5:cp6:sp16:hp35:tp1] EXIT '$block_catcher'(1)
[js_toplevel:21:f3:fp:5:cp6:sp16:hp35:tp1] EXIT person(socrates)
[js_toplevel:22:f2:fp:5:cp6:sp16:hp35:tp1] CALL '$soft_cut'
[js_toplevel:23:f2:fp:5:cp6:sp16:hp35:tp1] EXIT '$soft_cut'
[js_toplevel:24:f2:fp:5:cp6:sp16:hp35:tp1] CALL _8=true
[js_toplevel:25:f2:fp:5:cp6:sp16:hp35:tp2] EXIT true=true
[js_toplevel:26:f2:fp:5:cp6:sp16:hp35:tp2] CALL query_status(true,_7,_3)
[js_toplevel:27:f5:fp:6:cp7:sp18:hp35:tp5] CALL nonvar(_7)
[js_toplevel:28:f5:fp:6:cp7:sp18:hp35:tp5] FAIL nonvar(_7)
[js_toplevel:29:f2:fp:5:cp6:sp16:hp35:tp2] REDO query_status(true,_7,_3)
[js_toplevel:30:f5:fp:6:cp6:sp17:hp35:tp4] EXIT query_status(true,_7,success)
[js_toplevel:31:f2:fp:4:cp7:sp15:hp43:tp4] CALL nonvar(_7)
[js_toplevel:32:f2:fp:4:cp7:sp15:hp43:tp4] FAIL nonvar(_7)
[js_toplevel:33:f2:fp:4:cp6:sp15:hp43:tp4] CALL _4=['X'=socrates]
[js_toplevel:34:f2:fp:4:cp6:sp15:hp43:tp5] EXIT ['X'=socrates]=['X'=socrates]
[js_toplevel:35:f2:fp:4:cp6:sp15:hp43:tp5] EXIT query_status(true,mortal(socrates),success)
[js_toplevel:36:f1:fp:4:cp6:sp15:hp43:tp5] CALL write_result(success,['X'=socrates])
[js_toplevel:37:f4:fp:5:cp6:sp17:hp43:tp5] CALL write(success)
success[js_toplevel:38:f4:fp:5:cp6:sp17:hp43:tp5] EXIT write(success)
[js_toplevel:39:f4:fp:5:cp6:sp17:hp43:tp5] CALL write(' ')
 [js_toplevel:40:f4:fp:5:cp6:sp17:hp43:tp5] EXIT write(' ')
[js_toplevel:41:f4:fp:5:cp6:sp17:hp43:tp5] CALL write(['X'=socrates])
[X=socrates][js_toplevel:42:f4:fp:5:cp6:sp17:hp43:tp5] EXIT write(['X'=socrates])
[js_toplevel:43:f4:fp:5:cp6:sp17:hp43:tp5] CALL nl

[js_toplevel:44:f4:fp:5:cp6:sp17:hp43:tp5] EXIT nl
[js_toplevel:45:f4:fp:5:cp6:sp17:hp43:tp5] EXIT write_result(success,['X'=socrates])
[js_toplevel:46:f1:fp:5:cp6:sp17:hp43:tp5] CALL flush_output
[js_toplevel:47:f1:fp:5:cp6:sp17:hp43:tp5] EXIT flush_output
[js_toplevel:48:f1:fp:5:cp6:sp17:hp43:tp5] EXIT write_result(mortal(socrates),['X'=socrates])
   true
; [js_toplevel:49:f3:fp:5:cp5:sp16:hp35:tp1] REDO '$block_catcher'(1)
[js_toplevel:50:f3:fp:5:cp5:sp16:hp35:tp1] FAIL '$block_catcher'(1)
[js_toplevel:51:f4:fp:5:cp4:sp16:hp35:tp0] REDO person(success) <---------------- why is it using 'success' here?
[js_toplevel:52:f4:fp:5:cp4:sp16:hp35:tp0] FAIL person(success)
[js_toplevel:53:f3:fp:4:cp3:sp15:hp35:tp0] REDO call(mortal(_5))
[js_toplevel:54:f3:fp:4:cp3:sp15:hp35:tp0] FAIL call(mortal(_5))
[js_toplevel:55:f3:fp:4:cp2:sp15:hp30:tp0] REDO call(call(mortal(_5)))
[js_toplevel:56:f3:fp:4:cp2:sp15:hp30:tp0] FAIL call(call(mortal(_5)))
[js_toplevel:57:f3:fp:4:cp1:sp15:hp24:tp0] REDO '$catch'(call(call(mortal(_5))),_7,call(true))
[js_toplevel:58:f3:fp:4:cp1:sp15:hp24:tp0] FAIL '$catch'(call(call(mortal(_5))),_7,call(true))
 false.

In particular step 51 looks suspect.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.