/* * This file is part of the PikaScript project. * http://github.com/pikastech/pikascript * * MIT License * * Copyright (c) 2021 lyon 李昂 liang6516@outlook.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "PikaVM.h" #include "BaseObj.h" #include "PikaObj.h" #include "PikaParser.h" #include "PikaPlatform.h" #include "dataQueue.h" #include "dataQueueObj.h" #include "dataStrs.h" static int32_t getLineSize(char* str) { int i = 0; while (1) { if (str[i] == '\n') { return i; } i++; } } enum Instruct { NON = 0, REF, RUN, STR, OUT, NUM, JMP, JEZ, OPT, DEF, RET, NEL, DEL, EST, BRK, CTN }; static char* strs_getLine(Args* buffs, char* code) { int32_t lineSize = getLineSize(code); char* line = args_getBuff(buffs, lineSize + 1); __platform_memcpy(line, code, lineSize); line[lineSize + 1] = 0; return line; } static enum Instruct getInstruct(char* line) { if (0 == strncmp(line + 2, "OPT", 3)) { /* operator */ return OPT; } if (0 == strncmp(line + 2, "REF", 3)) { /* reference */ return REF; } if (0 == strncmp(line + 2, "NUM", 3)) { /* number */ return NUM; } if (0 == strncmp(line + 2, "RUN", 3)) { /* run */ return RUN; } if (0 == strncmp(line + 2, "STR", 3)) { /* string */ return STR; } if (0 == strncmp(line + 2, "OUT", 3)) { /* out */ return OUT; } if (0 == strncmp(line + 2, "JMP", 3)) { /* jump */ return JMP; } if (0 == strncmp(line + 2, "JEZ", 3)) { /* jump equal zero */ return JEZ; } if (0 == strncmp(line + 2, "DEF", 3)) { /* define */ return DEF; } if (0 == strncmp(line + 2, "RET", 3)) { /* return */ return RET; } if (0 == strncmp(line + 2, "NEL", 3)) { /* not else */ return NEL; } if (0 == strncmp(line + 2, "DEL", 3)) { /* delete */ return DEL; } if (0 == strncmp(line + 2, "EST", 3)) { /* is exist */ return EST; } if (0 == strncmp(line + 2, "BRK", 3)) { /* break */ return BRK; } if (0 == strncmp(line + 2, "CTN", 3)) { /* continue */ return CTN; } return NON; } Arg* pikaVM_runInstruct(PikaObj* self, Parameters* locals, Parameters* globals, enum Instruct instruct, char* data, Queue* invokeQuene0, Queue* invokeQuene1, int32_t* jmp, char* programConter, char* asmStart) { if (instruct == NUM) { Arg* numArg = New_arg(NULL); if (strIsContain(data, '.')) { return arg_setFloat(numArg, "", atof(data)); } return arg_setInt(numArg, "", fast_atoi(data)); } if (instruct == STR) { Arg* strArg = New_arg(NULL); return arg_setStr(strArg, "", data); } if (instruct == OUT) { Arg* outArg = arg_copy(queue_popArg(invokeQuene0)); obj_setArg(locals, data, outArg); arg_deinit(outArg); return NULL; } if (instruct == DEL) { obj_removeArg(locals, data); return NULL; } if (instruct == REF) { if (strEqu(data, (char*)"True")) { return arg_setInt(NULL, "", 1); } if (strEqu(data, (char*)"False")) { return arg_setInt(NULL, "", 0); } /* find in local list first */ Arg* arg = arg_copy(obj_getArg(locals, data)); if (NULL == arg) { /* find in global list second */ arg = arg_copy(obj_getArg(globals, data)); } ArgType arg_type = arg_getType(arg); if (TYPE_OBJECT == arg_type) { arg = arg_setType(arg, TYPE_POINTER); } return arg; } if (instruct == EST) { Arg* arg = obj_getArg(locals, data); if (arg == NULL) { return arg_setInt(NULL, "", 0); } if (TYPE_NULL == arg_getType(arg)) { return arg_setInt(NULL, "", 0); } return arg_setInt(NULL, "", 1); } if (instruct == JMP) { *jmp = fast_atoi(data); return NULL; } if (instruct == RET) { /* exit jmp signal */ *jmp = -999; Arg* returnArg = arg_copy(queue_popArg(invokeQuene0)); method_returnArg(locals->list, returnArg); return NULL; } if (instruct == BRK) { /* break jmp signal */ *jmp = -998; return NULL; } if (instruct == CTN) { /* continue jmp signal */ *jmp = -997; return NULL; } if (instruct == DEF) { char* methodPtr = programConter; int offset = 0; int thisBlockDeepth = getThisBlockDeepth(asmStart, programConter, &offset); while (1) { if ((methodPtr[0] == 'B') && (methodPtr[1] - '0' == thisBlockDeepth + 1)) { class_defineMethod(self, data, (Method)methodPtr); break; } offset += gotoNextLine(methodPtr); methodPtr = programConter + offset; } return NULL; } if (instruct == JEZ) { int offset = 0; int thisBlockDeepth = getThisBlockDeepth(asmStart, programConter, &offset); Arg* assertArg = arg_copy(queue_popArg(invokeQuene0)); int assert = arg_getInt(assertArg); arg_deinit(assertArg); char __else[] = "__else0"; __else[6] = '0' + thisBlockDeepth; args_setInt(self->list, __else, !assert); if (0 == assert) { /* set __else flag */ *jmp = fast_atoi(data); } return NULL; } if (instruct == NEL) { int offset = 0; int thisBlockDeepth = getThisBlockDeepth(asmStart, programConter, &offset); char __else[] = "__else0"; __else[6] = '0' + thisBlockDeepth; if (0 == args_getInt(self->list, __else)) { /* set __else flag */ *jmp = fast_atoi(data); } return NULL; } if (instruct == OPT) { Arg* outArg = NULL; Arg* arg1 = arg_copy(queue_popArg(invokeQuene1)); Arg* arg2 = arg_copy(queue_popArg(invokeQuene1)); ArgType type_arg1 = arg_getType(arg1); ArgType type_arg2 = arg_getType(arg2); int num1_i = 0; int num2_i = 0; float num1_f = 0.0; float num2_f = 0.0; /* get int and float num */ if (type_arg1 == TYPE_INT) { num1_i = arg_getInt(arg1); num1_f = (float)num1_i; } if (type_arg1 == TYPE_FLOAT) { num1_f = arg_getFloat(arg1); num1_i = (int)num1_f; } if (type_arg2 == TYPE_INT) { num2_i = arg_getInt(arg2); num2_f = (float)num2_i; } if (type_arg2 == TYPE_FLOAT) { num2_f = arg_getFloat(arg2); num2_i = (int)num2_f; } if (strEqu("+", data)) { if ((type_arg1 == TYPE_FLOAT) || type_arg2 == TYPE_FLOAT) { outArg = arg_setFloat(outArg, "", num1_f + num2_f); goto OPT_exit; } outArg = arg_setInt(outArg, "", num1_i + num2_i); goto OPT_exit; } if (strEqu("-", data)) { if ((type_arg1 == TYPE_FLOAT) || type_arg2 == TYPE_FLOAT) { outArg = arg_setFloat(outArg, "", num1_f - num2_f); goto OPT_exit; } outArg = arg_setInt(outArg, "", num1_i - num2_i); goto OPT_exit; } if (strEqu("*", data)) { if ((type_arg1 == TYPE_FLOAT) || type_arg2 == TYPE_FLOAT) { outArg = arg_setFloat(outArg, "", num1_f * num2_f); goto OPT_exit; } outArg = arg_setInt(outArg, "", num1_i * num2_i); goto OPT_exit; } if (strEqu("/", data)) { outArg = arg_setFloat(outArg, "", num1_f / num2_f); goto OPT_exit; } if (strEqu("<", data)) { outArg = arg_setInt(outArg, "", num1_f < num2_f); goto OPT_exit; } if (strEqu(">", data)) { outArg = arg_setInt(outArg, "", num1_f > num2_f); goto OPT_exit; } if (strEqu("%", data)) { outArg = arg_setInt(outArg, "", num1_i % num2_i); goto OPT_exit; } if (strEqu("**", data)) { float res = 1; for (int i = 0; i < num2_i; i++) { res = res * num1_f; } outArg = arg_setFloat(outArg, "", res); goto OPT_exit; } if (strEqu("//", data)) { outArg = arg_setInt(outArg, "", num1_i / num2_i); goto OPT_exit; } if (strEqu("==", data)) { outArg = arg_setInt( outArg, "", (num1_f - num2_f) * (num1_f - num2_f) < 0.000001); goto OPT_exit; } if (strEqu("!=", data)) { outArg = arg_setInt(outArg, "", !((num1_f - num2_f) * (num1_f - num2_f) < 0.000001)); goto OPT_exit; } if (strEqu(">=", data)) { outArg = arg_setInt( outArg, "", (num1_f > num2_f) || ((num1_f - num2_f) * (num1_f - num2_f) < 0.000001)); goto OPT_exit; } if (strEqu("<=", data)) { outArg = arg_setInt( outArg, "", (num1_f < num2_f) || ((num1_f - num2_f) * (num1_f - num2_f) < 0.000001)); goto OPT_exit; } if (strEqu("&", data)) { outArg = arg_setInt(outArg, "", num1_i & num2_i); } if (strEqu("|", data)) { outArg = arg_setInt(outArg, "", num1_i | num2_i); } if (strEqu("~", data)) { outArg = arg_setInt(outArg, "", ~num1_i); } if (strEqu(">>", data)) { outArg = arg_setInt(outArg, "", num1_i >> num2_i); } if (strEqu("<<", data)) { outArg = arg_setInt(outArg, "", num1_i << num2_i); } if (strEqu(" and ", data)) { outArg = arg_setInt(outArg, "", num1_i && num2_i); } if (strEqu(" or ", data)) { outArg = arg_setInt(outArg, "", num1_i || num2_i); } if (strEqu(" not ", data)) { outArg = arg_setInt(outArg, "", !num1_i); } OPT_exit: arg_deinit(arg1); arg_deinit(arg2); if (NULL != outArg) { return outArg; } } if (instruct == RUN) { Args* buffs = New_strBuff(); Arg* returnArg = NULL; Parameters* subLocals = NULL; char* methodPath = data; PikaObj* methodHostObj; Arg* method_arg; Method methodPtr; char* methodDec; char* typeList; char* methodCode; char* sysOut; /* return arg directly */ if (strEqu(data, "")) { returnArg = arg_copy(queue_popArg(invokeQuene1)); goto RUN_exit; } /* get method host obj */ methodHostObj = obj_getObj(self, methodPath, 1); if (NULL == methodHostObj) { methodHostObj = obj_getObj(locals, methodPath, 1); } if (NULL == methodHostObj) { /* error, not found object */ args_setErrorCode(locals->list, 1); args_setSysOut(locals->list, "[error] runner: object no found."); goto RUN_exit; } /* get method */ method_arg = obj_getMethod(methodHostObj, methodPath); /* assert method*/ if (NULL == method_arg) { /* error, method no found */ args_setErrorCode(locals->list, 2); args_setSysOut(locals->list, "[error] runner: method no found."); goto RUN_exit; } /* get method Ptr */ methodPtr = (Method)methodArg_getPtr(method_arg); /* get method Decleartion */ methodDec = strsCopy(buffs, methodArg_getDec(method_arg)); arg_deinit(method_arg); /* get type list */ typeList = strsCut(buffs, methodDec, '(', ')'); if (typeList == NULL) { /* typeList no found */ args_setErrorCode(locals->list, 3); args_setSysOut(locals->list, "[error] runner: type list no found."); goto RUN_exit; } subLocals = New_PikaObj(); while (1) { Arg* methodArg = arg_copy(queue_popArg(invokeQuene1)); if (NULL == methodArg) { break; } char* argDef = strsPopToken(buffs, typeList, ','); char* argName = strsGetFirstToken(buffs, argDef, ':'); methodArg = arg_setName(methodArg, argName); args_setArg(subLocals->list, methodArg); } obj_setErrorCode(methodHostObj, 0); obj_setSysOut(methodHostObj, ""); /* run method */ methodCode = (char*)methodPtr; if (methodCode[0] == 'B' && methodCode[2] == '\n') { /* VM method */ subLocals = pikaVM_runAsmWithPars(methodHostObj, subLocals, globals, methodCode); /* get method return */ returnArg = arg_copy(args_getArg(subLocals->list, (char*)"return")); } else { /* native method */ methodPtr(methodHostObj, subLocals->list); /* get method return */ returnArg = arg_copy(args_getArg(subLocals->list, (char*)"return")); } /* transfer sysOut */ sysOut = obj_getSysOut(methodHostObj); if (NULL != sysOut) { args_setSysOut(locals->list, sysOut); } /* transfer errCode */ if (0 != obj_getErrorCode(methodHostObj)) { /* method error */ args_setErrorCode(locals->list, 6); } goto RUN_exit; RUN_exit: if (NULL != subLocals) { obj_deinit(subLocals); } args_deinit(buffs); return returnArg; } return NULL; } int32_t __clearInvokeQueues(Parameters* locals) { for (char deepthChar = '0'; deepthChar < '9'; deepthChar++) { char deepth[2] = {0}; deepth[0] = deepthChar; Queue* queue = (Queue*)args_getPtr(locals->list, deepth); if (NULL != queue) { args_deinit(queue); args_removeArg(locals->list, args_getArg(locals->list, deepth)); } } return 0; } int32_t gotoNextLine(char* code) { int offset = 0; while (1) { if (code[offset] == '\n') { break; } offset++; } return offset + 1; } int32_t gotoLastLine(char* start, char* code) { int offset = -2; while (1) { char* codeNow = code + offset; if (codeNow == start) { offset--; break; } if (codeNow[0] == '\n') { break; } offset--; } return offset + 1; } int getThisBlockDeepth(char* start, char* code, int* offset) { int thisBlockDeepth = -1; char* codeNow = code + *offset; while (1) { *offset += gotoLastLine(start, codeNow); codeNow = code + *offset; if (codeNow[0] == 'B') { thisBlockDeepth = codeNow[1] - '0'; break; } } return thisBlockDeepth; } int32_t asm_getAddrOffsetOfJUM(char* code) { int offset = 0; char* codeNow = code + offset; while (1) { offset += gotoNextLine(codeNow); codeNow = code + offset; if (0 == strncmp(codeNow, "0 JMP -1", 8)) { return offset; } } return 0; } int32_t getAddrOffsetFromJmp(char* start, char* code, int32_t jmp) { int offset = 0; int thisBlockDeepth = getThisBlockDeepth(start, code, &offset); char* codeNow = code + offset; int8_t blockNum = 0; if (jmp > 0) { offset = 0; codeNow = code + offset; while (1) { offset += gotoNextLine(codeNow); codeNow = code + offset; if (codeNow[0] == 'B') { uint8_t blockDeepth = codeNow[1] - '0'; if (blockDeepth <= thisBlockDeepth) { blockNum++; } } if (blockNum >= jmp) { break; } } } if (jmp < 0) { while (1) { offset += gotoLastLine(start, codeNow); codeNow = code + offset; if (codeNow[0] == 'B') { uint8_t blockDeepth = codeNow[1] - '0'; if (blockDeepth == thisBlockDeepth) { blockNum--; } } if (blockNum <= jmp) { break; } } } return offset; } int32_t pikaVM_runAsmLine(PikaObj* self, Parameters* locals, Parameters* globals, char* pikaAsm, int32_t lineAddr) { Args* buffs = New_strBuff(); char* programCounter = pikaAsm + lineAddr; char* line = strs_getLine(buffs, programCounter); int32_t nextAddr = lineAddr + strGetSize(line) + 1; int32_t jmp = 0; enum Instruct instruct; char invokeDeepth0[2] = {0}, invokeDeepth1[2] = {0}; char* data; Queue* invokeQuene0; Queue* invokeQuene1; Arg* resArg; /* Found new script Line, clear the queues*/ if ('B' == line[0]) { args_setErrorCode(locals->list, 0); args_setSysOut(locals->list, (char*)""); __clearInvokeQueues(locals); goto nextLine; } invokeDeepth0[0] = line[0]; invokeDeepth1[0] = line[0] + 1; instruct = getInstruct(line); data = line + 6; invokeQuene0 = args_getPtr(locals->list, invokeDeepth0); invokeQuene1 = args_getPtr(locals->list, invokeDeepth1); if (NULL == invokeQuene0) { invokeQuene0 = New_queue(); args_setPtr(locals->list, invokeDeepth0, invokeQuene0); } if (NULL == invokeQuene1) { invokeQuene1 = New_queue(); args_setPtr(locals->list, invokeDeepth1, invokeQuene1); } resArg = pikaVM_runInstruct(self, locals, globals, instruct, data, invokeQuene0, invokeQuene1, &jmp, programCounter, pikaAsm); if (NULL != resArg) { queue_pushArg(invokeQuene0, resArg); } goto nextLine; nextLine: args_deinit(buffs); /* exit */ if (-999 == jmp) { return -99999; } /* break or continue */ if ((-998 == jmp) || (-997 == jmp)) { int32_t loop_end_addr = asm_getAddrOffsetOfJUM(programCounter); /* break */ if (-998 == jmp) { loop_end_addr += gotoNextLine(programCounter + loop_end_addr); return lineAddr + loop_end_addr; } if (-997 == jmp) { loop_end_addr += gotoLastLine(pikaAsm, programCounter + loop_end_addr); return lineAddr + loop_end_addr; } } if (jmp != 0) { return lineAddr + getAddrOffsetFromJmp(pikaAsm, programCounter, jmp); } return nextAddr; } char* useFlashAsBuff(char* pikaAsm, Args* buffs) { /* not write flash when asm is old */ if (strEqu(pikaAsm, __platform_load_pikaAsm())) { args_deinit(buffs); buffs = NULL; return __platform_load_pikaAsm(); } /* write flash */ if (0 == __platform_save_pikaAsm(pikaAsm)) { args_deinit(buffs); buffs = NULL; return __platform_load_pikaAsm(); } return pikaAsm; } Parameters* pikaVM_runAsmWithPars(PikaObj* self, Parameters* locals, Parameters* globals, char* pikaAsm) { int lineAddr = 0; int size = strGetSize(pikaAsm); args_setErrorCode(locals->list, 0); args_setSysOut(locals->list, (char*)""); while (lineAddr < size) { if (lineAddr == -99999) { break; } char* thisLine = pikaAsm + lineAddr; lineAddr = pikaVM_runAsmLine(self, locals, globals, pikaAsm, lineAddr); char* sysOut = args_getSysOut(locals->list); uint8_t errcode = args_getErrorCode(locals->list); if (!strEqu("", sysOut)) { __platform_printf("%s\r\n", sysOut); } if (0 != errcode) { Args* buffs = New_strBuff(); char* onlyThisLine = strsGetFirstToken(buffs, thisLine, '\n'); __platform_printf("[info] input commond: %s\r\n", onlyThisLine); args_deinit(buffs); } } __clearInvokeQueues(locals); return locals; } Parameters* pikaVM_runAsm(PikaObj* self, char* pikaAsm) { return pikaVM_runAsmWithPars(self, self, self, pikaAsm); } Parameters* pikaVM_run(PikaObj* self, char* multiLine) { Args* buffs = New_strBuff(); Parameters* globals = NULL; char* pikaAsm = Parser_multiLineToAsm(buffs, multiLine); if (NULL == pikaAsm) { __platform_printf("[error]: Syntax error.\r\n"); globals = NULL; goto exit; } globals = pikaVM_runAsm(self, pikaAsm); goto exit; exit: if (NULL != buffs) { args_deinit(buffs); } return globals; }