#include #include #include #include #define MAX_HODNOT 1009 #define MAX_DELKA 1000 /* Typy tokenu (a vrcholu syntaktickeho stromu). */ enum typ { CISLO, PROMENNA, PLUS, MINUS, KRAT, DELENO, OT_ZAVORKA, ZAV_ZAVORKA, KONEC_VYRAZU }; /* A jejich jmena, pro chybove hlasky. */ const char *jmena_typu[] = { "cislo", "promenna", "plus", "minus", "krat", "deleno", "oteviraci zavorka", "zaviraci zavorka", "konec vyrazu" }; /* Znaky pro operatory. */ char oper[] = {0, 0, '+' ,'-' ,'*' ,'/' ,0 ,0 ,0}; /* Syntakticky strom. */ struct vrchol { enum typ typ; /* Typ vrcholu. */ unsigned hodnota; /* Cislo hodnoty (pomocne promenne). */ unsigned vyhodnoceno; /* 1 pokud hodnota jiz byla vypoctena. */ int podv[2]; /* Podvyrazy, resp. jejich cisla hodnot. */ }; /* Tabulky hodnot vyrazu, uzivatelskych promennych a cisel. Cisla hodnot se rozlisi podle zbytku po deleni 4 -- vyrazy jsou 1, promenne 2, cisla 3. */ #define JE_VYRAZ(N) ((N) % 4 == 1) #define JE_PROMENNA(N) ((N) % 4 == 2) #define JE_CISLO(N) ((N) % 4 == 3) #define DEF_VYRAZU(N) (&hodnota_na_vyraz[(N) / 4]) #define JMENO_PROMENNE(N) (hodnota_na_promennou[(N) / 4]) #define HODNOTA_CISLA(N) (hodnota_na_cislo[(N) / 4]) /* Tabulka cisel hodnot vyrazu (ty take tvori vrcholy syntaktickeho stromu) a hashovaci tabulka, ktera prevadi vyrazy na hodnoty. */ struct vrchol hodnota_na_vyraz[MAX_HODNOT]; unsigned vyraz_na_hodnotu[MAX_HODNOT]; unsigned max_hodnota_vyrazu = 0; /* Tabulka cisel hodnot ciselnych konstant v programu, a hash, ktera prevadi cislo na hodnotu. Konstanty do 8 jsou pro urychleni predgenerovane vzdy. */ unsigned hodnota_na_cislo[MAX_HODNOT] = {0,1,2,3,4,5,6,7}; unsigned cislo_na_hodnotu[MAX_HODNOT] = {3,7,11,15,19,23,27,31}; unsigned max_hodnota_cisla = 7; /* Tabulka cisel hodnot uzivatelskych promennych, a hash, ktera prevadi jmeno promenne na hodnotu. */ char *hodnota_na_promennou[MAX_HODNOT]; unsigned promenna_na_hodnotu[MAX_HODNOT]; unsigned max_hodnota_promenne = 0; /* Vstupni retezec. */ char vstup[MAX_DELKA]; /************************************* Funkce pro praci s hodnotami vyrazu *************************************/ unsigned vrat_hodnotu_vyrazu (unsigned opl, enum typ op, unsigned opr) { unsigned hash, h; struct vrchol *v; hash = (op + 167 * opl + 257 * opr) % MAX_HODNOT; while ((h = vyraz_na_hodnotu[hash]) != 0) { v = hodnota_na_vyraz + h; if (v->typ == op && v->podv[0] == opl && v->podv[1] == opr) return v->hodnota; hash++; if (hash == MAX_HODNOT) hash = 0; } /* Novy vyraz. */ h = ++max_hodnota_vyrazu; vyraz_na_hodnotu[hash] = h; v = hodnota_na_vyraz + h; h = 4 * h + 1; v->hodnota = h; v->typ = op; v->podv[0] = opl; v->podv[1] = opr; return h; } unsigned vrat_hodnotu_promenne (char *prom) { unsigned hash, h; char *p; hash = 0; for (p = prom; *p; p++) hash = (67 * hash + *p) % MAX_HODNOT; while ((h = promenna_na_hodnotu[hash]) != 0) { if (strcmp (prom, hodnota_na_promennou[h]) == 0) return 4 * h + 2; hash++; if (hash == MAX_HODNOT) hash = 0; } h = ++max_hodnota_promenne; hodnota_na_promennou[h] = strdup (prom); promenna_na_hodnotu[hash] = h; return 4 * h + 2; } unsigned vrat_hodnotu_cisla (unsigned cislo) { unsigned hash, h; if (cislo < 8) return cislo_na_hodnotu[cislo]; hash = cislo % MAX_HODNOT; while ((h = cislo_na_hodnotu[hash]) != 0) { if (HODNOTA_CISLA (h) == cislo) return h; hash++; if (hash == MAX_HODNOT) hash = 0; } h = ++max_hodnota_vyrazu; hodnota_na_cislo[h] = cislo; h = 4 * h + 3; cislo_na_hodnotu[hash] = h; return h; } /******************** Lexikalni analyza. ********************/ /* Obcas se nam hodi token nezpracovat rovnou, vratit ho zpatky, a nechat jeho zpracovani na starost jine funkci. */ enum typ vraceny_typ; unsigned vraceny_hodnota, vraceny_pos, vraceny_token; void vrat_token (enum typ typ, unsigned hodnota, unsigned pos) { /* Vratit jde jen jeden token. */ if (vraceny_token) abort (); vraceny_typ = typ; vraceny_hodnota = hodnota; vraceny_pos = pos; vraceny_token = 1; } void dalsi_token (enum typ *typ, unsigned *hodnota, unsigned *pos) { /* Aktualni poloha ve vstupu. */ static unsigned index = 0; char znak; if (vraceny_token) { *typ = vraceny_typ; *hodnota = vraceny_hodnota; *pos = vraceny_pos; vraceny_token = 0; return; } restart: znak = vstup[index]; /* Preskocime mezery. */ while (isspace(znak)) znak = vstup[++index]; *hodnota = 0; *pos = index; if (znak == 0) { *typ = KONEC_VYRAZU; return; } index++; switch (znak) { case '+': *typ = PLUS; return; case '-': *typ = MINUS; return; case '*': *typ = KRAT; return; case '/': *typ = DELENO; return; case '(': *typ = OT_ZAVORKA; return; case ')': *typ = ZAV_ZAVORKA; return; default: break; } if (isdigit (znak)) { unsigned val = 0, prilis_velke = 0; /* Cislo, nacteme jeho hodnotu. */ while (1) { unsigned cislice = znak - '0'; if (~0u / 10 < val) { prilis_velke = 1; break; } val *= 10; if (~0u - cislice < val) { prilis_velke = 1; break; } val += cislice; znak = vstup[index]; if (!isdigit (znak)) break; index++; } if (prilis_velke) { /* Cislo je moc velke, zahodime zbyvajici cislice. */ fprintf (stderr, "Prilis velke cislo na pozici %u.\n", *pos); while (1) { znak = vstup[index]; if (!isdigit (znak)) break; index++; } } *typ = CISLO; *hodnota = vrat_hodnotu_cisla (val); return; } if (isalpha (znak)) { while (1) { znak = vstup[index]; if (!isalnum (znak)) break; index++; } vstup[index] = 0; *typ = PROMENNA; *hodnota = vrat_hodnotu_promenne (vstup + *pos); vstup[index] = znak; return; } /* Neznamy znak. */ fprintf (stderr, "Neznamy znak '%c' na pozici %u.\n", znak, *pos); goto restart; } /*********************** Zjednodusovani vyrazu ***********************/ unsigned postav_strom (unsigned opl, enum typ op, unsigned opr, unsigned pos) { unsigned tmp, val; if (op == PLUS || op == KRAT) { /* Pro komutativni operatory usporadame podvyrazy tak, aby vpravo byly vzdy konstanty, a jinak podle velikosti. */ if (JE_CISLO (opl) || (!JE_CISLO (opr) && opl > opr)) { tmp = opl; opl = opr; opr = tmp; } } switch (op) { case PLUS: if (JE_CISLO (opl)) { val = HODNOTA_CISLA (opl) + HODNOTA_CISLA (opr); return vrat_hodnotu_cisla (val); } /* x + 0 = x */ if (JE_CISLO (opr) && HODNOTA_CISLA (opr) == 0) return opl; /* x + x = x * 2 */ if (opl == opr) return postav_strom (opl, KRAT, vrat_hodnotu_cisla (2), pos); if (JE_CISLO (opr) && JE_VYRAZ (opl)) { struct vrchol *l = DEF_VYRAZU (opl); /* (x + cst1) + cst2) = x + (cst1 + cst2) */ if (l->typ == PLUS && JE_CISLO (l->podv[1])) { tmp = postav_strom (l->podv[1], PLUS, opr, pos); return postav_strom (l->podv[0], PLUS, tmp, pos); } /* (cst1 - x) + cst2 = (cst1 + cst2) - x */ if (l->typ == MINUS && JE_CISLO (l->podv[0])) { tmp = postav_strom (l->podv[0], PLUS, opr, pos); return postav_strom (tmp, MINUS, l->podv[1], pos); } } break; case MINUS: if (JE_CISLO (opl) && JE_CISLO (opr)) { val = HODNOTA_CISLA (opl) - HODNOTA_CISLA (opr); return vrat_hodnotu_cisla (val); } /* x - 0 = x */ if (JE_CISLO (opr) && HODNOTA_CISLA (opr) == 0) return opl; /* x - cst = x + (-cst) */ if (JE_CISLO (opr)) { val = HODNOTA_CISLA (opr); tmp = vrat_hodnotu_cisla (-val); return postav_strom (opl, PLUS, tmp, pos); } /* x - x = 0 */ if (opl == opr) return vrat_hodnotu_cisla (0); break; case KRAT: if (JE_CISLO (opl)) { val = HODNOTA_CISLA (opl) * HODNOTA_CISLA (opr); return vrat_hodnotu_cisla (val); } /* x * 0 = 0 */ if (JE_CISLO (opr) && HODNOTA_CISLA (opr) == 0) return vrat_hodnotu_cisla (0); /* x * 1 = x */ if (JE_CISLO (opr) && HODNOTA_CISLA (opr) == 1) return opl; if (JE_CISLO (opr) && JE_VYRAZ (opl)) { struct vrchol *l = DEF_VYRAZU (opl); /* (x * cst1) * cst2 = x * (cst1 * cst2) */ if (l->typ == KRAT && JE_CISLO (l->podv[1])) { tmp = postav_strom (l->podv[1], KRAT, opr, pos); return postav_strom (l->podv[0], KRAT, tmp, pos); } } break; case DELENO: /* x / 0 je chyba */ if (HODNOTA_CISLA (opr) == 0) { fprintf (stderr, "Deleni 0 na pozici %u\n", pos); return opl; } if (JE_CISLO (opl) && JE_CISLO (opr)) { val = HODNOTA_CISLA (opl) / HODNOTA_CISLA (opr); return vrat_hodnotu_cisla (val); } /* x / 1 = x */ if (JE_CISLO (opr) && HODNOTA_CISLA (opr) == 1) return opl; /* x / x = 1 */ if (opl == opr) return vrat_hodnotu_cisla (1); break; default: abort (); } return vrat_hodnotu_vyrazu (opl, op, opr); } /********************* Syntakticka analyza *********************/ unsigned cti_term (void); unsigned cti_faktor (void); unsigned cti_vyraz (unsigned zacatek_zavorky) { enum typ token; unsigned hodnota, pos, hl, hr; hl = cti_faktor (); while (1) { /* Cteme zbyvajici faktory, dokud nedojdeme na konec vyrazu ci zavorky. */ dalsi_token (&token, &hodnota, &pos); switch (token) { case KONEC_VYRAZU: /* Pokud mame otevrenou zavorku, primyslime si novou zaviraci. */ if (zacatek_zavorky != ~0) { fprintf (stderr, "Zavorka na pozici %u neni ukoncena.\n", zacatek_zavorky); vrat_token (ZAV_ZAVORKA, 0, pos); } return hl; case ZAV_ZAVORKA: if (zacatek_zavorky == ~0) { /* Pokud nemame oteviraci zavorku, zaviraci zahodime. */ fprintf (stderr, "Zavorka na pozici %u nema odpovidajici oteviraci.\n", pos); break; } vrat_token (ZAV_ZAVORKA, 0, pos); return hl; case OT_ZAVORKA: case CISLO: case PROMENNA: /* Tady neco chybi. */ fprintf (stderr, "Ocekavano +, -, nebo %s, nalezeno %s, na pozici %u\n", zacatek_zavorky == ~0 ? "konec vyrazu" : ")", jmena_typu[token], pos); /* Primyslime si PLUS, a propadneme. */ vrat_token (token, hodnota, pos); token = PLUS; case PLUS: case MINUS: /* Tohle muze nastat jedine pokud nastala nejaka jina chyba, napriklad pokud tu byla nesparovana zaviraci zavorka, ktera ukoncila cteni faktoru. Ted uz to nijak rozumne nevyresime, proste se vratime do alespon trochu konzistentniho stavu tak, ze nacteme faktor a spojime ho s docasnym vysledkem. */ case KRAT: case DELENO: hr = cti_faktor (); hl = postav_strom (hl, token, hr, pos); break; default: abort (); } } } unsigned cti_faktor (void) { enum typ token; unsigned hodnota, pos, hl, hr; hl = cti_term (); while (1) { dalsi_token (&token, &hodnota, &pos); switch (token) { case KONEC_VYRAZU: case ZAV_ZAVORKA: case PLUS: case MINUS: /* Tady neco chybi, nicmene nechme to na cti_vyraz. */ case OT_ZAVORKA: case CISLO: case PROMENNA: vrat_token (token, hodnota, pos); return hl; case KRAT: case DELENO: hr = cti_term (); hl = postav_strom (hl, token, hr, pos); break; default: abort (); } } } unsigned cti_term (void) { enum typ token; unsigned hodnota, pos, hl; dalsi_token (&token, &hodnota, &pos); switch (token) { case OT_ZAVORKA: hl = cti_vyraz (pos); dalsi_token (&token, &hodnota, &pos); /* cti_vyraz nam vyrobi zavorku, pokud chybi. */ if (token != ZAV_ZAVORKA) abort (); return hl; case CISLO: case PROMENNA: return hodnota; case KONEC_VYRAZU: case ZAV_ZAVORKA: case PLUS: case MINUS: case KRAT: case DELENO: /* Tady neco chybi. Primysleme si nejaky neskodny faktor, treba 0. */ vrat_token (token, hodnota, pos); return vrat_hodnotu_cisla (0); default: abort (); } } /******************** Semanticka analyza ********************/ /* Cislo hodnoty pro vysledek. */ unsigned result; void vypis_hodnotu (unsigned hodnota) { if (JE_CISLO (hodnota)) printf ("%u", HODNOTA_CISLA (hodnota)); else if (JE_PROMENNA (hodnota)) printf ("%s", JMENO_PROMENNE (hodnota)); else if (hodnota == result) printf ("_result"); else printf ("_tmp%u", hodnota / 4); } void vyhodnot (unsigned hodnota) { struct vrchol *v; if (!JE_VYRAZ (hodnota)) return; v = DEF_VYRAZU (hodnota); if (v->vyhodnoceno) return; vyhodnot (v->podv[0]); vyhodnot (v->podv[1]); /* Protoze si pamatujeme vzdy nejstarsi zpusob, jak hodnota vznikla, nemuze se stat, ze by se nam vyskytla v nekterem z podstromu. */ if (v->vyhodnoceno) abort (); printf ("assign "); vypis_hodnotu (hodnota); printf (" ("); vypis_hodnotu (v->podv[0]); printf (" %c ", oper[v->typ]); vypis_hodnotu (v->podv[1]); printf (")\n"); v->vyhodnoceno = 1; } /********************************* A ted to cele spojime dohromady *********************************/ int main(void) { unsigned hodnota; fgets (vstup, MAX_DELKA, stdin); hodnota = cti_vyraz (~0); result = hodnota; vyhodnot (hodnota); /* Pokud je hodnota jen promenna nebo cislo, je jeste potreba ji priradit do vysledku. */ if (!JE_VYRAZ (hodnota)) { printf ("assign _result "); vypis_hodnotu (hodnota); printf ("\n"); } return 0; }