Last active
August 29, 2015 14:15
-
-
Save silverweed/fb471da4fde57daa6024 to your computer and use it in GitHub Desktop.
Roman numbers calculator
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
/* Roman numbers calculator | |
* compile with something like: `bison rocalc.y && gcc -o rocalc rocalc.tab.c -lm` | |
* Requires: Bison >= 3.0 | |
* Author: silverweed | |
*/ | |
%{ | |
#include <math.h> | |
#include <stdio.h> | |
#include <string.h> | |
int yylex(void); | |
void yyerror(char const*); | |
int starts_with(char*, char*); | |
int r_parse(char*); | |
char* r_unparse(int); | |
/* #define DEBUG */ | |
#ifdef DEBUG | |
#define DBGPRINT(z, x, y) { \ | |
fprintf(stderr, "{ %d = f(%d, %d) }\n", z, x, y); \ | |
} | |
#else | |
#define DBGPRINT(z, x, y) {} | |
#endif | |
%} | |
%define api.value.type {int} | |
%token NUM | |
/* Left associativity */ | |
%left '-' '+' | |
%left '*' '/' | |
%precedence NEG | |
%right '^' | |
%% | |
input: | |
/* empty */ | |
| input line | |
; | |
line: | |
'\n' | |
| exp '\n' { printf("\t%.10s\n", r_unparse($1)); } | |
; | |
exp: | |
NUM { DBGPRINT($$, $1, 0) } | |
| exp '+' exp { $$ = $1 + $3; DBGPRINT($$, $1, $3) } | |
| exp '-' exp { $$ = $1 - $3; DBGPRINT($$, $1, $3) } | |
| exp '*' exp { $$ = $1 * $3; DBGPRINT($$, $1, $3) } | |
| exp '/' exp { $$ = $1 / $3; DBGPRINT($$, $1, $3) } | |
| '-' exp %prec NEG { $$ = -$2; DBGPRINT($$, $2, 0) } | |
| exp '^' exp { $$ = (int)pow($1, $3); DBGPRINT($$, $1, $3) } | |
| '(' exp ')' { $$ = $2; } | |
; | |
%% | |
int starts_with(char *str, char *pattern) { | |
int i; | |
int len = strlen(pattern); | |
if (len > strlen(str)) return 0; | |
for (i = 0; i < len; ++i) | |
if (str[i] != pattern[i]) return 0; | |
return 1; | |
} | |
int r_parse(char *roman) { | |
if (strlen(roman) < 1) return 0; | |
if (starts_with(roman, "M")) return 1000 + r_parse(roman + 1); | |
if (starts_with(roman, "CM")) return 900 + r_parse(roman + 2); | |
if (starts_with(roman, "D")) return 500 + r_parse(roman + 1); | |
if (starts_with(roman, "CD")) return 400 + r_parse(roman + 2); | |
if (starts_with(roman, "C")) return 100 + r_parse(roman + 1); | |
if (starts_with(roman, "XC")) return 90 + r_parse(roman + 2); | |
if (starts_with(roman, "L")) return 50 + r_parse(roman + 1); | |
if (starts_with(roman, "XL")) return 40 + r_parse(roman + 2); | |
if (starts_with(roman, "X")) return 10 + r_parse(roman + 1); | |
if (starts_with(roman, "IX")) return 9 + r_parse(roman + 2); | |
if (starts_with(roman, "V")) return 5 + r_parse(roman + 1); | |
if (starts_with(roman, "IV")) return 4 + r_parse(roman + 2); | |
if (starts_with(roman, "I")) return 1 + r_parse(roman + 1); | |
} | |
char* r_unparse(int decimal) { | |
char *buf = (char*) malloc(sizeof(char)*256); | |
int i, idx = 0; | |
int m = decimal / 1000; | |
for (i = 0; i < m && idx < 256; ++i, ++idx) | |
buf[idx] = 'M'; | |
decimal -= m * 1000; | |
m = decimal / 900; | |
for (i = 0; i < m && idx < 256; ++i, ++idx) { | |
buf[idx++] = 'C'; | |
buf[idx] = 'M'; | |
} | |
decimal -= m * 900; | |
m = decimal / 500; | |
for (i = 0; i < m && idx < 256; ++i, ++idx) { | |
buf[idx] = 'D'; | |
} | |
decimal -= m * 500; | |
m = decimal / 400; | |
for (i = 0; i < m && idx < 256; ++i, ++idx) { | |
buf[idx++] = 'C'; | |
buf[idx] = 'D'; | |
} | |
decimal -= m * 400; | |
m = decimal / 100; | |
for (i = 0; i < m && idx < 256; ++i, ++idx) { | |
buf[idx] = 'C'; | |
} | |
decimal -= m * 100; | |
m = decimal / 90; | |
for (i = 0; i < m && idx < 256; ++i, ++idx) { | |
buf[idx++] = 'X'; | |
buf[idx] = 'C'; | |
} | |
decimal -= m * 90; | |
m = decimal / 50; | |
for (i = 0; i < m && idx < 256; ++i, ++idx) { | |
buf[idx] = 'L'; | |
} | |
decimal -= m * 50; | |
m = decimal / 40; | |
for (i = 0; i < m && idx < 256; ++i, ++idx) { | |
buf[idx++] = 'X'; | |
buf[idx] = 'L'; | |
} | |
decimal -= m * 40; | |
m = decimal / 10; | |
for (i = 0; i < m && idx < 256; ++i, ++idx) { | |
buf[idx] = 'X'; | |
} | |
decimal -= m * 10; | |
m = decimal / 9; | |
for (i = 0; i < m && idx < 256; ++i, ++idx) { | |
buf[idx++] = 'I'; | |
buf[idx] = 'X'; | |
} | |
decimal -= m * 9; | |
m = decimal / 5; | |
for (i = 0; i < m && idx < 256; ++i, ++idx) { | |
buf[idx] = 'V'; | |
} | |
decimal -= m * 5; | |
m = decimal / 4; | |
for (i = 0; i < m && idx < 256; ++i, ++idx) { | |
buf[idx++] = 'I'; | |
buf[idx] = 'V'; | |
} | |
decimal -= m * 4; | |
m = decimal; | |
for (i = 0; i < m && idx < 256; ++i, ++idx) { | |
buf[idx] = 'I'; | |
} | |
buf[idx] = '\0'; | |
return buf; | |
} | |
int yylex(void) { | |
char c; | |
char buffer[256] = {0}; | |
int i = 0; | |
/* Skip white space */ | |
while ((c = getchar()) == ' ' || c == '\t') | |
continue; | |
if (c == '(' || c == ')' || c == '+' || c == '-' || c == '*' || c == '/' || c == '^' || c == '\n') | |
return c; | |
/* Process numbers */ | |
do { | |
if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '(' || c == ')' | |
|| c == '+' || c == '-' || c == '*' || c == '/' || c == '^' || c == EOF) | |
{ | |
ungetc(c, stdin); | |
yylval = r_parse(buffer); | |
return NUM; | |
} else if (r_parse(&c)) { | |
buffer[i++] = c; | |
c = getchar(); | |
} else { | |
char s[256]; | |
sprintf(s, "Invalid symbol: %c\n", c); | |
yyerror(s); | |
return 0; | |
} | |
} while (c != EOF); | |
return 0; | |
} | |
/* Error reporting routine, called by yyparse on error */ | |
void yyerror(const char *s) { | |
fprintf(stderr, "%s\n", s); | |
} | |
int main(void) { | |
return yyparse(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment