#include <Arduino.h>
#include <math.h>
// --- Configuration ---
#define STACK_SIZE 100
#define MAX_PROGRAM_LINES 250
#define MAX_LOOP_DEPTH 8
// --- Constants ---
#define PI_CONST 3.141592653589793
#define E_CONST 2.718281828459045
// =================================================================
// Global Variables for BASIC Interpreter
// =================================================================
double variableValues[26];
String stringVariableValues[26]; // NEW: Storage for string variables
String programLines[MAX_PROGRAM_LINES];
int lineNumbers[MAX_PROGRAM_LINES];
int programSize = 0;
enum ProgramState { IDLE, RUNNING, AWAIT_INPUT };
ProgramState programState = IDLE;
int pc = 0;
int awaitingInputForVarIndex = -1;
bool isAwaitingString = false;
struct ForLoopState {
int variableIndex;
double endValue;
double stepValue;
int returnAddress;
};
ForLoopState forLoopStack[MAX_LOOP_DEPTH];
int forLoopStackPtr = -1;
// --- Forward Declarations & Helper Functions ---
void newProgram();
void listProgram();
void startProgram();
bool executeLine(String line, bool isConditional);
void reportError(String message, int lineNumber);
void processInput(String input);
String evaluateStringExpression(String expr); // NEW: For string manipulation
bool isInteger(String str);
bool isVariable(char c) {
char uc = toupper(c);
return uc >= 'A' && uc <= 'Z';
}
int getVariableIndex(char c) {
if (!isVariable(c)) return -1;
return toupper(c) - 'A';
}
// =================================================================
// Stacks & Calculator Class
// =================================================================
class CharStack {
public:
CharStack(int s = STACK_SIZE) { maxSize = s; stackArray = new String[maxSize]; top = -1; }
~CharStack() { delete[] stackArray; }
void push(String v) { if (!isFull()) stackArray[++top] = v; }
String pop() { return !isEmpty() ? stackArray[top--] : ""; }
String peek() { return !isEmpty() ? stackArray[top] : ""; }
bool isEmpty() { return top == -1; }
bool isFull() { return top == maxSize - 1; }
private:
String* stackArray; int top, maxSize;
};
class FloatStack {
public:
FloatStack(int s = STACK_SIZE) { maxSize = s; stackArray = new double[maxSize]; top = -1; }
~FloatStack() { delete[] stackArray; }
void push(double v) { if (!isFull()) stackArray[++top] = v; }
double pop() { return !isEmpty() ? stackArray[top--] : NAN; }
private:
double* stackArray; int top, maxSize; bool isEmpty() { return top == -1; } bool isFull() { return top == maxSize - 1; }
};
class Calculator {
public:
String toPostfix(String& i);
double evaluatePostfix(String p, bool isRad);
private:
int getPrecedence(String o);
bool isFunc(String t);
void handleUnary(String& i);
double power(double b, double e);
};
void Calculator::handleUnary(String& i) {
i.replace(" ", "");
i.replace("(+", "("); i.replace("*+", "*"); i.replace("/+", "/");
i.replace("++", "+"); i.replace("-+", "-"); i.replace("%+", "%"); i.replace("^+", "^");
if (i.startsWith("+")) i = i.substring(1);
if (i.startsWith("-")) i = "0" + i;
i.replace("(-", "(0-");
}
int Calculator::getPrecedence(String o) {
if (o == "!") return 5;
if (isFunc(o)) return 4;
if (o == "^") return 3;
if (o == "*" || o == "/" || o == "%") return 2;
if (o == "+" || o == "-") return 1;
return 0;
}
bool Calculator::isFunc(String t) {
const char* f[] = {"SIN", "COS", "TAN", "ASIN", "ACOS", "ATAN", "SIND", "COSD", "TAND", "SINH", "COSH", "TANH", "ASINH", "ACOSH", "ATANH", "LN", "LOG", "EXP", "SQRT", "LEN", "VAL", "ASC", "CHR$", "STR$", "LEFT$", "RIGHT$", "MID$"};
for (const char* i : f) {
if (t.equalsIgnoreCase(i)) return true;
}
return false;
}
void resolveStringFunctions(String& expr) {
int pos = -1;
String exprUpper = expr;
exprUpper.toUpperCase();
while ((pos = exprUpper.indexOf("LEN(")) != -1 || (pos = exprUpper.indexOf("VAL(")) != -1 || (pos = exprUpper.indexOf("ASC(")) != -1) {
int startPos = pos;
pos += 4;
int parenCount = 1;
int endPos = -1;
for (int i = pos; i < expr.length(); i++) {
if (expr.charAt(i) == '(') parenCount++;
if (expr.charAt(i) == ')') parenCount--;
if (parenCount == 0) { endPos = i; break; }
}
if (endPos == -1) { expr = "ERROR:Mismatched parens"; return; }
String arg = expr.substring(pos, endPos);
String funcName = exprUpper.substring(startPos, startPos + 3);
String resultStr;
if (funcName == "LEN") resultStr = String(evaluateStringExpression(arg).length());
else if (funcName == "VAL") resultStr = String(evaluateStringExpression(arg).toDouble());
expr = expr.substring(0, startPos) + resultStr + expr.substring(endPos + 1);
exprUpper = expr;
exprUpper.toUpperCase();
}
}
String Calculator::toPostfix(String& i) {
resolveStringFunctions(i);
handleUnary(i);
String p = "";
CharStack o;
bool lastTokenWasNumber = false;
for (int k = 0; k < i.length(); ) {
char c = i.charAt(k);
if (lastTokenWasNumber && (isDigit(c) || isAlpha(c) || c == '(' || (uint8_t)c == 0xCF)) {
while (!o.isEmpty() && o.peek() != "(" && getPrecedence("*") <= getPrecedence(o.peek())) { p += o.pop() + " "; }
o.push("*");
}
if (isDigit(c) || c == '.') { String n = ""; while (k < i.length() && (isDigit(i.charAt(k)) || i.charAt(k) == '.')) n += i.charAt(k++); p += n + " "; lastTokenWasNumber = true; continue; }
if ((uint8_t)c == 0xCF && k + 1 < i.length() && (uint8_t)i.charAt(k + 1) == 0x80) { p += "pi "; k += 2; lastTokenWasNumber = true; continue; }
if (isAlpha(c)) { String token = ""; while (k < i.length() && isAlpha(i.charAt(k))) token += i.charAt(k++); String upperToken = token; upperToken.toUpperCase(); if (isFunc(upperToken)) { o.push(upperToken); lastTokenWasNumber = false; } else if (upperToken == "PI") { p += "pi "; lastTokenWasNumber = true; } else if (upperToken == "E") { p += "e "; lastTokenWasNumber = true; } else if (upperToken.length() == 1 && isVariable(upperToken.charAt(0))) { p += upperToken + " "; lastTokenWasNumber = true; } continue; }
if (c == '(') { o.push("("); k++; lastTokenWasNumber = false; continue; }
if (c == ')') { while (!o.isEmpty() && o.peek() != "(") p += o.pop() + " "; if (o.isEmpty()) return "ERROR:Unbalanced parentheses"; o.pop(); if (!o.isEmpty() && isFunc(o.peek())) p += o.pop() + " "; k++; lastTokenWasNumber = true; continue; }
if (String("+-*/%^!").indexOf(c) != -1) { String o1 = String(c); while (!o.isEmpty() && o.peek() != "(" && getPrecedence(o1) <= getPrecedence(o.peek())) p += o.pop() + " "; o.push(o1); k++; lastTokenWasNumber = false; continue; }
k++;
}
while (!o.isEmpty()) { if (o.peek() == "(") return "ERROR:Unbalanced parentheses"; p += o.pop() + " "; }
p.trim();
return p;
}
double Calculator::evaluatePostfix(String p, bool r) {
if (p.startsWith("ERROR:")) return NAN;
FloatStack v;
int c = 0;
while (c < p.length()) {
int n = p.indexOf(' ', c); if (n == -1) n = p.length();
String t = p.substring(c, n); c = n + 1;
if (t.length() == 0) continue;
if (isDigit(t.charAt(0)) || (t.charAt(0) == '-' && t.length() > 1)) v.push(t.toDouble());
else if (t.equalsIgnoreCase("pi")) v.push(PI_CONST);
else if (t.equalsIgnoreCase("e")) v.push(E_CONST);
else if (t.length() == 1 && isVariable(t.charAt(0))) v.push(variableValues[getVariableIndex(t.charAt(0))]);
else if (isFunc(t)) {
double val = v.pop(), res = 0; String ut = t; ut.toUpperCase();
if (ut == "SIN") res = r ? sin(val) : sin(val * PI_CONST / 180.0); else if (ut == "COS") res = r ? cos(val) : cos(val * PI_CONST / 180.0); else if (ut == "TAN") res = r ? tan(val) : tan(val * PI_CONST / 180.0); else if (ut == "ASIN") res = r ? asin(val) : asin(val) * 180.0 / PI_CONST; else if (ut == "ACOS") res = r ? acos(val) : acos(val) * 180.0 / PI_CONST; else if (ut == "ATAN") res = r ? atan(val) : atan(val) * 180.0 / PI_CONST; else if (ut == "SIND") res = sin(val * PI_CONST / 180.0); else if (ut == "COSD") res = cos(val * PI_CONST / 180.0); else if (ut == "TAND") res = tan(val * PI_CONST / 180.0); else if (ut == "SINH") res = sinh(val); else if (ut == "COSH") res = cosh(val); else if (ut == "TANH") res = tanh(val); else if (ut == "ASINH") res = log(val + sqrt(val * val + 1.0)); else if (ut == "ACOSH") res = log(val + sqrt(val * val - 1.0)); else if (ut == "ATANH") res = 0.5 * log((1.0 + val) / (1.0 - val)); else if (ut == "LN") res = log(val); else if (ut == "LOG") res = log10(val); else if (ut == "EXP") res = exp(val); else if (ut == "SQRT") res = sqrt(val);
v.push(res);
} else if (t == "!") {
double val = v.pop(); if (val < 0 || floor(val) != val) { v.push(NAN); } else { double result = 1.0; for (double i = 1; i <= val; i++) { result *= i; } v.push(result); }
} else {
double v2 = v.pop(), v1 = v.pop();
if (t == "+") v.push(v1 + v2); else if (t == "-") v.push(v1 - v2); else if (t == "*") v.push(v1 * v2); else if (t == "/") v.push(v1 / v2); else if (t == "%") v.push(fmod(v1, v2)); else if (t == "^") v.push(power(v1, v2));
}
}
return v.pop();
}
double Calculator::power(double b, double e) { if (e == 0) return 1; if (b == 0) return 0; if (floor(e) == e && e > 0) { long i_e = (long)e; double r = 1.0; while (i_e > 0) { if (i_e % 2 == 1) r *= b; b *= b; i_e /= 2; } return r; } return pow(b, e); }
Calculator calculator;
bool isRadiansMode = true;
// =================================================================
// BASIC Interpreter Implementation
// =================================================================
String evaluateStringExpression(String expr) {
expr.trim();
if (expr.startsWith("\"") && expr.endsWith("\"")) return expr.substring(1, expr.length() - 1);
if (expr.endsWith("$") && isVariable(expr.charAt(0))) return stringVariableValues[getVariableIndex(expr.charAt(0))];
int plusPos = expr.indexOf('+');
if (plusPos != -1) {
String lhs = evaluateStringExpression(expr.substring(0, plusPos));
String rhs = evaluateStringExpression(expr.substring(plusPos + 1));
return lhs + rhs;
}
String upperExpr = expr; upperExpr.toUpperCase();
if (upperExpr.startsWith("STR$(")) { String arg = expr.substring(4, expr.length() - 1); double num = calculator.evaluatePostfix(calculator.toPostfix(arg), isRadiansMode); return String(num, 15); }
if (upperExpr.startsWith("CHR$(")) { String arg = expr.substring(4, expr.length() - 1); int ascii = calculator.evaluatePostfix(calculator.toPostfix(arg), isRadiansMode); return String((char)ascii); }
if (upperExpr.startsWith("LEFT$(") || upperExpr.startsWith("RIGHT$(") || upperExpr.startsWith("MID$(")) {
int firstComma = expr.indexOf(',');
if (firstComma != -1) {
String strArg = expr.substring(5, firstComma);
String restArgs = expr.substring(firstComma + 1, expr.length() - 1);
String sourceStr = evaluateStringExpression(strArg);
int secondComma = restArgs.indexOf(',');
if (upperExpr.startsWith("MID$(") && secondComma != -1) {
String startArg = restArgs.substring(0, secondComma); // <-- BUG FIX
String lenArg = restArgs.substring(secondComma + 1); // <-- BUG FIX
int start = calculator.evaluatePostfix(calculator.toPostfix(startArg), isRadiansMode);
int len = calculator.evaluatePostfix(calculator.toPostfix(lenArg), isRadiansMode);
return sourceStr.substring(start - 1, start - 1 + len);
} else {
String lenArg = restArgs; // <-- BUG FIX
int len = calculator.evaluatePostfix(calculator.toPostfix(lenArg), isRadiansMode);
if (upperExpr.startsWith("LEFT$")) return sourceStr.substring(0, len);
if (upperExpr.startsWith("RIGHT$")) return sourceStr.substring(sourceStr.length() - len);
}
}
}
return "";
}
void reportError(String message, int lineNumber) { programState = IDLE; Serial.print("\nError"); if (lineNumber > 0) { Serial.print(" in line "); Serial.print(lineNumber); } Serial.print(": "); Serial.println(message); }
int findLineIndex(int lineNum) { for (int i = 0; i < programSize; i++) { if (lineNumbers[i] == lineNum) return i; } return -1; }
void storeLine(String line) { int spacePos = line.indexOf(' '); if (spacePos == -1 && line.toInt() > 0) { spacePos = line.length(); } else if (spacePos == -1) { return; } int lineNum = line.substring(0, spacePos).toInt(); if (lineNum == 0) return; String code = line.substring(spacePos); code.trim(); int existingIndex = findLineIndex(lineNum); if (existingIndex != -1) { if (code.length() == 0) { programSize--; for (int i = existingIndex; i < programSize; i++) { lineNumbers[i] = lineNumbers[i + 1]; programLines[i] = programLines[i + 1]; } } else { programLines[existingIndex] = code; } } else { if (code.length() == 0) return; if (programSize >= MAX_PROGRAM_LINES) { Serial.println("Program memory full!"); return; } int insertPos = 0; while (insertPos < programSize && lineNumbers[insertPos] < lineNum) insertPos++; for (int i = programSize; i > insertPos; i--) { lineNumbers[i] = lineNumbers[i - 1]; programLines[i] = programLines[i - 1]; } lineNumbers[insertPos] = lineNum; programLines[insertPos] = code; programSize++; } }
void newProgram() { programSize = 0; for (int i = 0; i < 26; i++) { variableValues[i] = 0; stringVariableValues[i] = ""; } programState = IDLE; forLoopStackPtr = -1; Serial.println("OK"); }
void listProgram() { for (int i = 0; i < programSize; i++) { Serial.print(lineNumbers[i]); Serial.print(" "); Serial.println(programLines[i]); } }
void startProgram() { if (programSize > 0) { pc = 0; programState = RUNNING; forLoopStackPtr = -1; } }
bool isInteger(String str) { if (str.length() == 0) return false; for (unsigned int i = 0; i < str.length(); i++) { if (!isDigit(str.charAt(i))) return false; } return true; }
bool executeLine(String line, bool isConditional) {
String command, args; int firstSpace = line.indexOf(' '); if (firstSpace != -1) { command = line.substring(0, firstSpace); args = line.substring(firstSpace + 1); } else { command = line; args = ""; } command.toUpperCase(); args.trim();
int currentLineNum = isConditional ? -1 : lineNumbers[pc];
if (command == "REM") { return false; }
if (command == "END") { programState = IDLE; return true; }
if (command == "PRINT") { while (args.length() > 0) { int semiPos = args.indexOf(';'); String token; if (semiPos != -1) { token = args.substring(0, semiPos); args = args.substring(semiPos + 1); } else { token = args; args = ""; } token.trim(); if (token.startsWith("\"") || token.endsWith("$")) { Serial.print(evaluateStringExpression(token)); } else if (token.length() > 0) { String postfix = calculator.toPostfix(token); if (postfix.startsWith("ERROR:")) { reportError(postfix.substring(6), currentLineNum); return true; } double result = calculator.evaluatePostfix(postfix, isRadiansMode); if (isnan(result)) { reportError("Mathematical error", currentLineNum); return true; } Serial.print(result, 15); } } Serial.println(); return false; }
if (command == "INPUT") { awaitingInputForVarIndex = getVariableIndex(args.charAt(0)); isAwaitingString = args.endsWith("$"); if (awaitingInputForVarIndex != -1) { programState = AWAIT_INPUT; Serial.print("? "); } else { reportError("Invalid variable for INPUT", currentLineNum); } return true; }
if (command == "GOTO") { int newPc = findLineIndex(args.toInt()); if (newPc != -1) { pc = newPc; } else { reportError("GOTO target line not found", currentLineNum); } return true; }
if (command == "IF") {
String uArgs = args; uArgs.toUpperCase(); int thenPos = uArgs.indexOf("THEN"); if (thenPos == -1) { reportError("IF without THEN", currentLineNum); return true; }
String conditionStr = args.substring(0, thenPos); String op; int opPos = -1;
if ((opPos = conditionStr.indexOf("<>")) != -1) op = "<>"; else if ((opPos = conditionStr.indexOf("!=")) != -1) op = "!="; else if ((opPos = conditionStr.indexOf("<=")) != -1) op = "<="; else if ((opPos = conditionStr.indexOf(">=")) != -1) op = ">="; else if ((opPos = conditionStr.indexOf('<')) != -1) op = "<"; else if ((opPos = conditionStr.indexOf('>')) != -1) op = ">"; else if ((opPos = conditionStr.indexOf('=')) != -1) op = "=";
if (opPos == -1) { reportError("Invalid operator in IF", currentLineNum); return true; }
String lhsStr = conditionStr.substring(0, opPos); lhsStr.trim(); // <-- BUG FIX
String rhsStr = conditionStr.substring(opPos + op.length()); rhsStr.trim(); // <-- BUG FIX
bool conditionMet = false;
if (lhsStr.endsWith("$") || rhsStr.startsWith("\"")) { String lhsVal = evaluateStringExpression(lhsStr); String rhsVal = evaluateStringExpression(rhsStr); if (op == "=") conditionMet = (lhsVal == rhsVal); else if (op == "<>" || op == "!=") conditionMet = (lhsVal != rhsVal); }
else { double lhs = calculator.evaluatePostfix(calculator.toPostfix(lhsStr), isRadiansMode); double rhs = calculator.evaluatePostfix(calculator.toPostfix(rhsStr), isRadiansMode); if (op == "<") conditionMet = lhs < rhs; else if (op == ">") conditionMet = lhs > rhs; else if (op == "<=") conditionMet = lhs <= rhs; else if (op == ">=") conditionMet = lhs >= rhs; else if (op == "=") conditionMet = lhs == rhs; else if (op == "<>" || op == "!=") conditionMet = lhs != rhs; }
if (conditionMet) { String actionStr = args.substring(thenPos + 4); actionStr.trim(); if (isInteger(actionStr)) { int newPc = findLineIndex(actionStr.toInt()); if (newPc != -1) { pc = newPc; } else { reportError("IF target line not found", currentLineNum); } return true; } else { return executeLine(actionStr, true); } }
return false;
}
if (command == "FOR") { String uArgs = args; uArgs.toUpperCase(); int eqPos = uArgs.indexOf('='); int toPos = uArgs.indexOf("TO"); int stepPos = uArgs.indexOf("STEP"); if (eqPos == -1 || toPos == -1) { reportError("Invalid FOR syntax", currentLineNum); return true; } int varIdx = getVariableIndex(uArgs.charAt(0)); String startExpr = args.substring(eqPos + 1, toPos); String endExpr, stepExpr; if (stepPos != -1) { endExpr = args.substring(toPos + 2, stepPos); stepExpr = args.substring(stepPos + 4); } else { endExpr = args.substring(toPos + 2); stepExpr = "1"; } if (forLoopStackPtr >= MAX_LOOP_DEPTH - 1) { reportError("Loop depth exceeded", currentLineNum); return true; } forLoopStackPtr++; forLoopStack[forLoopStackPtr].variableIndex = varIdx; forLoopStack[forLoopStackPtr].endValue = calculator.evaluatePostfix(calculator.toPostfix(endExpr), isRadiansMode); forLoopStack[forLoopStackPtr].stepValue = calculator.evaluatePostfix(calculator.toPostfix(stepExpr), isRadiansMode); forLoopStack[forLoopStackPtr].returnAddress = pc; variableValues[varIdx] = calculator.evaluatePostfix(calculator.toPostfix(startExpr), isRadiansMode); return false; }
if (command == "NEXT") { int varIdx = getVariableIndex(args.charAt(0)); if (forLoopStackPtr < 0 || forLoopStack[forLoopStackPtr].variableIndex != varIdx) { reportError("NEXT without FOR", currentLineNum); return true; } variableValues[varIdx] += forLoopStack[forLoopStackPtr].stepValue; bool loopContinues = (forLoopStack[forLoopStackPtr].stepValue >= 0) ? (variableValues[varIdx] <= forLoopStack[forLoopStackPtr].endValue + 0.0001) : (variableValues[varIdx] >= forLoopStack[forLoopStackPtr].endValue - 0.0001); if (loopContinues) { pc = forLoopStack[forLoopStackPtr].returnAddress + 1; return true; } else { forLoopStackPtr--; return false; } }
String uLine = line; uLine.toUpperCase(); if (uLine.startsWith("LET ")) { line = line.substring(4); } int eqPos = line.indexOf('='); if (eqPos != -1 && isVariable(line.charAt(0))) { int varIdx = getVariableIndex(line.charAt(0)); String expression = line.substring(eqPos + 1); if (line.substring(eqPos - 1, eqPos) == "$") { stringVariableValues[varIdx] = evaluateStringExpression(expression); } else { String postfix = calculator.toPostfix(expression); if (postfix.startsWith("ERROR:")) { reportError(postfix.substring(6), currentLineNum); return true; } double result = calculator.evaluatePostfix(postfix, isRadiansMode); if (isnan(result)) { reportError("Mathematical error", currentLineNum); return true; } variableValues[varIdx] = result; } } else { reportError("Syntax error", currentLineNum); return true; } return false;
}
void processInput(String input) {
input.trim();
if (input.length() > 0) {
String firstToken = input; int spacePos = input.indexOf(' '); if (spacePos != -1) firstToken = input.substring(0, spacePos);
if (isInteger(firstToken)) { storeLine(input); }
else if (firstToken.equalsIgnoreCase("RUN")) { startProgram(); }
else if (firstToken.equalsIgnoreCase("LIST")) { listProgram(); }
else if (firstToken.equalsIgnoreCase("NEW")) { newProgram(); }
else if (firstToken.equalsIgnoreCase("BREAK")) {}
else {
String command = firstToken; command.toUpperCase();
if (command == "PRINT" || command == "IF" || command == "GOTO" || command == "FOR" || command == "NEXT" || isVariable(command.charAt(0)) || input.indexOf('$') != -1) {
executeLine(input, true);
} else {
String postfix = calculator.toPostfix(input);
if (postfix.startsWith("ERROR:")) { reportError(postfix.substring(6), -1); }
else { double res = calculator.evaluatePostfix(postfix, isRadiansMode); if (isnan(res)) { reportError("Mathematical error", -1); } else { Serial.println(res, 15); } }
}
}
}
}
// =================================================================
// Main Setup and Loop
// =================================================================
void setup() {
Serial.begin(9600);
while (!Serial) { ; }
newProgram();
Serial.println("\n--- Arduino BASIC Scientific Calculator ---");
Serial.println("Commands: RUN, LIST, NEW, LET, PRINT, INPUT, GOTO, IF/THEN, FOR/NEXT, REM, END");
Serial.println("String Functions: LEN, VAL, STR$, CHR$, LEFT$, RIGHT$, MID$");
Serial.println("Constants: e, pi, \xcf\x80");
Serial.println("To stop a running program, type BREAK and press Enter");
Serial.println("-------------------------------------");
Serial.print("Select angle mode - (r)adians or (d)egrees: ");
while (Serial.available() == 0) {}
char mode = Serial.read(); while (Serial.available() > 0) Serial.read();
if (mode == 'd' || mode == 'D') { isRadiansMode = false; Serial.println("Degree Mode."); }
else { isRadiansMode = true; Serial.println("Radian Mode."); }
Serial.println("-------------------------------------");
Serial.print("> ");
}
String readNextCommand() {
String cmd = "";
while (true) {
while (Serial.available() == 0) { delay(1); } // Small delay to prevent busy-waiting
char c = Serial.read();
if (c == '\n' || c == ',') { Serial.println(); cmd.trim(); return cmd; }
if (c == '\r') {}
else if (c == 8 || c == 127) { if (cmd.length() > 0) { cmd.remove(cmd.length() - 1); Serial.print("\b \b"); } }
else if (c >= 32) { cmd += c; Serial.print(c); }
}
}
void loop() {
if (programState == RUNNING) {
if (Serial.available() > 0) {
String input = Serial.readStringUntil('\n'); input.trim(); Serial.println(input);
if (input.equalsIgnoreCase("BREAK")) {
programState = IDLE;
Serial.println("\n*** PROGRAM HALTED BY USER ***");
while (Serial.available() > 0) Serial.read();
}
}
if (programState == RUNNING) {
if (pc >= programSize) {
programState = IDLE;
if (forLoopStackPtr != -1) reportError("FOR without NEXT", -1);
} else {
bool jumped = executeLine(programLines[pc], false);
if (!jumped) { pc++; }
}
}
if (programState == IDLE) { Serial.print("> "); }
} else { // IDLE or AWAIT_INPUT
String input = readNextCommand();
if (programState == AWAIT_INPUT) {
if (awaitingInputForVarIndex != -1) {
if (isAwaitingString) stringVariableValues[awaitingInputForVarIndex] = input;
else variableValues[awaitingInputForVarIndex] = input.toDouble();
}
programState = RUNNING;
pc++;
} else {
processInput(input);
Serial.print("> ");
}
}
}