%{ #include #include #include int yylex(); void yyerror(char *s) { fflush(stdout); fprintf(stderr, "%s\n", s); } /***************************************************************************/ /* Data structures for storing a programme. */ typedef struct var // a variable { char *name; int class; int value; // 0 = bool, 1 = int struct var *next; } var; typedef struct varlist // variable reference (used for print statement) { struct var *var; struct varlist *next; } varlist; typedef struct expr // boolean expression { int type; // TRUE, FALSE, OR, AND, EQUIV, NOT, 0 (variable), PLUS, TIMES, INT, EQUALS, LT int class; // 0 = bool, 1 = int, 2 = both var *var; struct expr *left, *right; } expr; typedef struct stmt // command { int type; // ASSIGN, ';', WHILE, IF, PRINT var *var; expr *expr; struct stmt *left, *right; varlist *list; } stmt; /****************************************************************************/ /* All data pertaining to the programme are accessible from these two vars. */ var *program_vars; var *program_ints; stmt *program_stmts; /****************************************************************************/ /* Functions for settting up data structures at parse time. */ var* make_ident (char *s, int class) // type = 0 for a boolean var, type = 1 for an int var { var *v = malloc(sizeof(var)); v->name = s; v->class = class; v->value = 0; // make variable false initially v->next = NULL; return v; } var* find_ident (char *s) { var *v = program_vars; while (v && strcmp(v->name,s)) v = v->next; if (!v) { // Check in int vars v = program_ints; while (v && strcmp(v->name, s)) v = v->next; } if (!v) { yyerror("undeclared variable"); exit(1); } return v; } var* make_int(int val) { var *v = malloc(sizeof(var)); v->name = NULL; v->class = 1; // int v->value = val; v->next = NULL; return v; } varlist* make_varlist (char *s) { var *v = find_ident(s); varlist *l = malloc(sizeof(varlist)); l->var = v; l->next = NULL; return l; } expr* make_expr (int type, int class, var *var, expr *left, expr *right) { expr *e = malloc(sizeof(expr)); e->type = type; e->class = class; if (var != NULL) e->class = var->class; e->var = var; e->left = left; e->right = right; return e; } stmt* make_stmt (int type, var *var, expr *expr, stmt *left, stmt *right, varlist *list) { stmt *s = malloc(sizeof(stmt)); s->type = type; s->var = var; s->expr = expr; s->left = left; s->right = right; s->list = list; return s; } %} /****************************************************************************/ /* types used by terminals and non-terminals */ %union { char *i; var *v; varlist *l; expr *e; stmt *s; int val; } %type bool_declist %type int_declist %type varlist %type expr %type stmt assign %token BOOL TYPE_INT WHILE DO OD IF THEN ELSE FI ASSIGN PRINT OR AND XOR NOT TRUE FALSE %token IDENT %token INT %left ';' %left OR XOR %left AND %left EQUIV %left EQUALS %left LT %left PLUS %left TIMES %right NOT %% prog : bools ints stmt { program_stmts = $3; } | bools stmt { program_stmts = $2; } | ints stmt { program_stmts = $2; } | stmt { program_stmts = $1; } bools : BOOL bool_declist ';' { program_vars = $2; } bool_declist : IDENT { $$ = make_ident($1, 0); } | bool_declist ',' IDENT { ($$ = make_ident($3, 0))->next = $1; } ints : TYPE_INT int_declist ';' { program_ints = $2; } int_declist : IDENT { $$ = make_ident($1, 1); } | int_declist ',' IDENT { ($$ = make_ident($3, 1))->next = $1; } stmt : assign | stmt ';' stmt { $$ = make_stmt(';',NULL,NULL,$1,$3,NULL); } | WHILE expr DO stmt OD { $$ = make_stmt(WHILE,NULL,$2,$4,NULL,NULL); } | IF expr THEN stmt ELSE stmt FI { $$ = make_stmt(IF,NULL,$2,$4,$6,NULL); } | IF expr THEN stmt FI { $$ = make_stmt(IF,NULL,$2,$4,NULL,NULL); } | PRINT varlist { $$ = make_stmt(PRINT,NULL,NULL,NULL,NULL,$2); } assign : IDENT ASSIGN expr { $$ = make_stmt(ASSIGN,find_ident($1),$3,NULL,NULL,NULL); } varlist : IDENT { $$ = make_varlist($1); } | varlist ',' IDENT { ($$ = make_varlist($3))->next = $1; } expr : IDENT { $$ = make_expr(0, 2, find_ident($1), NULL, NULL); } | expr XOR expr { $$ = make_expr(XOR, 0, NULL, $1, $3); } | expr OR expr { $$ = make_expr(OR, 0, NULL, $1, $3); } | expr AND expr { $$ = make_expr(AND, 0, NULL, $1, $3); } | expr EQUIV expr { $$ = make_expr(EQUIV, 0, NULL, $1, $3); } | NOT expr { $$ = make_expr(NOT, 0, NULL, $2, NULL); } | TRUE { $$ = make_expr(TRUE, 0, NULL, NULL, NULL); } | FALSE { $$ = make_expr(FALSE, 0, NULL, NULL, NULL); } | '(' expr ')' { $$ = $2; } | INT { $$ = make_expr(INT, 1, make_int($1), NULL, NULL); } | expr TIMES expr { $$ = make_expr(TIMES, 1, NULL, $1, $3); } | expr PLUS expr { $$ = make_expr(PLUS, 1, NULL, $1, $3); } | expr LT expr { $$ = make_expr(LT, 0, NULL, $1, $3); } | expr EQUALS expr { $$ = make_expr(EQUALS, 0, NULL, $1, $3); } %% #include "langlex.c" /****************************************************************************/ /* programme interpreter : */ int eval (expr *e) { switch (e->type) { case TRUE: return 1; case FALSE: return 0; case XOR: { if (e->left->class != 0 || e->right->class != 0) { yyerror("A xor expression has wrong types"); exit(-1); } return eval(e->left) ^ eval(e->right); } case OR: { if (e->left->class != 0 || e->right != NULL && e->right->class != 0) { yyerror("An or expression has wrong types"); exit(-1); } return eval(e->left) || eval(e->right); } case AND: { if (e->left->class != 0 || e->right->class != 0) { yyerror("An and expression has wrong types"); exit(-1); } return eval(e->left) && eval(e->right); } case EQUIV: { if (e->left->class != 0 || e->right->class != 0) { yyerror("An equivalence expression has wrong types"); exit(-1); } int right = eval(e->right); int left = eval(e->left); return (right && left) || (!right && !left); // Équivalent avec left == right avec la définition. // Toutefois, on privilégie cette forme car faux = 0 et vrai = tout entier non nul } case NOT: { if (e->left->class != 0) { yyerror("A not expression has wrong types"); exit(-1); } return !eval(e->left); } case PLUS: { if (e->left->class != 1 || e->right->class != 1) { yyerror("A plus expression has wrong types"); exit(-1); } return eval(e->left) + eval(e->right); } case TIMES: { if (e->left->class != 1 || e->right->class != 1) { yyerror("A times expression has wrong types"); exit(-1); } return eval(e->left) * eval(e->right); } case EQUALS: { if (e->left->class != 1 || e->right->class != 1) { yyerror("An equality expression has wrong types"); exit(-1); } printf("%d, %d\n", eval(e->left), eval(e->right)); return eval(e->left) == eval(e->right) ? 1 : 0; } case LT: { if (e->left->class != 1 || e->right->class != 1) { yyerror("A comparison expression has wrong types"); exit(-1); } return eval(e->left) < eval(e->right) ? 1 : 0; } case INT: case 0: return e->var->value; } } void print_vars (varlist *l) { // Attention, varlist is in reverse order (this was easier to set up // at parse time, see the code for varlist in the grammar). if (!l) return; print_vars(l->next); if (l->var->class == 0) printf("%s = %c ", l->var->name, l->var->value ? 'T' : 'F'); else if (l->var->class == 1) printf("%s = %d ", l->var->name, l->var->value); else fprintf(stderr, "The type of the var %s is unknown", l->var->name); } void execute (stmt *s) { switch(s->type) { case ASSIGN: if (s->var->class != s->expr->class) { yyerror("An assignment has the wrong type"); exit(-1); } s->var->value = eval(s->expr); break; case ';': execute(s->left); execute(s->right); break; case WHILE: { if (s->expr->class != 0 && s->expr->class != 2) { yyerror("A while condition must be a boolean expression"); exit(-1); } while (eval(s->expr)) execute(s->left); break; } case IF: { if (s->expr->class != 0 && s->expr->class != 2) { yyerror("An if condition must be a boolean expression"); exit(-1); } if (eval(s->expr)) execute(s->left); else if (s->right != NULL) execute(s->right); break; } case PRINT: print_vars(s->list); puts(""); break; } } /****************************************************************************/ int main (int argc, char **argv) { if (argc <= 1) { yyerror("no file specified"); exit(1); } yyin = fopen(argv[1],"r"); if (!yyparse()) execute(program_stmts); }