Skip to content

Instantly share code, notes, and snippets.

@haseeb-heaven
Last active February 19, 2025 02:32
Show Gist options
  • Save haseeb-heaven/129dcdc8c2d58ae3663502ba27f53259 to your computer and use it in GitHub Desktop.
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.
#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