Browse Source

initial for support, deindexing arrays, dereferenceStorage(), resolveStringToKey(), Initial ternany support, prototype/param function call improvement, handle assignment to stack

master
Odilitime 9 months ago
parent
commit
5995a4b562
1 changed files with 301 additions and 17 deletions
  1. 301
    17
      src/parsers/scripting/javascript/JSParser.cpp

+ 301
- 17
src/parsers/scripting/javascript/JSParser.cpp View File

@@ -33,6 +33,14 @@ std::string typeOfStorage(js_internal_storage *storage) {
return "unknown";
}

bool isStrInt(std::string s) {
return (s.find_first_not_of( "0123456789" ) == std::string::npos);
}

bool isStrRational(std::string s) {
return (s.find_first_not_of( "0123456789." ) == std::string::npos);
}

bool jsIsFalse(js_internal_storage *generic) {
std::string type = typeOfStorage(generic);
if (type == "string") {
@@ -291,14 +299,42 @@ std::vector<std::string> jsGetTokens(const std::string &source, const size_t sta
return tokens;
}

js_internal_storage *jsFunctionCall(std::string funcName, js_function *func, std::string params, js_function &scope) {
js_internal_storage *jsFunctionCall(std::string funcName, js_function *func, std::string paramsStr, js_function &scope) {
if (isConstruct(funcName)) {
return executeConstruct(funcName, params, scope);
return executeConstruct(funcName, paramsStr, scope);
} else {
if (func == nullptr) {
std::cout << "passed null into jsFunctionCall\n";
std::cout << "HALT passed null into jsFunctionCall" << std::endl;
return nullptr;
}
// what about the params?
if (paramsStr != "") {
// FIXME: need to parse them ...
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 params = parseSepButNotBetween(paramsStr, ",", opens, closes);
// get the prototype, so we know where to assign this variables into the scope
//std::cout << "parameters supported [" << func->parameters.size() << "]" << std::endl;
uint16_t i = 0;
for(auto it = func->parameters.begin(); it != func->parameters.end(); ++it) {
std::string value = "";
if (i < params.size()) {
value = params[i];
}
js_internal_storage *storeVal = doExpression(scope, value);
std::cout << "Assigning prototype[" << *it << "] = [" << value << "]" << std::endl;
scope.locals.value[*it] = storeVal;
assignfile << *it << "=" << value << "\n";
i++;
}
// well it's implemented, just not sure how well
//std::cout << "HALT/writeme! jsFunctionCall has params[" << paramsStr << "]!" << std::endl;
}
return jsParseTokens(func->tokens, &scope);
}
}
@@ -621,6 +657,31 @@ js_internal_storage *doExpression(js_function &rootScope, std::string token) {
continue;
}

// handle assignemnt to stack
if (trimmedToken == "=") {
state = 6;
continue;
}
/*
if (trimmedToken[0] == '[') {
// like [0] potentially longer?
size_t n = std::count(trimmedToken.begin(), trimmedToken.end(), ']');
if (n != 1) {
std::cout << "we dont support multiple deindexes, write me\n";
}
// deindex
js_array *arr = dynamic_cast<js_array *>(stack);
if (!arr) {
std::cout << "we only deindex arrarys [" << typeOfStorage(stack) << "], write me\n";
} else {
}
// don't try and deference (object.value)
continue;
}
*/

// function name
// (
if (trimmedToken == "(") {
@@ -650,9 +711,13 @@ js_internal_storage *doExpression(js_function &rootScope, std::string token) {

// variable
// object.property
// object.property or object[property] or [0]
// well first lets parse the key out
// then resolve the key
// then resolve the value
bool deref = dereferenceHasBase(trimmedToken, &rootScope);
if (deref) {
// if trimmedToken is [0] it needs the stack...
js_internal_storage *dereferenceTest = dereferenceObject(trimmedToken, &rootScope);
if (dereferenceTest) {
if (typeOfStorage(dereferenceTest)=="func") {
@@ -697,6 +762,8 @@ js_internal_storage *doExpression(js_function &rootScope, std::string token) {
continue;
}
// number constant
bool allDigits = isStrRational(trimmedToken);
/*
bool allDigits = true;
for(auto it2 : trimmedToken) {
if (it2 >= '0' && it2 <= '9') {
@@ -707,14 +774,16 @@ js_internal_storage *doExpression(js_function &rootScope, std::string token) {
}
}
}
*/
if (allDigits) {
js_number *jnum = new js_number;
// FIXME: float double support
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";
@@ -758,6 +827,7 @@ js_internal_storage *doExpression(js_function &rootScope, std::string token) {
rootScope.locals.value[remainingExpr] = new js_internal_storage;
}
stack = rootScope.locals.value[remainingExpr];
/*
rootScope.locals.value[remainingExpr] = new js_internal_storage;
js_internal_storage *exprRes = doExpression(rootScope, remainingExpr);
@@ -779,7 +849,7 @@ js_internal_storage *doExpression(js_function &rootScope, std::string token) {
}
}
}
} else if (state == 1) {
} else if (state == 1) { // call function?
// in func call, double check the (
if (it == "(") {
state = 2;
@@ -789,10 +859,10 @@ js_internal_storage *doExpression(js_function &rootScope, std::string token) {
stack = callFunc;
state = 0;
}
} else if (state == 2) {
} else if (state == 2) { // start function call?
params = it;
state = 3;
} else if (state == 3) {
} else if (state == 3) { // finish function call
// in func call, double check the )
if (it == ")") {
/*
@@ -815,14 +885,14 @@ js_internal_storage *doExpression(js_function &rootScope, std::string token) {
stack = callFunc;
state = 0;
}
} else if (state == 4) {
} else if (state == 4) { // NOT operator
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) {
} else if (state == 5) { // JSON
/*
js_function *objectScope = new js_function;
js_object *newObject = new js_object;
@@ -840,6 +910,9 @@ js_internal_storage *doExpression(js_function &rootScope, std::string token) {
stack = jsGetObject(rootScope, it);
std::cout << "doExpression getObject got [" << stack << "]\n";
state = 0;
} else if (state == 6) { // stack =
std::cout << "State 6 = got [" << it << "]\n";
params += it;
} else {
std::cout << "doExpression unknown state[" << std::to_string(state) << "]\n";
}
@@ -851,6 +924,14 @@ js_internal_storage *doExpression(js_function &rootScope, std::string token) {
std::cout << "doExpressionEND1 - stack is [" << stack << "] will now have to set it to [" << callFunc << "]\n";
stack = callFunc;
state = 0;
} else
if (state == 6) {
std::cout << "Finishing state 6 [" << params << "]\n";
std::cout << "Store result in [" << typeOfStorage(stack) << "]\n";
auto resStack = doExpression(rootScope, params);
// store resStack where ever stack is pointing
params = "";
state = 0;
}
if (state != 0) {
std::cout << "doExpression final state[" << std::to_string(state) << "]\n";
@@ -1038,7 +1119,7 @@ size_t getNextExpression(const std::string source, const size_t start) {
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";
//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
@@ -1342,7 +1423,9 @@ js_function *makeFunctionFromString(const std::string body, const std::string pr
std::cout << "makeFunctionFromString [" << prototype << "][" << body << "]\n";
js_function *func = new js_function;
func->tokens = jsGetTokens(body, 0);
// FIXME: needs to be smarter
func->parameters = split(prototype, ',');
//for(auto it = func->parameters.begin(); it != func->)
if (!func->tokens.size()) {
std::cout << "empty function?\n";
}
@@ -1355,7 +1438,7 @@ js_array *jsGetArray(js_function &rootScope, std::string token) {
js_array *jsArr = new js_array;
parseJSON(*objectScope, token);
for(auto it2 : objectScope->locals.value) {
jsArr->value.push_back(*it2.second);
jsArr->value.push_back(it2.second);
}
return jsArr;
}
@@ -1820,6 +1903,7 @@ void parseJSON(js_function &rootScope, std::string token) {
}

// return value is if there was any error (false for error, true for no error)
// actually assignments are expressions
int doAssignmentLevel = 0;
bool doAssignment(js_function &rootScope, std::string token) {
doAssignmentLevel++;
@@ -2168,6 +2252,7 @@ bool doAssignment(js_function &rootScope, std::string token) {
std::cout << std::string(doAssignmentLevel * 2, ' ') << "doAssignment expression(" << doAssignmentLevel << ") token[" << it << "]" << std::to_string(state) << std::endl;
// default: put token in left, get op (usually =) and then get right side
std::string trimmedToken = trim(it);
if (trimmedToken == "") continue; // whitespace should be meaningless
if (state==0) {
negate = false;
prototype = "";
@@ -2193,15 +2278,26 @@ bool doAssignment(js_function &rootScope, std::string token) {
std::cout << std::string(doAssignmentLevel * 2, ' ') << "doAssignment - function expr start Adjust left[" << left << "] to [" << left.substr(0, cursor) << "]\n";
state = 3;
}
if (trimmedToken == "?") {
std::cout << "Ternany" << std::endl;
// FIXME: our current value should determine what expr we replace our value with
state = 11; // ternary operator
continue;
}
// most likely a function call
// but also can be an expression
// (function() {}())
// token will matter here
// check for ()
if (trimmedToken == "(") {
if (lastToken == "for") {
std::cout << "not an assignment, fix doExpression\n";
}
callFuncName = lastToken;
state = 8;
}
} else if (state == 1) {
// expression part of the assignment (the value part of what we're setting)
if (trimmedToken == "") continue; // skip empties, don't fire the assignment too early
if (it == " ") continue; // skip empties, don't fire the assignment too early
if (trimmedToken == "!") continue; // valid expression
@@ -2236,13 +2332,18 @@ bool doAssignment(js_function &rootScope, std::string token) {
if (storage) {
// state 1 can be an expression that we need to push the result onto the stack for && ||
stack = storage;
// if it's a function value, it's a function naming (var bob = func), not a call
/*
js_function *funcTest = dynamic_cast<js_function*>(storage);
if (funcTest) {
std::cout << "assigning " << trimmedToken << " to some value" << std::endl;
callFuncName = trimmedToken;
callFunc = funcTest;
state = 9;
continue;
}
*/
std::string tLeft = trim(left);
if (tLeft[0]=='"' && tLeft[tLeft.length() - 1]=='"') {
//std::cout << "dequoting[" << tLeft << "]" << std::endl;
@@ -2257,6 +2358,9 @@ bool doAssignment(js_function &rootScope, std::string token) {
alreadySet = true;
*key = storage;
}
} else {
// no dot
std::cout << "Does tLeft[" << tLeft << "] has .?" << std::endl;
}
if (!alreadySet) {
std::cout << "doAssignment final assign[" << tLeft << "] of type[" << typeOfStorage(storage) << "] storageFalse[" << jsIsFalse(storage) <<"] scope[" << &rootScope << "]\n";
@@ -2264,6 +2368,11 @@ bool doAssignment(js_function &rootScope, std::string token) {
jsDisplayScope(&rootScope, 0);
left = ""; // reset left
assignfile << tLeft << "=" << it << "\n";
} else {
std::cout << "doAssignment final re-assigned[" << tLeft << "] of type[" << typeOfStorage(storage) << "] storageFalse[" << jsIsFalse(storage) <<"] scope[" << &rootScope << "]" << std::endl;
rootScope.locals.value[tLeft] = storage;
left = ""; // reset left
assignfile << tLeft << "=" << it << "\n";
}
} else {
std::cout << std::string(doAssignmentLevel * 2, ' ') << "HALT can't get value from [" << it << "]\n";
@@ -2383,8 +2492,24 @@ bool doAssignment(js_function &rootScope, std::string token) {
if (trimmedToken == ")") {
state = 0; // reset state
} else {
// call params
std::cout << std::string(doAssignmentLevel * 2, ' ') << "HALT need to parse these params[" << it << "]\n";
// call params
// we pass the params str to jsFunctionCall
//js_internal_storage *paramValue = doExpression(rootScope, trimmedToken);
// actually call function?
// resolve func by name
std::cout << "calling function [" << callFuncName << "](" << params << ")\n";
js_internal_storage *func = jsLocateKey(&rootScope, callFuncName);
if (!func) {
std::cout << "function 404[" << callFuncName << "]\n";
}
callFunc = dynamic_cast<js_function *>(func);
if (!callFunc) {
std::cout << "found but not a function[" << callFuncName << "]\n";
}
js_internal_storage *storage = jsFunctionCall(callFuncName, callFunc, trimmedToken, rootScope);
std::cout << std::string(doAssignmentLevel * 2, ' ') << "HALT need to parse these params[" << it << "]\n";
}
} else if (state == 9) { // are we a function call with return value, or assigning a function reference
js_internal_storage *storage = nullptr;
@@ -2460,13 +2585,24 @@ bool doAssignment(js_function &rootScope, std::string token) {
// collect params
params = trimmedToken;
}
} else if (state == 11) { // lastToken ? expr1 : expr2
std::cout << "next token: [" << trimmedToken << "]" << std::endl;
state = 12;
} else if (state == 12) { // : expr2
if (trimmedToken == ":") {
state = 13;
} else {
std::cout << "need to handle: [" << trimmedToken << "]" << std::endl;
}
} else if (state == 13) { // expr2
std::cout << "final token: [" << trimmedToken << "]" << std::endl;
}
// { starts JSON capture (should be exactly one block before the } token)
// you create a scope for that variable and recurse
lastToken = it;
}
std::cout << std::string(doAssignmentLevel * 2, ' ') << "expression end, state " << std::to_string(state) << std::endl << std::endl;
//displayScope(&rootScope, 0);
//jsDisplayScope(&rootScope, 0);
/*
auto hasTripleEqual = token.find("===");
auto hasDoubleEqual = std::string::npos;
@@ -2595,6 +2731,58 @@ bool dereferenceHasBase(const std::string input, const js_function *scope) {
return false;
}

// detect stirng constants, rationals and variables
js_internal_storage *resolveStringToKey(const std::string key, js_function &scope) {
char lastChar = key[key.length() - 1];
if (isStrRational(key)) {
js_number *num = new js_number;
num->value = std::stoi(key);
return num;
} else if((key[0] == '"' && lastChar == '"') || (key[0] == '\'' && lastChar == '\'')) {
js_string *str = new js_string;
str->value = key.substr(1, key.length() - 2);
return str;
} else {
// get variable's value
return doExpression(scope, key);
}
return nullptr;
}

/// look into stack for key (stack.key or stack[key]), think `return bob()[0]` where the stack isn't going to be in the string
// key can be a stirng constant or a number
// what about variable? you figure that before hand and deference it and get it's current value and pass that in
js_internal_storage *dereferenceStorage(js_internal_storage *stack, const std::string key, const js_function *scope) {
js_object *jobj = dynamic_cast<js_object*>(stack);
if (jobj) {
std::cout << "deindexObject bracket style [<" << key << ">\n";
if (jobj->value.find(key) != jobj->value.end()) {
std::cout << "dereferenceObject - number[" << key << "] is inside base" << std::endl;
return jobj->value[key]; // we will now point to this storage
}
}
js_array *arr = dynamic_cast<js_array *>(stack);
if (arr) {
std::cout << "deindexArray bracket style [<" << key << ">\n";
int idx = std::stoi(key);
if (arr->value.size() > idx) {
return arr->value[idx];
}
}
js_string *str = dynamic_cast<js_string *>(stack);
if (str) {
std::cout << "deindexString bracket style [<" << key << ">\n";
int idx = std::stoi(key);
if (str->value.size() > idx) {
js_string *nchar = new js_string();
nchar->value = std::string(1, str->value[idx]);
return nchar;
}
}
std::cout << "Couldn't deindex [" << key <<"] on type [" << typeOfStorage(stack) << "]\n";
return nullptr; // or should we return a js_bool false? probably should return js_undefined
}

js_internal_storage *dereferenceObject(const std::string input, const js_function *scope) {
// FIXME: too simple, catches quoted strings with . in them
// FIXME: make sure we're not inside quotes
@@ -2635,7 +2823,36 @@ js_internal_storage *dereferenceObject(const std::string input, const js_functio
char lastChar = part2[part2.length() - 1];
// probably should run this through doExpression...
// FIXME: "asdf" + var + "zxcv"
if ((part2[0] == '"' && lastChar == '"') || (part2[0] == '\'' && lastChar == '\'')) {
if (isStrRational(part2)) {
// part2 is just an index because array[0]
js_object *jobj = dynamic_cast<js_object*>(baseStorage);
if (jobj) {
std::cout << "deindexObject bracket style <" << baseObj << ">[<" << part2 << "><" << input.substr(end) << ">\n";
if (jobj->value.find(part2) != jobj->value.end()) {
std::cout << "dereferenceObject - number[" << part2 << "] is inside base" << std::endl;
return jobj->value[part2]; // we will now point to this storage
}
}
js_array *arr = dynamic_cast<js_array *>(baseStorage);
if (arr) {
std::cout << "deindexArray bracket style <" << baseObj << ">[<" << part2 << "><" << input.substr(end) << ">\n";
int idx = std::stoi(part2);
if (arr->value.size() > idx) {
return arr->value[idx];
}
}
js_string *str = dynamic_cast<js_string *>(baseStorage);
if (str) {
std::cout << "deindexString bracket style <" << baseObj << ">[<" << part2 << "><" << input.substr(end) << ">\n";
int idx = std::stoi(part2);
if (str->value.size() > idx) {
js_string *nchar = new js_string();
nchar->value = std::string(1, str->value[idx]);
return nchar;
}
}
std::cout << "Couldn't deindex [" << part2 <<"] on type [" << typeOfStorage(baseStorage) << "] baseObj[" << baseObj << "] lastChar[" << input.substr(end) << "]\n";
} else if ((part2[0] == '"' && lastChar == '"') || (part2[0] == '\'' && lastChar == '\'')) {
js_object *jobj = dynamic_cast<js_object*>(baseStorage);
if (jobj) {
std::cout << "dereferenceObject bracket style <" << baseObj << ">[<" << part2 << "><" << input.substr(end) << ">\n";
@@ -2725,9 +2942,70 @@ js_internal_storage *jsParseTokens(const std::vector<std::string> &tokens, js_fu
// now execute remaining code
// FIXME: there should be no expressions after an if statement
js_internal_storage *lastRes = doExpression(*scope, lastToken);
// FIXME: what do we with lastRes, do we have a stack?
}
//std::cout << "HALT if not implemented" << std::endl;
//return nullptr;
} else if (ttoken.substr(0, 3)=="for") {
std::cout << "ForToken[" << ttoken << "]\n";
// FIXME: detect for in

size_t pos = findClosing(ttoken, 0, '(', ')');
std::string afterFor = ttoken.substr(4, pos - 4); // -4 because we started at 4 (it's a size, not pos) but won't include ( or )
std::cout << "afterFor[" << afterFor << "]\n";
std::string forScope = ttoken.substr(pos + 2);
std::cout << "forScope[" << forScope << "]\n";
// good idea but getNextExpression goes up to first comma (var x,y) and we need to first ;
/*
size_t end = getNextExpression(afterFor, 0); // works great so far
//end = ifStr.find(')');
std::string forStages = afterFor.substr(0, end);
std::cout << "initialization[" << forStages << "]\n";

size_t end2 = getNextExpression(afterFor, end); // works great so far
std::string forStages2 = afterFor.substr(end, end2);
std::cout << "condition[" << forStages2 << "]\n";

//size_t end3 = getNextExpression(afterFor, end2); // works great so far
std::string forStages3 = afterFor.substr(end2);
std::cout << "iterator[" << forStages3 << "]\n";
*/

// get initializer, condition and iterator
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(afterFor, ";", opens, closes);
if (varList.size() < 3) {
std::cout << "HALT/badjs - not enough params for FOR loop\n";
}
std::string initialization = varList[0];
std::string condition = varList[1];
std::string iterator = varList[2];
std::cout << "initialization[" << initialization << "]\n";
std::cout << "condition[" << condition << "]\n";
std::cout << "iterator[" << iterator << "]\n";
// FIXME: we probably should populate this current scope but nest a new one
doExpression(*scope, initialization); // for initialization ignores the return value of it
js_internal_storage *condRes = doExpression(*scope, condition);
while(!jsIsFalse(condRes)) {
// run content...
// run jsParseTokens forScope
// FIXME: handle break / return
doExpression(*scope, iterator); // for iterator ignores the return value of it
condRes = doExpression(*scope, condition); // check condition again
}

std::cout << "HALT writeme! for loop" << std::endl;
} else if (ttoken.substr(0, 3)=="var") {
std::string listStr = it.substr(3);
// FIXME: , in quotes or {} (JSON) <= top priority for 4chan
@@ -2796,16 +3074,22 @@ js_internal_storage *jsParseTokens(const std::vector<std::string> &tokens, js_fu
// find { (func start)
end = defStr.find('{');
defStr = defStr.substr(end + 1, defStr.size() - 2); // from { to the end
js_function *newFunc = makeFunctionFromString(defStr, prototype, scope);
/*
//std::cout << "jsParseTokens Function Declartion start[" << defStr[0] << "] last[" << defStr[defStr.length() - 1] << "]\n";
auto funcTokens = jsGetTokens(defStr, 0);
//std::cout << "function [" << funcName << "] prototype [" << prototype << "] has [" << funcTokens.size() << "] tokens" << std::endl;
// __netrunner_function_definition is 31 chars
//scope->variables[funcName] = "__netrunner_function_definition = { prototype: \"" + prototype + "\", code: \"" + defStr + "\" }";
js_function *newFunc = new js_function;
newFunc->parameters.push_back(prototype);
newFunc->tokens = funcTokens;
newFunc->parentScope = scope;
*/
scope->locals.value[funcName] = newFunc;
execfile << "function declaration [" << funcName << "](" << prototype << ") tokens[" << funcTokens.size() << "]\n";
execfile << "function declaration [" << funcName << "](" << prototype << ") tokens[" << newFunc->tokens.size() << "]\n";
} else if (ttoken.substr(0, 6)=="return") {
std::string afterReturn = ttoken.substr(7);
return doExpression(*scope, afterReturn);

Loading…
Cancel
Save