#define string_evaluate
//string_evaluate(expression)

var expr, queue, stack, unary, token, char, i;

expr = argument0;
queue = ds_queue_create();
stack = ds_stack_create();
unary = true;

for (i = 1; i <= string_length(expr); i += 1) {
    char = string_char_at(expr,i);
    
    switch (char) {
        case "0":
        case "1":
        case "2":
        case "3":
        case "4":
        case "5":
        case "6":
        case "7":
        case "8":
        case "9":
        case ".":
            token = char;
            char = string_char_at(expr,i + 1);
            
            while (ord(char) >= 48 && ord(char) <= 57 || 
            (char == "." && string_count(".",token) == 0)) {
                token += char;
                i += 1;
                char = string_char_at(expr,i + 1);
            }
            
            unary = false;
            ds_queue_enqueue(queue,real(token));
            continue;
            
        case "+":
        case "-":
        case "*":
        case "/":
            if (unary) {
                if (char == "+" || char == "-") {
                    char += char;
                } else {
                    ds_queue_destroy(queue);
                    ds_stack_destroy(stack);
                    return ("Unexpected unary operator: " + char);
                }
            }
            
            while (!ds_stack_empty(stack)) {
                token = ds_stack_top(stack);
                
                if ((char != "*" && char != "/") || 
                (token != "+" && token != "-") && 
                (char != "++" && char != "--") && (token != "(")) {
                    ds_queue_enqueue(queue,ds_stack_pop(stack));
                } else break;
            }
            
        case "(":
            unary = true;
            ds_stack_push(stack,char);
            continue;
            
        case ")":
            if (ds_stack_empty(stack)) {
                ds_queue_destroy(queue);
                ds_stack_destroy(stack);
                return ("Symbol ( expected");
            }
            
            token = ds_stack_pop(stack);
            
            while (token != "(") {
                ds_queue_enqueue(queue,token);
                
                if (ds_stack_empty(stack)) {
                    ds_queue_destroy(queue);
                    ds_stack_destroy(stack);
                    return ("Symbol ( expected");
                }
                
                token = ds_stack_pop(stack);
            }
            
            unary = false;
            
        case " ":
            continue;
            
        default:
            ds_queue_destroy(queue);
            ds_stack_destroy(stack);
            return ("Unexpected character: " + char);
    }
}

while (!ds_stack_empty(stack)) {
    token = ds_stack_pop(stack);
    
    if (token == "(") {
        ds_queue_destroy(queue);
        ds_stack_destroy(stack);
        return ("Symbol ) expected");
    }
    
    ds_queue_enqueue(queue,token);
}

while (!ds_queue_empty(queue)) {
    token = ds_queue_dequeue(queue);
    
    if (is_string(token)) {
        char = ds_stack_pop(stack);
        
        switch (token) {
            case "++": token = char; break
            case "--": token = -char; break;
            case "+": token = ds_stack_pop(stack) + char; break;
            case "-": token = ds_stack_pop(stack) - char; break;
            case "*": token = ds_stack_pop(stack) * char; break;
            case "/":
                if (char == 0) {
                    ds_queue_destroy(queue);
                    ds_stack_destroy(stack);
                    return ("Division by 0");
                }
            
                token = ds_stack_pop(stack) / char; 
                break;
        }
    }
    
    ds_stack_push(stack,token);
}

if (ds_stack_size(stack) != 1) {
    ds_queue_destroy(queue);
    ds_stack_destroy(stack);
    return ("Operator expected");
}

token = ds_stack_pop(stack);

ds_queue_destroy(queue);
ds_stack_destroy(stack);

return token;

#define string_evaluate_ext
//string_evaluate_ext(expression)

var expr, queue, stack, arity, unary, token, char, i;

expr = argument0;
queue = ds_queue_create();
stack = ds_stack_create();
arity = ds_stack_create();
unary = true;

for (i = 1; i <= string_length(expr); i += 1) {
    char = string_char_at(expr,i);
    
    switch (char) {
        case "0":
        case "1":
        case "2":
        case "3":
        case "4":
        case "5":
        case "6":
        case "7":
        case "8":
        case "9":
        case ".":
            token = char;
            char = string_char_at(expr,i + 1);
            
            while (ord(char) >= 48 && ord(char) <= 57 || 
            (char == "." && string_count(".",token) == 0)) {
                token += char;
                i += 1;
                char = string_char_at(expr,i + 1);
            }
            
            unary = false;
            ds_queue_enqueue(queue,real(token));
            continue;
            
        case "=":
        case ">":
        case "<":  
        case "&":
        case "|":
        case "^":     
        case "+":
        case "-":
        case "*":
        case "/":
        case "!":
        case "~":
            token = char;
            char = string_char_at(expr,i + 1);
        
            if (valid_operator(token,char)) {
                token += char;
                i += 1;
            }
            
            if (unary) {
                switch (token) {
                    case "+":
                    case "-": 
                        token += token;
                        
                    case "!":
                    case "~": 
                        break;
                        
                    default:
                        return error("Unexpected unary operator: " + token,queue,stack,arity);
                }
            }
            
            while (!ds_stack_empty(stack)) {
                char = ds_stack_top(stack);
                
                if (precedence(token) <= precedence(char) && precedence(token) < 6 && char != "(") {
                    ds_queue_enqueue(queue,ds_stack_pop(stack));
                } else break;
            }
            
            char = token;
            
        case "(":
            unary = true;
            ds_stack_push(stack,char);
            continue;
            
        case ",":
            if (ds_stack_empty(stack)) return error("Symbol ( expected",queue,stack,arity);
            token = ds_stack_top(stack);
            
            while (token != "(") {
                ds_queue_enqueue(queue,ds_stack_pop(token));
                
                if (ds_stack_empty(stack)) return error("Symbol ( expected",queue,stack,arity);
                token = ds_stack_top(stack);
            }
            
            if (ds_stack_empty(arity)) return error("Unexpected symbol ,",queue,stack,arity);
            char = string_char_at(expr,i + 1);
            
            while (char == " ") {
                i += 1;
                char = string_char_at(expr,i + 1);
            }
            
            if (char == ")" || char == ",") return error("Unexpected symbol ,",queue,stack,arity);  
            ds_stack_push(arity,ds_stack_pop(arity) + 1);
            unary = true;
            continue;
            
        case ")":
            if (ds_stack_empty(stack)) return error("Symbol ( expected",queue,stack,arity);
            token = ds_stack_pop(stack);
            
            while (token != "(") {
                ds_queue_enqueue(queue,token);
                
                if (ds_stack_empty(stack)) return error("Symbol ( expected",queue,stack,arity);
                token = ds_stack_pop(stack);
            }
            
            if (!ds_stack_empty(stack)) {
                token = ds_stack_top(stack);
                char = string_char_at(token,0);
            
                if ((ord(char) >= 65 && ord(char) <= 90) || (ord(char) >= 97 && ord(char) <= 122) && precedence(token) == -1) {
                    ds_queue_enqueue(queue,ds_stack_pop(stack));
                    ds_queue_enqueue(queue,ds_stack_pop(arity));
                }
            }
            
            unary = false;
            
        case " ":
            continue;
            
        default:
            if ((ord(char) < 65 || ord(char) > 90) && (ord(char) < 97 || ord(char) > 122)) return error("Unexpected character: " + char,queue,stack,arity);
            token = char;
            char = string_char_at(expr,i + 1);
            
            while ((ord(char) >= 65 && ord(char) <= 90) || (ord(char) >= 97 && ord(char) <= 122) || (ord(char) >= 48 && ord(char) <= 57) || char == "_") {
                token += char;
                i += 1;
                char = string_char_at(expr,i + 1);
            }
            
            if (char == "(") {
                do {
                    i += 1;
                    char = string_char_at(expr,i + 1);
                } until (char != " ");
                
                if (char == ")") {
                    i += 1;
                    unary = false;
                    ds_queue_enqueue(queue,token);
                    ds_queue_enqueue(queue,0);
                } else {
                    if (char == ",") return error("Unexpected symbol ,",queue,stack,arity);
                    unary = true;
                    ds_stack_push(stack,token);
                    ds_stack_push(stack,"(");
                    ds_stack_push(arity,1);
                } 
            } else {
                switch (token) {
                    case "and":
                    case "or":
                    case "xor":
                    case "mod":
                    case "div":
                    case "not":
                        unary = true;
                        ds_stack_push(stack,token);
                        break;
                        
                    default:
                        token = constant(token);
                        
                        if (is_string(token)) return error("Unknown constant: " + token,queue,stack,arity);
                        unary = false;
                        ds_queue_enqueue(queue,token);
                }
            }
            
            continue;
    }
}

while (!ds_stack_empty(stack)) {
    token = ds_stack_pop(stack);
    
    if (token == "(") return error("Symbol ) expected",queue,stack,arity);
    ds_queue_enqueue(queue,token);
}

while (!ds_queue_empty(queue)) {
    token = ds_queue_dequeue(queue);
    
    if (is_string(token)) {
        switch (token) {
            case "++":
            case "--":
            case "~":
            case "!":
            case "not":
                token = operator_unary(token,stack);
            
                if (is_string(token)) return error(token,queue,stack,arity);
                break;
            
            case "&&":
            case "||":
            case "^^": 
            case "and":
            case "or":
            case "xor":
            case "=":
            case "==":
            case ">":
            case "<":
            case ">=":
            case "<=":
            case "!=":
            case "&":
            case "|":
            case "^":
            case "<<":
            case ">>":
            case "+":
            case "-":
            case "/":
            case "*":
            case "mod":
            case "div":
                token = operator_binary(token,stack);
            
                if (is_string(token)) return error(token,queue,stack,arity);
                break;
            
            default:
                token = function(token,ds_queue_dequeue(queue),stack);
            
                if (is_string(token)) return error(token,queue,stack,arity);
                break;
        }
    }
    
    ds_stack_push(stack,token);
}

if (ds_stack_size(stack) != 1) return error("Operator expected",queue,stack,arity);
token = ds_stack_pop(stack);

ds_queue_destroy(queue);
ds_stack_destroy(stack);
ds_stack_destroy(arity);

return token;

#define precedence
//precedence(operator)

switch (argument0) {
    case "&&":
    case "||":
    case "^^": 
    case "and":
    case "or":
    case "xor":
        return 0;
        
    case "=":
    case "==":
    case ">":
    case "<":
    case ">=":
    case "<=":
    case "!=":
        return 1;
        
    case "&":
    case "|":
    case "^":
        return 2;
        
    case "<<":
    case ">>":
        return 3;
        
    case "+":
    case "-":
        return 4;
        
    case "*":
    case "/": 
    case "mod":
    case "div":
        return 5;
        
    case "++":
    case "--":
    case "~":
    case "!":
    case "not":
        return 6;
}

return -1;

#define valid_operator
//valid_operator(token,char)

switch (argument0) {
    case "=":
    case "!":
        return (argument1 == "=");
        
    case ">":
    case "<":
        if (argument1 == "=") return true;
        
    case "&":
    case "^":
    case "|":
        return (argument1 == argument0);
}

return false;

#define operator_unary
//operator_unary(token,stack)

if (ds_stack_empty(argument1)) return ("Wrong number of arguments to unary operator: " + argument0);

var a;

a = ds_stack_pop(argument1);

switch (argument0) {
    case "++": return a;
    case "--": return -a;
    case "~": return ~a;
    case "!":
    case "not": return !a;
}

#define operator_binary
//operator_binary(token,stack)

if (ds_stack_size(argument1) < 2) return ("Wrong number of arguments to binary operator: " + argument0);

var a, b;

b = ds_stack_pop(argument1);
a = ds_stack_pop(argument1);

switch (argument0) {
    case "&&": 
    case "and": return (a && b);
    case "||": 
    case "or": return (a || b);
    case "^^": 
    case "xor": return (a ^^ b);
    case "=":
    case "==": return (a == b);
    case ">": return (a > b);
    case "<": return (a < b);
    case ">=": return (a >= b);
    case "<=": return (a <= b);
    case "!=": return (a != b);
    case "&": return (a & b);
    case "|": return (a | b);
    case "^": return (a ^ b);
    case "<<": return (a << b);
    case ">>": return (a >> b);
    case "+": return (a + b);
    case "-": return (a - b);
    case "*": return (a * b);
    case "mod": return (a mod b);
    case "/":
        if (b == 0) return ("Division by 0");
        return (a / b);
        
    case "div":
        if (b == 0) return ("Division by 0");
        return (a div b);
}

#define constant
//constant(token)

switch (argument0) {
    case "false": return 0;
    case "true": return 1;
    case "pi": return 3.14;
    case "c_aqua": return c_aqua;
    case "c_black": return c_black;
    case "c_blue": return c_blue;
    case "c_dkgray": return c_dkgray;
    case "c_fuchsia": return c_fuchsia;
    case "c_gray": return c_gray;
    case "c_green": return c_green;
    case "c_lime": return c_lime;
    case "c_ltgray": return c_ltgray;
    case "c_maroon": return c_maroon;
    case "c_navy": return c_navy;
    case "c_olive": return c_olive;
    case "c_orange": return c_orange;
    case "c_purple": return c_purple;
    case "c_red": return c_red;
    case "c_silver": return c_silver;
    case "c_teal": return c_teal;
    case "c_white": return c_white;
    case "c_yellow": return c_yellow;
}

return argument0;

#define function
//function(token,count,stack)

var count, arg, i;

count = argument1;

for (i = count - 1; i >= 0; i -= 1) {
    arg[i] = ds_stack_pop(argument2);
}

switch (argument0) {
    case "abs": if (count != 1) break else return abs(arg[0]);
    case "arcsin": if (count != 1) break else return arcsin(arg[0]);
    case "arccos": if (count != 1) break else return arccos(arg[0]);
    case "arctan": if (count != 1) break else return arctan(arg[0]);
    case "arctan2": if (count != 2) break else return arctan2(arg[0],arg[1]);
    case "ceil": if (count != 1) break else return ceil(arg[0]);
    case "color_get_blue": if (count != 1) break else return color_get_blue(arg[0]);
    case "color_get_green": if (count != 1) break else return color_get_green(arg[0]);
    case "color_get_hue": if (count != 1) break else return color_get_hue(arg[0]);
    case "color_get_red": if (count != 1) break else return color_get_red(arg[0]);
    case "color_get_saturation": if (count != 1) break else return color_get_saturation(arg[0]);
    case "color_get_value": if (count != 1) break else return color_get_value(arg[0]);
    case "cos": if (count != 1) break else return cos(arg[0]);
    case "degtorad": if (count != 1) break else return degtorad(arg[0]);
    case "exp": if (count != 1) break else return exp(arg[0]);
    case "floor": if (count != 1) break else return floor(arg[0]);
    case "frac": if (count != 1) break else return frac(arg[0]);
    case "irandom": if (count != 1) break else return irandom(arg[0]);
    case "irandom_range": if (count != 2) break else return irandom_range(arg[0],arg[1]);
    case "lengthdir_x": if (count != 2) break else return lengthdir_x(arg[0],arg[1]);
    case "lengthdir_y": if (count != 2) break else return lengthdir_y(arg[0],arg[1]);
    case "log2": if (count != 1) break else return log2(arg[0]);
    case "log10": if (count != 1) break else return log10(arg[0]);
    case "logn": if (count != 2) break else return logn(arg[0],arg[1]);
    case "ln": if (count != 1) break else return ln(arg[0]);
    case "make_color_hsv": if (count != 3) break else return make_color_hsv(arg[0],arg[1],arg[2]);
    case "make_color_rgb": if (count != 3) break else return make_color_rgb(arg[0],arg[1],arg[2]);
    case "merge_color": if (count != 3) break else return merge_color(arg[0],arg[1],arg[2]);
    case "power": if (count != 2) break else return power(arg[0],arg[1]);
    case "point_distance": if (count != 4) break else return point_distance(arg[0],arg[1],arg[2],arg[3]);
    case "point_direction": if (count != 4) break else return point_direction(arg[0],arg[1],arg[2],arg[3]);
    case "radtodeg": if (count != 1) break else return radtodeg(arg[0]);
    case "random": if (count != 1) break else return random(arg[0]);
    case "random_range": if (count != 2) break else return random_range(arg[0],arg[1]);
    case "round": if (count != 1) break else return round(arg[0]);
    case "sign": if (count != 1) break else return sign(arg[0]);
    case "sqrt": if (count != 1) break else return sqrt(arg[0]);
    case "sqr": if (count != 1) break else return sqr(arg[0]);
    case "sin": if (count != 1) break else return sin(arg[0]);
    case "tan": if (count != 1) break else return tan(arg[0]);
    default:
        return ("Unknown function: " + argument0);
}

return ("Wrong number of arguments to function: " + argument0);

#define error
//error(str,queue,stack,arity)

ds_queue_destroy(argument1);
ds_stack_destroy(argument2);
ds_stack_destroy(argument3);

return argument0;

