/* ro.c roman numeral translator and adder by Bob Terrell 7/11/MCMXCV an exercise in the dragon book +---------------------------------------+ you input mDCXI+3 and you get the output _ MDCXIV=1000614 +---------------------------------------+ line NEWLINE | expr NEWLINE | HELP NEWLINE | ABOUT NEWLINE | QUIT NEWLINE expr expr+num | num num posint | roman roman thoubar hunbar tenbar thou hun ten one thoubar thoubar m | nothing hunbar cm | dcccc | dccc | dcc | dc | d | cd | cccc | ccc | cc | c | nothing tenbar xc | lxxxx | lxxx | lxx | lx | l | xl | xxxx | xxx | xx | x |nothing thou Mx | vMMMM | vMMM | vMM | vM | v | Mv | MMMM | MMM | MM | M | nothing hun CM | DCCCC | DCCC | DCC | DC | D | CD | CCCC | CCC | CC | C | nothing ten XC | LXXXX | LXXX | LXX | LX | L | XL | XXXX | XXX | XX | X | nothing one IX | VIIII | VIII | VII | VI | V | IV | IIII | III | II | I | nothing */ #include #include #include #include enum token { NEWLINE='\n', HELP='h', ABOUT='a', QUIT='q', PLUS='+', NUM }; enum token lookahead, lex(void); void setup(void), parse(void), line(void), prompt(void), matchNlook(enum token t), err(char k), errmsg(const char* s), help(void), about(void), prnt(long); long number, posint(void), roman(void), thoubar(void), hunbar(void), tenbar(void), thou(void), hun(void), ten(void), one(void), rome(char myC, char myD, char myM,long factor); int ishigher(char a, char b); main(void) { setup(); parse(); exit(0); } void setup(void) { printf("\t Roman Numeral Translator and Adder\n"); printf("\t\t type h for help,\n"); printf("\t\t q to quit, and\n"); printf("\t\t a for information about the program\n"); } void parse(void) { prompt(); lookahead=lex(); while(lookahead!=QUIT) { line(); matchNlook(NEWLINE); } } void line(void) { long sum; if(lookahead==NEWLINE) { prompt(); return; } else if(lookahead==HELP) { help(); prompt(); matchNlook(HELP); } else if(lookahead==ABOUT) { about(); prompt(); matchNlook(ABOUT); } else { sum=number; while(1) switch(lookahead) { case PLUS: matchNlook(PLUS); sum+=number; continue; case NUM: matchNlook(NUM); continue; default: prnt(sum); prompt(); return; } } } void about(void) { printf("\t ro \t\t\t\t\t\t\t\t 7/95\n"); printf("\t This software may be freely distributed for educational purposes.\n"); printf("\t ro was inspired by an exercise in the 'dragon book', Compilers, \n"); printf("\t by Aho, Sethi, and Ullman\n"); printf("\t Comments may be sent to bterrell@math.cornell.edu\n"); } void help(void) { printf("\t Roman numerals are built from the symbols I=1,V=5,X=10,L=50,\n"); printf("\t C=100,D=500,and M=1000. You can type, for example,\n"); printf("\t\t ro>> XV\n"); printf("\t and the program will return\n"); printf("\t\t\t XV = 15\n"); printf("\t For things that are not allowed, like VM, you get an error message.\n"); printf("\t If any of these letters except I has a bar over it, it is worth\n"); printf("\t 1000 times as much. Since there is no bar on your keyboard, just\n"); printf("\t type lower-case when you want a bar, like\n"); printf("\t\t ro>> mXXI\n"); printf("\t\t\t _\n"); printf("\t\t\t MXXI = 1000021\n"); printf("\t The barred symbols were a late development, maybe after the Romans\n"); printf("\t had a lot of things to count.\n"); printf("\t You can also add, like IV+3, or X+M, or C+2+V, or 12+6+57123+9321.\n"); } void prompt(void) { printf(" ro>> "); } void err(char k) { printf("\t There is a mistake near the %c. \n",k); printf("\t Ignore the next output.\n",k); } void errmsg(const char* s) { printf("\t %s \n",s); } void matchNlook(enum token t) { if(lookahead!=t) err(t); else lookahead=lex(); } enum token lex(void) { char c; c=getc(stdin); while(isspace(c)&&(c!='\n')) c=getc(stdin); if(c=='\n') return NEWLINE; else if(isdigit(c)) { ungetc(c, stdin); number=posint(); return NUM; } else switch(c) { case '+': return PLUS; case 'q': return QUIT; case 'h': return HELP; case 'a': return ABOUT; case 'I':case 'V':case 'X':case 'L':case 'C':case 'D': case 'M':case 'v':case 'x':case 'l':case 'c':case 'd': case 'm': ungetc(c,stdin); number=roman(); return NUM; default: err(c); return NEWLINE; } } long posint(void) { char m[20],*n=m,*end,i=0; m[0]=getchar(); while(isdigit(m[i])) { i++; m[i]=getchar(); } ungetc(m[i], stdin); if(i>9) { errmsg("That number is too big for me!"); errmsg("ignore output"); return 0; } else return strtol(n, &end,10); } long roman(void) { long r=thoubar(); r+=hunbar(); r+=tenbar(); r+=thou(); r+=hun(); r+=ten(); return r+one(); } long thoubar(void) { long t=0; char ch; while((ch=getc(stdin))=='m') t++; ungetc(ch,stdin); return 1000000*t; } long hunbar(void) { return rome('c','d','m', 100000); } long tenbar(void) { return rome('x','l','c', 10000); } long thou(void) { return rome('M','v','x', 1000); } long hun(void) { return rome('C','D','M', 100); } long ten(void) { return rome('X','L','C', 10); } long one(void) { return rome('I','V','X', 1); } long rome(char myC, char myD, char myM,long factor) { char ch=getchar(), what; if(ch==myC) { if((what=getchar())==myM) { if(ishigher(what,myM)) { err(what); return 0; } return 9*factor; } else if(what==myC) { if((what=getchar())==myC) { if((what=getchar())==myC) { if((what=getchar())==myC) { err(what); errmsg("too many repetitions"); return 0; } ungetc(what,stdin); return 4*factor; } else { if(ishigher(what,myC)) { err(what); return 0; } ungetc(what,stdin); return 3*factor; } } else { if(ishigher(what,myC)) { err(what); return 0; } ungetc(what,stdin); return 2*factor; } } else if(what==myD) return 4*factor; else { if(ishigher(what,myD)) { err(what); return 0; } ungetc(what,stdin); return 1*factor; } } else if (ch==myD) { if((what=getchar())==myC) { if((what=getchar())==myC) { if((what=getchar())==myC) { if((what=getchar())==myC) { if((what=getchar())==myC) { err(what); errmsg("too many repetitions"); return 0; } ungetc(what,stdin); return 9*factor; } else { if(ishigher(what,myC)) { err(what); return 0; } ungetc(what,stdin); return 8*factor; } } else { if(ishigher(what,myC)) { err(what); return 0; } ungetc(what,stdin); return 7*factor; } } else { if(ishigher(what,myC)) { err(what); return 0; } ungetc(what,stdin); return 6*factor; } } else { if(ishigher(what,myD)) { err(what); return 0; } ungetc(what,stdin); return 5*factor; } } else { ungetc(ch,stdin); return 0; } } void prnt(long y) { long mills=y/1000000,hthous,tenthous,thous,huns,tens,ones, savey=y, i; char mixedcaseform[80]="", barline[80], baseline[80], ch; if(mills>10) { errmsg("The number is too big to write easily with Roman numerals"); } else { for(i=0;ibi; } /* end ro.c */