/**
* 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.
*/
export const MASK_PRED_PREHASH = 0x00000010;
export const MASK_PRED_ARITH = 0x00000008;
export const MASK_PRED_SPECIAL = 0x00000004;
export const MASK_PRED_TEST = 0x00000002;
export const MASK_PRED_DYNAMIC = 0x00000001;
export const MASK_TOUCH_PREHASH = 0x00000004;
export const MASK_TOUCH_BOTTOM = 0x00000002;
export const MASK_TOUCH_DYNAMIC = 0x00000001;
export const MASK_REMOVE_REVERSE = 0x00000001;
export const VAR_MASK_SEEN = 0x40000000;
export const VAR_MASK_EVEN = 0x20000000;
export const VAR_MASK_ODD = 0x10000000;
export const VAR_MASK_STATE = VAR_MASK_EVEN | VAR_MASK_ODD;
export const VAR_MASK_BITS = 28;
export const VAR_MASK_SERNO = (1 << VAR_MASK_BITS) - 1;
export let kb = {};
export let stage = -1;
export const MAX_INDEX = 2;
const ROPE_LOW = 24;
const ROPE_HIGH = 40;
/**
* Set the clause and predicate current stage.
*
* @param num The current stage.
*/
export function set_stage(num) {
stage = num;
}
/**
* Set the clause current partition.
*
* @param path The current partition.
*/
export function set_partition(path) {
engine.partition = path;
}
/**************************************************************/
/* Engine */
/**************************************************************/
export const SYS_MASK_STATE_ASYNC = 0x00000080;
/**
* Create a slow state engine.
*
* @constructor The engine.
*/
export function Engine() {
this.signal = undefined;
this.abort = () => {};
this.flags = SYS_MASK_STATE_ASYNC;
this.text_output = undefined;
this.text_error = undefined;
this.text_input = undefined;
this.low = 0;
this.high = 0;
this.serno = 0;
this.backtrail = null;
this.partition = (stage === -1 ? "system" : "user");
}
export let engine = new Engine();
export function set_engine(ptr) {
engine = ptr;
}
/**************************************************************/
/* Variable & Compound */
/**************************************************************/
/**
* Create a Prolog variable.
*
* @constructor The new variable.
*/
export function Variable() {
this.instantiated = undefined;
this.flags = VAR_MASK_SERNO;
this.tail = null;
}
/**
* Check whether an object is a Prolog variable.
*
* @param obj The object.
* @return boolean True if the object is a variable, otherwise false.
*/
export function is_variable(obj) {
return obj instanceof Variable;
}
/**
* Compute a variable serno.
*
* @param alpha The variable.
* @return The serno.
*/
export function variable_serno(alpha) {
let val = alpha.flags & VAR_MASK_SERNO;
if (val === VAR_MASK_SERNO) {
if (engine.low < engine.high) {
val = engine.low;
engine.low = val + 1;
} else {
val = engine.serno;
engine.serno = val + 1;
}
alpha.flags &= ~VAR_MASK_SERNO;
alpha.flags |= val;
}
return val;
}
/**
* Create an element.
*
* @param functor The functor.
* @param args The arguments.
* @constructor The new element.
*/
export function Element(functor, args) {
this.functor = functor;
this.args = args;
}
/**
* Check whether an object is an element.
*
* @param obj The object.
* @return boolean True if the object is an element, otherwise false.
*/
export function is_element(obj) {
return obj instanceof Element;
}
/**
* Create a Prolog structure.
*
* @param functor The functor.
* @param args The arguments.
* @constructor The new structure.
*/
export class Structure extends Element {
constructor(functor, args) {
super(functor, args);
}
}
/**
* Check whether an object is a Prolog structure.
*
* @param obj The object.
* @return boolean True if the object is a structure, otherwise false.
*/
export function is_structure(obj) {
return obj instanceof Structure;
}
/**
* Create a Prolog compound.
*
* @param functor The functor.
* @param args The arguments.
* @constructor The new compound.
*/
export class Compound extends Structure {
constructor(functor, args) {
super(functor, args);
this.walk = 0;
}
}
/**
* Check whether an object is a Prolog compound.
*
* @param obj The object.
* @return boolean True if the object is a compound, otherwise false.
*/
export function is_compound(obj) {
return obj instanceof Compound;
}
/**************************************************************/
/* Place & Skeleton */
/**************************************************************/
/**
* Create a Albufeira Code place.
*/
export function Place(index) {
this.index = index;
}
/**
* Check whether an object is a place.
*
* @param obj The object.
* @return boolean True if the object is a place, otherwise false.
*/
export function is_place(obj) {
return obj instanceof Place;
}
/**
* Create a Albufeira Code skeleton.
*
* @param functor The functor.
* @param args The arguments.
*/
export class Skeleton extends Element {
constructor(functor, args) {
super(functor, args);
}
}
/**
* Check whether an object is a skeleton.
*
* @param obj The object.
* @return boolean True if the object is a skeleton, otherwise false.
*/
export function is_skeleton(obj) {
return obj instanceof Skeleton;
}
/**************************************************************/
/* deref() and copy_term() */
/**************************************************************/
/**
* Dereference a Prolog term.
*
* @param term The Prolog term.
* @return any The dereferenced Prolog term.
*/
export function deref(term) {
while (is_variable(term) && term.instantiated !== undefined)
term = term.instantiated;
return term;
}
/**
* Copy a Prolog term. Can handle cyclic terms.
*
* @param first The Prolog term.
* @return any The copy.
*/
export function copy_term(first) {
let res;
try {
res = walk_copy(first);
} finally {
walk_uncopy(first);
}
return res;
}
function walk_copy(first) {
let stack = null;
for (; ; ) {
first = deref(first);
if (is_variable(first)) {
if (first.tail === null) {
let peek = new Variable();
first.tail = peek;
first = peek;
} else {
first = first.tail;
}
} else if (is_compound(first)) {
if (!is_compound(first.functor)) {
first.functor = new Compound(first.functor,
new Array(first.args.length));
first.walk &= ~VAR_MASK_SERNO;
stack = stack_push(stack, first);
first = first.args[0];
continue;
} else {
first = first.functor;
}
}
let item = stack_peek(stack);
while (item !== null &&
(item.walk & VAR_MASK_SERNO) === item.args.length - 1) {
item.functor.args[item.walk & VAR_MASK_SERNO] = first;
first = item.functor;
stack_pop(stack);
item = stack_peek(stack);
}
if (item === null) {
return first;
} else {
item.functor.args[item.walk & VAR_MASK_SERNO] = first;
item.walk++;
first = item.args[item.walk & VAR_MASK_SERNO];
}
}
}
function walk_uncopy(first) {
let stack = null;
for (; ; ) {
first = deref(first);
if (is_variable(first)) {
if (first.tail !== null)
first.tail = null;
} else if (is_compound(first)) {
if (is_compound(first.functor)) {
first.functor = first.functor.functor;
if (0 !== first.args.length - 1) {
first.walk &= ~VAR_MASK_SERNO;
stack = stack_push(stack, first);
}
first = first.args[0];
continue;
}
}
let item = stack_peek(stack);
if (item === null) {
return;
} else {
item.walk++;
first = item.args[item.walk & VAR_MASK_SERNO];
if ((item.walk & VAR_MASK_SERNO) === item.args.length - 1)
stack_pop(stack);
}
}
}
/**************************************************************/
/* Binary Work */
/**************************************************************/
/**
* Create a binary work item.
*
* @param first The first Prolog term.
* @param second The second Prolog term.
* @param idx The index.
*/
export function Item(first, second, idx) {
this.first = first;
this.second = second;
this.idx = idx;
}
/**************************************************************/
/* Stack Array */
/**************************************************************/
/**
* The function returns the same stack extended by a work item.
*
* @param stack The stack.
* @param elem The work item.
* @return The new stack.
*/
export function stack_push(stack, elem) {
if (stack === null)
stack = new Array(0);
stack.push(elem);
return stack;
}
/**
* The function returns the top work item or null.
*
* @param stack The stack.
* @return The work item.
*/
export function stack_peek(stack) {
if (stack === null)
return null;
if (stack.length === 0)
return null;
return stack[stack.length-1];
}
/**
* The routine removes the top work item from the stack.
*
* @param stack The stack.
*/
export function stack_pop(stack) {
stack.pop();
}
/**************************************************************/
/* Clauses Lifecycle */
/**************************************************************/
export function Goal(size, body, cutvar) {
this.size = size;
this.body = body;
this.cutvar = cutvar;
}
/**
* Check whether the object is a goal.
*
* @param obj The object.
* @return boolean if the object is a goal, otherwise false.
*/
export function is_goal(obj) {
return obj instanceof Goal;
}
export function Clause(size, functor, head, body, cutvar) {
this.size = size;
this.functor = functor;
this.head = head;
this.body = body;
this.cutvar = cutvar;
this.creator = undefined;
this.remover = undefined;
this.shard = "";
}
/**
* Check whether the object is a clause.
*
* @param obj The object.
* @return boolean if the object is a clause, otherwise false.
*/
export function is_clause(obj) {
return obj instanceof Clause;
}
/**
* Create a logical.
*
* @param cache The cache of non-deleted clauses.
* @param count The counter of non-deleted clauses.
* @param data The clause list.
*/
export function Logical(cache, count, data) {
this.cache = cache;
this.count = count;
this.data = data;
}
/**
* Check whether an object is a logical.
*
* @param obj The object.
* @return boolean if the object is a logical, otherwise false.
*/
export function is_logical(obj) {
return obj instanceof Logical;
}
/**
* Create a stick.
*
* @param rope The rope.
* @param guards The variable guards.
* @param maps The index map.
*/
export function Stick(rope, guards, maps) {
this.rope = rope;
this.guards = guards;
this.maps = maps;
}
/**
* Check whether an object is a stick.
*
* @param obj The object.
* @return boolean if the object is a stick, otherwise false.
*/
export function is_stick(obj) {
return obj instanceof Stick;
}
export function index_value(temp, body, flags) {
let j = 0;
for (;;) {
if (!is_place(temp)) {
if ((flags & MASK_PRED_PREHASH) !== 0) {
if (is_frozen(temp)) {
temp = temp.hash;
} else if (!is_skeleton(temp)) {
temp = object_hash_code(temp);
} else {
temp = undefined;
}
} else {
if (is_element(temp))
temp = temp.functor;
}
break;
}
if (temp !== undefined &&
(flags & MASK_PRED_DYNAMIC) === 0 &&
j < body.length) {
let peek = body[j];
j++;
if (is_element(peek) &&
"=" === site_name(peek.functor) &&
peek.args.length === 2) {
if (temp === peek.args[0]) {
temp = peek.args[1];
} else if (temp === peek.args[1]) {
temp = peek.args[0];
}
continue;
} else if (is_element(peek) &&
"nonvar" === site_name(peek.functor) &&
peek.args.length === 1) {
continue;
}
}
temp = undefined;
break;
}
return temp;
}
function site_name(obj) {
if (is_cache(obj)) {
return obj.name;
} else {
return obj;
}
}
/**************************************************************/
/* Knowledgebase */
/**************************************************************/
/**
* Create a provable.
*
* @constructor The new provable.
*/
export function Provable() {
this.flags = 0;
this.func = undefined;
this.creator = stage;
this.remover = undefined;
this.overlay = undefined;
}
/**
* Check whether an object is a provable.
*
* @param obj The object.
* @return boolean True if the object is a provable, otherwise false.
*/
export function is_provable(obj) {
return obj instanceof Provable;
}
/**
* Create a cache node.
*
* @param name The functor.
* @constructor The new cache node.
*/
export function Cache(name) {
this.link = undefined;
this.name = name;
}
/**
* Check whether an object is a cache.
*
* @param obj The object.
* @return boolean True if the object is a cache, otherwise false.
*/
export function is_cache(obj) {
return obj instanceof Cache;
}
/*********************************************************************/
/* Snapshot Data */
/*********************************************************************/
export function snapshot_peek(func) {
if (is_stick(func)) {
return snapshot_rope(func.rope);
} else {
return snapshot_rope(func);
}
}
/**
* Make snapshot of a logical.
*
* @param rope The logical.
* @return array The clause list snapshot.
*/
export function snapshot_rope(rope) {
let res = rope.cache;
if (res === null) {
res = new Array(rope.count);
copy_rope(res, rope);
rope.cache = res;
}
return res;
}
function copy_rope(res, rope) {
if (Array.isArray(rope.data)) {
let data = rope.data;
let j = 0;
for (let i = 0; i < data.length; i++) {
let clause = data[i];
if (clause.remover === undefined)
res[j++] = clause;
}
} else {
let data = rope.data;
let j = 0;
for (let clause of data) {
if (clause.remover === undefined)
res[j++] = clause;
}
}
}
/*********************************************************************/
/* Linked Provables */
/*********************************************************************/
/**
* Retrieve a provable from monomorphic cache.
*
* @param cache The cache.
* @param arity The arity.
* @return The provable or undefined.
*/
export function ensure_link(cache, arity) {
let peek = cache.link;
if (peek === undefined || peek.remover !== undefined) {
peek = pred_link(cache.name, arity);
cache.link = peek;
}
return peek;
}
export function pred_link(functor, arity) {
let temp = kb[functor];
if (temp === undefined)
return undefined;
let peek = temp[arity];
if (peek === undefined || peek.remover !== undefined)
return undefined;
return peek;
}
export function resolve_link(functor, arity) {
if (is_cache(functor)) {
return ensure_link(functor, arity);
} else if (is_atom(functor)) {
return pred_link(functor, arity);
} else if (is_provable(functor)) {
return functor;
} else {
check_nonvar(functor);
functor = copy_term(functor);
throw make_error(new Compound("type_error",
["callable", functor]));
}
}
/**
* Assure that the object is a nonvar.
*
* @param beta The object.
*/
export function check_nonvar(beta) {
if (is_variable(beta))
throw make_error("instantiation_error");
}
/*********************************************************************/
/* Dynamic Predicates */
/*********************************************************************/
/**
* Retrieve or create a provable from monomorphic cache.
*
* @param cache The cache.
* @param arity The arity.
* @param flags The flags.
* @return The provable
*/
export function ensure_touch(cache, arity, flags) {
let peek = cache.link;
if (peek === undefined || peek.remover !== undefined) {
peek = pred_touch(cache.name, arity, flags);
cache.link = peek;
}
return peek;
}
export function pred_touch(functor, arity, flags) {
let temp = kb[functor];
if (temp === undefined) {
temp = new Array(0);
kb[functor] = temp;
}
let peek = temp[arity];
if (peek === undefined || peek.remover !== undefined) {
let res = make_defined(VOID_ARGS);
if (peek !== undefined)
res.overlay = peek;
if ((flags & MASK_TOUCH_DYNAMIC) !== 0)
res.flags |= MASK_PRED_DYNAMIC;
temp[arity] = res;
peek = res;
} else {
if (!is_logical(peek.func) && !is_stick(peek.func))
throw make_error(new Compound("permission_error",
["modify", "static_procedure",
make_indicator(functor, arity)]));
if ((flags & MASK_TOUCH_DYNAMIC) !== 0)
if ((peek.flags & MASK_PRED_DYNAMIC) === 0)
throw make_error(new Compound("permission_error",
["modify", "static_procedure",
make_indicator(functor, arity)]));
}
if ((flags & MASK_TOUCH_PREHASH) !== 0) {
if ((peek.flags & MASK_PRED_PREHASH) === 0) {
if (is_stick(peek.func))
peek.func = peek.func.rope;
peek.flags |= MASK_PRED_PREHASH;
}
}
return peek;
}
function resolve_touch(functor, arity, flags) {
if (is_cache(functor)) {
return ensure_touch(functor, arity, flags);
} else if (is_atom(functor)) {
return pred_touch(functor, arity, flags);
} else if (is_provable(functor)) {
return functor;
} else {
check_nonvar(functor);
functor = copy_term(functor);
throw make_error(new Compound("type_error",
["callable", functor]));
}
}
/*********************************************************************/
/* Index Extension */
/*********************************************************************/
/**
* The function returns an anonymous predicate for the given clauses.
* The index is not yet built, so only a logical is stored.
*
* @param data The clauses.
* @return Provable The predicate.
*/
export function make_defined(data) {
let peek = new Provable();
peek.func = new_rope();
for (let i = 0; i < data.length; i++)
add_peek(peek.func, 0, data[i], MASK_TOUCH_BOTTOM, peek.flags);
return peek;
}
function new_rope() {
return new Logical(null, 0, new Array(0));
}
function new_bouquet(rope, width) {
let guards = new Array(width);
let maps = new Array(width);
return new Stick(rope, guards, maps);
}
function extend_bouquet(peek, j, offset, rope, flags) {
peek.guards[j] = new_rope();
peek.maps[j] = new Map();
if (Array.isArray(rope.data)) {
let data = rope.data;
for (let i = 0; i < data.length; i++) {
let clause = data[i];
if (clause.remover === undefined)
add_map(peek, j, offset, clause, MASK_TOUCH_BOTTOM, flags);
}
} else {
let data = rope.data;
for (let clause of data) {
if (clause.remover === undefined)
add_map(peek, j, offset, clause, MASK_TOUCH_BOTTOM, flags);
}
}
}
/**************************************************************/
/* Clauses Retrieval */
/**************************************************************/
/**
* Retrieve a logical from a provable possibly using an index.
* If only a logical is stored, build an index before retrieval.
*
* @param peek The provable.
* @param args The actual arguments.
* @return Logical The logical.
*/
export function defined_pred(peek, args) {
let width = Math.min(MAX_INDEX, args.length);
let offset = 0;
let func = peek.func;
let flags = peek.flags;
let val = null;
let k = 0;
for (;;) {
let rope;
if (is_stick(func)) {
rope = func.rope;
} else {
rope = func;
}
if (rope.count < 2)
return rope;
let i = 0;
for (; i < width; i++) {
let key = key_value(args[i+offset], flags);
if (key === undefined)
continue;
if (!is_stick(func)) {
func = new_bouquet(rope, width);
if (is_stick(peek)) {
if (val === undefined) {
peek.guards[k] = func;
} else {
peek.maps[k].set(val, func);
}
} else {
peek.func = func;
}
}
peek = func;
if (peek.maps[i] === undefined)
extend_bouquet(peek, i, offset, rope, flags);
if (peek.maps[i].size === 0)
continue;
func = peek.maps[i].get(key);
if (func === undefined) {
func = peek.guards[i];
val = undefined;
} else {
val = key;
}
k = i;
width = width-i-1;
offset = i+offset+1;
i = -1;
break;
}
if (!(i < width))
return rope;
}
}
function key_value(term, flags) {
term = deref(term);
if (!is_variable(term)) {
if ((flags & MASK_PRED_PREHASH) !== 0) {
if (is_frozen(term)) {
term = term.hash;
} else if (!is_compound(term)) {
term = object_hash_code(term);
} else {
term = undefined;
}
} else {
if (is_structure(term))
term = term.functor;
}
} else {
term = undefined;
}
return term;
}
import {
is_atom, make_error, make_indicator, VOID_ARGS,
is_frozen, object_hash_code
} from "./machine.mjs";
/*********************************************************************/
/* Destroy Provable */
/*********************************************************************/
/**
* Remove a predicate from the knowledge base.
*
* @param functor The functor.
* @param arity The arity.
*/
export function pred_destroy(functor, arity) {
let temp = kb[functor];
if (temp === undefined)
return;
let peek = temp[arity];
if (peek === undefined || peek.remover !== undefined)
return;
if (peek.creator === stage) {
peek = clear_pop(peek);
temp[arity] = peek;
if (peek === undefined && trim_arities(temp))
delete kb[functor];
} else {
peek.remover = stage;
}
}
/**
* Clear and pop a provable.
*
* @param peek The provable.
* @return The parent provable.
*/
function clear_pop(peek) {
peek.remover = stage;
peek.func = undefined;
let back = peek;
peek = back.overlay;
back.overlay = undefined;
return peek;
}
/**
* Trim the arities array.
*
* @param peek The arities array.
* @return boolean True if empty, otherwise fale.
*/
function trim_arities(peek) {
let pos = peek.length;
while (pos > 0 && peek[pos - 1] === undefined)
pos--;
if (pos === 0)
return true;
if (pos !== peek.length)
peek.splice(pos, peek.length - pos);
return false;
}
/**************************************************************/
/* Clauses Transactions */
/**************************************************************/
/**
* Rollback the clauses.
*/
export function clear() {
for (let functor in kb) {
let temp = kb[functor];
for (let i = 0; i < temp.length; i++) {
let peek = temp[i];
if (peek === undefined)
continue;
if (peek.creator === stage) {
peek = clear_pop(peek);
temp[i] = peek;
if (peek === undefined)
continue;
}
if (peek.remover === stage)
peek.remover = undefined;
if (is_logical(peek.func) || is_stick(peek.func))
rollback_peek(peek.func, true);
}
if (trim_arities(temp))
delete kb[functor]
}
}
function rollback_peek(peek, update) {
if (is_stick(peek)) {
if (!has_action(peek.rope))
return;
for (let i = 0; i < peek.maps.length; i++) {
let temp = peek.maps[i];
if (temp === undefined)
continue;
for (let [key, val] of temp) {
rollback_peek(val, false);
if (size_peek(val) === 0)
temp.delete(key);
}
rollback_peek(peek.guards[i], false);
}
rollback_rope(peek.rope, update);
} else {
if (!has_action(peek))
return;
rollback_rope(peek, update);
}
}
export function size_peek(func) {
let rope;
if (is_stick(func)) {
rope = func.rope;
} else {
rope = func;
}
if (Array.isArray(rope.data)) {
return rope.data.length;
} else {
return rope.data.size;
}
}
/**
* Check whether a logical has some action.
*
* @param rope The logical.
* @return boolean if the logical has some action, false otherwise.
*/
function has_action(rope) {
if (Array.isArray(rope.data)) {
let data = rope.data;
for (let i = 0; i < data.length; i++) {
let clause = data[i];
if (clause.creator === stage)
return true;
if (clause.remover === stage)
return true;
}
return false;
} else {
let data = rope.data;
for (let clause of data) {
if (clause.creator === stage)
return true;
if (clause.remover === stage)
return true;
}
return false;
}
}
/**
* Rollback clauses from a logical.
*
* @param rope The logical.
* @param update The update flag.
*/
function rollback_rope(rope, update) {
if (Array.isArray(rope.data)) {
let data = rope.data;
let j = 0;
for (let i = 0; i < data.length; i++) {
let clause = data[i];
if (clause.creator === stage) {
rope.count--;
} else {
if (clause.remover === stage) {
if (update)
clause.remover = undefined;
rope.count++;
}
data[j++] = clause;
}
}
data.splice(j);
rope.cache = null;
} else {
let data = rope.data;
for (let clause of data) {
if (clause.creator === stage) {
rope.count--;
data.delete(clause);
} else {
if (clause.remover === stage) {
if (update)
clause.remover = undefined;
rope.count++;
}
}
}
rope.cache = null;
}
}
/*********************************************************************/
/* Provable Addition */
/*********************************************************************/
/**
* Enhance the knowledge base by a provable.
*
* @param functor The functor.
* @param arity The arity.
* @param pred The provable.
*/
export function set(functor, arity, pred) {
let temp = kb[functor];
if (temp === undefined) {
temp = new Array(0);
kb[functor] = temp;
}
let peek = temp[arity];
if (peek === undefined || peek.remover !== undefined) {
if (peek !== undefined)
pred.overlay = peek;
temp[arity] = pred;
} else {
throw make_error(new Compound("permission_error",
["coerce", "procedure", make_indicator(functor, arity)]));
}
}
/*********************************************************************/
/* Clause Addition */
/*********************************************************************/
/**
* Enhance the knowledge base by a clause.
*
* @param clause The clause.
*/
export function add(clause) {
add_clause(clause, MASK_TOUCH_BOTTOM);
}
export function add_clause(clause, flags) {
if (clause.remover !== undefined)
throw make_error(new Compound("permission_error",
["modify", "static_procedure",
make_indicator(clause.functor, clause.head.length)]));
if (clause.creator !== undefined)
return;
let peek = resolve_touch(clause.functor, clause.head.length, flags);
add_peek(peek.func, 0, clause, flags, peek.flags);
clause.creator = stage;
if ((flags & MASK_TOUCH_DYNAMIC) === 0)
clause.shard = engine.partition;
}
function add_peek(peek, offset, clause, flags, flags2) {
if (is_stick(peek)) {
add_rope(peek.rope, clause, flags);
for (let i = 0; i < peek.maps.length; i++)
if (peek.maps[i] !== undefined)
add_map(peek, i, offset, clause, flags, flags2);
} else {
add_rope(peek, clause, flags);
}
}
function add_map(peek, i, offset, clause, flags, flags2) {
let key = index_value(clause.head[i+offset], clause.body, flags2);
let temp = peek.maps[i];
if (key === undefined) {
for (let [key, val] of temp)
add_peek(val, i+offset+1, clause, flags, flags2);
add_peek(peek.guards[i], i+offset+1, clause, flags, flags2);
} else {
let val = temp.get(key);
if (val === undefined) {
val = clone_peek(peek.guards[i]);
temp.set(key, val);
}
add_peek(val, i+offset+1, clause, flags, flags2);
}
}
function add_rope(rope, clause, flags) {
if (Array.isArray(rope.data)) {
let data = rope.data;
if ((flags & MASK_TOUCH_BOTTOM) !== 0) {
data.push(clause);
} else {
data.unshift(clause);
}
if (data.length >= ROPE_HIGH)
rope.data = new Set(data);
rope.count++;
rope.cache = null;
} else {
let data = rope.data;
if ((flags & MASK_TOUCH_BOTTOM) !== 0) {
data.add(clause);
} else {
rope.data = new Set((function *() {yield clause; yield * data})());
}
rope.count++;
rope.cache = null;
}
}
function clone_peek(func) {
let rope;
if (is_stick(func)) {
rope = func.rope;
} else {
rope = func;
}
if (Array.isArray(rope.data)) {
return new Logical(rope.cache, rope.count, rope.data.slice());
} else {
return new Logical(rope.cache, rope.count, new Set(rope.data));
}
}
/*********************************************************************/
/* Clause Removal */
/*********************************************************************/
/**
* Remove a clause from the knowledge base.
*
* @param clause The clause.
* @param flags The flags.
* @return boolean True if the clause was removed, false otherwise.
*/
export function remove_clause(clause, flags) {
if (clause.remover !== undefined)
return false;
if (clause.creator === undefined)
return false;
let peek = resolve_link(clause.functor, clause.head.length);
if (peek === undefined)
return false;
if (!is_logical(peek.func) && !is_stick(peek.func))
throw make_error(new Compound("permission_error",
["modify", "static_procedure",
make_indicator(clause.functor, clause.head.length)]));
remove_peek(peek.func, 0, clause, flags, peek.flags);
if (clause.creator !== stage) {
clause.remover = stage;
} else {
clause.creator = undefined;
}
return true;
}
function remove_peek(peek, offset, clause, flags, flags2) {
if (is_stick(peek)) {
remove_rope(peek.rope, clause, flags);
for (let i = 0; i < peek.maps.length; i++) {
let temp = peek.maps[i];
if (temp === undefined)
continue;
let key = index_value(clause.head[i+offset], clause.body, flags2);
if (key === undefined) {
for (let [key, val] of temp) {
remove_peek(val, i+offset+1, clause, flags, flags2);
if (size_peek(val) === 0)
temp.delete(key);
}
remove_peek(peek.guards[i], i+offset+1, clause, flags, flags2);
} else {
let val = temp.get(key);
remove_peek(val, i+offset+1, clause, flags, flags2);
if (size_peek(val) === 0)
temp.delete(key);
}
}
} else {
remove_rope(peek, clause, flags);
}
}
function remove_rope(rope, clause, flags) {
if (Array.isArray(rope.data)) {
let data = rope.data;
let index;
if ((flags & MASK_REMOVE_REVERSE) === 0) {
index = data.indexOf(clause);
} else {
index = data.lastIndexOf(clause);
}
if (clause.creator === stage) {
if (index === -1)
throw new Error("rope corrupted");
data.splice(index, 1);
} else {
if (index === -1)
throw new Error("rope corrupted");
}
rope.count--;
rope.cache = null;
} else {
let data = rope.data;
if (clause.creator === stage) {
if (!data.delete(clause))
throw new Error("rope corrupted");
} else {
if (!data.has(clause))
throw new Error("rope corrupted");
}
if (data.size < ROPE_LOW)
rope.data = [...data];
rope.count--;
rope.cache = null;
}
}