Το μεταεργαλείο yacc

Διομήδης Σπινέλλης
Τμήμα Διοικητικής Επιστήμης και Τεχνολογίας
Οικονομικό Πανεπιστήμιο Αθηνών
dds@aueb.gr

Το μεταεργαλείο yacc

Διαδικασία χρήσης

Σημείωση

Στο εργαστήριο θα χρησιμοποιηθεί το πρόγραμμα bison που είναι συμβατό υπερσύνολο του yacc.

Αρχείο εισόδου

Δηλώσεις του yacc

Οι δηλώσεις του yacc μας επιτρέπουν να ορίσουμε:

Συντακτικοί κανόνες

Τερματικά και μη τερματικά σύμβολα

Τιμές των συμβόλων

Σημασιολογικοί κανόνες

Απλό παράδειγμα

Το παρακάτω πρόγραμμα υλοποιεί μια απλή αριθμομηχανή.
/* Infix notation calculator--calc */

%{
#define YYSTYPE double
#include <math.h>               /* Needed for pow */
%}

/* YACC Declarations */
%token NUM
%left '-' '+'
%left '*' '/'
%left NEG     /* negation--unary minus */
%right '^'    /* exponentiation        */

/* Grammar follows */
%%
input:    /* empty string */
        | input line
;

line:     '\n'
        | exp '\n'  { printf ("\t%.10g\n", $1); }
;

exp:      NUM                { $$ = $1;         }
        | exp '+' exp        { $$ = $1 + $3;    }
        | exp '-' exp        { $$ = $1 - $3;    }
        | exp '*' exp        { $$ = $1 * $3;    }
        | exp '/' exp        { $$ = $1 / $3;    }
        | '-' exp  %prec NEG { $$ = -$2;        }
        | exp '^' exp        { $$ = pow ($1, $3); }
        | '(' exp ')'        { $$ = $2;         }
;
%%
#include <ctype.h>
#include <stdio.h>

/* Lexical analyzer */
int
yylex ()
{
        int c;

        /* skip white space  */
        while ((c = getchar ()) == ' ' || c == '\t')  
                ;
        /* process numbers   */
        if (c == '.' || isdigit (c)) {
                ungetc (c, stdin);
                scanf ("%lf", &yylval);
                return NUM;
        }
        /* return end-of-file  */
        if (c == EOF)                            
                return 0;
        /* return single chars */
        return c;                                
}

main ()
{
        yyparse();
}

/* error function */
void
yyerror (char *s)  /* Called by yyparse on error */
{
        printf("%s\n", s);
}

Μεταγλώττιση και χρήση

Η διαδικασία μεταγλώττισης και χρήσης του αρχείου της αριθμομηχανής (αν έχει ονομαστεί calc.y) φαίνεται από τις παρακάτω εντολές:
$ yacc -vd calc.y
$ ls -rtl
total 23
-rw-r--r--   1 dspin    users        1304 Nov 16 22:51 calc.y
-rw-r--r--   1 dspin    users          32 Nov 16 22:51 y.tab.h
-rw-r--r--   1 dspin    users       12078 Nov 16 22:51 y.tab.c
-rw-r--r--   1 dspin    users        3773 Nov 16 22:51 y.output
$ cc -o calc y.tab.c -lm
$ ./calc
1+2*3
        7
3*(1+2)
        9
sdkjfh
syntax error
$

Τρόπος λειτουργίας

Το παρακάτω απόσπασμα από το αρχείο y.output απεικονίζει μια κατάσταση, τους κανόνες που είναι υποψήφιοι για αναγνώριση (τι βρίσκεται στη στοίβα), και τις ενέργειες ανάλογα με το σύμβολο που θα διαβαστεί:
state 17
        exp : exp . '+' exp  (6)
        exp : exp . '-' exp  (7)
        exp : exp '-' exp .  (7)
        exp : exp . '*' exp  (8)
        exp : exp . '/' exp  (9)
        exp : exp . '^' exp  (11)

        '*'  shift 12
        '/'  shift 13
        '^'  shift 14
        '-'  reduce 7
        '+'  reduce 7
        '\n'  reduce 7
        ')'  reduce 7

Ασκήσεις

  1. Να υλοποιήσετε με το μεταεργαλείο yacc έναν υπολογιστή που να επεξεργάζεται διανύσματα (δύο διαστάσεων) με:
    1. τους δυαδικούς τελεστές σε διανύσματα + -
    2. το μοναδικό τελεστή -
    3. τους τελεστές μεταξύ διανύσματος και σταθεράς * /
    4. παρενθέσεις
    Το κάθε διάνυσμα θα μπορεί να γράφεται ως [χ, ψ]. Κάθε γραμμή εισόδου θα αποτελεί μια εντολή για πράξη. Παράδειγμα:
    [1, 2]
    Result = [1, 2]
    [1, 1] + [1, 2]
    Result = [2, 2]
    -([1, 2] * 2 + [1, 1])
    Result = [-3, -5]
    

Βιβλιογραφία