From b0dd71bd4fde6decdf290360ff8a7fbb1347d4ca Mon Sep 17 00:00:00 2001 From: suchmememanyskill Date: Mon, 28 Dec 2020 14:51:59 +0100 Subject: [PATCH] implement TScript v2.5 --- source/fs/menus/filemenu.c | 27 ++- source/gfx/gfx.c | 2 + source/script/args.c | 404 +++++++++++++++++++++++++++++++++++++ source/script/args.h | 7 + source/script/functions.c | 134 ++++++++++++ source/script/functions.h | 4 + source/script/lexer.c | 269 ++++++++++++++++++++++++ source/script/lexer.h | 6 + source/script/parser.c | 109 ++++++++++ source/script/parser.h | 6 + source/script/types.h | 140 +++++++++++++ source/script/variables.c | 105 ++++++++++ source/script/variables.h | 16 ++ source/utils/vector.c | 7 + source/utils/vector.h | 11 +- 15 files changed, 1245 insertions(+), 2 deletions(-) create mode 100644 source/script/args.c create mode 100644 source/script/args.h create mode 100644 source/script/functions.c create mode 100644 source/script/functions.h create mode 100644 source/script/lexer.c create mode 100644 source/script/lexer.h create mode 100644 source/script/parser.c create mode 100644 source/script/parser.h create mode 100644 source/script/types.h create mode 100644 source/script/variables.c create mode 100644 source/script/variables.h diff --git a/source/fs/menus/filemenu.c b/source/fs/menus/filemenu.c index cf8a6c2..86b0d20 100644 --- a/source/fs/menus/filemenu.c +++ b/source/fs/menus/filemenu.c @@ -11,6 +11,10 @@ #include #include "../../utils/utils.h" #include "../../keys/nca.h" +#include "../../script/lexer.h" +#include "../../script/parser.h" +#include "../../script/variables.h" +#include MenuEntry_t FileMenuEntries[] = { // Still have to think up the options @@ -66,6 +70,27 @@ void DeleteFile(char *path, FSEntry_t entry){ free(thing); } +void RunScript(char *path, FSEntry_t entry){ + char *thing = CombinePaths(path, entry.name); + u32 size; + char *script = sd_file_read(thing, &size); + free(thing); + if (!script) + return; + + gfx_clearscreen(); + scriptCtx_t ctx = createScriptCtx(); + ctx.script = runLexar(script, size); + free(script); + + printError(mainLoop(&ctx)); + + freeVariableVector(&ctx.varDict); + lexarVectorClear(&ctx.script); + gfx_printf("\nend of script"); + hidWait(); +} + menuPaths FileMenuPaths[] = { CopyClipboard, MoveClipboard, @@ -73,7 +98,7 @@ menuPaths FileMenuPaths[] = { DeleteFile, UnimplementedException, LaunchPayload, - UnimplementedException + RunScript }; void FileMenu(char *path, FSEntry_t entry){ diff --git a/source/gfx/gfx.c b/source/gfx/gfx.c index 06d5e08..60c167e 100644 --- a/source/gfx/gfx.c +++ b/source/gfx/gfx.c @@ -244,6 +244,8 @@ void gfx_putc(char c) if (gfx_con.y < 16){ gfx_con.y = YLeftConfig; gfx_con.x += 16; + if (gfx_con.x > 719) + gfx_con.x = 0; } } else if (c == '\n') diff --git a/source/script/args.c b/source/script/args.c new file mode 100644 index 0000000..d9174a4 --- /dev/null +++ b/source/script/args.c @@ -0,0 +1,404 @@ +#include "args.h" +#include "types.h" +#include "functions.h" +#include "variables.h" +#include +#include +#include "../utils/utils.h" + +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; +} + +// do not include first openToken! +int distanceBetweenTokens(lexarToken_t* tokens, u32 len, int openToken, int closeToken) { + int i = 0; + int layer = 0; + for (; i < len; i++) { + if (tokens[i].token == openToken) + layer++; + else if (tokens[i].token == closeToken) { + if (layer == 0) + return i; + layer--; + } + } + + return -1; +} + +Vector_t extractVars(scriptCtx_t* ctx, lexarToken_t* tokens, u32 len) { + Vector_t args = newVec(sizeof(Variable_t), 5); + int lastLoc = 0; + for (int i = 0; i < len; i++) { + if (tokens[i].token == LSBracket) { + int distance = distanceBetweenTokens(&tokens[i + 1], len - i - 1, LSBracket, RSBracket); + i += distance + 1; + } + if (tokens[i].token == Seperator) { + Variable_t res = solveEquation(ctx, &tokens[lastLoc], i - lastLoc, 0); + lastLoc = i + 1; + + vecAddElement(&args, res); + } + + if (i + 1 >= len) { + Variable_t res = solveEquation(ctx, &tokens[lastLoc], i + 1 - lastLoc, 0); + vecAddElement(&args, res); + } + } + + return args; +} + +#define ErrValue(err) (Variable_t) {.varType = ErrType, .integerType = err} +#define IntValue(i) (Variable_t) {.varType = IntType, .integerType = i} +#define StrValue(s) (Variable_t) {.varType = StringType, .stringType = s} + +#define ELIFTX(x) else if (tokens[i].token == x) + +Variable_t getVarFromToken(scriptCtx_t* ctx, lexarToken_t* tokens, int* index, u32 maxLen) { + Variable_t val = { 0 }; + int i = *index; + + if (tokens[i].token == Variable) { + Variable_t* var = dictVectorFind(&ctx->varDict, tokens[i].text); + if (var != NULL) { + val = *var; + val.free = 0; + } + else { + val = ErrValue(ERRNOVAR); + } + } + ELIFTX(IntLit) { + val = IntValue(tokens[i].val); + } + ELIFTX(StrLit) { + val = StrValue(tokens[i].text); + val.free = 0; + } + ELIFTX(ArrayVariable) { + Variable_t* var = dictVectorFind(&ctx->varDict, tokens[i].text); + i += 2; + + if (var == NULL) + return ErrValue(ERRNOVAR); + + int argCount = distanceBetweenTokens(&tokens[i], maxLen - 2, LSBracket, RSBracket); + if (argCount < 0) + return ErrValue(ERRSYNTAX); + + Variable_t index = solveEquation(ctx, &tokens[i], argCount, 0); + i += argCount; + + if (index.varType != IntType) + return ErrValue(ERRINVALIDTYPE); + + if (var->vectorType.count <= index.integerType || index.integerType < 0) + return ErrValue(ERRSYNTAX); + + switch (var->varType) { + case StringArrayType: + val = StrValue((vecGetArray(char**, var->vectorType))[index.integerType]); + break; + case IntArrayType: + val = IntValue((vecGetArray(int*, var->vectorType))[index.integerType]); + break; + case ByteArrayType: + val = IntValue((vecGetArray(u8*, var->vectorType))[index.integerType]); + break; + default: + return ErrValue(ERRINVALIDTYPE); + } + } + ELIFTX(Function) { + i += 2; + + int argCount = distanceBetweenTokens(&tokens[i], maxLen - 2, LBracket, RBracket); + if (argCount < 0) + return ErrValue(ERRSYNTAX); + + val = executeFunction(ctx, tokens[i - 2].text, &tokens[i], argCount); + + //val = IntValue(1); + i += argCount; + } + ELIFTX(LSBracket) { + i++; + + int argCount = distanceBetweenTokens(&tokens[i], maxLen - 1, LSBracket, RSBracket); + if (argCount <= 0) + return ErrValue(ERRSYNTAX); + + // ArrayVars should be a Vector_t containing Variable_t's. Not implemented yet! + Vector_t arrayVars = extractVars(ctx, &tokens[i], argCount); + Variable_t* variables = vecGetArray(Variable_t*, arrayVars); + int type = variables[0].varType; + if (!(type == StringType || type == IntType)) + return ErrValue(ERRINVALIDTYPE); + + val.varType = (type + 2); + val.free = 1; + val.vectorType = newVec((type == IntType) ? sizeof(int) : sizeof(char*), arrayVars.count); + + for (int i = 0; i < arrayVars.count; i++) { + if (variables[i].varType != type) + return ErrValue(ERRINVALIDTYPE); // Free-ing issue!! + + if (type == StringType) { + char* temp = CpyStr(variables[i].stringType); + vecAddElement(&val.vectorType, temp); + } + else { + vecAddElement(&val.vectorType, variables[i].integerType); + } + } + + i += argCount; + freeVariableVector(&arrayVars); + } + ELIFTX(LBracket) { + i++; + int argCount = distanceBetweenTokens(&tokens[i], maxLen - 1, LBracket, RBracket); + if (argCount < 0) + return ErrValue(ERRSYNTAX); + + val = solveEquation(ctx, &tokens[i], argCount, 0); + i += argCount; + } + else { + // ERR + return ErrValue(ERRSYNTAX); + } + + *index = i; + return val; +} + +int matchTypes(Variable_t d1, Variable_t d2, int type) { + return (d1.varType == d2.varType && d1.varType == type); +} + +#define ELIFT(token) else if (localOpToken == token) + +Variable_t solveEquation(scriptCtx_t* ctx, lexarToken_t* tokens, u32 len, u8 shouldFree) { + Variable_t res = { 0 }; + u8 lastToken = 0; + u8 invertValue = 0; + lexarToken_t* varToken = NULL; + + for (int i = 0; i < len; i++) { + if (tokens[i].token == Not) + invertValue = !invertValue; + + else if (tokens[i].token == ArrayVariableAssignment || tokens[i].token == VariableAssignment) { + varToken = &tokens[i]; + if (tokens[i].token == ArrayVariableAssignment) { + int distance = distanceBetweenTokens(&tokens[i] + 2, len, LSBracket, RSBracket); + i += distance + 2; + } + } + + else if (tokens[i].token >= Variable && tokens[i].token <= LSBracket) { + Variable_t val = getVarFromToken(ctx, tokens, &i, len - i); + + if (val.varType == ErrType) + return val; + + if (val.varType == IntType && invertValue) { + val.integerType = !val.integerType; + } + invertValue = 0; + + if (lastToken) { + u16 localOpToken = lastToken; // do we need local op token? + lastToken = 0; + + if (matchTypes(res, val, IntType)) { + if (localOpToken == Plus) + res.integerType += val.integerType; + ELIFT(Minus) + res.integerType -= val.integerType; + ELIFT(Multiply) + res.integerType *= val.integerType; + ELIFT(Division) { + if (!val.integerType) + res = ErrValue(ERRDIVBYZERO); + else + res.integerType = res.integerType / val.integerType; + } + ELIFT(Mod) + res.integerType %= val.integerType; + ELIFT(Smaller) + res.integerType = res.integerType < val.integerType; + ELIFT(SmallerEqual) + res.integerType = res.integerType <= val.integerType; + ELIFT(Bigger) + res.integerType = res.integerType > val.integerType; + ELIFT(BiggerEqual) + res.integerType = res.integerType >= val.integerType; + ELIFT(EqualEqual) + res.integerType = res.integerType == val.integerType; + ELIFT(NotEqual) + res.integerType = res.integerType != val.integerType; + ELIFT(LogicAND) { + res.integerType = res.integerType && val.integerType; + if (!res.integerType) + break; + } + ELIFT(LogicOR) { + res.integerType = res.integerType || val.integerType; + if (res.integerType) + break; + } + ELIFT(AND) + res.integerType = res.integerType & val.integerType; + ELIFT(OR) + res.integerType = res.integerType | val.integerType; + else + return ErrValue(ERRBADOPERATOR); + } + else if (matchTypes(res, val, StringType)) { + if (localOpToken == Plus) { + char* buff = calloc(strlen(res.stringType) + strlen(val.stringType) + 1, 1); + strcpy(buff, res.stringType); + strcat(buff, val.stringType); + + if (res.free) free(res.stringType); // we should replace these with variablefree + if (val.free) free(val.stringType); + res.stringType = buff; + res.free = 1; + } + ELIFT(EqualEqual) { + res.typeUnion = IntType; + int compRes = !strcmp(res.stringType, val.stringType); + if (res.free) free(res.stringType); + if (val.free) free(val.stringType); + res.integerType = compRes; + res.free = 0; + } + ELIFT(Minus) { + if (!strcmp(res.stringType + strlen(res.stringType) - strlen(val.stringType), val.stringType)) { + *(res.stringType + strlen(res.stringType) - strlen(val.stringType)) = 0; + } + + if (val.free) free(val.stringType); + } + ELIFT(Division) { + int valLen = strlen(val.stringType); + if (!valLen) { + res = ErrValue(ERRSYNTAX); + continue; + } + + char* start = res.stringType; + char* find = NULL; + //char** arr = malloc(20); // should be dynamic + Vector_t arr = newVec(sizeof(char**), 10); + char* temp; + + while ((find = (strstr(start, val.stringType))) != NULL) { + temp = utils_copyStringSize(start, find - start); + vecAddElement(&arr, temp); + + start = find + valLen; + } + + temp = utils_copyStringSize(start, res.stringType + strlen(res.stringType) - start); + vecAddElement(&arr, temp); + if (res.free) free(res.stringType); // do we free here? + if (val.free) free(val.stringType); + + res.varType = StringArrayType; + res.free = 1; + res.vectorType = arr; + } + else + return ErrValue(ERRBADOPERATOR); + } + else if ((res.varType == IntArrayType || res.varType == ByteArrayType) && val.varType == IntType) { + if (localOpToken == Plus) { + Vector_t newV = vecCopy(&res.vectorType); + freeVariable(res); + res.vectorType = newV; + res.free = 1; + if (res.varType == IntArrayType) + vecAddElement(&res.vectorType, val.integerType); + else { + u8 in = ((u8)val.integerType & 0xFF); + vecAddElement(&res.vectorType, in); + } + } + } + else + return ErrValue(ERRBADOPERATOR); + } + else { + res = val; + } + } + else if (tokens[i].token >= Plus && tokens[i].token <= OR) { + lastToken = tokens[i].token; + } + } + + if (varToken != NULL) { + if (varToken->token == VariableAssignment) { + dict_t newVar = newDict(CpyStr(varToken->text), res); + dictVectorAdd(&ctx->varDict, newVar); + } + else { + Variable_t* var = dictVectorFind(&ctx->varDict, varToken->text); + if (var != NULL) { + if (var->varType - 2 == res.varType || (var->varType == ByteArrayType && res.varType == IntType)) { + int distance = distanceBetweenTokens(varToken + 2, len, LSBracket, RSBracket); + if (distance < 0) { + // ERR + return ErrValue(ERRSYNTAX); + } + Variable_t index = solveEquation(ctx, varToken + 2, distance, 0); + if (index.varType != IntType) { + // ERR + } + else if (index.integerType < 0 || var->vectorType.count <= index.integerType) { + // ERR + } + else { + if (var->varType == IntArrayType) { + int* arr = vecGetArray(int*, var->vectorType); + arr[index.integerType] = res.integerType; + } + else if (var->varType == StringArrayType) { + char** arr = vecGetArray(char**, var->vectorType); + arr[index.integerType] = CpyStr(res.stringType); + } + else if (var->varType == ByteArrayType) { + u8* arr = vecGetArray(u8*, var->vectorType); + arr[index.integerType] = res.integerType & 0xFF; + } + } + } + else { + // ERR + } + } + else { + //ERR + } + } + } + else { + if (shouldFree) // we should get rid of this ugly free. why not set .free to 0 when assigning it then free in parser.c? + freeVariable(res); + } + + return res; +} \ No newline at end of file diff --git a/source/script/args.h b/source/script/args.h new file mode 100644 index 0000000..d900961 --- /dev/null +++ b/source/script/args.h @@ -0,0 +1,7 @@ +#pragma once +#include "types.h" + +char* utils_copyStringSize(const char* in, int size); +Variable_t solveEquation(scriptCtx_t* ctx, lexarToken_t* tokens, u32 len, u8 shouldFree); +int distanceBetweenTokens(lexarToken_t* tokens, u32 len, int openToken, int closeToken); +Vector_t extractVars(scriptCtx_t* ctx, lexarToken_t* tokens, u32 len); \ No newline at end of file diff --git a/source/script/functions.c b/source/script/functions.c new file mode 100644 index 0000000..8eacfe6 --- /dev/null +++ b/source/script/functions.c @@ -0,0 +1,134 @@ +#include "types.h" +#include "args.h" +#include "variables.h" +#include +#include "../gfx/gfx.h" +#include +#include "lexer.h" + +#define scriptFunction(name) Variable_t name(scriptCtx_t *ctx, Variable_t *vars, u32 varLen) + +scriptFunction(funcIf) { + setCurIndentInstruction(ctx, (vars[0].integerType == 0), 0, -1); + return NullVar; +} + +scriptFunction(funcPrint) { + for (u32 i = 0; i < varLen; i++) { + if (vars[i].varType == IntType) + gfx_printf("%d", vars[i].integerType); + else if (vars[i].varType == StringType) + gfx_printf("%s", vars[i].stringType); + else if (vars[i].varType == IntArrayType) { + gfx_printf("["); + int* v = vecGetArray(int*, vars[i].vectorType); + for (u32 j = 0; j < vars[i].vectorType.count; j++) + gfx_printf((j + 1 == vars[i].vectorType.count) ? "%d" : "%d, ", v[j]); + gfx_printf("]"); + } + } + + return NullVar; +} + +scriptFunction(funcPrintln) { + funcPrint(ctx, vars, varLen); + gfx_printf("\n"); + return NullVar; +} + +scriptFunction(funcWhile) { + setCurIndentInstruction(ctx, (vars[0].integerType == 0), 0, ctx->startEquation); + + return NullVar; +} + +scriptFunction(funcElse) { + indentInstructor_t* curInstruction = getCurIndentInstruction(ctx); + setCurIndentInstruction(ctx, !curInstruction->skip, 0, -1); + return NullVar; +} + +scriptFunction(funcLen) { + if (vars[0].varType >= IntArrayType && vars[0].varType <= ByteArrayType) + return IntVal(vars[0].vectorType.count); + else if (vars[0].varType == StringType) { + if (vars[0].stringType != NULL) + return IntVal(strlen(vars[0].stringType)); + } + + return ErrVar(ERRINVALIDTYPE); +} + +scriptFunction(funcMakeByteArray){ + u8 *buff = malloc(vars[0].vectorType.count); + vecDefArray(int*, entries, vars[0].vectorType); + for (int i = 0; i < vars[0].vectorType.count; i++) + buff[i] = (u8)(entries[i] & 0xFF); + + Vector_t v = vecFromArray(buff, vars[0].vectorType.count, sizeof(u8)); + return newVar(ByteArrayType, 1, .vectorType = v); +} + +u8 singleIntArray[] = { IntArrayType }; +u8 singleInt[] = { IntType }; +u8 singleAny[] = { varArgs }; + +functionStruct_t scriptFunctions[] = { + {"if", funcIf, 1, singleInt}, + {"print", funcPrint, varArgs, NULL}, + {"println", funcPrintln, varArgs, NULL}, + {"while", funcWhile, 1, singleInt}, + {"else", funcElse, 0, NULL}, + {"len", funcLen, 1, singleAny}, + {"byte", funcMakeByteArray, 1, singleIntArray}, +}; + +Variable_t executeFunction(scriptCtx_t* ctx, char* func_name, lexarToken_t *start, u32 len) { + Vector_t args = { 0 }; + + if (len > 0) { + args = extractVars(ctx, start, len); + Variable_t* vars = vecGetArray(Variable_t*, args); + for (int i = 0; i < args.count; i++) { + if (vars[i].varType == ErrType) + return vars[i]; + } + } + + Variable_t* vars = vecGetArray(Variable_t*, args); + + for (u32 i = 0; i < ARRAY_SIZE(scriptFunctions); i++) { + if (scriptFunctions[i].argCount == args.count || scriptFunctions[i].argCount == varArgs) { + if (!strcmp(scriptFunctions[i].key, func_name)) { + if (scriptFunctions[i].argCount != varArgs && scriptFunctions[i].argCount != 0) { + u8 argsMatch = 1; + for (u32 j = 0; j < args.count; j++) { + if (vars[j].varType != scriptFunctions[i].typeArray[j] && scriptFunctions[i].typeArray[j] != varArgs) { + argsMatch = 0; + break; + } + } + + if (!argsMatch) + continue; + } + + Variable_t ret = scriptFunctions[i].value(ctx, vars, args.count); + freeVariableVector(&args); + return ret; + } + } + } + + Variable_t* var = dictVectorFind(&ctx->varDict, func_name); + if (var != NULL) { + if (var->varType == JumpType) { + setCurIndentInstruction(ctx, 0, 1, ctx->curPos); + ctx->curPos = var->integerType - 1; + return NullVar; + } + } + + return ErrVar(ERRNOFUNC); +} \ No newline at end of file diff --git a/source/script/functions.h b/source/script/functions.h new file mode 100644 index 0000000..b5640e8 --- /dev/null +++ b/source/script/functions.h @@ -0,0 +1,4 @@ +#pragma once +#include "variables.h" + +Variable_t executeFunction(scriptCtx_t* ctx, char* func_name, lexarToken_t* start, u32 len); \ No newline at end of file diff --git a/source/script/lexer.c b/source/script/lexer.c new file mode 100644 index 0000000..f2bd685 --- /dev/null +++ b/source/script/lexer.c @@ -0,0 +1,269 @@ +#include "lexer.h" +#include "types.h" +#include "args.h" +#include + +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')); +} + +#define makeLexarToken(token, var) ((lexarToken_t) {token, var}) + +typedef struct { + u8 tokenC; + u8 tokenN; +} lexarTranslation_t; + +lexarTranslation_t lexarTranslations[] = { + {'}', RCBracket}, + {',', Seperator}, + {'+', Plus}, + {'-', Minus}, + {'*', Multiply}, + {'/', Division}, + {'%', Mod}, + {'<', Smaller}, + {'>', Bigger}, + {'!', Not}, + {')', RBracket}, + {']', RSBracket}, + {'(', LBracket}, + {'{', LCBracket}, + {'=', Equal}, + {'[', LSBracket}, + {'\0', 0}, +}; + +/* + Should we make vars with next char being '(' a function and vars with an equals (or [x] wait how are we gonna spot that) after it to be an assignmentVar +*/ + +char lexarDebugGetTokenC(u8 tokenN) { + for (int i = 0; lexarTranslations[i].tokenC; i++) { + if (lexarTranslations[i].tokenN == tokenN) { + return lexarTranslations[i].tokenC; + } + } + + if (tokenN == EquationSeperator) + return ';'; + + return '?'; +} + +/* +* !! we need to remake this +void lexarVectorClear(lexarVector_t* vec) { + for (int i = 0; i < vec->stored; i++) { + if (vec->tokens[i].token == Variable || vec->tokens[i].token == StrLit) + if (vec->tokens[i].text != NULL) + free(vec->tokens[i].text); + } + free(vec->tokens); +} +*/ + +void lexarVectorClear(Vector_t *v){ + vecPDefArray(lexarToken_t*, entries, v); + + for (int i = 0; i < v->count; i++){ + if (entries[i].token != IntLit && entries[i].text != NULL){ + free(entries[i].text); + } + } + + vecFreePtr(v); +} + +#define ELIFC(c) else if (*in == c) + +Vector_t runLexar(const char* in, u32 len) { + const char *start = in; + Vector_t vec = newVec(sizeof(lexarToken_t), 16); + // store last var for re-assignment + // var -> func if next obj is '(' + // var -> assignment if next obj is '=' + // var -> arrassignment if next obj is '[' and before '=' is ']' + // maybe measure len between ( ) and [ ], so this doesn't have to be done during runtime? + // We also have to support (()). maybe if '(' set indent level, then if ')' minus indent level, set len. indent level contains {u8 level, u16 token, u16 startoffset} + + while ((in - start) < len) { + lexarToken_t* lx = vecGetArray(lexarToken_t*, vec); + + if ((lx[vec.count - 2].token == StrLit || lx[vec.count - 2].token == IntLit || lx[vec.count - 2].token == Variable || lx[vec.count - 2].token == RSBracket || lx[vec.count - 2].token == RBracket) + && (lx[vec.count - 1].token == Variable || lx[vec.count - 1].token == LCBracket || lx[vec.count - 1].token == RCBracket)) { + lexarToken_t holder = lx[vec.count - 1]; + lx[vec.count - 1] = makeLexarToken(EquationSeperator, 0); + vecAddElement(&vec, holder); + lx = vecGetArray(lexarToken_t*, vec); + } + + if (isValidWord(*in)) { + char* startWord = in; + in++; + while (isValidVar(*in)) + in++; + + vecAddElement(&vec, (makeLexarToken(Variable, utils_copyStringSize(startWord, in - startWord)))); + continue; + } + else if (isValidNum(*in) || (*in == '-' && isValidNum(in[1]))) { + int 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; + + vecAddElement(&vec, makeLexarToken(IntLit, parse)); + continue; + } + ELIFC('(') { + if (lx[vec.count - 1].token == Variable) + lx[vec.count - 1].token = Function; + + vecAddElement(&vec, makeLexarToken(LBracket, 0)); + } + ELIFC('[') { + if (lx[vec.count - 1].token == Variable) + lx[vec.count - 1].token = ArrayVariable; + + vecAddElement(&vec, makeLexarToken(LSBracket, 0)); + } + ELIFC('=') { // Do we need to keep = if the vars are assignments anyway? + if (lx[vec.count - 1].token == Variable) + lx[vec.count - 1].token = VariableAssignment; + + else if (lx[vec.count - 1].token == RSBracket) { + int back = 1; + while (lx[vec.count - back].token != ArrayVariable) { + back++; + if (vec.count - back < 0) + break; // major error + } + if (lx[vec.count - back].token == ArrayVariable) { + lx[vec.count - back].token = ArrayVariableAssignment; + } + } + } + ELIFC('{') { + if (lx[vec.count - 1].token == VariableAssignment) { + lx[vec.count - 1].token = FunctionAssignment; + } + vecAddElement(&vec, makeLexarToken(LCBracket, 0)); + } + 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'; + + vecAddElement(&vec, makeLexarToken(StrLit, storage)); + } + ELIFC('#') { + while (*in != '\n') + in++; + } + ELIFC('&') { + if (in[1] == '&') { + vecAddElement(&vec, makeLexarToken(LogicAND, 0)); + in++; + } + else { + vecAddElement(&vec, makeLexarToken(AND, 0)); + } + } + ELIFC('|') { + if (in[1] == '|') { + vecAddElement(&vec, makeLexarToken(LogicOR, 0)); + in++; + } + else { + vecAddElement(&vec, makeLexarToken(OR, 0)); + } + } + else { + int val = 0; + + for (int i = 0; lexarTranslations[i].tokenC; i++) { + if (lexarTranslations[i].tokenC == *in) { + val = lexarTranslations[i].tokenN; + break; + } + } + + in++; + + if (*in == '=' && val >= Smaller && val <= Not) { + val++; + in++; + } + + if (val != Invalid) + vecAddElement(&vec, makeLexarToken(val, 0)); + + continue; + } + in++; + } + + lexarToken_t* lx = vecGetArray(lexarToken_t*, vec); + if ((lx[vec.count - 2].token == StrLit || lx[vec.count - 2].token == IntLit || lx[vec.count - 2].token == Variable || lx[vec.count - 2].token == RSBracket || lx[vec.count - 2].token == RBracket) + && (lx[vec.count - 1].token == Variable || lx[vec.count - 1].token == LCBracket || lx[vec.count - 1].token == RCBracket)) { + lexarToken_t holder = lx[vec.count - 1]; + lx[vec.count - 1] = makeLexarToken(EquationSeperator, 0); + vecAddElement(&vec, holder); + } + + vecAddElement(&vec, makeLexarToken(EquationSeperator, 0)); + return vec; +} \ No newline at end of file diff --git a/source/script/lexer.h b/source/script/lexer.h new file mode 100644 index 0000000..b2eeb6c --- /dev/null +++ b/source/script/lexer.h @@ -0,0 +1,6 @@ +#pragma once +#include "types.h" + +Vector_t runLexar(const char* in, u32 len); +char lexarDebugGetTokenC(u8 tokenN); +void lexarVectorClear(Vector_t *v); \ No newline at end of file diff --git a/source/script/parser.c b/source/script/parser.c new file mode 100644 index 0000000..1a8ab5c --- /dev/null +++ b/source/script/parser.c @@ -0,0 +1,109 @@ +#include "args.h" +#include "types.h" +#include "variables.h" +#include "lexer.h" +#include "../gfx/gfx.h" +#include "../utils/utils.h" +#include + +#define scriptResultCreate(resCode, nearToken) (scriptResult_t) {resCode, nearToken, 1} +#define scriptResultCreateLen(resCode, nearToken, len) (scriptResult_t) {resCode, nearToken, len} + +scriptResult_t runFunction(scriptCtx_t* ctx, u32 len) { + lexarToken_t* tokens = vecGetArray(lexarToken_t*, ctx->script); + Variable_t res = solveEquation(ctx, &tokens[ctx->startEquation], len, 1); + + if (res.varType == ErrType) { + return scriptResultCreateLen(res.integerType, &tokens[ctx->startEquation], len); + } + + return scriptResultCreate(0, 0); +} + +#define RUNFUNCWITHPANIC(ctx, len) scriptResult_t res = runFunction(ctx, len); if (res.resCode != 0) return res + +static inline int checkIfVar(u8 token) { + return (token == StrLit || token == IntLit || token == Variable || token == RSBracket || token == ArrayVariable); +} + +scriptResult_t mainLoop(scriptCtx_t* ctx) { + ctx->startEquation = 0; + lexarToken_t* lexArr = vecGetArray(lexarToken_t*, ctx->script); + for (ctx->curPos = 0; ctx->curPos < ctx->script.count; ctx->curPos++) { + u32 i = ctx->curPos; + lexarToken_t curToken = lexArr[i]; + + if (curToken.token == EquationSeperator) { + RUNFUNCWITHPANIC(ctx, ctx->curPos - ctx->startEquation); + ctx->startEquation = ctx->curPos + 1; + } + else if (curToken.token == FunctionAssignment) { + setCurIndentInstruction(ctx, 1, 0, -1); + dict_t x = newDict(CpyStr(curToken.text), newVar(JumpType, 0, .integerType = ctx->curPos + 1)); + vecAddElement(&ctx->varDict, x); + } + else if (curToken.token == LCBracket) { + indentInstructor_t* ins = getCurIndentInstruction(ctx); + if (ins->active) { + if (ins->skip) { + int distance = distanceBetweenTokens(&lexArr[i + 1], ctx->script.count - i - 1, LCBracket, RCBracket); + if (distance < 0) + return scriptResultCreate(ERRSYNTAX, &lexArr[i]); + ctx->curPos += distance + 1; + } + else + ctx->indentIndex++; + } + else + return scriptResultCreate(ERRINACTIVEINDENT, &lexArr[i]); + + ctx->startEquation = ctx->curPos + 1; + } + else if (curToken.token == RCBracket) { + ctx->indentIndex--; + indentInstructor_t* ins = getCurIndentInstruction(ctx); + if (ins->active && ins->jump) { + ctx->curPos = ins->jumpLoc - 1; + } + + ins->active = 0; + ctx->startEquation = ctx->curPos + 1; + } + } + + return scriptResultCreate(0, 0); +} + +void printToken(lexarToken_t* token) { + switch (token->token) { + case Variable: + case VariableAssignment: + case Function: + case FunctionAssignment: + case ArrayVariable: + case ArrayVariableAssignment: + gfx_printf("%s", token->text); + break; + case StrLit: + //printf("%d: '%s'\n", vec.tokens[i].token, vec.tokens[i].text); + gfx_printf("\"%s\"", token->text); + break; + case IntLit: + //printf("%d: %d\n", vec.tokens[i].token, vec.tokens[i].val); + gfx_printf("%d", token->val); + break; + default: + //printf("%d: %c\n", vec.tokens[i].token, lexarDebugGetTokenC(vec.tokens[i].token)); + gfx_printf("%c", lexarDebugGetTokenC(token->token)); + break; + } +} + +void printError(scriptResult_t res) { + if (res.resCode) { + gfx_printf("Error %d found!\nNear: ", res.resCode); + for (int i = 0; i < res.len; i++) { + printToken(&res.nearToken[i]); + } + } +} \ No newline at end of file diff --git a/source/script/parser.h b/source/script/parser.h new file mode 100644 index 0000000..37c21be --- /dev/null +++ b/source/script/parser.h @@ -0,0 +1,6 @@ +#pragma once + +#include "types.h" + +scriptResult_t mainLoop(scriptCtx_t* ctx); +void printError(scriptResult_t res); \ No newline at end of file diff --git a/source/script/types.h b/source/script/types.h new file mode 100644 index 0000000..0372ebc --- /dev/null +++ b/source/script/types.h @@ -0,0 +1,140 @@ +#pragma once + +#include +#include "../utils/vector.h" + +enum Tokens { + Invalid = 0, + Variable = 1, + ArrayVariable, + Function, + LBracket, + StrLit, + IntLit, + LSBracket, + VariableAssignment, + ArrayVariableAssignment, + FunctionAssignment, + RBracket, + RCBracket, + LCBracket, + Seperator, + Plus, + Minus, + Multiply, + Division, + Mod, + Smaller, + SmallerEqual, + Bigger, + BiggerEqual, + Equal, + EqualEqual, + Not, + NotEqual, + LogicAND, + LogicOR, + RSBracket, + AND, + OR, + EquationSeperator, +}; + +typedef struct { + u8 token; + union { + char* text; + int val; + }; +} lexarToken_t; + +enum Errors { + ERRBADOPERATOR = 1, + ERRDOUBLENOT, + ERRSYNTAX, + ERRINVALIDTYPE, + ERRNOVAR, + ERRNOFUNC, + ERRINACTIVEINDENT, + ERRDIVBYZERO +}; + +enum Variables { + IntType = 0, + StringType, + IntArrayType, + StringArrayType, + ByteArrayType, + JumpType, + DictType, + NullType, + ErrType, +}; + +typedef struct { // this is to keep track of how many {} we passed. Keep an internal var with the "indentation level", +1 for {, -1 for }. have an array with the following def on what to do (on func: enter, set indentation & jump back, on while, jump to while, use while as if, on if simply set true or false) + union { + struct { + u8 active : 1; + u8 skip : 1; + u8 jump : 1; + u8 function : 1; + }; + u8 container; + }; + int jumpLoc; +} indentInstructor_t; + +typedef struct { + union { + struct { + u8 varType : 7; + u8 free : 1; + }; + u8 typeUnion; + }; + union { + int integerType; + char* stringType; + Vector_t vectorType; + }; +} Variable_t; + +typedef struct { + char* key; + Variable_t value; +} dict_t; + +typedef struct { + Vector_t indentInstructors; // Type indentInstructor_t + Vector_t varDict; // Type dict_t + Vector_t script; // Type lexarToken_t + u32 startEquation; + u32 curPos; + u8 indentIndex; +} scriptCtx_t; + +typedef Variable_t(*func_int_ptr)(scriptCtx_t* ctx, Variable_t *vars, u32 len); + +typedef struct { + char* key; + func_int_ptr value; + u8 argCount; + u8* typeArray; +} functionStruct_t; + +typedef struct { + int resCode; + lexarToken_t* nearToken; + u32 len; +} scriptResult_t; + + + +#define newDict(strName, var) (dict_t) {strName, var} +#define newVar(var, frii, value) (Variable_t) {.varType = var, .free = frii, value} + +#define varArgs 255 + +#define NullVar newVar(NullType, 0, 0) +#define ErrVar(err) newVar(ErrType, 0, err) +#define IntVal(val) newVar(IntType, 0, val) \ No newline at end of file diff --git a/source/script/variables.c b/source/script/variables.c new file mode 100644 index 0000000..88f0f45 --- /dev/null +++ b/source/script/variables.c @@ -0,0 +1,105 @@ +#include "variables.h" +#include "types.h" +#include +#include + +void freeVariable(Variable_t dv) { + if (!dv.free) + return; + + switch (dv.varType) { + case StringType: + FREE(dv.stringType); + break; + + case StringArrayType:; + char** strArray = vecGetArray(char**, dv.vectorType); + for (u32 i = 0; i < dv.vectorType.count; i++){ + FREE(strArray[i]); + } + + case IntArrayType: + case ByteArrayType: + vecFree(dv.vectorType); + break; + } +} + +void freeVariableVector(Vector_t *v) { + Variable_t* vars = vecGetArrayPtr(v, Variable_t*); + for (int i = 0; i < v->count; i++) { + freeVariable(vars[i]); + } + vecFreePtr(v); +} + +void freeDictVector(Vector_t *v) { + dict_t* dic = vecGetArrayPtr(v, dict_t*); + for (int i = 0; i < v->count; i++) { + FREE(dic[i].key); + freeVariable(dic[i].value); + } + vecFreePtr(v); +} + +Variable_t* dictVectorFind(Vector_t* v, const char* key) { + dict_t* dic = vecGetArrayPtr(v, dict_t*); + for (int i = 0; i < v->count; i++) { + if (!strcmp(dic[i].key, key)) + return &dic[i].value; + } + + return NULL; +} + +void dictVectorAdd(Vector_t* v, dict_t add) { + if (add.key == NULL) + return; + + Variable_t* var = dictVectorFind(v, add.key); + + if (var != NULL) { + if ((var->varType >= StringType && var->varType <= ByteArrayType && var->varType == add.value.varType) && (add.value.stringType == var->stringType || add.value.vectorType.data == var->vectorType.data)) + return; + + freeVariable(*var); + *var = add.value; + free(add.key); + return; + } + else { + vecAddElement(v, add); + } +} + +scriptCtx_t createScriptCtx() { + scriptCtx_t s = { + .indentInstructors = newVec(sizeof(indentInstructor_t), 64), + .varDict = newVec(sizeof(dict_t), 8), + .startEquation = 0, + .curPos = 0 + }; + return s; +} + +// We should rewrite this to actually use the vector! +u8 setIndentInstruction(scriptCtx_t* ctx, u8 level, u8 skip, u8 func, int jumpLoc) { + if (level >= 64) + return 1; + + indentInstructor_t* instructors = vecGetArray(indentInstructor_t*, ctx->indentInstructors); + indentInstructor_t* instructor = &instructors[level]; + + instructor->skip = skip; + instructor->active = 1; + instructor->function = func; + instructor->jump = (jumpLoc >= 0) ? 1 : 0; + instructor->jumpLoc = jumpLoc; + + return 0; +} + +indentInstructor_t* getCurIndentInstruction(scriptCtx_t* ctx) { + indentInstructor_t* instructors = vecGetArray(indentInstructor_t*, ctx->indentInstructors); + return &instructors[ctx->indentIndex]; +} \ No newline at end of file diff --git a/source/script/variables.h b/source/script/variables.h new file mode 100644 index 0000000..dac6115 --- /dev/null +++ b/source/script/variables.h @@ -0,0 +1,16 @@ +#pragma once +#include "types.h" + +void dictVectorAdd(Vector_t* v, dict_t add); +Variable_t* dictVectorFind(Vector_t* v, const char* key); +void freeDictVector(Vector_t* v); +void freeVariableVector(Vector_t* v); +void freeVariable(Variable_t dv); +scriptCtx_t createScriptCtx(); + +u8 setIndentInstruction(scriptCtx_t* ctx, u8 level, u8 skip, u8 func, int jumpLoc); +indentInstructor_t* getCurIndentInstruction(scriptCtx_t* ctx); + +static inline u8 setCurIndentInstruction(scriptCtx_t* ctx, u8 skip, u8 func, int jumpLoc) { + return setIndentInstruction(ctx, ctx->indentIndex, skip, func, jumpLoc); +} \ No newline at end of file diff --git a/source/utils/vector.c b/source/utils/vector.c index 5779987..c0530f0 100644 --- a/source/utils/vector.c +++ b/source/utils/vector.c @@ -45,4 +45,11 @@ bool vecAdd(Vector_t* v, void* elem, u32 sz) memcpy((char*)v->data + usedbytes, elem, sz); v->count++; return true; +} + +Vector_t vecCopy(Vector_t* orig) { + Vector_t dst = newVec(orig->elemSz, orig->count); + memcpy(dst.data, orig->data, orig->count * orig->elemSz); + dst.count = orig->count; + return dst; } \ No newline at end of file diff --git a/source/utils/vector.h b/source/utils/vector.h index 597cc99..b845773 100644 --- a/source/utils/vector.h +++ b/source/utils/vector.h @@ -9,12 +9,21 @@ typedef struct { // u32 typeTag; } Vector_t; +#define FREE(x) free(x); x = NULL; + #define vecAddElem(v, elem) vecAdd(v, &elem, sizeof(elem)) +#define vecAddElement(v, elem) vecAdd(v, &elem, sizeof(elem)) #define vecDefArray(type, varName, vec) type varName = (type)((vec).data) #define vecGetArray(type, vec) (type)((vec).data) #define vecPDefArray(type, varName, vec) type varName = (type)((vec)->data) #define vecPGetArray(type, vec) (type)((vec)->data) +#define vecFreePtr(vec) FREE(vec->data) +#define vecFree(vec) FREE(vec.data) +#define vecGetCapacity(vec) (vec.capacity / vec.elemSz) + +#define vecGetArrayPtr(vec, type) (type)((vec)->data) Vector_t newVec(u32 typesz, u32 preallocate); Vector_t vecFromArray(void* array, u32 count, u32 typesz); -bool vecAdd(Vector_t* v, void* elem, u32 sz); \ No newline at end of file +bool vecAdd(Vector_t* v, void* elem, u32 sz); +Vector_t vecCopy(Vector_t* orig); \ No newline at end of file