Last active
January 13, 2020 12:48
-
-
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.
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
/** | |
* 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