Skip to content

Instantly share code, notes, and snippets.

@rj00a
Last active October 10, 2019 10:55
Show Gist options
  • Save rj00a/2e98a2b19ebf6056cc94f8a4be282256 to your computer and use it in GitHub Desktop.
Save rj00a/2e98a2b19ebf6056cc94f8a4be282256 to your computer and use it in GitHub Desktop.
Simple Recursive Descent Calculator in C
// (Public domain, created by Ryan Johnson)
// A simple recursive descent calculator in C.
// compile with `cc -lm calc.c`
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <inttypes.h>
#include <ctype.h>
#include <math.h>
typedef double f64;
typedef struct {
int type;
f64 num;
} Token;
// The current token.
Token curr;
// Exit with an error
void error(const char *fmt, ...) {
va_list list;
va_start(list, fmt);
vfprintf(stderr, fmt, list);
fputc('\n', stderr);
exit(1);
}
// Get the next token from stdin
Token next(void) {
for (;;) {
int c = getchar();
if (isdigit(c)) {
f64 num = 0;
do num = num * 10 + (c - '0');
while (isdigit(c = getchar()));
ungetc(c, stdin);
curr.type = 'n';
curr.num = num;
return curr;
}
if (isspace(c))
continue;
switch (c) {
case '+':
case '-':
case '*':
case '/':
case '%':
case '^':
case '(':
case ')':
case EOF:
curr.type = c;
return curr;
}
if (isprint(c))
error("Unexpected character: \"%c\"", (char)c);
else
error("Unexpected character: 0x%2X", (unsigned int)c);
}
}
// Return the current Token as a string.
const char *str_type(void) {
switch (curr.type) {
case '+':
return "'+'";
case '-':
return "'-'";
case '*':
return "'*'";
case '/':
return "'/'";
case '%':
return "'%'";
case '^':
return "'^'";
case 'n':
return "number";
case EOF:
return "EOF";
}
return NULL;
}
// Production Rules:
// Add -> Mul (("+" | "-") Mul)*
// Mul -> Unary (("*" | "/" | "%") Unary)*
// Unary -> ("+" | "-")? Exp
// Exp -> Primary ("^" Exp)?
// Primary -> Number | "(" Add ")"
f64 p_add(void);
f64 p_mul(void);
f64 p_unary(void);
f64 p_exp(void);
f64 p_primary(void);
f64 p_add(void) {
f64 v = p_mul();
for (;;) {
if (curr.type == '+') {
next();
v += p_mul();
} else if (curr.type == '-') {
next();
v -= p_mul();
} else break;
}
return v;
}
f64 p_mul(void) {
f64 v = p_unary();
for (;;) {
if (curr.type == '*') {
next();
v *= p_unary();
} else if (curr.type == '/') {
next();
v /= p_unary();
} else if (curr.type == '%') {
next();
v = fmod(v, p_unary());
} else break;
}
return v;
}
f64 p_unary(void) {
if (curr.type == '+') {
next();
return p_exp();
} else if (curr.type == '-') {
next();
return -p_exp();
}
return p_exp();
}
f64 p_exp(void) {
f64 v = p_primary();
if (curr.type == '^') {
next();
return pow(v, p_exp());
}
return v;
}
f64 p_primary(void) {
f64 v;
switch (curr.type) {
case 'n':
v = curr.num;
next();
return v;
case '(':
next();
v = p_add();
if (curr.type != ')')
error("Expected ')', found %s", str_type());
next();
return v;
}
error("Unexpected %s", str_type());
}
int main(void) {
next();
printf("%.2f\n", p_add());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment