955 lines
26 KiB
Plaintext
955 lines
26 KiB
Plaintext
/*
|
|
|
|
LSLisp 0.2
|
|
Implementation copyright 2018, Nanite Systems Research
|
|
|
|
*/
|
|
|
|
#define INTERPRETER_VERSION "0.2.3"
|
|
#define INLINE_OCCUR
|
|
// #define COMPANION
|
|
|
|
#include <utils.lsl>
|
|
#include <lslisp.h.lsl>
|
|
|
|
#ifdef COMPANION
|
|
#include <system.lsl>
|
|
#include <System/pipes.lsl>
|
|
#include <System/tasks.lsl>
|
|
#endif
|
|
|
|
// floats with a decimal place smaller than this value
|
|
// will be rounded to the nearest integer:
|
|
#define ROUNDING_ERROR 0.00000762939453125
|
|
// (~6 bits of mantissa error in an IEEE 32-bit float)
|
|
|
|
// #define GENERAL_DEBUG
|
|
|
|
list v_names;
|
|
list v_values;
|
|
|
|
string filename;
|
|
key parse_q; // retrieval key
|
|
integer parse_n; // line number
|
|
|
|
list parse_queue; // tokens waiting to be analyzed
|
|
|
|
string interrupted_frame;
|
|
list interrupted_stack;
|
|
key interrupt; // increment PC and resume execution when matching value arrives
|
|
|
|
key user;
|
|
#ifdef ARES
|
|
key outs;
|
|
#endif
|
|
|
|
/* structure for program frames
|
|
[
|
|
'c':json - parsed symbol tree (SF)
|
|
'i':json array - program counter (S)
|
|
'p':integer - stack location of previous stack frame (S)
|
|
'sp':integer - stack location of previous verb symbol (SV)
|
|
'v':json {v_names:v_values} - variables in this scope (S)
|
|
'r':integer - one of:
|
|
0: return value and backtrack (S)
|
|
1: conditional (used in (if) and (while)) (S)
|
|
't':integer - one of:
|
|
0: stack frame (S)
|
|
1: verb frame (V)
|
|
2: function frame (F)
|
|
's':string - symbol for verb name (V)
|
|
'a':string - space-separated arglist (F)
|
|
]
|
|
*/
|
|
|
|
#define stack_frame(code_json, ixx, parent_stack_index, vars, retmode, last_symbol) ( \
|
|
"{\"c\":" + code_json \
|
|
+ ",\"i\":" + list2js(JSON_ARRAY, ixx) \
|
|
+ ",\"sp\":" + (string)last_symbol \
|
|
+ ",\"p\":" + (string)parent_stack_index \
|
|
+ ",\"v\":" + vars \
|
|
+ ",\"r\":" + (string)retmode \
|
|
+ ",\"t\":0}" \
|
|
)
|
|
|
|
#define verb_frame(symbol, last_symbol) ( \
|
|
"{\"s\":\"" + symbol + "\"" \
|
|
+ ",\"t\":1,\"sp\":" + (string)last_symbol \
|
|
+ "}")
|
|
|
|
#define func_frame(arg_list, code_json) ( \
|
|
"{\"c\":" + code_json + "" \
|
|
+ ",\"t\":2,\"a\":\"" + (string)arg_list + "\"}" \
|
|
)
|
|
|
|
list execute(list stack) {
|
|
string prog;
|
|
list ix;
|
|
integer parent;
|
|
string local_vars;
|
|
integer return_mode;
|
|
integer last_symbol;
|
|
|
|
{
|
|
string frame = gets(stack, LAST);
|
|
stack = delitem(stack, LAST);
|
|
|
|
prog = getjs(frame, (list)"c");
|
|
ix = js2list(getjs(frame, (list)"i"));
|
|
parent = (integer)getjs(frame, (list)"p");
|
|
local_vars = getjs(frame, (list)"v");
|
|
return_mode = (integer)getjs(frame, (list)"r");
|
|
last_symbol = (integer)getjs(frame, (list)"sp");
|
|
}
|
|
|
|
// resuming interrupted code:
|
|
|
|
if(interrupt) {
|
|
interrupted_frame = "";
|
|
interrupted_stack = [];
|
|
interrupt = "";
|
|
}
|
|
|
|
// no program:
|
|
if(prog == "")
|
|
return stack;
|
|
|
|
// the next token will be the head of a list:
|
|
integer descending = FALSE;
|
|
|
|
// we just ended a parenthesized expression:
|
|
integer backtrack = FALSE;
|
|
|
|
if(ix == []) {
|
|
ix = (list)NOWHERE; // start at -1 since we begin with incrementing this
|
|
descending = TRUE;
|
|
}
|
|
|
|
while(TRUE) {
|
|
@next;
|
|
|
|
// llOwnerSay("Stack: " + concat(stack, "; "));
|
|
|
|
// step forward:
|
|
ix = alter(ix, [1 + geti(ix, LAST)], LAST, LAST);
|
|
|
|
// integer pcount = 0;
|
|
|
|
if(jstype(prog, ix) == JSON_INVALID) { // no successor node
|
|
if(count(ix) == 1) { // EOF
|
|
if(parent == NOWHERE) {
|
|
//echo(">> Return to nowhere. Done.");
|
|
return stack;
|
|
} else {
|
|
//echo(">> Return to " + (string)parent);
|
|
|
|
integer previous_return_mode = return_mode;
|
|
|
|
string frame = gets(stack, parent);
|
|
|
|
if(substr(frame, 0, 0) != "{") {
|
|
echo(">> No frame at " + (string)parent + " in: " + list2js(JSON_ARRAY, stack));
|
|
return [];
|
|
}
|
|
|
|
stack = delitem(stack, parent);
|
|
|
|
prog = getjs(frame, (list)"c");
|
|
ix = js2list(getjs(frame, (list)"i"));
|
|
parent = (integer)getjs(frame, (list)"p");
|
|
local_vars = getjs(frame, (list)"v");
|
|
return_mode = (integer)getjs(frame, (list)"r");
|
|
last_symbol = (integer)getjs(frame, (list)"sp");
|
|
|
|
#ifdef GENERAL_DEBUG
|
|
if(~last_symbol)
|
|
echo(">> Return from subframe; last symbol was " + gets(stack, last_symbol));
|
|
else
|
|
echo(">> Return from subframe; no last symbol");
|
|
#endif
|
|
|
|
string structure = getjs(prog, ix);
|
|
|
|
list step_again = ix;
|
|
|
|
if(previous_return_mode == 1) { // leaving a conditional expression
|
|
integer retval = (integer)gets(stack, LAST);
|
|
// echo(">> " + structure + " conditional results: rv=" + (string)retval + "; ix=" + concat(ix, "."));
|
|
stack = delitem(stack, LAST);
|
|
|
|
//frame = stack_frame(prog, ix, parent, local_vars, return_mode);
|
|
|
|
ix = delitem(ix, LAST);
|
|
|
|
//list step_forward = ix; // alter(ix, [1 + geti(ix, LAST)], LAST, LAST);
|
|
|
|
if(structure == "if") { // entering 'if' or 'else' body
|
|
string next_frame = setjs(frame, (list)"i", list2js(JSON_ARRAY, ix));
|
|
list subix = ix;
|
|
if(retval) {
|
|
subix += 2;
|
|
} else if(jstype(prog, ix + 3) == JSON_ARRAY) { // else branch exists; activate
|
|
subix += 3;
|
|
} else { // else 'else' branch doesn't exist; carry on
|
|
// ix = step_forward;
|
|
// proceed to increment
|
|
jump next;
|
|
}
|
|
prog = "[" + getjs(prog, subix) + "]";
|
|
stack += next_frame;
|
|
return_mode = 0;
|
|
} else if(structure == "while") { // entering 'while' body
|
|
if(retval) {
|
|
// echo(">> ENTERING A WHILE BODY; ix IS " + concat(ix, "."));
|
|
stack += frame;
|
|
prog = "[" + getjs(prog, ix + 2) + "]";
|
|
descending = TRUE;
|
|
return_mode = 2;
|
|
//echo(">> stack: " + list2js(JSON_ARRAY, stack));
|
|
} else {
|
|
// echo(">> NOT ENTERING A WHILE BODY; TEST FAILED");
|
|
// ix = step_forward;
|
|
// proceed to increment
|
|
jump next;
|
|
}
|
|
} /*else {
|
|
echo(">>#12: " + structure);
|
|
}*/
|
|
|
|
// init new stack frame for descent
|
|
|
|
local_vars = "{}";
|
|
parent = count(stack) - 1;
|
|
last_symbol = NOWHERE;
|
|
ix = [NOWHERE];
|
|
|
|
// echo(">> Initialized new stack frame; " + (string)parent + " is now address of parent");
|
|
|
|
} else if(previous_return_mode == 2) { // leaving a 'while' body; evaluate conditional again
|
|
// echo(">> LEAVING A WHILE BODY; ix IS " + concat(ix, "."));
|
|
|
|
stack += frame; //stack_frame(prog, ix, parent, local_vars, return_mode);
|
|
prog = "[" + getjs(prog, alter(step_again, (list)1, LAST, LAST)) + "]";
|
|
|
|
// echo(">> RE-CHECKING CONDITION " + (string)prog);
|
|
|
|
// echo(">> stack after re-check frame is added: " + list2js(JSON_ARRAY, stack));
|
|
|
|
return_mode = 1;
|
|
local_vars = "{}";
|
|
parent = count(stack) - 1;
|
|
ix = [NOWHERE];
|
|
last_symbol = NOWHERE;
|
|
descending = TRUE;
|
|
} /*else {
|
|
echo(">>#11:" + structure);
|
|
}*/
|
|
jump next;
|
|
}
|
|
}
|
|
|
|
backtrack = TRUE;
|
|
// pcount = geti(ix, LAST);
|
|
/*if(jstype(prog, ix) == JSON_ARRAY) {
|
|
echo(">> Empty backtrack warning.");
|
|
// ++pcount;
|
|
}*/
|
|
|
|
ix = delitem(ix, LAST);
|
|
// ix = alter(ix, (list)(1 + geti(ix, -2)), LAST, LAST); // back out
|
|
|
|
} else while(jstype(prog, ix) == JSON_ARRAY) { // at a list, so go in
|
|
// echo(">> Engage descent");
|
|
ix += 0;
|
|
/*if(jstype(prog, ix) == JSON_ARRAY) { // THIS is the implicit progn
|
|
echo(">> Empty descent!");
|
|
// stack += JSON_NULL;
|
|
}*/
|
|
descending = TRUE;
|
|
}
|
|
|
|
string type = jstype(prog, ix);
|
|
|
|
// we have an entry point; continue
|
|
// parse should be in depth-first *exit*, so (9 (8 (5 (3 1 2) 4) 6 7)) (13 (12 10 11))
|
|
|
|
if(type == JSON_NUMBER) {
|
|
// echo(">> Number");
|
|
stack += (float)getjs(prog, ix);
|
|
} else if(descending) {
|
|
string verb = getjs(prog, ix);
|
|
|
|
#ifdef GENERAL_DEBUG
|
|
echo(">> verb: " + verb);
|
|
#endif
|
|
|
|
if(verb == "if" || verb == "while") {
|
|
/*
|
|
ix = delitem(ix, LAST);
|
|
string condition = "[" + getjs(prog, ix + [1]) + "]";
|
|
string body = "[" + getjs(prog, ix + [2]) + "]";
|
|
string alternative = "[" + getjs(prog, ix + [3]) + "]";
|
|
|
|
// construct frame, put on stack and replace state with conditional
|
|
|
|
list retval = execute([stack_frame(condition, [], TODO:parent, "{}", 1)]);
|
|
|
|
// move to retmode evaluation within EOF backtracking case
|
|
|
|
if(geti(retval, 0)) {
|
|
stack += execute(stack_frame(body, [], TODO:parent, "{}", 0));
|
|
} else if(alternative != JSON_INVALID) {
|
|
stack += execute(stack_frame(alternative, [], TODO:parent, "{}", 0));
|
|
}
|
|
|
|
descending = FALSE;
|
|
*/
|
|
|
|
stack += stack_frame(prog, ix, parent, local_vars, return_mode, last_symbol); // push old state
|
|
|
|
// set up conditional frame:
|
|
prog = "[" + getjs(prog, delitem(ix, LAST) + 1) + "]";
|
|
return_mode = 1;
|
|
local_vars = "{}";
|
|
parent = count(stack) - 1;
|
|
ix = (list)NOWHERE;
|
|
last_symbol = NOWHERE;
|
|
|
|
// entering conditional structure:
|
|
descending = TRUE;
|
|
|
|
// do not backtrack:
|
|
jump next;
|
|
|
|
} else if(verb == "lambda") {
|
|
ix = delitem(ix, LAST);
|
|
string arglist = concat(js2list(getjs(prog, ix + 1)), " ");
|
|
string code = getjs(prog, ix + 2);
|
|
descending = backtrack = FALSE;
|
|
stack += func_frame(arglist, code);
|
|
jump next;
|
|
|
|
} else {
|
|
stack += verb_frame(verb, last_symbol);
|
|
last_symbol = count(stack) - 1;
|
|
// echo(">> (descend verb " + verb + " is non-structural)");
|
|
}
|
|
} else if(type == JSON_STRING) {
|
|
// variable name or other literal:
|
|
string s = getjs(prog, ix);
|
|
if(s == "\\\"\\\"") {
|
|
stack += "";
|
|
} else if(substr(s, 0, 1) == "\\\"") { // list2js eats quotes
|
|
stack += substr(s, 2, (integer)-3);
|
|
// stack += s;
|
|
} else if(substr(s, 0, 0) == "$") {
|
|
string name = delstring(s, 0, 0);
|
|
string local_type = jstype(local_vars, (list)name);
|
|
integer temp_parent = parent;
|
|
|
|
#ifdef GENERAL_DEBUG
|
|
echo(">> searching for var " + name + " in stack (p=" + (string)parent + "):");
|
|
integer sti = count(stack);
|
|
while(sti--)
|
|
echo("/me .. [" + (string)sti + "] " + gets(stack, sti));
|
|
#endif
|
|
|
|
string value;
|
|
|
|
if(local_type != JSON_INVALID) {
|
|
value = getjs(local_vars, (list)name);
|
|
} else while(local_type == JSON_INVALID) {
|
|
#ifdef GENERAL_DEBUG
|
|
echo(">> get looking for " + name + " in " + (string)temp_parent);
|
|
#endif
|
|
if(~temp_parent) {
|
|
string fr = gets(stack, temp_parent);
|
|
local_type = jstype(fr, ["v", name]);
|
|
value = getjs(fr, ["v", name]);
|
|
|
|
#ifdef GENERAL_DEBUG
|
|
if(local_type != JSON_INVALID)
|
|
echo(">> get found " + name + " = " + value + " in " + (string)temp_parent);
|
|
#endif
|
|
temp_parent = (integer)getjs(fr, (list)"p");
|
|
} else {
|
|
integer si = index(v_names, name);
|
|
if(~si) {
|
|
integer global_type = llGetListEntryType(v_values, si);
|
|
if(global_type == TYPE_FLOAT)
|
|
stack += getf(v_values, si);
|
|
else if(global_type == TYPE_INTEGER)
|
|
stack += geti(v_values, si);
|
|
else
|
|
stack += gets(v_values, si);
|
|
|
|
// stack += value;
|
|
jump next;
|
|
} else {
|
|
// value = "?uninitialized?";
|
|
echo(">> uninitialized variable: " + s);
|
|
return [];
|
|
}
|
|
}
|
|
}
|
|
|
|
if(local_type == JSON_NUMBER) {
|
|
if(llFabs((float)value - (float)( (integer)((float)value + 0.5) )) < ROUNDING_ERROR)
|
|
stack += (integer)value;
|
|
else
|
|
stack += (float)value;
|
|
} else {
|
|
stack += value;
|
|
}
|
|
|
|
/*} else if(s == "") {
|
|
echo(">> Literal null string");*/
|
|
} else { // no idea; transmit literally
|
|
// echo(">> Literal oddity " + s);
|
|
stack += s;
|
|
}
|
|
} else if(backtrack) { // we are leaving; pop stack
|
|
// integer pcount = geti(ix, LAST);
|
|
|
|
/*
|
|
if(!~last_symbol) {
|
|
echo(">>attempted to dereference void symbol");
|
|
return stack;
|
|
}
|
|
*/
|
|
|
|
string verbf = gets(stack, last_symbol);
|
|
string verb = getjs(verbf, (list)"s");
|
|
|
|
if(verb == "") {
|
|
last_symbol = (integer)getjs(verbf, (list)"sp");
|
|
backtrack = descending = FALSE;
|
|
jump next;
|
|
}
|
|
|
|
integer c;
|
|
list params;
|
|
{
|
|
integer scount = count(stack) - last_symbol;
|
|
c = scount - 1;
|
|
if(c)
|
|
params = sublist(stack, last_symbol + 1, LAST);
|
|
}
|
|
|
|
#ifdef GENERAL_DEBUG
|
|
echo(">> " + verbf + " execute: (" + verb + " " + concat(params, " ") + "), c=" + (string)c);
|
|
#endif
|
|
|
|
stack = delrange(stack, last_symbol, LAST);
|
|
|
|
last_symbol = (integer)getjs(verbf, (list)"sp");
|
|
/*
|
|
echo(">> Backtrack verb: " + verb);
|
|
if(~parent)
|
|
echo(">> Stack: " + concat(alter(stack, ["~"], parent, parent), "; "));
|
|
else
|
|
echo(">> Stack: " + concat(stack, "; "));*/
|
|
|
|
if(verb == "list") {
|
|
stack += list2js(JSON_ARRAY, params);
|
|
} else if(verb == "print") {
|
|
#ifdef COMPANION
|
|
pipe_send((string)(params), user);
|
|
#elif defined(ARES)
|
|
print(outs, user, (string)params);
|
|
#else
|
|
echo((string)params);
|
|
#endif
|
|
} else if(verb == "global") {
|
|
string name = gets(params, 0);
|
|
string data;
|
|
if(c > 2)
|
|
data = list2js(JSON_ARRAY, sublist(params, 1, LAST));
|
|
else if(c == 2)
|
|
data = gets(params, 1);
|
|
|
|
integer si = index(v_names, name);
|
|
if(~si) {
|
|
v_values = alter(v_values, [data], si, si);
|
|
} else {
|
|
v_names += name;
|
|
v_values += data;
|
|
}
|
|
|
|
} else if(verb == "set") {
|
|
if(c < 2) {
|
|
echo(">> unary set.");
|
|
return [];
|
|
}
|
|
|
|
string name = gets(params, 0);
|
|
list data = sublist(params, 1, LAST);
|
|
|
|
string local_type = jstype(local_vars, (list)name);
|
|
integer old_parent = NOWHERE;
|
|
integer temp_parent = parent;
|
|
|
|
string fr;
|
|
|
|
while(local_type == JSON_INVALID) {
|
|
#ifdef GENERAL_DEBUG
|
|
echo(">> set looking for " + name + " in " + (string)temp_parent);
|
|
echo(">> stack: " + list2js(JSON_ARRAY, stack));
|
|
#endif
|
|
if(~temp_parent) {
|
|
fr = gets(stack, temp_parent);
|
|
local_type = jstype(fr, ["v", name]);
|
|
old_parent = temp_parent;
|
|
temp_parent = (integer)getjs(fr, (list)"p");
|
|
#ifdef GENERAL_DEBUG
|
|
if(local_type != JSON_INVALID)
|
|
echo(">> set storing " + list2js(JSON_ARRAY, data) + " in frame " + (string)old_parent + " as " + name);
|
|
#endif
|
|
} else {
|
|
integer si = index(v_names, name);
|
|
if(~si) {
|
|
#ifdef GENERAL_DEBUG
|
|
echo(">> set storing " + list2js(JSON_ARRAY, data) + " in globals as " + name);
|
|
#endif
|
|
v_values = alter(v_values, data, si, si);
|
|
} else {
|
|
local_vars = setjs(local_vars, (list)name, (string)data);
|
|
#ifdef GENERAL_DEBUG
|
|
echo(">> set creating " + list2js(JSON_ARRAY, data) + " in locals as " + name);
|
|
#endif
|
|
}
|
|
jump done_set;
|
|
}
|
|
}
|
|
|
|
if(~old_parent)
|
|
stack = alter(stack, (list)setjs(fr, ["v", name], (string)data), old_parent, old_parent);
|
|
else
|
|
local_vars = setjs(local_vars, (list)name, (string)data);
|
|
|
|
@done_set;
|
|
|
|
// } else if(verb == "sum" || verb == "+" || verb == "add") {
|
|
} else if(verb == "+") {
|
|
float out = 0;
|
|
while(c--)
|
|
out += getf(params, c);
|
|
|
|
stack += out;
|
|
|
|
//stack += llListStatistics(LIST_STAT_SUM, params);
|
|
|
|
//} else if(verb == "product" || verb == "*") {
|
|
} else if(verb == "*") {
|
|
float out = 1;
|
|
while(c--)
|
|
out *= getf(params, c);
|
|
|
|
stack += out;
|
|
|
|
//} else if(verb == "eq" || verb == "==") {
|
|
} else if(verb == "==") {
|
|
if(etype(params, 0) == TYPE_STRING)
|
|
stack += gets(params, 0) == gets(params, 1);
|
|
else if(etype(params, 0) == TYPE_INTEGER)
|
|
stack += geti(params, 0) == geti(params, 1);
|
|
else
|
|
stack += getf(params, 0) == getf(params, 1);
|
|
|
|
// } else if(verb == "ne" || verb == "!=") {
|
|
} else if(verb == "!=") {
|
|
if(etype(params, 0) == TYPE_STRING)
|
|
stack += gets(params, 0) != gets(params, 1);
|
|
else if(etype(params, 0) == TYPE_INTEGER)
|
|
stack += geti(params, 0) ^ geti(params, 1);
|
|
else
|
|
stack += getf(params, 0) != getf(params, 1);
|
|
|
|
// } else if(verb == "lt" || verb == "<") {
|
|
} else if(verb == "<") {
|
|
stack += getf(params, 0) < getf(params, 1);
|
|
|
|
//} else if(verb == "gt" || verb == ">") {
|
|
} else if(verb == ">") {
|
|
stack += getf(params, 0) > getf(params, 1);
|
|
|
|
//} else if(verb == "lte" || verb == "<=") {
|
|
} else if(verb == "<=") {
|
|
stack += getf(params, 0) <= getf(params, 1);
|
|
|
|
//} else if(verb == "gte" || verb == ">=") {
|
|
} else if(verb == ">=") {
|
|
stack += getf(params, 0) >= getf(params, 1);
|
|
|
|
} else if(verb == "and") {
|
|
integer out = 1;
|
|
while(c--)
|
|
out = out & geti(params, c);
|
|
|
|
stack += out;
|
|
|
|
} else if(verb == "or") {
|
|
integer out = 0;
|
|
while(c--)
|
|
out = out | geti(params, c);
|
|
|
|
stack += out;
|
|
|
|
} else if(verb == "not" || verb == "!") {
|
|
stack += !geti(params, 0);
|
|
|
|
//} else if(verb == "-" || verb == "neg" || verb == "sub") {
|
|
} else if(verb == "-") {
|
|
if(c > 1) {
|
|
float out = getf(params, 0);
|
|
--c;
|
|
while(c--) {
|
|
out -= getf(params, c + 1);
|
|
}
|
|
stack += out;
|
|
} else {
|
|
stack += -getf(params, 0);
|
|
}
|
|
|
|
//} else if(verb == "/" || verb == "div") {
|
|
} else if(verb == "/") {
|
|
if(c > 1) {
|
|
float out = getf(params, 0);
|
|
--c;
|
|
while(c--) {
|
|
float newv = getf(params, c + 1);
|
|
if(newv != 0)
|
|
out /= newv;
|
|
else {
|
|
echo(">> Division by zero.");
|
|
return [];
|
|
}
|
|
}
|
|
stack += out;
|
|
} else {
|
|
float out = getf(params, 0);
|
|
if(out != 0)
|
|
stack += (1.0 / out);
|
|
else {
|
|
echo(">> Division by zero.");
|
|
// stack += 0;
|
|
return [];
|
|
}
|
|
}
|
|
} else if(verb == "apply") {
|
|
stack += stack_frame(prog, ix, parent, local_vars, return_mode, last_symbol);
|
|
|
|
local_vars = "{}";
|
|
ix = [NOWHERE];
|
|
parent = count(stack) - 1;
|
|
|
|
{
|
|
string func = gets(params, 0);
|
|
|
|
list arg_names = split(getjs(func, (list)"a"), " ");
|
|
// bind args:
|
|
integer an = count(arg_names);
|
|
while(an--) {
|
|
local_vars = setjs(local_vars, (list)gets(arg_names, an), gets(params, 1 + an));
|
|
}
|
|
|
|
prog = "[" + getjs(func, (list)"c") + "]";
|
|
#ifdef GENERAL_DEBUG
|
|
echo(">> applying function " + prog);
|
|
#endif
|
|
}
|
|
|
|
descending = backtrack = FALSE;
|
|
|
|
jump next;
|
|
|
|
} else if(verb == "concat") {
|
|
stack += concat(js2list(gets(params, 1)), gets(params, 0));
|
|
|
|
} else if(verb == "split") {
|
|
stack += list2js(JSON_ARRAY, split(gets(params, 1), gets(params, 0)));
|
|
|
|
} else if(verb == "get") {
|
|
stack += getjs(gets(params, 0), js2list(gets(params, 1)));
|
|
|
|
} else if(verb == "index") {
|
|
stack += index(js2list(gets(params, 0)), gets(params, 1));
|
|
|
|
} else if(verb == "count") {
|
|
stack += count(js2list(gets(params, 0)));
|
|
|
|
} else if(verb == "put") {
|
|
#ifdef GENERAL_DEBUG
|
|
echo("Putting '" + gets(params, 2) + "' into '" + gets(params, 0) + "' at position: " + gets(params, 1));
|
|
#endif
|
|
stack += setjs(gets(params, 0), js2list(gets(params, 1)), gets(params, 2));
|
|
|
|
} else if(verb == "char") {
|
|
integer p = geti(params, 1);
|
|
stack += substr(gets(params, 0), p, p);
|
|
|
|
} else if(verb == "substr") {
|
|
stack += substr(gets(params, 0), geti(params, 1), geti(params, 2));
|
|
|
|
} else if(verb == "strpos") {
|
|
stack += strpos(gets(params, 0), gets(params, 1));
|
|
|
|
} else if(verb == "rand") {
|
|
stack += llFrand(1);
|
|
|
|
} else if(verb == "int") {
|
|
stack += (integer)gets(params, 0);
|
|
|
|
#ifdef ARES
|
|
} else if(verb == "getd") {
|
|
stack += getdb(gets(params, 0), gets(params, 1));
|
|
} else if(verb == "setd") {
|
|
setdb(gets(params, 0), gets(params, 1), gets(params, 2));
|
|
} else if(verb == "deleted") {
|
|
deletedb(gets(params, 0), gets(params, 1));
|
|
} else if(substr(verb, 0, 0) == "@") {
|
|
invoke(delstring(verb, 0, 0) + " " + (string)params, outs, NULL_KEY, user);
|
|
}
|
|
#endif
|
|
|
|
#ifdef COMPANION
|
|
} else if(verb == "external") {
|
|
linked(LINK_ROOT, geti(params, 0), gets(params, 1), interrupt = llGenerateKey());
|
|
// llSleep(0.25); // prevent self-activation
|
|
interrupted_stack = stack;
|
|
interrupted_frame = stack_frame(prog, ix, parent, local_vars, return_mode, last_symbol);
|
|
// interrupted_program = prog;
|
|
// interrupted_ix = ix;
|
|
// echo("Paused execution.");
|
|
return [];
|
|
} else if(substr(verb, 0, 0) == "@") {
|
|
start_task(EXECUTE, delstring(verb, 0, 0) + " " + (string)params, user);
|
|
} else if(substr(verb, 0, 0) == "#") {
|
|
start_task((integer)delstring(verb, 0, 0), (string)params, user);
|
|
}
|
|
#else
|
|
else if(substr(verb, 0, 0) == "#") {
|
|
llMessageLinked(LINK_THIS, (integer)delstring(verb, 0, 0), (string)params, user);
|
|
}
|
|
#endif
|
|
else if(verb == JSON_INVALID) { // nil
|
|
stack += "[]";
|
|
} else if(verb == JSON_NULL) { // progn
|
|
// echo(">> Null backtrack verb; params: " + concat(params, ", "));
|
|
|
|
if(c)
|
|
stack += sublist(params, LAST, LAST);
|
|
} else {
|
|
echo(">> unknown verb: " + verb);
|
|
}
|
|
} else {
|
|
echo(">> unknown data type: " + type);
|
|
}
|
|
|
|
// result += getjs(prog, ix);
|
|
|
|
descending = backtrack = FALSE;
|
|
|
|
// echo(concat(ix, ">"));
|
|
}
|
|
|
|
// echo("Finished.");
|
|
|
|
return stack;
|
|
}
|
|
|
|
string parse(list pq) {
|
|
// match parens
|
|
list opens; // indices of open parens
|
|
integer tcount = count(pq);
|
|
integer open_quote = NOWHERE;
|
|
integer comment_start = NOWHERE;
|
|
integer i;
|
|
for(; i < tcount; ++i) {
|
|
string t = gets(pq, i);
|
|
if(~comment_start) {
|
|
if(t == "\n") {
|
|
pq = delrange(pq, comment_start, i);
|
|
tcount -= (i - comment_start + 1);
|
|
i = comment_start;
|
|
comment_start = NOWHERE;
|
|
}
|
|
} else if(~open_quote) {
|
|
if(t == "\"") {
|
|
string str;
|
|
if(open_quote + 1 == i)
|
|
str = "";
|
|
else
|
|
str = "\\\"" + concat(sublist(pq, open_quote + 1, i - 1), " ") + "\\\"";
|
|
pq = alter(pq, [str], open_quote, i);
|
|
i = open_quote;
|
|
open_quote = NOWHERE;
|
|
}
|
|
} else {
|
|
if(t == "") {
|
|
pq = delitem(pq, i);
|
|
--i;
|
|
--tcount;
|
|
// --i can't go in macro args; gets executed twice!
|
|
} else if(t == "\"") {
|
|
open_quote = i;
|
|
} else if(t == "(") {
|
|
opens += i;
|
|
} else if(t == ";") {
|
|
comment_start = i;
|
|
} else if(t == "\n") {
|
|
pq = delitem(pq, i);
|
|
--i;
|
|
--tcount;
|
|
} else if(t == ")") {
|
|
if(count(opens) == 0) {
|
|
echo("++ Excess closing parens at " + (string)i + ".");
|
|
return "";
|
|
}
|
|
|
|
integer L = geti(opens, LAST);
|
|
opens = delitem(opens, LAST);
|
|
string frag;
|
|
if(L + 1 == i)
|
|
frag = "[]";
|
|
else
|
|
frag = list2js(JSON_ARRAY, sublist(pq, L + 1, i - 1));
|
|
if(jstype(frag, [0]) == JSON_ARRAY)
|
|
frag = list2js(JSON_ARRAY, [JSON_NULL] + sublist(pq, L + 1, i - 1));
|
|
pq = alter(pq, [frag], L, i);
|
|
tcount = count(pq);
|
|
i = L;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(~open_quote) {
|
|
echo("++ unclosed quote following: " + concat(sublist(pq, open_quote - 2, open_quote + 4), " "));
|
|
return "";
|
|
} else if(opens != []) {
|
|
echo("++ imbalanced parens (" + (string)count(opens) + ").");
|
|
return "";
|
|
} else {
|
|
// return json tree:
|
|
|
|
/* #ifdef GENERAL_DEBUG
|
|
echo("++ Parse results: " + list2js(JSON_ARRAY, pq));
|
|
#endif */
|
|
// return parsed;
|
|
|
|
return list2js(JSON_ARRAY, pq);
|
|
}
|
|
}
|
|
|
|
#ifndef ARES
|
|
default {
|
|
state_entry() {
|
|
#ifdef COMPANION
|
|
list messages = [LISP_EXECUTE_FILE, LISP_EXECUTE_FUNC];
|
|
list commands = ["lisp", "eval"];
|
|
set_identity(MT_SERVICE, "interpreter for LSLisp", messages, commands);
|
|
send_identity();
|
|
#else
|
|
echo((string)(llGetMemoryLimit() - llGetUsedMemory()) + " bytes available.");
|
|
#endif
|
|
|
|
//linked(LINK_THIS, LISP_EXECUTE_FILE, "ls_test", llGetOwner());
|
|
}
|
|
|
|
dataserver(key q, string m) {
|
|
if(q == parse_q) {
|
|
// llOwnerSay(".");
|
|
if(m == EOF) {
|
|
// llOwnerSay("-- Executing...");
|
|
string prog = parse(parse_queue);
|
|
parse_queue = [];
|
|
list r = execute([stack_frame(prog, [], NOWHERE, "{}", 0, NOWHERE)]);
|
|
|
|
if(interrupt) {
|
|
// llOwnerSay("-- Interrupted!");
|
|
} else if(r != []) {
|
|
pipe_send(concat(r, " "), user);
|
|
}
|
|
} else {
|
|
parse_queue += ["\n"] + tokenize(m);
|
|
parse_q = llGetNotecardLine(filename, ++parse_n);
|
|
}
|
|
}
|
|
}
|
|
|
|
link_message(integer src, integer n, string m, key id) {
|
|
#ifdef COMPANION
|
|
// llOwnerSay("Incoming message from " + (string)src + ": " + (string)n + "; " + (string)m + "; " + string(id));
|
|
if(n == EXECUTE || n == COMMAND) {
|
|
list argv = split(m, " ");
|
|
string cmd = gets(argv, 0);
|
|
integer argc = count(argv);
|
|
if(cmd == "lisp") {
|
|
if(argc == 1 || gets(argv, 1) == "help") {
|
|
list file_list;
|
|
integer fi = llGetInventoryNumber(INVENTORY_NOTECARD);
|
|
while(fi--) {
|
|
string nc = llGetInventoryName(INVENTORY_NOTECARD, fi);
|
|
if(substr(nc, 0, 2) == "ls_")
|
|
file_list = substr(nc, 3, LAST) + file_list;
|
|
}
|
|
|
|
tell(id, 0, "lisp <filename>\n\nExecutes the notecard ls_<filename> using the LSLisp interpreter. See http://develop.nanite-systems.com/lisp for more information. Available cards: " + concat(file_list, ", "));
|
|
} else {
|
|
start_task(LISP_EXECUTE_FILE, "ls_" + concat(sublist(argv, 1, LAST), " "), id);
|
|
}
|
|
} else if(cmd == "eval") {
|
|
if(argc == 1 || gets(argv, 1) == "help") {
|
|
tell(id, 0, "eval <function>\n\nExecutes the provided source code directly using the LSLisp interpreter. See http://develop.nanite-systems.com/lisp for more information.");
|
|
} else {
|
|
start_task(LISP_EXECUTE_FUNC, concat(sublist(argv, 1, LAST), " "), id);
|
|
}
|
|
}
|
|
} else if(n == PROBE_MODULES && (m == "" || m == llGetScriptName())) {
|
|
send_identity();
|
|
} else
|
|
#endif
|
|
|
|
if(n == LISP_EXECUTE_FILE) {
|
|
if(id)
|
|
user = id;
|
|
else
|
|
user = llGetOwner();
|
|
|
|
// program = "";
|
|
parse_queue = v_names = v_values = interrupted_stack = [];
|
|
interrupt = "";
|
|
|
|
// llOwnerSay("-- Loading...");
|
|
parse_q = llGetNotecardLine(filename = m, parse_n = 0);
|
|
} else if(n == LISP_EXECUTE_FUNC) {
|
|
if(id)
|
|
user = id;
|
|
else
|
|
user = llGetOwner();
|
|
|
|
// llOwnerSay("-- Evaluating...");
|
|
list r = execute([stack_frame(parse(tokenize(m)), [], NOWHERE, "{}", 0, NOWHERE)]);
|
|
if(interrupt) {
|
|
// llOwnerSay("-- Interrupted!");
|
|
} else if(r != []) {
|
|
pipe_send(concat(r, " "), user);
|
|
}
|
|
} else if(interrupt) {
|
|
if(id == interrupt) {
|
|
interrupted_stack += m;
|
|
// interrupt = ""; - now cleared within execute()
|
|
|
|
list r = execute(interrupted_stack + interrupted_frame);
|
|
if(interrupt) {
|
|
// llOwnerSay("-- Interrupted!");
|
|
} else if(r != []) {
|
|
pipe_send(concat(r, " "), user);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // ARES
|
|
|