package nova;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.NoSuchFileException;
import java.time.DayOfWeek;
import java.time.Month;
import java.time.format.TextStyle;
import java.util.*;
import java.util.function.BiFunction;
/**
* 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 theatre {
/**
* https://www.baeldung.com/java-sneaky-throws
*/
static <E extends Throwable> void sneaky(Throwable x) throws E {
throw (E) x;
}
/**************************************************************/
/* Core API */
/**************************************************************/
/**
* Initialize the Novacore Prolog system subset.
*
* @param args The program args.
* @throws IOException Shit happens.
* @throws URISyntaxException Shith appens.
*/
public static void core(String[] args) throws IOException, URISyntaxException {
special.set_args(args);
special.boot();
runtime.boot();
eval.boot();
boot();
Bootload.boot9();
Bootload.boot11();
Bootload.boot2();
Bootload.boot4();
Bootload.boot8();
Bootload.boot10();
Streams.boot5();
Streams.boot6();
Streams.boot7();
String[] classpath = findRoots(runtime.class.getClassLoader());
special.set_classpath(classpath);
String bootbase = (classpath.length > 0 ? classpath[0] : "");
runtime.set_bootbase(bootbase);
}
private static final String MARKER = "root_marker.properties";
/**
* Find the roots.
*
* @param loader The loader.
* @return The roots.
* @throws IOException Shit happens.
*/
public static String[] findRoots(ClassLoader loader)
throws IOException, URISyntaxException {
ArrayList list = new ArrayList();
Enumeration it = loader.getResources(MARKER);
while (it.hasMoreElements()) {
String path = it.nextElement().toString();
path = path.substring(0, path.length() - MARKER.length());
list.add(minifyURI(path));
}
String[] res = new String[list.size()];
list.toArray(res);
return res;
}
public static String minifyURI(String adr) throws URISyntaxException {
URI uri = new URI(adr);
if ("file".equals(uri.getScheme()))
return new File(uri).toString() + File.separatorChar;
return adr;
}
/**************************************************************/
/* Main API */
/**************************************************************/
/**
* Post a message to the Prolog interpreter.
*
* @param message The message.
*/
public static void post(Object message) {
Machine.register_signal(Machine.CTX_MAIN, message);
Machine.invoke_interrupt(Machine.CTX_MAIN);
}
/**
* 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.
*/
public static Object perform(Object goal) {
goal = new Store.Compound(".", new Object[]{goal, "[]"});
return Machine.launch(goal, Machine.CTX_MAIN, Machine.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.
*/
public static boolean perform_async(Object goal) {
goal = new Store.Compound(".", new Object[]{goal, "[]"});
return Machine.launch_async(goal, Machine.CTX_MAIN, Machine.VOID_ARGS);
}
/**************************************************************/
/* Register API */
/**************************************************************/
/**
* The flag indicates a non-void dispatch foreign function.
*/
public static int FFI_FUNC = 0x00000100;
/**
* Add a Java function to the knowledge base. The given
* function is invoked with rather slow argument copying. Also
* the function is wrapped and registered as a special, therefore
* not eligible to neck optimization.
*
* @param functor The functor.
* @param arity The arity.
* @param func The Java function.
* @param flags The flags.
*/
public static void register(String functor, int arity, Handler.Builtin func, int flags) {
Store.Provable res;
if ((flags & FFI_FUNC) != 0) {
res = special.make_check((args) -> invoke_func(func, args));
} else {
res = special.make_check((args) -> invoke(func, args));
}
Store.set(functor, arity, res);
}
private static boolean invoke(Handler.Builtin func, Object[] args) {
Object[] temp = new Object[args.length];
for (int i = 0; i < temp.length; i++)
temp[i] = Machine.exec_build(args[i]);
func.run(temp);
return true;
}
private static boolean invoke_func(Handler.Builtin func, Object[] args) {
Object[] temp = new Object[args.length - 1];
for (int i = 0; i < temp.length; i++)
temp[i] = Machine.exec_build(args[i]);
Object res = func.run(temp);
return Machine.exec_unify(args[args.length - 1], res);
}
/*****************************************************************/
/* ir_object_new/1 and ir_object_current/3 */
/*****************************************************************/
/**
* ir_object_new(O):
* The predicate succeeds in O with a new Java object.
*/
private static boolean test_ir_object_new(Object[] args) {
Map res = new HashMap();
return Machine.exec_unify(args[0], res);
}
/**
* ir_object_current(O, K, V):
* The predicate succeeds in V with the value of the key K in the Java object O.
*/
private static boolean test_ir_object_current(Object[] args) {
Object obj = Machine.exec_build(args[0]);
Object key = Machine.exec_build(args[1]);
special.check_atom(key);
Object res;
if (obj instanceof Map) {
res = ((Map) obj).getOrDefault(key, Store.UNDEF_OBJ);
} else {
try {
Field field = obj.getClass().getField((String)key);
res = field.get(obj);
} catch (NullPointerException | NoSuchFieldException |
IllegalAccessException e) {
res = Store.UNDEF_OBJ;
}
}
if (res == Store.UNDEF_OBJ)
return false;
return Machine.exec_unify(args[2], res);
}
/*****************************************************************/
/* ir_object_set/3, ir_object_reset/2 and ir_object_keys/2 */
/*****************************************************************/
/**
* ir_object_set(O, K, V):
* The predicate sets the value of the key K in the Java object O to V.
*/
private static boolean test_ir_object_set(Object[] args) {
Object obj = Machine.exec_build(args[0]);
Object key = Machine.exec_build(args[1]);
special.check_atom(key);
Object value = Machine.exec_build(args[2]);
if (obj instanceof Map) {
((Map) obj).put(key, value);
} else {
try {
Field field = obj.getClass().getField((String)key);
field.set(obj, value);
} catch (NullPointerException | NoSuchFieldException |
IllegalAccessException e) {
throw Machine.make_error(new Store.Compound("permission_error",
new Object[]{"modify", "field", key}));
}
}
return true;
}
/**
* ir_object_reset(O, K):
* The predicate removes the key K from the Java object O.
*/
private static boolean test_ir_object_reset(Object[] args) {
Object obj = Machine.exec_build(args[0]);
Object key = Machine.exec_build(args[1]);
special.check_atom(key);
if (obj instanceof Map) {
((Map) obj).remove(key);
} else {
throw Machine.make_error(new Store.Compound("permission_error",
new Object[]{"modify", "field", key}));
}
return true;
}
/**
* ir_object_keys(O, L):
* The predicate succeeds in L with the keys of the Java object O.
*/
private static boolean test_ir_object_keys(Object[] args) {
Object obj = Machine.exec_build(args[0]);
if (obj instanceof Map) {
obj = ((Map) obj).keySet();
} else {
Field[] fields = obj.getClass().getFields();
obj = new FieldNames(fields);
}
obj = set_to_list((Collection)obj);
return Machine.exec_unify(args[1], obj);
}
private static class FieldNames extends AbstractList<Object> {
private final Field[] fields;
public FieldNames(Field[] fields) {
this.fields = fields;
}
public Object get(int index) {
return fields[index].getName();
}
public int size() {
return fields.length;
}
}
/**
* Convert a native set into a Prolog list.
*
* @param temp The native set.
*/
public static Object set_to_list(Collection temp) {
Store.Structure back = null;
Object res = null;
if (temp != null) {
Iterator it = temp.iterator();
while (it.hasNext()) {
Object value = it.next();
Store.Structure peek = new Store.Compound(".",
new Object[]{value, Store.UNDEF_OBJ});
if (back == null) {
res = peek;
} else {
back.args[1] = peek;
}
back = peek;
}
}
if (back == null) {
res = "[]";
} else {
back.args[1] = "[]";
}
return res;
}
/******************************************************************/
/* ir_float_value/2 */
/******************************************************************/
/**
* ir_float_value(P, H):
* If P is instantiated, then the predicate succeeds in H with the
* Java float of P, otherwise in P with the Prolog float of H.
*/
private static boolean test_ir_float_value(Object[] args) {
Object alpha = Machine.exec_deref(args[0]);
if (Store.is_variable(alpha) || Machine.is_pending(alpha)) {
Object beta = Machine.exec_build(args[1]);
special.check_number(beta);
Object res = (Machine.is_special(beta) ? beta :
special.norm_float(special.narrow_float((Number) beta)));
return Machine.exec_unify(alpha, res);
} else {
alpha = Machine.exec_build(alpha);
special.check_number(alpha);
Object res = (Machine.is_special(alpha) ? alpha :
Double.valueOf(special.narrow_float((Number) alpha)));
return Machine.exec_unify(args[1], res);
}
}
/**************************************************************/
/* os_call_later/3 and timer_cancel/1 */
/**************************************************************/
private static Timer timer = null;
private static Timer getTimer() {
if (timer == null)
timer = new Timer(true);
return timer;
}
/**
* os_call_later(N, D, T): internal only
* The predicate succeeds in T with a new timer. As a side
* effect it schedules the compiled goal N to be executed
* after D milliseconds.
*/
private static boolean test_os_call_later(Object[] args) {
Object goal = Machine.exec_build(args[0]);
special.check_goal(goal);
Object obj = Machine.exec_build(args[1]);
special.check_integer(obj);
if (eval.integer_signum((Number) obj) < 0)
throw Machine.make_error(new Store.Compound("domain_error",
new Object[]{"not_less_than_zero", obj}));
if (!runtime.is_long((Number) obj))
throw Machine.make_error(new Store.Compound("representation_error",
new Object[]{"long", obj}));
long delay = ((Number) obj).longValue();
Object buf = Machine.ctx;
TimerTask tid = setDelay(() ->
Machine.callback(goal, buf, Machine.VOID_ARGS), delay);
return Machine.exec_unify(args[2], tid);
}
/**
* timer_cancel(T):
* The predicate succeeds. As a side effect it cancels the timer T.
*/
private static boolean test_timer_cancel(Object[] args) {
Object obj = Machine.exec_build(args[0]);
check_timer(obj);
((TimerTask) obj).cancel();
return true;
}
public static void check_timer(Object beta) {
if (!(beta instanceof TimerTask)) {
Store.check_nonvar(beta);
beta = Store.copy_term(beta);
throw Machine.make_error(new Store.Compound("type_error",
new Object[]{"timer", beta}));
}
}
/**
* Delay a coroutine by some milliseconds.
*
* @param proc The coroutine.
* @param delay The milliseconds.
* @return The timer.
*/
public static TimerTask setDelay(Runnable proc, long delay) {
TimerTask tid = new Later(proc);
Timer timer = getTimer();
timer.schedule(tid, delay);
return tid;
}
private static class Later extends TimerTask {
Runnable proc;
public Later(Runnable proc) {
this.proc = proc;
}
public void run() {
new Handler.Coroutine(() -> proc.run()).async();
}
}
/**************************************************************/
/* current_task/1, task_abort/2 and os_task_create/2 */
/**************************************************************/
/**
* current_task(E):
* The predicate succeeds in E with the current task.
*/
private static boolean test_current_task(Object[] args) {
return Machine.exec_unify(args[0], Machine.ctx);
}
/**
* task_abort(E, M):
* The predicate succeeds. As a side effect the task E gets
* the message M signalled.
*/
private static boolean test_task_abort(Object[] args) {
Object buf = Machine.exec_build(args[0]);
check_task(buf);
Object msg = Machine.exec_build(args[1]);
msg = Store.copy_term(msg);
Machine.register_signal(buf, msg);
Machine.invoke_interrupt(buf);
return true;
}
public static void check_task(Object beta) {
if (!Machine.CTX_MAIN.equals(beta) &&
!(beta instanceof Machine.Context)) {
Store.check_nonvar(beta);
beta = Store.copy_term(beta);
throw Machine.make_error(new Store.Compound("type_error",
new Object[]{"task", beta}));
}
}
/**
* os_task_create(N, E): internal only
* The predicate succeeds in E with a new task for the compiled
* goal N. The task is scheduled to execute immediately.
*/
private static boolean test_os_task_create(Object[] args) {
Object goal = Machine.exec_build(args[0]);
special.check_goal(goal);
Machine.Context buf = new Machine.Context();
setDelay(() -> Machine.task_async(goal, buf, Machine.VOID_ARGS), 0);
return Machine.exec_unify(args[1], buf);
}
/******************************************************************/
/* net_open_promise/4 */
/******************************************************************/
private static HttpClient client = null;
public static HttpClient getHttpClient() {
if (client == null)
client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
return client;
}
/**
* net_open_promise(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_net_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]);
runtime.Source stream = new runtime.Source();
if (!Machine.exec_unify(args[2], stream))
return false;
Object buf = Machine.ctx;
Handler.Promise prom = open_http_promise_opts(buf, stream, url, opts);
return Machine.exec_unify(args[3], prom);
}
private static Handler.Promise open_http_promise_opts(Object buf,
runtime.Source stream, String url, Map opts) {
return new Handler.Promise(() -> {
Thread self = Thread.currentThread();
Machine.register_interrupt(buf, self::interrupt);
try {
String method;
HttpRequest.BodyPublisher body;
if (opts != null && opts.get("method") != null) {
method = opts.get("method").toString();
} else if (opts != null && opts.get("body") != null) {
method = "POST";
} else {
method = "GET";
}
if (opts != null && opts.get("body") != null) {
Map opts2 = (Map) opts.get("body");
body = HttpRequest.BodyPublishers.ofString(
opts2.get("text").toString(),
runtime.get_encoding(opts2));
} else {
body = HttpRequest.BodyPublishers.noBody();
}
HttpRequest.Builder builder = HttpRequest.newBuilder().method(method, body);
if (opts != null && opts.get("headers") != null) {
Map headers = (Map) opts.get("headers");
Iterator it = headers.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
builder = builder.header((String)entry.getKey(), entry.getValue().toString());
}
}
HttpClient client = getHttpClient();
HttpResponse<InputStream> response;
try {
response = client.send(builder.uri(URI.create(url)).build(),
HttpResponse.BodyHandlers.ofInputStream());
} catch (IOException err) {
Machine.register_signal(buf, runtime.map_file_error(err, url));
return;
}
if (response.statusCode() < 200 || 399 < response.statusCode()) {
Machine.register_signal(buf, map_http_result(response.statusCode(), url));
} else {
if (opts != null) {
opts.put("uri", response.uri().toString());
opts.put("status", Integer.valueOf(response.statusCode()));
Map res = new HashMap();
Map<String,List<String>> map = response.headers().map();
Iterator it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
res.put(entry.getKey(), ((List)entry.getValue()).get(0));
}
opts.put("fields", res);
}
InputStream ins = response.body();
stream.data = new InputStreamReader(ins, runtime.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 |= runtime.MASK_SRC_AREAD;
}
} catch (InterruptedException x) {
/* */
} finally {
Machine.register_interrupt(buf, () -> {
});
}
});
}
public static Object map_http_result(int res, String url) {
switch (res) {
case 403: // Forbidden
return new Store.Compound("permission_error",
new Object[]{"open", "source_sink", url});
case 404: // Not Found
return new Store.Compound("existence_error",
new Object[]{"source_sink", url});
case 405: // Method Not Allowed
return new Store.Compound("resource_error",
new Object[]{"illegal_method"});
case 500: // Internal Server Error
return new Store.Compound("resource_error",
new Object[]{"internal_error"});
case 503: // Service Unavailable
return new Store.Compound("resource_error",
new Object[]{"service_unavailable"});
default:
return new Store.Compound("resource_error",
new Object[]{"io_exception"});
}
}
/*******************************************************************/
/* net_prop_promise/3 */
/*******************************************************************/
private static final String HTTP_TIME = "%a, %d %b %Y %H:%M:%S GMT";
/**
* net_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_net_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_http_promise(buf, url, res);
return Machine.exec_unify(args[2], prom);
}
public static Handler.Promise prop_http_promise(Object buf, String url, Map res) {
return new Handler.Promise(() -> {
Thread self = Thread.currentThread();
Machine.register_interrupt(buf, self::interrupt);
try {
HttpRequest.BodyPublisher body = HttpRequest.BodyPublishers.noBody();
String method = "HEAD";
HttpRequest.Builder builder = HttpRequest.newBuilder().method(method, body);
HttpClient client = theatre.getHttpClient();
HttpResponse<Void> response;
try {
response = client.send(builder.uri(URI.create(url)).build(),
HttpResponse.BodyHandlers.discarding());
} catch (IOException err) {
Machine.register_signal(buf, runtime.map_file_error(err, url));
return;
}
if (response.statusCode() != 200) {
Machine.register_signal(buf, theatre.map_http_result(response.statusCode(), url));
} else {
Optional<String> val = response.headers().firstValue("last-modified");
long mtime = (val.isPresent() ? sys_time_parse(
val.get(), HTTP_TIME, 1) : -1);
res.put("last_modified", special.norm_smallint(mtime));
res.put("absolute_path", response.uri().toString());
res.put("type", "regular");
res.put("can_write", "false");
}
} catch (InterruptedException x) {
/* */
} finally {
Machine.register_interrupt(buf, () -> {});
}
});
}
/******************************************************************/
/* net_open_sync/4 and net_cntrl_sync/2 */
/******************************************************************/
/**
* net_open_sync(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_net_open_sync(Object[] args) {
Object obj = Machine.exec_build(args[0]);
special.check_atom(obj);
String url = (String) obj;
throw Machine.make_error(new Store.Compound("permission_error",
new Object[]{"access", "source_sink", url}));
}
/**
* net_cntrl_sync(F, P):
* The predicate assigns the property P to the file F.
*/
private static boolean test_net_cntrl_sync(Object[] args) {
Object obj = Machine.exec_build(args[0]);
special.check_atom(obj);
String url = (String) obj;
throw Machine.make_error(new Store.Compound("permission_error",
new Object[]{"access", "source_sink", url}));
}
/******************************************************************/
/* sys_atom_time/4 */
/******************************************************************/
/**
* sys_atom_time(A, F, O, T): internal only
* If A is a variable, the built-in succeeds in A with the millisecond
* time T formatted by the pattern F and the flag O. Otherwise the
* built-in succeeds in T with the millisecond time parsed from A
* by the pattern F and the flag O.
*/
private static boolean test_sys_atom_time(Object[] args) {
Object fmt = Machine.exec_build(args[1]);
special.check_atom(fmt);
Object flag = Machine.exec_build(args[2]);
special.check_integer(flag);
Object text = Machine.exec_deref(args[0]);
if (Store.is_variable(text) || Machine.is_pending(text)) {
Object obj = Machine.exec_build(args[3]);
special.check_integer(obj);
if (!runtime.is_long((Number) obj))
throw Machine.make_error(new Store.Compound("representation_error",
new Object[]{"long", obj}));
long tms = ((Number) obj).longValue();
String res;
try {
res = sys_time_format((String) fmt, tms, ((Number) flag).intValue());
} catch (IllegalArgumentException x) {
throw Machine.make_error(new Store.Compound("domain_error",
new Object[]{"time_format", fmt}));
}
return Machine.exec_unify(text, res);
} else {
text = Machine.exec_build(text);
special.check_atom(text);
long tms;
try {
tms = sys_time_parse((String) text, (String) fmt, ((Number) flag).intValue());
} catch (NumberFormatException x) {
throw Machine.make_error(new Store.Compound("syntax_error",
new Object[]{"illegal_date"}));
} catch (IllegalArgumentException x) {
throw Machine.make_error(new Store.Compound("domain_error",
new Object[]{"time_format", fmt}));
}
return Machine.exec_unify(args[3], special.norm_smallint(tms));
}
}
/**
* Parse a date time string.
*
* @param res The date time as a string.
* @param pat The pattern.
* @param utc The UTC calendar flag.
* @return The date time in UTC milliseconds.
*/
public static long sys_time_parse(String res, String pat, int utc) {
int year = 1900; int month = 1; int day = 1;
int hour = 0; int minute = 0; int second = 0;
int j = 0;
for (int i = 0; i < pat.length(); i++) {
char ch = pat.charAt(i);
if (ch == '%') {
i++;
int k;
switch (pat.charAt(i)) {
case '%':
if (j < res.length() && res.charAt(j) == '%') {
j++;
} else {
throw new NumberFormatException("illegal date");
}
break;
case 'a':
k = scan_alphanum(res, j);
int weekday = parse_weekday(res.substring(j, k),
(utc != 0 ? Locale.US : Locale.getDefault()));
j = k;
break;
case 'b':
k = scan_alphanum(res, j);
month = parse_month(res.substring(j, k),
(utc != 0 ? Locale.US : Locale.getDefault()));
j = k;
break;
case 'd':
k = scan_integer(res, j);
day = Integer.parseInt(res.substring(j, k));
j = k;
break;
case 'm':
k = scan_integer(res, j);
month = Integer.parseInt(res.substring(j, k));
j = k;
break;
case 'Y':
k = scan_integer(res, j);
year = Integer.parseInt(res.substring(j, k));
j = k;
break;
case 'H':
k = scan_integer(res, j);
hour = Integer.parseInt(res.substring(j, k));
j = k;
break;
case 'M':
k = scan_integer(res, j);
minute = Integer.parseInt(res.substring(j, k));
j = k;
break;
case 'S':
k = scan_integer(res, j);
second = Integer.parseInt(res.substring(j, k));
j = k;
break;
default:
throw new IllegalArgumentException("illegal format");
}
} else {
if (j < res.length() && res.charAt(j) == ch) {
j++;
} else {
throw new NumberFormatException("illegal date");
}
}
}
Calendar cal;
if (utc != 0) {
cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
} else {
cal = Calendar.getInstance();
}
cal.set(year, month - 1, day, hour, minute, second);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTimeInMillis();
}
/**
* Scan alphanum.
*
* @param res The text.
* @param j The old position.
* @return The new position.
*/
private static int scan_alphanum(String res, int j) {
while (j < res.length() && Character.isLetterOrDigit(res.charAt(j)))
j++;
return j;
}
/**
* Parse a week day.
*
* @param str The text.
* @param locale The locale to use, not null
* @return The index.
*/
private static int parse_weekday(String str, Locale locale) {
str = str.toLowerCase();
for (int i = 1; i < 8; i++) {
DayOfWeek wday = DayOfWeek.of(i);
String val = wday.getDisplayName(TextStyle.SHORT_STANDALONE, locale);
val = val.toLowerCase();
if (str.equals(val))
return i;
}
throw new NumberFormatException("illegal date");
}
/**
* Parse a month.
*
* @param str The text.
* @param locale The locale to use, not null
* @return The index.
*/
private static int parse_month(String str, Locale locale) {
str = str.toLowerCase();
for (int i = 1; i < 13; i++) {
Month mon = Month.of(i);
String val = mon.getDisplayName(TextStyle.SHORT_STANDALONE, locale);
val = val.toLowerCase();
if (str.equals(val))
return i;
}
throw new NumberFormatException("illegal date");
}
/**
* Scan integer.
*
* @param res The text.
* @param j The old position.
* @return The new position.
*/
private static int scan_integer(String res, int j) {
while (j < res.length() && Character.isDigit(res.charAt(j)))
j++;
return j;
}
/**
* Format a date time string.
*
* @param pat The format pattern.
* @param tms The date time in UTC milliseconds.
* @param utc The UTC calendar flag.
* @return The date time as a string.
*/
public static String sys_time_format(String pat, long tms, int utc) {
Calendar cal;
if (utc != 0) {
cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
} else {
cal = Calendar.getInstance();
}
cal.setTimeInMillis(tms);
StringBuilder buf = new StringBuilder();
int k = 0;
for (int i = 0; i < pat.length(); i++) {
char ch = pat.charAt(i);
if (ch == '%') {
buf.append(pat, k, i);
i++;
if (i < pat.length()) {
switch (pat.charAt(i)) {
case '%':
buf.append('%');
break;
case 'a':
int j = cal.get(Calendar.DAY_OF_WEEK);
DayOfWeek wday = DayOfWeek.of((j != 1 ? j - 1 : 7));
buf.append(wday.getDisplayName(TextStyle.SHORT_STANDALONE,
(utc != 0 ? Locale.US : Locale.getDefault())));
break;
case 'b':
Month mon = Month.of(cal.get(Calendar.MONTH) + 1);
buf.append(mon.getDisplayName(TextStyle.SHORT_STANDALONE,
(utc != 0 ? Locale.US : Locale.getDefault())));
break;
case 'd':
append_padded(buf, cal.get(Calendar.DAY_OF_MONTH));
break;
case 'm':
append_padded(buf, cal.get(Calendar.MONTH) + 1);
break;
case 'Y':
buf.append(cal.get(Calendar.YEAR));
break;
case 'H':
append_padded(buf, cal.get(Calendar.HOUR_OF_DAY));
break;
case 'M':
append_padded(buf, cal.get(Calendar.MINUTE));
break;
case 'S':
append_padded(buf, cal.get(Calendar.SECOND));
break;
default:
throw new IllegalArgumentException("illegal format");
}
k = i + 1;
} else {
k = i;
}
}
}
buf.append(pat, k, pat.length());
return buf.toString();
}
/**
* Append an integer value zero padded.
*
* @param buf The string builder.
* @param value The integer value.
*/
private static void append_padded(StringBuilder buf, int value) {
if (value < 10)
buf.append('0');
buf.append(value);
}
/******************************************************************/
/* os_get_gestalt/1 and os_get_host/1 */
/******************************************************************/
/**
* os_get_gestalt(M): Internal only
* The built-in succeeds in M with a gestalt properties map.
*/
private static boolean test_os_get_gestalt(Object[] args) {
Map gestalt = new HashMap();
String scm = System.getenv("TERM_SCHEME");
gestalt.put("sys_color", (scm != null ? scm : "normal"));
Locale loc = Locale.getDefault();
gestalt.put("sys_locale", loc.toString());
Object res = special.objects_list(special.main_args, 0, special.main_args.length);
gestalt.put("argv", res);
return Machine.exec_unify(args[0], gestalt);
}
/**
* os_get_host(M): internal only
* The built-in succeeds in W with a host properties map.
*/
private static boolean test_os_get_host(Object[] args) {
Map res = new HashMap();
res.put("system_url", runtime.bootbase);
res.put("foreign_ext", special.NAT_EXT);
res.put("host_info", System.getProperty("java.vendor")+", Java "+System.getProperty("java.version"));
res.put("mach_info", System.getProperty("os.arch")+", "+System.getProperty("os.name"));
return Machine.exec_unify(args[0], res);
}
/******************************************************************/
/* os_get_workdir/1 and os_set_workdir/1 */
/******************************************************************/
/**
* os_get_workdir(D): internal only
* The built-in succeeds in D with the working directory.
*/
private static boolean test_os_get_workdir(Object[] args) {
Object url = runtime.codebase;
return Machine.exec_unify(args[0], url);
}
/**
* os_set_workdir(D): internal only
* The built-in succeeds. As a side effect it changes the working directory to D.
*/
private static boolean test_os_set_workdir(Object[] args) {
Object obj = Machine.exec_build(args[0]);
special.check_atom(obj);
String url = (String)obj;
File file = runtime.findWrite(runtime.codebase, url);
try {
if (!file.exists())
throw new NoSuchFileException("");
if (!file.isDirectory())
throw new IOException("");
} catch (IOException err) {
throw Machine.make_error(runtime.map_file_error(err, url));
}
runtime.set_codebase(file.getPath() + File.separatorChar);
return true;
}
/**************************************************************/
/* dg_get_flags/1, dg_get_partition/1 and dg_set_partition/1 */
/**************************************************************/
/**
* dg_get_flags(W): internal only
* The built-in succeeds in W with the engine flags.
*/
private static boolean test_dg_get_flags(Object[] args) {
return Machine.exec_unify(args[0], Integer.valueOf(Store.engine.flags));
}
/**
* dg_get_partition(D): internal only
* The built-in succeeds in D with the current stage.
*/
private static boolean test_dg_get_partition(Object[] args) {
return Machine.exec_unify(args[0], Store.engine.partition);
}
/**
* dg_set_partition(D): internal only
* The built-in succeeds. As a side effect it changes the current partition.
*/
private static boolean test_dg_set_partition(Object[] args) {
Object value = Machine.exec_build(args[0]);
special.check_atom(value);
Store.set_partition((String) value);
return true;
}
/**************************************************************/
/* dg_clear_stage/0, dg_get_stage/1 and dg_set_stage/1 */
/**************************************************************/
/**
* dg_clear_stage: internal only
* The built-in succeeds. As a side effect it clears the current stage.
*/
private static boolean test_dg_clear_stage(Object[] args) {
Store.clear();
return true;
}
/**
* dg_get_stage(D): internal only
* The built-in succeeds in D with the current stage.
*/
private static boolean test_dg_get_stage(Object[] args) {
return Machine.exec_unify(args[0], Integer.valueOf(Store.stage));
}
/**
* dg_set_stage(D): internal only
* The built-in succeeds. As a side effect it changes the current stage.
*/
private static boolean test_dg_set_stage(Object[] args) {
Object value = Machine.exec_build(args[0]);
special.check_integer(value);
Store.set_stage(((Number) value).intValue());
return true;
}
/******************************************************************/
/* Theatre Init */
/******************************************************************/
public static void boot() {
// object specials
Store.set("ir_object_new", 1, special.make_check(theatre::test_ir_object_new));
Store.set("ir_object_current", 3, special.make_check(theatre::test_ir_object_current));
Store.set("ir_object_set", 3, special.make_check(theatre::test_ir_object_set));
Store.set("ir_object_reset", 2, special.make_check(theatre::test_ir_object_reset));
Store.set("ir_object_keys", 2, special.make_check(theatre::test_ir_object_keys));
// object specials II
Store.set("ir_float_value", 2, special.make_check(theatre::test_ir_float_value));
// system specials, coroutines
Store.set("os_call_later", 3, special.make_check(theatre::test_os_call_later));
Store.set("timer_cancel", 1, special.make_check(theatre::test_timer_cancel));
Store.set("current_task", 1, special.make_check(theatre::test_current_task));
Store.set("task_abort", 2, special.make_check(theatre::test_task_abort));
Store.set("os_task_create", 2, special.make_check(theatre::test_os_task_create));
// net specials, async
Store.set("net_open_promise", 4, special.make_check(theatre::test_net_open_promise));
Store.set("net_prop_promise", 3, special.make_check(theatre::test_net_prop_promise));
// net specials, sync
Store.set("net_open_sync", 4, special.make_check(theatre::test_net_open_sync));
Store.set("net_cntrl_sync", 2, special.make_check(theatre::test_net_cntrl_sync));
// atom specials, time
Store.set("sys_atom_time", 4, special.make_check(theatre::test_sys_atom_time));
// object specials, gestalt
Store.set("os_get_gestalt", 1, special.make_check(theatre::test_os_get_gestalt));
Store.set("os_get_host", 1, special.make_check(theatre::test_os_get_host));
Store.set("os_get_workdir", 1, special.make_check(theatre::test_os_get_workdir));
Store.set("os_set_workdir", 1, special.make_check(theatre::test_os_set_workdir));
// system specials, staging, internal only
Store.set("dg_get_flags", 1, special.make_check(theatre::test_dg_get_flags));
Store.set("dg_get_partition", 1, special.make_check(theatre::test_dg_get_partition));
Store.set("dg_set_partition", 1, special.make_check(theatre::test_dg_set_partition));
Store.set("dg_clear_stage", 0, special.make_check(theatre::test_dg_clear_stage));
Store.set("dg_get_stage", 1, special.make_check(theatre::test_dg_get_stage));
Store.set("dg_set_stage", 1, special.make_check(theatre::test_dg_set_stage));
}
}