Skip to content

Instantly share code, notes, and snippets.

@M0r13n
Last active January 13, 2020 12:48
Show Gist options
  • Save M0r13n/67d13a1a2b1923681853e4207bbd533e to your computer and use it in GitHub Desktop.
Save M0r13n/67d13a1a2b1923681853e4207bbd533e to your computer and use it in GitHub Desktop.
Evaluate a string in Prefix Notation (Polish Notation) and calculate the result. Interact with a user through a simple REPL.
/**
* A simple REPL for Polish Notation / Prefix Notation.
* Compile with:
* gcc -std=c99 -Wall calc.c -ledit -o calc
*
* Examples:
* -----------
* calc> * / + 2 2 2 + 1 2
* Result is: 6.000000
*
* calc> + 0 33.5544
* Result is: 33.554400
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <editline/readline.h>
/* A simple stack that supports only push and pop */
#define push(sp, v) (*((sp)++) = (v))
#define pop(sp) (*--(sp))
/* Basic arithmetic operations */
const static char DIV = '/';
const static char MUL = '*';
const static char ADD = '+';
const static char SUB = '-';
const static int STACK_SIZE = 100;
/* ERROR CODES*/
const static int STACK_SIZE_LIMIT_REACHED = 1;
const static int TOO_FEW_OPERANDS = 2;
const static int TOO_MANY_OPERANDS = 3;
/* Global Vars*/
static int ERROR = 0;
/* Function definitions*/
double calculate(const char *operator, double op1, double op2);
int is_operator(const char *s);
int is_operand(const char *s);
/*
* Function: calculate
* ----------------------------
* Returns the result of an arithmetic operation defined by a given operator.
*
* operator: the arithmetic operator as a string (is internally converted to char)
* op1: the first operand
* op2: the second operand
*
* returns: the result of operand1 operation operand2 (e.g. 1 + 2 = 3)
*/
double calculate(const char *operator, const double op1, const double op2)
{
char op = operator[0];
switch (op)
{
case ADD:
return op1 + op2;
case SUB:
return op1 - op2;
case MUL:
return op1 * op2;
default:
return op1 / op2;
}
}
/*
* Function: is_operator
* ----------------------------
* Check whether a given string a operator (matches +, -, * or /)
*
* s: the string to check
*
* returns: 1 if the string is an operator else 0
*/
int is_operator(const char *s)
{
if (strlen(s) > 1)
return 0;
char cc = s[0];
return cc == ADD || cc == SUB || cc == DIV || cc == MUL;
}
/*
* Function: is_operand
* ----------------------------
* Check whether a given string is a number (double)
*
* s: the string to check
*
* returns: 1 if the string is a number (double) else 0
*/
int is_operand(const char *s)
{
if (s == NULL || *s == '\0' || isspace(*s))
return 0;
char *p;
strtod(s, &p);
return *p == '\0';
}
/*
* Function: evaluate
* ----------------------------
* Evaluates a string in prefix notation and returns the result
*
* str: the string to evaluate
*
* returns: the result of the evaluated string
*/
double evaluate(char *str)
{
/* a stack that can hold up to STACK_SIZE items */
double stack[STACK_SIZE];
double *sp = stack;
/* a word buffer that can hold up to STACK_SIZE individual words */
char *words[STACK_SIZE];
int wc = 0;
/* split a string into it's words */
char *pch;
pch = strtok(str, " ");
while (pch != NULL)
{
if (wc == STACK_SIZE)
{
printf("Input has to many words. Limit is %d\n", STACK_SIZE);
ERROR = STACK_SIZE_LIMIT_REACHED;
}
words[wc++] = pch;
pch = strtok(NULL, " ");
}
/* iterate over the tokenized string in reverse oder (right to left) */
char *cur, *end;
double op;
int n_op = 0;
while (wc > 0)
{
cur = words[--wc];
if (is_operand(cur))
{
/* convert operands to double and push em onto the stack */
op = strtod(cur, &end);
push(sp, op);
n_op++;
}
else if (is_operator(cur))
{
/* is there are less than two pending operators, the input is malformed */
if (n_op < 2)
{
ERROR = TOO_FEW_OPERANDS;
puts("Too many operators supplied. Skipping..");
return 0;
}
/* calculate result for an operator */
double op1 = pop(sp);
double op2 = pop(sp);
double result = calculate(cur, op1, op2);
push(sp, result);
n_op--;
}
else
{
printf("Encountered unknown token %s. Skipping.\n", cur);
}
}
if (n_op > 1)
{
ERROR = TOO_MANY_OPERANDS;
printf("Too many operands. Please supply a valid postfix string. Skipping\n");
}
/* the result is the last item on the stack */
return pop(sp);
}
int main(int argc, char **argv)
{
/* Version information and exit information */
puts("Prefix Calculator Version 0.0.1");
puts("Press Ctrl+c or type exit to Exit\n");
/* Main Loop */
while (1)
{
/* Read the input without tailing new line separator (\n) into a dynamic sized buffer */
char *input = readline("calc> ");
double result = 0;
/* Add input to history */
add_history(input);
/* Check if the input equals exit */
if (strncmp(input, "exit", 4) == 0)
{
break;
}
/* Evaluate the user input */
result = evaluate(input);
/* Print the result */
if (!ERROR)
{
printf("Result is: %lf\n", result);
}
ERROR = 0;
/* Free allocated space fo input */
free(input);
}
/* Exit with success */
puts("Bye..");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment