Last active
February 19, 2025 02:32
-
-
Save haseeb-heaven/129dcdc8c2d58ae3663502ba27f53259 to your computer and use it in GitHub Desktop.
A terminal-based calculator program written in C that simulates a physical calculator. Uses C with classes, handles errors safely, and shows a colorful number pad interface. Supports basic math operations with high decimal precision.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <setjmp.h> | |
#include <stdbool.h> | |
#include <string.h> | |
#include <stdint.h> | |
#ifdef _WIN32 | |
#include <conio.h> | |
void clear_screen() { system("cls"); } | |
#else | |
#include <termios.h> | |
#include <unistd.h> | |
void clear_screen() { system("clear"); } | |
int getch(void) { | |
struct termios oldt, newt; | |
int ch; | |
tcgetattr(STDIN_FILENO, &oldt); | |
newt = oldt; | |
newt.c_lflag &= ~(ICANON | ECHO); | |
tcsetattr(STDIN_FILENO, TCSANOW, &newt); | |
ch = getchar(); | |
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); | |
return ch; | |
} | |
#endif | |
// Extended error codes | |
#define DIVISION_BY_ZERO_ERROR 1 | |
#define GENERAL_ERROR 2 | |
#define INVALID_EXPRESSION_ERROR 3 | |
#define UNKNOWN_OPERATOR_ERROR 4 | |
#define BUFFER_OVERFLOW_ERROR 5 | |
// Try/catch macros using constants: | |
static jmp_buf* __current_jmp_buf = NULL; | |
#define try do { jmp_buf ex_buf__; __current_jmp_buf = &ex_buf__; int exception = setjmp(ex_buf__); if (exception == 0) { | |
#define catch_division(x) } else if (exception == DIVISION_BY_ZERO_ERROR) { (void)(x); | |
#define catch_all(x) } else { (void)(x); | |
#define endtry } __current_jmp_buf = NULL; } while(0) | |
#define throw(x) longjmp(*__current_jmp_buf, (x)) | |
// Calculator "class" using long double for large values. | |
typedef struct Calculator { | |
long double current_value; | |
long double (*addition)(struct Calculator*, long double); | |
long double (*subtraction)(struct Calculator*, long double); | |
long double (*multiplication)(struct Calculator*, long double); | |
long double (*division)(struct Calculator*, long double); | |
void (*display_calculator)(struct Calculator*); | |
} Calculator; | |
long double calculator_addition(Calculator* self, long double number) { | |
if (!self) throw(GENERAL_ERROR); | |
self->current_value += number; | |
return self->current_value; | |
} | |
long double calculator_subtraction(Calculator* self, long double number) { | |
if (!self) throw(GENERAL_ERROR); | |
self->current_value -= number; | |
return self->current_value; | |
} | |
long double calculator_multiplication(Calculator* self, long double number) { | |
if (!self) throw(GENERAL_ERROR); | |
self->current_value *= number; | |
return self->current_value; | |
} | |
long double calculator_division(Calculator* self, long double number) { | |
if (!self) throw(GENERAL_ERROR); | |
if (number == 0) throw(DIVISION_BY_ZERO_ERROR); | |
self->current_value /= number; | |
return self->current_value; | |
} | |
void calculator_display(Calculator* self) { | |
clear_screen(); | |
printf("\033[1;34m"); // Set color blue | |
printf(" _____________________\n"); | |
printf("| 7 | 8 | 9 | / |\n"); | |
printf("| 4 | 5 | 6 | x |\n"); | |
printf("| 1 | 2 | 3 | - |\n"); | |
printf("| 0 | . | = | + |\n"); | |
printf(" _____________________\n"); | |
printf("\033[0m"); // Reset color | |
printf("\nExpression: %.8Lf\n", self->current_value); | |
} | |
Calculator* initialize_calculator(long double initial_value) { | |
Calculator* calculator = malloc(sizeof(Calculator)); | |
calculator->current_value = initial_value; | |
calculator->addition = calculator_addition; | |
calculator->subtraction = calculator_subtraction; | |
calculator->multiplication = calculator_multiplication; | |
calculator->division = calculator_division; | |
calculator->display_calculator = calculator_display; | |
return calculator; | |
} | |
// Define keypad keys and availability. | |
#define KEYPAD_SIZE 16 | |
const char keypad_symbols[KEYPAD_SIZE] = {'7','8','9','/','4','5','6','x','1','2','3','-','0','.', '=', '+'}; | |
bool keypad_symbol_available[KEYPAD_SIZE] = { true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true }; | |
// Improved UI: Top section shows last evaluated expression and result, | |
// while the keypad layout shows keys that are available (pressed keys removed). | |
void display_user_interface(const char* last_expression, long double calculation_result) { | |
clear_screen(); | |
// Top section | |
printf("_____________________\n"); | |
printf(" Expression: %s\n", last_expression); | |
printf(" Result: \033[1;32m%.2Lf\033[0m\n", calculation_result); | |
printf("_____________________\n\n"); | |
// Keypad layout: print in 4 columns | |
for(uint64_t position = 0; position < KEYPAD_SIZE; position++) { | |
// Print a cell: if available display the key; else blank space. | |
if(keypad_symbol_available[position]) | |
printf("| %c ", keypad_symbols[position]); | |
else | |
printf("| "); | |
if((position + 1) % 4 == 0) | |
printf("|\n"); | |
} | |
printf("\n'=' to evaluate, 'c' to clear, 'q' to quit.\n"); | |
} | |
// Advanced ASCII keypad calculator: pressed keys are removed from keypad display. | |
void process_calculation(Calculator* calculator, char* current_input, char* previous_expression, long double* calculation_result) { | |
if (!calculator || !current_input || !previous_expression || !calculation_result) | |
throw(GENERAL_ERROR); | |
long double operand1, operand2; | |
char operator; | |
if(sscanf(current_input, "%Lf%c%Lf", &operand1, &operator, &operand2) != 3) | |
throw(INVALID_EXPRESSION_ERROR); | |
strncpy(previous_expression, current_input, sizeof(previous_expression)-1); | |
previous_expression[sizeof(previous_expression)-1] = '\0'; | |
calculator->current_value = operand1; | |
switch(operator) { | |
case '+': | |
*calculation_result = calculator->addition(calculator, operand2); | |
break; | |
case '-': | |
*calculation_result = calculator->subtraction(calculator, operand2); | |
break; | |
case 'x': | |
*calculation_result = calculator->multiplication(calculator, operand2); | |
break; | |
case '/': | |
*calculation_result = calculator->division(calculator, operand2); | |
break; | |
default: | |
throw(UNKNOWN_OPERATOR_ERROR); | |
} | |
} | |
void interactive_calculator_ui() { | |
char current_input[100] = ""; // hidden input (for evaluation) | |
char previous_expression[100] = ""; // last evaluated expression (shown on top) | |
uint64_t input_position = 0; | |
long double calculation_result = 0; | |
int32_t input_character; | |
Calculator* calculator = initialize_calculator(0); | |
// Initial display with all keys available. | |
display_user_interface(previous_expression, calculation_result); | |
while (true) { | |
try { | |
input_character = getch(); | |
if(input_character == 'q' || input_character == 'Q') { | |
free(calculator); | |
return; | |
} | |
if(input_character == 'c' || input_character == 'C') { | |
input_position = 0; | |
current_input[0] = '\0'; | |
previous_expression[0] = '\0'; // Clear the previous expression | |
calculation_result = 0; // Reset the result | |
// Reset keypad availability. | |
for(uint64_t position = 0; position < KEYPAD_SIZE; position++) { | |
keypad_symbol_available[position] = true; | |
} | |
display_user_interface(previous_expression, calculation_result); | |
continue; | |
} | |
// If key is '=' then evaluate. | |
if(input_character == '=') { | |
process_calculation(calculator, current_input, previous_expression, &calculation_result); | |
input_position = 0; | |
current_input[0] = '\0'; | |
for(uint64_t position = 0; position < KEYPAD_SIZE; position++) { | |
keypad_symbol_available[position] = true; | |
} | |
display_user_interface(previous_expression, calculation_result); | |
getch(); | |
continue; | |
} | |
// Allowed keys: digits, dot, operators. | |
// When a key is pressed, mark it as removed in keypad_symbol_available. | |
if((input_character >= '0' && input_character <= '9') || input_character=='.' || input_character=='+' || input_character=='-' || input_character=='x' || input_character=='/') { | |
if(input_position >= sizeof(current_input)-2) | |
throw(BUFFER_OVERFLOW_ERROR); | |
// Find the key index if it exists. | |
for(uint64_t position = 0; position < KEYPAD_SIZE; position++) { | |
if(keypad_symbols[position] == input_character && keypad_symbol_available[position]) { | |
keypad_symbol_available[position] = false; | |
break; | |
} | |
} | |
if(input_position < sizeof(current_input)-2) { | |
current_input[input_position++] = (char)input_character; | |
current_input[input_position] = '\0'; | |
} | |
} | |
display_user_interface(previous_expression, calculation_result); | |
} | |
catch_division("Division by zero") { | |
printf("\n\033[1;31mError: Division by zero!\033[0m\n"); | |
getch(); | |
} | |
catch_all("Error occurred") { | |
printf("\n\033[1;31mError: Operation failed!\033[0m\n"); | |
getch(); | |
} | |
endtry; | |
} | |
free(calculator); | |
} | |
int main() { | |
try { | |
interactive_calculator_ui(); | |
} catch_all("General error") { | |
printf("\n\033[1;31mError: General error occurred!\033[0m\n"); | |
} endtry; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment