Skip to content

Instantly share code, notes, and snippets.

@silverweed
Last active August 29, 2015 14:15
Show Gist options
  • Save silverweed/30f1f88a2fc267851f96 to your computer and use it in GitHub Desktop.
Save silverweed/30f1f88a2fc267851f96 to your computer and use it in GitHub Desktop.
Roman numbers calculator - enhanced
int starts_with(const char*, const char*);
int r_parse(const char*);
char* r_unparse(int);
rovars: rovars.tab.c lex.yy.c
gcc -o rovars $^ -lm
rovars.tab.c: rovars.y
bison -d $<
lex.yy.c: rovars.l
flex $<
%{
#include "rovars.tab.h"
#include "functions.h"
#define SAVE_VAL yylval.val = r_parse(yytext);
#define SAVE_TOKEN yylval.str = strndup(yytext, yyleng)
%}
%option noyywrap
%%
[ \t] ;
[=+\-*/^(),] return (int)yytext[0];
[\r\n;] return ENDLN;
"$^" return LASTRES;
[MDCLXVI]+ SAVE_VAL; return NUM;
[0-9]+ yylval.val = atoi(yytext); return NUM;
[a-zA-Z_][a-zA-Z0-9_]* SAVE_TOKEN; return NAME;
. printf("Unknown token!\n");
%%
/* Roman numbers calculator
* Requires: Bison >= 3.0
* Author: silverweed
*/
%{
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "functions.h"
#define NAME_MAX_SIZE 256
#define NUM_MAX_SIZE 512
int yylex(void);
void yyerror(char const*);
/* #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
typedef struct Var {
int value;
char *name;
struct Var *next;
} Var;
Var *symbols = NULL;
void newvar(char*, int);
Var* getvar(char*);
void updatevar(char*, int);
int lastresult = 0;
%}
/*%define api.value.type {int}*/
%union {
int val;
char *str;
}
%token<val> NUM
%token<str> NAME
%token ENDLN LASTRES
%type<val> exp
/* Left associativity */
%left '-' '+'
%left '*' '/'
%precedence NEG
%right '^'
%%
input:
/* empty */
| input line
;
line:
ENDLN
| exp ENDLN { printf("\t%.512s\n", r_unparse($1)); }
| decl ENDLN
| assign ENDLN
;
exp:
NUM { DBGPRINT($$, $1, 0) }
| NAME {
Var *v = getvar($1);
if (v == NULL) {
printf("undefined\n");
$$ = 0;
} else {
$$ = v->value;
DBGPRINT($$, $1, 0)
}
}
| LASTRES { $$ = lastresult; }
| exp '+' exp { $$ = lastresult = $1 + $3; DBGPRINT($$, $1, $3) }
| exp '-' exp { $$ = lastresult = $1 - $3; DBGPRINT($$, $1, $3) }
| exp '*' exp { $$ = lastresult = $1 * $3; DBGPRINT($$, $1, $3) }
| exp '/' exp { $$ = lastresult = $1 / $3; DBGPRINT($$, $1, $3) }
| '-' exp %prec NEG { $$ = lastresult = -$2; DBGPRINT($$, $2, 0) }
| exp '^' exp { $$ = lastresult = (int)pow((double)$1, (double)$3); DBGPRINT($$, $1, $3) }
| '(' exp ')' { $$ = lastresult = $2; }
;
decl:
assignlist
;
assignlist:
assign
| assignlist ',' assign
;
/*decllist:
NAME { newvar($1, 0); }
| NAME '=' exp { newvar($1, $3); }
| decllist ',' decllist
;*/
assign:
NAME '=' exp { updatevar($1, $3); }
;
%%
void newvar(char *name, int val) {
if (getvar(name) != NULL) {
char s[NAME_MAX_SIZE];
sprintf(s, "Already defined: %s\n", name);
yyerror(s);
return;
}
Var *var = (Var*) malloc(sizeof(Var));
var->name = (char*)malloc(sizeof(name));
strcpy(var->name, name);
var->value = val;
var->next = symbols;
symbols = var;
}
Var* getvar(char *name) {
Var *it;
for (it = symbols; it != NULL; it = it->next)
if (strcmp(it->name, name) == 0)
return it;
return NULL;
}
void updatevar(char *name, int val) {
Var *v = getvar(name);
if (v) v->value = val;
else newvar(name, val);
}
int starts_with(const char *str, const 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(const 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)*NUM_MAX_SIZE);
int i, idx = 0;
int m = decimal / 1000;
for (i = 0; i < m && idx < NUM_MAX_SIZE; ++i, ++idx)
buf[idx] = 'M';
decimal -= m * 1000;
m = decimal / 900;
for (i = 0; i < m && idx < NUM_MAX_SIZE; ++i, ++idx) {
buf[idx++] = 'C';
buf[idx] = 'M';
}
decimal -= m * 900;
m = decimal / 500;
for (i = 0; i < m && idx < NUM_MAX_SIZE; ++i, ++idx) {
buf[idx] = 'D';
}
decimal -= m * 500;
m = decimal / 400;
for (i = 0; i < m && idx < NUM_MAX_SIZE; ++i, ++idx) {
buf[idx++] = 'C';
buf[idx] = 'D';
}
decimal -= m * 400;
m = decimal / 100;
for (i = 0; i < m && idx < NUM_MAX_SIZE; ++i, ++idx) {
buf[idx] = 'C';
}
decimal -= m * 100;
m = decimal / 90;
for (i = 0; i < m && idx < NUM_MAX_SIZE; ++i, ++idx) {
buf[idx++] = 'X';
buf[idx] = 'C';
}
decimal -= m * 90;
m = decimal / 50;
for (i = 0; i < m && idx < NUM_MAX_SIZE; ++i, ++idx) {
buf[idx] = 'L';
}
decimal -= m * 50;
m = decimal / 40;
for (i = 0; i < m && idx < NUM_MAX_SIZE; ++i, ++idx) {
buf[idx++] = 'X';
buf[idx] = 'L';
}
decimal -= m * 40;
m = decimal / 10;
for (i = 0; i < m && idx < NUM_MAX_SIZE; ++i, ++idx) {
buf[idx] = 'X';
}
decimal -= m * 10;
m = decimal / 9;
for (i = 0; i < m && idx < NUM_MAX_SIZE; ++i, ++idx) {
buf[idx++] = 'I';
buf[idx] = 'X';
}
decimal -= m * 9;
m = decimal / 5;
for (i = 0; i < m && idx < NUM_MAX_SIZE; ++i, ++idx) {
buf[idx] = 'V';
}
decimal -= m * 5;
m = decimal / 4;
for (i = 0; i < m && idx < NUM_MAX_SIZE; ++i, ++idx) {
buf[idx++] = 'I';
buf[idx] = 'V';
}
decimal -= m * 4;
m = decimal;
for (i = 0; i < m && idx < NUM_MAX_SIZE; ++i, ++idx) {
buf[idx] = 'I';
}
if (idx == NUM_MAX_SIZE)
fprintf(stderr, "Warning: number size overflow. Truncating to first %d ciphers.\n", NUM_MAX_SIZE);
buf[idx] = '\0';
return buf;
}
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