Last active
October 10, 2019 10:55
-
-
Save rj00a/2e98a2b19ebf6056cc94f8a4be282256 to your computer and use it in GitHub Desktop.
Simple Recursive Descent Calculator in C
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
// (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