You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2783 lines
126 KiB
2783 lines
126 KiB
#include "JSParser.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 == "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 *doFunctionCall(js_function *func, std::string params, js_function &scope) { |
|
if (func == nullptr) { |
|
std::cout << "passed null into doFunctionCall\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 + 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] != '=' && 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 + 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 + 1; |
|
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 + 1; |
|
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 + 1; |
|
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 + 1; |
|
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 + 1; |
|
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 (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 == "!") { |
|
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 |
|
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 = it; |
|
callFunc = dynamic_cast<js_function *>(dereferenceTest); |
|
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) { |
|
stack = scopeTest; |
|
} else { |
|
jsDisplayScope(&rootScope, 0); |
|
std::cout << "is Not a Var\n"; |
|
} |
|
} |
|
} 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 = doFunctionCall(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; |
|
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; |
|
} |
|
|
|
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; |
|
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 JSON = token.substr(parenStart + 1, cursor - parenStart); |
|
std::cout << "Need to deArray [" << 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; |
|
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; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
// |
|
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; |
|
js_function *func = nullptr; |
|
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; |
|
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; |
|
} |
|
// || |
|
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 prototype = ""; |
|
std::string params = ""; |
|
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 |
|
if (state==0) { |
|
negate = false; |
|
prototype = ""; |
|
if (it == "=") { |
|
left = lastToken; |
|
state = 1; |
|
} |
|
if (it == "&&") { |
|
// if stack is false... |
|
//return true; |
|
} |
|
// can actually go to 9 (include the space) |
|
std::string first8 = it.substr(0, 8); |
|
if (first8 == "function") { |
|
left = it.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 (it == "(") { |
|
state = 8; |
|
} |
|
} else if (state == 1) { |
|
if (it == "") continue; // skip empties, don't fire the assignment too early |
|
if (it == " ") continue; // skip empties, don't fire the assignment too early |
|
if (it == "!") continue; // valid expression |
|
if (it == "}") 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 (it == "(") continue; // valid part of an expression |
|
if (it == ")") continue; // valid part of an expression |
|
if (it == "&&") continue; // valid part of an expression |
|
if (it == "{") { |
|
state = 2; |
|
} else { |
|
|
|
js_internal_storage *storage = doExpression(rootScope, it); |
|
if (storage) { |
|
js_function *funcTest = dynamic_cast<js_function*>(storage); |
|
if (funcTest) { |
|
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) << "] 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_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 (it == "}" |