Created
October 30, 2009 00:24
-
-
Save zach-klippenstein/221979 to your computer and use it in GitHub Desktop.
Simple console calculator. Uses postfix notation.
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 <ctype.h> | |
#include <string.h> | |
#define STACK_SIZE 1000 | |
#define MAX_OPERATORS 10 | |
#define MAX_LINE 300 | |
// Clear any exception | |
#define clear_ex ex.message = NULL | |
// Set the current exception and mark the line where it occured | |
#define set_ex(msg) clear_ex; ex.message = msg; ex.lineNum = __LINE__ | |
// Return true if an exception has been set | |
#define is_ex_set (NULL != ex.message) | |
// Macro that hides the type of slot_t | |
#define strtoslot(str) strtod(str, NULL) | |
typedef unsigned int uint; | |
typedef double slot_t; | |
typedef struct OPERATOR operator_t; | |
typedef struct STACK stack_t; | |
typedef struct EXCEPTION exception_t; | |
typedef slot_t (*operator_method_t)(slot_t, slot_t); | |
typedef enum | |
{ | |
true, false | |
} bool; | |
struct OPERATOR | |
{ | |
char opCode; | |
operator_method_t method; | |
}; | |
struct STACK | |
{ | |
slot_t *items; | |
uint size; | |
uint top; | |
}; | |
struct EXCEPTION | |
{ | |
char *message; | |
uint lineNum; | |
}; | |
// ----- | |
// File Variables | |
// ----- | |
// Characters that separate tokens on a line | |
const char *TOKEN_DELIM = " \t"; | |
// The global exception object. Used by the exception macros. | |
exception_t ex; | |
// ----- | |
// Operators | |
// ----- | |
slot_t op_add(slot_t s1, slot_t s2); | |
slot_t op_sub(slot_t s1, slot_t s2); | |
slot_t op_mult(slot_t s1, slot_t s2); | |
slot_t op_div(slot_t s1, slot_t s2); | |
slot_t op_mod(slot_t s1, slot_t s2); | |
// Add all the above ops to the given array. | |
// If there are more operators than MAX_OPERATORS, the | |
// list is set to empty. | |
void register_ops(operator_t *ops, uint *numOps); | |
// ----- | |
// Calculator Functions | |
// ----- | |
// Evaluates a single line, returning the result. | |
// NOTE: at the moment, cannot read floating point numbers | |
slot_t evaluate(char *line, uint len, stack_t *stack, const operator_t *ops, uint numOps); | |
// Evaluates each line read from input and prints the result | |
void run(FILE *input, FILE *output, slot_t *stack, uint stackSize, const operator_t *ops, uint numOps); | |
// ----- | |
// Operator Functions | |
// ----- | |
// Return the operator method for the given opcode | |
operator_method_t getOp(const operator_t *ops, uint numOps, char opCode); | |
// Call the operator method for the given opcode, returning it's result if it exists. | |
slot_t callOp(const operator_t *ops, uint numOps, char opCode, slot_t slot1, slot_t slot2); | |
// ----- | |
// Stack Functions | |
// ----- | |
// Push a value onto the stack | |
void push(stack_t *stack, slot_t val); | |
// Pop a value off the top of the stack | |
// If stack is empty, return value is undefined. | |
slot_t pop(stack_t *stack); | |
// Peek at the top of the stack | |
// If stack is empty, return value is undefined. | |
slot_t top(const stack_t *stack); | |
// Check if the stack is empty | |
bool isEmpty(const stack_t *stack); | |
// Check if the stack can't be added to | |
bool isFull(const stack_t *stack); | |
// Empty the stack | |
void clear(stack_t *stack); | |
int main( int argc, char *argv[] ) | |
{ | |
slot_t stack[STACK_SIZE]; | |
operator_t operators[MAX_OPERATORS]; | |
uint numOps; | |
clear_ex; | |
register_ops(operators, &numOps); | |
run(stdin, stdout, stack, STACK_SIZE, operators, numOps); | |
return EXIT_SUCCESS; | |
} | |
slot_t op_add(slot_t s1, slot_t s2) | |
{ | |
return s1 + s2; | |
} | |
slot_t op_sub(slot_t s1, slot_t s2) | |
{ | |
return s1 - s2; | |
} | |
slot_t op_mult(slot_t s1, slot_t s2) | |
{ | |
return s1 * s2; | |
} | |
slot_t op_div(slot_t s1, slot_t s2) | |
{ | |
if (0 == s2) | |
{ | |
set_ex("cannot divide by 0"); | |
} | |
return s1 / s2; | |
} | |
slot_t op_mod(slot_t s1, slot_t s2) | |
{ | |
return (int)s1 % (int)s2; | |
} | |
void register_ops(operator_t *ops, uint *numOps) | |
{ | |
int i; | |
if (ops && numOps) | |
{ | |
*numOps = 5; | |
if (*numOps < MAX_OPERATORS) | |
{ | |
ops[0].opCode = '+'; | |
ops[0].method = op_add; | |
ops[1].opCode = '-'; | |
ops[1].method = op_sub; | |
ops[2].opCode = '*'; | |
ops[2].method = op_mult; | |
ops[3].opCode = '/'; | |
ops[3].method = op_div; | |
ops[4].opCode = '%'; | |
ops[4].method = op_mod; | |
// Initialize any leftover operators to null | |
for (i = *numOps; i < MAX_OPERATORS; i++) | |
{ | |
ops[i].opCode = '\0'; | |
ops[i].method = NULL; | |
} | |
} | |
// Not enought room to store all the operators | |
else | |
{ | |
*numOps = 0; | |
} | |
} | |
} | |
slot_t evalOperator(stack_t *stack, const operator_t *ops, uint numOps, char opCode) | |
{ | |
slot_t slots[2]; | |
slot_t result; | |
// =================================== | |
// This is where the order of operands comes in!!!! | |
// =================================== | |
slots[0] = pop(stack); | |
slots[1] = pop(stack); | |
result = callOp(ops, numOps, opCode, slots[0], slots[1]); | |
return result; | |
} | |
slot_t evaluate(char *line, uint len, stack_t *stack, const operator_t *ops, uint numOps) | |
{ | |
slot_t curNum; | |
slot_t result; | |
char *token = NULL; | |
int tokLen; | |
token = strtok(line, TOKEN_DELIM); | |
while (NULL != token) | |
{ | |
tokLen = strlen(token); | |
if (tokLen > 0) | |
{ | |
// If first char is a digit, assume the rest of the token is a number | |
if (isdigit(token[0]) || token[0] == '.') | |
{ | |
curNum = strtoslot(token); | |
push(stack, curNum); | |
} | |
// Token must be an operator | |
else if (!isspace(token[0])) | |
{ | |
result = evalOperator(stack, ops, numOps, token[0]); | |
if (!is_ex_set) | |
{ | |
push(stack, result); | |
} | |
} | |
} | |
// If error occured, abort | |
if (is_ex_set) | |
{ | |
token = NULL; | |
} | |
// No errors, get next token and continue | |
else | |
{ | |
token = strtok(NULL, TOKEN_DELIM); | |
} | |
} | |
// Don't bother trying to get a return value if an error occured | |
if (!is_ex_set) | |
{ | |
if (!isEmpty(stack)) | |
{ | |
result = pop(stack); | |
if (!isEmpty(stack)) | |
{ | |
set_ex("stack not empty"); | |
} | |
} | |
} | |
return result; | |
} | |
void run(FILE *input, FILE *output, slot_t *stack, uint stackSize, const operator_t *ops, uint numOps) | |
{ | |
char line[MAX_LINE]; | |
stack_t stackStruct; | |
slot_t result; | |
if (input && stack && ops) | |
{ | |
stackStruct.items = stack; | |
stackStruct.size = stackSize; | |
stackStruct.top = 0; | |
// Read line-by-line | |
while (NULL != fgets(line, MAX_LINE, input)) | |
{ | |
result = evaluate(line, strlen(line), &stackStruct, ops, numOps); | |
if (is_ex_set) | |
{ | |
fprintf(output, "error at line %d: %s\n", ex.lineNum, ex.message); | |
} | |
else | |
{ | |
fprintf(output, "%f\n", result); | |
} | |
} | |
} | |
} | |
operator_method_t getOp(const operator_t *ops, uint numOps, char opCode) | |
{ | |
operator_method_t method = NULL; | |
int i; | |
clear_ex; | |
if (ops) | |
{ | |
for (i = 0; i < numOps && NULL == method; i++) | |
{ | |
if (ops[i].opCode == opCode) | |
{ | |
method = ops[i].method; | |
} | |
} | |
} | |
else | |
{ | |
set_ex("null operators list"); | |
} | |
return method; | |
} | |
slot_t callOp(const operator_t *ops, uint numOps, char opCode, slot_t slot1, slot_t slot2) | |
{ | |
slot_t result; | |
operator_method_t method = NULL; | |
clear_ex; | |
if (ops) | |
{ | |
method = getOp(ops, numOps, opCode); | |
if (method) | |
{ | |
result = (*method) (slot1, slot2); | |
} | |
// No method defined for that operator | |
// Note: this is a runtime error, that could happen often, | |
// so the message should be user-friendly | |
else | |
{ | |
set_ex("invalid operator"); | |
} | |
} | |
else | |
{ | |
set_ex("null operators list"); | |
} | |
return result; | |
} | |
void push(stack_t *stack, slot_t val) | |
{ | |
clear_ex; | |
if (stack) | |
{ | |
if (!isFull(stack)) | |
{ | |
stack->items[stack->top] = val; | |
stack->top++; | |
} | |
else | |
{ | |
set_ex("stack is full"); | |
} | |
} | |
else | |
{ | |
set_ex("null stack pointer"); | |
} | |
} | |
slot_t pop(stack_t *stack) | |
{ | |
slot_t popped; | |
clear_ex; | |
if (stack) | |
{ | |
if (!isEmpty(stack)) | |
{ | |
popped = top(stack); | |
// Remove the item | |
if (stack->top > 0) | |
stack->top--; | |
} | |
else | |
{ | |
set_ex("stack is empty"); | |
} | |
} | |
else | |
{ | |
set_ex("null stack pointer"); | |
} | |
return popped; | |
} | |
slot_t top(const stack_t *stack) | |
{ | |
slot_t t; | |
clear_ex; | |
if (stack) | |
{ | |
if (!isEmpty(stack)) | |
{ | |
t = stack->items[stack->top - 1]; | |
} | |
else | |
{ | |
set_ex("stack is empty"); | |
} | |
} | |
else | |
{ | |
set_ex("null stack pointer"); | |
} | |
return t; | |
} | |
bool isEmpty(const stack_t *stack) | |
{ | |
bool empty; | |
clear_ex; | |
if (stack) | |
{ | |
empty = (stack->top == 0); | |
} | |
else | |
{ | |
set_ex("null stack pointer"); | |
} | |
return empty; | |
} | |
bool isFull(const stack_t *stack) | |
{ | |
bool full = false; | |
clear_ex; | |
if (stack) | |
{ | |
full = (stack->top == stack->size); | |
} | |
else | |
{ | |
set_ex("null stack pointer"); | |
} | |
return full; | |
} | |
void clear(stack_t *stack) | |
{ | |
clear_ex; | |
if (stack) | |
{ | |
stack->top = 0; | |
} | |
else | |
{ | |
set_ex("null stack pointer"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment