- Csym, a symbolic manipulation engine for the CE
- 15 Dec 2025 08:49:32 pm
- Last edited by claculator on 19 Dec 2025 12:09:18 pm; edited 1 time in total
I have been working on my Csym project, and after over 400 lines of code, I have created the base engine (does not include symbolic manipulation). It works pretty well, to my knowledge, and I am hoping to soon implement the symbolic functions. Here is the code:
Code:
If anyone has any suggestions on how to improve this, please tell.
Code:
#include <tice.h>
#include <graphx.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <stdio.h>
#define MAX_INPUT_LENGTH 50
#define MAX_HISTORY 10
long long intPow(long long base, long long exp) {
long long result = 1;
for (long long i = 0; i < exp; i++) {
result *= base;
}
return result;
}
// Simple expression evaluator
typedef struct {
const char* expr;
int pos;
} Parser;
double parseExpression(Parser* p);
double parseTerm(Parser* p);
double parseFactor(Parser* p);
double parsePower(Parser* p);
void skipWhitespace(Parser* p) {
while (p->expr[p->pos] == ' ') p->pos++;
}
double parseNumber(Parser* p) {
skipWhitespace(p);
double result = 0;
bool hasDecimal = false;
double decimal = 0.1;
while (isdigit(p->expr[p->pos]) || p->expr[p->pos] == '.') {
if (p->expr[p->pos] == '.') {
hasDecimal = true;
p->pos++;
continue;
}
if (!hasDecimal) {
result = result * 10 + (p->expr[p->pos] - '0');
} else {
result += (p->expr[p->pos] - '0') * decimal;
decimal *= 0.1;
}
p->pos++;
}
return result;
}
double parsePower(Parser* p) {
double result = parseFactor(p);
skipWhitespace(p);
if (p->expr[p->pos] == '^') {
p->pos++;
double exponent = parsePower(p);
// Check if both base and exponent are integers
double roundedBase = round(result);
double roundedExp = round(exponent);
if (fabs(result - roundedBase) < 1e-9 &&
fabs(exponent - roundedExp) < 1e-9 &&
roundedExp >= 0) {
// Exact integer power
result = (double)intPow((long long)roundedBase, (long long)roundedExp);
} else {
// Fall back to floating point pow()
result = pow(result, exponent);
}
}
return result;
}
double parseFactor(Parser* p) {
skipWhitespace(p);
// Handle negative numbers
if (p->expr[p->pos] == '-') {
p->pos++;
return -parseFactor(p);
}
// Handle parentheses
if (p->expr[p->pos] == '(') {
p->pos++;
double result = parseExpression(p);
skipWhitespace(p);
if (p->expr[p->pos] == ')') {
p->pos++;
}
return result;
}
// Handle functions: sin, cos, tan, log, ln
if (strncmp(&p->expr[p->pos], "sin", 3) == 0) {
p->pos += 3;
if (p->expr[p->pos] == '(') p->pos++;
double arg = parseExpression(p);
if (p->expr[p->pos] == ')') p->pos++;
return sin(arg);
}
if (strncmp(&p->expr[p->pos], "cos", 3) == 0) {
p->pos += 3;
if (p->expr[p->pos] == '(') p->pos++;
double arg = parseExpression(p);
if (p->expr[p->pos] == ')') p->pos++;
return cos(arg);
}
if (strncmp(&p->expr[p->pos], "tan", 3) == 0) {
p->pos += 3;
if (p->expr[p->pos] == '(') p->pos++;
double arg = parseExpression(p);
if (p->expr[p->pos] == ')') p->pos++;
return tan(arg);
}
if (strncmp(&p->expr[p->pos], "log", 3) == 0) {
p->pos += 3;
if (p->expr[p->pos] == '(') p->pos++;
double arg = parseExpression(p);
if (p->expr[p->pos] == ')') p->pos++;
return log10(arg);
}
if (strncmp(&p->expr[p->pos], "ln", 2) == 0) {
p->pos += 2;
if (p->expr[p->pos] == '(') p->pos++;
double arg = parseExpression(p);
if (p->expr[p->pos] == ')') p->pos++;
return log(arg);
}
if (strncmp(&p->expr[p->pos], "pi", 2) == 0) {
p->pos += 2;
return M_PI;
}
if (strncmp(&p->expr[p->pos], "e", 1) == 0) {
p->pos += 1;
return M_E;
}
// Parse number
return parseNumber(p);
}
double parseTerm(Parser* p) {
double result = parsePower(p);
while (1) {
skipWhitespace(p);
char op = p->expr[p->pos];
if (op == '*') {
p->pos++;
result *= parsePower(p);
} else if (op == '/') {
p->pos++;
double divisor = parsePower(p);
if (divisor != 0) {
result /= divisor;
} else {
return NAN; // Division by zero
}
} else {
break;
}
}
return result;
}
double parseExpression(Parser* p) {
double result = parseTerm(p);
while (1) {
skipWhitespace(p);
char op = p->expr[p->pos];
if (op == '+') {
p->pos++;
result += parseTerm(p);
} else if (op == '-') {
p->pos++;
result -= parseTerm(p);
} else {
break;
}
}
return result;
}
double evaluate(const char* expr) {
Parser p = {expr, 0};
return parseExpression(&p);
}
int main(void) {
gfx_Begin();
gfx_SetDrawBuffer();
gfx_FillScreen(255);
gfx_SetTextBGColor(255);
gfx_SetTextScale(2,2);
const char* text = "Csym Pre-Alpha";
const char* promptText = "PRESS ENTER";
int textWidth = gfx_GetStringWidth(text);
int y = 0;
int ySpeed = 1;
int x = (LCD_WIDTH - textWidth) / 2;
int promptX = (120);
int promptY = LCD_HEIGHT - 30;
int flickerCounter = 0;
bool showPrompt = true;
while (1) {
gfx_FillScreen(255);
gfx_SetTextFGColor(6);
gfx_SetTextScale(2, 2);
gfx_PrintStringXY(text, x, y);
if (showPrompt) {
gfx_SetTextFGColor(0);
gfx_SetTextScale(1,1);
gfx_PrintStringXY(promptText, promptX, promptY);
}
gfx_SwapDraw();
y += ySpeed;
if (y >= 60) {
y = 60;
ySpeed = -1;
} else if (y <= 0) {
y = 0;
ySpeed = 1;
}
flickerCounter++;
if (flickerCounter >= 30) {
showPrompt = !showPrompt;
flickerCounter = 0;
}
if (os_GetCSC() == sk_Enter) {
break;
}
delay(10);
}
char inputBuffer[MAX_INPUT_LENGTH + 1] = {0};
char historyExpr[MAX_HISTORY][MAX_INPUT_LENGTH + 1] = {0};
char historyResult[MAX_HISTORY][MAX_INPUT_LENGTH + 1] = {0};
int historyCount = 0;
int cursorPos = 0;
sk_key_t key;
bool running = true;
bool alphaMode = false;
bool secondMode = false;
while (running) {
// Clear screen
gfx_FillScreen(255);
// Draw prompt
gfx_SetTextFGColor(0);
gfx_SetTextScale(1, 1);
gfx_PrintStringXY("Csym Pre-Alpha", 10, 10);
// Draw Alpha text
if (alphaMode) {
gfx_SetTextFGColor(6);
gfx_PrintStringXY("A", 200, 10);
}
// Draw 2nd Text
if (secondMode) {
gfx_SetTextFGColor(8);
gfx_PrintStringXY("2nd",210,10);
}
// Draw input text
gfx_SetTextFGColor(0);
gfx_SetTextScale(1, 1);
gfx_PrintStringXY(inputBuffer, 10, 40);
// Draw cursor (blinking underscore)
if ((timer_1_Counter / 20000) % 2) {
gfx_PrintStringXY("_", 10 + gfx_GetStringWidth(inputBuffer), 40);
}
// Draw history - expressions on left, results on right
gfx_SetTextScale(1, 1);
gfx_SetTextFGColor(0);
int historyY = 60;
for (int i = 0; i < historyCount && i < MAX_HISTORY; i++) {
// Draw expression on left
gfx_PrintStringXY(historyExpr[i], 10, historyY);
// Draw result on right
gfx_PrintStringXY(historyResult[i], LCD_WIDTH - gfx_GetStringWidth(historyResult[i]) - 10, historyY);
historyY += 15;
}
gfx_SwapDraw();
key = os_GetCSC();
if (key) {
if (boot_CheckOnPressed()) {
break;
}
if (key == sk_Enter) {
// Save to history if not empty
if (inputBuffer[0] != '\0' && historyCount < MAX_HISTORY) {
// Save expression
strcpy(historyExpr[historyCount], inputBuffer);
// Evaluate and save result
double result = evaluate(inputBuffer);
if (isnan(result)) {
strcpy(historyResult[historyCount], "Error");
} else if (isinf(result)) {
strcpy(historyResult[historyCount], "Infinity");
} else {
// Format result (remove trailing zeros)
sprintf(historyResult[historyCount], "%.12f", result);
// Remove trailing zeros
int len = strlen(historyResult[historyCount]);
while (len > 0 && historyResult[historyCount][len-1] == '0') {
historyResult[historyCount][len-1] = '\0';
len--;
}
// Remove trailing decimal point
if (len > 0 && historyResult[historyCount][len-1] == '.') {
historyResult[historyCount][len-1] = '\0';
}
}
historyCount++;
}
// Clear input buffer
inputBuffer[0] = '\0';
cursorPos = 0;
} else if (key == sk_Alpha) {
alphaMode = !alphaMode;
} else if (key == sk_2nd) {
secondMode = !secondMode;
} else if (key == sk_Clear) {
inputBuffer[0] = '\0';
cursorPos = 0;
} else if (key == sk_Del && cursorPos > 0) {
cursorPos--;
inputBuffer[cursorPos] = '\0';
} else if (cursorPos < MAX_INPUT_LENGTH) {
// Map keys to characters
char ch = '\0';
// 2nd Definitions
if (secondMode) {
if (key == sk_Square) {
if (cursorPos + 6 <= MAX_INPUT_LENGTH) {
inputBuffer[cursorPos++] = '^';
inputBuffer[cursorPos++] = '(';
inputBuffer[cursorPos++] = '1';
inputBuffer[cursorPos++] = '/';
inputBuffer[cursorPos++] = '2';
inputBuffer[cursorPos++] = ')';
inputBuffer[cursorPos] = '\0';
secondMode = false;
}
}
else if (key == sk_Power) {
if (cursorPos + 2 <= MAX_INPUT_LENGTH) {
inputBuffer[cursorPos++] = 'p';
inputBuffer[cursorPos++] = 'i';
inputBuffer[cursorPos] = '\0';
secondMode = false;
}
}
else if (key == sk_Div) {
if (cursorPos + 1 <= MAX_INPUT_LENGTH) {
inputBuffer[cursorPos++] = 'e';
inputBuffer[cursorPos] = '\0';
secondMode = false;
}
}
}
// ALPHA Definitions
else if (alphaMode) {
// Alpha mode - letters
if (key == sk_Math) ch = 'A';
else if (key == sk_Apps) ch = 'B';
else if (key == sk_Prgm) ch = 'C';
else if (key == sk_Recip) ch = 'D';
else if (key == sk_Sin) ch = 'E';
else if (key == sk_Cos) ch = 'F';
else if (key == sk_Tan) ch = 'G';
else if (key == sk_Power) ch = 'H';
else if (key == sk_Square) ch = 'I';
else if (key == sk_Comma) ch = 'J';
else if (key == sk_LParen) ch = 'K';
else if (key == sk_RParen) ch = 'L';
else if (key == sk_Div) ch = 'M';
else if (key == sk_Log) ch = 'N';
else if (key == sk_7) ch = 'O';
else if (key == sk_8) ch = 'P';
else if (key == sk_9) ch = 'Q';
else if (key == sk_Mul) ch = 'R';
else if (key == sk_Ln) ch = 'S';
else if (key == sk_4) ch = 'T';
else if (key == sk_5) ch = 'U';
else if (key == sk_6) ch = 'V';
else if (key == sk_Sub) ch = 'W';
else if (key == sk_Store) ch = 'X';
else if (key == sk_1) ch = 'Y';
else if (key == sk_2) ch = 'Z';
else if (key == sk_0) ch = ' ';
else if (key == sk_DecPnt) ch = ':';
else if (key == sk_Add) ch = '"';
// Add character to buffer if one was set
if (ch != '\0') {
inputBuffer[cursorPos] = ch;
cursorPos++;
inputBuffer[cursorPos] = '\0';
// Auto-disable alpha after letter entry (but not for space)
if (ch != ' ') {
alphaMode = false;
}
}
}
else {
// Normal mode - Numbers and operators
if (key == sk_0) ch = '0';
else if (key == sk_1) ch = '1';
else if (key == sk_2) ch = '2';
else if (key == sk_3) ch = '3';
else if (key == sk_4) ch = '4';
else if (key == sk_5) ch = '5';
else if (key == sk_6) ch = '6';
else if (key == sk_7) ch = '7';
else if (key == sk_8) ch = '8';
else if (key == sk_9) ch = '9';
// Common symbols
else if (key == sk_Add) ch = '+';
else if (key == sk_Sub) ch = '-';
else if (key == sk_Mul) ch = '*';
else if (key == sk_Div) ch = '/';
else if (key == sk_LParen) ch = '(';
else if (key == sk_RParen) ch = ')';
else if (key == sk_Power) ch = '^';
else if (key == sk_DecPnt) ch = '.';
else if (key == sk_Chs) ch = '-';
else if (key == sk_Comma) ch = ',';
else if (key == sk_GraphVar) ch = 'X';
else if (key == sk_Store) ch = '=';
else if (key == sk_Square) {
if (cursorPos + 2 <= MAX_INPUT_LENGTH) {
inputBuffer[cursorPos++] = '^';
inputBuffer[cursorPos++] = '2';
inputBuffer[cursorPos] = '\0';
}
}
else if (key == sk_Recip) {
if (cursorPos + 3 <= MAX_INPUT_LENGTH) {
inputBuffer[cursorPos++] = '^';
inputBuffer[cursorPos++] = '-';
inputBuffer[cursorPos++] = '1';
inputBuffer[cursorPos] = '\0';
}
}
else if (key == sk_Sin) {
if (cursorPos + 4 <= MAX_INPUT_LENGTH) {
inputBuffer[cursorPos++] = 's';
inputBuffer[cursorPos++] = 'i';
inputBuffer[cursorPos++] = 'n';
inputBuffer[cursorPos++] = '(';
inputBuffer[cursorPos] = '\0';
}
}
else if (key == sk_Cos) {
if (cursorPos + 4 <= MAX_INPUT_LENGTH) {
inputBuffer[cursorPos++] = 'c';
inputBuffer[cursorPos++] = 'o';
inputBuffer[cursorPos++] = 's';
inputBuffer[cursorPos++] = '(';
inputBuffer[cursorPos] = '\0';
}
}
else if (key == sk_Tan) {
if (cursorPos + 4 <= MAX_INPUT_LENGTH) {
inputBuffer[cursorPos++] = 't';
inputBuffer[cursorPos++] = 'a';
inputBuffer[cursorPos++] = 'n';
inputBuffer[cursorPos++] = '(';
inputBuffer[cursorPos] = '\0';
}
}
else if (key == sk_Log) {
if (cursorPos + 4 <= MAX_INPUT_LENGTH) {
inputBuffer[cursorPos++] = 'l';
inputBuffer[cursorPos++] = 'o';
inputBuffer[cursorPos++] = 'g';
inputBuffer[cursorPos++] = '(';
inputBuffer[cursorPos] = '\0';
}
}
else if (key == sk_Ln) {
if (cursorPos + 3 <= MAX_INPUT_LENGTH) {
inputBuffer[cursorPos++] = 'l';
inputBuffer[cursorPos++] = 'n';
inputBuffer[cursorPos++] = '(';
inputBuffer[cursorPos] = '\0';
}
}
// Add single character to buffer if one was set
if (ch != '\0') {
inputBuffer[cursorPos] = ch;
cursorPos++;
inputBuffer[cursorPos] = '\0';
}
}
// Wait for key release
while (os_GetCSC());
}
delay(10);
}
}
gfx_End();
return 0;
}If anyone has any suggestions on how to improve this, please tell.






