2023-08-12 15:43:18 +02:00
|
|
|
|
#region symbols
|
2023-11-23 01:41:18 +01:00
|
|
|
|
global.LOG_EXPRESSION = false;
|
2023-08-12 15:43:18 +02:00
|
|
|
|
|
|
|
|
|
global.EQUATION_PRES = ds_map_create();
|
|
|
|
|
global.EQUATION_PRES[? "+"] = 1;
|
|
|
|
|
global.EQUATION_PRES[? "-"] = 1;
|
|
|
|
|
global.EQUATION_PRES[? "∸"] = 9; //unary negative
|
|
|
|
|
global.EQUATION_PRES[? "*"] = 2;
|
|
|
|
|
global.EQUATION_PRES[? "/"] = 2;
|
|
|
|
|
global.EQUATION_PRES[? "%"] = 2;
|
2023-09-11 16:08:58 +02:00
|
|
|
|
global.EQUATION_PRES[? "$"] = 3; //power
|
2023-08-12 15:43:18 +02:00
|
|
|
|
|
|
|
|
|
global.EQUATION_PRES[? "&"] = 5;
|
|
|
|
|
global.EQUATION_PRES[? "|"] = 4;
|
|
|
|
|
global.EQUATION_PRES[? "^"] = 3;
|
|
|
|
|
global.EQUATION_PRES[? "<"] = 3;
|
|
|
|
|
global.EQUATION_PRES[? "»"] = 6;
|
|
|
|
|
global.EQUATION_PRES[? "«"] = 6;
|
|
|
|
|
global.EQUATION_PRES[? "~"] = 9;
|
|
|
|
|
|
|
|
|
|
global.EQUATION_PRES[? "="] = -99;
|
|
|
|
|
global.EQUATION_PRES[? "⊕"] = -99; //+=
|
|
|
|
|
global.EQUATION_PRES[? "⊖"] = -99; //-=
|
|
|
|
|
global.EQUATION_PRES[? "⊗"] = -99; //*=
|
|
|
|
|
global.EQUATION_PRES[? "⊘"] = -99; ///=
|
|
|
|
|
|
|
|
|
|
global.EQUATION_PRES[? "⩵"] = -1; //==
|
|
|
|
|
global.EQUATION_PRES[? "≠"] = -1; //!=
|
|
|
|
|
global.EQUATION_PRES[? "<"] = 0;
|
|
|
|
|
global.EQUATION_PRES[? ">"] = 0;
|
|
|
|
|
global.EQUATION_PRES[? "≤"] = 0;
|
|
|
|
|
global.EQUATION_PRES[? "≥"] = 0;
|
|
|
|
|
|
|
|
|
|
global.EQUATION_PRES[? "@"] = 5; //array accerssor symbol
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region parser
|
2023-11-23 01:41:18 +01:00
|
|
|
|
function functionStringClean(fx) { #region
|
2023-12-07 15:08:09 +01:00
|
|
|
|
static __BRACKETS = [ "(", "[", "," ];
|
2023-08-12 15:43:18 +02:00
|
|
|
|
|
|
|
|
|
var ch = "", ind = 0, len = string_length(fx);
|
|
|
|
|
var _fx = "", str = false;
|
|
|
|
|
var _prevSym = true;
|
|
|
|
|
|
|
|
|
|
while(ind++ <= len) {
|
|
|
|
|
ch = string_char_at(fx, ind);
|
|
|
|
|
|
|
|
|
|
if(ch == " ") {
|
|
|
|
|
if(str) _fx += ch;
|
|
|
|
|
} else {
|
|
|
|
|
if(ch == "-" && _prevSym)
|
|
|
|
|
_fx += $"0∸";
|
|
|
|
|
else
|
|
|
|
|
_fx += ch;
|
|
|
|
|
|
|
|
|
|
_prevSym = ds_map_exists(global.EQUATION_PRES, ch) || array_exists(__BRACKETS, ch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(ch == "\"")
|
|
|
|
|
str = !str;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fx = _fx;
|
|
|
|
|
|
|
|
|
|
fx = string_replace_all(fx, "\n", "");
|
|
|
|
|
fx = string_replace_all(fx, "**", "$");
|
|
|
|
|
fx = string_replace_all(fx, "<<", "«");
|
|
|
|
|
fx = string_replace_all(fx, ">>", "»");
|
|
|
|
|
|
|
|
|
|
fx = string_replace_all(fx, "==", "⩵");
|
|
|
|
|
fx = string_replace_all(fx, "!=", "≠");
|
|
|
|
|
fx = string_replace_all(fx, "<>", "≠");
|
|
|
|
|
fx = string_replace_all(fx, ">=", "≥");
|
|
|
|
|
fx = string_replace_all(fx, "<=", "≤");
|
|
|
|
|
|
|
|
|
|
fx = string_replace_all(fx, "++", "⊕1");
|
|
|
|
|
fx = string_replace_all(fx, "--", "⊖1");
|
|
|
|
|
|
|
|
|
|
fx = string_replace_all(fx, "+=", "⊕");
|
|
|
|
|
fx = string_replace_all(fx, "-=", "⊖");
|
|
|
|
|
fx = string_replace_all(fx, "*=", "⊗");
|
|
|
|
|
fx = string_replace_all(fx, "/=", "⊘");
|
2023-11-17 06:24:31 +01:00
|
|
|
|
|
|
|
|
|
fx = string_replace_all(fx, "]", ",]");
|
2023-08-12 15:43:18 +02:00
|
|
|
|
|
|
|
|
|
fx = string_trim(fx);
|
|
|
|
|
|
|
|
|
|
return fx;
|
2023-11-23 01:41:18 +01:00
|
|
|
|
} #endregion
|
2023-08-12 15:43:18 +02:00
|
|
|
|
|
2023-11-23 01:41:18 +01:00
|
|
|
|
function functionStrip(fx) { #region
|
2023-08-12 15:43:18 +02:00
|
|
|
|
var el_st = 1;
|
|
|
|
|
var el_ed = 1;
|
|
|
|
|
|
|
|
|
|
for( var i = 1; i <= string_length(fx); i++ ) {
|
|
|
|
|
var cch = string_char_at(fx, i);
|
|
|
|
|
if(cch == "(") {
|
|
|
|
|
el_st = i + 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for( var i = string_length(fx); i >= 1; i-- ) {
|
|
|
|
|
var cch = string_char_at(fx, i);
|
|
|
|
|
if(cch == ")") {
|
|
|
|
|
el_ed = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string_copy(fx, el_st, el_ed - el_st)
|
2023-11-23 01:41:18 +01:00
|
|
|
|
} #endregion
|
2023-08-12 15:43:18 +02:00
|
|
|
|
|
2023-11-23 01:41:18 +01:00
|
|
|
|
function evaluateFunctionList(fx) { #region
|
2023-08-12 15:43:18 +02:00
|
|
|
|
fx = string_replace_all(fx, "{", "\n{\n");
|
|
|
|
|
fx = string_replace_all(fx, "}", "\n}\n");
|
|
|
|
|
|
|
|
|
|
var fxs = string_split(fx, "\n", true);
|
|
|
|
|
|
|
|
|
|
var flist = new __funcList();
|
|
|
|
|
|
|
|
|
|
var call_st = ds_stack_create();
|
|
|
|
|
var blok_st = ds_stack_create();
|
|
|
|
|
ds_stack_push(call_st, flist);
|
|
|
|
|
|
|
|
|
|
for( var i = 0, n = array_length(fxs); i < n; i++ ) {
|
|
|
|
|
var _fx = functionStringClean(fxs[i]);
|
|
|
|
|
//print($"Eval line {i}: {_fx} [stack size = {ds_stack_size(call_st)}]");
|
|
|
|
|
|
|
|
|
|
if(_fx == "" || _fx == "{") continue;
|
|
|
|
|
if(_fx == "}") {
|
|
|
|
|
ds_stack_pop(call_st);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var _fx_sp = string_split(_fx, "(");
|
|
|
|
|
var _cmd = string_trim(_fx_sp[0]);
|
|
|
|
|
var _cond = functionStrip(_fx);
|
|
|
|
|
|
|
|
|
|
switch(_cmd) {
|
|
|
|
|
case "if":
|
|
|
|
|
var con_if = new __funcIf();
|
|
|
|
|
con_if.condition = evaluateFunctionTree(_cond);
|
|
|
|
|
ds_stack_top(call_st).addFunction(con_if);
|
|
|
|
|
ds_stack_push(call_st, con_if.if_true);
|
|
|
|
|
ds_stack_push(blok_st, con_if);
|
|
|
|
|
continue;
|
2024-03-02 10:08:44 +01:00
|
|
|
|
|
2023-08-12 15:43:18 +02:00
|
|
|
|
case "elseif":
|
|
|
|
|
var con_if = ds_stack_pop(blok_st);
|
|
|
|
|
var con_elif = new __funcIf();
|
|
|
|
|
con_elif.condition = evaluateFunctionTree(_cond);
|
|
|
|
|
|
|
|
|
|
con_if.if_false.addFunction(con_elif);
|
|
|
|
|
ds_stack_push(call_st, con_elif.if_true);
|
|
|
|
|
ds_stack_push(blok_st, con_elif);
|
|
|
|
|
continue;
|
2024-03-02 10:08:44 +01:00
|
|
|
|
|
2023-08-12 15:43:18 +02:00
|
|
|
|
case "else":
|
|
|
|
|
var con_if = ds_stack_pop(blok_st);
|
|
|
|
|
|
|
|
|
|
ds_stack_push(call_st, con_if.if_false);
|
|
|
|
|
continue;
|
2024-03-02 10:08:44 +01:00
|
|
|
|
|
2023-08-12 15:43:18 +02:00
|
|
|
|
case "for":
|
2024-03-02 10:08:44 +01:00
|
|
|
|
|
2023-08-12 15:43:18 +02:00
|
|
|
|
var con_for = new __funcFor();
|
|
|
|
|
var cond = string_splice(_cond, ":");
|
|
|
|
|
if(array_length(cond) == 2) {
|
|
|
|
|
con_for.itr_array = true;
|
|
|
|
|
con_for.cond_arr = evaluateFunctionTree(cond[1]);
|
|
|
|
|
|
|
|
|
|
cond[0] = string_trim(cond[0]);
|
|
|
|
|
var _itr = string_split(cond[0], ",");
|
|
|
|
|
if(array_length(_itr) == 1)
|
|
|
|
|
con_for.cond_iter = cond[0];
|
|
|
|
|
else if(array_length(_itr) == 2) {
|
|
|
|
|
con_for.cond_indx = string_trim(_itr[0]);
|
|
|
|
|
con_for.cond_iter = string_trim(_itr[1]);
|
|
|
|
|
}
|
|
|
|
|
} else if(array_length(cond) == 3) {
|
|
|
|
|
con_for.itr_array = false;
|
|
|
|
|
con_for.cond_init = evaluateFunctionTree(cond[0]);
|
|
|
|
|
con_for.cond_iter = evaluateFunctionTree(cond[1]);
|
|
|
|
|
con_for.cond_term = evaluateFunctionTree(cond[2]);
|
|
|
|
|
}
|
|
|
|
|
ds_stack_top(call_st).addFunction(con_for);
|
|
|
|
|
ds_stack_push(call_st, con_for.action);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(ds_stack_empty(call_st)) {
|
|
|
|
|
print("Block stack empty, how?");
|
|
|
|
|
} else {
|
|
|
|
|
var _top = ds_stack_top(call_st);
|
|
|
|
|
_top.addFunction(evaluateFunctionTree(_fx));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_stack_destroy(call_st);
|
|
|
|
|
ds_stack_destroy(blok_st);
|
|
|
|
|
|
|
|
|
|
return flist;
|
2023-11-23 01:41:18 +01:00
|
|
|
|
} #endregion
|
2023-08-12 15:43:18 +02:00
|
|
|
|
|
2023-11-23 01:41:18 +01:00
|
|
|
|
function evaluateFunctionTree(fx) { #region //////////////////////////////////////////// STATEMENT PARSER ////////////////////////////////////////////
|
2023-11-17 06:24:31 +01:00
|
|
|
|
static __BRACKETS = [ "(", ")", "[", "]", "]" ];
|
2023-08-12 15:43:18 +02:00
|
|
|
|
|
|
|
|
|
var pres = global.EQUATION_PRES;
|
|
|
|
|
var vl = ds_stack_create();
|
|
|
|
|
var op = ds_stack_create();
|
|
|
|
|
var last_push = "";
|
|
|
|
|
|
|
|
|
|
fx = functionStringClean(fx);
|
|
|
|
|
var len = string_length(fx);
|
|
|
|
|
var l = 1;
|
|
|
|
|
var ch = "";
|
|
|
|
|
var cch = "";
|
|
|
|
|
var _ch = "";
|
|
|
|
|
var in_str = false;
|
|
|
|
|
|
|
|
|
|
printIf(global.LOG_EXPRESSION, $"===== Evaluating function: {fx} =====");
|
|
|
|
|
|
|
|
|
|
while(l <= len) {
|
|
|
|
|
ch = string_char_at(fx, l);
|
|
|
|
|
|
2023-11-17 06:24:31 +01:00
|
|
|
|
printIf(global.LOG_EXPRESSION, $"Analyzing {ch}");
|
2023-08-12 15:43:18 +02:00
|
|
|
|
|
|
|
|
|
if(ds_map_exists(pres, ch)) { //symbol is operator
|
|
|
|
|
last_push = "op";
|
|
|
|
|
|
|
|
|
|
if(ds_stack_empty(op)) ds_stack_push(op, ch);
|
|
|
|
|
else {
|
|
|
|
|
var _top = ds_stack_top(op);
|
|
|
|
|
if(_top == "(" || ds_map_exists(global.FUNCTIONS, _top) || pres[? ch] > pres[? _top]) {
|
|
|
|
|
ds_stack_push(op, ch);
|
|
|
|
|
} else {
|
|
|
|
|
while(pres[? ch] <= pres[? ds_stack_top(op)] && !ds_stack_empty(op))
|
|
|
|
|
ds_stack_push(vl, buildFuncTree(ds_stack_pop(op), vl));
|
|
|
|
|
ds_stack_push(op, ch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l++;
|
|
|
|
|
} else if (ch == "(") {
|
|
|
|
|
if(last_push == "fn") ds_stack_push(op, [ "〚", ds_stack_size(vl) ]);
|
|
|
|
|
else ds_stack_push(op, ch);
|
|
|
|
|
last_push = "op";
|
|
|
|
|
l++;
|
|
|
|
|
} else if (ch == ")") {
|
|
|
|
|
while(!ds_stack_empty(op)) {
|
|
|
|
|
var _top = ds_stack_pop(op);
|
|
|
|
|
if(_top == "(") break;
|
|
|
|
|
if(is_array(_top) && _top[0] == "〚") {
|
|
|
|
|
var arr = [];
|
|
|
|
|
while(ds_stack_size(vl) > _top[1])
|
|
|
|
|
array_insert(arr, 0, ds_stack_pop(vl));
|
|
|
|
|
|
|
|
|
|
ds_stack_push(vl, new __funcTree(ds_stack_pop(op), arr));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_stack_push(vl, buildFuncTree(_top, vl));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_push = "vl";
|
|
|
|
|
l++;
|
|
|
|
|
} else if (ch == "[") {
|
2023-11-17 06:24:31 +01:00
|
|
|
|
if(last_push == "vl") { // Get array member | a[1]
|
2023-08-12 15:43:18 +02:00
|
|
|
|
ds_stack_push(op, "@");
|
|
|
|
|
ds_stack_push(op, ch);
|
2023-11-17 06:24:31 +01:00
|
|
|
|
} else // Create array member | a = [1]
|
2023-08-12 15:43:18 +02:00
|
|
|
|
ds_stack_push(op, [ "{", ds_stack_size(vl) ]);
|
|
|
|
|
|
|
|
|
|
last_push = "op";
|
|
|
|
|
l++;
|
2023-11-17 06:24:31 +01:00
|
|
|
|
} else if (ch == "]") {
|
2023-08-12 15:43:18 +02:00
|
|
|
|
while(!ds_stack_empty(op)) {
|
|
|
|
|
var _top = ds_stack_pop(op);
|
|
|
|
|
if(_top == "[") break;
|
|
|
|
|
if(is_array(_top) && _top[0] == "{") {
|
|
|
|
|
var arr = [];
|
|
|
|
|
while(ds_stack_size(vl) > _top[1])
|
|
|
|
|
array_insert(arr, 0, ds_stack_pop(vl));
|
2023-11-17 06:24:31 +01:00
|
|
|
|
|
2023-08-12 15:43:18 +02:00
|
|
|
|
ds_stack_push(vl, new __funcTree("【", arr));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_stack_push(vl, buildFuncTree(_top, vl));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_push = "vl";
|
|
|
|
|
l++;
|
|
|
|
|
} else if (ch == ",") {
|
|
|
|
|
while(!ds_stack_empty(op)) {
|
|
|
|
|
var _top = ds_stack_top(op);
|
2023-11-23 01:41:18 +01:00
|
|
|
|
if(_top == "[" || _top == "(") break;
|
|
|
|
|
if(is_array(_top) && (_top[0] == "{" || _top[0] == "〚")) break;
|
2023-08-12 15:43:18 +02:00
|
|
|
|
|
2023-11-17 06:24:31 +01:00
|
|
|
|
var _top = ds_stack_pop(op);
|
2023-08-12 15:43:18 +02:00
|
|
|
|
ds_stack_push(vl, buildFuncTree(_top, vl));
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-17 06:24:31 +01:00
|
|
|
|
last_push = "op";
|
2023-08-12 15:43:18 +02:00
|
|
|
|
l++;
|
|
|
|
|
} else {
|
|
|
|
|
var vsl = "";
|
|
|
|
|
|
|
|
|
|
while(l <= len) {
|
|
|
|
|
cch = string_char_at(fx, l);
|
|
|
|
|
if(ds_map_exists(pres, cch) || array_exists(__BRACKETS, cch)) break;
|
2023-11-17 06:24:31 +01:00
|
|
|
|
if(cch == ",")
|
2023-08-12 15:43:18 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
vsl += cch;
|
|
|
|
|
l++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(vsl == "") continue;
|
|
|
|
|
|
|
|
|
|
if(ds_map_exists(global.FUNCTIONS, vsl)) { //function
|
|
|
|
|
ds_stack_push(op, vsl);
|
|
|
|
|
last_push = "fn";
|
|
|
|
|
} else {
|
|
|
|
|
vsl = string_trim(vsl);
|
|
|
|
|
|
|
|
|
|
switch(vsl) {
|
2024-03-02 10:08:44 +01:00
|
|
|
|
case "e" : ds_stack_push(vl, 2.7182818284); break;
|
|
|
|
|
case "pi": ds_stack_push(vl, pi); break;
|
2023-08-12 15:43:18 +02:00
|
|
|
|
default : ds_stack_push(vl, isNumber(vsl)? toNumber(vsl) : vsl); break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
last_push = "vl";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-17 06:24:31 +01:00
|
|
|
|
printIf(global.LOG_EXPRESSION, $"\tvl = {ds_stack_to_array(vl)}\n\top = {ds_stack_to_array(op)}");
|
2023-08-12 15:43:18 +02:00
|
|
|
|
|
|
|
|
|
_ch = ch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while(!ds_stack_empty(op))
|
|
|
|
|
ds_stack_push(vl, buildFuncTree(ds_stack_pop(op), vl));
|
|
|
|
|
|
|
|
|
|
var tree = ds_stack_empty(vl)? noone : ds_stack_pop(vl);
|
|
|
|
|
|
|
|
|
|
ds_stack_destroy(op);
|
|
|
|
|
ds_stack_destroy(vl);
|
|
|
|
|
|
|
|
|
|
if(!is_struct(tree))
|
|
|
|
|
tree = new __funcTree("", tree);
|
|
|
|
|
|
|
|
|
|
printIf(global.LOG_EXPRESSION, tree);
|
|
|
|
|
printIf(global.LOG_EXPRESSION, "");
|
|
|
|
|
|
|
|
|
|
return tree;
|
2023-11-23 01:41:18 +01:00
|
|
|
|
} #endregion
|
2023-08-12 15:43:18 +02:00
|
|
|
|
|
2023-11-23 01:41:18 +01:00
|
|
|
|
function buildFuncTree(operator, vl) { #region
|
2023-08-12 15:43:18 +02:00
|
|
|
|
if(ds_stack_empty(vl)) return noone;
|
|
|
|
|
|
|
|
|
|
if(ds_map_exists(global.FUNCTIONS, operator)) {
|
|
|
|
|
if(ds_stack_empty(vl))
|
|
|
|
|
return noone;
|
|
|
|
|
|
|
|
|
|
var _v1 = ds_stack_pop(vl);
|
|
|
|
|
return new __funcTree(operator, _v1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(operator) {
|
|
|
|
|
case "@":
|
|
|
|
|
var _v1 = ds_stack_pop(vl);
|
|
|
|
|
var _v2 = ds_stack_pop(vl);
|
|
|
|
|
return new __funcTree(operator, _v2, _v1);
|
|
|
|
|
|
|
|
|
|
case "-":
|
|
|
|
|
case "∸":
|
|
|
|
|
|
|
|
|
|
case "+": //binary operators
|
|
|
|
|
case "*":
|
|
|
|
|
case "$":
|
|
|
|
|
case "/":
|
|
|
|
|
case "%":
|
|
|
|
|
|
|
|
|
|
case "|":
|
|
|
|
|
case "&":
|
|
|
|
|
case "^":
|
|
|
|
|
case "»":
|
|
|
|
|
case "«":
|
|
|
|
|
|
|
|
|
|
case "=":
|
|
|
|
|
case "⩵":
|
|
|
|
|
case "≠":
|
|
|
|
|
case "≤":
|
|
|
|
|
case "≥":
|
|
|
|
|
case "<":
|
|
|
|
|
case ">":
|
|
|
|
|
|
|
|
|
|
case "⊕":
|
|
|
|
|
case "⊖":
|
|
|
|
|
case "⊗":
|
|
|
|
|
case "⊘":
|
|
|
|
|
|
|
|
|
|
if(ds_stack_size(vl) >= 2) {
|
|
|
|
|
var _v1 = ds_stack_pop(vl);
|
|
|
|
|
var _v2 = ds_stack_pop(vl);
|
|
|
|
|
return new __funcTree(operator, _v2, _v1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default: return new __funcTree(operator, ds_stack_pop(vl));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return noone;
|
2023-11-23 01:41:18 +01:00
|
|
|
|
} #endregion
|
2023-08-12 15:43:18 +02:00
|
|
|
|
#endregion
|