JavaScript "store"

Admin User, erstellt 27. Apr. 2024
         
/**
* 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_DYNAMIC = 0x00000001;
export const MASK_PRED_SPECIAL = 0x00000002;
export const MASK_PRED_CHECK = 0x00000004;
export const MASK_PRED_ARITHMETIC = 0x00000008;
export const MASK_TOUCH_DYNAMIC = 0x00000001;
export const MASK_ADD_BOTTOM = 0x00000001;
export const MASK_ADD_DYNAMIC = 0x00000002;
export const MASK_REMOVE_BOTTOM = 0x00000001;
export let kb = {};
export let stage = -1;
/**
* 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 */
/**************************************************************/
/**
* Create a slow state engine.
*
* @constructor The engine.
*/
export function Engine() {
this.signal = undefined;
this.interrupt = () => {};
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.backcount = 0;
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;
if (engine.low < engine.high) {
this.flags = engine.low;
engine.low++;
} else {
this.flags = engine.serno;
engine.serno++;
}
this.tail = null;
}
/**
* Check whether an object is a 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;
}
/**
* Create a Prolog compound.
*
* @param functor The functor.
* @param args The arguments.
* @constructor The new compound.
*/
export function Compound(functor, args) {
this.functor = functor;
this.args = args;
}
/**
* Check whether an object is a 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 function Skeleton(functor, args) {
this.functor = functor;
this.args = 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;
}
/**************************************************************/
/* Quote */
/**************************************************************/
/**
* Create a Albufeira Code quote.
*/
export function Quote(obj) {
this.obj = obj;
}
/**
* Check whether an object is a quote.
*
* @param obj The object.
* @return boolean True if the object is a quote, otherwise false.
*/
export function is_quote(obj) {
return obj instanceof Quote;
}
export function unquote_objects(body) {
for (let i = 0; i < body.length; i++) {
let alpha = body[i];
if (is_quote(alpha))
body[i] = alpha.obj;
}
}
/**************************************************************/
/* Clauses Lifecycle */
/**************************************************************/
/**
* Create a clause.
*
* @param size The display size.
* @param head The unify Albufeira code.
* @param body The build Albufeira code.
* @param cutvar The choice point variable index or -1.
* @param idxval The index value.
* @constructor The new clause.
*/
export function Clause(size, head, body, cutvar, idxval) {
this.size = size;
this.head = head;
this.body = body;
this.cutvar = cutvar;
this.idxval = idxval;
this.creator = stage;
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.
* @constructor The new logical.
*/
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;
}
/**************************************************************/
/* Knowledgebase */
/**************************************************************/
/**
* Create a provable.
*
* @param flags The flags.
* @constructor The new provable.
*/
export function Provable(flags) {
this.flags = flags;
this.rope = undefined;
this.nonguard = undefined;
this.idxmap = undefined;
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 */
/*********************************************************************/
/**
* Make snapshot of a logical.
*
* @param rope The logical.
* @return array The clause list snapshot.
*/
export function snapshot_data(rope) {
let res = rope.cache;
if (res === null) {
res = new Array(rope.count);
rope_copy(res, rope);
rope.cache = res;
}
return res;
}
function rope_copy(res, rope) {
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;
}
}
/*********************************************************************/
/* 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;
}
/**
* Retrieve a provable by predicate indicator.
*
* @param functor The functor.
* @param arity The arity.
* @return The provable or undefined.
*/
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;
}
/**
* The function returns an anonymous predicate for the given clauses.
*
* @param rope The clauses.
* @return Provable The predicate.
*/
export function make_defined(rope) {
let peek = new Provable(0);
peek.rope = new_rope();
peek.nonguard = new_rope();
peek.idxmap = new Map();
for (let i = 0; i < rope.length; i++)
add_peek(peek, rope[i], MASK_ADD_BOTTOM);
return peek;
}
function new_rope() {
return new Logical(null, 0, new Array(0));
}
/*********************************************************************/
/* Dynamic Predicates */
/*********************************************************************/
import {
make_error, make_indicator, VOID_ARGS
} from "./machine.mjs";
/**
* Create a new dynamic predicate entry.
*
* @param functor The functor.
* @param arity The arity.
* @param flags The flags.
*/
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;
} else {
if (!is_logical(peek.rope))
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)]));
}
}
/*********************************************************************/
/* 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.rope = null;
peek.idxmap = null;
peek.nonguard = null;
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.rope))
rollback_peek(peek);
}
if (trim_arities(temp))
delete kb[functor]
}
}
/**
* Rollback the clauses of a predicate.
*
* @param peek The predicate.
*/
function rollback_peek(peek) {
if (!has_action(peek.rope))
return;
for (let [key,value] of peek.idxmap) {
if (has_action(value)) {
rollback_rope(value, false);
if (value.data.length === 0)
peek.idxmap.delete(key);
}
}
if (has_action(peek.nonguard))
rollback_rope(peek.nonguard, false);
rollback_rope(peek.rope, true);
}
/**
* 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) {
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;
}
/**
* Rollback clauses from a logical.
*
* @param rope The logical.
* @param update The update flag.
*/
function rollback_rope(rope, update) {
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;
}
/*********************************************************************/
/* Clause Addition */
/*********************************************************************/
/**
* Enhance the knowledge base by a claus or predicate.
*
* @param functor The functor.
* @param arity The arity.
* @param clause_or_pred The clause or predicate.
*/
export function add(functor, arity, clause_or_pred) {
add_clause(functor, arity, clause_or_pred, MASK_ADD_BOTTOM);
}
/**
* Enhance the knowledge base by a claus or predicate.
*
* @param functor The functor.
* @param arity The arity.
* @param clause_or_pred The clause or predicate.
* @param flags The flags.
*/
export function add_clause(functor, arity, clause_or_pred, flags) {
let temp = kb[functor];
if (temp === undefined) {
temp = new Array(0);
kb[functor] = temp;
}
let peek = temp[arity];
if (is_clause(clause_or_pred)) {
if (peek === undefined || peek.remover !== undefined) {
let res = make_defined(VOID_ARGS);
if (peek !== undefined)
res.overlay = peek;
if ((flags & MASK_ADD_DYNAMIC) !== 0)
res.flags |= MASK_PRED_DYNAMIC;
temp[arity] = res;
peek = res;
} else {
if (!is_logical(peek.rope))
throw make_error(new Compound("permission_error",
["modify", "static_procedure",
make_indicator(functor, arity)]));
if ((flags & MASK_ADD_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_ADD_DYNAMIC) === 0)
clause_or_pred.shard = engine.partition;
add_peek(peek, clause_or_pred, flags);
} else {
if (peek === undefined || peek.remover !== undefined) {
if (peek !== undefined)
clause_or_pred.overlay = peek;
temp[arity] = clause_or_pred;
} else {
throw make_error(new Compound("permission_error",
["coerce", "procedure", make_indicator(functor, arity)]));
}
}
}
export function add_peek(peek, clause, flags) {
add_rope(peek.rope, clause, flags);
let idxval = clause.idxval;
if (idxval === undefined) {
for (let [key, value] of peek.idxmap)
add_rope(value, clause, flags);
add_rope(peek.nonguard, clause, flags);
} else {
let value = peek.idxmap.get(idxval);
if (value === undefined) {
value = clone_rope(peek.nonguard);
peek.idxmap.set(idxval, value);
}
add_rope(value, clause, flags);
}
}
function add_rope(rope, clause, flags) {
if ((flags & MASK_ADD_BOTTOM) !== 0) {
rope.data.push(clause);
} else {
rope.data.unshift(clause);
}
rope.count++;
rope.cache = null;
}
function clone_rope(rope) {
return new Logical(rope.cache, rope.count, rope.data.slice());
}
/*********************************************************************/
/* Clause Removal */
/*********************************************************************/
/**
* Remove a clause from the knowledge base.
*
* @param functor The functor.
* @param arity The arity.
* @param clause The clause.
* @param flags The flags.
* @return boolean True if the clause was removed, false otherwise.
*/
export function remove_clause(functor, arity, clause, flags) {
if (!is_clause(clause))
throw make_error(new Compound("permission_error",
["coerce", "procedure", make_indicator(functor, arity)]));
let temp = kb[functor];
if (temp === undefined)
return false;
let peek = temp[arity];
if (peek === undefined || peek.remover !== undefined)
return false;
if (!is_logical(peek.rope))
throw make_error(new Compound("permission_error",
["modify", "static_procedure", make_indicator(functor, arity)]));
return remove_peek(peek, clause, flags);
}
function remove_peek(peek, clause, flags) {
if (clause.remover !== undefined)
return false;
if (!remove_rope(peek.rope, clause, flags))
return false;
let idxval = clause.idxval;
if (idxval === undefined) {
for (let [key,value] of peek.idxmap) {
remove_rope(value, clause, flags);
if (value.data.length === 0)
peek.idxmap.delete(key);
}
remove_rope(peek.nonguard, clause, flags);
} else {
let value = peek.idxmap.get(idxval);
remove_rope(value, clause, flags);
if (value.data.length === 0)
peek.idxmap.delete(idxval);
}
clause.remover = stage;
return true;
}
function remove_rope(rope, clause, flags) {
let data = rope.data;
let index;
if ((flags & MASK_REMOVE_BOTTOM) === 0) {
index = data.indexOf(clause);
} else {
index = data.lastIndexOf(clause);
}
if (index === -1)
return false;
if (clause.creator === stage)
data.splice(index, 1);
rope.count--;
rope.cache = null;
return true;
}
/**************************************************************/
/* Variable & Compound */
/**************************************************************/