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.
 
 
 
 

2001 lines
89 KiB

#include "JSParser.h"
#include "../../../tools/StringUtils.h"
#include <iostream>
#include <fstream>
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_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";
return "unknown";
}
js_internal_storage *locateKey(js_scope *scope, std::string key) {
// do we have it?
auto locate = scope->data.find(key);
if (locate == scope->data.end()) {
// we do have a parent?
if (scope->parent) {
return locateKey(scope->parent, key);
}
// no parent
return nullptr;
}
// we have it
return locate->second;
}
void displayScope(js_scope *scope, size_t level) {
std::cout << "There are " << scope->data.size() << " elements at this level " << level << "\n";
for(auto it : scope->data) {
std::cout << "[" << level << "]" << "[" << it.first << "] type[" << typeOfStorage(it.second) << "]\n";
}
if (scope->parent) {
level++;
displayScope(scope->parent, level);
}
}
std::vector<std::string> getTokensInternal(const std::string &source, const size_t start) {
std::vector<std::string> tokens;
//std::cout << "source: " << source << "\n" << std::endl;
// 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++) {
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] == '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 ) {
return tokens;
}
//std::cout << "getTokensInternal - 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;
}
std::vector<std::string> JSParser::getTokens(const std::string &source) const {
return getTokensInternal(source, 0);
}
void doExpression(js_scope &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;
for (cursor = 0; cursor < token.length(); cursor++) {
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) {
expression_parts.push_back(token.substr(last, cursor - last - 1));
}
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 + 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] != '=' && 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("?");
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;
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;
}
}
}
}
}
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();
}
int getNextExpressionLevel = 0;
// we return the terminator if ,;\n or )}
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;
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++;
state = 1;
}
// 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 << "getNextExpression parseFunctionBody last[" << source[cursor] << "]\n";
}
// start scope
if (source[cursor]=='{') {
parenLevel++;
state = 2;
}
// minification technique
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--;
return cursor - 1;
}
// this is the last element in a list
if (source[cursor]==')') {
getNextExpressionLevel--;
return cursor - 1;
}
break;
case 1:
// (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;
}
}
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 parseFunctionBody(std::string source, size_t start) {
std::cout << "\nparseFunctionBody start[" << source.substr(start) << "]\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 scanning[" << source[cursor] << "] at[" << cursor << "/" << source.length() << "] state[" << std::to_string(state) << "]\n";
switch(state) {
case 0:
if (source[cursor] == '{') {
//std::cout << "last " << last << " 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:
// what we can enter that has {
// well a function
// so we need to step expression by expression tbh
size_t next = getNextExpression(source, cursor);
if (next < cursor) next = cursor; // advanced at least 1 char
std::cout << "parseFunctionBody - start[" << cursor << "] next[" << next << "]\n";
std::string exp = source.substr(cursor, next + 1 - cursor);
std::cout << "parseFunctionBody - 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) {
std::cout << "parseFunctionBody final [" << source.substr(start, cursor + 1 - start) << "]\n";
return cursor;
}
}
}
cursor = next;
std::cout << "parseFunctionBody1 now at " << cursor << "/" << source.length() << " char[" << source[cursor] << "]\n";
if (cursor == source.length() - 1) {
// we just ended on
if (source[cursor]=='}') {
return cursor;
}
}
/*
if (source[cursor] == '{') {
parenLevel++;
} else
if (source[cursor] == '}') {
parenLevel--;
if (!parenLevel) {
return cursor;
}
}
*/
break;
}
}
std::cout << "parseFunctionBody - HALT didnt find closing element for function\n";
return source.length();
/*
//getTokensInternal will tokenize the entire body of the function
// and return it as one token
std::vector<std::string> tokens = getTokensInternal(source, start + 1); // +1 to skip {
std::cout << "\nparseFunctionBody - getTokensInternal got " << tokens.size() << " tokens\n";
for(auto it: tokens) {
std::cout << "[" << it << "]" "\n";
}
std::string funcBody = tokens[0];
return start + funcBody.length();
*/
}
js_function *makeFunctionFromString(std::string body, js_scope *parent) {
js_function *func = new js_function;
func->tokens = getTokensInternal(body, 0);
func->scope.parent = parent;
return func;
}
void parseArray(js_scope &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.data[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_scope *objectScope = new js_scope;
parseJSON(*objectScope, JSON);
js_object *newObject = new js_object;
rootScope.data[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_scope *objectScope = new js_scope;
parseJSON(*objectScope, JSON);
js_object *newObject = new js_object;
rootScope.data[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;
}
}
}
}
}
// we handle right value
// we could be in JSON { key:
// we could be in variable bob=
// token: we have a complete expression
// if right is:
// - object
// - array
// - string
// - function
// we could avoid returning, if a pointer to the lvalue is passed in
js_internal_storage *parseExpression(js_scope &rootScope, std::string token) {
js_internal_storage *returnValue = nullptr;
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++) {
// 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 != "") {
js_string *newString = new js_string();
newString->value = value;
}
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 << "PE Need to deJSON [" << JSON << "]" << std::endl;
// well we can get a scope back
js_scope *objectScope = new js_scope;
parseJSON(*objectScope, JSON);
//js_object *newObject = new js_object;
//rootScope.data[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 arrayStr = token.substr(parenStart + 1, cursor - parenStart);
std::cout << "Need to deArray [" << arrayStr << "]" << std::endl;
// well we can get a scope back
js_scope *objectScope = new js_scope;
parseArray(*objectScope, arrayStr);
//js_object *newObject = new js_object;
//rootScope.data[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;
}
}
return returnValue;
}
void parseJSON(js_scope &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;
for (cursor = 0; cursor < token.length(); cursor++) {
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;
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);
cursor = locateFunctionParamsEnd(token, cursor);
// you need to find first brace
last = parseFunctionBody(token, cursor);
//std::cout << "parseJSON last[" << token[last] << "]\n";
std::string funcContents = token.substr(cursor, last - cursor + 1);
std::cout << "parsingJSON - function " << json_keys.back() << " {" << funcContents << "}" << std::endl;
cursor = last + 1;
// 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.data[json_keys.back()] = makeFunctionFromString(funcContents, &rootScope);
valueState = 6;
//keyState = 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.data[json_keys.back()] = newString;
}
last = cursor + 1;
keyState = 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_scope *objectScope = new js_scope;
parseJSON(*objectScope, JSON);
js_object *newObject = new js_object;
assignfile << "JSON." << json_keys.back() << "=" << "_NTRJSON" << "\n";
rootScope.data[json_keys.back()] = newObject;
last = cursor + 1;
valueState = 0;
keyState = 0;
}
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_scope *arrayScope = new js_scope;
parseArray(*arrayScope, arrayStr);
js_array *newArray = new js_array;
assignfile << "JSON." << json_keys.back() << "=" << "_NTRARRAY" << "\n";
rootScope.data[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
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;
*/
keyState = 0;
quoteStart = 0;
quoteEnd = 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(keyState) << "]\n";
if (keyState == 2) {
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.data[json_keys.back()] = newString;
}
}
displayScope(&rootScope, 0);
}
bool doAssignment(js_scope &rootScope, std::string token) {
// 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++) {
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) {
expression_parts.push_back(token.substr(last, cursor - last - 1));
}
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 + 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] != '=' && 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;
size_t next = parseFunctionBody(token, cursor);
std::string funcBody = token.substr(cursor, 1 + next - cursor);
cursor = next;
std::cout << "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 << "doAssignment6 parseFunctionBody last[" << token[cursor] << "]\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) {
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':
state = 0;
break;
case ';':
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;
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 << "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 << "expression string[" << token << "]" << std::endl;
std::cout << "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 << "Assigning [" << tLeft << "=]" << std::endl;
rootScope.variables[tLeft] = "";
std::cout << "expression end" << std::endl << std::endl;
return true;
}
js_function *func = nullptr;
for(auto it : expression_parts) {
// probably should trim these
std::cout << "expression token[" << it << "]" << std::to_string(state) << std::endl;
// default: put token in left, get op (usually =) and then get right side
if (state==0) {
if (it == "=") {
left = lastToken;
state = 1;
}
std::string first8 = it.substr(0, 8);
if (first8 == "function") {
left = it.substr(8);
state = 3;
}
// check for ()
if (it == "(") {
state = 8;
}
} else if (state == 1) {
if (it == "") continue; // skip empties, don't fire the assignment too early
std::string first8 = it.substr(0, 8);
if (first8 == "function") {
// FIXME: we have params in it...
state = 3;
} else if (it == "{") {
// FIXME: make sure it's not a function definition!
// could be JSON or a scope
state = 2;
} else {
// do assignment
std::string tLeft = trim(left);
std::string value = trim(it);
//std::cout << "left[" << tLeft[0] << "] right[" << tLeft[tLeft.length() - 1] << "]" << std::endl;
if (tLeft[0]=='"' && tLeft[tLeft.length() - 1]=='"') {
//std::cout << "dequoting[" << tLeft << "]" << std::endl;
tLeft=tLeft.substr(1, tLeft.length() - 2);
}
rootScope.variables[tLeft] = value;
// is value a constant or an other variable
js_internal_storage *storage=nullptr;
// FIXME: too simple, catches quoted strings with . in them
size_t dotPos = value.find('.');
if (dotPos != std::string::npos) {
std::cout << "doAssignment - Value Has . might be object deferencing\n";
//displayScope(&rootScope, 0);
std::string baseObj = value.substr(0, dotPos);
//std::cout << "doAssignment - base[" << baseObj << "]" << std::endl;
js_internal_storage *baseStorage = locateKey(&rootScope, baseObj);
if (baseStorage) {
std::string part2 = value.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";
}
}
} else {
std::cout << "doAssignment - baseStorage couldn't be made a js_object. type[" << typeOfStorage(baseStorage) << "]\n";
}
//
//js_internal_storage *p2Storage = locateKey(&rootScope, value);
} else {
std::cout << "doAssignment - Couldnt find base[" << baseObj << "] in scope" << std::endl;
}
}
if ((value[0] == '"' && value[value.length() - 1] == '"') || (value[0] == '\'' && value[value.length() - 1] == '\'')) {
// it's quote, so definitely a constant
js_string *jstring = new js_string;
jstring->value = value.substr(1, -1);
storage = jstring;
}
if (storage == nullptr) {
if (value == "true") {
js_bool *jbool = new js_bool;
jbool->value = true;
storage = jbool;
} else if (value == "null") {
// FIXME: is null a type?
js_bool *jbool = new js_bool;
jbool->value = false;
storage = jbool;
} else if (value == "false") {
js_bool *jbool = new js_bool;
jbool->value = false;
storage = jbool;
}
}
if (storage == nullptr) {
bool allDigits = true;
for(auto it2 : it) {
if (it2 >= '0' && it2 <= '9') {
} else {
if (it2 != '.') {
allDigits = false;
}
}
}
if (allDigits) {
js_string *jstring = new js_string;
jstring->value = value.substr(1, -1);
storage = jstring;
} else {
// potentially a variable
displayScope(&rootScope, 0);
storage = locateKey(&rootScope, value);
if (storage == nullptr) {
// could be a yet to be defined variable
std::cout << "Likely unknown variable [" << value << "]" << std::endl;
}
}
}
if (storage) {
bool alreadySet = false;
dotPos = tLeft.find('.');
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 = locateKey(&rootScope, baseObj);
if (baseStorage) {
std::string part2 = tLeft.substr(dotPos + 1);
js_object *jobj = dynamic_cast<js_object*>(baseStorage);
if (jobj) {
jobj->value[part2] = storage;
assignfile << tLeft << "=" << value << "\n";
alreadySet = true;
}
} else {
std::cout << "doAssignment - Couldnt find base[" << baseObj << "] in scope" << std::endl;
displayScope(&rootScope, 0);
}
}
if (!alreadySet) {
rootScope.data[tLeft] = storage;
assignfile << tLeft << "=" << value << "\n";
}
} else {
std::cout << "HALT unknown type [" << value << "]\n";
return false;
}
std::cout << "Assigning [" << tLeft << "=" << value << "]" << std::endl;
state = 0;
}
} else if (state == 2) {
js_scope *objectScope = new js_scope;
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->data) {
//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.data[tLeft] = newObject;
state = 0;
} else if (state == 3) {
// function body
//std::cout << "function body[" << it << "]\n";
//func = new js_function;
//parseFunctionBody(it, 0, *func);
func = makeFunctionFromString(it, &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
state = 4;
} else if (state == 4) {
if (it == "}") {
std::cout << "doAssignment - creating function [" << left << "]\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 = locateKey(&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 << "doAssignment - baseStorage couldn't be made a js_object. type[" << typeOfStorage(baseStorage) << "]\n";
}
//
//js_internal_storage *p2Storage = locateKey(&rootScope, value);
} else {
std::cout << "doAssignment - Couldnt find base[" << baseObj << "] in scope" << std::endl;
}
}
if (!alreadySet) {
rootScope.data[tLeft] = func;
}
//displayScope(&rootScope, 0);
// FIXME: we need to check to see if this function is called
//
state = 0; // reset state
}
} else if (state == 8) {
if (it == ")") {
state = 0; // reset state
} else {
// call params
std::cout << "HALT need to parse these params[" << it << "]\n";
}
}
// { starts JSON capture (should be exactly one block before the } token)
// you create a scope for that variable and recurse
lastToken = it;
}
std::cout << "expression end, state " << std::to_string(state) << std::endl << std::endl;
//displayScope(&rootScope, 0);
/*
auto hasTripleEqual = token.find("===");
auto hasDoubleEqual = std::string::npos;
auto hasSingleEqual = std::string::npos;
if (hasTripleEqual == std::string::npos) {
hasDoubleEqual = token.find("==");
} else {
// process === expression
std::cout << "JSParser:::doAssignment - strict compare not implemented" << std::endl;
//std::cout << "token[" << token << "]" << std::endl;
}
if (hasDoubleEqual == std::string::npos) {
hasSingleEqual = token.find("=");
} else {
// process == expression
std::cout << "JSParser:::doAssignment - compare not implemented" << std::endl;
}
if (hasSingleEqual != std::string::npos) {
auto keyValue = split(token, '=');
if (keyValue.size() < 2) {
std::cout << "JSParser:::doAssignment - bad var parse " << keyValue[0] << std::endl;
return false;
}
// FIXME: dot notation in keys
auto key = trim(keyValue[0]);
// FIXME: is value a lambda
auto value = trim(keyValue[1]);
//std::cout << "[" << key << "=" << value << "]" << std::endl;
rootScope.variables[key] = value;
} else {
// var bob; just make sure the variable exists
rootScope.variables[token] = "";
}
*/
return true;
}
// should return if we're halted or not...
void JSParser::parseTokens(const std::vector<std::string> &tokens, js_scope *scope) const {
// we need to at least build the root scope
//std::cout << "\nstart script" << std::endl;
for(auto it : tokens) {
//std::cout << "parse token[" << it << "]" << std::endl;
if (it.substr(0, 2)=="if") {
std::string ifStr = it.substr(2);
// find (
size_t end = ifStr.find('(');
ifStr = ifStr.substr(0, end);
// find )
end = ifStr.find(')');
std::string ifCondition = ifStr.substr(0, end);
//std::cout << "ifCondition[" << ifCondition << "]" << std::endl;
ifStr = ifStr.substr(0, end);
// do we have a block start?
// find { (block start)
end = ifStr.find('{');
// if not block start
// else
end = ifStr.find('{');
std::cout << "HALT if not implemented" << std::endl;
return;
} else if (it.substr(0, 3)=="var") {
std::string listStr = it.substr(3);
// FIXME: , in quotes or {} (JSON) <= top priority for 4chan
std::vector<std::string> opens, closes;
opens.push_back("{");
opens.push_back("'");
opens.push_back("\"");
closes.push_back("}");
closes.push_back("'");
closes.push_back("\"");
auto varList = parseSepButNotBetween(listStr, ",", opens, closes);
//std::cout << "has " << varList.size() << " variables" << std::endl;
for(auto it2 : varList) {
//std::cout << "var processing [" << it2 << "]" << std::endl;
/*
// FIXME: make sure = isn't in quotes or JSON?
// FIXME: double or triple equal differentiation
//std::cout << "looking at [" << it2 << "]" << std::endl;
auto hasTripleEqual = it2.find("===");
auto hasDoubleEqual = std::string::npos;
auto hasSingleEqual = std::string::npos;
if (hasTripleEqual == std::string::npos) {
hasDoubleEqual = it2.find("==");
} else {
// process expression
std::cout << "var strict compare not implemented" << std::endl;
}
if (hasDoubleEqual == std::string::npos) {
hasSingleEqual = it2.find("=");
} else {
// process expression
std::cout << "var compare not implemented" << std::endl;
}
if (hasSingleEqual != std::string::npos) {
auto keyValue = split(it2, '=');
if (keyValue.size() < 2) {
std::cout << "bad var parse " << keyValue[0] << std::endl;
continue;
}
// FIXME: dot notation in keys
auto key = trim(keyValue[0]);
auto value = trim(keyValue[1]);
//std::cout << "[" << key << "=" << value <<s"]" << std::endl;
script->rootScope.variables[key] = value;
} else {
// var bob; just make sure the variable exists
script->rootScope.variables[it2] = "";
}
*/
//std::cout << "About to assign, current scope: \n";
//displayScope(scope, 0);
doAssignment(*scope, it2);
}
} else if (it.substr(0, 9)=="function ") {
// ParseFunction
std::string defStr = it.substr(9);
// find ( (name end, prototype start)
size_t end = defStr.find('(');
std::string funcName = defStr.substr(0, end);
defStr = defStr.substr(end + 1); // next char after (
// find ) (prototype end)
end = defStr.find(')');
std::string prototype = defStr.substr(0, end);
defStr = defStr.substr(end + 1); // next char after )
// find { (func start)
end = defStr.find('{');
defStr = defStr.substr(end + 1, defStr.size() - 2); // from { to the end
auto funcTokens = this->getTokens(defStr);
//std::cout << "function [" << funcName << "] prototype [" << prototype << "] has [" << funcTokens.size() << "] tokens" << std::endl;
// __netrunner_function_definition is 31 chars
scope->variables[funcName] = "__netrunner_function_definition = { prototype: \"" + prototype + "\", code: \"" + defStr + "\" }";
js_function *newFunc = new js_function;
newFunc->tokens = funcTokens;
newFunc->scope.parent = scope;
scope->data[funcName] = newFunc;
} else if (it.substr(0, 6)=="return") {
// js expression here
// probably don't need to do anything here atm
std::cout << "HALT return not implemented" << std::endl;
return;
} else if (it.find("=") != std::string::npos) {
// has = so it's an expression
//std::cout << "assignment[" << it << "]" << std::endl;
//std::cout << "assignment not implemented" << std::endl;
doAssignment(*scope, it);
} else if (it.find("(") != std::string::npos && it.find(")") != std::string::npos) {
// has () so it's a function call
//std::cout << "funcCall[" << it << "]" << std::endl;
// we need to start passed any && or ||
// need to parse any expression before the function call...
if (it.find("&&") == std::string::npos && it.find("||") == std::string::npos) {
// figure out function name
size_t parenStart = it.find("(");
std::string funcName = it.substr(0, parenStart);
//std::cout << "I think the function name is [" << funcName << "]" << std::endl;
js_internal_storage *storage = locateKey(scope, funcName);
if (storage == nullptr) {
std::cout << "HALT Function [" << funcName << "] d.n.e in this scope or parents" << std::endl;
displayScope(scope, 0);
return;
}
std::string arguments = it.substr(parenStart + 1, it.find(")") - parenStart - 1);
//std::cout << "functionCall[" << funcName << "](" << arguments << ") not implemented" << std::endl;
js_function *func = dynamic_cast<js_function *>(storage);
if (!func) {
std::cout << "HALT Function [" << funcName << "] data isn't a function" << std::endl;
return;
}
// make sure function is parsed
// and step through tokens
// we should update the parameter values...
std::cout << "WARNING functionCall[" << funcName << "](" << arguments << ") parameters not implemented" << std::endl;
this->parseTokens(func->tokens, &func->scope);
//std::cout << "parameters[" << arguments << "]" << std::endl;
} else {
//std::cout << "HALT expression before functionCall not implemented [" << it << "]" << std::endl;
// we have an || or && instead ()
// window.clickable_ids&&document.addEventListener("4chanParsingDone",onParsingDone,!1)
// well it's technically an expression
// as long as we handle the && first
// then the left side before the right
// well fuck, lets tokenize left to right
doExpression(*scope, it);
return;
}
//std::cout << "functionCall not implemented" << std::endl;
} else {
std::cout << "unknown_type[" << it << "]" << std::endl;
}
}
return;
}
// removed because we need to import the state after the script is created and before we parse
/*
// extract scopes & scope.variables
// build exeecution tree
std::shared_ptr<JavaScript> JSParser::parse(const std::string &source) const {
std::shared_ptr<JavaScript> script = std::make_shared<JavaScript>();
script->tokens = this->getTokens(source);
this->parseTokens(script->tokens, &script->rootScope);
//std::cout << "end script" << "\n" << std::endl;
return script;
}
*/
std::shared_ptr<JavaScript> JSParser::append(std::shared_ptr<JavaScript> &destination, const std::shared_ptr<JavaScript> &source) const {
for(auto it : source->tokens) {
destination->tokens.push_back(it);
}
// merge scopes (instead of reparsing)
//std::cout << "JSParser::append - merge scope" << std::endl;
for(auto it : source->rootScope.variables) {
destination->rootScope.variables[it.first] = it.second;
}
for(auto it : source->rootScope.data) {
//std::cout << "JSParser::append - copying " << it.first << std::endl;
destination->rootScope.data[it.first] = it.second;
}
//std::cout << "JSParser::append - after scope" << std::endl;
//displayScope(&destination->rootScope, 0);
return destination;
}