package nova;
import java.io.*;
import java.math.*;
import java.net.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.nio.file.*;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Warranty & Liability
* To the extent permitted by applicable law and unless explicitly
* otherwise agreed upon, XLOG Technologies AG makes no warranties
* regarding the provided information. XLOG Technologies AG assumes
* no liability that any problems might be solved with the information
* provided by XLOG Technologies AG.
*
* Rights & License
* All industrial property rights regarding the information - copyright
* and patent rights in particular - are the sole property of XLOG
* Technologies AG. If the company was not the originator of some
* excerpts, XLOG Technologies AG has at least obtained the right to
* reproduce, change and translate the information.
*
* Reproduction is restricted to the whole unaltered document. Reproduction
* of the information is only allowed for non-commercial uses. Selling,
* giving away or letting of the execution of the library is prohibited.
* The library can be distributed as part of your applications and libraries
* for execution provided this comment remains unchanged.
*
* Restrictions
* Only to be distributed with programs that add significant and primary
* functionality to the library. Not to be distributed with additional
* software intended to replace any components of the library.
*
* Trademarks
* Jekejeke is a registered trademark of XLOG Technologies AG.
*/
public final class runtime {
private static final int MAX_BUF = 4096;
public static String bootbase = "";
public static String codebase = System.getProperty("user.dir") + File.separatorChar;
/**
* Set the boot base.
*
* @param url The boot base.
*/
public static void set_bootbase(String url) {
bootbase = url;
}
/**
* Set the code base.
*
* @param url The code base.
*/
public static void set_codebase(String url) {
codebase = url;
}
/**
* Resolve a child path against a parent.
*
* @param parent The parent path.
* @param child The child path.
* @return The result.
*/
public static File findWrite(String parent, String child) {
File res = new File(child);
if (!res.isAbsolute())
res = new File(parent, child);
return res;
}
/**************************************************************/
/* current_output/1 and current_error/1 */
/**************************************************************/
public static final int MASK_DST_COLR = 0x00000004;
public static final int MASK_DST_CYCL = 0x00000008;
public static final int MASK_DST_FEAT = MASK_DST_COLR | MASK_DST_CYCL;
public static final int MASK_DST_LINE = 0x00000010;
public static final class Sink {
public StringBuilder buf;
public BiFunction<Object,String,Object> send;
public int last;
public int flags;
public Consumer notify;
public Consumer release;
public Object data;
public int indent;
public int offset;
/**
* Create a text output.
*/
public Sink() {
this.buf = new StringBuilder();
this.send = (fd, buf) -> fd;
this.last = -1;
this.flags = 0;
this.notify = (fd) -> { };
this.release = (fd) -> { };
this.data = Store.UNDEF_OBJ;
this.indent = 0;
this.offset = 0;
}
}
/**
* current_output(S): [ISO 8.11.2]
* The built-in succeeds in S with the current output.
*/
private static boolean test_current_output(Object[] args) {
Object alpha = Store.engine.text_output;
return Machine.exec_unify(args[0], alpha);
}
/**
* current_error(S):
* The built-in succeeds in S with the current error.
*/
private static boolean test_current_error(Object[] args) {
Object alpha = Store.engine.text_error;
return Machine.exec_unify(args[0], alpha);
}
/**************************************************************/
/* set_output/1 and set_error/1 */
/**************************************************************/
/**
* set_output(S): [ISO 8.11.4]
* The built-in succeeds. As a side effect the current output is set to S.
*/
private static boolean test_set_output(Object[] args) {
Object obj = Machine.exec_build(args[0]);
check_sink(obj);
Store.engine.text_output = (Sink) obj;
return true;
}
/**
* set_error(S):
* The built-in succeeds. As a side effect the current error is set to S.
*/
private static boolean test_set_error(Object[] args) {
Object obj = Machine.exec_build(args[0]);
check_sink(obj);
Store.engine.text_error = (Sink) obj;
return true;
}
/**
* Assure that the object is a sink.
*
* @param beta The object.
*/
public static void check_sink(Object beta) {
if (!(beta instanceof Sink)) {
Store.check_nonvar(beta);
beta = Store.copy_term(beta);
throw Machine.make_error(new Store.Compound("type_error",
new Object[]{"writer", beta}));
}
}
/**************************************************************/
/* put_atom/2 */
/**************************************************************/
/**
* put_atom(S, A):
* The built-in succeeds. As a side effect, it adds
* the atom to the stream S.
*/
private static boolean test_put_atom(Object[] args) {
Object stream = Machine.exec_build(args[0]);
check_sink(stream);
Object text = Machine.exec_build(args[1]);
special.check_atom(text);
put_atom((Sink) stream, (String) text);
return true;
}
public static void put_atom(Sink stream, String text) {
if (text.length() > 0) {
int pos = text.lastIndexOf('\n');
if (pos != -1) {
stream.offset = text.codePointCount(pos+1, text.length());
} else {
stream.offset += text.codePointCount(0, text.length());
}
stream.last = text.codePointBefore(text.length());
if (stream.buf != null) {
stream.buf.append(text);
if (stream.buf.length() >= MAX_BUF)
flush_buffer(stream);
} else {
stream.data = stream.send.apply(stream.data, text);
}
}
}
private static void flush_buffer(Sink stream) {
if (stream.buf != null && stream.buf.length() > 0) {
String text = stream.buf.toString();
stream.buf = new StringBuilder();
stream.data = stream.send.apply(stream.data, text);
}
}
/**************************************************************/
/* current_input/1 and set_input/1 */
/**************************************************************/
public static final int MASK_SRC_SKIP = 0x00000001;
public static final int MASK_SRC_AREAD = 0x00000002;
public static final class Source {
public String buf;
public int pos;
public Object receive;
public int flags;
public int lineno;
public Object release;
public Object data;
/**
* Create a text input.
*/
public Source() {
this.buf = "";
this.pos = 0;
this.receive = (Function) ((fd) -> "");
this.flags = 0;
this.lineno = 0;
this.release = (Consumer) ((fd) -> {});
this.data = Store.UNDEF_OBJ;
}
}
/**
* current_input(S): [ISO 8.11.1]
* The built-in succeeds in S with the current input.
*/
private static boolean test_current_input(Object[] args) {
Object alpha = Store.engine.text_input;
return Machine.exec_unify(args[0], alpha);
}
/**
* set_input(S): [ISO 8.11.3]
* The built-in succeeds. As a side effect it sets the current input to S.
*/
private static boolean test_set_input(Object[] args) {
Object obj = Machine.exec_build(args[0]);
check_source(obj);
Store.engine.text_input = (Source) obj;
return true;
}
/**
* Assure that the object is a source.
*
* @param beta The object.
*/
private static void check_source(Object beta) {
if (!(beta instanceof Source)) {
Store.check_nonvar(beta);
beta = Store.copy_term(beta);
throw Machine.make_error(new Store.Compound("type_error",
new Object[]{"reader", beta}));
}
}
/*******************************************************************/
/* os_get_code/2 and os_peek_code/2 */
/*******************************************************************/
/**
* os_get_code(S, C):
* The predicate succeeds in C with the Unicode point from the stream buffer S.
* As a side effect the stream position is advanced.
*/
private static boolean test_os_get_code(Object[] args) {
Source stream = (Source) Machine.exec_build(args[0]);
int pos = stream.pos;
String buf = stream.buf;
if (pos < buf.length()) {
int ch = buf.codePointAt(pos);
pos += Character.charCount(ch);
if (ch == 13 || (ch == 10 && (stream.flags & MASK_SRC_SKIP) == 0))
stream.lineno++;
if (ch == 13) {
stream.flags |= MASK_SRC_SKIP;
} else {
stream.flags &= ~MASK_SRC_SKIP;
}
stream.pos = pos;
return Machine.exec_unify(args[1], Integer.valueOf(ch));
} else {
return false;
}
}
/**
* os_peek_code(S, C):
* The built-in succeeds in C with the Unicode point from the stream buffer S.
*/
private static boolean test_os_peek_code(Object[] args) {
Source stream = (Source) Machine.exec_build(args[0]);
int pos = stream.pos;
String buf = stream.buf;
if (pos < buf.length()) {
int ch = buf.codePointAt(pos);
return Machine.exec_unify(args[1], Integer.valueOf(ch));
} else {
return false;
}
}
/****************************************************************/
/* os_open_promise_opts/4 */
/****************************************************************/
/**
* os_open_promise_opts(P, L, S, Q):
* The predicate succeeds in Q with a promise for open input S
* on path P and option list L.
*/
private static boolean test_os_open_promise(Object[] args) {
Object obj = Machine.exec_build(args[0]);
special.check_atom(obj);
String url = (String) obj;
Map opts = (Map) Machine.exec_build(args[1]);
Source stream = new Source();
if (!Machine.exec_unify(args[2], stream))
return false;
Object buf = Machine.ctx;
Handler.Promise prom = open_file_promise_opts(buf, stream, url, opts);
return Machine.exec_unify(args[3], prom);
}
public static Charset get_encoding(Map opts) {
if (opts != null && opts.get("encoding") != null) {
String encoding = opts.get("encoding").toString();
return Charset.forName(encoding);
} else {
return StandardCharsets.UTF_8;
}
}
private static Handler.Promise open_file_promise_opts(Object buf,
Source stream, String url, Map opts) {
return new Handler.Promise(() -> {
File file = findWrite(codebase, url);
try {
InputStream ins = new FileInputStream(file);
stream.data = new InputStreamReader(ins, get_encoding(opts));
stream.receive = (BiFunction<Object, runtime.Source, Handler.Promise>) runtime::file_read_promise;
stream.release = (BiFunction<Object, runtime.Source, Handler.Promise>) runtime::file_close_promise;
stream.flags |= MASK_SRC_AREAD;
} catch (IOException err) {
Machine.register_signal(buf, map_file_error(err, url));
}
});
}
public static Object map_file_error(Exception err, String url) {
if (err instanceof InterruptedException) {
return new Store.Compound("resource_error",
new Object[]{"interrupted_exception"});
} else if (err instanceof FileNotFoundException ||
err instanceof NoSuchFileException) {
return new Store.Compound("existence_error",
new Object[]{"source_sink", url});
} else if (err instanceof ConnectException) {
Throwable cause = err.getCause();
if (cause != null && cause.getCause() instanceof UnresolvedAddressException) {
return new Store.Compound("resource_error", new Object[]{"unknown_host"});
} else {
return new Store.Compound("resource_error", new Object[]{"connect_failed"});
}
} else {
return new Store.Compound("resource_error", new Object[]{"remote_error"});
}
}
/*******************************************************************/
/* os_read_promise/2 and os_close_promise/2 */
/*******************************************************************/
/**
* os_read_promise(S, P):
* The predicate suceeds in P with a read promise for a input S.
*/
private static boolean test_os_read_promise(Object[] args) {
Source stream = (Source) Machine.exec_build(args[0]);
Object buf = Machine.ctx;
return Machine.exec_unify(args[1], ((BiFunction) stream.receive).apply(buf, stream));
}
public static Handler.Promise file_read_promise(Object buf, Source stream) {
return new Handler.Promise(() -> {
try {
char[] cbuf = new char[8192];
int len = ((Reader)stream.data).read(cbuf, 0, cbuf.length);
stream.buf = (len > 0 ? new String(cbuf, 0, len) : "");
stream.pos = 0;
} catch (IOException err) {
Machine.register_signal(buf, map_stream_error(err));
}
});
}
/**
* Map a stream error when in transit.
*
* @param err The offending error.
* @return The Prolog error term.
*/
public static Object map_stream_error(Exception err) {
if (err instanceof SocketTimeoutException) {
return new Store.Compound("resource_error",
new Object[]{"socket_timeout"});
} else if (err instanceof BindException) {
return new Store.Compound("resource_error",
new Object[]{"port_error"});
} else if (err instanceof SocketException) {
return new Store.Compound("resource_error",
new Object[]{"remote_error"});
} else {
return new Store.Compound("resource_error",
new Object[]{"io_exception"});
}
}
/**
* os_close_promise(S, P):
* The predicate suceeds in P with a read promise for a input S.
*/
private static boolean test_os_close_promise(Object[] args) {
Source stream = (Source) Machine.exec_build(args[0]);
Object buf = Machine.ctx;
return Machine.exec_unify(args[1], ((BiFunction) stream.release).apply(buf, stream));
}
public static Handler.Promise file_close_promise(Object buf, Source stream) {
return new Handler.Promise(() -> {
try {
((Closeable) stream.data).close();
} catch (IOException err) {
Machine.register_signal(buf, map_stream_error(err));
}
});
}
/*******************************************************************/
/* os_prop_promise/3 */
/*******************************************************************/
/**
* os_prop_promise(F, M, Q):
* The predicate succeeds in Q with with a promise for the
* properties M of the file F. Barks if path F doesn't exist
* or io exception while resolving.
*/
private static boolean test_os_prop_promise(Object[] args) {
Object obj = Machine.exec_build(args[0]);
special.check_atom(obj);
String url = (String) obj;
Map res = new HashMap();
if (!Machine.exec_unify(args[1], res))
return false;
Object buf = Machine.ctx;
Handler.Promise prom = prop_file_promise(buf, url, res);
return Machine.exec_unify(args[2], prom);
}
private static Handler.Promise prop_file_promise(Object buf, String url, Map res) {
return new Handler.Promise(() -> {
File file = findWrite(codebase, url);
try {
if (!file.exists())
throw new NoSuchFileException("");
String ftype = (file.isFile() ? "regular" :
(file.isDirectory() ? "directory" : "other"));
res.put("last_modified", special.norm_smallint(file.lastModified()));
res.put("absolute_path", (ftype.equals("directory") ?
file.getPath() + File.separatorChar : file.getPath()));
res.put("type", ftype);
res.put("can_write", (file.canWrite() ? "true" : "false"));
} catch (IOException err) {
Machine.register_signal(buf, map_file_error(err, url));
}
});
}
/**************************************************************/
/* os_open_sync_opts/4 */
/**************************************************************/
/**
* os_open_sync_opts(P, M, L, S):
* The predicate succeeds. As a side effect the stream S is
* opened on the path P with the mode M and the option list L.
*/
private static boolean test_os_open_sync(Object[] args) {
Object obj = Machine.exec_build(args[0]);
special.check_atom(obj);
String url = (String) obj;
Object mode = Machine.exec_build(args[1]);
special.check_atom(mode);
Map opts = (Map) Machine.exec_build(args[2]);
Object stream;
if ("read".equals(mode)) {
throw Machine.make_error(new Store.Compound("resource_error",
new Object[]{"not_supported"}));
} else if ("write".equals(mode)) {
stream = open_write(url, false, opts);
} else if ("append".equals(mode)) {
stream = open_write(url, true, opts);
} else {
throw Machine.make_error(new Store.Compound("domain_error",
new Object[]{"io_mode", mode}));
}
return Machine.exec_unify(args[3], stream);
}
public static Object open_write(String url, boolean append, Map opts) {
try {
File file = findWrite(codebase, url);
OutputStream outs = new FileOutputStream(file, append);
Sink dst = new Sink();
dst.data = new OutputStreamWriter(outs, get_encoding(opts));
dst.send = runtime::file_write;
dst.release = runtime::file_close;
dst.notify = runtime::file_flush;
return dst;
} catch (IOException err) {
throw Machine.make_error(map_file_error(err, url));
}
}
public static Object file_write(Object data, String buf) {
try {
int len = buf.length();
int pos = 0;
char[] cbuf = null;
while (len > 0) {
int span = Math.min(len, 8192);
if (cbuf == null)
cbuf = new char[span];
buf.getChars(pos, span, cbuf, 0);
((Writer) data).write(cbuf, 0, span);
pos += span;
len -= span;
}
return data;
} catch (IOException err) {
throw Machine.make_error(map_stream_error(err));
}
}
public static void file_flush(Object data) {
try {
((Flushable) data).flush();
} catch (IOException err) {
throw Machine.make_error(map_stream_error(err));
}
}
public static void file_close(Object data) {
try {
((Closeable) data).close();
} catch (IOException err) {
throw Machine.make_error(map_stream_error(err));
}
}
/**************************************************************/
/* flush_output/1 and os_close_sync/1 */
/**************************************************************/
/**
* flush_output(S): [ISO 8.11.7]
* The built-in succeeds. As a side effect, it flushes the stream S buffer.
*/
private static boolean test_flush_output(Object[] args) {
Object stream = Machine.exec_build(args[0]);
check_sink(stream);
flush(stream);
return true;
}
public static void flush(Object obj) {
Sink stream = (Sink)obj;
flush_buffer(stream);
stream.notify.accept(stream.data);
}
/**
* close(S): [ISO 8.11.6]
* The built-in succeeds. As a side effect, the stream S is closed.
*/
private static boolean test_os_close_sync(Object[] args) {
Object stream = Machine.exec_build(args[0]);
if (stream instanceof Sink) {
/* */
} else {
check_source(stream);
}
close(stream);
return true;
}
public static void close(Object stream) {
if (stream instanceof Sink) {
flush_buffer((Sink) stream);
((Sink) stream).release.accept(((Sink) stream).data);
} else {
((Consumer)((Source) stream).release).accept(((Source) stream).data);
}
}
/*******************************************************************/
/* os_cntrl_sync/2 and os_read_sync/1 */
/*******************************************************************/
/**
* os_cntrl_sync(F, P):
* The predicate assigns the property P to the file F.
*/
private static boolean test_os_cntrl_sync(Object[] args) {
Object obj = Machine.exec_build(args[0]);
special.check_atom(obj);
String url = (String)obj;
File file = findWrite(codebase, url);
Object prop = Machine.exec_build(args[1]);
if (!file.exists())
throw Machine.make_error(new Store.Compound("existence_error",
new Object[]{"source_sink", url}));
if (Store.is_structure(prop) &&
((Store.Structure) prop).functor.equals("last_modified") &&
((Store.Structure) prop).args.length == 1) {
Object val = Store.deref(((Store.Structure) prop).args[0]);
special.check_integer(val);
if (eval.integer_signum((Number) val) < 0)
throw Machine.make_error(new Store.Compound("domain_error",
new Object[]{"not_less_than_zero", val}));
if (!is_long((Number) val))
throw Machine.make_error(new Store.Compound("representation_error",
new Object[]{"long", val}));
if (!file.setLastModified(((Number) val).longValue())) {
throw Machine.make_error(new Store.Compound("resource_error",
new Object[]{"io_exception"}));
}
} else if (Store.is_structure(prop) &&
((Store.Structure) prop).functor.equals("can_write") &&
((Store.Structure) prop).args.length == 1) {
Object val = Store.deref(((Store.Structure) prop).args[0]);
special.check_atom(val);
if (!"true".equals(val) && !"false".equals(val))
throw Machine.make_error(new Store.Compound("domain_error",
new Object[]{"boolean", val}));
if (!file.setWritable("true".equals(val))) {
throw Machine.make_error(new Store.Compound("resource_error",
new Object[]{"io_exception"}));
}
} else {
Store.check_nonvar(prop);
prop = Store.copy_term(prop);
throw Machine.make_error(new Store.Compound("domain_error",
new Object[]{"file_property", prop}));
}
return true;
}
private static final BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
private static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
public static boolean is_long(Number num) {
if (num instanceof Integer) {
return true;
} else {
return MIN_LONG.compareTo((BigInteger) num) <= 0 &&
((BigInteger) num).compareTo(MAX_LONG) <= 0;
}
}
/**
* os_read_sync(S):
* The predicate succeeds. As a side effect the stream buffer is read.
*/
private static boolean test_os_read_sync(Object[] args) {
Source stream = (Source) Machine.exec_build(args[0]);
stream.buf = ((Function<Object, String>) stream.receive).apply(stream.data);
stream.pos = 0;
return true;
}
/**************************************************************/
/* ir_site_new/2, ir_is_site/1 and ir_site_name/2 */
/**************************************************************/
/**
* ir_site_new(X, Y): internal only
* The built-in succeeds in Y with a cacheable X.
*/
private static boolean test_ir_site_new(Object[] args) {
Object alpha = Machine.exec_build(args[0]);
if (Store.is_structure(alpha)) {
if (Machine.is_atom(((Store.Structure) alpha).functor))
alpha = new Store.Compound(new Store.Cache((String)
((Store.Structure) alpha).functor),
((Store.Structure) alpha).args);
} else if (Machine.is_atom(alpha)) {
alpha = new Store.Cache((String) alpha);
}
return Machine.exec_unify(args[1], alpha);
}
/**
* ir_is_site(X): internal only
* The built-in succeeds if X is cacheable.
*/
private static boolean test_ir_is_site(Object[] args) {
Object alpha = Machine.exec_build(args[0]);
if (Store.is_structure(alpha))
return Store.is_cache(((Store.Structure) alpha).functor);
return Store.is_cache(alpha);
}
/**
* ir_site_name(X, Y): internal only
* The built-in succeeds in Y with a cachefree X.
*/
private static boolean test_ir_site_name(Object[] args) {
Object alpha = Machine.exec_build(args[0]);
if (Store.is_structure(alpha)) {
if (Store.is_cache(((Store.Structure) alpha).functor))
alpha = new Store.Compound(((Store.Cache) ((Store.Structure)
alpha).functor).name, ((Store.Structure) alpha).args);
} else if (Store.is_cache(alpha)) {
alpha = ((Store.Cache) alpha).name;
}
return Machine.exec_unify(args[1], alpha);
}
/**************************************************************/
/* ir_link_new/2 and ir_is_link/1 */
/**************************************************************/
/**
* ir_link_new(L, P): internal only
* The built-in succeeds in P with an anonymous predicate
* for the clauses L.
*/
private static boolean test_ir_link_new(Object[] args) {
Object alpha = Machine.exec_build(args[0]);
Object[] rope = special.list_objects(alpha);
return Machine.exec_unify(args[1], Store.make_defined(rope));
}
/**
* ir_is_link(Q): internal only
* The built-in succeeds if Q is a provable.
*/
private static boolean test_ir_is_link(Object[] args) {
Object alpha = Machine.exec_build(args[0]);
if (Store.is_structure(alpha))
return Store.is_provable(((Store.Structure) alpha).functor);
return Store.is_provable(alpha);
}
/**************************************************************/
/* kb_clause_ref/3 and kb_pred_touch/3 */
/**************************************************************/
private static final int MASK_FIND_MODIFY = 0x00000001;
private static final int MASK_FIND_DYNAMIC = 0x00000002;
private static final int MASK_FIND_REVERSE = 0x00000004;
private static final int MASK_FIND_SIGNAL = 0x00000008;
/**
* kb_clause_ref(H, F, C): internal only
* The built-in succeeds in C with the clause references
* for the head H and the flags F.
*/
private static Object special_kb_clause_ref(Object[] args) {
Object head = Store.deref(args[0]);
special.check_callable(head);
Object beta = Store.deref(args[1]);
special.check_integer(beta);
int flags = ((Number) beta).intValue();
Store.Provable peek = Machine.lookup_pred(head);
if (peek == null) {
if ((flags & MASK_FIND_SIGNAL) != 0) {
throw Machine.make_error(new Store.Compound("existence_error",
new Object[]{"procedure", Machine.make_indicator_term(head)}));
} else {
return false;
}
}
if ((flags & MASK_FIND_DYNAMIC) != 0) {
if ((peek.flags & Store.MASK_PRED_DYNAMIC) == 0) {
make_error_find(head, flags);
}
}
if (!Store.is_logical(peek.func) && !Store.is_stick(peek.func)) {
if ((flags & MASK_FIND_SIGNAL) != 0) {
make_error_find(head, flags);
} else {
return false;
}
}
Object[] temp;
if (Store.is_structure(head)) {
temp = ((Store.Structure) head).args;
} else {
temp = Machine.VOID_ARGS;
}
Store.Logical rope = Store.defined_pred(peek, temp);
Store.Clause[] data = Store.snapshot_rope(rope);
if ((flags & MASK_FIND_REVERSE) == 0) {
return solve2_ref(args, data, 0, null);
} else {
return solve2_ref_reverse(args, data, data.length, null);
}
}
private static void make_error_find(Object head, int flags) {
if ((flags & MASK_FIND_MODIFY) != 0) {
throw Machine.make_error(new Store.Compound("permission_error",
new Object[]{"modify", "static_procedure", Machine.make_indicator_term(head)}));
} else {
throw Machine.make_error(new Store.Compound("permission_error",
new Object[]{"access", "private_procedure", Machine.make_indicator_term(head)}));
}
}
private static Object solve_ref(Object rope, int at, Machine.Choice choice) {
Object goal = Store.deref(((Store.Structure) Machine.call).args[0]);
return solve2_ref(((Store.Structure) goal).args, rope, at, choice);
}
private static Object solve_ref_reverse(Object rope, int at, Machine.Choice choice) {
Object goal = Store.deref(((Store.Structure) Machine.call).args[0]);
return solve2_ref_reverse(((Store.Structure) goal).args, rope, at, choice);
}
/**
* Search a Prolog clause and return it.
*
* @param args The current arguments.
* @param data The clause list.
* @param at The clause index.
* @param choice The choice point for reuse or null.
* @return boolean True if search succeeds, otherwise false.
*/
private static Object solve2_ref(Object[] args, Object data, int at, Machine.Choice choice) {
Store.Clause[] rope = (Store.Clause[]) data;
Store.Variable mark = Machine.trail;
while (at < rope.length) {
Store.Clause clause = rope[at++];
if (Machine.unify(args[2], clause)) {
if (at < rope.length) {
if (choice == null) {
choice = new Machine.Choice(runtime::solve_ref, rope, at, mark);
} else {
choice.at = at;
}
Machine.more(choice);
}
Machine.cont(((Store.Structure) Machine.call).args[1]);
return Boolean.TRUE;
}
Machine.unbind(mark);
}
return Boolean.FALSE;
}
/**
* Search a Prolog clause backwards and return it.
*
* @param args The current arguments.
* @param data The clause list.
* @param at The clause index.
* @param choice The choice point for reuse or null.
* @return boolean True if search succeeds, otherwise false.
*/
private static Object solve2_ref_reverse(Object[] args, Object data, int at, Machine.Choice choice) {
Store.Clause[] rope = (Store.Clause[]) data;
Store.Variable mark = Machine.trail;
while (at > 0) {
Store.Clause clause = rope[--at];
if (Machine.unify(args[2], clause)) {
if (at > 0) {
if (choice == null) {
choice = new Machine.Choice(runtime::solve_ref_reverse, rope, at, mark);
} else {
choice.at = at;
}
Machine.more(choice);
}
Machine.cont(((Store.Structure) Machine.call).args[1]);
return Boolean.TRUE;
}
Machine.unbind(mark);
}
return Boolean.FALSE;
}
/**
* kb_pred_touch(F, N, O): internal only
* The built-in succeeds. As a side effect the predicate
* indicator F/N with options O is touched.
*/
private static boolean test_kb_pred_touch(Object[] args) {
Object functor = Machine.exec_build(args[0]);
special.check_atom(functor);
Object beta = Machine.exec_build(args[1]);
special.check_integer(beta);
Object gamma = Machine.exec_build(args[2]);
special.check_integer(gamma);
Store.pred_touch((String) functor, ((Number) beta).intValue(),
((Number) gamma).intValue());
return true;
}
/**************************************************************/
/* kb_clause_remove/2 and kb_pred_destroy/2 */
/**************************************************************/
/**
* kb_clause_remove(C, O): internal only
* The built-in succeeds if the clause C could
* be removed according to the options O.
*/
private static boolean test_kb_clause_remove(Object[] args) {
Object alpha = Machine.exec_build(args[0]);
special.check_clause(alpha);
Object beta = Machine.exec_build(args[1]);
special.check_integer(beta);
return Store.remove_clause((Store.Clause) alpha, ((Integer) beta).intValue());
}
/**
* kb_pred_destroy(F, N): internal only
* The built-in succeeds. As a side effect the
* predicate indicator F/N is destroyed.
*/
private static boolean test_kb_pred_destroy(Object[] args) {
Object functor = Machine.exec_build(args[0]);
special.check_atom(functor);
Object beta = Machine.exec_build(args[1]);
special.check_integer(beta);
int arity = ((Number) beta).intValue();
Store.pred_destroy((String) functor, arity);
return true;
}
/**************************************************************/
/* kb_pred_link/3, kb_link_flags/2 and kb_pred_list/1 */
/**************************************************************/
/**
* kb_pred_link(F, A, Q): internal only
* The built-in succeeds in Q with the provable of the
* predicate indicator F/A. Otherwise if no such provable
* exists the built-in fails.
*/
private static boolean test_kb_pred_link(Object[] args) {
Object functor = Machine.exec_build(args[0]);
special.check_atom(functor);
Object beta = Machine.exec_build(args[1]);
special.check_integer(beta);
int arity = ((Number) beta).intValue();
Store.Provable peek = Store.pred_link((String) functor, arity);
if (peek == null)
return false;
return Machine.exec_unify(args[2], peek);
}
/**
* kb_link_flags(Q, F): internal only
* The built-in succeeds in F with the flags of the provable Q.
*/
private static boolean test_kb_link_flags(Object[] args) {
Object peek = Machine.exec_build(args[0]);
check_provable(peek);
int res = ((Store.Provable) peek).flags;
return Machine.exec_unify(args[1], Integer.valueOf(res));
}
/**
* Assure that the object is a provable.
*
* @param beta The object.
*/
public static void check_provable(Object beta) {
if (!Store.is_provable(beta)) {
Store.check_nonvar(beta);
beta = Store.copy_term(beta);
throw Machine.make_error(new Store.Compound("type_error",
new Object[]{"provable", beta}));
}
}
/**
* kb_pred_list(L): internal only
* The built-in succeeds in L with the current predicate indicators.
*/
private static boolean test_kb_pred_list(Object[] args) {
Object res = kb_pred_list();
return Machine.exec_unify(args[0], res);
}
private static Object kb_pred_list() {
Store.Structure back = null;
Object res = null;
Iterator it = Store.kb.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
List temp = (List)entry.getValue();
for (int i = 0; i < temp.size(); i++) {
Store.Provable peek = (Store.Provable)temp.get(i);
if (peek == null || peek.remover != Store.UNDEF_INT)
continue;
Store.Structure elem = new Store.Compound(".",
new Object[]{Machine.make_indicator(entry.getKey(), i),
Store.UNDEF_OBJ});
if (back == null) {
res = elem;
} else {
back.args[1] = elem;
}
back = elem;
}
}
if (back == null) {
res = "[]";
} else {
back.args[1] = "[]";
}
return res;
}
/**************************************************************/
/* kb_clause_shard/2, kb_clause_head/2 and kb_clause_data/4 */
/**************************************************************/
/**
* kb_clause_shard(C, S): internal only
* The built-in succeeds in S with the shard of the clause C.
*/
private static boolean test_kb_clause_shard(Object[] args) {
Object clause = Machine.exec_build(args[0]);
special.check_clause(clause);
return Machine.exec_unify(args[1], ((Store.Clause) clause).shard);
}
/**
* kb_clause_head(C, H): internal only
* The built-in succeeds in H with the head of the clause C.
*/
private static Object special_kb_clause_head(Object[] args) {
Object beta = Store.deref(args[0]);
special.check_clause(beta);
Store.Clause clause = (Store.Clause) beta;
Object head = Store.deref(args[1]);
Object[] display;
if (clause.size != 0) {
display = new Object[clause.size];
} else {
display = null;
}
if (!Store.is_variable(head)) {
Object[] paras;
if (Store.is_structure(head)) {
paras = ((Store.Structure) head).args;
} else {
paras = Machine.VOID_ARGS;
}
if (!Machine.exec_head(clause.head, display, paras))
return Boolean.FALSE;
} else {
Object alpha = Machine.exec_frame(clause.functor, clause.head, display);
Machine.bind(alpha, (Store.Variable)head);
}
Machine.cont(((Store.Structure) Machine.call).args[1]);
return Boolean.TRUE;
}
/**
* kb_clause_data(C, H, O, L): internal only
* The built-in succeeds in H, O and L with the head,
* the cut var and the compiled body of the clause C.
*/
private static Object special_kb_clause_data(Object[] args) {
Object beta = Store.deref(args[0]);
special.check_clause(beta);
Store.Clause clause = (Store.Clause) beta;
Object head = Store.deref(args[1]);
Object[] display;
if (clause.size != 0) {
display = new Object[clause.size];
} else {
display = null;
}
if (!Store.is_variable(head)) {
Object[] paras;
if (Store.is_structure(head)) {
paras = ((Store.Structure) head).args;
} else {
paras = Machine.VOID_ARGS;
}
if (!Machine.exec_head(clause.head, display, paras))
return Boolean.FALSE;
} else {
Object alpha = Machine.exec_frame(clause.functor, clause.head, display);
Machine.bind(alpha, (Store.Variable)head);
}
Object temp;
int peek = clause.cutvar;
if (peek != -1) {
temp = new Store.Variable();
display[peek] = temp;
temp = new Store.Compound("just", new Object[]{temp});
} else {
temp = "nothing";
}
if (!Machine.unify(args[2], temp))
return Boolean.FALSE;
temp = Machine.exec_body(clause.body, display);
if (!Machine.unify(args[3], temp))
return Boolean.FALSE;
Machine.cont(((Store.Structure) Machine.call).args[1]);
return Boolean.TRUE;
}
/**************************************************************/
/* Runtime Init */
/**************************************************************/
public static void boot() {
// stream specials, output
Store.set("current_output", 1, special.make_check(runtime::test_current_output));
Store.set("current_error", 1, special.make_check(runtime::test_current_error));
Store.set("set_output", 1, special.make_check(runtime::test_set_output));
Store.set("set_error", 1, special.make_check(runtime::test_set_error));
Store.set("put_atom", 2, special.make_check(runtime::test_put_atom));
// stream specials, input
Store.set("current_input", 1, special.make_check(runtime::test_current_input));
Store.set("set_input", 1, special.make_check(runtime::test_set_input));
Store.set("os_get_code", 2, special.make_check(runtime::test_os_get_code));
Store.set("os_peek_code", 2, special.make_check(runtime::test_os_peek_code));
// stream specials, async
Store.set("os_open_promise", 4, special.make_check(runtime::test_os_open_promise));
Store.set("os_read_promise", 2, special.make_check(runtime::test_os_read_promise));
Store.set("os_close_promise", 2, special.make_check(runtime::test_os_close_promise));
Store.set("os_prop_promise", 3, special.make_check(runtime::test_os_prop_promise));
// stream specials, sync
Store.set("os_open_sync", 4, special.make_check(runtime::test_os_open_sync));
Store.set("flush_output", 1, special.make_check(runtime::test_flush_output));
Store.set("os_close_sync", 1, special.make_check(runtime::test_os_close_sync));
Store.set("os_cntrl_sync", 2, special.make_check(runtime::test_os_cntrl_sync));
Store.set("os_read_sync", 1, special.make_check(runtime::test_os_read_sync));
// intermediate representation, Albufeira terms
Store.set("ir_site_new", 2, special.make_check(runtime::test_ir_site_new));
Store.set("ir_is_site", 1, special.make_check(runtime::test_ir_is_site));
Store.set("ir_site_name", 2, special.make_check(runtime::test_ir_site_name));
Store.set("ir_link_new", 2, special.make_check(runtime::test_ir_link_new));
Store.set("ir_is_link", 1, special.make_check(runtime::test_ir_is_link));
// knowledge base specials, dynamic database, internal only
Store.set("kb_clause_ref", 3, special.make_special(runtime::special_kb_clause_ref));
Store.set("kb_pred_touch", 3, special.make_check(runtime::test_kb_pred_touch));
Store.set("kb_clause_remove", 2, special.make_check(runtime::test_kb_clause_remove));
Store.set("kb_pred_destroy", 2, special.make_check(runtime::test_kb_pred_destroy));
// knowledge base specials, linked provables, internal only
Store.set("kb_pred_link", 3, special.make_check(runtime::test_kb_pred_link));
Store.set("kb_link_flags", 2, special.make_check(runtime::test_kb_link_flags));
Store.set("kb_pred_list", 1, special.make_check(runtime::test_kb_pred_list));
// knowledge base specials, meta data, internal only
Store.set("kb_clause_shard", 2, special.make_check(runtime::test_kb_clause_shard));
Store.set("kb_clause_head", 2, special.make_special(runtime::special_kb_clause_head));
Store.set("kb_clause_data", 4, special.make_special(runtime::special_kb_clause_data));
}
}