JavaScript "index"

Admin User, created Mar 06. 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.
*/
import {
set_stage, add, set_partition, engine, Compound
} from "./nova/store.mjs";
import {
deref, unify, cont, call, VOID_ARGS, invoke_interrupt,
register_signal, fs, launch, launch_async, url, make_error
} from "./nova/machine.mjs";
import {check_atom, make_special
} from "./nova/special.mjs";
import {
bootbase, stream_flush, Source, set_bootbase, Sink, set_codebase,
fiddle_out, fiddle_err, MASK_SRC_AREAD, map_stream_error
} from "./nova/runtime.mjs";
import "./nova/eval.mjs";
import "./bootload.mjs";
import "./streams.mjs";
/**************************************************************/
/* Main API */
/**************************************************************/
/**
* Initializes the Prolog system.
*/
export function init() {
set_stage(0);
set_partition("user");
}
/**
* Include the given text into the current stage.
*
* @param text The text.
*/
export async function consult_async(text) {
check_atom(text);
let back_input = engine.text_input;
let src = new Source();
src.buf = text;
engine.text_input = src;
try {
await perform_async(new Compound("ensure_loaded", ["user"]));
} finally {
stream_flush(engine.text_error);
stream_flush(engine.text_output);
engine.text_input = back_input;
}
}
/**
* Query the input stream into the current stage.
*/
export async function toplevel_async() {
await perform_async("sys_launch");
}
/**
* Post a message to the Prolog interpreter.
*
* @param message The message.
*/
export function post(message) {
register_signal("main", message);
invoke_interrupt("main");
}
/**
* Writes the Prolog term to the error stream.
*
* @param term The Prolog term.
*/
export function show(term) {
perform(new Compound("sys_print_error", [term]));
}
/**************************************************************/
/* Foreign Function */
/**************************************************************/
/**
* The flag indicates a non-void dispatch foreign function.
*/
export const FFI_FUNC = 0x00000100;
/**
* Add a JavaScript function to the knowledge base. The
* registration is staged, so clear() will remove.
*
* @param functor The functor.
* @param arity The arity.
* @param func The JavaScript function.
* @param flags The flags.
*/
export function register(functor, arity, func, flags) {
let res;
if ((flags & FFI_FUNC) !== 0) {
res = make_special((args) => invoke_func(func, args));
} else {
res = make_special((args) => invoke(func, args));
}
add(functor, arity, res);
}
function invoke(func, args) {
let temp = new Array(args.length);
for (let i = 0; i < temp.length; i++)
temp[i] = deref(args[i]);
func(...temp);
cont(call.args[1]);
return true;
}
function invoke_func(func, args) {
let temp = new Array(args.length-1);
for (let i = 0; i < temp.length; i++)
temp[i] = deref(args[i]);
temp = func(...temp);
if (!unify(args[args.length-1], temp))
return false;
cont(call.args[1]);
return true;
}
/**
* Run a Prolog term once. The goal is run with auto-yield
* disabled and promises are not accepted.
*
* @param goal The Prolog term.
* @return boolean True or false.
*/
export function perform(goal) {
goal = new Compound(".", [goal, "[]"]);
return launch(goal, "main", VOID_ARGS);
}
/**
* Run a Prolog term once. The goal is run with auto-yield
* enabled and promises are accepted.
*
* @param goal The Prolog term.
* @return boolean True or false.
*/
export async function perform_async(goal) {
goal = new Compound(".", [goal, "[]"]);
return await launch_async(goal, "main", VOID_ARGS);
}
/**************************************************************/
/* Plain Console */
/**************************************************************/
function plain_out(data, buf) {
try {
fs.writeSync(data, buf);
} catch (err) {
throw make_error(map_stream_error(err));
}
}
function plain_err(data, buf) {
try {
fs.writeSync(data, buf);
} catch (err) {
throw make_error(map_stream_error(err));
}
}
// See also:
// https://github.com/Financial-Times/ebi/issues/86#issuecomment-487510849
function console_promise(buf, stream) {
return new Promise(resolve => {
let bytes = Buffer.alloc(1024);
fs.read(stream.data, bytes, (err, len, bytes) => {
if (err === null) {
stream.buf = bytes.toString("utf8", 0, len);
stream.pos = 0;
} else {
register_signal(buf, map_stream_error(err));
}
resolve();
});
});
}
/**************************************************************/
/* Color Console */
/**************************************************************/
const ANSI_RESET = "\x1B[0m";
const ANSI_INPUT = "\x1B[92m"; // Intense Green
const ANSI_ERROR = "\x1B[91m"; // Intense Red
function ansi_out(data, buf) {
try {
fs.writeSync(data, ANSI_RESET);
fs.writeSync(data, buf);
fs.writeSync(data, ANSI_INPUT);
} catch (err) {
throw make_error(map_stream_error(err));
}
}
function ansi_err(data, buf) {
try {
fs.writeSync(data, ANSI_ERROR);
fs.writeSync(data, buf);
fs.writeSync(data, ANSI_INPUT);
} catch (err) {
throw make_error(map_stream_error(err));
}
}
/**************************************************************/
/* When Main or Browser */
/**************************************************************/
function ctrlc_abort() {
post(new Compound("system_error", ["user_abort"]));
}
/**
* Simply colored process standard input/output.
*/
async function dogelog_async() {
init();
process.on("SIGINT", ctrlc_abort);
if (process.argv.length > 2) {
await toplevel_async();
} else {
fs.writeSync(process.stdout.fd, ANSI_INPUT);
let dst = new Sink();
dst.data = process.stdout.fd;
dst.send = ansi_out;
engine.text_output = dst;
dst = new Sink();
dst.data = process.stderr.fd;
dst.send = ansi_err;
engine.text_error = dst;
await toplevel_async();
fs.writeSync(process.stdout.fd, ANSI_RESET);
}
}
if (fs !== undefined) {
set_bootbase(url.fileURLToPath(import.meta.url));
let dst = new Sink();
dst.data = process.stdout.fd;
dst.send = plain_out;
engine.text_output = dst;
dst = new Sink();
dst.data = process.stderr.fd;
dst.send = plain_err;
engine.text_error = dst;
// engine.text_input = new Source("", console_in, (data) => {}, 0);
let src = new Source();
src.data = 0;
src.receive = console_promise;
src.flags |= MASK_SRC_AREAD;
engine.text_input = src;
if (bootbase === process.argv[1])
(async () => { await dogelog_async() })();
} else {
set_bootbase(import.meta.url);
let dst = new Sink();
dst.data = document.body;
dst.send = fiddle_out;
engine.text_output = dst;
dst = new Sink();
dst.data = document.body;
dst.send = fiddle_err;
engine.text_error = dst;
if (self.document !== undefined) {
set_codebase(document.URL);
}
}