#include #include #include #include /* ================================================================ * Calculette s-expression — cours 3 * Usage : * ./calc REPL interactif (évaluation) * ./calc "expression" évalue et affiche * ./calc -vm "expression" compile vers bytecode VM * ./calc -vm "expr" -o f écrit le bytecode dans le fichier f * ./calc -f entree.txt évalue une expression depuis un fichier * ./calc -vm -f e -o f.c lit et compile fichier → fichier * ================================================================ */ /* --------------------------------------------------------------- * Opcodes VM (identiques à vm.c) * --------------------------------------------------------------- */ enum { PUSH, DUP, DROP, SWAP, OVER, ADD, SUB, MUL, NEG, INC, DEC, EQ, NE, GT, LT, JMP, JZ, JNZ, LOAD, STORE, PRINT, PRTS, HALT }; /* --------------------------------------------------------------- * AST — Arbre Syntaxique Abstrait * --------------------------------------------------------------- */ typedef struct Node { int type; /* 0 = nombre, 1 = opérateur */ int valeur; /* pour les nombres */ char op; /* pour les opérateurs : + - * / */ struct Node *gauche; struct Node *droite; } Node; Node *nouveau_nombre(int v) { Node *n = malloc(sizeof(Node)); n->type = 0; n->valeur = v; n->gauche = NULL; n->droite = NULL; return n; } Node *nouvel_operateur(char op, Node *g, Node *d) { Node *n = malloc(sizeof(Node)); n->type = 1; n->op = op; n->gauche = g; n->droite = d; return n; } void liberer_node(Node *n) { if (!n) return; liberer_node(n->gauche); liberer_node(n->droite); free(n); } /* --------------------------------------------------------------- * Tokeniseur * --------------------------------------------------------------- */ static char *pos; static char token[64]; void token_suivant(void) { while (*pos && isspace((unsigned char)*pos)) pos++; if (!*pos) { token[0] = '\0'; return; } if (*pos == '(' || *pos == ')') { token[0] = *pos; token[1] = '\0'; pos++; return; } if (strchr("+-*/", *pos)) { token[0] = *pos; token[1] = '\0'; pos++; return; } if (*pos == '-' || isdigit((unsigned char)*pos)) { int i = 0; if (*pos == '-') { token[i++] = *pos; pos++; } while (isdigit((unsigned char)*pos)) { token[i++] = *pos; pos++; } token[i] = '\0'; return; } fprintf(stderr, "ERREUR: caractère inattendu '%c'\n", *pos); exit(1); } /* --------------------------------------------------------------- * Parseur récursif descendant * expr → '(' op expr expr ')' | nombre * --------------------------------------------------------------- */ Node *parse_expr(void) { if (strcmp(token, "(") == 0) { token_suivant(); if (!strchr("+-*/", token[0]) || token[1] != '\0') { fprintf(stderr, "ERREUR: opérateur attendu, trouvé '%s'\n", token); exit(1); } char op = token[0]; token_suivant(); Node *g = parse_expr(); Node *d = parse_expr(); if (strcmp(token, ")") != 0) { fprintf(stderr, "ERREUR: ')' attendue, trouvé '%s'\n", token); exit(1); } token_suivant(); return nouvel_operateur(op, g, d); } if (isdigit((unsigned char)token[0]) || (token[0] == '-' && isdigit((unsigned char)token[1]))) { int v = atoi(token); token_suivant(); return nouveau_nombre(v); } fprintf(stderr, "ERREUR: token inattendu '%s'\n", token); exit(1); } /* --------------------------------------------------------------- * Évaluateur récursif * --------------------------------------------------------------- */ int evaluer(Node *n) { if (n->type == 0) return n->valeur; int vg = evaluer(n->gauche); int vd = evaluer(n->droite); switch (n->op) { case '+': return vg + vd; case '-': return vg - vd; case '*': return vg * vd; case '/': if (vd == 0) { fprintf(stderr, "ERREUR: division par zéro\n"); exit(1); } return vg / vd; default: fprintf(stderr, "ERREUR: opérateur inconnu\n"); exit(1); } } /* --------------------------------------------------------------- * Bytecode (tableau dynamique pour la compilation VM) * --------------------------------------------------------------- */ typedef struct { int *code; int taille; int capacite; } Bytecode; Bytecode *nouveau_bytecode(void) { Bytecode *bc = malloc(sizeof(Bytecode)); bc->capacite = 64; bc->taille = 0; bc->code = malloc(bc->capacite * sizeof(int)); return bc; } void emettre(Bytecode *bc, int v) { if (bc->taille >= bc->capacite) { bc->capacite *= 2; bc->code = realloc(bc->code, bc->capacite * sizeof(int)); } bc->code[bc->taille++] = v; } void liberer_bytecode(Bytecode *bc) { free(bc->code); free(bc); } /* --------------------------------------------------------------- * Compilateur — s-expression → bytecode VM * Parcours postfixé : gauche, droite, opérateur * --------------------------------------------------------------- */ void compiler_expr(Bytecode *bc, Node *n) { if (n->type == 0) { emettre(bc, PUSH); emettre(bc, n->valeur); return; } compiler_expr(bc, n->gauche); compiler_expr(bc, n->droite); switch (n->op) { case '+': emettre(bc, ADD); break; case '-': emettre(bc, SUB); break; case '*': emettre(bc, MUL); break; case '/': fprintf(stderr, "ERREUR: la VM n'a pas d'instruction DIV\n"); fprintf(stderr, " (la division est laissée en exercice)\n"); exit(1); } } /* --------------------------------------------------------------- * Noms des opcodes (désassembleur + générateur C) * --------------------------------------------------------------- */ static const char *nom_opcode(int op) { static const char *noms[] = { "PUSH", "DUP", "DROP", "SWAP", "OVER", "ADD", "SUB", "MUL", "NEG", "INC", "DEC", "EQ", "NE", "GT", "LT", "JMP", "JZ", "JNZ", "LOAD", "STORE", "PRINT", "PRTS", "HALT" }; if (op >= 0 && op < HALT + 1) return noms[op]; return "???"; } int opcode_prend_operande(int op) { return op == PUSH || op == JMP || op == JZ || op == JNZ || op == LOAD || op == STORE || op == PRTS; } /* --------------------------------------------------------------- * Afficher le bytecode (désassemblage + tableau C) * --------------------------------------------------------------- */ void afficher_bytecode(Bytecode *bc) { printf("\nDésassemblage VM :\n"); int i = 0; while (i < bc->taille) { printf("%4d: ", i); int op = bc->code[i]; if (opcode_prend_operande(op)) { printf("%-5s %d\n", nom_opcode(op), bc->code[i + 1]); i += 2; } else { printf("%s\n", nom_opcode(op)); i++; } } printf("\nTableau C (%d entiers) :\n", bc->taille); printf("int bytecode[] = {\n"); i = 0; while (i < bc->taille) { printf(" %s", nom_opcode(bc->code[i])); if (opcode_prend_operande(bc->code[i])) { printf(", %d", bc->code[i + 1]); i += 2; } else { i++; } if (i < bc->taille) printf(","); printf("\n"); } printf("};\n"); } /* --------------------------------------------------------------- * Générer un fichier .c avec le bytecode * --------------------------------------------------------------- */ void ecrire_fichier_c(const char *chemin, Bytecode *bc, const char *expr) { FILE *f = fopen(chemin, "w"); if (!f) { fprintf(stderr, "ERREUR: impossible d'écrire '%s'\n", chemin); exit(1); } fprintf(f, "/* Bytecode VM généré par calc.c\n"); fprintf(f, " * Expression : %s\n", expr); fprintf(f, " * Usage : copier dans vm.c ou #include\n"); fprintf(f, " */\n\n"); fprintf(f, "int bytecode[] = {\n"); int i = 0; while (i < bc->taille) { fprintf(f, " %s", nom_opcode(bc->code[i])); if (opcode_prend_operande(bc->code[i])) { fprintf(f, ", %d", bc->code[i + 1]); i += 2; } else { i++; } if (i < bc->taille) fprintf(f, ","); fprintf(f, "\n"); } fprintf(f, "};\n"); fprintf(f, "\nint taille_bytecode = sizeof(bytecode) / sizeof(bytecode[0]);\n"); fclose(f); printf("Écrit dans %s\n", chemin); } /* --------------------------------------------------------------- * Traiter une expression : évaluer ou compiler * --------------------------------------------------------------- */ void traiter_expression(const char *ligne, int mode_vm, const char *outpath) { pos = (char *)ligne; token_suivant(); if (token[0] == '\0') return; Node *ast = parse_expr(); if (token[0] != '\0') { fprintf(stderr, "ERREUR: token superflu '%s'\n", token); liberer_node(ast); return; } if (mode_vm) { Bytecode *bc = nouveau_bytecode(); printf("Expression : %s\n", ligne); compiler_expr(bc, ast); emettre(bc, PRINT); emettre(bc, HALT); afficher_bytecode(bc); if (outpath) ecrire_fichier_c(outpath, bc, ligne); liberer_bytecode(bc); } else { printf(" => %d\n", evaluer(ast)); } liberer_node(ast); } /* ================================================================ * REPL (Read Eval Print Loop) * ================================================================ */ void repl(void) { char ligne[1024]; printf("> "); fflush(stdout); while (fgets(ligne, sizeof(ligne), stdin)) { size_t len = strlen(ligne); if (len > 0 && ligne[len - 1] == '\n') ligne[len - 1] = '\0'; if (strcmp(ligne, "quit") == 0 || strcmp(ligne, "exit") == 0) break; if (ligne[0] != '\0') traiter_expression(ligne, 0, NULL); printf("> "); fflush(stdout); } printf("Au revoir !\n"); } /* --------------------------------------------------------------- * Point d'entrée — analyse des arguments * --------------------------------------------------------------- */ int main(int argc, char *argv[]) { int mode_vm = 0; const char *expr = NULL; const char *fichier_entree = NULL; const char *fichier_sortie = NULL; /* * argv[1] argv[2] argv[3] argv[4] * -------- -------- -------- -------- * "expression" — — — * -vm "expression" — — * -vm "expression" -o fichier * -f fichier.txt — — * -vm -f fichier -o fichier.c * etc. * * On fait une boucle simple pour tout analyser. */ for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-vm") == 0) { mode_vm = 1; } else if (strcmp(argv[i], "-o") == 0) { if (++i >= argc) { fprintf(stderr, "ERREUR: -o nécessite un fichier\n"); return 1; } fichier_sortie = argv[i]; } else if (strcmp(argv[i], "-f") == 0) { if (++i >= argc) { fprintf(stderr, "ERREUR: -f nécessite un fichier\n"); return 1; } fichier_entree = argv[i]; } else { expr = argv[i]; } } /* Si -f est présent, lire l'expression depuis le fichier */ if (fichier_entree) { FILE *f = fopen(fichier_entree, "r"); if (!f) { fprintf(stderr, "ERREUR: impossible de lire '%s'\n", fichier_entree); return 1; } /* on concatène tout le fichier dans un tampon */ static char buf[4096]; buf[0] = '\0'; size_t pos_buf = 0; char ligne[512]; while (fgets(ligne, sizeof(ligne), f)) { size_t l = strlen(ligne); if (l > 0 && ligne[l - 1] == '\n') ligne[l - 1] = ' '; if (pos_buf + l < sizeof(buf)) { memcpy(buf + pos_buf, ligne, l); pos_buf += l; } } buf[pos_buf] = '\0'; fclose(f); expr = buf; } if (expr) { traiter_expression(expr, mode_vm, fichier_sortie); } else { /* Mode REPL interactif */ if (mode_vm) printf("(mode VM sans expression — bascule en REPL standard)\n"); repl(); } return 0; }