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

#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 == "}"