/**
* Modern Albufeira Prolog Interpreter
*
* 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.
*/
import {
set, deref, bind, is_structure, Compound, make_special,
is_number, make_error, VOID_ARGS, cont, get_cont,
check_atomic, make_check, stack_pop, stack_push,
stack_peek, Item, Variable, check_integer,
walk_vars, is_compound,
is_integer, exec_build, exec_unify, VAR_MASK_SEEN
} from "../nova/core.mjs";
/*********************************************************************/
/* numbervars/3 */
/*********************************************************************/
/**
* numbervars(X, N, M):
* The predicate succeeds in M with the next index after
* numbering all variables in X starting with index N.
*/
function test_numbervars(args) {
let alpha = exec_build(args[0]);
let beta = exec_build(args[1]);
check_integer(beta);
if (beta >= 0) {
beta = numbervars("$VAR", alpha, beta);
} else {
beta = (-1) - beta;
beta = numbervars("_", alpha, beta);
beta = (-1) - beta;
}
return exec_unify(args[2], beta);
}
function numbervars(functor, alpha, beta) {
function numbervars2(node) {
bind(new Compound(functor, [beta]), node);
beta++;
return false;
}
walk_vars(alpha, node => false, VAR_MASK_SEEN);
walk_vars(alpha, numbervars2, 0);
return beta;
}
/**************************************************************/
/* Association Map */
/**************************************************************/
function assoc_get(map, key) {
if (map === null)
return undefined;
return map.get(key);
}
function assoc_set(map, key, value) {
if (map === null)
map = new Map();
map.set(key, value);
return map;
}
/******************************************************************/
/* unnumbervars/3 */
/******************************************************************/
/**
* unnumbervars(S, N, T):
* The predicate succeeds in T with a copy of S with the
* numbering greater or equal than N replaced by variables.
*/
function test_unnumbervars(args) {
let alpha = exec_build(args[0]);
let beta = exec_build(args[1]);
check_integer(beta);
let res;
try {
if (beta >= 0) {
res = walk_replace("$VAR", beta, alpha);
} else {
res = (-1) - beta;
res = walk_replace("_", res, alpha);
}
} finally {
if (beta >= 0) {
walk_unmax("$VAR", alpha);
} else {
walk_unmax("_", alpha);
}
}
return exec_unify(args[2], res);
}
function walk_replace(functor, beta, first) {
let stack = null;
let map = null;
for (; ; ) {
first = deref(first);
if (is_structure(first)) {
if (functor === first.functor &&
first.args.length === 1) {
let peek = deref(first.args[0]);
if (is_integer(peek) && beta <= peek) {
first = assoc_get(map, peek);
if (first === undefined) {
first = new Variable();
map = assoc_set(map, peek, first);
}
}
}
if (is_structure(first)) {
if (!is_compound(first.functor)) {
first.functor = new Compound(first.functor,
new Array(first.args.length));
let item = new Item(first, null, 0);
stack = stack_push(stack, item);
first = first.args[0];
continue;
} else {
first = first.functor;
}
}
}
let item = stack_peek(stack);
while (item !== null &&
item.idx === item.first.args.length - 1) {
item.first.functor.args[item.idx] = first;
first = item.first.functor;
stack_pop(stack);
item = stack_peek(stack);
}
if (item === null) {
return first;
} else {
item.first.functor.args[item.idx] = first;
item.idx++;
first = item.first.args[item.idx];
}
}
}
/******************************************************************/
/* maxvars/2 */
/******************************************************************/
/**
* maxvars(X, M):
* The predicate succeeds in M with the max numbering of the term X.
*/
function test_maxvars(args) {
let alpha = exec_build(args[0]);
let res;
try {
res = walk_max("$VAR", alpha);
} finally {
walk_unmax("$VAR", alpha);
}
return exec_unify(args[1], res);
}
function walk_max(functor, first) {
let stack = null;
let res = -1;
for (; ; ) {
first = deref(first);
if (is_structure(first)) {
if (functor === first.functor &&
first.args.length === 1) {
let peek = deref(first.args[0]);
if (is_integer(peek)) {
if (res < peek)
res = peek;
first = null;
}
}
if (first !== null) {
if (!is_compound(first.functor)) {
first.functor = new Compound(
first.functor, VOID_ARGS);
if (0 !== first.args.length - 1) {
let item = new Item(first, null, 0);
stack = stack_push(stack, item);
}
first = first.args[0];
continue;
}
}
}
let item = stack_peek(stack);
if (item == null) {
return res;
} else {
item.idx++;
first = item.first.args[item.idx];
if (item.idx === item.first.args.length - 1)
stack_pop(stack);
}
}
}
function walk_unmax(functor, first) {
let stack = null;
for (; ; ) {
first = deref(first);
if (is_structure(first)) {
if (functor === first.functor &&
first.args.length === 1) {
let peek = deref(first.args[0]);
if (is_integer(peek)) {
first = null;
}
}
if (first !== null) {
if (is_compound(first.functor)) {
first.functor = first.functor.functor;
if (0 !== first.args.length - 1) {
let item = new Item(first, null, 0);
stack = stack_push(stack, item);
}
first = first.args[0];
continue;
}
}
}
let item = stack_peek(stack);
if (item == null) {
return;
} else {
item.idx++;
first = item.first.args[item.idx];
if (item.idx === item.first.args.length - 1)
stack_pop(stack);
}
}
}
/*********************************************************************/
/* call/2-4 */
/*********************************************************************/
/**
* call(F, A1, .., An): [Corr.2 8.15.4.4]
* The predicate succeeds in calling the goal which results from
* appending the arguments A1, .., An to the callable F.
*/
function special_calln(args) {
let alpha = deref(args[0]);
let functor;
let oldargs;
if (is_structure(alpha)) {
functor = alpha.functor;
oldargs = alpha.args;
} else {
functor = alpha;
check_atomic(functor);
oldargs = VOID_ARGS;
}
let arity = oldargs.length + args.length - 1;
let goal;
if (arity === 0) {
goal = functor;
} else {
if (is_number(functor))
throw make_error(new Compound("type_error", ["atom", functor]));
let newargs = new Array(arity);
for (let i = 0; i < oldargs.length; i++)
newargs[i] = deref(oldargs[i]);
for (let i = 0; i < args.length - 1; i++)
newargs[i + oldargs.length] = deref(args[i + 1]);
goal = new Compound(functor, newargs);
}
goal = new Compound(".", [goal, get_cont().args[1]]);
cont(goal);
return true;
}
/*********************************************************************/
/* Asm Lib Init */
/*********************************************************************/
export function main() {
set("numbervars", 3, make_check(test_numbervars));
set("unnumbervars", 3, make_check(test_unnumbervars));
set("maxvars", 2, make_check(test_maxvars));
set("call", 2, make_special(special_calln));
set("call", 3, make_special(special_calln));
set("call", 4, make_special(special_calln));
set("call", 5, make_special(special_calln));
set("call", 6, make_special(special_calln));
set("call", 7, make_special(special_calln));
set("call", 8, make_special(special_calln));
}