%{ /* This is a self-sufficient yacc file. Use as follows. yacc -d baseconv.y gcc y.tab.c ./a.out input.txt If you want the input to be echoed, compile y.tab.c with the -DECHO flag. */ #include #include #include #include #include "y.tab.h" FILE *yyin; int yylex ( ) { char c; while (1) { fscanf(yyin, "%c", &c); if (feof(yyin)) return 0; if ((c == ' ') || (c == '\t')) continue; if ((c >= '0') && (c <= '9')) { yylval = c - '0'; #ifdef ECHO printf("%c", c); #endif return decdigit; } if ((c >= 'a') && (c <= 'z')) { yylval = 10 + (c - 'a'); #ifdef ECHO printf("%c", c); #endif return othdigit; } if ((c == ':') || (c == '.') || (c == '`')) { #ifdef ECHO printf("%c", c); #endif return c; } if (c == '\n') return c; fprintf(stderr, "*** Bad character: '%c'\n", c); } } void yyerror ( char *msg ) { fprintf(stderr, "Error: %s\n", msg); } %} %start PROG %token decdigit othdigit %% BASE : decdigit { $$ = $1; } | decdigit decdigit { $$ = 10 * $1 + $2; } ; DIGIT : decdigit { $$ = $1; } | othdigit { $$ = $1; } ; PROG : PROG LINE '\n' | PROG '\n' { printf("\n"); } | LINE '\n' | '\n' ; LINE : BASE ':' NUM1 | '`' NUM2 ; NUM1 : LIST1 { #ifdef ECHO printf(" = "); #endif printf("%d\n", $$); } | LIST1 '.' DEN LIST2 { #ifdef ECHO printf(" = "); #endif printf("%d + %d / %d = %lf\n", $1, $4, $3, $1 + (double)$4 / (double)$3); } ; DEN : { $$ = 1; } ; LIST1 : { $$ = 0; /* Initialize number to 0 */ } | LIST1 DIGIT { $$ = $1 * $-1 + $2; /* One step of Horner's rule */ } ; LIST2 : { $$ = 0; /* Initialize numerator to 0 */ } | LIST2 DIGIT { $0 *= $-4; /* Update numerator: one step of Horner's rule */ $$ = $1 * $-4 + $2; /* Update denominator */ } ; NUM2 : BS DTR0 MUL DUMMY LIST3 { /* DTR0 stores the current denominator as 0. */ if ($2 == 0) { /* Point not found. An integer is to be printed. */ #ifdef ECHO printf(" = "); #endif printf("%d\n", $5); } else { /* Point found. A floating-point number is to be printed. */ #ifdef ECHO printf(" = "); #endif printf("%d / %d = %lf\n", $5, $2, (double)$5 / (double)$2); } } ; LIST3 : BS DTR MUL DIGIT LIST3 { $-1 = $3 * $1; /* Update previous multiplier by mulytiplying with the base */ $-2 = $2; /* Copy to previous denominator */ $-3 = $1; /* Copy to previous base */ $$ = $3 * $4 + $5; /* Add one term to the numerator */ } | ':' BASE { $-1 = 1; /* Initialize multiplier to 1 */ $-3 = $2; /* Copy base */ $$ = 0; /* Initialize numerator to 0 */ } | BS DTR1 MUL '.' LIST4 { /* DTR1 stores the current denominator as 1. */ $-1 = $3; /* Copy to previous multiplier */ $-2 = $2; /* Copy to previous denominator (not to be updated in LIST3) */ $-3 = $1; /* Copy to previous base */ $$ = $5; /* Copy numerator */ } ; LIST4 : BS DTR MUL DIGIT LIST4 { $-1 = $3 * $1; /* Update multiplier (multiply by base) */ $-2 = $3 * $1; /* Update previous denominator ((multiply by base) */ $-3 = $1; /* Copy to previous base */ $$ = $3 * $4 + $5; /* Update numerator by adding one term */ } | ':' BASE { $-1 = 1; /* Initialize topmost multiplier to 1 */ $-3 = $2; /* Initialize topmost base */ $$ = 0; /* Initialize numerator to 0 */ } ; BS : { /* Stores the base. Copied after it is seen at the end. */ } ; DTR0 : { $$ = 0; /* Initialize denominator to 0 (needed for whole numbers) */ } ; DTR1 : { $$ = 1; /* Initialize denominator to 1 (needed for fractional numbers) */ } ; DTR : { $$ = $-3; /* Copy from previous denominator */ } ; MUL : { /* Multiplier for computing a term in the numerator */ } ; DUMMY : { /* A place-holder for an input token */ } ; %% int main ( int argc , char *argv[] ) { yyin = (argc == 1) ? stdin : (FILE *)fopen(argv[1],"r"); yyparse(); if (argc > 1) fclose(yyin); exit(0); }