code:
#include "JSParser.h"
#include "BrowserJS.h"
#include "../../../tools/StringUtils.h"

#include <iostream>
#include <fstream>
#include <algorithm>

extern std::ofstream assignfile;
extern std::ofstream execfile;

std::string typeOfStorage(js_internal_storage *storage) {
    if (!storage) {
        std::cout << "null passed into typeOfStorage\n";
        return "Corrupt";
    }
    js_string *string = dynamic_cast<js_string*>(storage);
    if (string) return "string";
    js_number *number = dynamic_cast<js_number*>(storage);
    if (number) return "number";
    js_bool *jbool = dynamic_cast<js_bool*>(storage);
    if (jbool) return "bool";
    js_function *func = dynamic_cast<js_function*>(storage);
    if (func) return "func";
    js_array *arr = dynamic_cast<js_array*>(storage);
    if (arr) return "array";
    js_object *obj = dynamic_cast<js_object*>(storage);
    if (obj) return "object";
    js_reference *ref = dynamic_cast<js_reference*>(storage);
    if (ref) return "reference";
    js_forward *fwd = dynamic_cast<js_forward*>(storage);
    if (fwd) return "forward";
    return "unknown";
}

bool jsIsFalse(js_internal_storage *generic) {
    std::string type = typeOfStorage(generic);
    if (type == "string") {
        js_string *string = dynamic_cast<js_string*>(generic);
        return string->value == "" || string->value == "0";
    } else
    if (type == "number") {
        js_number *jnum = dynamic_cast<js_number*>(generic);
        return !jnum->value;
    } else
        if (type == "bool") {
        js_bool *jb = dynamic_cast<js_bool*>(generic);
        return !jb->value;
    } else if (type == "func") {
        return false; // func are always true
    } else
    //if (type == "unknown") {
        // why is this needed?
        //return true;
    //} else
    if (type == "Corrupt") {
        return true;
    } else {
        std::cout << "unknown type [" << type << "]\n";
    }
    return false;
}

js_internal_storage *jsLocateKey(const js_function *scope, const std::string key) {
    // do we have it?
    auto locate = scope->locals.value.find(key);
    if (locate == scope->locals.value.end()) {
        // we do have a parent?
        if (scope->parentScope) {
            return jsLocateKey(scope->parentScope, key);
        }
        // no parent
        return nullptr;
    }
    // we have it
    return locate->second;
}

std::string jsLocatePtrKey(const js_function *scope, js_internal_storage *val) {
    // do we have it?
    std::string sourceKey = "";
    for(auto it2 : scope->locals.value) {
        std::cout << "ptr[" << val << "]==[" << it2.second << "] root[" << scope << "]\n";
        if (it2.second == val) {
            return it2.first;
            break;
        }
    }
    // we do have a parent?
    if (scope->parentScope) {
        return jsLocatePtrKey(scope->parentScope, val);
    }
    // no parent
    return "";
}

void jsDisplayScope(const js_function *scope, size_t level) {
    std::cout << "There are " << scope->locals.value.size() << " elements at this level " << level << "\n";
    for(auto it : scope->locals.value) {
        std::string type = typeOfStorage(it.second);
        if (type == "string") {
            js_string *string = dynamic_cast<js_string*>(it.second);
            std::cout << "[" << level << "]" << "[" << it.first << "] stringValue[" << string->value << "] address[" << it.second << "]\n";
        } else {
            std::cout << "[" << level << "]" << "[" << it.first << "] type[" << type << "] address[" << it.second << "]\n";
        }
    }
    if (scope->parentScope) {
        level++;
        jsDisplayScope(scope->parentScope, level);
    }
}

// if a function don't pass a starting or ending {}
std::vector<std::string> jsGetTokens(const std::string &source, const size_t start) {
    std::vector<std::string> tokens;
    //std::cout << "jsGetTokens start [" << source.substr(start) << "]\n";
    // tokenize it
    size_t cursor;
    unsigned char state = 0;
    size_t last = start?start - 1 : 0;
    size_t quoteStart = 0;
    size_t scopeLevel = 0;
    size_t jsonStart = 0;
    size_t jsonLevel = 0;
    size_t parenLevel = 0;
    size_t parenStart = 0;
    size_t functionStart = 0;
    for (cursor = start; cursor < source.length(); cursor++) {
        //std::cout << "jsGetTokens step [" << cursor << "] char[" << source[cursor] << "] state[" << std::to_string(state) << "]\n";
        if (state == 0) {
            if (source[cursor] == '{') {
                state = 1; // JSON
                jsonStart = cursor;
                jsonLevel++;
                //std::cout << "Entering JSON: " << cursor << std::endl;
            } else if (source[cursor] == '(') {
                state = 8; // in a function call or prototype
                parenStart = cursor;
                parenLevel++;
            } else if (source[cursor] == '\'') { // quotes just for allowing [;{}\n] in quotes
                quoteStart = cursor;
                state = 4;
            } else if (source[cursor] == '"') {
                quoteStart = cursor;
                state = 5;
            } else if (source[cursor] == '/' && source.length() > cursor + 1 && source[cursor + 1] == '/') {
                // single line comment
                state = 2;
            } else if (source[cursor] == '/' && source.length() > cursor + 1 && source[cursor + 1] == '*') {
                // Multiline comment
                state = 3;
            } else if (source[cursor] == '/') {
                // regex
                if (source.length() > cursor + 1) {
                    size_t endex = locateRegexEnd(source, cursor + 1); // +1 because it can't start on /
                    cursor = endex; // put here after regex
                    // state = 9;
                } else {
                    std::cout << "jsGetTokens - warning - no characters left in state 0\n";
                }
            } else if (source[cursor] == 'v' && source.length() > cursor + 3 && source[cursor + 1] == 'a'
                       && source[cursor + 2] == 'r' && source[cursor + 3] == ' ') {
                // var
                state = 7;
            } else if (source[cursor] == 'f' && source.length() > cursor + 8 && source[cursor + 1] == 'u'
                       && source[cursor + 2] == 'n'  && source[cursor + 3] == 'c'  && source[cursor + 4] == 't'
                       && source[cursor + 5] == 'i'  && source[cursor + 6] == 'o'  && source[cursor + 7] == 'n') {
                //std::cout << "Entering function: " << cursor << std::endl;
                state = 6;
                functionStart = cursor;
            }
        } else if (state == 1) {
            // inside a scope (JSON)
            if (source[cursor] == '{') {
                jsonLevel++;
            } else if (source[cursor] == '}') {
                jsonLevel--;
                if (!jsonLevel) {
                    //std::cout << "Exiting JSON: " << source.substr(jsonStart, cursor - jsonStart) << "\n" << std::endl;
                    state = 0; // exit JSON
                }
            }
        } else if (state == 8) {
            // inside a paren (function)
            //std::cout << "looking at [" << source[cursor] << "]@" << cursor << std::endl;
            if (source[cursor] == '(') {
                parenLevel++;
            } else if (source[cursor] == ')') {
                parenLevel--;
                if (!parenLevel) {
                    //std::cout << "Exiting Paren: " << source.substr(parenStart, cursor - parenStart) << "\n" << std::endl;
                    state = 0; // exit JSON
                }
            }
        } else if (state == 2) {
            // inside a single line comment
            if (source[cursor] == '\n') {
                last = cursor;
                state = 0;
            }
        } else if (state == 3) {
            // inside a multiline comment
            if (source[cursor] == '*'  && source.length() > cursor + 1 && source[cursor + 1] == '/') {
                // end multiline comment
                last = cursor;
                state = 0;
            }
        } else if (state == 4) {
            // inside single quote
            if (source[cursor] == '\'') {
                if (source[cursor - 1] != '\\') {
                    //std::string quote = source.substr(quoteStart + 1, cursor - quoteStart - 1);
                    //std::cout << "single quote: " << quote << std::endl;
                    state = 0;
                }
            }
        } else if (state == 5) {
            // inside double quote
            if (source[cursor] == '"') {
                if (source[cursor - 1] != '\\') {
                    //std::string quote = source.substr(quoteStart + 1, cursor - quoteStart - 1);
                    //std::cout << "double quote: " << quote << std::endl;
                    state = 0;
                }
            }
        } else if (state == 7) {
        }

        //
        if (source[cursor] == '{') {
            scopeLevel++;
        }
        bool endIt = false;
        if (source[cursor] == '}') {
            scopeLevel--;
            if (state == 6 && !scopeLevel) {
                //std::cout << "Exiting function: " << source.substr(functionStart, cursor - functionStart) << "\n" << std::endl;
                state = 0;
                endIt = true;
            }
        }

        // state 0 or 7, ignore states 1-6
        if ((state == 0 || state == 7) && !scopeLevel) {
            if (source[cursor] == '\n' || source[cursor] == ';' || endIt || (source[cursor] == ',' && state != 7)) {
                // FIXME: ; in for loops
                std::string token = source.substr(last ? last + 1 : last, last ? (cursor - last - 1) : cursor );
                if (source[cursor] == '}') {
                    token += '}';
                }
                // scopeLevel[" << scopeLevel << "]"
                //std::cout << "got token [" << token << "] ending[" << source[cursor] << "] endIt[" << endIt << "]" << std::endl;
                if (token.length()<3) {
                    //std::cout << "token too short [" << token << "]" << std::endl;
                } else {
                    tokens.push_back(token);
                }
                last = cursor;

                if (state == 7) { // allow var constructs to end normally and take us out of var construct
                    state = 0; // reset state
                }
            }
        }
    }
    std::string token = source.substr(last ? last + 1 : last, last ? (cursor - last - 1) : cursor );
    //&& !token.length() // all look like complete valid tokens
    if (!state ) {
        if (token.length()) {
            // we can't just be discarding stuff
            tokens.push_back(token);
        }
        return tokens;
    }
    std::cout << "jsGetTokens - out of characters in state " << std::to_string(state) << " token[" << token << "]" << std::endl;
    std::cout << "got token [" << token << "] ending[" << source[cursor] << "]" << std::endl;
    if (token.length()<3) {
        std::cout << "token too short [" << token << "]" << std::endl;
    } else {
        tokens.push_back(token);
    }
    return tokens;
}

js_internal_storage *jsFunctionCall(std::string funcName, js_function *func, std::string params, js_function &scope) {
    if (isConstruct(funcName)) {
        return executeConstruct(funcName, params, scope);
    } else {
        if (func == nullptr) {
            std::cout << "passed null into jsFunctionCall\n";
            return nullptr;
        }
        return jsParseTokens(func->tokens, &scope);
    }
}

// this should evaluate an expression and return it's return value
int doExpressionLevel = 0;
js_internal_storage *doExpression(js_function &rootScope, std::string token) {
    std::vector<std::string> expression_parts;
    size_t cursor;
    size_t last = 0;
    size_t quoteStart = 0;
    size_t parenStart = 0;
    size_t parenLevel = 0;
    size_t trinaryLevel = 0;
    unsigned char state = 0;
    std::cout << "doExpression start[" << token << "]\n";
    // parse expression
    for (cursor = 0; cursor < token.length(); cursor++) {
        std::cout << "doExpression parse phase state[" << std::to_string(state) << "] char[" << token[cursor] << "]\n";
        if (state == 0) {
            // ||
            // &&
            // <, >, <=, >=, ==, ===, !=, !==
            // +, -
            // *, /, %, !
            // ?, >>, <<
            if (token[cursor] == 'f' && token.length() > cursor + 7 && token[cursor + 1] == 'u'
                && token[cursor + 2] == 'n' && token[cursor + 3] == 'c' && token[cursor + 4] == 't'
                && token[cursor + 5] == 'i' && token[cursor + 6] == 'o' && token[cursor + 7] == 'n') {
                state = 3;
            }
            if (token[cursor] == '\'') {
                quoteStart = cursor;
                state = 4;
            } else
            if (token[cursor] == '"') {
                quoteStart = cursor;
                state = 5;
            } else
            if (token[cursor] == '(') {
                parenStart = cursor;
                parenLevel++;
                state = 8;
                if (last != cursor) {
                    // could be a func call or func def (w/ or w/o call)
                    expression_parts.push_back(token.substr(last, cursor - last));
                }
                last = cursor + 1;
                expression_parts.push_back("(");
            } else
            if (token[cursor] == '{') {
                parenStart = cursor;
                parenLevel++;
                state = 1;
                //std::cout << "last " << last << " cursor " << cursor << std::endl;
                if (last != cursor) {
                    expression_parts.push_back(token.substr(last, cursor - last - 1));
                }
                last = cursor + 1;
                expression_parts.push_back("{");
            }

            // single =
            if (token[cursor] == '=' && token.length() > cursor + 1 && token[cursor + 1] != '=') {
                //state = 1;
                //std::cout << "starting = at " << cursor << " last: " << last << std::endl;
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("=");
            }
            // hack for JSON parsing
            if (token[cursor] == ':') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("=");
                state = 2;
            }
            // ||
            if (token[cursor] == '|' && token.length() > cursor + 1 && token[cursor + 1] == '|') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 2;
                expression_parts.push_back("||");
            }
            if (token[cursor] == '&' && token.length() > cursor + 1 && token[cursor + 1] == '&') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 2;
                expression_parts.push_back("&&");
            }
            if (token[cursor] == '>' && token.length() > cursor + 1 && token[cursor + 1] != '=' && token[cursor + 1] != '>') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back(">");
            }
            if (token[cursor] == '<' && token.length() > cursor + 1 && token[cursor + 1] != '=' && token[cursor + 1] != '<') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("<");
            }
            if (token[cursor] == '<' && token.length() > cursor + 1 && token[cursor + 1] == '&') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 2;
                expression_parts.push_back("<=");
            }
            if (token[cursor] == '>' && token.length() > cursor + 1 && token[cursor + 1] == '&') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 2;
                expression_parts.push_back(">=");
            }
            if (token[cursor] == '=' && token.length() > cursor + 2 && token[cursor + 1] == '=' && token[cursor + 2] != '=') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 2;
                expression_parts.push_back("==");
            }
            if (token[cursor] == '=' && token.length() > cursor + 2 && token[cursor + 1] == '=' && token[cursor + 2] == '=') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 3;
                expression_parts.push_back("===");
            }
            if (token[cursor] == '!' && token.length() > cursor + 2 && token[cursor + 1] == '=' && token[cursor + 2] != '=') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 2;
                expression_parts.push_back("!=");
            }
            if (token[cursor] == '!' && token.length() > cursor + 2 && token[cursor + 1] == '=' && token[cursor + 2] == '=') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 3;
                expression_parts.push_back("!==");
            }
            // +
            if (token[cursor] == '+') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("+");
            } else
            if (token[cursor] == '-') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("-");
            } else
            if (token[cursor] == '*') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("*");
            } else
            if (token[cursor] == '/') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("/");
            } else
            if (token[cursor] == '%') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("%");
            } else
            if (token[cursor] == '!') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("!");
            } else
                if (token[cursor] == '?') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("?");
                trinaryLevel++;
                state = 9;
            }

        } else if (state == 1) {
            if (token[cursor] == '{') {
                parenLevel++;
            } else
                if (token[cursor] == '}') {
                    parenLevel--;
                    if (!parenLevel) {
                        expression_parts.push_back(token.substr(last, cursor - last));
                        last = cursor + 1;
                        expression_parts.push_back("}");
                        state = 0;
                    }
                }
        } else if (state == 2) { // json state (can be moved)
            if (token[cursor] == ',') {
                expression_parts.push_back(token.substr(last, cursor - last));
                last = cursor + 1;
                state = 0;
            }
        } else if (state == 3) { // function start (can be moved)
            if (token[cursor] == '{') {
                // lets put the function prototype
                expression_parts.push_back(token.substr(last, cursor - last));
                last = cursor + 1;
                parenLevel++;
                state = 6; // move to the function body
            }
        } else if (state == 4) {
            if (token[cursor] == '\'') {
                if (token[cursor - 1] != '\\') {
                    //std::string quote = token.substr(quoteStart + 1, cursor - quoteStart - 1);
                    //expression_parts.push_back(quote);
                    //std::cout << "single quote: " << quote << std::endl;
                    state = 0;
                }
            }
        } else if (state == 5) {
            if (token[cursor] == '"') {
                if (token[cursor - 1] != '\\') {
                    //std::string quote = token.substr(quoteStart + 1, cursor - quoteStart - 1);
                    //expression_parts.push_back(quote);
                    //std::cout << "double quote: " << quote << std::endl;
                    state = 0;
                }
            }
        } else if (state == 6) {
            if (token[cursor] == '{') {
                parenLevel++;
            } else
                if (token[cursor] == '}') {
                    parenLevel--;
                    if (!parenLevel) {
                        expression_parts.push_back(token.substr(last, cursor - last));
                        last = cursor + 1;
                        expression_parts.push_back("}"); // end function
                        state = 0;
                    }
                }
        } else if (state == 8) {
            if (token[cursor] == '(') {
                parenLevel++;
            } else
                if (token[cursor] == ')') {
                    parenLevel--;
                    if (!parenLevel) {
                        expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                        expression_parts.push_back(")");
                        state = 0;
                    }
                }
        } else if (state == 9) {
            if (token[cursor] == '?') {
                trinaryLevel++;
            } else
                if (token[cursor] == ':') {
                    trinaryLevel--;
                    if (!trinaryLevel) {
                        expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                        expression_parts.push_back(":");
                        state = 0;
                    }
                }
        }
    }
    // flush last part
    std::cout << "doExpression end state[" << std::to_string(state) << "] cursor[" << cursor << "] last[" << last << "]\n";
    if (cursor != last) {
        std::cout << "pushing last part\n";
        expression_parts.push_back(token.substr(last, cursor - last));
    }
    // execute expression
    std::cout << "doExpression has [" << expression_parts.size() << "]parts\n";
    js_internal_storage *stack = nullptr;
    state = 0;
    std::string callFuncName = "";
    js_function *callFunc = nullptr;
    std::string params = "";
    //js_function *func = nullptr;
    size_t i = 0;
    for(auto it : expression_parts) {
        std::string trimmedToken = trim(it);
        i++;
        if (trimmedToken == "") continue;
        std::cout << "doExpression part[" << (i - 1) << "][" << it << "] state[" << std::to_string(state) << "]\n";
        if (state == 0) {
            // &&
            if (trimmedToken == "&&") {
                // if stack is false abort
                std::cout << "typeOf stack is [" << typeOfStorage(stack) << "]\n";
                std::string type = typeOfStorage(stack);
                if (jsIsFalse(stack)) {
                    std::cout << "&& stack isFalse\n";
                }
                if (type=="string") {
                    js_string *string = dynamic_cast<js_string*>(stack);
                    std::cout << "stack points to [" << stack << "] value[" << string->value << "]\n";
                    if (string!=nullptr) {
                        if (string->value == "" || string->value == "0") {
                            std::cout << "I believe this value to be false [" << string->value << "]\n";
                            return nullptr;
                        } else {
                            std::cout << "I believe this value to be not false [" << string->value << "]\n";
                        }
                    } else {
                        std::cout << "couldnt convert string\n";
                    }
                } else if (type == "number") {
                    js_number *jnum = dynamic_cast<js_number*>(stack);
                    std::cout << "stack points to [" << stack << "] value[" << jnum->value << "]\n";
                    if (jnum!=nullptr) {
                        if (!jnum->value) {
                            std::cout << "I believe this value to be false [" << jnum->value << "]\n";
                            return nullptr;
                        } else {
                            std::cout << "I believe this value to be not false [" << jnum->value << "]\n";
                        }
                    } else {
                        std::cout << "couldnt convert string\n";
                    }
                } else {
                    std::cout << "doExpression, I don't know how to process this type - WRITE ME\n";
                }
                continue;
            }
            // ||
            if (trimmedToken == "||") {
                std::cout << "typeOf stack is [" << typeOfStorage(stack) << "]\n";
                std::string type = typeOfStorage(stack);
                if (jsIsFalse(stack)) {
                    // stack is false, we have to execute next expression
                    continue;
                }
                // if stack is true, we don't need to evaulate the rest
                std::cout << "No need to execute the rest of this expression\n";
            }
            if (trimmedToken == "!") {
                state = 4;
                continue;
            }
            if (trimmedToken == "{") {
                // parseJSON
                state = 5;
                continue;
            }
            std::string first8 = trimmedToken.substr(0, 8);
            if (first8 == "function") {
                std::string prototype = trimmedToken.substr(9, trimmedToken.length() - 10);
                std::cout << "extracted[" << prototype << "]\n";
                js_function *jsfunc = new js_function;
                jsfunc->parameters = split(prototype, ',');
                stack = jsfunc;
                continue;
            }

            // function name
            // (
            if (trimmedToken == "(") {
                // function call
                std::cout << "func call param start\n";

                state = 2;
                continue;
            }
            // function params
            // )
            if (trimmedToken == ")") {
                std::cout << "func call param end\n";
                state = 3;
                continue;
            }
            if (trimmedToken == "}") {
                // {return document.getElementById(e)}
                /*
                doExpression has [3]parts
                doExpression part[{] state[0]
                doExpression part[return document.getElementById(e)] state[5]
                */
                // ?
                continue;
            }

            // variable

            // object.property
            bool deref = dereferenceHasBase(trimmedToken, &rootScope);
            if (deref) {
                js_internal_storage *dereferenceTest = dereferenceObject(trimmedToken, &rootScope);
                if (dereferenceTest) {
                    if (typeOfStorage(dereferenceTest)=="func") {
                        // it could be a call...
                        callFuncName = it;
                        callFunc = dynamic_cast<js_function *>(dereferenceTest);
                        if (!callFunc) {
                            std::cout << "Couldnt cast deference to func\n";
                        }
                        state = 1;
                    } else {
                        stack = dereferenceTest;
                    }
                    continue;
                }
            }

            // it's a string constant
            if ((trimmedToken[0] == '"' && trimmedToken[trimmedToken.length() - 1] == '"') ||
                (trimmedToken[0] == '\'' && trimmedToken[trimmedToken.length() - 1] == '\'')) {
                js_string *jstring = new js_string;
                jstring->value = trimmedToken.substr(1, -1);
                stack = jstring;
                continue;
            }

            if (trimmedToken == "true") {
                js_bool *jbool = new js_bool;
                jbool->value = true;
                stack = jbool;
                continue;
            } else if (trimmedToken == "null") {
                // FIXME: is null a type?
                js_bool *jbool = new js_bool;
                jbool->value = false;
                stack = jbool;
                continue;
            } else if (trimmedToken == "false") {
                js_bool *jbool = new js_bool;
                jbool->value = false;
                stack = jbool;
                continue;
            }
            // number constant
            bool allDigits = true;
            for(auto it2 : trimmedToken) {
                if (it2 >= '0' && it2 <= '9') {

                } else {
                    if (it2 != '.') {
                        allDigits = false;
                    }
                }
            }
            if (allDigits) {
                js_number *jnum = new js_number;
                jnum->value =  std::stoi(trimmedToken);
                std::cout << "allDigits value[" << trimmedToken << "] => jnum[" << jnum->value << "]\n";
                stack = jnum;
                continue;
            }

            js_internal_storage **isVar = getObjectKeyPointer(it, &rootScope);
            if (isVar != nullptr) {
                std::cout << "isVar [" << isVar << "]\n";
                std::cout << "stack could be [" << *isVar << "] type[" << typeOfStorage(stack) << "]\n";
                if (typeOfStorage(stack)=="func") {
                    callFuncName = trimmedToken;
                    callFunc = dynamic_cast<js_function *>(stack);
                    state = 1;
                    // (
                    // 1
                    // )
                } else {
                    stack = *isVar; // set stack to point to this variable
                }
                /*
                if (typeOfStorage(stack)=="string") {
                    js_string *string = dynamic_cast<js_string*>(stack);
                    std::cout << "string stack value[" << string->value << "]\n";
                }
                */
            } else {
                js_internal_storage *scopeTest = jsLocateKey(&rootScope, it);
                if (scopeTest) {
                    std::cout << "locatedKey stack[" << scopeTest << "] stackFalse[" << jsIsFalse(scopeTest) << "]\n";
                    stack = scopeTest;
                } else {
                    if (!deref) {
                        jsDisplayScope(&rootScope, 0);
                        std::cout << "is Not a Var\n";
                    } else {
                        // is an non-existant key
                    }
                }
            }
        } else if (state == 1) {
            // in func call, double check the (
            if (it == "(") {
                state = 2;
            } else {
                std::cout << "doExpression1 - func call did not have (\n";
                std::cout << "doExpression1 - stack is [" << stack << "] will now have to set it to [" << callFunc << "]\n";
                stack = callFunc;
                state = 0;
            }
        } else if (state == 2) {
            params = it;
            state = 3;
        } else if (state == 3) {
            // in func call, double check the )
            if (it == ")") {
                /*
                js_function *jfunc = dynamic_cast<js_function*>(stack);
                if (!jfunc) {
                    std::cout << "Could cast [" << stack << "] to func\n";
                    continue;
                }
                 */
                if (!callFunc) {
                    std::cout << "HALT callFunc is null!\n";
                    continue;
                }
                std::cout << "doExpression3 - calling [" << callFuncName << "](" << params << ") at [" << callFunc << "] with [" << callFunc->tokens.size() << "]tokens\n";
                stack = jsFunctionCall(callFuncName, callFunc, params, rootScope);
                state = 0;
            } else {
                std::cout << "doExpression3 - func call did not have (\n";
                std::cout << "doExpression3 - stack is [" << stack << "] will now have to set it to [" << callFunc << "]\n";
                stack = callFunc;
                state = 0;
            }
        } else if (state == 4) {
            js_internal_storage *expRes = doExpression(rootScope, it);
            // invert expRes;
            js_bool *jb = new js_bool;
            jb->value = !jsIsFalse(expRes);
            stack = jb;
            state = 0;
        } else if (state == 5) {
            /*
            js_function *objectScope = new js_function;
            js_object *newObject = new js_object;
            //doAssignment(*objectScope, it);
            parseJSON(*objectScope, it);
            // translate the scope into js_object
            //std::cout << "JSON object output" << std::endl;
            for(auto it2 : objectScope->locals.value) {
                //std::cout << "[" << it2.first << "=" << it2.second << "]" << std::endl;
                newObject->value[it2.first] = it2.second;
            }
            //std::cout << "JSON object done" << std::endl;
            stack = newObject;
             */
            stack = jsGetObject(rootScope, it);
            std::cout << "doExpression getObject got [" << stack << "]\n";
            state = 0;
        } else {
            std::cout << "doExpression unknown state[" << std::to_string(state) << "]\n";
        }
    }
    // FIXME: any remainder?
    if (state == 1) {
        // x = func ref that's never called
        std::cout << "doExpressionEND1 - func call did not have (\n";
        std::cout << "doExpressionEND1 - stack is [" << stack << "] will now have to set it to [" << callFunc << "]\n";
        stack = callFunc;
        state = 0;
    }
    if (state != 0) {
        std::cout << "doExpression final state[" << std::to_string(state) << "]\n";
    }
    return stack;
}

size_t findClosing(std::string token, size_t start, char open, char close) {
    std::cout << "\nfindClosing start[" << token.substr(start) << "]\n";
    size_t parenLevel = 0;
    for (size_t cursor = start; cursor < token.length(); cursor++) {
        std::cout << "findClosing scanning[" << token[cursor] << "] at[" << cursor << "/" << token.length() << "] parenLevel[" << parenLevel << "]\n";
        if (token[cursor] == open) {
            parenLevel++;
        } else
            if (token[cursor] == close) {
                parenLevel--;
                if (!parenLevel) {
                    return cursor;
                }
            }
    }
    std::cout << "findClosing - HALT didnt find closing element[" << close << "]\n";
    return token.length();
}

// start is right after the "function" 8
size_t locateFunctionNameEnd(std::string source, size_t start) {
    std::cout << "\nlocateFunctionNameEnd start[" << source.substr(start) << "]\n";
    for (size_t cursor = start; cursor < source.length(); cursor++) {
        if (source[cursor] == '(') {
            return cursor;
        }
    }
    std::cout << "locateFunctionNameEnd - HALT didnt find start paren for function\n";
    return source.length();
}

// start is right after the "function name(" 8
size_t locateFunctionParamsEnd(std::string source, size_t start) {
    std::cout << "\nlocateFunctionParamsEnd start[" << source.substr(start) << "]\n";
    for (size_t cursor = start; cursor < source.length(); cursor++) {
        if (source[cursor] == '{') {
            return cursor;
        }
    }
    std::cout << "locateFunctionParamsEnd - HALT didnt find start brace for function\n";
    return source.length();
}

size_t locateSingleQuoteEnd(const std::string source, const size_t start) {
    for (size_t cursor = start; cursor < source.length(); cursor++) {
        if (source[cursor] == '\'') {
            if (source[cursor - 1] != '\\') {
                return cursor;
            }
        }
    }
    return start;
}

size_t locateDoubleQuoteEnd(const std::string source, const size_t start) {
    for (size_t cursor = start; cursor < source.length(); cursor++) {
        if (source[cursor] == '"') {
            if (source[cursor - 1] != '\\') {
                return cursor;
            }
        }
    }
    return start;
}

// well a regex is like a variable, can only be right side
// where a divide is between two variables

// can't start on the /
// also could be division
// end on the / or the last modifier (/x/g)
size_t locateRegexEnd(const std::string source, const size_t start) {
    size_t adj = 0;
    if (source[start]=='/') {
        std::cout << "WARNING starting locateRegexEnd on /\n";
        adj = 1;
    }
    std::cout << "locateRegexEnd start [" << source.substr(start) << "]\n";
    unsigned char state = 0;
    for (size_t cursor = start + adj; cursor < source.length(); cursor++) {
        std::cout << "locateRegexEnd step [" << cursor << "/" << source.length() << "] char[" << source[cursor] << "] state[" << std::to_string(state) << "]\n";
        switch(state) {
            case 0:
                if (source[cursor] == '/') {
                    if (source[cursor - 1] != '\\') {
                        state = 1;
                    }
                }
                // we need to search until the end of the string for any potential closing element
            break;
            case 1:
                // ,;\n
                switch(source[cursor]) {
                    case '.': // regex obj
                        return cursor - 1;
                    case ',':
                        return cursor - 1;
                    break;
                    case ';':
                        return cursor - 1;
                    break;
                    case '\n':
                        return cursor - 1;
                    break;
                    case ')':
                        return cursor - 1;
                    break;
                    case '}':
                        return cursor - 1;
                    break;
                }
            break;
        }
    }
    // could be division
    std::cout << "locateRegexEnd division?\n";
    // division confirmation
    state = 0;
    for (size_t cursor = start + adj; cursor < source.length(); cursor++) {
        std::cout << "locateRegexEnd divisionStep [" << cursor << "/" << source.length() << "] char[" << source[cursor] << "] state[" << std::to_string(state) << "]\n";
        switch(state) {
            case 0:
                if (source[cursor] == '/') {
                    if (source[cursor - 1] != '\\') {
                        state = 1;
                    }
                }
                switch(source[cursor]) {
                    case ',':
                        return start;
                        break;
                    case ';':
                        return start;
                        break;
                    case '\n':
                        return start;
                        break;
                        // .)} ?
                }
            break;
            case 1:
                // ,;\n
                switch(source[cursor]) {
                    case '.': // regex obj
                        return cursor;
                    break;
                    case ',':
                        return cursor;
                    break;
                    case ';':
                        return cursor;
                    break;
                    case '\n':
                        return cursor;
                    break;
                    case ')':
                        return cursor - 1;
                    break;
                    case '}':
                        return cursor - 1;
                    break;
                }
            break;
        }
    }
    // what the hell is this...
    return start;
}

int getNextExpressionLevel = 0;
// we return the terminator if ,;\n or )}
// if the expression is a function definition, then we return the () as part of it
// when returning a function block, it needs to end on the }
size_t getNextExpression(const std::string source, const size_t start) {
    getNextExpressionLevel++;
    std::cout << "\n" << std::string(getNextExpressionLevel * 2, ' ') << "getNextExpression(" << getNextExpressionLevel << ") start[" << source.substr(start) << "]\n";
    unsigned char state = 0;
    size_t parenLevel = 0;
    size_t stateStart = 0;
    for (size_t cursor = start; cursor < source.length(); cursor++) {
        std::cout << std::string(getNextExpressionLevel * 2, ' ') << "getNextExpression(" << getNextExpressionLevel << ") scanning[" << source[cursor] << "] at[" << cursor << "/" << source.length() << "] state[" << std::to_string(state) << "] parenLevel[" << parenLevel << "]\n";
        switch(state) {
            case 0:
                // start function call
                if (source[cursor]=='(') {
                    parenLevel++;
                    stateStart = cursor;
                    state = 1;
                } else
                if (source[cursor]=='\'') {
                    state = 4;
                } else
                if (source[cursor]=='"') {
                    state = 5;
                }
                // detect function definition and recurse
                if (source[cursor] == 'f' && source.length() > cursor + 7 && source[cursor + 1] == 'u'
                    && source[cursor + 2] == 'n' && source[cursor + 3] == 'c' && source[cursor + 4] == 't'
                    && source[cursor + 5] == 'i' && source[cursor + 6] == 'o' && source[cursor + 7] == 'n') {
                    size_t last = cursor;
                    // parse name
                    last = locateFunctionNameEnd(source, cursor);
                    // parse params
                    last = locateFunctionParamsEnd(source, last);
                    // you need to find first brace
                    // call parseFunctionBody
                    last = parseFunctionBody(source, last);
                    std::cout << std::string(getNextExpressionLevel * 2, ' ') << "getNextExpression(" << getNextExpressionLevel << ") parseFunctionBody last[" << source[last] << "] just extracted[" << source.substr(cursor, last - cursor + 1) << "]\n";
                    cursor = last + 1; // artificially inflact the end because we're going to strip it
                }
                // detect for
                // and end expression after }

                // start regex
                if (source[cursor]=='/' && source.length() > cursor + 1 && source[cursor + 1] != '/') {
                    // is this a regex or division?
                    size_t next = locateRegexEnd(source, cursor + 1);
                    if (next != start) {
                        // was a regex
                        cursor = next;
                    } // else was division element
                }

                // start scope
                if (source[cursor]=='{') {
                    parenLevel++;
                    state = 2;
                }
                // minification technique (or list of variables, still an end of an expression tho)
                if (source[cursor]==',') {
                    getNextExpressionLevel--;
                    return cursor;
                }
                // normal expression ender
                if (source[cursor]==';') {
                    getNextExpressionLevel--;
                    return cursor;
                }
                // modern experession ender
                if (source[cursor]=='\n') {
                    getNextExpressionLevel--;
                    return cursor;
                }
                // this is the last expression in a function
                if (source[cursor]=='}') {
                    getNextExpressionLevel--;
                    // -1 doesn't work if the previous call in this block was from parseFunctionBody
                    // but we need -1 when found a stray }
                    return cursor - 1; // need to include the end }
                }
                // this is the last element in a list
                if (source[cursor]==')') {
                    getNextExpressionLevel--;
                    return cursor;
                }
                /// maybe && and || should be expression terminators too
            break;
            case 1: // inside a function call, (
                // make sure function isn't in a quote
                if (source[cursor]=='\'') {
                    cursor = locateSingleQuoteEnd(source, cursor + 1);
                } else
                if (source[cursor]=='"') {
                    cursor = locateDoubleQuoteEnd(source, cursor + 1);
                }
                // we can't do this because
                // getNextExpression(1) start[!!e.tailSize&&(t=$.cls("replyContainer"),!(a=t[t.length-e.tailSize])||(a=$.cls("dateTime",a)[0],n=(0|e.lastUpdated/1e3)-+a.getAttribute("data-utc"),i=0|(Date.now()-e.lastUpdated)/1e3,n>i))}]
                // after e.lastUpdated
                // getNextExpression(1) scanning[/] at[192/267] state[1] parenLevel[3]
                // locateRegexEnd start [1e3)-+a.getAttribute("data-utc"),i=0|(Date.now()-e.lastUpdated)/1e3,n>i))}]
                // we're just not smart enough to tell if we're division or regex (left/right or middle)

                // if we're right after the starting (, then we know we're a regex in all cases.
                if (stateStart + 1 == cursor) {
                    // start regex
                    if (source[cursor]=='/') {
                        // is this a regex or division?
                        size_t next = locateRegexEnd(source, cursor + 1);
                        if (next != start) {
                            // was a regex
                            cursor = next;
                        } // else was division element
                    }
                }

                // (function() {})
                // detect function definition and recurse
                if (source[cursor] == 'f' && source.length() > cursor + 7 && source[cursor + 1] == 'u'
                    && source[cursor + 2] == 'n' && source[cursor + 3] == 'c' && source[cursor + 4] == 't'
                    && source[cursor + 5] == 'i' && source[cursor + 6] == 'o' && source[cursor + 7] == 'n') {
                    // parse name
                    cursor = locateFunctionNameEnd(source, cursor);
                    // parse params
                    cursor = locateFunctionParamsEnd(source, cursor);
                    // you need to find first brace
                    // call parseFunctionBody
                    cursor = parseFunctionBody(source, cursor);
                    std::cout << std::string(getNextExpressionLevel * 2, ' ') << "getNextExpression - after parseFunctionBody now at " << cursor << "/" << source.length() << " char[" << source[cursor] << "] prev[" << source[cursor - 1] << "]\n";
                }
                // find end of function call
                if (source[cursor] == '(') {
                    parenLevel++;
                } else
                if (source[cursor] == ')') {
                    parenLevel--;
                    if (!parenLevel) {
                        // while nothing can happen after a function call
                        // we need to process the terminating ,; or \n
                        state = 0;
                    }
                }
            break;
            case 2:
                // find end of scope
                if (source[cursor] == '{') {
                    parenLevel++;
                } else
                if (source[cursor] == '}') {
                    parenLevel--;
                    if (!parenLevel) {
                        state = 0;
                    }
                }
            break;
            case 4:
                if (source[cursor] == '\'') {
                    if (source[cursor - 1] != '\\') {
                        //quoteEnd = cursor - 1;
                        state = 0;
                    }
                }
            break;
            case 5:
                if (source[cursor] == '"') {
                    if (source[cursor - 1] != '\\') {
                        //quoteEnd = cursor - 1;
                        state = 0;
                    }
                }
            break;
        }
    }
    std::cout << std::string(getNextExpressionLevel * 2, ' ') << "getNextExpression[" << source.substr(start) << "] - WARNING didnt find another expression\n";
    return source.length();
}

// also finishing character would be good to return
// we just need to find the start and ends, we don't need to parse contents
// we'll parse contents on execution

// sometimes the token starts at function()
// othertimes inside function
// we're supposed to be parsing a function definition
// ok we should only parse function bodies after the parameters
// so no () unless after
// so do we start at { or right before? well we don't need to...
// start should be after {?
// we should return a character that points to }
size_t parseFunctionBodyLevel = 0;
size_t parseFunctionBody(std::string source, size_t start) {
    parseFunctionBodyLevel++;
    std::cout << "\nparseFunctionBody(" << parseFunctionBodyLevel << ") start[" << source.substr(start) << "] next8[" << source.substr(start, 8) << "]\n";
    size_t state = 0;
    size_t parenLevel = 0;
    //size_t last = start ? start - 1 : 0;
    for (size_t cursor = start; cursor < source.length(); cursor++) {
        std::cout << "parseFunctionBody(" << parseFunctionBodyLevel << ") scanning[" << source[cursor] << "] at[" << cursor << "/" << source.length() << "] state[" << std::to_string(state) << "]\n";
        switch(state) {
            case 0: // look for function body start
                if (source[cursor] == '{') {
                    std::cout << "detected{ at" << " cursor " << cursor << std::endl;
                    // there really shouldn't be anything between ) and {
                    parenLevel++;

                    //last = cursor + 1;
                    state = 1;
                    //cursor = findClosing(source, cursor, '{', '}');
                    // FIXME: take function string and send it to a tokenizer
                    // save tokens to func.tokens
                    //expression_parts.push_back("{");
                    //return cursor;
                }
            break;
            case 1:
                // we're not done with outer function, but we are with the inner
                /*
                if (source[cursor] == '}') {
                    parenLevel--;
                    if (!parenLevel) {
                        std::cout << "parseFunctionBody(" << parseFunctionBodyLevel << ") PRE final} [" << source.substr(start, cursor + 1 - start) << "]\n";
                        parseFunctionBodyLevel--;
                        return cursor; // return a character ending on } according to spec
                    }
                }
                */
                // what we can enter that has {
                // well a function
                // so we need to step expression by expression tbh
                size_t next = getNextExpression(source, cursor);
                std::cout << "parseFunctionBody(" << parseFunctionBodyLevel << ") - state1 start[" << cursor << "] next[" << next << "]\n";
                if (next < cursor) next = cursor; // advanced at least 1 char
                std::string exp = source.substr(cursor, next + 1 - cursor);
                exp = trim(exp);
                std::cout << "parseFunctionBody(" << parseFunctionBodyLevel << ") - NextExpression [" << exp << "]\n";
                // we're basically looking for a next expression of {
                /*
                if (exp.length() == 0) {
                    return cursor;
                }
                */
                if (exp.length() == 1) {
                    if (exp == "{") {
                        parenLevel++;
                    } else
                    if (exp == "}") {
                        parenLevel--;
                        if (!parenLevel) {
                            // this doesn't mean the function is over
                            // how it can it not, we have equal amounts of level
                            // well the levels aren't at the character level
                            std::cout << "parseFunctionBody(" << parseFunctionBodyLevel << ") final} [" << source.substr(start, next + 1 - start) << "]\n";
                            parseFunctionBodyLevel--;
                            return next; // return a character ending on } according to spec
                        }
                    }
                }
                // FIXME: kind of an ugly hack
                // can't just be () because the expression could just be a call itself
                // needs to be }()
                if (exp.length() > 2) {
                    std::string last3 = exp.substr(exp.length() - 3, 3);
                    std::cout << "last3[" << last3 << "]\n";
                    if (last3 == "}()") {
                        // function declartion & call
                        std::cout << "parseFunctionBody(" << parseFunctionBodyLevel << ") final}() [" << source.substr(start, cursor + 1 - start) << "]\n";
                        parseFunctionBodyLevel--;
                        return cursor;
                    }
                }
                cursor = next;
                std::cout << "parseFunctionBody1(" << parseFunctionBodyLevel << ") now at " << cursor << "/" << source.length() << " char[" << source[cursor] << "]\n";
                if (cursor == source.length() - 1) {
                    // we just ended on
                    if (source[cursor]=='}') {
                        parseFunctionBodyLevel--;
                        return cursor;
                    }
                }
                /*
                if (source[cursor] == '{') {
                    parenLevel++;
                } else
                if (source[cursor] == '}') {
                    parenLevel--;
                    if (!parenLevel) {
                        return cursor;
                    }
                }
                */
            break;
        }
    }
    parseFunctionBodyLevel--;
    std::cout << "parseFunctionBody(" << parseFunctionBodyLevel << ") - HALT didnt find closing element for function\n";
    return source.length();
    /*
    //jsGetTokens will tokenize the entire body of the function
    // and return it as one token
    std::vector<std::string> tokens = jsGetTokens(source, start + 1); // +1 to skip {
    std::cout << "\nparseFunctionBody - jsGetTokens got " << tokens.size() << " tokens\n";
    for(auto it: tokens) {
        std::cout << "[" << it << "]" "\n";
    }
    std::string funcBody = tokens[0];
    return start + funcBody.length();
     */
}

// don't include the start { or ending }
// prototype doesn't include ()
js_function *makeFunctionFromString(const std::string body, const std::string prototype, js_function *parent) {
    std::cout << "makeFunctionFromString [" << prototype << "][" << body << "]\n";
    js_function *func = new js_function;
    func->tokens = jsGetTokens(body, 0);
    func->parameters = split(prototype, ',');
    if (!func->tokens.size()) {
        std::cout << "empty function?\n";
    }
    func->parentScope = parent;
    return func;
}

js_array *jsGetArray(js_function &rootScope, std::string token) {
    js_function *objectScope = new js_function;
    js_array *jsArr = new js_array;
    parseJSON(*objectScope, token);
    for(auto it2 : objectScope->locals.value) {
        jsArr->value.push_back(*it2.second);
    }
    return jsArr;
}

void parseArray(js_function &rootScope, std::string token) {
    std::vector<std::string> json_keys;
    std::vector<std::string> json_values;
    size_t cursor;
    size_t last = 0;
    size_t quoteStart = 0;
    size_t quoteEnd = 0;
    size_t parenStart = 0;
    size_t parenLevel = 0;
    unsigned char state = 0;
    for (cursor = 0; cursor < token.length(); cursor++) {
        // we should only look for [:'"]
        if (state == 0) {
            switch(token[cursor]) {
                // { or [ could be expression parsed I think...
                case '\'':
                    quoteStart = cursor;
                    state = 4;
                    break;
                case '"':
                    quoteStart = cursor;
                    state = 5;
                    break;
                case ',':
                    std::string key = "";
                    if (quoteStart) {
                        // constant
                        key = token.substr(quoteStart + 1, quoteEnd - quoteStart); // skip the quotes
                    } else {
                        // variable
                        key = token.substr(last, cursor - last);
                    }
                    key = trim(key);
                    std::cout << "element[" << key << "] quoted: " << quoteStart << std::endl;
                    json_keys.push_back(key);
                    last = cursor + 1;
                    state = 2;
                    quoteStart = 0;
                    quoteEnd = 0;
                    break;
            }
        } else if (state == 2) { // get value state
            // now we haven't to make sure we don't enter {, } or function
            // ', " are ok, but have to turn off parsing until we encounter the next
            // a-zA-Z$ for variables assignment
            switch(state) {
                case 0:
                    switch(token[cursor]) {
                        case '{':
                            parenStart = cursor;
                            parenLevel++;
                            state = 1;
                            break;
                        case '[':
                            parenStart = cursor;
                            parenLevel++;
                            state = 2;
                            break;
                        case '\'':
                            quoteStart = cursor;
                            state = 4;
                            break;
                        case '"':
                            quoteStart = cursor;
                            state = 5;
                            break;
                        case ',':
                            std::string value = "";
                            if (quoteStart) {
                                value = token.substr(quoteStart + 1, quoteEnd - quoteStart); // skip the quotes
                            } else {
                                value = token.substr(last, cursor - last);
                                value = trim(value);
                            }
                            std::cout << "value[" << value << "] quoted" << quoteStart << std::endl;
                            // JSON objects will already be assigned and empty
                            if (value != "") {
                                json_values.push_back(value);
                                js_string *newString = new js_string();
                                newString->value = value;
                                rootScope.locals.value[json_keys.back()] = newString;
                            }
                            last = cursor + 1;
                            state = 0;
                            quoteStart = 0;
                            quoteEnd = 0;
                            break;
                    }
                    break;
                case 1:
                    switch(token[cursor]) {
                        case '{':
                            parenLevel++;
                            break;
                        case '}':
                            parenLevel--;
                            if (!parenLevel) {
                                std::string JSON = token.substr(parenStart + 1, cursor - parenStart);
                                std::cout << "PA Need to deJSON [" << JSON << "]" << std::endl;
                                /*
                                // well we can get a scope back
                                js_function *objectScope = new js_function;
                                parseJSON(*objectScope, JSON);
                                js_object *newObject = new js_object;
                                */
                                js_object *newObject = jsGetObject(rootScope, JSON);
                                rootScope.locals.value[json_keys.back()] = newObject;
                                last = cursor + 1;
                                state = 0;
                            }
                            break;
                    }
                    break;
                case 2:
                    switch(token[cursor]) {
                        case '[':
                            parenLevel++;
                        break;
                        case ']':
                            parenLevel--;
                            if (!parenLevel) {
                                std::string arrStr = token.substr(parenStart + 1, cursor - parenStart);
                                std::cout << "Need to deArray [" << arrStr << "]" << std::endl;
                                // well we can get a scope back
                                js_function *objectScope = new js_function;
                                parseArray(*objectScope, arrStr);
                                js_object *newObject = new js_object;
                                rootScope.locals.value[json_keys.back()] = newObject;
                                last = cursor + 1;
                                state = 0;
                            }
                        break;
                    }
                break;
                case 4:
                    if (token[cursor] == '\'') {
                        if (token[cursor - 1] != '\\') {
                            quoteEnd = cursor - 1;
                            state = 0;
                        }
                    }
                break;
                case 5:
                    if (token[cursor] == '"') {
                        if (token[cursor - 1] != '\\') {
                            quoteEnd = cursor - 1;
                            state = 0;
                        }
                    }
                break;
            }
        } else if (state == 4) {
            if (token[cursor] == '\'') {
                if (token[cursor - 1] != '\\') {
                    quoteEnd = cursor - 1;
                    state = 0;
                }
            }
        } else if (state == 5) {
            if (token[cursor] == '"') {
                if (token[cursor - 1] != '\\') {
                    quoteEnd = cursor - 1;
                    state = 0;
                }
            }
        }
    }
}

js_object *jsGetObject(js_function &rootScope, std::string token) {
    js_function *objectScope = new js_function;
    js_object *jsonObj = new js_object;
    parseJSON(*objectScope, token);
    for(auto it2 : objectScope->locals.value) {
        jsonObj->value[it2.first] = it2.second;
    }
    return jsonObj;
}

// we still need rootScope to read any vars from
// FIXME: take js_object and start
void parseJSON(js_function &rootScope, std::string token) {
    std::vector<std::string> json_keys;
    std::vector<std::string> json_values;
    size_t cursor;
    size_t last = 0;
    size_t quoteStart = 0;
    size_t quoteEnd = 0;
    size_t parenStart = 0;
    size_t parenLevel = 0;
    unsigned char keyState = 0;
    unsigned char valueState = 0;
    std::cout << "parseJSON start[" << token << "]\n";
    for (cursor = 0; cursor < token.length(); cursor++) {
        std::cout << "parseJSON step cursor[" << cursor << "/" << token.length() << "] char[" << token[cursor] << "] keyState[" << std::to_string(keyState) << "] valueState[" << std::to_string(valueState) << "]\n";
        if (keyState == 0) {
            valueState = 0; // reset value state
            switch(token[cursor]) {
                    case '\'':
                        quoteStart = cursor;
                        keyState = 4;
                    break;
                    case '"':
                        quoteStart = cursor;
                        keyState = 5;
                    break;
                    case ':':
                        std::string key = "";
                        if (quoteStart) {
                            key = token.substr(quoteStart + 1, quoteEnd - quoteStart); // skip the quotes
                        } else {
                            key = token.substr(last, cursor - last);
                        }
                        key = trim(key);
                        std::cout << "parsingJSON - key[" << key << "] quoted: " << quoteStart << std::endl;
                        if (key == "prettyPrint") {
                            std::cout << "Found your shit, now step it\n";
                        }
                        json_keys.push_back(key);
                        last = cursor + 1;
                        keyState = 2;
                        quoteStart = 0;
                        quoteEnd = 0;
                    break;
            }
        } else if (keyState == 2) { // get value state
            // now we haven't to make sure we don't enter {, } or function
            // ', " are ok, but have to turn off parsing until we encounter the next
            // a-zA-Z$ for variables assignment
            // we should only look for [:'"]
            //std::cout << "parseJSON ks2 looking at [" << token[cursor] << "] vs[" << std::to_string(valueState) << "]\n";
            switch(valueState) {
                case 0:
                    switch(token[cursor]) {
                        case 'f':
                            if (cursor + 7 < token.length()) {
                                std::string next8 = token.substr(cursor, 8);
                                //std::cout << "parsingJSON - isFunction [" << next8 << "]" << std::endl;
                                if (next8 == "function") {
                                    cursor = locateFunctionNameEnd(token, cursor);
                                    last = locateFunctionParamsEnd(token, cursor);
                                    std::string prototype = token.substr(cursor + 1, last - cursor - 1);

                                    cursor = last;
                                    // you need to find first brace
                                    last = parseFunctionBody(token, cursor);
                                    if (token[last]!='}') {
                                        // should end on }
                                        // but should it be last + 1? definitely not
                                        std::cout << "parsingJSON - parseFunctionBody broke spec, ending on [" << token[last] << "]\n";
                                    }
                                    //std::cout << "parseJSON last[" << token[last] << "]\n";
                                    // + 1 to skip initial { and not + 1 because } and -1 because of that starting + 1
                                    std::string funcContents = token.substr(cursor + 1, last - cursor - 1);
                                    std::cout << "parsingJSON - function " << json_keys.back() << "[" << prototype << "] [" << funcContents << "]" << std::endl;

                                    std::cout << "parsingJSON - current char [" << token[last] << "]\n";
                                    cursor = last; // continue after } (for loop will advance this)
                                    // we have this key now but we'll wait for the , to do it's thing
                                    //valueState = 6;

                                    json_values.push_back(funcContents);
                                    // [" << funcContents << "]
                                    assignfile << "JSON." << json_keys.back() << "=" << "_NTRFUNC0[" << funcContents.length() << "]" << "\n";
                                    rootScope.locals.value[json_keys.back()] = makeFunctionFromString(funcContents, prototype, &rootScope);
                                    valueState = 6;
                                    //keyState = 0;
                                    //valueState = 0;
                                }
                            }
                            break;
                        case '{':
                            parenStart = cursor;
                            parenLevel++;
                            valueState = 1;
                        break;
                        case '[':
                            parenStart = cursor;
                            parenLevel++;
                            valueState = 2;
                        break;
                        case '\'':
                            quoteStart = cursor;
                            valueState = 4;
                        break;
                        case '"':
                            quoteStart = cursor;
                            valueState = 5;
                        break;
                        case ',':
                            std::string value = "";
                            if (quoteStart) {
                                value = token.substr(quoteStart + 1, quoteEnd - quoteStart); // skip the quotes
                            } else {
                                value = token.substr(last, cursor - last);
                                value = trim(value);
                            }
                            //std::cout << "parsingJSON - value[" << value << "] quoted" << quoteStart << std::endl;
                            // JSON objects will already be assigned and empty
                            if (value != "") {
                                json_values.push_back(value);
                                js_string *newString = new js_string();
                                newString->value = value;
                                assignfile << "JSON." << json_keys.back() << "=_NTRSTRING(,):'" << value << "'\n";
                                rootScope.locals.value[json_keys.back()] = newString;
                            } else {
                                if (quoteStart) {
                                    // likely ""
                                    json_values.push_back("");
                                    js_string *newString = new js_string();
                                    newString->value = "";
                                    assignfile << "JSON." << json_keys.back() << "=_NTRSTRING(,):'" << value << "'\n";
                                    rootScope.locals.value[json_keys.back()] = newString;
                                }
                            }
                            last = cursor + 1;
                            std::cout << "remainder last now points to[" << token[last] << "] should not be comma\n";
                            keyState = 0;
                            valueState = 0;
                            quoteStart = 0;
                            quoteEnd = 0;
                        break;
                    }
                break;
                case 1:
                    switch(token[cursor]) {
                        case '{':
                            parenLevel++;
                        break;
                        case '}':
                            parenLevel--;
                            if (!parenLevel) {
                                std::string JSON = token.substr(parenStart + 1, cursor - parenStart);
                                //std::cout << "parsingJSON - PJ Need to deJSON [" << JSON << "]" << std::endl;
                                // well we can get a scope back
                                /*
                                js_function *objectScope = new js_function;
                                parseJSON(*objectScope, JSON);
                                js_object *newObject = new js_object;
                                */
                                js_object *newObject = jsGetObject(rootScope, JSON);
                                assignfile << "JSON." << json_keys.back() << "=" << "_NTRJSON" << "\n";
                                rootScope.locals.value[json_keys.back()] = newObject;
                                last = cursor + 1;
                                valueState = 0;
                                // let keyState 2 finish it out
                            }
                        break;
                    }
                break;
                case 2:
                    switch(token[cursor]) {
                        case '[':
                            parenLevel++;
                            break;
                        case ']':
                            parenLevel--;
                            if (!parenLevel) {
                                std::string arrayStr = token.substr(parenStart + 1, cursor - parenStart);
                                //std::cout << "parsingJSON - Need to deArray [" << arrayStr << "]" << std::endl;
                                // well we can get a scope back
                                js_function *arrayScope = new js_function;
                                parseArray(*arrayScope, arrayStr);
                                js_array *newArray = new js_array;
                                assignfile << "JSON." << json_keys.back() << "=" << "_NTRARRAY" << "\n";
                                rootScope.locals.value[json_keys.back()] = newArray;
                                last = cursor + 1;
                                valueState = 0;
                                keyState = 0;
                            }
                            break;
                    }
                break;
                case 4:
                    if (token[cursor] == '\'') {
                        if (token[cursor - 1] != '\\') {
                            quoteEnd = cursor - 1;
                            valueState = 0;
                        }
                    }
                break;
                case 5:
                    if (token[cursor] == '"') {
                        if (token[cursor - 1] != '\\') {
                            quoteEnd = cursor - 1;
                            valueState = 0;
                        }
                    }
                break;
                case 6: // function push back
                    std::cout << "parseJSON state6 char[" << token[cursor] << "]\n";
                    if (token[cursor] == ',') {
                        /*
                        std::string value = "";
                        if (quoteStart) {
                            value = token.substr(quoteStart + 1, quoteEnd - quoteStart); // skip the quotes
                        } else {
                            value = token.substr(last, cursor - last);
                            value = trim(value);
                        }
                        std::cout << "parsingJSON - value[" << value << "] quoted" << quoteStart << std::endl;
                        if (value != "") {
                            json_values.push_back(value);
                            assignfile << "JSON." << json_keys.back() << "=" << "_NTRFUNC6" << "\n";
                            rootScope.data[json_keys.back()] = func;
                        }
                         */
                        last = cursor + 1; // set to after the current character (next char after ,)
                        keyState = 0;
                        valueState = 0;
                        quoteStart = 0;
                        quoteEnd = 0;
                    } else if (token[cursor] == '}') {
                        // it's the end of the JSON
                        keyState = 0;
                    }
                break;
            }
        } else if (keyState == 4) {
            if (token[cursor] == '\'') {
                if (token[cursor - 1] != '\\') {
                    quoteEnd = cursor - 1;
                    keyState = 0;
                }
            }
        } else if (keyState == 5) {
            if (token[cursor] == '"') {
                if (token[cursor - 1] != '\\') {
                    quoteEnd = cursor - 1;
                    keyState = 0;
                }
            }
        }
    }
    std::cout << "done parsingJSON keyState[" << std::to_string(keyState) << "] valueState[" << std::to_string(valueState) << "]\n";
    // we can end safely if after a function, likely just the } left over
    if (keyState == 2 && valueState != 6) {
        std::string value = "";
        if (quoteStart) {
            value = token.substr(quoteStart + 1, quoteEnd - quoteStart); // skip the quotes
        } else {
            value = token.substr(last, cursor - last);
            value = trim(value);
        }
        //std::cout << "parsingJSON - value[" << value << "] quoted" << quoteStart << std::endl;
        // JSON objects will already be assigned and empty
        if (value != "") {
            json_values.push_back(value);
            js_string *newString = new js_string();
            newString->value = value;
            assignfile << "JSON." << json_keys.back() << "=_NTRSTRING(END):'" << value << "'\n";
            rootScope.locals.value[json_keys.back()] = newString;
        }
    }
    std::cout << "exiting parseJSON with: \n";
    jsDisplayScope(&rootScope, 0);
}

// return value is if there was any error (false for error, true for no error)
int doAssignmentLevel = 0;
bool doAssignment(js_function &rootScope, std::string token) {
    doAssignmentLevel++;
    // FIXME: make sure = isn't in quotes or JSON?
    // FIXME: double or triple equal differentiation
    //std::cout << "looking at [" << token << "]" << std::endl;
    // document.documentElement.classList?($.hasClass=function(e,t){return e.classList.contains(t)},$.addClass=function(e,t){e.classList.add(t)},$.removeClass=function(e,t){e.classList.remove(t)}):($.hasClass=function(e,t){return-1!=(" "+e.className+" ").indexOf(" "+t+" ")},$.addClass=function(e,t){e.className=""===e.className?t:e.className+" "+t},$.removeClass=function(e,t){e.className=(" "+e.className+" ").replace(" "+t+" ","")})

    std::vector<std::string> expression_parts;
    size_t cursor;
    size_t last = 0;
    size_t quoteStart = 0;
    size_t parenStart = 0;
    size_t parenLevel = 0;
    size_t trinaryLevel = 0;
    unsigned char state = 0;
    for (cursor = 0; cursor < token.length(); cursor++) {
        std::cout << std::string(doAssignmentLevel * 2, ' ') << "doAssignment(" << doAssignmentLevel << ") step [" << cursor << "/" << token.length() << "] char[" << token[cursor] << "] state[" << std::to_string(state) << "] parenLevel[" << parenLevel << "]\n";
        if (state == 0) {
            // =
            // ||
            // &&
            // <, >, <=, >=, ==, ===, !=, !==
            // +, -
            // *, /, %
            // ?, >>, <<
            if (token[cursor] == 'f' && token.length() > cursor + 7 && token[cursor + 1] == 'u'
                && token[cursor + 2] == 'n' && token[cursor + 3] == 'c' && token[cursor + 4] == 't'
                && token[cursor + 5] == 'i' && token[cursor + 6] == 'o' && token[cursor + 7] == 'n') {
                state = 3;
            }
            // if? yea we have to, otherwise it's scope becomes a JSON decode
            // but that's not exactly a problem
            // else?
            if (token[cursor] == '\'') {
                quoteStart = cursor;
                state = 4;
            } else
            if (token[cursor] == '"') {
                quoteStart = cursor;
                state = 5;
            } else
            if (token[cursor] == '(') {
                parenStart = cursor;
                parenLevel++;
                state = 8;
                if (last != cursor) {
                    expression_parts.push_back(token.substr(last, cursor - last));
                }
                last = cursor + 1;
                expression_parts.push_back("(");
            } else
            if (token[cursor] == '{') {
                parenStart = cursor;
                parenLevel++;
                state = 1;
                //std::cout << "last " << last << " cursor " << cursor << std::endl;
                if (last != cursor) {
                    expression_parts.push_back(token.substr(last, cursor - last - 1));
                }
                last = cursor + 1;
                expression_parts.push_back("{");
            }

            // single =
            if (token[cursor] == '=' && token.length() > cursor + 1 && token[cursor + 1] != '=') {
                //state = 1;
                //std::cout << "starting = at " << cursor << " last: " << last << std::endl;
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("=");
            }
            // hack for JSON parsing
            if (token[cursor] == ':') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("=");
                state = 2;
            }
            // FIXME: pull out all the expression parsing stuff
            // ||
            if (token[cursor] == '|' && token.length() > cursor + 1 && token[cursor + 1] == '|') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 2;
                expression_parts.push_back("||");
                cursor++;
            }
            if (token[cursor] == '&' && token.length() > cursor + 1 && token[cursor + 1] == '&') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 2;
                expression_parts.push_back("&&");
                cursor++;
            }
            if (token[cursor] == '>' && token.length() > cursor + 1 && token[cursor + 1] != '=' && token[cursor + 1] != '>') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back(">");
                cursor++;
            }
            if (token[cursor] == '<' && token.length() > cursor + 1 && token[cursor + 1] != '=' && token[cursor + 1] != '<') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("<");
                cursor++;
            }
            if (token[cursor] == '<' && token.length() > cursor + 1 && token[cursor + 1] == '=') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 2;
                expression_parts.push_back("<=");
                cursor++;
            }
            if (token[cursor] == '>' && token.length() > cursor + 1 && token[cursor + 1] == '=') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 2;
                expression_parts.push_back(">=");
                cursor++;
            }
            if (token[cursor] == '=' && token.length() > cursor + 2 && token[cursor + 1] == '=' && token[cursor + 2] != '=') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 2;
                expression_parts.push_back("==");
                cursor++;
            }
            if (token[cursor] == '=' && token.length() > cursor + 2 && token[cursor + 1] == '=' && token[cursor + 2] == '=') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 3;
                expression_parts.push_back("===");
                cursor+=2;
            }
            if (token[cursor] == '!' && token.length() > cursor + 2 && token[cursor + 1] == '=' && token[cursor + 2] != '=') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 2;
                expression_parts.push_back("!=");
                cursor++;
            }
            if (token[cursor] == '!' && token.length() > cursor + 2 && token[cursor + 1] == '=' && token[cursor + 2] == '=') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 3;
                expression_parts.push_back("!==");
                cursor+=2;
            }
            // +
            if (token[cursor] == '+') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("+");
            } else
            if (token[cursor] == '-') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("+");
            } else
            if (token[cursor] == '*') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("*");
            } else
            if (token[cursor] == '/') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("/");
            } else
            if (token[cursor] == '%') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("%");
            } else
            if (token[cursor] == '!') {
                if (last != cursor) {
                    expression_parts.push_back(token.substr(last, cursor - last));
                }
                expression_parts.push_back("!"); last = cursor + 1;
            } else
            if (token[cursor] == '?') {
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("?");
                trinaryLevel++;
                state = 9;
            } else if (token[cursor] == 'i' && token.length() > cursor + 2 && token[cursor + 1] == 'n' && token[cursor + 2] == ' ') {
                // "property"in object (or new object)
                expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                expression_parts.push_back("in");
            }

        } else if (state == 1) {
            if (token[cursor] == '{') {
                parenLevel++;
            } else
                if (token[cursor] == '}') {
                    parenLevel--;
                    if (!parenLevel) {
                        expression_parts.push_back(token.substr(last, cursor - last));
                        last = cursor + 1;
                        expression_parts.push_back("}");
                        state = 0;
                    }
                }
        } else if (state == 2) { // json state (can be moved)
            if (token[cursor] == ',') {
                expression_parts.push_back(token.substr(last, cursor - last));
                last = cursor + 1;
                state = 0;
            }
        } else if (state == 3) { // function start (can be moved)
            if (token[cursor] == '{') {
                // lets put the function prototype
                expression_parts.push_back(token.substr(last, cursor - last));
                last = cursor + 1;

                size_t next = parseFunctionBody(token, cursor);
                std::string funcBody = token.substr(cursor, 1 + next - cursor);
                cursor = next;
                std::cout << std::string(doAssignmentLevel * 2, ' ') << "parseFunctionBody returned[" << funcBody << "]\n";
                //std::cout << "doAssignment3 parseFunctionBody last[" << token[cursor] << "]\n";
                expression_parts.push_back(funcBody);
                last = cursor + 1;
                expression_parts.push_back("}"); // end function
                state = 7; // move to execution/call check

                //std::cout << "doAssignment - s3 - parenLevel: " << parenLevel << "\n";
                //parenLevel++;
                //state = 6; // move to the function body
            }
        } else if (state == 4) {
            if (token[cursor] == '\'') {
                if (token[cursor - 1] != '\\') {
                    //std::string quote = token.substr(quoteStart + 1, cursor - quoteStart - 1);
                    //expression_parts.push_back(quote);
                    //std::cout << "single quote: " << quote << std::endl;
                    state = 0;
                }
            }
        } else if (state == 5) {
            if (token[cursor] == '"') {
                if (token[cursor - 1] != '\\') {
                    //std::string quote = token.substr(quoteStart + 1, cursor - quoteStart - 1);
                    //expression_parts.push_back(quote);
                    //std::cout << "double quote: " << quote << std::endl;
                    state = 0;
                }
            }
        } else if (state == 6) {
            // function body
            // now regexes can have unbalanced {}
            cursor = parseFunctionBody(token, cursor);
            std::cout << std::string(doAssignmentLevel * 2, ' ') << "doAssignment6 parseFunctionBody first[" << token[cursor] << "] last[" << token[last] << "]\n";
            expression_parts.push_back(token.substr(last, cursor - last));
            last = cursor + 1;
            expression_parts.push_back("}"); // end function
            state = 7;
            /*
            if (token[cursor] == '{') {
                parenLevel++;
            } else
                if (token[cursor] == '}') {
                    parenLevel--;
                    if (!parenLevel) {
                        expression_parts.push_back(token.substr(last, cursor - last));
                        last = cursor + 1;
                        expression_parts.push_back("}"); // end function
                        state = 7;
                    }
                }*/
        } else if (state == 7) {
            // cursor should be passed }
            switch(token[cursor]) {
                case '(':
                    if (!parenLevel) {
                        expression_parts.push_back("("); // start function call
                    }
                    parenLevel++;
                break;
                case ')':
                    parenLevel--;
                    if (!parenLevel) {
                        expression_parts.push_back(token.substr(last, cursor - last));
                        last = cursor + 1;
                        expression_parts.push_back(")"); // end function call
                        state = 0;
                    }
                break;
                case '\n':
                    last = cursor + 1;
                    state = 0;
                break;
                case ';':
                    last = cursor + 1;
                    state = 0;
                break;
                case ',':
                    last = cursor + 1;
                    state = 0;
                break;
            }
        } else if (state == 8) {
            if (token[cursor] == '(') {
                parenLevel++;
            } else
            if (token[cursor] == ')') {
                parenLevel--;
                if (!parenLevel) {
                    expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                    expression_parts.push_back(")");
                    state = 0;
                }
            }
        } else if (state == 9) {
            if (token[cursor] == '?') {
                trinaryLevel++;
            } else
            if (token[cursor] == ':') {
                trinaryLevel--;
                if (!trinaryLevel) {
                    expression_parts.push_back(token.substr(last, cursor - last)); last = cursor + 1;
                    expression_parts.push_back(":");
                    state = 0;
                }
            }
        }
    }
    std::cout << std::string(doAssignmentLevel * 2, ' ') << "doAssignment ending state " << std::to_string(state) << std::endl;
    if (last != token.length() && cursor != last) {
        expression_parts.push_back(token.substr(last, token.length()));
        if (state == 6) {
            expression_parts.push_back("}");
        }
    }

    std::cout << std::string(doAssignmentLevel * 2, ' ') << "expression string[" << token << "]" << std::endl;
    std::cout << std::string(doAssignmentLevel * 2, ' ') << "expression debug" << std::endl;
    std::string lastToken = "";
    state = 0;
    std::string left = "";
    if (expression_parts.size() == 1) {
        // usually just a variable declaration
        std::string tLeft = trim(expression_parts[0]);
        if (tLeft[0]=='"' && tLeft[tLeft.length() - 1]=='"') {
            //std::cout << "dequoting[" << tLeft << "]" << std::endl;
            tLeft=tLeft.substr(1, tLeft.length() - 2);
        }
        std::cout << std::string(doAssignmentLevel * 2, ' ') << "Assigning (end) [" << tLeft << "]" << std::endl;
        // we're just initialling a blank variable
        js_internal_storage **storage = getObjectKeyPointer(tLeft, &rootScope);
        if (storage != nullptr) {
            storage = nullptr; // FIXME;
        } else {
            rootScope.locals.value[tLeft] = nullptr;
        }
        std::cout << std::string(doAssignmentLevel * 2, ' ') << "expression end" << std::endl << std::endl;
        doAssignmentLevel--;
        return true;
    }
    bool negate = false;
    js_function *func = nullptr;
    js_function *callFunc = nullptr;
    std::string callFuncName = "";
    std::string prototype = "";
    std::string params = "";
    js_internal_storage *stack = nullptr;
    for(auto it : expression_parts) {
        // probably should trim these
        std::cout << std::string(doAssignmentLevel * 2, ' ') << "doAssignment expression(" << doAssignmentLevel << ") token[" << it << "]" << std::to_string(state) << std::endl;
        // default: put token in left, get op (usually =) and then get right side
        std::string trimmedToken = trim(it);
        if (state==0) {
            negate = false;
            prototype = "";
            if (trimmedToken == "=") {
                left = lastToken;
                state = 1;
            }
            if (trimmedToken == "&&") {
                // if stack is false...
                std::cout << "stack[" << stack << "] stackIsFalse[" << jsIsFalse(stack) << "]\n";
                //return true;
            }
            // can actually go to 9 (include the space)
            std::string first8 = trimmedToken.substr(0, 8);
            if (first8 == "function") {
                left = trimmedToken.substr(8);
                // well it can be "function()"
                // no left and no starting brace...
                cursor = locateFunctionParamsEnd(left, 0);
                if (cursor != 0) {
                    left = left.substr(0, cursor);
                }
                std::cout << std::string(doAssignmentLevel * 2, ' ') << "doAssignment - function expr start Adjust left[" << left << "] to [" << left.substr(0, cursor) << "]\n";
                state = 3;
            }
            // most likely a function call
            // but also can be an expression
            // (function() {}())
            // token will matter here
            // check for ()
            if (trimmedToken == "(") {
                state = 8;
            }
        } else if (state == 1) {
            if (trimmedToken == "") continue; // skip empties, don't fire the assignment too early
            if (it == " ") continue; // skip empties, don't fire the assignment too early
            if (trimmedToken == "!") continue; // valid expression
            if (trimmedToken == "}") continue; // valid part of an expression (why are we getting this, should have a starting { and be in state 2)
            // so these next 2 execute a function that's on the stack
            // state 0: elem =
            // state 1: funcName,(,proto,),;
            if (trimmedToken == "(") continue; // valid part of an expression
            if (trimmedToken == ")") continue; // valid part of an expression
            if (trimmedToken == "&&") {
                // if stack is false...
                std::cout << "state1 && stack[" << stack << "] stackIsFalse[" << jsIsFalse(stack) << "]\n";
                if (jsIsFalse(stack)) {
                    // skip the rest of the expression
                    // set variable to false
                    std::string tLeft = trim(left);
                    if (tLeft[0]=='"' && tLeft[tLeft.length() - 1]=='"') {
                        //std::cout << "dequoting[" << tLeft << "]" << std::endl;
                        tLeft=tLeft.substr(1, tLeft.length() - 2);
                    }
                    rootScope.locals.value[tLeft] = stack;
                    assignfile << tLeft << "=" << it << "\n";
                    std::cout << "doAssignment &&false final assign[" << tLeft << "]=false\n";
                    return true;
                }
            } // valid part of an expression
            if (trimmedToken == "{") {
                state = 2;
            } else {

                js_internal_storage *storage = doExpression(rootScope, trimmedToken);
                if (storage) {
                    // state 1 can be an expression that we need to push the result onto the stack for && ||
                    stack = storage;
                    js_function *funcTest = dynamic_cast<js_function*>(storage);
                    if (funcTest) {
                        callFuncName = trimmedToken;
                        callFunc = funcTest;
                        state = 9;
                        continue;
                    }
                    std::string tLeft = trim(left);
                    if (tLeft[0]=='"' && tLeft[tLeft.length() - 1]=='"') {
                        //std::cout << "dequoting[" << tLeft << "]" << std::endl;
                        tLeft=tLeft.substr(1, tLeft.length() - 2);
                    }
                    bool alreadySet = false;

                    size_t dotPos = tLeft.find('.');
                    if (dotPos != std::string::npos) {
                        js_internal_storage **key = getObjectKeyPointer(tLeft, &rootScope);
                        if (key != nullptr) {
                            alreadySet = true;
                            *key = storage;
                        }
                    }
                    if (!alreadySet) {
                        std::cout << "doAssignment final assign[" << tLeft << "] of type[" << typeOfStorage(storage) << "] storageFalse[" << jsIsFalse(storage) <<"] scope[" << &rootScope << "]\n";
                        rootScope.locals.value[tLeft] = storage;
                        jsDisplayScope(&rootScope, 0);
                        left = ""; // reset left
                        assignfile << tLeft << "=" << it << "\n";
                    }
                } else {
                    std::cout << std::string(doAssignmentLevel * 2, ' ') << "HALT can't get value from [" << it << "]\n";
                }
            }
        } else if (state == 2) {
            js_object *newObject = jsGetObject(rootScope, it);
            /*
            js_function *objectScope = new js_function;
            js_object *newObject = new js_object;
            //doAssignment(*objectScope, it);
            parseJSON(*objectScope, it);
            // translate the scope into js_object
            //std::cout << "JSON object output" << std::endl;
            for(auto it2 : objectScope->locals.value) {
                //std::cout << "[" << it2.first << "=" << it2.second << "]" << std::endl;
                newObject->value[it2.first] = it2.second;
            }
             */
            //std::cout << "JSON object done" << std::endl;
            std::string tLeft = trim(left);
            assignfile << tLeft << "=" << "_NTRJSON" << "\n";
            rootScope.locals.value[tLeft] = newObject;
            state = 0;
        } else if (state == 3) { // started with state 1 function (we're in a function definition)
            // function body
            //std::cout << "function body[" << it << "]\n";
            //func = new js_function;

            // parse params
            //cursor = locateFunctionParamsEnd(it, 0);
            // you need to find first brace
            // call parseFunctionBody
            //last = parseFunctionBody(it, cursor);

            //parseFunctionBody(it, 0, *func);
            // + 1 to skip initial { and not + 1 because }
            //func = makeFunctionFromString(it.substr(cursor + 1, cursor - last), &rootScope);
            //std::cout << "doAssignment makeFunctionFromString start[" << it[0] << "] last[" << it[it.length() - 1] << "]\n";
            std::string body = it.substr(1, it.length() - 2);
            //std::cout << "doAssignment makeFunctionFromString2 start[" << body[0] << "] last[" << body[body.length() - 1] << "]\n";
            func = makeFunctionFromString(body, prototype, &rootScope);
            // we already have the body of the function

            // we need to get this token (string that is the body of the function)
            // into js_function

            std::cout << std::string(doAssignmentLevel * 2, ' ') << "doAssignment - creating function [" << left << "] with [" << func->tokens.size() << "]tokens\n";
            // warning copy & paste
            std::string tLeft = trim(left);
            size_t dotPos = tLeft.find('.');
            bool alreadySet = false;
            if (dotPos != std::string::npos) {
                //std::cout << "doAssignment - Key Has . \n";
                std::string baseObj = tLeft.substr(0, dotPos);
                //std::cout << "doAssignment - base[" << baseObj << "]" << std::endl;
                js_internal_storage *baseStorage = jsLocateKey(&rootScope, baseObj);
                if (baseStorage) {
                    std::string part2 = tLeft.substr(dotPos + 1);
                    //std::cout << "doAssignment - part2[" << part2 << "]" << std::endl;
                    js_object *jobj = dynamic_cast<js_object*>(baseStorage);
                    if (jobj) {
                        /*
                         if (jobj->value.find(part2) != jobj->value.end()) {
                         std::cout << "doAssignment - part2[" << part2 << "] is inside base" << std::endl;
                         //storage = jobj->value[part2]; // we will now point to this storage
                         } else {
                         std::cout << "doAssignment - part2[" << part2 << "] not in base" << std::endl;
                         for(auto it2 : jobj->value) {
                         std::cout << "[" << it2.first << "]\n";
                         }
                         }
                         */
                        assignfile << tLeft << "=" << "_NTRFUNC" << "\n";
                        jobj->value[part2] = func;
                        alreadySet = true;
                    } else {
                        std::cout << std::string(doAssignmentLevel * 2, ' ') << "doAssignment - baseStorage couldn't be made a js_object. type[" << typeOfStorage(baseStorage) << "]\n";
                    }
                    //
                    //js_internal_storage *p2Storage = locateKey(&rootScope, value);
                } else {
                    std::cout << std::string(doAssignmentLevel * 2, ' ') << "doAssignment - Couldnt find base[" << baseObj << "] in scope" << std::endl;
                }
            }
            if (!alreadySet) {
                rootScope.locals.value[tLeft] = func;
            }

            state = 5;
        } else if (state == 4) {
            if (trimmedToken == "}") {
                //displayScope(&rootScope, 0);
                state = 5; // reset state
            }
        } else if (state == 5) { // function definition call check
            // wait for ()
            if (trimmedToken == "(") {
                state = 6;
            } else {
                state = 0; // reset
            }
        } else if (state == 6) { // call function just defined
            if (trimmedToken == ")") {
                // parse/execute the tokens of said function
                std::cout << std::string(doAssignmentLevel * 2, ' ') << "doAssignment6 - calling just defined function, tokens[" << func->tokens.size() << "]\n";
                if (func->tokens.size()) {
                    std::cout << std::string(doAssignmentLevel * 2, ' ') << "doAssignment6 - calling just defined function, first token[" << func->tokens[0] << "]\n";
                }
                jsParseTokens(func->tokens, func);
                state = 0; // reset
            } else {
                // collect params
                std::cout << std::string(doAssignmentLevel * 2, ' ') << "doAssignment6 - WRITE ME collect params [" << it << "]\n";
            }
        } else if (state == 8) { // is a function call or expression
            if (trimmedToken == ")") {
                state = 0; // reset state
            } else {
                // call params
                std::cout << std::string(doAssignmentLevel * 2, ' ') << "HALT need to parse these params[" << it << "]\n";
            }
        } else if (state == 9) { // are we a function call with return value, or assigning a function reference
            js_internal_storage *storage = nullptr;
            // we'll need a way to get tLeft back to set it
            if (trimmedToken == "(") {
                // it's a function call
                // set state = 10 until we get all prototype
                state = 10;
                //storage = jsParseTokens(callFunc->tokens, callFunc);
            } else {
                // it's a function reference
                storage = callFunc;
                state = 0; // assuming end of this expression
            }
            if (storage) {
                std::string tLeft = trim(left);
                if (tLeft[0]=='"' && tLeft[tLeft.length() - 1]=='"') {
                    //std::cout << "dequoting[" << tLeft << "]" << std::endl;
                    tLeft=tLeft.substr(1, tLeft.length() - 2);
                }
                bool alreadySet = false;

                size_t dotPos = tLeft.find('.');
                if (dotPos != std::string::npos) {
                    js_internal_storage **key = getObjectKeyPointer(tLeft, &rootScope);
                    if (key != nullptr) {
                        alreadySet = true;
                        *key = storage;
                    }
                }
                if (!alreadySet) {
                    std::cout << "doAssignment final assign[" << tLeft << "] of type[" << typeOfStorage(storage) << "] scope[" << &rootScope << "]\n";
                    rootScope.locals.value[tLeft] = storage;
                    jsDisplayScope(&rootScope, 0);
                    left = ""; // reset left
                    assignfile << tLeft << "=" << it << "\n";
                }
            }
        } else if (state == 10) { // in function call params
            if (trimmedToken == ")") {
               // use doFunctionCall
                js_internal_storage *storage = nullptr;
                std::cout << "calling function [" << callFunc << "](" << params << ")\n";
                storage = jsFunctionCall(callFuncName, callFunc, params, rootScope);
                //storage = doFunctionCall(callFunc, params, rootScope);
                std::cout << "function retval[" << storage << "] type[" << typeOfStorage(storage) << "]\n";
                if (storage) {
                    std::string tLeft = trim(left);
                    if (tLeft[0]=='"' && tLeft[tLeft.length() - 1]=='"') {
                        //std::cout << "dequoting[" << tLeft << "]" << std::endl;
                        tLeft=tLeft.substr(1, tLeft.length() - 2);
                    }
                    bool alreadySet = false;

                    size_t dotPos = tLeft.find('.');
                    if (dotPos != std::string::npos) {
                        js_internal_storage **key = getObjectKeyPointer(tLeft, &rootScope);
                        if (key != nullptr) {
                            alreadySet = true;
                            *key = storage;
                        }
                    }
                    if (!alreadySet) {
                        std::cout << "doAssignment final assign[" << tLeft << "] of type[" << typeOfStorage(storage) << "] scope[" << &rootScope << "]\n";
                        rootScope.locals.value[tLeft] = storage;
                        jsDisplayScope(&rootScope, 0);
                        left = ""; // reset left
                        assignfile << tLeft << "=" << it << "\n";
                    }
                }
                state = 0;
            } else {
                // collect params
                params = trimmedToken;
            }
        }
        // { starts JSON capture (should be exactly one block before the } token)
        // you create a scope for that variable and recurse
        lastToken = it;
    }
    std::cout << std::string(doAssignmentLevel * 2, ' ') << "expression end, state " << std::to_string(state) << std::endl << std::endl;
    //displayScope(&rootScope, 0);
    /*
    auto hasTripleEqual = token.find("===");
    auto hasDoubleEqual = std::string::npos;
    auto hasSingleEqual = std::string::npos;
    if (hasTripleEqual == std::string::npos) {
        hasDoubleEqual = token.find("==");
    } else {
        // process === expression
        std::cout << "JSParser:::doAssignment - strict compare not implemented" << std::endl;
        //std::cout << "token[" << token << "]" << std::endl;
    }
    if (hasDoubleEqual == std::string::npos) {
        hasSingleEqual = token.find("=");
    } else {
        // process == expression
        std::cout << "JSParser:::doAssignment - compare not implemented" << std::endl;
    }
    if (hasSingleEqual != std::string::npos) {
        auto keyValue = split(token, '=');
        if (keyValue.size() < 2) {
            std::cout << "JSParser:::doAssignment - bad var parse " << keyValue[0] << std::endl;
            return false;
        }
        // FIXME: dot notation in keys
        auto key = trim(keyValue[0]);
        // FIXME: is value a lambda
        auto value = trim(keyValue[1]);
        //std::cout << "[" << key << "=" << value << "]" << std::endl;
        rootScope.variables[key] = value;
    } else {
        // var bob; just make sure the variable exists
        rootScope.variables[token] = "";
    }
    */
    doAssignmentLevel--;
    return true;
}

js_internal_storage **getObjectKeyPointer(const std::string input, const js_function *scope) {

    size_t dotPos = input.find('.');
    if (dotPos != std::string::npos) {
        std::cout << "getObjectKeyPointer - Key Has . scope[" << scope << "]\n";
        std::string baseObj = input.substr(0, dotPos);
        std::cout << "getObjectKeyPointer - base[" << baseObj << "]" << std::endl;
        js_internal_storage *baseStorage = jsLocateKey(scope, baseObj);
        if (baseStorage) {
            std::string part2 = input.substr(dotPos + 1);
            js_object *jobj = dynamic_cast<js_object*>(baseStorage);
            // if not object, try ref and func
            if (jobj == nullptr) {
                js_reference *jref = dynamic_cast<js_reference*>(baseStorage);
                if (jref) {
                    // try to cast it as an object
                    jobj = dynamic_cast<js_object*>(jref->ptr);
                    if (jobj == nullptr) {
                        // if not an object, try function
                        js_function *jfunc = dynamic_cast<js_function*>(jref->ptr);
                        if (jfunc) {
                            jobj = &jfunc->locals;
                        }
                    }
                }
            }
            if (jobj) {
                //jobj->value[part2] = storage;
                //assignfile << "[" << baseObj << "].[" << part2 << "]=" << "\n";
                //return true;
                /*
                std::cout << "getObjectKeyPointer - precheck\n";
                for(auto it2 : jobj->value) {
                    std::cout << "getObjectKeyPointer - precheck key[" << it2.first << "] valueType [" << typeOfStorage(it2.second) << "]\n";
                }
                */
                std::cout << "getObjectKeyPointer found base value[" << jobj->value[part2] << "] type[" << typeOfStorage(jobj->value[part2]) << "]\n";
                if (jobj->value[part2] == nullptr) {
                    std::cout << "getObjectKeyPointer - NULL value\n";
                    for(auto it2 : jobj->value) {
                        std::string type = typeOfStorage(it2.second);
                        if (type == "string") {
                            js_string *string = dynamic_cast<js_string*>(it2.second);
                            std::cout << "getObjectKeyPointer - NULL key[" << it2.first << "] address[" << it2.second << "] stringValue[" << string->value << "]\n";
                        } else {
                            std::cout << "getObjectKeyPointer - NULL key[" << it2.first << "] address[" << it2.second << "] valueType [" << type << "]\n";
                        }
                    }
                }
                return &jobj->value[part2];
            } else {
                std::cout << "getObjectKeyPointer [" << baseObj << "] isnt an object\n";
                // it's fine as long as the variable is defined
            }
        } else {
            std::cout << "getObjectKeyPointer - Couldnt find base[" << baseObj << "] in scope" << std::endl;
            jsDisplayScope(scope, 0);
        }
    } else {
        // still check our scope for
    }
    return nullptr;
}

// we need the difference between base is there and not there
// dereferenceBaseExists
bool dereferenceHasBase(const std::string input, const js_function *scope) {
    size_t dotPos = input.find('.');
    if (dotPos != std::string::npos) {
        std::string baseObj = input.substr(0, dotPos);
        js_internal_storage *baseStorage = jsLocateKey(scope, baseObj);
        if (baseStorage) {
            return true;
        }
    }
    dotPos = input.find('[');
    if (dotPos != std::string::npos) {
        size_t end = input.find(']');
        if (end != std::string::npos) {
            // we have an open and close
            std::string baseObj = input.substr(0, dotPos);
            js_internal_storage *baseStorage = jsLocateKey(scope, baseObj);
            if (baseStorage) {
                return true;
            }
        }
    }
    return false;
}

js_internal_storage *dereferenceObject(const std::string input, const js_function *scope) {
    // FIXME: too simple, catches quoted strings with . in them
    // FIXME: make sure we're not inside quotes
    size_t dotPos = input.find('.');
    if (dotPos != std::string::npos) {
        std::string baseObj = input.substr(0, dotPos);
        js_internal_storage *baseStorage = jsLocateKey(scope, baseObj);
        if (baseStorage) {
            std::string part2 = input.substr(dotPos + 1);
            js_object *jobj = dynamic_cast<js_object*>(baseStorage);
            if (jobj) {
                if (jobj->value.find(part2) != jobj->value.end()) {
                    std::cout << "dereferenceObject - part2[" << part2 << "] is inside base" << std::endl;
                    return jobj->value[part2]; // we will now point to this storage
                } else {
                    std::cout << "dereferenceObject - part2[" << part2 << "] not in base" << std::endl;
                    for(auto it2 : jobj->value) {
                        std::cout << "[" << it2.first << "]\n";
                    }
                }
            } else {
                std::cout << "dereferenceObject - baseStorage couldn't be made a js_object. type[" << typeOfStorage(baseStorage) << "]\n";
                // it's ok for a key to not exists in a defined variable
            }
        } else {
            std::cout << "dereferenceObject - Couldnt find base[" << baseObj << "] in scope" << std::endl;
        }
    }
    dotPos = input.find('[');
    if (dotPos != std::string::npos) {
        size_t end = input.find(']');
        if (end != std::string::npos) {
            // we have an open and close
            std::string baseObj = input.substr(0, dotPos);
            js_internal_storage *baseStorage = jsLocateKey(scope, baseObj);
            if (baseStorage) {
                std::string part2 = input.substr(dotPos + 1, end - dotPos - 1);
                char lastChar = part2[part2.length() - 1];
                // probably should run this through doExpression...
                // FIXME: "asdf" + var + "zxcv"
                if ((part2[0] == '"' && lastChar == '"') || (part2[0] == '\'' && lastChar == '\'')) {
                    js_object *jobj = dynamic_cast<js_object*>(baseStorage);
                    if (jobj) {
                        std::cout << "dereferenceObject bracket style <" << baseObj << ">[<" << part2 << "><" << input.substr(end) << ">\n";
                        // is constant
                        std::string constant = part2.substr(1, part2.length() - 2);
                        std::cout << "constant[" << constant << "]\n";
                        if (jobj->value.find(constant) != jobj->value.end()) {
                            std::cout << "dereferenceObject - constant[" << constant << "] is inside base" << std::endl;
                            return jobj->value[part2]; // we will now point to this storage
                        } else {
                            std::cout << "dereferenceObject - constant[" << constant << "] not in base" << std::endl;
                            for(auto it2 : jobj->value) {
                                std::cout << "[" << it2.first << "]\n";
                            }
                        }
                    } else {
                        std::cout << "dereferenceObject - baseStorage[" << baseObj << "] couldn't be made a js_object. type[" << typeOfStorage(baseStorage) << "]\n";
                        if (typeOfStorage(baseStorage) == "string") {
                            js_string *jstring = dynamic_cast<js_string *>(baseStorage);
                            std::cout << "baseStorage string value[" << jstring->value << "]\n";
                        }
                    }
                } else {
                    // is variable
                    std::cout << "dereferenceObject bracket style HALT, expression dereference\n";
                }
            } else {
                std::cout << "dereferenceObject - Couldnt find base[" << baseObj << "] in scope" << std::endl;
            }
        }
    }
    return nullptr;
}

// should return if we're halted or not...
js_internal_storage *jsParseTokens(const std::vector<std::string> &tokens, js_function *scope) {
    // we need to at least build the root scope
    //std::cout << "\nstart script" << std::endl;
    js_internal_storage *stack = nullptr;
    for(auto it : tokens) {
        std::string ttoken = trim(it);
        if (ttoken == "") continue; // skip empty tokens
        std::cout << "parse token[" << it << "]" << std::endl;
        if (ttoken.substr(0, 2)=="if") {
            execfile << "if not implemented\n";
            std::string ifStr = it.substr(2);
            std::cout << "ifStr1[" << ifStr << "]" << std::endl;
            // find (
            size_t end = ifStr.find('(');
            ifStr = ifStr.substr(end + 1); // skip the (
            std::cout << "ifStr2[" << ifStr << "]" << std::endl;
            // find )
            // too simple, won't work if expression has a function call...
            end = getNextExpression(ifStr, 0); // works great so far
            //end = ifStr.find(')');
            std::string ifCondition = ifStr.substr(0, end);
            js_internal_storage *expRes = doExpression(*scope, ifCondition);
            bool conditionRes = !jsIsFalse(expRes);
            std::cout << "ifCondition[" << ifCondition << "] res[" << conditionRes << "]" << std::endl;
            ifStr = ifStr.substr(0, end);
            // then we'll use !isFalse to figure out if we need to exec this attached scope of code
            // also don't forget about the else block and potentially elif
            // FIXME: do we have a block start? yes but we won't have the other lines....
            std::cout << "true block start search[" << ttoken.substr(3 + ifCondition.size()) << "]" << std::endl;
            size_t start = ttoken.substr(3 + ifCondition.size()).find(')') + 3 + ifCondition.size() + 1; // after the )
            end = parseFunctionBody(ttoken, start);
            std::cout << "true block[" << ttoken.substr(start, end - start) << "]" << std::endl;
            if (conditionRes) {
                // a return in an if scope, would return from the function it's in
                std::vector<std::string> tokens = jsGetTokens(ttoken, start);
                js_internal_storage *val=jsParseTokens(tokens, scope);
                std::cout << "value res[" << val << "]\n";
                // skip all elses
            } else {
                // handle else
                std::cout << "looking for else [" << ttoken.substr(end) << "]\n";
                start = ttoken.substr(end).find("else");
                if (start != std::string::npos) {
                    std::cout << "false condition ELSE not implemented" << std::endl;
                }
            }
            //std::cout << "HALT if not implemented" << std::endl;
            //return nullptr;
        } else if (ttoken.substr(0, 3)=="var") {
            std::string listStr = it.substr(3);
            // FIXME: , in quotes or {} (JSON) <= top priority for 4chan
            std::vector<std::string> opens, closes;
            opens.push_back("{");
            opens.push_back("'");
            opens.push_back("\"");
            closes.push_back("}");
            closes.push_back("'");
            closes.push_back("\"");
            auto varList = parseSepButNotBetween(listStr, ",", opens, closes);
            //std::cout << "has " << varList.size() << " variables" << std::endl;
            for(auto it2 : varList) {
                //std::cout << "var processing [" << it2 << "]" << std::endl;
                /*
                 // FIXME: make sure = isn't in quotes or JSON?
                 // FIXME: double or triple equal differentiation
                 //std::cout << "looking at [" << it2 << "]" << std::endl;
                 auto hasTripleEqual = it2.find("===");
                 auto hasDoubleEqual = std::string::npos;
                 auto hasSingleEqual = std::string::npos;
                 if (hasTripleEqual == std::string::npos) {
                 hasDoubleEqual = it2.find("==");
                 } else {
                 // process expression
                 std::cout << "var strict compare not implemented" << std::endl;
                 }
                 if (hasDoubleEqual == std::string::npos) {
                 hasSingleEqual = it2.find("=");
                 } else {
                 // process expression
                 std::cout << "var compare not implemented" << std::endl;
                 }
                 if (hasSingleEqual != std::string::npos) {
                 auto keyValue = split(it2, '=');
                 if (keyValue.size() < 2) {
                 std::cout << "bad var parse " << keyValue[0] << std::endl;
                 continue;
                 }
                 // FIXME: dot notation in keys
                 auto key = trim(keyValue[0]);
                 auto value = trim(keyValue[1]);
                 //std::cout << "[" << key << "=" << value <<s"]" << std::endl;
                 script->rootScope.variables[key] = value;
                 } else {
                 // var bob; just make sure the variable exists
                 script->rootScope.variables[it2] = "";
                 }
                 */
                //std::cout << "About to assign, current scope: \n";
                //displayScope(scope, 0);
                execfile << "var - " << trim(it2) << " scope[" << scope << "]\n";
                doAssignment(*scope, it2);
            }
        } else if (ttoken.substr(0, 9)=="function ") {
            // ParseFunction
            std::string defStr = it.substr(9);
            // find ( (name end, prototype start)
            size_t end = defStr.find('(');
            std::string funcName = defStr.substr(0, end);
            defStr = defStr.substr(end + 1); // next char after (
            // find ) (prototype end)
            end = defStr.find(')');
            std::string prototype = defStr.substr(0, end);
            defStr = defStr.substr(end + 1); // next char after )
            // find { (func start)
            end = defStr.find('{');
            defStr = defStr.substr(end + 1, defStr.size() - 2); // from { to the end
            //std::cout << "jsParseTokens Function Declartion start[" << defStr[0] << "] last[" << defStr[defStr.length() - 1] << "]\n";
            auto funcTokens = jsGetTokens(defStr, 0);
            //std::cout << "function [" << funcName << "] prototype [" << prototype << "] has [" << funcTokens.size() << "] tokens" << std::endl;
            // __netrunner_function_definition is 31 chars
            //scope->variables[funcName] = "__netrunner_function_definition = { prototype: \"" + prototype + "\", code: \"" + defStr + "\" }";
            js_function *newFunc = new js_function;
            newFunc->tokens = funcTokens;
            newFunc->parentScope = scope;
            scope->locals.value[funcName] = newFunc;
            execfile << "function declaration [" << funcName << "](" << prototype << ") tokens[" << funcTokens.size() << "]\n";
        } else if (ttoken.substr(0, 6)=="return") {
            std::string afterReturn = ttoken.substr(7);
            return doExpression(*scope, afterReturn);
        } else if (ttoken.find("=") != std::string::npos) {
            execfile << "assignment - " << it << "\n";
            // has = so it's an expression
            //std::cout << "assignment[" << it << "]" << std::endl;
            //std::cout << "assignment not implemented" << std::endl;
            doAssignment(*scope, it);
        } else if (ttoken.find("(") != std::string::npos && ttoken.find(")") != std::string::npos) {
            execfile << "function call\n";
            // has () so it's a function call
            //std::cout << "funcCall[" << it << "]" << std::endl;
            // we need to start passed any && or ||
            // need to parse any expression before the function call...
            if (it.find("&&") == std::string::npos && it.find("||") == std::string::npos) {
                // figure out function name
                size_t parenStart = it.find("(");
                std::string funcName = it.substr(0, parenStart);
                //std::cout << "I think the function name is [" << funcName << "]" << std::endl;
                std::string arguments = it.substr(parenStart + 1, it.find(")") - parenStart - 1);
                //std::cout << "functionCall[" << funcName << "](" << arguments << ") not implemented" << std::endl;
                execfile << "function call [" << funcName << "](" << arguments << ")\n";

                js_internal_storage *storage = nullptr;
                bool deref = dereferenceHasBase(funcName, scope);
                if (deref) {
                    storage = dereferenceObject(funcName, scope);
                }
                // need to convert to storage to create js_function object
                if (storage == nullptr) {
                    storage = jsLocateKey(scope, funcName);
                }
                if (storage == nullptr) {
                    std::cout << "HALT Function [" << funcName << "] d.n.e in this scope or parents, creating forwardCall" << std::endl;
                    jsDisplayScope(scope, 0);
                    return nullptr;
                    //scope->forwardCalls.push_back(funcName);
                    // we can't do much without the body of the function
                } else {
                    js_function *func = dynamic_cast<js_function *>(storage);
                    if (!func) {
                        std::cout << "HALT Function [" << funcName << "] data isn't a function" << std::endl;
                        return nullptr;
                    }
                    // make sure function is parsed
                    // and step through tokens
                    // we should update the parameter values...
                    std::cout << "WARNING functionCall[" << funcName << "](" << arguments << ") parameters not implemented" << std::endl;
                    jsParseTokens(func->tokens, func);
                    //std::cout << "parameters[" << arguments << "]" << std::endl;
                }
            } else {
                execfile << "function call - expression scope[" << scope << "]\n";
                // we have an || or && instead ()
                // window.clickable_ids&&document.addEventListener("4chanParsingDone",onParsingDone,!1)
                // well it's technically an expression
                // as long as we handle the && first
                // then the left side before the right

                // well fuck, lets tokenize left to right
                stack = doExpression(*scope, it);
                //std::cout << "HALT expression before functionCall not implemented [" << it << "]" << std::endl;
                //return nullptr;
            }
            //std::cout << "functionCall not implemented" << std::endl;
        } else {
            std::cout << "jsParseTokens - unknown_type[" << it << "]" << std::endl;
        }
    }
    return stack;
}

void JavaScript::parse(const std::string source) {
    // tokenize source
    //std::cout << "JavaScript::parse source[" << source << "]\n\n";
    this->tokens = jsGetTokens(source, 0);
    jsParseTokens(this->tokens, &this->rootScope);
}

void JavaScript::execute() {
    // probably not needed because JS is executed as parsed in the browser
}

void JavaScript::append(const std::shared_ptr<JavaScript> &source) {
    for(auto it : source->tokens) {
        this->tokens.push_back(it);
    }
}

void JavaScript::applyScope(const std::shared_ptr<JavaScript> &source) {
    // merge scopes (instead of reparsing)
    //std::cout << "JavaScript::append - merge scope" << std::endl;
    for(auto it : source->rootScope.locals.value) {
        //std::cout << "JavaScript::append - copying " << it.first << std::endl;
        std::string type = typeOfStorage(it.second);
        if (type == "reference") {
            js_reference *srcRefDest = dynamic_cast<js_reference*>(it.second);
            if (srcRefDest != nullptr) {
                /*
                bool found = false;
                std::string sourceKey = "";
                for(auto it2 : source->rootScope.locals.value) {
                    std::cout << "ptr[" << srcRefDest->ptr << "]==[" << it2.second << "]\n";
                    if (it2.second == srcRefDest->ptr) {
                        sourceKey = it2.first;
                        found = true;
                        break;
                    }
                }
                */
                if (srcRefDest->ptr == &source->rootScope) {
                    //std::cout << "JavaScript::append - copying reference " << it.first << " points to root" << std::endl;
                    js_reference *trgRef = new js_reference; // FIXME: clean up somewhere
                    trgRef->ptr = &this->rootScope;
                    this->rootScope.locals.value[it.first] = trgRef;
                } else {
                    std::string sourceKey = jsLocatePtrKey(&source->rootScope, srcRefDest->ptr);
                    if (sourceKey != "") {
                        std::cout << "JavaScript::append - WRITE ME copying reference " << it.first << " points to sourceKey[" << sourceKey << "] type[" << typeOfStorage(srcRefDest->ptr) << "]" << std::endl;
                    } else {
                       std::cout << "JavaScript::append - Reference[" << it.first << "] couldnt find reference\n";
                    }
                }
            } else {
                std::cout << "JavaScript::append - Reference[" << it.first << "] doesnt point to a reference\n";
            }
        } else {
            this->rootScope.locals.value[it.first] = it.second;
        }
    }
    //std::cout << "JavaScript::append - after scope" << std::endl;
    //jsDisplayScope(&this->rootScope, 0);
}