1
0
Fork 0
mirror of https://github.com/suchmememanyskill/TegraExplorer.git synced 2024-12-29 12:56:02 +00:00
TegraExplorer/source/script/parser.c
2021-07-29 09:42:20 +02:00

670 lines
No EOL
18 KiB
C

#include "model.h"
#include "compat.h"
#include "compat.h"
#include "parser.h"
#include <stdlib.h>
#include <string.h>
#include "intClass.h"
#include "StringClass.h"
#include "unsolvedArrayClass.h"
#include "functionClass.h"
#include "scriptError.h"
#include "standardLibrary.h"
#ifndef WIN32
#include "../tegraexplorer/tconf.h"
#include <storage/nx_sd.h>
#endif
static inline int isValidWord(char c) {
char r = c | 0x20;
return ((r >= 'a' && r <= 'z') || c == '_');
}
static inline int isValidNum(char c) {
return (c >= '0' && c <= '9');
}
static inline int isValidVar(char c) {
return (isValidWord(c) || isValidNum(c));
}
static inline int isValidHexNum(char c) {
char r = c | 0x20;
return (isValidNum(r) || (r >= 'a' && r <= 'f'));
}
char* getTokenText(u8 token) {
for (u32 i = 0; i < tokenConvertionCount; i++) {
if (tokenConvertions[i].token == token)
return tokenConvertions[i].strToken;
}
return NULL;
}
char* utils_copyStringSize(const char* in, int size) {
if (size > strlen(in) || size < 0)
size = strlen(in);
char* out = calloc(size + 1, 1);
//strncpy(out, in, size);
if (size)
memcpy(out, in, size);
return out;
}
#define ELIFC(c) else if (*in == c)
Vector_t script;
s64 lineNumber;
enum TokenType {
Token_Variable = 0,
Token_String,
Token_Int,
Token_Token,
Token_Err,
Token_Fatal_Err,
};
typedef enum {
History_Function = 0,
History_Bracket,
History_Array,
} StackHistory_t;
char* end;
u8 nextToken(char** inPtr, void** val) {
char* in = *inPtr;
u8 ret = Token_Err;
while (ret == Token_Err) {
if (*in == '#') {
if (!memcmp(in + 1, "REQUIRE ", 8)) {
if (!memcmp(in + 9, "VER ", 4)) {
char* verStart = in + 13;
char* verEnd = verStart;
while (isValidNum(*verEnd) || *verEnd == '.')
verEnd++;
u8 outdated = (verEnd - verStart != strlen(LP_VER));
if (!outdated){
outdated = (memcmp(LP_VER, verStart, verEnd - verStart) < 0);
}
if (outdated) {
printScriptError(SCRIPT_LEXER_FATAL, "Script requires a newer TegraExplorer version!");
return Token_Fatal_Err;
}
}
else if (!memcmp(in + 9, "MINERVA", 7)) {
#ifdef WIN32
u8 minervaEnabled = 0;
#else
u8 minervaEnabled = TConf.minervaEnabled;
#endif
if (!minervaEnabled) {
printScriptError(SCRIPT_LEXER_FATAL, "Extended memory required.\nPut the bootloader folder from hekate on your sd!");
return Token_Fatal_Err;
}
}
else if (!memcmp(in + 9, "KEYS", 4)) {
#ifdef WIN32
u8 gotKeys = 0;
#else
u8 gotKeys = TConf.keysDumped;
#endif
if (!gotKeys){
printScriptError(SCRIPT_LEXER_FATAL, "Keys required.\nMake sure you're on the latest version of TegraExplorer!");
return Token_Fatal_Err;
}
}
else if (!memcmp(in + 9, "SD", 2)) {
#ifdef WIN32
u8 gotSd = 0;
#else
u8 gotSd = sd_mount();
#endif
if (!gotSd){
printScriptError(SCRIPT_LEXER_FATAL, "Sd required.");
return Token_Fatal_Err;
}
}
}
while (*in && *in != '\n')
in++;
lineNumber++;
scriptCurrentLine = lineNumber;
}
else if (isValidWord(*in)) {
char* startWord = in;
in++;
while (isValidVar(*in))
in++;
char* str = utils_copyStringSize(startWord, in - startWord);
//gfx_printf("Variable: '%s'\n", str);
ret = Token_Variable;
*val = str;
break;
}
else if (isValidNum(*in) || (*in == '-' && isValidNum(in[1]))) {
s64 parse = 0;
u8 negative = (*in == '-');
if (negative)
in++;
if (*in == '0' && (in[1] | 0x20) == 'x') {
in += 2;
while (isValidHexNum(*in)) {
parse = parse * 16 + (*in & 0x0F) + (*in >= 'A' ? 9 : 0);
in++;
}
}
else while (isValidNum(*in)) {
parse = parse * 10 + *in++ - '0';
}
if (negative)
parse *= -1;
//gfx_printf("Integer: '%d'\n", parse);
ret = Token_Int;
s64* parsePersistent = malloc(sizeof(s64));
*parsePersistent = parse;
*val = parsePersistent;
break;
}
ELIFC('"') {
char* startStr = ++in;
int len = 0;
while (*in != '"') {
in++;
}
len = in - startStr;
char* storage = malloc(len + 1);
int pos = 0;
for (int i = 0; i < len; i++) {
if (startStr[i] == '\\') {
if (startStr[i + 1] == 'n') {
storage[pos++] = '\n';
i++;
continue;
}
if (startStr[i + 1] == 'r') {
storage[pos++] = '\r';
i++;
continue;
}
}
storage[pos++] = startStr[i];
}
storage[pos] = '\0';
//gfx_printf("String: '%s'\n", storage);
ret = Token_String;
*val = storage;
}
else if (*in == '\0' || in > end) {
*inPtr = in;
return ret;
}
else if (*in == '\n'){
lineNumber++;
scriptCurrentLine = lineNumber;
}
else {
for (u32 i = 0; i < tokenConvertionCount; i++) {
TokenConvertion_t t = tokenConvertions[i];
if (!memcmp(t.strToken, in, (t.strToken[1] == '\0') ? 1 : 2)) {
//gfx_printf("Token: '%s'\n", t.strToken);
ret = Token_Token;
*val = t.token;
if (t.strToken[1] != '\0')
in++;
break;
}
}
}
in++;
}
*inPtr = in;
return ret;
}
#define CreateVariableReferenceStatic(var) VariableReference_t reference = { .staticVariable = var, .staticVariableSet = 1, .staticVariableRef = 1 }
#define CreateVariableReferenceStr(str) VariableReference_t reference = { .name = str }
void setNextActionOperator(Vector_t *opHolder, ActionType_t action, ActionExtraType_t actionExtra, void *extra) {
Operator_t* ops = opHolder->data;
Operator_t* lastOp = &ops[opHolder->count - 1];
if (lastOp->token == CallArgs) {
CallArgs_t* last = &lastOp->callArgs;
for (; last->next != NULL; last = last->next);
last->next = calloc(sizeof(CallArgs_t), 1);
last->next->action = action;
last->next->extra = extra;
last->next->extraAction = actionExtra;
}
else {
Operator_t newOp = { .token = CallArgs };
newOp.callArgs.action = action;
newOp.callArgs.extra = extra;
newOp.callArgs.extraAction = actionExtra;
vecAdd(opHolder, newOp);
}
}
CallArgs_t* getLastRef(CallArgs_t* ref) {
for (; ref->next != NULL; ref = ref->next);
return ref;
}
int isLastVarSet(Operator_t* opHolder) {
return (opHolder->token == CallArgs && getLastRef(&opHolder->callArgs)->action == ActionSet);
}
int isLastVarCall(Operator_t* opHolder) {
return (opHolder->token == CallArgs && getLastRef(&opHolder->callArgs)->action == ActionCall);
}
ParserRet_t parseScript(char* in, u32 len) {
Vector_t functionStack; // Function_t
Vector_t stackHistoryHolder; // StaticHistory_t
Vector_t staticVariableHolder; // Variable_t
functionStack = newVec(sizeof(Function_t), 0);
Function_t firstFunction = createEmptyFunction();
vecAdd(&functionStack, firstFunction);
staticVariableHolder = newVec(sizeof(Variable_t), 0);
stackHistoryHolder = newVec(sizeof(StackHistory_t), 1);
StackHistory_t firstHistory = History_Function;
vecAdd(&stackHistoryHolder, firstHistory);
u8 notNext = 0;
lineNumber = 1;
scriptCurrentLine = 1;
end = in + len;
while (*in && in <= end) {
Function_t* lastFunc = getStackEntry(&functionStack);
StackHistory_t* lastHistory = getStackEntry(&stackHistoryHolder);
Operator_t* lastOp = NULL;
if (lastFunc) {
lastOp = getStackEntry(&lastFunc->operations);
}
void* var = NULL;
u8 tokenType = nextToken(&in, &var);
if (tokenType == Token_Err)
break;
if (tokenType == Token_Fatal_Err)
return (ParserRet_t) { 0 };
Operator_t op = { .token = Variable };
if (tokenType >= Token_Variable && tokenType <= Token_Int && lastOp) {
if (lastOp->token == Variable || lastOp->token == BetweenBrackets || (lastOp->token == CallArgs && !isLastVarSet(lastOp))) {
op.token = EquationSeperator;
op.lineNumber = lineNumber;
vecAdd(&lastFunc->operations, op);
op.token = Variable;
}
}
if (tokenType == Token_Variable) {
u8 stdLen = 0;
ClassFunctionTableEntry_t* cfte = searchStdLib(var, &stdLen);
if (cfte == NULL) {
CreateVariableReferenceStr(var);
op.variable = reference;
}
else {
VariableReference_t reference = { .staticVariableType = 3, .staticFunction = cfte, .staticFunctionLen = stdLen };
op.variable = reference;
}
}
else if (tokenType == Token_Int) {
/*
Variable_t a = newIntVariable(*((s64*)var));
a.gcDoNotFree = 1;
free(var);
vecAdd(&staticVariableHolder, a);
CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1));
*/
VariableReference_t reference = { .staticVariableType = 1, .integerType = *((s64*)var) };
op.variable = reference;
free(var);
}
else if (tokenType == Token_String) {
/*
Variable_t a = newStringVariable(var, 1, 1);
a.gcDoNotFree = 1;
vecAdd(&staticVariableHolder, a);
CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1));
*/
VariableReference_t reference = { .staticVariableType = 2, .stringType = var };
op.variable = reference;
}
else if (tokenType == Token_Token) {
u8 token = (u8)var;
if (token == Equals && lastOp) {
if (lastOp->token == Variable) {
if (lastOp->variable.staticVariableSet) {
SCRIPT_PARSER_ERR("Trying to assign to a static variable");
}
else {
setNextActionOperator(&lastFunc->operations, ActionSet, ActionExtraNone, NULL);
continue;
}
}
else if (lastOp->token == CallArgs) {
CallArgs_t* last = getLastRef(&lastOp->callArgs);
last->action = ActionSet;
continue;
}
else {
SCRIPT_PARSER_ERR("Trying to assign to non-object");
}
}
else if (token == LeftCurlyBracket) {
Function_t templateFunction = createEmptyFunction();
vecAdd(&functionStack, templateFunction);
StackHistory_t functionHistory = History_Function;
vecAdd(&stackHistoryHolder, functionHistory);
continue;
}
else if (token == RightCurlyBracket) {
if (stackHistoryHolder.count != 1 && *lastHistory == History_Function) {
Function_t *popFunc = popStackEntry(&functionStack);
popStackEntry(&stackHistoryHolder);
lastFunc = getStackEntry(&functionStack);
if (lastFunc) { // TODO: Add check for null deref
lastOp = getStackEntry(&lastFunc->operations);
}
if (lastOp && (lastOp->token == Variable || (lastOp->token == CallArgs && !isLastVarSet(lastOp)))) {
if (lastOp->token == Variable) {
SCRIPT_PARSER_ERR("GET variable before {}");
continue;
}
// Set last arg to {}
CallArgs_t* lastCall = getLastRef(&lastOp->callArgs);
if (lastCall->extraAction == ActionExtraCallArgs) {
Function_t* funcArgs = lastCall->extra;
if (funcArgs->operations.count != 0) {
op.token = EquationSeperator;
op.lineNumber = lineNumber;
vecAdd(&funcArgs->operations, op);
op.token = Variable;
}
Variable_t a = newFunctionVariable(createFunctionClass(*popFunc, NULL));
vecAdd(&staticVariableHolder, a);
CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1));
op.variable = reference;
vecAdd(&funcArgs->operations, op);
continue;
}
}
Variable_t a = newFunctionVariable(createFunctionClass(*popFunc, NULL));
vecAdd(&staticVariableHolder, a);
CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1));
op.variable = reference;
vecAdd(&lastFunc->operations, op);
op.token = EquationSeperator;
op.lineNumber = lineNumber;
}
else {
SCRIPT_PARSER_ERR("Stack count is 1 or state is not a function");
}
}
else if (token == Dot) {
if (lastOp && (lastOp->token == Variable || lastOp->token == BetweenBrackets || (lastOp->token == CallArgs && !isLastVarSet(lastOp)))) {
tokenType = nextToken(&in, &var);
if (tokenType != Token_Variable) {
SCRIPT_PARSER_ERR("Acessing member with non-dynamic token");
}
else {
setNextActionOperator(&lastFunc->operations, ActionGet, ActionExtraMemberName, var);
continue;
}
}
else {
SCRIPT_PARSER_ERR("Member access on non-variable");
}
}
else if (token == LeftBracket) {
Function_t templateFunction = createEmptyFunction();
vecAdd(&functionStack, templateFunction);
StackHistory_t functionHistory = History_Bracket;
vecAdd(&stackHistoryHolder, functionHistory);
continue;
}
else if (token == RightBracket) {
if (*lastHistory == History_Bracket) {
Function_t* bstack = popStackEntry(&functionStack);
popStackEntry(&stackHistoryHolder);
lastFunc = getStackEntry(&functionStack);
if (lastFunc) { // TODO: Add check for null deref
lastOp = getStackEntry(&lastFunc->operations);
}
if (lastOp && (lastOp->token == Variable || lastOp->token == BetweenBrackets || (lastOp->token == CallArgs && !isLastVarSet(lastOp) && !isLastVarCall(lastOp)))) {
Function_t* newBStack = malloc(sizeof(Function_t));
*newBStack = *bstack;
setNextActionOperator(&lastFunc->operations, ActionCall, ActionExtraCallArgs, newBStack); // Maybe pass NULL if array is empty?
continue;
}
else {
if (lastOp && isLastVarCall(lastOp)) {
op.token = EquationSeperator;
vecAdd(&lastFunc->operations, op);
}
if (!countTokens(bstack, EquationSeperator)) {
op.variable.betweenBrackets.data = bstack->operations.data;
op.variable.betweenBrackets.len = bstack->operations.count;
op.token = BetweenBrackets;
}
else {
SCRIPT_PARSER_ERR("Priority brackets can only contain 1 argument");
}
}
}
else {
SCRIPT_PARSER_ERR(") without (");
}
}
else if (token == LeftSquareBracket) {
Function_t templateFunction = createEmptyFunction();
vecAdd(&functionStack, templateFunction);
StackHistory_t functionHistory = History_Array;
vecAdd(&stackHistoryHolder, functionHistory);
continue;
}
else if (token == RightSquareBracket) {
if (*lastHistory == History_Array) {
Function_t* astack = popStackEntry(&functionStack);
popStackEntry(&stackHistoryHolder);
lastFunc = getStackEntry(&functionStack);
if (lastFunc) { // TODO: Add check for null deref
lastOp = getStackEntry(&lastFunc->operations);
}
if (lastOp && (lastOp->token == Variable || (lastOp->token == CallArgs && !isLastVarSet(lastOp)))) {
if (!countTokens(astack, EquationSeperator)) {
Function_t* newAStack = malloc(sizeof(Function_t));
*newAStack = *astack;
setNextActionOperator(&lastFunc->operations, ActionGet, ActionExtraArrayIndex, newAStack);
continue;
}
else {
// We're just assuming that it's a new array lol
op.token = EquationSeperator;
vecAdd(&lastFunc->operations, op);
op.token = Variable;
Variable_t a = createUnsolvedArrayVariable(astack);
vecAdd(&staticVariableHolder, a);
CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1));
op.variable = reference;
//gfx_printf("[FATAL] indexes cannot contain mutiple arguments");
}
}
else {
// TODO: optimize output to a typed array, if possible
Variable_t a = createUnsolvedArrayVariable(astack);
vecAdd(&staticVariableHolder, a);
CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1));
op.variable = reference;
/*
vecAdd(&lastFunc->operations, op);
op.token = EquationSeperator;
op.lineNumber = lineNumber;
*/
}
}
else {
SCRIPT_PARSER_ERR("] without [");
}
}
else if (token == Not) {
notNext = !notNext;
continue;
}
else {
op.token = token;
for (u32 i = 0; i < tokenConvertionCount; i++) {
if (token == tokenConvertions[i].token) {
op.tokenStr = tokenConvertions[i].strToken;
break;
}
}
}
}
if (notNext) {
op.not = 1;
notNext = 0;
}
vecAdd(&lastFunc->operations, op);
}
if (functionStack.count != 1 || stackHistoryHolder.count != 1) {
SCRIPT_PARSER_ERR("There seems to be an open bracket somewhere. EOF reached");
}
ParserRet_t parse = { .main = (*(Function_t*)getStackEntry(&functionStack)), .staticVarHolder = staticVariableHolder, .valid = 1 };
vecFree(functionStack);
vecFree(stackHistoryHolder);
scriptCurrentLine = 1;
return parse;
}
void exitFunction(Operator_t* start, u32 len) {
for (u32 i = 0; i < len; i++) {
if (start[i].token == Variable) {
if (start[i].variable.staticVariableOptionsUnion == 0) {
FREE(start[i].variable.name);
}
if (start[i].variable.staticVariableType == 2) {
FREE(start[i].variable.stringType);
}
}
else if (start[i].token == BetweenBrackets) {
exitFunction(start[i].variable.betweenBrackets.data, start[i].variable.betweenBrackets.len);
FREE(start[i].variable.betweenBrackets.data);
}
else if (start[i].token == CallArgs) {
CallArgs_t* call = &start[i].callArgs;
while (call != NULL) {
if (call->extraAction == ActionExtraArrayIndex) {
Function_t* f = call->extra;
exitFunction(f->operations.data, f->operations.count);
vecFree(f->operations);
FREE(f);
}
else if (call->extraAction == ActionExtraMemberName) {
FREE(call->extra);
}
else if (call->extraAction == ActionExtraCallArgs) {
Function_t* f = call->extra;
exitFunction(f->operations.data, f->operations.count);
vecFree(f->operations);
FREE(f);
}
CallArgs_t* nextCall = call->next;
if (call != &start[i].callArgs) {
FREE(call);
}
call = nextCall;
}
}
}
}
void exitStaticVars(Vector_t* v) {
vecForEach(Variable_t*, staticVar, v) {
if (staticVar->variableType == FunctionClass) {
if (!staticVar->function.builtIn) {
exitFunction(staticVar->function.function.operations.data, staticVar->function.function.operations.count);
vecFree(staticVar->function.function.operations);
}
}
else {
freeVariableInternal(staticVar);
}
}
}