#include "Arduino.h" #include "ProgLines.h" #include "Basic.h" const char e1[] = " Invalid statement type"; const char e2[] = " GOTO invalid line number"; const char e3[] = " Operator does not follow variable or constant"; const char e4[] = " Expected variable name"; const char e5[] = " Expression missing an operator"; const char e6[] = " Incomplete expression"; const char e7[] = " Unbalanced parenthases"; const char e8[] = " FOR expected assignment operator"; const char e9[] = " LET without variable"; const char e10[] = " LET without assignment operator"; const char e11[] = " FOR without counter variable"; const char e12[] = " INPUT invalid in emmediate mode"; const char e13[] = " NEXT without FOR encountered"; const char e14[] = " Invalid initial value for FOR loop"; const char e15[] = " Missing TO in FOR statement"; const char e16[] = " Invalid target value for FOR loop"; const char e17[] = " Invalid use of FOR statement"; const char e18[] = " IF statement without THEN"; const char e19[] = " Invalid conditional statement"; const char e20[] = " Invalid INPUT statement"; const char e21[] = " Divide by zero error"; const char e22[] = " GOSUB invalid line number"; const char e23[] = " RETURN without GOSUB"; const char e24[] = " STEP without a value or expression"; const char* errMess[] = {e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24 }; const char l1[] = "10 PRINT \"This is the test program\""; const char l2[] = "20 INPUT \"Please enter a number: \" A%"; const char l3[] = "30 INPUT \"Now, how about entering your name \" A$"; const char l4[] = "40 PRINT \"Hello \" & A$ & \", I hope you are well\""; const char l5[] = "50 PRINT \"The number you entered was \" & A%"; const char l6[] = "60 INPUT \"Would you like to change it (Y or N)? \" B$"; const char l7[] = "70 IF B$ = \"Y\" OR B$ = \"y\" THEN GOSUB 300"; const char l8[] = "80 LET B% = A% * 12"; const char l9[] = "90 PRINT \"Your number multiplied by 12 = \" & B%"; const char l10[] = "100 FOR C% = A% TO 1 STEP -1"; const char l11[] = "110 PRINT C%"; const char l12[] = "120 NEXT"; const char l13[] = "200 END"; const char l14[] = "300 INPUT \"Enter another number: \" A%"; const char l15[] = "310 RETURN"; const char* testProg[] = {l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15 }; ProgLines progLines; ProgLines::ProgLines() { nextLine = 0; firstLine = NULL; gotoExec = false; } ProgLines::~ProgLines() { // this is not going to be called but anyway newProgram(); // which would clear all statement memory allocations } bool ProgLines::setNextLine(short nLine){ // validate that the line is in the list and return true or false if(isLine(nLine)) { nextLine = nLine; gotoExec = true; return true; } return false; } void ProgLines::executeNext(){ // causes the next line to be executed by Basic class char buff[STRINGMAX]; bLine* temp = firstLine; while (temp != NULL) { if(temp->lNum == nextLine) { memset(buff, '\0', STRINGMAX); memcpy(buff, temp->cText, temp->textLen); int errCode = basic.parseLine(buff); if(errCode == 0) { errCode = basic.executeLine(); } if(errCode) { displayError(errCode, nextLine); basic.rStatus = Basic::USER; // stop on error } goto LineDone; // hey a C goto (must have written too much BASIC) } temp = temp->next; } LineDone: if(basic.rStatus == Basic::RUN && !gotoExec) { advanceLine(); // set the next one unless a GOTO or FOR loop already did } gotoExec = false; } // increment nextline or terminate program void ProgLines::advanceLine() { bLine* temp = firstLine; while (temp != NULL) { if(temp->lNum == nextLine) { if(temp->next != NULL) { nextLine = temp->next->lNum; return; } else { // no more lines so end basic.rStatus = Basic::USER; Serial.println("Program terminated after final line"); } } temp = temp->next; } //Serial.println("Error - in advanceLine()"); } int ProgLines::getNextLine() { // returns the line after currently executing line (which is nextLine sigh...) bLine* temp = firstLine; while (temp != NULL) { if(temp->lNum == nextLine) { if(temp->next != NULL) { return temp->next->lNum; } else { break; } } temp = temp->next; } return 0; } void ProgLines::runProg() { basic.setForRun(); nextLine = firstLine->lNum; executeNext(); } bool ProgLines::addLine(char *pLine){ // decide if this is a new line of code or a command short ln = atoi(pLine); if(ln == 0) { if(strncmp(pLine, "LIST", 4) == 0) { Serial.println("Program Listing"); listLines(); return true; } else if (strcmp(pLine, "RUN") == 0) { Serial.println("Run Program"); runProg(); return false; } else if(strcmp(pLine, "NEW") == 0) { Serial.println("Setting for new program"); newProgram(); return true; } else if(strncmp(pLine, "RENUM", 5) == 0){ Serial.println("Line renumbering - not implemented"); return true; } else if(strncmp(pLine, "LOAD", 4) == 0) { Serial.println("Loading Test Program"); loadTest(); } else { Serial.println("Immediate Execute Request?"); // parse and execute checking return code while (*pLine != '\0') { if(*pLine >= 'A' && *pLine <= 'Z') { break; } pLine++; } if(strlen(pLine) > 0) { int errCode = basic.parseLine(pLine); if(errCode == 0) { errCode = basic.executeLine(); } if(errCode) { displayError(errCode, 0); } } } return true; }else { if(ln < 0) { Serial.println("Invalid line number"); return true; } // store the new program line while (*pLine != '\0') { if(*pLine >= 'A' && *pLine <= 'Z') { break; } pLine++; } int cLen = strlen(pLine); if(cLen == 0) { deleteLine(ln); return true; } int errCode = basic.parseLine(pLine); if(errCode == 0) { if(!storeLine(ln, pLine, cLen)){ Serial.println("Error storing program line"); } } else { // tell user about parse error displayError(errCode, 0); } } return true; } // program lines are stored in a linked list which maintains line number order bool ProgLines::storeLine(short lNum, char* cText, int cLen){ struct bLine* lNode = (bLine*)malloc(sizeof(struct bLine)); if(lNode == NULL) {return false;} lNode->lNum = lNum; lNode->prev = NULL; lNode->next = NULL; lNode->textLen = cLen; lNode->cText = malloc(cLen); if(lNode->cText == NULL) {return false;} //copy the text to the new memory area memcpy(lNode->cText, cText, cLen); if(firstLine == NULL) { firstLine = lNode; } else { // walk the existing nodes to find the insertion point if(lNum < firstLine->lNum) { lNode->next = firstLine; firstLine = lNode; } else { bLine *temp = firstLine; while(temp != NULL) { if(temp->lNum >= lNum) { break; } lNode->prev = temp; temp = temp->next; } if(temp != NULL) { // we are inserting or replacing if(temp->lNum == lNum) { free(temp->cText); temp->cText = lNode->cText; temp->textLen = cLen; free(lNode); } else { lNode->prev = temp->prev; temp->prev->next = lNode; temp->prev = lNode; lNode->next = temp; } } else { lNode->prev->next = lNode; // add at end } } } return true; } bool ProgLines::deleteLine(short lNum) { // deletes a line item from linked list if it exists Serial.print("Delete Line: "); Serial.println(lNum); bLine* temp = firstLine; while (temp != NULL) { if(temp->lNum == lNum) { if(temp->prev != NULL) { temp->prev->next = temp->next; } if(temp->next != NULL) { temp->next->prev = temp->prev; } free(temp->cText); free(temp); break; } temp = temp->next; } return true; } bool ProgLines::isLine(short iLine) { bLine* temp = firstLine; while (temp != NULL) { if(temp->lNum == iLine) { return true; } temp = temp->next; } return false; } void ProgLines::listLines() { bLine* temp = firstLine; char pBuffer[STRINGMAX]; while (temp != NULL) { memset(pBuffer, '\0', STRINGMAX); memcpy(pBuffer, temp->cText, temp->textLen); Serial.print(temp->lNum); Serial.print(" "); Serial.println(pBuffer); temp = temp->next; } } void ProgLines::newProgram() { bLine* temp = firstLine; bLine* last = NULL; while (temp != NULL) { free(temp->cText); last = temp; temp = temp->next; free(last); } firstLine = NULL; } void ProgLines::displayError(int errCode, short lNum) { errCode--; if(errCode < (sizeof(errMess) / sizeof(char*))) { Serial.print("Error "); Serial.print(errCode + 1); Serial.print(": "); Serial.print(errMess[errCode]); if(lNum > 0) { Serial.print(" at line "); Serial.println(lNum); } else { Serial.println(); } } else { Serial.println("Undefined Error"); } } void ProgLines::loadTest() { newProgram(); for (int i = 0; i < (sizeof(testProg) / sizeof(char*)); i++) { addLine((char*)testProg[i]); } }