Skip to content

Instantly share code, notes, and snippets.

@zach-klippenstein
Created October 30, 2009 00:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zach-klippenstein/221979 to your computer and use it in GitHub Desktop.
Save zach-klippenstein/221979 to your computer and use it in GitHub Desktop.
Simple console calculator. Uses postfix notation.
#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