Skip to content

Instantly share code, notes, and snippets.

@silverweed
Last active August 29, 2015 14:15
Show Gist options
  • Save silverweed/fb471da4fde57daa6024 to your computer and use it in GitHub Desktop.
Save silverweed/fb471da4fde57daa6024 to your computer and use it in GitHub Desktop.
Roman numbers calculator
/* 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