From 95e73b8e7641658b54a5b4d86c250b1684ff3bcd Mon Sep 17 00:00:00 2001 From: M3tex <82944306+M3tex@users.noreply.github.com> Date: Sun, 7 Jan 2024 23:49:02 +0100 Subject: [PATCH] Fin du projet --- .gitignore | 3 +- README.md | 3 + arc_manpage | 64 +++++ compte_rendu.txt | 192 ++++++++++++++ include/arc_options.h | 7 + include/arc_utils.h | 10 +- include/ast.h | 132 ++++++++-- include/codegen.h | 35 ++- include/preprocessor.h | 2 - include/ram_os.h | 85 ++++--- include/semantic.h | 32 ++- include/symbol_table.h | 12 +- libstd/math.algo | 25 +- libstd/utilitaires.algo | 63 ++++- makefile | 2 +- src/arc_options.c | 89 +++++++ src/arc_utils.c | 127 ++++++++-- src/ast.c | 218 +++++++++++++++- src/codegen.c | 371 +++++++++++++++++++++------ src/lexer.lex | 75 +++++- src/parser.y | 164 ++++++------ src/preprocessor.c | 18 +- src/semantic.c | 541 ++++++++++++++++++++++++++++++++++------ src/symbol_table.c | 5 +- tests/all.algo | 52 ---- tests/alloc.algo | 21 ++ tests/arithmetique.algo | 18 +- tests/boucles.algo | 7 +- tests/exemple1.algo | 4 +- tests/exemple2.algo | 16 +- tests/exemple3.algo | 2 +- tests/fizzbuzz.algo | 13 +- tests/fonctions.algo | 24 +- tests/func.algo | 13 - tests/inclure.algo | 12 - tests/io.algo | 5 +- tests/preproc.algo | 13 + tests/prototype.algo | 25 ++ tests/ptr.algo | 18 ++ tests/recu.algo | 8 +- tests/si.algo | 2 +- tests/tab.algo | 10 + tests/test.algo | 27 +- tests/test_lib.algo | 10 +- tests/tri_par_tas.algo | 81 ++++++ tests/tri_rapide.algo | 82 ++++++ 46 files changed, 2233 insertions(+), 505 deletions(-) create mode 100644 arc_manpage create mode 100644 compte_rendu.txt create mode 100644 include/arc_options.h create mode 100644 src/arc_options.c delete mode 100644 tests/all.algo create mode 100644 tests/alloc.algo delete mode 100644 tests/func.algo delete mode 100644 tests/inclure.algo create mode 100644 tests/preproc.algo create mode 100644 tests/prototype.algo create mode 100644 tests/ptr.algo create mode 100644 tests/tab.algo create mode 100644 tests/tri_par_tas.algo create mode 100644 tests/tri_rapide.algo diff --git a/.gitignore b/.gitignore index 21a197c..5c22325 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ src/lexer.c include/parser.h *.ram *.png -*.dot \ No newline at end of file +*.dot +vgcore* \ No newline at end of file diff --git a/README.md b/README.md index f588d5d..fe8848a 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,9 @@ la [machine RAM][RAM]. ### Blancs: Espace et `\t` +### Commentaires: +Comme en C et en Python: `// commentaire`, `/* commentaire */` ou `# commentaire`. + ### Nombres: Entiers décimaux sans 0 superflus. diff --git a/arc_manpage b/arc_manpage new file mode 100644 index 0000000..dc35951 --- /dev/null +++ b/arc_manpage @@ -0,0 +1,64 @@ +.\" Manpage for arc. +.\" Contact mathis-sedkaoui@etud.univ-tln.fr to correct errors or typos. +.TH ARC 1 "17 December 2023" "0.1" +.SH NAME +arc \- Algo-RAM Compiler +.SH SYNOPSIS +arc [\fB-o\fR \fIoutfile\fR] [\fB-d\fR | \fB--debug\fR] + [\fB--print-tree\fR] [\fB--print-table\fR] [\fB-I\fR \fIdir\fR] + [\fB--mem-size\fR \fIsize\fR] \fIinfile\fR +.SH DESCRIPTION +arc is a compiler developed as a final project for the "Language theory and +compilation (I53)" module at the University of Toulon. +It is used to compile algorithmic pseudocode into a \fIRandom-Access-Machine\fR +(\fBRAM\fR) executable code. +.PP +The RAM is very similar to the counter machine but with the added capability of +\'indirect addressing\' of its registers. Like the counter machine, the RAM has +its instructions in the finite-state portion of the machine +(Harvard architecture). It is an example of the so-called von Neumann +architecture and is closest to the common notion of a computer (Wikipedia). +The set of instructions used can be found here: +.sp +https://zanotti.univ-tln.fr/ALGO/I31/MachineRAM.html + +.SH OPTIONS +.PP +.IP "\fB\-o\fR \fIfile\fR" 4 +.IX Item "-o file" +Allows to specify an output file. +.sp +If \fB-o\fR is not specified, the default ouput file will be \fIa.out\fR +.IP "\fB\-I\fR \fIdir\fR" 4 +.IX Item "-I dir" +Used to specify the directory containing the included files. +.sp +If \fB\-I\fR is specified, the included files will first be searched for in the +given directory, and if they are not found, they will be searched for in the +standard directory for libstd. +.sp +.IP "\fB\--draw-tree\fR" 4 +.IX Item "--draw-tree" +Outputs the abstract syntaxic tree (\fIast.png\fR) using graphviz +https://graphviz.org/ +.sp +.IP "\fB\--draw-table\fR" 4 +.IX Item "--draw-table" +Outputs the symbol table (\fItable.png\fR) using graphviz: https://graphviz.org/ +.sp +.IP "\fB--mem-size\fR \fIsize\fR" 4 +.IX Item "--mem-size size" +Sets the maximum available adress (used to determine the adress of the stack), +65535 by default. +.sp +.IP "\fB-d\fR, \fB--debug\fR" 4 +.IX Item "-d, --debug" +Shows debug informations (compares the calculated codelen and the "real" one ( +number of lines in the exefile)). +.SH SEE ALSO +dot(1) +.SH BUGS +During the printing of warnings and errors, the error's column location might be +wrong. +.SH AUTHOR +Mathis Sedkaoui (mathis-sedkaoui@etud.univ-tln.fr) diff --git a/compte_rendu.txt b/compte_rendu.txt new file mode 100644 index 0000000..e94bc23 --- /dev/null +++ b/compte_rendu.txt @@ -0,0 +1,192 @@ +SEDKAOUI Mathis Compte-rendu projet I53 L3 informatique + + + +Important: un avertissement concernant les tests est présent au début de la +section "Tests". + + +Fonctionnement du programme +La compilation se déroule en plusieurs phases: + 1/ Phase pré-processeur: le fichier source est parcouru à la recherche de + la seule instruction pré-processeur supportée: $ INCLURE . + Si cette instruction est présente, elle sera remplacée par l'intégralité du + fichier qu'elle indique (il est donc primordial que ce fichier ne possède + pas de fonction principale). Le fichier sera d'abord cherché dans le + répertoire éventuellement spécifié via l'option -I, puis dans le chemin de + la "librairie" standard (./libstd depuis le répertoire où est situé ce + fichier). + Limitation: je n'ai pas implémenté de header guard par défaut (cela + s'implémenterai assez facilement via une liste chaînée contenant les chemins + des fichiers déjà inclus, il suffirait alors de la parcourir à chaque fois + pour déterminer s'il faut inclure ou non le fichier) + + 2/ Analyse lexicale / syntaxique à l'aide de flex / bison: le programme + reconnaît l'entièreté des constructions demandées dans le sujet. J'ai ajouté + un séparateur additionnel (';') pour pouvoir placer les instructions sur une + même ligne si souhaité. J'ai également ajouté d'autres constructions (voir + + bas). + La construction de l'arbre se fait au cours de + l'analyse syntaxique. + + 3/ Construction de l'arbre syntaxique abstrait: les noeuds nécessitants une + explicitation sont commentés dans ast.h + + 4/ Analyse sémantique: Permet de remplir la table des symboles, de détecter + les erreurs sémantiques et de préparer le travail de génération de code. + + "Limitation" sur la partie sémantique: j'ai voulu faire un affichage propre + comme gcc sur les erreurs, mes noeuds de l'ASA récupèrent donc le yylloc. + En revanche, sur les règles syntaxique contenant plusieurs tokens, le yylloc + peut être faux puisque je n'en récupère qu'un (par exemple pour une + déclaration de variable, les erreurs peuvent être à 2 endroits: le nom de la + variable, ou l'expression affectée, mais je n'ai qu'un seul yylloc dans la + structure). Les informations de colonnes peuvent donc être erronées sur + certaines erreurs / warning. + Je pense avoir trouvé une solution mais qui est très lourde (voir l'action + pour les prototypes). + + 5/ Génération de code. + +Diverses options de compilation sont disponibles, il est par exemple possible de +visualiser l'arbre syntaxique abstrait et la table des symboles en utilisant +respectivement les options --draw-tree et --draw-table (il faut avoir dot +d'installé: https://graphviz.org/download/) + +Un manuel est disponible en faisant `man ./arc_manpage` + + +Structure de la mémoire en machine RAM: + ┌───────────────────┐ <── 0 + │ ACC │ + ├───────────────────┤ <── 1 + │ │ + │ ramOS │ + │ │ + ├───────────────────┤ <── RAM_OS_SIZE + │ │ + │ mémoire statique │ + │ │ + ├───────────────────┤ <── Début tas + │ │ + │ │ + │ tas │ + │ │ + │ │ + ├────────────────┬──┤ <── HEAP_REG (pointeur du tas) + │ ▼ │ + │ │ + │ │ + │ espace libre │ + │ │ + │ │ + │ ▲ │ + ├────────────────┴──┤ <── STACK_REG (pointeur de la pile) + │ │ + │ │ + │ pile │ + │ │ + │ │ + └───────────────────┘ <── Début pile + + +Choix de conception: + J'ai fais le choix de refaire entièrement ce qui était fourni au début du + projet, en me basant bien évidemment dessus, afin d'être sûr d'en comprendre + parfaitement le fonctionnement. + J'ai totalement modifié l'affichage de l'arbre et de la table des symboles + en utilisant graphviz. + J'ai ajouté diverses fonctions utilitaires (arc_utils.[ch]) + Je n'ai pas fait de choix particuliers pour la grammaire, je pense que + j'aurais pû gérer autrement les pointeurs / tableaux pour rendre possible + les tableaux à plusieurs dimensions. Je les ai ajouté à la fin du projet, ce + qui m'a forcé à adapter le code existant aux tableaux/pointeurs, alors que + si je les avais pris en compte dès le début j'aurais pu les implémenter + + proprement (et facilement). + + +Type de programmes gérés par le compilateur: + Le compilateur gère l'entièreté des programmes demandés: + - Expressions arithmétiques / booléenne (0 est considéré comme faux, + tout ce qui est différent de 0 est considéré comme vrai). + - Gestion des variables (affectation et utilisation au sein des + expressions) + - Boucle TQ + - SI + - Gestion des tableaux avec allocation statique et dynamique. + - Gestion des fonctions + - Opérations d'entrées / sorties (LIRE et ECRIRE) + - Gestion des pointeurs + - Opérateur d'adresse `@` (coquille dans le sujet, il est mentionné + comme opérateur de déréférencement). + - Commentaires (#, // et /* */) + - etc. + + De plus j'ai rajouté plusieurs fonctionnalités: + - Le $ INCLURE comme mentionné + haut + - La boucle FAIRE ... TQ (équivalent au do while en C) + - La boucle POUR var DANS debut...fin (équivalent au for var in range en + python). var prenant les valeurs dans l'intervale [debut, fin[ + - La gestion de la récursivité (je ne sais pas si c'était demandé). + - La gestion des prototypes de fonctions. La syntaxe est la suivante: + PROTO nom_fonction(param_1, ...) + - L'opérateur de déréférencement * (idem qu'en C) + + + notes: + Pour le SI, il supporte les SI ... ALORS ... FSI ou bien les + SI ... ALORS ... SINON ..., mais il n'est pas possible de faire des + SINON SI ... (il suffirait de rajouter une structure de liste dans le + noeud SI pour le gérer mais j'ai préféré me concentrer sur le reste). + + + + +Tests: + IMPORTANT: Le simulateur de mr. Zanotti réalise un troncage modulo 32768. + La taille de la mémoire considérée par défaut par mon compilateur est de + 2^16 - 1: Nos nombres doivent être sur 16bits, une adresse < 0 n'a pas de + sens, j'ai donc fait le choix d'avoir des adresses sur 16 bits non-signés. + + Je n'ai eu aucun problème en testant tous les fichiers de tests sur le + simulateur de mr Zanotti, mais j'ai ajouté une option permettant de le faire + afin d'éviter tout problème. + (La pile commençant à 2^16 - 1, soit 32767 si on tronque, il faudrait que la + taille cumulée de la pile et du tas vale + de 30 000 pour avoir des erreurs + qui ne seraient pas arrivées sans le troncage). + + Je vous conseille donc de compiler avec --mem-size=32767 si vous utilisez le + simulateur de mr Zanotti et que vos programmes de tests sont coûteux en + mémoire (ainsi les potentielles erreurs que vous aurez ne seront pas dûes au + troncage mais au manque de vrai OS pour délimiter les zones mémoires). + + + Vous trouverez plusieurs fichiers de tests dans le répertoire `tests`, qui + contient également les 4 exemples originaux fournis avec le sujet. + Parmi les exemples fournis avec le sujet: + - L'exemple2 ne compile pas car une variable n'est pas déclarée (je ne + sais pas si c'est intentionnel) + - L'exemple4 lève un warning pour une variable non utilisée + + Pour les boucles: + Les boucles apparaissent dans plusieurs de ces fichiers (principalement + TQ et POUR), mais elles peuvent toutes être testées via boucles.algo + + Pour le SI: si.algo (et plusieurs autres) + + Pour les fonctions: la plupart des fichiers, fonctions.algo contient presque + tous les scénarios possibles (appels imbriqués, utilisation de la valeur de + retour dans une expression, etc.) et recu.algo contient un exemple de + récursivité. + + Pour les tableaux: exemple4.algo, tab.algo, alloc.algo + + Pour l'allocation dynamique: alloc.algo, exemple4.algo + + Pour les pointeurs: ptr.algo, alloc.algo + + Pour la phase préprocesseur: preproc.algo, test.algo (attention à bien lire + les commentaires pour le chemin d'inclusion) + + + Boss finaux: tri_par_tas.algo & tri_rapide.algo diff --git a/include/arc_options.h b/include/arc_options.h new file mode 100644 index 0000000..5efd84e --- /dev/null +++ b/include/arc_options.h @@ -0,0 +1,7 @@ +#ifndef _ARC_OPTIONS_HEADER +#define _ARC_OPTIONS_HEADER + + +void handle_options(int argc, char **argv); + +#endif \ No newline at end of file diff --git a/include/arc_utils.h b/include/arc_utils.h index 01710e2..eeeade5 100644 --- a/include/arc_utils.h +++ b/include/arc_utils.h @@ -2,7 +2,9 @@ #define _ARC_UTILS_HEADER -/* Contient toutes les fonctions non liées à des "vraies" modules */ +#include "parser.h" + +/* Contient toutes les fonctions non liées à des "vrais" modules */ /* Codes d'erreur */ @@ -40,12 +42,10 @@ /* Structure contenant les informations de l'erreur (un peu comme un errno) */ struct error_info { int has_info; - int lig; - int col; + YYLTYPE loc; }; -extern struct error_info ERROR_INFO; extern char *src; @@ -58,7 +58,7 @@ void fatal_error(char *fmt, ...); void warning(char *fmt, ...); -void set_error_info(int l, int c); +void set_error_info(YYLTYPE infos); void unset_error_info(); void check_alloc(void *ptr); diff --git a/include/ast.h b/include/ast.h index a267cc6..6bf9392 100644 --- a/include/ast.h +++ b/include/ast.h @@ -5,11 +5,15 @@ #include #include +#include "parser.h" /* Pour les infos de ligne/colonne */ + + /* * Contient toutes les fonctions / types nécessaires à la gestion de - * l'arbre syntaxique abstrait (Abstract Syntax Tree). + * l'arbre syntaxique abstrait (Abstract Syntax Tree = AST). */ +/* 32 caractères max pour un identifiant */ #define ID_MAX_SIZE 32 @@ -24,10 +28,14 @@ typedef enum { nb_type, b_op_type, u_op_type, id_type, affect_type, instr_type, decla_type, var_decla_type, prog_type, func_decla_type, while_type, if_type, io_type, func_call_type, exp_list_type, do_while_type, - return_type, for_type + return_type, for_type, array_access_type, array_decla_type, + alloc_type, proto_type } node_type; +typedef enum {integer, pointer, array, func} type_symb; + + typedef struct { int val; } nb_leaf; @@ -37,7 +45,7 @@ typedef struct { char name[ID_MAX_SIZE]; } id_leaf; - +/* l_memb le membre de gauche et r_memb celui de droite */ typedef struct { int ope; ast *l_memb; @@ -51,6 +59,10 @@ typedef struct { } u_op_node; +/* + * instr contient l'instruction courante, next la suivante, ou NULL s'il n'y a + * pas d'instruction suivante + */ typedef struct { ast *instr; ast *next; @@ -60,9 +72,11 @@ typedef struct { typedef struct { ast *id; ast *expr; + int is_deref; } affect_node; +/* Idem que pour les instructions, avec exp l'expression courante */ typedef struct { ast *exp; ast *next; @@ -79,10 +93,17 @@ typedef struct { } decla_node; +/* + * var: feuile id ou noeud tableau. + * expr: l'expression d'initialisation si var est un id + * next: la déclaration de variable suivante + * type: le type de la variable (pointeur, int ou tableau) + */ typedef struct { - ast *id; + ast *var; ast *expr; ast *next; + type_symb type; } var_decla_node; @@ -92,6 +113,12 @@ typedef struct { } prog_root; +/* + * id: la feuille contenant le nom de la fonction. + * params: la liste des paramètres + * list_decl: la liste des déclarations + * list_instr: la liste des instructions + */ typedef struct { ast *id; ast *params; @@ -102,18 +129,29 @@ typedef struct { } func_decla_node; +/* + * expr: l'expression controllant la boucle + * list_instr: la liste des instructions à exécuter tant que expr est vraie + */ typedef struct { ast *expr; ast *list_instr; } while_node; +/* idem */ typedef struct { ast *list_instr; ast *expr; } do_while_node; +/* + * expr: l'expression évaluée + * list_instr1: la liste des instructions à exécuter si expr est vraie + * list_intr2: la liste des instructions à exécuter si expr est fausse (=sinon). + * Peut-être NULL si pas de sinon. + */ typedef struct { ast *expr; ast *list_instr1; @@ -121,12 +159,17 @@ typedef struct { } if_node; +/* + * mode: 'r' pour LIRE et 'w' pour ECRIRE + * expr: l'expression à écrire + */ typedef struct { ast *expr; char mode; } io_node; + typedef struct { ast *func_id; ast *params; @@ -138,6 +181,16 @@ typedef struct { } return_node; +/* + * id: la variable qui prendra les différentes valeurs + * affect_init: la 1 ère valeur que prendra la variable + * end_exp: la 1 ère valeur que ne peut pas prendre la variable + * list_instr: les instructions exécutées tant que la variable ne dépasse pas la + * dernière valeur. + * + * Par exemple dans: POUR i dans n...2 * n + 7, affect_init est n, end_exp est + * 2 * n + 7. + */ typedef struct { ast *id; ast *end_exp; @@ -146,12 +199,45 @@ typedef struct { } for_node; +/* + * ind_expr est l'exression contenue entre les crochets. + * affect_expr est l'éventuelle initialisation du tableau. + */ +typedef struct { + ast *id; + ast *ind_expr; + ast *affect_expr; +} array_access_node; + + + +typedef struct { + ast *id; + int size; + ast *list_expr; +} array_decla_node; + + +typedef struct { + ast *id; + ast *expr; +} alloc_node; + + +typedef struct { + ast *id; + ast *params; + size_t nb_params; +} proto_node; + + + + typedef struct ast { node_type type; int mem_adr; size_t codelen; - int lig; - int col; + YYLTYPE pos_infos; union { nb_leaf nb; id_leaf id; @@ -171,39 +257,41 @@ typedef struct ast { if_node if_n; io_node io; for_node for_n; + array_access_node arr_access; + array_decla_node arr_decla; + alloc_node alloc; + proto_node proto; }; } ast; -ast *init_ast(node_type type); void free_ast(ast *t); +ast *init_ast(node_type type); ast *create_nb_leaf(int value); +ast *create_return_node(ast *expr); ast *create_id_leaf(const char *id); - -ast *create_b_op_node(int op, ast *m1, ast *m2); ast *create_u_op_node(int op, ast *c); +ast *create_io_node(ast *expr, char m); ast *create_instr_node(ast *instr, ast *l); -ast *create_affect_node(char *id, ast *expr); -ast *create_decla_node(ast *decla, ast *l_decla); -ast *create_var_decla_node(char *id, ast *expr, ast *next); -ast *create_function_node(char *id, ast *params, ast *l_decl, ast *l_i); -ast *create_function_call_node(const char *id, ast *params); +ast *create_alloc_node(char *id, ast *expr); +ast *create_proto_node(char *id, ast *params); +ast *create_b_op_node(int op, ast *m1, ast *m2); ast *create_exp_list_node(ast *expr, ast *next); -ast *create_return_node(ast *expr); - -ast *create_io_node(ast *expr, char m); - -ast *create_prog_root(ast *list_decl, ast *m_prog); - ast *create_while_node(ast *expr, ast *l_instr); +ast *create_decla_node(ast *decla, ast *l_decla); ast *create_do_while_node(ast *l_instr, ast *expr); +ast *create_prog_root(ast *list_decl, ast *m_prog); +ast *create_affect_node(char *id, ast *expr, int deref); +ast *create_arr_decla_node(char *id, int size, ast *l_exp); +ast *create_function_call_node(const char *id, ast *params); ast *create_if_node(ast *expr, ast *l_instr1, ast *l_instr2); +ast *create_arr_access_node(char *id, ast *ind_expr, ast *aff_expr); +ast *create_function_node(char *id, ast *params, ast *l_decl, ast *l_i); +ast *create_var_decla_node(ast *var, ast *expr, ast *next, type_symb type); ast *create_for_node(char *id, ast *start_exp, ast *end_exp, ast *l_instr); - int ast_to_dot(ast *t, FILE *fp); void ast_to_img(ast *t, char *filename, char *fmt); - #endif \ No newline at end of file diff --git a/include/codegen.h b/include/codegen.h index 5ea5d70..9369cc9 100644 --- a/include/codegen.h +++ b/include/codegen.h @@ -8,11 +8,6 @@ #include -/* Coût des opérations sur la pile (en nb d'instructions RAM) */ -#define PUSH_COST 2 -#define POP_COST 2 -#define PEEK_COST 1 - /* Utilisé dans `add_instr` */ typedef enum { @@ -40,34 +35,34 @@ extern FILE *fp_out; void init_ram_os(); + void codegen(ast *t); void codegen_nb(ast *t); -void codegen_b_op(ast *t); -void codegen_u_op(ast *t); void codegen_id(ast *t); - void codegen_or(ast *t); -void codegen_and(ast *t); -void codegen_not(ast *t); void codegen_lt(ast *t); void codegen_gt(ast *t); void codegen_le(ast *t); void codegen_ge(ast *t); void codegen_eq(ast *t); void codegen_ne(ast *t); - -void codegen_affect(ast *t); -void codegen_instr(ast *t); void codegen_io(ast *t); -void codegen_var_decla(ast *t); -void codegen_func_decla(ast *t); -void codegen_func_call(ast *t); -void codegen_return(ast *t); - -void codegen_while(ast *t); -void codegen_do_while(ast *t); void codegen_if(ast *t); +void codegen_and(ast *t); +void codegen_not(ast *t); void codegen_for(ast *t); +void codegen_b_op(ast *t); +void codegen_u_op(ast *t); +void codegen_alloc(ast *t); +void codegen_instr(ast *t); +void codegen_while(ast *t); +void codegen_affect(ast *t); +void codegen_return(ast *t); +void codegen_do_while(ast *t); +void codegen_var_decla(ast *t); +void codegen_func_call(ast *t); +void codegen_func_decla(ast *t); +void codegen_arr_access(ast *t); void add_instr(instr_ram instr, char t_adr, int adr); diff --git a/include/preprocessor.h b/include/preprocessor.h index 4903f33..205ea22 100644 --- a/include/preprocessor.h +++ b/include/preprocessor.h @@ -3,8 +3,6 @@ #include - - FILE *cpy_file(FILE *src, const char *dest_name); FILE *preprocessor(char *src, int *nb_inserted); diff --git a/include/ram_os.h b/include/ram_os.h index 717943c..f1ba0da 100644 --- a/include/ram_os.h +++ b/include/ram_os.h @@ -14,52 +14,75 @@ * * Schéma de l'organisation de la mémoire: * - * _______________ 0 - * | ACC | - * |_______________| 1 - * | | - * | ramOS | - * | | - * |_______________| ? - * | | - * | Tas | | - * |_______________| HEAP_REG v - * | | - * | libre | - * |_______________| STACK_REG ^ - * | | | - * | pile | - * |_______________| - * - * + * ┌───────────────────┐ <── 0 + * │ ACC │ + * ├───────────────────┤ <── 1 + * │ │ + * │ ramOS │ + * │ │ + * ├───────────────────┤ <── STATIC_START + * │ │ + * │ mémoire statique │ + * │ │ + * ├───────────────────┤ <── Début tas + * │ │ + * │ │ + * │ tas │ + * │ │ + * │ │ + * ├────────────────┬──┤ <── HEAP_REG + * │ ▼ │ + * │ │ + * │ │ + * │ espace libre │ + * │ │ + * │ │ + * │ ▲ │ + * ├────────────────┴──┤ <── STACK_REG + * │ │ + * │ │ + * │ pile │ + * │ │ + * │ │ + * └───────────────────┘ <── Début pile */ - -/* Registres temporaires */ -#define TMP_REG_CMP 1 -#define TMP_REG_STK_ADR 2 -#define TMP_REG_ACC_SWP 3 -#define TMP_REG_4 4 -#define TMP_REG_SWP 5 -#define REG_RETURN_VALUE 6 -#define REG_RETURN_ADR 7 +/* + * Registres temporaires. + * Ils ne doivent PAS être utilisés si un codegen est appelé entre le stockage + * et l'accès au registre temporaire (car il pourrait être modifié entre temps). + * + * Exemple de mauvaise utilisation: + * ... + * add_instr(STORE, ' ', TMP_REG_SWP); + * ... + * codegen(...); + * add_instr(LOAD, ' ', TMP_REG_SWP); + */ +#define TMP_REG_STK_ADR 1 +#define TMP_REG_ACC_SWP 2 +#define TMP_REG_REL_STK_CPY 3 +#define TMP_REG_SWP 4 +#define REG_RETURN_VALUE 5 +#define REG_RETURN_ADR 6 /* * Gestion de la pile. * Entiers sur 16 bits: les adresses traitées sont donc au + USHRT_MAX * (pour être + précis SHRT_MAX car les entiers sont signés). - * La fin de la mémoire est donc à USHRT_MAX + * La fin de la mémoire est donc à USHRT_MAX. + * Attention: en fonction du simulateur utilisé, USHRT_MAX peut-être trop grand. */ #define STACK_START USHRT_MAX #define STACK_REG 10 #define STACK_REL_START 11 +#define HEAP_REG 12 -#define HEAP_REG 5 -/* Début du tas: fin de ramOS */ -#define HEAP_START 20 +/* Début de la mémoire statique: fin de ramOS */ +#define STATIC_START 20 #endif \ No newline at end of file diff --git a/include/semantic.h b/include/semantic.h index 4f7416d..6dbe045 100644 --- a/include/semantic.h +++ b/include/semantic.h @@ -5,28 +5,38 @@ #include "ast.h" #include "symbol_table.h" -extern symb_table table; -extern char current_ctx[32]; +/* Coût des opérations sur la pile (en nb d'instructions RAM) */ +#define PUSH_COST 2 +#define POP_COST 2 +#define PEEK_COST 1 + + +extern symb_table table; void semantic(ast *t); +void second_turn_semantic(ast *t, ast *parent); + void semantic_nb(ast *t); void semantic_id(ast *t); -void semantic_b_op(ast *t); +void semantic_io(ast *t); +void semantic_if(ast *t); +void semantic_for(ast *t); void semantic_u_op(ast *t); -void semantic_affect(ast *t); +void semantic_b_op(ast *t); void semantic_instr(ast *); void semantic_decla(ast *t); void semantic_instr(ast *t); -void semantic_io(ast *t); -void semantic_var_decla(ast *t); -void semantic_func_decla(ast *t); -void semantic_func_call(ast *t); void semantic_while(ast *t); -void semantic_do_while(ast *t); -void semantic_if(ast *t); -void semantic_for(ast *t); +void semantic_alloc(ast *t); +void semantic_proto(ast *t); +void semantic_affect(ast *t); void semantic_return(ast *t); +void semantic_do_while(ast *t); void semantic_exp_list(ast *t); +void semantic_func_call(ast *t); +void semantic_var_decla(ast *t); +void semantic_arr_access(ast *t); +void semantic_func_decla(ast *t); #endif \ No newline at end of file diff --git a/include/symbol_table.h b/include/symbol_table.h index 249226c..0edaef6 100644 --- a/include/symbol_table.h +++ b/include/symbol_table.h @@ -2,12 +2,9 @@ #define _SYMBOL_TABLE_HEADER -#include "ast.h" -#include - -/* Les types disponibles */ -typedef enum {integer, pointer, array, func} type_symb; +#include +#include "ast.h" /* @@ -19,20 +16,21 @@ typedef enum {integer, pointer, array, func} type_symb; * mem_zone: 'h' si stocké dans le tas, 's' si dans la pile. * next: le symbole suivant dans le contexte * is_used: 1 si le symbole est utilisé, 0 sinon (pour warnings) - * is_modified: pour les optimisations + * is_modified: prévu pour les optimisations (pas faites) * is_init: pour les erreurs + * is_checked: pour ne pas afficher les erreurs plusieurs fois */ typedef struct _symbol { char id[ID_MAX_SIZE]; type_symb type; int size; int adr; - int has_return; char mem_zone; struct _symbol *next; int is_used; int is_modified; int is_init; + int is_checked; // Pour le 2ème parcourt de l'analyse sémantique } symbol; diff --git a/libstd/math.algo b/libstd/math.algo index f441f23..914cad4 100644 --- a/libstd/math.algo +++ b/libstd/math.algo @@ -27,7 +27,7 @@ ALGO puissance_mod(x, n, m) VAR y <- x DEBUT SI n = 0 ALORS - RENVOYER 1 + RETOURNER 1 FSI TQ n != 0 FAIRE @@ -37,7 +37,7 @@ DEBUT FSI FTQ - RENVOYER y + RETOURNER y FIN @@ -45,9 +45,9 @@ FIN ALGO factorielle(n) DEBUT SI n = 1 ALORS - RENVOYER 1 + RETOURNER 1 SINON - RENVOYER n * factorielle(n - 1) + RETOURNER n * factorielle(n - 1) FSI FIN @@ -63,12 +63,25 @@ DEBUT a <- tmp FTQ - RENVOYER a + RETOURNER a FIN + /* Calcule le PPCM de a et de b */ ALGO ppcm(a, b) DEBUT - RENVOYER a * b / pgcd(a, b) + RETOURNER a * b / pgcd(a, b) FIN + +/* Calcule la valeur entière du log à base b de x */ +ALGO log_b(x, b) +VAR res <- 1 +DEBUT + TQ x > 0 FAIRE + x <- x / b + res <- res + 1 + FTQ + + RETOURNER res +FIN \ No newline at end of file diff --git a/libstd/utilitaires.algo b/libstd/utilitaires.algo index 6db0fec..bd24252 100644 --- a/libstd/utilitaires.algo +++ b/libstd/utilitaires.algo @@ -1,4 +1,4 @@ -/* Librairie standard contenant les fonction utilitaires */ +/* Librairie standard contenant des fonction utilitaires */ /* Renvoie le min de a et b */ ALGO min(a, b) @@ -21,4 +21,65 @@ DEBUT FSI RETOURNER c +FIN + + +/* Échange les valeurs d'indice i et j dans le tableau tab */ +ALGO echanger(@tab, i, j) +VAR tmp +DEBUT + tmp <- tab[i] + tab[i] <- tab[j] + tab[j] <- tmp +FIN + +/* Échange les valeurs situées aux adresses a et b */ +ALGO echanger_val(@a, @b) +VAR c <- *a; +DEBUT + *a <- *b + *b <- c +FIN + + +/* Retourne une copie du tableau src de taille n */ +ALGO copie_tab(@src, n) +VAR i, @res +DEBUT + ALLOUER(res, n) + POUR i DANS 0...n FAIRE + res[i] <- src[i] + FPOUR + + RETOURNER res +FIN + + +/* Retourne la valeur minimale du tableau tab de taille n */ +ALGO min_tab(@tab, n) +VAR m, i +DEBUT + m <- tab[0] + POUR i DANS 1...n FAIRE + SI tab[i] < m ALORS + m <- tab[i] + FSI + FPOUR + + RETOURNER m +FIN + + +/* Retourne la valeur maximale du tableau tab de taille n */ +ALGO max_tab(@tab, n) +VAR m, i +DEBUT + m <- tab[0] + POUR i DANS 1...n FAIRE + SI tab[i] > m ALORS + m <- tab[i] + FSI + FPOUR + + RETOURNER m FIN \ No newline at end of file diff --git a/makefile b/makefile index 9e642c7..1cf110c 100644 --- a/makefile +++ b/makefile @@ -20,7 +20,7 @@ OBJS := $(patsubst $(SRC)/%.c, $(BUILD)/%.o, $(C_FILES)) arc: $(OBJS) gcc $(C_FLAGS) $^ -o $@ $(C_LIBS) - cp arc $(BIN_PATH) + # cp arc $(BIN_PATH) # Un fichier objet dépend de sa source mais également des headers. diff --git a/src/arc_options.c b/src/arc_options.c new file mode 100644 index 0000000..971bcc3 --- /dev/null +++ b/src/arc_options.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include "arc_utils.h" +#include "preprocessor.h" + + +extern char *include_path; +extern char *exename; +extern int is_dbg_mode; +extern int print_tree; +extern int print_table; +extern int mem_size; + + +static void print_help() +{ + fprintf(stderr, "Utilisation: arc [-o outfile] [-d | --debug] "\ + "[--print-tree] [--print-table] [-I dir] infile\n"); + fprintf(stderr, "Consultez le man pour plus d'informations\n"); +} + + +void handle_options(int argc, char **argv) +{ + int opt; + const struct option options[] = { + {"draw-tree", no_argument, NULL, 1}, + {"draw-table", optional_argument, NULL, 2}, + {"debug", no_argument, NULL, 'd'}, + {"mem-size", required_argument, NULL, 3}, + {NULL, 0, NULL, '\0'} + }; + + while((opt = getopt_long(argc, argv, "I:o:d", options, NULL)) != -1) + { + switch (opt) + { + case 'I': + include_path = (char *) malloc(sizeof(char) * (strlen(optarg) + 2)); + check_alloc(include_path); + strcpy(include_path, optarg); + if (optarg[strlen(optarg) - 1] != '/') strcat(include_path, "/"); + break; + case 'o': + exename = (char *) malloc(sizeof(char) * (strlen(optarg) + 1)); + check_alloc(exename); + strcpy(exename, optarg); + break; + case 'd': + is_dbg_mode = 1; + break; + case 1: + print_tree = 1; + break; + case 2: + print_table = 1; + break; + case 3: + mem_size = atoi(optarg); + break; + default: + print_help(); + exit(1); + break; + } + } + + /* Le seul argument sans option doit être le fichier.algo */ + if (optind >= argc) + { + fatal_error("pas de fichier en entrée"); + exit(F_INPUT_ERROR); + } + + src = (char *) malloc(sizeof(char) * (strlen(argv[optind]) + 1)); + check_alloc(src); + strcpy(src, argv[optind]); + + if (exename == NULL) + { + exename = (char *) malloc(sizeof(char) * (strlen("a.out") + 1)); + check_alloc(exename); + strcpy(exename, "a.out"); + } + +} \ No newline at end of file diff --git a/src/arc_utils.c b/src/arc_utils.c index bba0994..d73b1c2 100644 --- a/src/arc_utils.c +++ b/src/arc_utils.c @@ -6,7 +6,8 @@ -struct error_info ERROR_INFO; +struct error_info INFOS; +extern FILE *yyin; /** @@ -107,13 +108,81 @@ static char *ansi_fmt(const char *fmt) } +/** + * @brief Retourne la nième ligne du fichier passé en paramètre + * + * @param fp + * @param n + * @return char* + */ +static char *get_nth_line(FILE *fp, int n) +{ + long old_cur = ftell(fp); + rewind(fp); + + int i = 0; + char *res = (char *) calloc(4096, sizeof(char)); + check_alloc(res); + while (i < n) + { + fgets(res, 4095, fp); + i++; + } + + fseek(fp, old_cur, SEEK_SET); + return res; +} + + + +/** + * @brief Affiche une erreur formatée, préfixée du message "erreur fatale" en + * rouge et en gras. + * Si set_error_infos a été utilisée, affiche la position de l'erreur (BUG) + * + * @param fmt + * @param ... + */ void fatal_error(char *fmt, ...) { - if (ERROR_INFO.has_info) + if (INFOS.has_info) { - fprintf(stderr, "\033[1m%s:%d:%d:\033[0m ", src, ERROR_INFO.lig, - ERROR_INFO.col); + // printf("first column: %d, last: %d\n", INFOS.loc.first_column, INFOS.loc.last_column); + fprintf(stderr, "\033[1m%s:%d:%d:\033[0m ", src, INFOS.loc.first_line, + INFOS.loc.first_column); + + fprintf(stderr, "\033[31;1merreur fatale:\033[0m "); + + /* On se le permet car fmt n'est pas réutilisé après */ + fmt = ansi_fmt(fmt); + + va_list arglist; + va_start(arglist, fmt); + vfprintf(stderr, fmt, arglist); + va_end(arglist); + + fprintf(stderr, "\n"); + free(fmt); + + char *buff = get_nth_line(yyin, INFOS.loc.first_line); + + char line_info[128]; + sprintf(line_info, " %d |", INFOS.loc.first_line); + + fprintf(stderr, "%s %s", line_info, buff); + for (size_t i = 0; i < strlen(line_info) - 1; i++) fprintf(stderr, " "); + fprintf(stderr, "|"); + + int i; + for (i = 0; i < INFOS.loc.first_column; i++) fprintf(stderr, " "); + fprintf(stderr, "\033[31m^"); + for (i = i + 1; i < INFOS.loc.last_column; i++) fprintf(stderr, "~"); + fprintf(stderr, "\033[0m\n"); + + free(buff); + unset_error_info(); + return; } fprintf(stderr, "\033[31;1merreur fatale:\033[0m "); @@ -133,10 +202,43 @@ void fatal_error(char *fmt, ...) void warning(char *fmt, ...) { - if (ERROR_INFO.has_info) + if (INFOS.has_info) { - fprintf(stderr, "\033[1m%s:%d:%d:\033[0m ", src, ERROR_INFO.lig, - ERROR_INFO.col); + // printf("first column: %d, last: %d\n", INFOS.loc.first_column, INFOS.loc.last_column); + fprintf(stderr, "\033[1m%s:%d:%d:\033[0m ", src, INFOS.loc.first_line, + INFOS.loc.first_column); + + fprintf(stderr, "\033[35;1mwarning:\033[0m "); + + /* On se le permet car fmt n'est pas réutilisé après */ + fmt = ansi_fmt(fmt); + + va_list arglist; + va_start(arglist, fmt); + vfprintf(stderr, fmt, arglist); + va_end(arglist); + + fprintf(stderr, "\n"); + free(fmt); + + char *buff = get_nth_line(yyin, INFOS.loc.first_line); + + char line_info[128]; + sprintf(line_info, " %d |", INFOS.loc.first_line); + + fprintf(stderr, "%s %s", line_info, buff); + for (size_t i = 0; i < strlen(line_info) - 1; i++) fprintf(stderr, " "); + fprintf(stderr, "|"); + + int i; + for (i = 0; i < INFOS.loc.first_column; i++) fprintf(stderr, " "); + fprintf(stderr, "\033[35m^"); + for (i = i + 1; i < INFOS.loc.last_column; i++) fprintf(stderr, "~"); + fprintf(stderr, "\033[0m\n"); + + free(buff); + unset_error_info(); + return; } fprintf(stderr, "\033[35;1mwarning:\033[0m "); @@ -154,18 +256,15 @@ void warning(char *fmt, ...) } -void set_error_info(int l, int c) +void set_error_info(YYLTYPE infos) { - ERROR_INFO.has_info = 1; - ERROR_INFO.lig = l; - ERROR_INFO.col = c; + INFOS.loc = infos; + INFOS.has_info = 1; } void unset_error_info() { - ERROR_INFO.has_info = 0; - ERROR_INFO.lig = 0; - ERROR_INFO.col = 0; + INFOS.has_info = 0; } diff --git a/src/ast.c b/src/ast.c index b59434c..1c3a761 100644 --- a/src/ast.c +++ b/src/ast.c @@ -23,10 +23,13 @@ ast *init_ast(node_type type) t->mem_adr = -1; t->codelen = 0; - t->lig = yylloc.first_line - line_offset; - t->col = yylloc.first_column; + t->pos_infos.first_line = yylloc.first_line - line_offset; + t->pos_infos.last_line = yylloc.last_line - line_offset; + t->pos_infos.first_column = yylloc.first_column; + t->pos_infos.last_column = yylloc.last_column; - return t; + + return t; } @@ -40,6 +43,7 @@ ast *init_ast(node_type type) ast *create_nb_leaf(int value) { ast *t = init_ast(nb_type); + t->nb.val = value; return t; @@ -138,11 +142,12 @@ ast *create_instr_node(ast *instr, ast *l) * @param expr * @return ast* */ -ast *create_affect_node(char *id, ast *expr) +ast *create_affect_node(char *id, ast *expr, int deref) { ast *t = init_ast(affect_type); t->affect.id = create_id_leaf(id); t->affect.expr = expr; + t->affect.is_deref = deref; return t; } @@ -173,11 +178,12 @@ ast *create_decla_node(ast *decla, ast *l_decla) -ast *create_var_decla_node(char *id, ast *expr, ast *next) +ast *create_var_decla_node(ast *var, ast *expr, ast *next, type_symb type) { ast *t = init_ast(var_decla_type); t->var_decla.expr = expr; - t->var_decla.id = create_id_leaf(id); + t->var_decla.type = type; + t->var_decla.var = var; /* Il faut faire une opération d'enfilage pour garder le bon ordre */ if (next == NULL) @@ -289,7 +295,7 @@ ast *create_for_node(char *id, ast *start_exp, ast *end_exp, ast *l_instr) t->for_n.id = create_id_leaf(id); t->for_n.end_exp = create_b_op_node('<', t->for_n.id, end_exp); t->for_n.list_instr = l_instr; - t->for_n.affect_init = create_affect_node(id, start_exp); + t->for_n.affect_init = create_affect_node(id, start_exp, 0); return t; } @@ -319,13 +325,24 @@ ast *create_exp_list_node(ast *expr, ast *next) ast *t = init_ast(exp_list_type); t->exp_list.exp = expr; + t->pos_infos.last_column = expr->pos_infos.last_column; + + if (next != NULL) + { + t->pos_infos.first_column = next->pos_infos.first_column; + next->pos_infos = t->pos_infos; + } + else t->pos_infos.first_column = expr->pos_infos.first_column; + + + /* Il faut faire une opération d'enfilage pour garder le bon ordre */ if (next == NULL) { - t->exp_list.next = next; + t->exp_list.next = NULL; return t; } - + ast *aux = next; while (aux->exp_list.next != NULL) { @@ -333,6 +350,8 @@ ast *create_exp_list_node(ast *expr, ast *next) } aux->exp_list.next = t; + + return next; } @@ -350,6 +369,74 @@ ast *create_function_call_node(const char *id, ast *params) +ast *create_arr_access_node(char *id, ast *ind_expr, ast *aff_expr) +{ + ast *t = init_ast(array_access_type); + t->arr_access.id = create_id_leaf(id); + t->arr_access.ind_expr = ind_expr; + t->arr_access.affect_expr = aff_expr; + + return t; +} + + +/** + * @brief Créé le noeud représentant une déclaration de tableau. + * Soit: @tab (taille indéfinie) + * ou tab[5] (de taille 5 sans initialisation) + * ou tab[3] <- [1, 2, 3] (de taille 3, initialisé) + * + * @param id + * @param size + * @param l_exp + * @return ast* + */ +ast *create_arr_decla_node(char *id, int size, ast *l_exp) +{ + ast *t = init_ast(array_decla_type); + + t->arr_decla.id = create_id_leaf(id); + t->arr_decla.size = size; + t->arr_decla.list_expr = l_exp; + + return t; +} + + + +ast *create_alloc_node(char *id, ast *expr) +{ + ast *t = init_ast(alloc_type); + + t->alloc.id = create_id_leaf(id); + t->alloc.expr = expr; + + return t; +} + + + +ast *create_proto_node(char *id, ast *params) +{ + ast *t = init_ast(proto_type); + + t->proto.id = create_id_leaf(id); + t->proto.params = params; + + /* On compte le nombre de paramètres */ + t->proto.nb_params = 0; + ast *aux = t->proto.params; + while (aux != NULL) + { + t->proto.nb_params++; + aux = aux->var_decla.next; + } + + return t; +} + + + /** * @brief Libère la mémoire utilisée par l'ASA. @@ -383,7 +470,7 @@ void free_ast(ast *t) break; case var_decla_type: free_ast(t->var_decla.expr); - free_ast(t->var_decla.id); + free_ast(t->var_decla.var); free_ast(t->var_decla.next); break; case prog_type: @@ -428,6 +515,23 @@ void free_ast(ast *t) case io_type: free_ast(t->io.expr); break; + case array_access_type: + free_ast(t->arr_access.id); + free_ast(t->arr_access.ind_expr); + free_ast(t->arr_access.affect_expr); + break; + case array_decla_type: + free_ast(t->arr_decla.id); + free_ast(t->arr_decla.list_expr); + break; + case alloc_type: + free_ast(t->alloc.id); + free_ast(t->alloc.expr); + break; + case proto_type: + free_ast(t->proto.id); + free_ast(t->proto.params); + break; default: break; } @@ -573,10 +677,44 @@ static void decla_to_dot(ast *t, int c_id, FILE *fp) +static void arr_decla_to_dot(ast *t, int c_id, FILE *fp) +{ + array_decla_node node = t->arr_decla; + printf("Taille %d\n", node.size); + + + char pref[16] = "décla"; + char symb[16]; + + if (node.list_expr != NULL) strcat(pref, " + init"); + (node.list_expr == NULL) ? strcpy(symb, " ") : strcpy(symb, "reçoit"); + + static char *fmt = "\"{%s du tab %s (taille: %d)|{ %s | next}}\""; + sprintf(buff, fmt, pref, node.id->id.name, node.size, c_id, symb, c_id); + fprintf(fp, " %d [label=%s];\n", c_id, buff); + + + if (node.list_expr == NULL) return; + + tmp = ast_to_dot(node.list_expr, fp); + fprintf(fp, " %d:c%dc -- %d;\n", c_id, c_id, tmp); +} + + static void var_init_to_dot(ast *t, int c_id, FILE *fp) { var_decla_node node = t->var_decla; + + if (node.type == array) + { + arr_decla_to_dot(node.var, c_id, fp); + if (node.next == NULL) return; + tmp = ast_to_dot(node.next, fp); + fprintf(fp, " %d:r%dr -- %d;\n", c_id, c_id, tmp); + return; + } + char pref[16] = "décla"; char symb[16]; @@ -584,7 +722,7 @@ static void var_init_to_dot(ast *t, int c_id, FILE *fp) (node.expr == NULL) ? strcpy(symb, " ") : strcpy(symb, "reçoit"); static char *fmt = "\"{%s de %s|{ %s | next}}\""; - sprintf(buff, fmt, pref, node.id->id.name, c_id, symb, c_id); + sprintf(buff, fmt, pref, node.var->id.name, c_id, symb, c_id); fprintf(fp, " %d [label=%s];\n", c_id, buff); if (node.expr != NULL) @@ -777,6 +915,52 @@ static void return_to_dot(ast *t, int c_id, FILE *fp) +static void arr_access_to_dot(ast *t, int c_id, FILE *fp) +{ + array_access_node node = t->arr_access; + static char *fmt = "\"{Accès tableau %s|{indice|reçoit}}\""; + + sprintf(buff, fmt, node.id->id.name , c_id, c_id); + fprintf(fp, " %d [label=%s];\n", c_id, buff); + + if (node.ind_expr == NULL) return; + + tmp = ast_to_dot(node.ind_expr, fp); + fprintf(fp, " %d:l%dl -- %d;\n", c_id, c_id, tmp); + + if (node.affect_expr == NULL) return; + tmp = ast_to_dot(node.affect_expr, fp); + fprintf(fp, " %d:r%dr -- %d;\n", c_id, c_id, tmp); +} + + +static void alloc_to_dot(ast *t, int c_id, FILE *fp) +{ + alloc_node node = t->alloc; + static char *fmt = "\"{Allocation pour %s|{}}\""; + + sprintf(buff, fmt, node.id->id.name, c_id); + fprintf(fp, " %d [label=%s];\n", c_id, buff); + + tmp = ast_to_dot(node.expr, fp); + fprintf(fp, " %d:c%dc -- %d;\n", c_id, c_id, tmp); +} + + +static void proto_to_dot(ast *t, int c_id, FILE *fp) +{ + proto_node node = t->proto; + + static char *fmt = "\"{Prototype de %s|{%d params}}\""; + + sprintf(buff, fmt, node.id->id.name, c_id, node.nb_params); + fprintf(fp, " %d [label=%s];\n", c_id, buff); +} + + + + + /** * @brief Convertit l'ASA en fichier utilisable avec dot * https://graphviz.org/doc/info/shapes.html#record @@ -847,6 +1031,18 @@ int ast_to_dot(ast *t, FILE *fp) case exp_list_type: exp_list_to_dot(t, c_id, fp); break; + case array_access_type: + arr_access_to_dot(t, c_id, fp); + break; + case array_decla_type: + arr_decla_to_dot(t, c_id, fp); + break; + case alloc_type: + alloc_to_dot(t, c_id, fp); + break; + case proto_type: + proto_to_dot(t, c_id, fp); + break; default: break; } diff --git a/src/codegen.c b/src/codegen.c index 025906c..bd3035c 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -12,6 +12,9 @@ static char old_context[32]; /* Compteur d'instruction. Utilisé pour les JUMP */ static int nb_instr = 0; +/* Pour la taille de la pile */ +extern int mem_size; + /** @@ -113,12 +116,14 @@ void peek() */ void init_ram_os() { - /* ToDo: Inclure + proprement ram_OS ? */ - add_instr(LOAD, '#', STACK_START); + int adr = mem_size == 0 ? STACK_START : mem_size; + add_instr(LOAD, '#', adr); add_instr(STORE, ' ', STACK_REG); add_instr(STORE, ' ', STACK_REL_START); - add_instr(LOAD, '#', HEAP_START); + add_instr(LOAD, '#', STATIC_START); add_instr(STORE, ' ', HEAP_REG); + add_instr(LOAD, '#', 0); + add_instr(STORE, ' ', TMP_REG_REL_STK_CPY); } @@ -181,6 +186,12 @@ void codegen(ast *t) case var_decla_type: codegen_var_decla(t); break; + case array_access_type: + codegen_arr_access(t); + break; + case alloc_type: + codegen_alloc(t); + break; default: break; } @@ -282,6 +293,8 @@ void codegen_b_op(ast *t) void codegen_u_op(ast *t) { + symbol *tmp; + switch (t->u_op.ope) { case NOT_OP: @@ -290,6 +303,31 @@ void codegen_u_op(ast *t) case '-': codegen(t->u_op.child); add_instr(MUL, '#', -1); + break; + case '@': + tmp = get_symbol(table, c_context, t->u_op.child->id.name); + if (tmp->mem_zone == 's') + { + add_instr(LOAD, ' ', STACK_REL_START); + add_instr(SUB, '#', tmp->adr); + } + else add_instr(LOAD, '#', tmp->adr); + break; + case '*': + tmp = get_symbol(table, c_context, t->u_op.child->id.name); + if (tmp->mem_zone == 's') + { + add_instr(LOAD, ' ', STACK_REL_START); + add_instr(SUB, '#', tmp->adr); + + /* L'ACC contient maintenant l'adresse du ptr */ + add_instr(LOAD, '@', 0); + + /* L'ACC contient maintenant le contenu du ptr */ + add_instr(LOAD, '@', 0); + } + else add_instr(LOAD, '@', tmp->adr); + break; default: break; } @@ -301,7 +339,24 @@ void codegen_id(ast *t) { symbol *tmp = get_symbol(table, c_context, t->id.name); - char adr_type = tmp->type == pointer ? '@' : ' '; + /* + * Si l'id est un tableau, il ne faut pas charger la valeur contenue à + * l'adresse de l'id mais il faut charger l'adresse de l'id. + */ + if (tmp->type == array) + { + /* Si dans la pile il faut calculer l'adresse à l'exécution */ + if (tmp->mem_zone == 's') + { + add_instr(LOAD, ' ', STACK_REL_START); + add_instr(SUB, '#', tmp->adr); + } + else add_instr(LOAD, '#', tmp->adr); + return; + } + + /* Sinon on charge simplement la valeur contenue à l'adresse de l'id */ + char adr_type = ' '; /* Si dans la pile il faut calculer l'adresse à l'exécution */ int adr = tmp->adr; @@ -412,12 +467,13 @@ void codegen_lt(ast *t) */ codegen(t->b_op.r_memb); - /* On stocke dans un registre temporaire */ - add_instr(STORE, ' ', TMP_REG_CMP); + /* On empile */ + push(); /* Génération du code de l'expression de gauche */ codegen(t->b_op.l_memb); - add_instr(SUB, ' ', TMP_REG_CMP); + add_instr(INC, ' ', STACK_REG); + add_instr(SUB, '@', STACK_REG); /* Si inférieur (strictement) à 0: a < b */ add_instr(JUML, ' ', nb_instr + 3); @@ -443,11 +499,12 @@ void codegen_gt(ast *t) codegen(t->b_op.r_memb); /* On empile */ - add_instr(STORE, ' ', TMP_REG_CMP); + push(); /* Génération du code de l'expression de gauche */ codegen(t->b_op.l_memb); - add_instr(SUB, ' ', TMP_REG_CMP); + add_instr(INC, ' ', STACK_REG); + add_instr(SUB, '@', STACK_REG); /* Si supérieur (strictement) à 0: a > b */ add_instr(JUMG, ' ', nb_instr + 3); @@ -472,12 +529,13 @@ void codegen_eq(ast *t) */ codegen(t->b_op.r_memb); - /* On stocke dans un registre temporaire */ - add_instr(STORE, ' ', TMP_REG_CMP); + /* On empile */ + push(); /* Génération du code de l'expression de gauche */ codegen(t->b_op.l_memb); - add_instr(SUB, ' ', TMP_REG_CMP); + add_instr(INC, ' ', STACK_REG); + add_instr(SUB, '@', STACK_REG); /* Si égal à 0: a = b */ add_instr(JUMZ, ' ', nb_instr + 3); @@ -502,12 +560,13 @@ void codegen_ne(ast *t) */ codegen(t->b_op.r_memb); - /* On stocke dans un registre temporaire */ - add_instr(STORE, ' ', TMP_REG_CMP); + /* On empile */ + push(); /* Génération du code de l'expression de gauche */ codegen(t->b_op.l_memb); - add_instr(SUB, ' ', TMP_REG_CMP); + add_instr(INC, ' ', STACK_REG); + add_instr(SUB, '@', STACK_REG); /* Si différent de 0: a != b */ add_instr(JUMZ, ' ', nb_instr + 3); @@ -532,12 +591,13 @@ void codegen_ge(ast *t) */ codegen(t->b_op.r_memb); - /* On stocke dans un registre temporaire */ - add_instr(STORE, ' ', TMP_REG_CMP); + /* On empile */ + push(); /* Génération du code de l'expression de gauche */ codegen(t->b_op.l_memb); - add_instr(SUB, ' ', TMP_REG_CMP); + add_instr(INC, ' ', STACK_REG); + add_instr(SUB, '@', STACK_REG); /* Si >= à 0: a >= b */ add_instr(JUMG, ' ', nb_instr + 4); @@ -567,12 +627,13 @@ void codegen_le(ast *t) */ codegen(t->b_op.r_memb); - /* On stocke dans un registre temporaire */ - add_instr(STORE, ' ', TMP_REG_CMP); + /* On empile */ + push(); /* Génération du code de l'expression de gauche */ codegen(t->b_op.l_memb); - add_instr(SUB, ' ', TMP_REG_CMP); + add_instr(INC, ' ', STACK_REG); + add_instr(SUB, '@', STACK_REG); /* Si <= à 0: a <= b */ add_instr(JUML, ' ', nb_instr + 4); @@ -603,8 +664,8 @@ void codegen_affect(ast *t) codegen(node.expr); symbol *tmp = get_symbol(table, c_context, node.id->id.name); - /* Si pointeur on utilise l'adressage indirect */ - char adr_type = tmp->type == pointer ? '@' : ' '; + /* Si déréférencement on utilise l'adressage indirect */ + char adr_type = node.is_deref ? '@' : ' '; /* Si dans la pile il faut calculer l'adresse à l'exécution */ int adr = tmp->adr; @@ -614,6 +675,7 @@ void codegen_affect(ast *t) add_instr(STORE, ' ', TMP_REG_ACC_SWP); add_instr(LOAD, ' ', STACK_REL_START); add_instr(SUB, '#', adr); + if (node.is_deref) add_instr(LOAD, '@', 0); add_instr(STORE, ' ', TMP_REG_STK_ADR); /* On remet l'ACC dans son état initial */ @@ -725,12 +787,13 @@ void codegen_if(ast *t) } - -void codegen_var_decla(ast *t) +static void codegen_int_decla(ast *t) { var_decla_node node = t->var_decla; - symbol *tmp = get_symbol(table, c_context, node.id->id.name); + char *id = node.var->id.name; + symbol *tmp = get_symbol(table, c_context, id); + /* * S'il y a une expression, il faut initialiser la variable. * On commence par générer le code pour l'expression puis on stocke @@ -740,14 +803,13 @@ void codegen_var_decla(ast *t) { codegen(node.expr); - /* Si pointeur on utilise l'adressage indirect */ - char adr_type = tmp->type == pointer ? '@' : ' '; + char adr_type = ' '; /* Si dans la pile il faut calculer l'adresse à l'exécution */ int adr = tmp->adr; if (tmp->mem_zone == 's') { - /* On stocke la valeur de l'ACC */ + /* On stocke la valeur de l'ACC et on calcule l'adresse réelle */ add_instr(STORE, ' ', TMP_REG_ACC_SWP); add_instr(LOAD, ' ', STACK_REL_START); add_instr(SUB, '#', adr); @@ -762,10 +824,64 @@ void codegen_var_decla(ast *t) } /* MAJ du tas / pile */ - if (tmp->mem_zone == 'h') add_instr(INC, ' ', HEAP_REG); - else if (tmp->mem_zone == 's') add_instr(DEC, ' ', STACK_REG); + if (tmp->mem_zone == 's') add_instr(DEC, ' ', STACK_REG); + else add_instr(INC, ' ', HEAP_REG); +} + + + +static void codegen_arr_decla(ast *t) +{ + var_decla_node node = t->var_decla; + array_decla_node arr_node = node.var->arr_decla; + + char *id = arr_node.id->id.name; + symbol *tmp = get_symbol(table, c_context, id); + + /* On parcourt les expressions et on les stocke */ + ast *aux = arr_node.list_expr; + + /* Si pas d'initialisation on alloue quand même la mémoire */ + if (tmp->mem_zone == 's') + { + add_instr(LOAD, ' ', STACK_REG); + add_instr(SUB, '#', arr_node.size); + add_instr(STORE, ' ', STACK_REG); + } + else if (aux == NULL) + { + add_instr(LOAD, ' ', HEAP_REG); + add_instr(ADD, '#', arr_node.size); + add_instr(STORE, ' ', HEAP_REG); + } + + while (aux != NULL) + { + codegen(aux->exp_list.exp); + if (tmp->mem_zone == 's') + { + add_instr(STORE, '@', STACK_REG); + add_instr(INC, ' ', STACK_REG); + } + else + { + add_instr(STORE, '@', HEAP_REG); + add_instr(INC, ' ', HEAP_REG); + } + aux = aux->exp_list.next; + } +} + + + +void codegen_var_decla(ast *t) +{ + var_decla_node node = t->var_decla; + + if (node.type == integer) codegen_int_decla(t); + else if (node.type == array) codegen_arr_decla(t); + else if (node.type == pointer) codegen_int_decla(t); // même chose - /* On génère le code pour la déclaration de variable suivante */ codegen(node.next); } @@ -791,12 +907,6 @@ void codegen_func_decla(ast *t) { add_instr(JUMP, ' ', nb_instr + t->codelen); } - - - /* MAJ de l'adresse de la fonction */ // ! attention si appel avant - symbol *tmp = get_symbol(table, c_context, node.id->id.name); - tmp->adr = nb_instr; - t->mem_adr = nb_instr; codegen(node.list_decl); codegen(node.list_instr); @@ -808,32 +918,7 @@ void codegen_func_decla(ast *t) return; } - /* Si pas de "RETOURNER" spécifié */ - if (!tmp->has_return) add_instr(LOAD, '#', 0); - - /* On stocke le contenu de la valeur de retour */ - add_instr(STORE, ' ', REG_RETURN_VALUE); - - /* - * On dépile jusqu'à atteindre le point de départ pour récupérer - * l'adresse de retour. - */ - int jumb_back = nb_instr; - add_instr(LOAD, ' ', STACK_REL_START); - add_instr(SUB, ' ', STACK_REG); - add_instr(JUMZ, ' ', nb_instr + 3); - add_instr(INC, ' ', STACK_REG); - add_instr(JUMP, ' ', jumb_back); - - /* La pile pointe mtn sur l'adresse de retour */ - add_instr(LOAD, '@', STACK_REG); - add_instr(STORE, ' ', REG_RETURN_ADR); - - /* On recharge la valeur de retour puis on jump */ - add_instr(LOAD, ' ', REG_RETURN_VALUE); - push(); - add_instr(JUMP, '@', REG_RETURN_ADR); - + /* On remet le bon contexte */ strcpy(c_context, old_context); } @@ -868,6 +953,8 @@ void codegen_func_decla(ast *t) * │ Début pile relatif │ * │ fonction appelante │ * ├────────────────────────┤ + * │CPY_TMP_REG_REL_STK_CPY │ + * ├────────────────────────┤ * │ │ * │ pile fonction │ * │ appelante │ @@ -887,6 +974,15 @@ void codegen_func_call(ast *t) { func_call_node node = t->func_call; + /* + * Pour gérer les appels de fonctions imbriqués (du style foo(bar(1), 2)). + * Ce registre est utilisé pour stocker le sommet de la pile temporairement + * (avant l'empilage des paramètres) afin qu'il devienne le début de pile + * relatif de la fonction appelée. + */ + add_instr(LOAD, ' ', TMP_REG_REL_STK_CPY); + push(); + /* * On empile le début relatif de la pile actuel pour le récupérer lorsque * l'on reviendra de la fonction appelée @@ -902,18 +998,17 @@ void codegen_func_call(ast *t) * utilisé + bas pour empiler les paramètres. */ add_instr(LOAD, ' ', STACK_REG); - add_instr(STORE, ' ', TMP_REG_4); + add_instr(STORE, ' ', TMP_REG_REL_STK_CPY); /* * On empile l'adresse de retour. - * -7 pour retirer les 7 dernières instructions (qui sont celles exécutées) + * -10 pour retirer les 10 dernières instructions (qui sont celles exécutées) * après la fonction appelée). - * -6 pour ajouter le décalage des 5 premières instructions (dont celle-ci) + * -9 pour ajouter le décalage des 9 premières instructions (dont celle-ci) */ - add_instr(LOAD, '#', nb_instr + t->codelen - 7 - 6 + 1); - add_instr(STORE, '@', STACK_REG); - add_instr(DEC, ' ', STACK_REG); + add_instr(LOAD, '#', nb_instr + t->codelen - 10 - 9 + 1); + push(); /* * On empile les paramètres (on a fait en sorte de faire l'analyse @@ -930,7 +1025,7 @@ void codegen_func_call(ast *t) } /* On met à jour le début relatif de la pile maintenant */ - add_instr(LOAD, ' ', TMP_REG_4); + add_instr(LOAD, ' ', TMP_REG_REL_STK_CPY); add_instr(STORE, ' ', STACK_REL_START); @@ -953,6 +1048,14 @@ void codegen_func_call(ast *t) pop(); add_instr(STORE, ' ', STACK_REL_START); + /* + * On récupère la valeur initiale de TMP_REG_REL_STK_CPY pour la remettre + * dans le bon état, au cas où cet appel de fonction était un paramètre d'un + * autre appel de fonction. + */ + pop(); + add_instr(STORE, ' ', TMP_REG_REL_STK_CPY); + /* Et on remet enfin la valeur de retour dans l'ACC */ add_instr(LOAD, ' ', TMP_REG_SWP); } @@ -969,4 +1072,130 @@ void codegen_return(ast *t) */ if (node.expr != NULL) codegen(node.expr); else add_instr(LOAD, '#', 0); + + if (strcmp(c_context, "PROGRAMME") == 0) + { + add_instr(STOP, ' ', 0); + return; + } + + /* On stocke le contenu de la valeur de retour */ + add_instr(STORE, ' ', REG_RETURN_VALUE); + + /* + * On dépile jusqu'à atteindre le point de départ pour récupérer + * l'adresse de retour. + */ + int jumb_back = nb_instr; + add_instr(LOAD, ' ', STACK_REL_START); + add_instr(SUB, ' ', STACK_REG); + add_instr(JUMZ, ' ', nb_instr + 3); + add_instr(INC, ' ', STACK_REG); + add_instr(JUMP, ' ', jumb_back); + + /* La pile pointe mtn sur l'adresse de retour */ + add_instr(LOAD, '@', STACK_REG); + add_instr(STORE, ' ', REG_RETURN_ADR); + + /* On recharge la valeur de retour puis on jump */ + add_instr(LOAD, ' ', REG_RETURN_VALUE); + push(); + add_instr(JUMP, '@', REG_RETURN_ADR); +} + + + +void codegen_arr_access(ast *t) +{ + array_access_node node = t->arr_access; + + codegen(node.ind_expr); + + symbol *tmp = get_symbol(table, c_context, node.id->id.name); + + /* + * Si le tableau est dans le tas, tab[x] est la valeur située à l'adresse + * de tab + x. + * Si dans la pile, c'est la valeur située à l'adresse de tab - x. + * + * Si c'est un pointeur il faut charger le contenu de la variable dans l'ACC + * et ensuite calculer le décalage. + */ + if (tmp->type == array) + { + if (tmp->mem_zone == 's') + { + add_instr(STORE, ' ', TMP_REG_ACC_SWP); + add_instr(LOAD, ' ', STACK_REL_START); + add_instr(SUB, '#', tmp->adr); + add_instr(ADD, ' ', TMP_REG_ACC_SWP); + } + else add_instr(ADD, '#', tmp->adr); + } + else + { + add_instr(STORE, ' ', TMP_REG_ACC_SWP); + if (tmp->mem_zone == 's') + { + add_instr(LOAD, ' ', STACK_REL_START); + add_instr(SUB, '#', tmp->adr); + add_instr(LOAD, '@', 0); + add_instr(ADD, ' ', TMP_REG_ACC_SWP); + } + else + { + add_instr(LOAD, '#', tmp->adr); + add_instr(ADD, ' ', TMP_REG_ACC_SWP); + } + } + + + /* L'ACC contient maintenant la bonne adresse */ + if (node.affect_expr == NULL) add_instr(LOAD, '@', 0); + else + { + push(); + codegen(node.affect_expr); + add_instr(STORE, ' ', TMP_REG_ACC_SWP); + pop(); + add_instr(STORE, ' ', TMP_REG_SWP); + add_instr(LOAD, ' ', TMP_REG_ACC_SWP); + add_instr(STORE, '@', TMP_REG_SWP); + } +} + + + + +void codegen_alloc(ast *t) +{ + alloc_node node = t->alloc; + symbol *tmp = get_symbol(table, c_context, node.id->id.name); + + /* On stocke l'adresse du tas dans le pointeur */ + if (tmp->mem_zone == 's') + { + add_instr(LOAD, ' ', STACK_REL_START); + add_instr(SUB, '#', tmp->adr); + add_instr(STORE, ' ', TMP_REG_STK_ADR); + + add_instr(LOAD, ' ', HEAP_REG); + add_instr(STORE, '@', TMP_REG_STK_ADR); + } + else + { + add_instr(LOAD, ' ', HEAP_REG); + add_instr(STORE, '@', tmp->adr); + } + + /* On génère l'expression donnant la taille à allouer */ + codegen(node.expr); + + /* Si <= 0: "segfault" -> on quitte */ + add_instr(JUMG, ' ', nb_instr + 2); + add_instr(STOP, ' ', 0); + + /* Sinon on augmente la taille du tas */ + add_instr(ADD, ' ', HEAP_REG); + add_instr(STORE, ' ', HEAP_REG); } \ No newline at end of file diff --git a/src/lexer.lex b/src/lexer.lex index 90a88f8..bea5a7b 100644 --- a/src/lexer.lex +++ b/src/lexer.lex @@ -3,19 +3,48 @@ #include "parser.h" #include "arc_utils.h" + + /* https://stackoverflow.com/a/19149193 */ + static void update_loc() { + static int curr_line = 1; + static int curr_col = 1; + + yylloc.first_line = curr_line; + yylloc.first_column = curr_col; + + {char * s; for(s = yytext; *s != '\0'; s++) { + if(*s == '\n') { + curr_line++; + curr_col = 1; + } else { + curr_col++; + } + }} + + yylloc.last_line = curr_line; + yylloc.last_column = curr_col; + + // printf(" first_line: %d, last_line: %d, firt_col: %d, last_col: %d\n\n", yylloc.first_line, + // yylloc.last_line, yylloc.first_column, yylloc.last_column); + + } + + #define YY_USER_ACTION update_loc(); + + /* * MACRO pour mettre à jour la variable yylloc avec la position du * token. */ - #define YY_USER_ACTION \ + /*#define YY_USER_ACTION \ yylloc.first_line = yylloc.last_line; \ yylloc.first_column = yylloc.last_column; \ if (yylloc.last_line == yylineno) yylloc.last_column += yyleng; \ else { \ yylloc.last_line = yylineno; \ yylloc.last_column = 1; \ - } \ - + yylloc.first_column = 1; \ + } */ extern char *src; @@ -25,21 +54,40 @@ %option noinput %option yylineno +%x IN_MULTILINE_COMMENT + /* CHIFFRE [0-9] */ NOMBRE [0-9]+ IDENT [_a-zA-Z]+[_a-zA-Z0-9]* -COMM ([#]+.*|\/\/.*|\/\*.*\*\/) +/* Commentaire sur une ligne (débutée par # ou //) */ +COMM ([#]+.*|\/\/.*) + %% {COMM} {/* Ignore les commentaires */} +"/*" BEGIN(IN_MULTILINE_COMMENT); +{ + /* http://westes.github.io/flex/manual/How-can-I-match-C_002dstyle-comments_003f.html */ + "*/" BEGIN(INITIAL); + [^*\n]+ // eat comment in chunks + "*" // eat the lone star + \n +} + + {NOMBRE} {yylval.nb = atoi(yytext); return NB;} -[-*+/=%<>)(;\n,] {return yytext[0];} +"[" {return '[';} +"]" {return ']';} + +[-*+/=%<>)(;,@] {return yytext[0];} +[\n] {return yytext[0];} [ \t] {/* Ignore les caractères blancs */} + "PROGRAMME" {return PROGRAMME;} "DEBUT" {return DEBUT;} "FIN" {return FIN;} @@ -70,6 +118,14 @@ COMM ([#]+.*|\/\/.*|\/\*.*\*\/) "FPOUR" {return FPOUR;} "RETOURNER" {return RETOURNER;} +"RENVOYER" {return RETOURNER;} + +"ALLOUER" {return ALLOUER;} + +"VRAI" {return VRAI;} +"FAUX" {return FAUX;} + +"PROTO" {return PROTO;} "!=" {return DIFF;} "<=" {return LE;} @@ -78,11 +134,8 @@ COMM ([#]+.*|\/\/.*|\/\*.*\*\/) {IDENT} {strcpy(yylval.id, yytext); return ID;} . { - colored_error(RED|BOLD, 0, "[erreur lexicale] "); - colored_error(BOLD, 0, "%s:%d:%d:", src, yylloc.first_line, - yylloc.first_column); - print_error(0, " caractère ‘"); - colored_error(MAGENTA|BOLD, 0, "%s", yytext); - print_error(LEX_ERROR, "‘ inattendu\n"); + set_error_info(yylloc); + fatal_error("erreur lexicale, caractère ‘~m~B%s~E‘ inattendu", yytext); + exit(LEX_ERROR); } %% \ No newline at end of file diff --git a/src/parser.y b/src/parser.y index 11478dc..e3d068e 100644 --- a/src/parser.y +++ b/src/parser.y @@ -1,5 +1,4 @@ %{ - #include #include #include @@ -12,13 +11,13 @@ #include "symbol_table.h" #include "semantic.h" #include "preprocessor.h" +#include "arc_options.h" extern int yylex(); extern int yylex_destroy(); void yyerror(const char *s); -// ToDo: Table symboles ast *abstract_tree = NULL; symb_table table = NULL; @@ -27,13 +26,16 @@ char *src = NULL; char *exename = NULL; char *include_path = NULL; +int is_dbg_mode = 0; +int print_tree = 0; +int print_table = 0; + int line_offset = 0; +int mem_size = 0; char PROJECT_PATH[PATH_MAX]; FILE *fp_out; -char current_ctx[32] = "global"; - %} @@ -50,6 +52,7 @@ char current_ctx[32] = "global"; %token NB %token ID %token PROGRAMME DEBUT FIN VAR ALGO +%token PROTO %token AFFECT_SYMB "<-" %token LE "<=" %token GE ">=" @@ -68,6 +71,8 @@ char current_ctx[32] = "global"; %token FPOUR %token RANGE_SYMB "..." %token RETOURNER +%token ALLOUER +%token VRAI FAUX %token '\n' /* Priorités (du - prioritaire au + prioritaire) */ @@ -93,7 +98,7 @@ char current_ctx[32] = "global"; %type PROG_MAIN %type AFFECTATION %type DECLARATION -%type LIST_INIT +%type LIST_DECLA_VAR %type STRUCT_SI %type STRUCT_TQ %type STRUCT_FAIRE_TQ @@ -105,6 +110,11 @@ char current_ctx[32] = "global"; %type APPEL_FUNC %type LIRE_INSTR %type ECRIRE_INSTR +%type AFFECT_TAB +%type ACCES_TAB +%type DECLA_TAB +%type ALLOC_INSTR +%type PROTO_FONCTION %% @@ -123,16 +133,26 @@ SEP_STRUCT: SEP ; -LIST_INIT: ID "<-" EXP {$$ = create_var_decla_node($1, $3, NULL);} -| ID {$$ = create_var_decla_node($1, NULL, NULL);} -| LIST_INIT ',' ID {$$ = create_var_decla_node($3, NULL, $1);} -| LIST_INIT ',' ID "<-" EXP {$$ = create_var_decla_node($3, $5, $1);} +DECLA_TAB: ID '[' NB ']' {$$ = create_arr_decla_node($1, $3, NULL);} +| ID '[' NB ']' "<-" '[' LIST_EXPR ']' + {$$ = create_arr_decla_node($1, $3, $7);} ; +LIST_DECLA_VAR: ID "<-" EXP {$$ = create_var_decla_node(create_id_leaf($1), $3, NULL, integer);} +| ID {$$ = create_var_decla_node(create_id_leaf($1), NULL, NULL, integer);} +| LIST_DECLA_VAR ',' ID {$$ = create_var_decla_node(create_id_leaf($3), NULL, $1, integer);} +| LIST_DECLA_VAR ',' ID "<-" EXP {$$ = create_var_decla_node(create_id_leaf($3), $5, $1, integer);} +| DECLA_TAB {$$ = create_var_decla_node($1, NULL, NULL, array);} +| LIST_DECLA_VAR ',' DECLA_TAB {$$ = create_var_decla_node($3, NULL, $1, array);} +| '@'ID {$$ = create_var_decla_node(create_id_leaf($2), NULL, NULL, pointer);} +| LIST_DECLA_VAR ',' '@'ID {$$ = create_var_decla_node(create_id_leaf($4), NULL, $1, pointer);} +; + -DECLARATION: VAR LIST_INIT SEP {$$ = create_decla_node($2, NULL);} -| FONCTION {$$ = create_decla_node($1, NULL);} +DECLARATION: VAR LIST_DECLA_VAR SEP {$$ = create_decla_node($2, NULL);} +| FONCTION {$$ = create_decla_node($1, NULL);} +| PROTO_FONCTION {$$ = create_decla_node($1, NULL);} ; @@ -141,14 +161,14 @@ LIST_DECLA: %empty {$$ = NULL;} ; -AFFECTATION: ID "<-" EXP {$$ = create_affect_node($1, $3);} +AFFECTATION: ID "<-" EXP {$$ = create_affect_node($1, $3, 0);} +| '*'ID "<-" EXP {$$ = create_affect_node($2, $4, 1);} ; - PROGRAMME_ALGO: SEP_STRUCT LIST_DECLA PROG_MAIN END_FILE {$$ = create_prog_root($2, $3); abstract_tree = $$;} -; +; PROG_MAIN: PROGRAMME '(' ')' SEP @@ -160,8 +180,10 @@ FIN {$$ = create_function_node("PROGRAMME", NULL, $5, $8);} LIST_ARGS: %empty {$$ = NULL;} -| ID {$$ = create_var_decla_node($1, NULL, NULL);} -| LIST_ARGS ',' ID {$$ = create_var_decla_node($3, NULL, $1);} +| ID {$$ = create_var_decla_node(create_id_leaf($1), NULL, NULL, integer);} +| LIST_ARGS ',' ID {$$ = create_var_decla_node(create_id_leaf($3), NULL, $1, integer);} +| '@'ID {$$ = create_var_decla_node(create_id_leaf($2), NULL, NULL, pointer);} +| LIST_ARGS ',' '@'ID {$$ = create_var_decla_node(create_id_leaf($4), NULL, $1, pointer);} ; @@ -171,6 +193,14 @@ LIST_EXPR: %empty {$$ = NULL;} ; +PROTO_FONCTION: PROTO ID '(' LIST_ARGS ')' SEP { + /* Un exemple de comment gérer proprement les erreurs (très lourd) */ + yylloc.first_line = @2.first_line; + yylloc.last_column = @5.last_column; + $$ = create_proto_node($2, $4); + } +; + FONCTION: ALGO ID '(' LIST_ARGS ')' SEP LIST_DECLA DEBUT SEP @@ -179,6 +209,7 @@ FIN SEP {$$ = create_function_node($2, $4, $7, $10);} ; + CORPS_FUNC: LISTE_INSTR {$$ = $1;} ; @@ -186,11 +217,22 @@ APPEL_FUNC: ID '(' LIST_EXPR ')' {$$ = create_function_call_node($1, $3);} ; +ACCES_TAB: ID '[' EXP ']' {$$ = create_arr_access_node($1, $3, NULL);} +; + + +AFFECT_TAB: ID '[' EXP ']' "<-" EXP {$$ = create_arr_access_node($1, $3, $6);} +; + + EXP: '(' EXP ')' {$$ = $2;} +| ACCES_TAB {$$ = $1;} | ID {$$ = create_id_leaf($1);} | NB {$$ = create_nb_leaf(yylval.nb);} | NON EXP {$$ = create_u_op_node(NOT_OP, $2);} | '-' EXP {$$ = create_u_op_node('-', $2);} +| '@' ID {$$ = create_u_op_node('@', create_id_leaf($2));} +| '*' ID {$$ = create_u_op_node('*', create_id_leaf($2));} | EXP '+' EXP {$$ = create_b_op_node('+', $1, $3);} | EXP '-' EXP {$$ = create_b_op_node('-', $1, $3);} | EXP '*' EXP {$$ = create_b_op_node('*', $1, $3);} @@ -204,6 +246,8 @@ EXP: '(' EXP ')' {$$ = $2;} | EXP "!=" EXP {$$ = create_b_op_node(NE_OP, $1, $3);} | EXP OU EXP {$$ = create_b_op_node(OR_OP, $1, $3);} | EXP ET EXP {$$ = create_b_op_node(AND_OP, $1, $3);} +| VRAI {$$ = create_nb_leaf(1);} +| FAUX {$$ = create_nb_leaf(0);} | APPEL_FUNC {$$ = $1;} | LIRE_INSTR {$$ = $1;} ; @@ -211,6 +255,7 @@ EXP: '(' EXP ')' {$$ = $2;} INSTR: EXP SEP {$$ = $1;} | AFFECTATION SEP {$$ = $1;} +| AFFECT_TAB SEP {$$ = $1;} | ECRIRE_INSTR SEP {$$ = $1;} | STRUCT_TQ SEP {$$ = $1;} | STRUCT_SI SEP {$$ = $1;} @@ -218,6 +263,7 @@ INSTR: EXP SEP {$$ = $1;} | STRUCT_POUR SEP {$$ = $1;} | RETOURNER EXP SEP {$$ = create_return_node($2);} | RETOURNER SEP {$$ = create_return_node(NULL);} +| ALLOC_INSTR SEP {$$ = $1;} ; LISTE_INSTR: INSTR {$$ = create_instr_node($1, NULL);} @@ -226,6 +272,11 @@ LISTE_INSTR: INSTR {$$ = create_instr_node($1, NULL);} // Autres structures +ALLOC_INSTR: ALLOUER '(' ID ',' EXP ')' {$$ = create_alloc_node($3, $5);} +; + + + LIRE_INSTR: LIRE '(' ')' {$$ = create_io_node(NULL, 'r');} ; @@ -273,44 +324,15 @@ int main(int argc, char **argv) * dans le makefile. * * La 2ème ligne permet de supprimer le bout de chemin en trop. + * + * Ainsi, peu importe d'où l'exécutable est lancé, le chemin vers la + * librairie standard sera correct. */ strcpy(PROJECT_PATH, __FILE__); PROJECT_PATH[strlen(PROJECT_PATH) - strlen("/src/parser.y")] = '\0'; - - int opt, dbg = 0; - char *options = "o:dI:"; - while ((opt = getopt(argc, argv, options)) != -1) - { - switch (opt) - { - case 'o': - exename = (char *) malloc(sizeof(char) * (strlen(optarg) + 1)); - check_alloc(exename); - strcpy(exename, optarg); - break; - case 'd': - dbg = 1; - break; - case 'I': - include_path = (char *) malloc(sizeof(char) * (strlen(optarg) + 1)); - check_alloc(include_path); - strcpy(include_path, optarg); - break; - default: - break; - } - } - - /* Le seul argument sans option doit être le fichier.algo */ - if (optind >= argc) - { - fatal_error("pas de fichier en entrée"); - exit(F_INPUT_ERROR); - } - - src = (char *) malloc(sizeof(char) * (strlen(argv[optind]) + 1)); - check_alloc(src); - strcpy(src, argv[optind]); + + /* Traitement des options de la ligne de commande */ + handle_options(argc, argv); /* Phase préprocesseur */ yyin = preprocessor(src, &line_offset); @@ -319,54 +341,48 @@ int main(int argc, char **argv) table = init_symb_table("global"); abstract_tree = NULL; + /* Analyse lexicale / syntaxique */ yyparse(); - fclose(yyin); /* Supprime le fichier intermédiaire utilisé */ - system("rm __arc_PP.algo_pp"); + system("rm ./__arc_PP.algo_pp"); /* Analyse sémantique */ semantic(abstract_tree); + second_turn_semantic(abstract_tree, NULL); - - if (dbg) - { - ast_to_img(abstract_tree, "ast", "png"); - symb_to_img(table, "table", "png"); - } - - - if (exename == NULL) - { - exename = (char *) malloc(sizeof(char) * (strlen("a.out") + 1)); - check_alloc(exename); - strcpy(exename, "a.out"); - } + /* Affichage si demandé par l'utilisateur */ + if (print_tree) ast_to_img(abstract_tree, "ast", "png"); + if (print_table) symb_to_img(table, "table", "png"); + /* Ouverture du fichier de sortie */ fp_out = fopen(exename, "w"); if (fp_out == NULL) { fatal_error("impossible d'ouvrir ~U%s~E", exename); exit(F_INPUT_ERROR); } + + /* Génération du code */ init_ram_os(); codegen(abstract_tree); + + fclose(yyin); free_table(table); fclose(fp_out); - if (dbg) + if (is_dbg_mode) { /* Temporaire, pour vérifier que semantic marche bien */ char buff[256]; - size_t codelen_total = abstract_tree->codelen + 5; // + 5 pour ramOS + size_t codelen_total = abstract_tree->codelen + 7; // + 7 pour ramOS sprintf(buff, "echo \"Codelen total: %ld\nNombre de lignes "\ "dans le fichier produit: \" && wc -l %s", codelen_total, exename); system(buff); } - - + /* Libération de la mémoire */ free(src); free(exename); free_ast(abstract_tree); @@ -382,7 +398,7 @@ int main(int argc, char **argv) void yyerror(const char *s) { - int lig = yylloc.first_line - line_offset; - colored_error(RED|BOLD, 0, "%s:%d:%d:", src, lig, yylloc.first_column); - print_error(BISON_ERROR, " %s\n", s); + set_error_info(yylloc); + fatal_error("%s", s); + exit(BISON_ERROR); } \ No newline at end of file diff --git a/src/preprocessor.c b/src/preprocessor.c index 0b5d8b3..589f945 100644 --- a/src/preprocessor.c +++ b/src/preprocessor.c @@ -86,8 +86,14 @@ static void do_preproc_action(FILE *dest, char *line, size_t line_nb, int *nb) { /* On récupère le nom du fichier.algo */ char buff[4096]; - set_error_info(line_nb, 1); - if (sscanf(line, " %% INCLURE %s \n", buff) != 1) + YYLTYPE err; + err.first_line = line_nb; + err.first_column = 1; + err.last_line = line_nb; + err.last_column = strlen(line); + + set_error_info(err); + if (sscanf(line, " $ INCLURE %s \n", buff) != 1) { fatal_error("instuction pré-processeur invalide: ~B%s~E", line); exit(1); @@ -134,7 +140,7 @@ FILE *preprocessor(char *src, int *nb_inserted) } /* Fichier qui sera analysé etc. (écriture + lecture) */ - FILE *pp_f = fopen("__arc_PP.algo_pp", "w+"); + FILE *pp_f = fopen("./__arc_PP.algo_pp", "w+"); check_alloc(pp_f); /* Parcours des lignes */ @@ -145,9 +151,9 @@ FILE *preprocessor(char *src, int *nb_inserted) while (fgets(line, 4095, og_file) != NULL) { - /* Si on lit un '%' (marqueur d'opérations préprocesseur) */ - for (i = 0; line[i] != '\0' && line[i] != '%'; i++); - if (line[i] == '%') do_preproc_action(pp_f, line, num_lig, nb_inserted); + /* Si on lit un '$' (marqueur d'opérations préprocesseur) */ + for (i = 0; line[i] != '\0' && line[i] != '$'; i++); + if (line[i] == '$') do_preproc_action(pp_f, line, num_lig, nb_inserted); else fwrite(line, sizeof(char), strlen(line), pp_f); num_lig++; } diff --git a/src/semantic.c b/src/semantic.c index 6b8de07..6b53a57 100644 --- a/src/semantic.c +++ b/src/semantic.c @@ -5,17 +5,18 @@ #include -/** +/* * Adresses relatives. - * Par exemple, heap_rel_adr est l'adresse relative par rapport au tas - * (0 pour la 1ère variable stockée dans le tas, 1 pour la deuxième, - * etc.). + * Par exemple, static_rel_adr est l'adresse relative par rapport à la mémoire + * statique (0 pour la 1ère variable stockée, 1 pour la deuxième, etc.). * Idem pour la pile, à l'exception que l'adresse relative sera "reset" * à chaque fin de fonction. */ -static int heap_rel_adr = 0; +int static_rel_adr = 0; static int stack_rel_adr = 0; +/* Contexte par défaut */ +char current_ctx[32] = "global"; /* Pour swap les contextes */ static char old_context[32]; @@ -24,6 +25,8 @@ static char old_context[32]; static int has_return_instr = 0; + + /** * @brief Réalise l'analyse sémantique de l'arbre syntaxique abstrait * @@ -98,6 +101,178 @@ void semantic(ast *t) case io_type: semantic_io(t); break; + case array_access_type: + semantic_arr_access(t); + break; + case alloc_type: + semantic_alloc(t); + break; + case proto_type: + semantic_proto(t); + break; + default: + break; + } +} + + + +/** + * @brief 2ème parcourt de l'arbre, après l'analyse sémantique. + * Permet de lever différents warning et de mettre à jour correctement l'adresse + * des fonctions. + * Les optimisations auraient aussi eu lieues ici. + * + * @param t + * @param parent + */ +void second_turn_semantic(ast *t, ast *parent) +{ + static int is_param_decl = 0; + static size_t offset_cdln = 0; + symbol *tmp; + + char *id; + + if (t == NULL) return; + + switch (t->type) + { + case id_type: + tmp = get_symbol(table, current_ctx, t->id.name); + set_error_info(t->pos_infos); + if (!tmp->is_used && !tmp->is_checked) + { + warning("l'identificateur ‘~m%s~E‘ n'est pas utilisé", t->id.name); + } + else if (!tmp->is_init && !tmp->is_checked && !is_param_decl) + { + warning("l'identificateur ‘~m%s~E‘ n'est pas initialisé", t->id.name); + } + tmp->is_checked = 1; + break; + case b_op_type: + second_turn_semantic(t->b_op.l_memb, t); + second_turn_semantic(t->b_op.r_memb, t); + break; + case u_op_type: + second_turn_semantic(t->u_op.child, t); + break; + case affect_type: + second_turn_semantic(t->affect.expr, t); + second_turn_semantic(t->affect.id, t); + break; + case instr_type: + second_turn_semantic(t->list_instr.instr, t); + second_turn_semantic(t->list_instr.next, t); + break; + case while_type: + second_turn_semantic(t->while_n.expr, t); + second_turn_semantic(t->while_n.list_instr, t); + break; + case do_while_type: + second_turn_semantic(t->while_n.expr, t); + second_turn_semantic(t->while_n.list_instr, t); + break; + case if_type: + second_turn_semantic(t->if_n.expr, t); + second_turn_semantic(t->if_n.list_instr1, t); + second_turn_semantic(t->if_n.list_instr2, t); + break; + case for_type: + second_turn_semantic(t->for_n.affect_init, t); + second_turn_semantic(t->for_n.end_exp, t); + second_turn_semantic(t->for_n.id, t); + second_turn_semantic(t->for_n.list_instr, t); + break; + case decla_type: + second_turn_semantic(t->decla_list.decla, t); + second_turn_semantic(t->decla_list.next, t); + break; + case var_decla_type: + second_turn_semantic(t->var_decla.expr, t); + second_turn_semantic(t->var_decla.next, t); + second_turn_semantic(t->var_decla.var, t); + break; + case prog_type: + if (t->root.list_decl != NULL) offset_cdln = t->root.list_decl->codelen; + second_turn_semantic(t->root.list_decl, t); + second_turn_semantic(t->root.main_prog, t); + break; + case func_decla_type: + id = t->func_decla.id->id.name; + strcpy(old_context, current_ctx); + strcpy(current_ctx, id); + + + /* + * Calcul de l'adresse de la fonction. + * 7 pour ramOs et 1 pour le JUMP avant la fonction + */ + size_t adr = offset_cdln + 7 + 1; + adr -= t->codelen; + if (parent->decla_list.next != NULL) + { + adr -= parent->decla_list.next->codelen; + } + + if (strcmp(id, "PROGRAMME") == 0) adr = offset_cdln + 7; + + tmp = get_symbol(table, current_ctx, id); + tmp->adr = adr; + t->mem_adr = adr; + + is_param_decl = 1; + second_turn_semantic(t->func_decla.params, t); + is_param_decl = 0; + + second_turn_semantic(t->func_decla.id, t); + second_turn_semantic(t->func_decla.list_decl, t); + second_turn_semantic(t->func_decla.list_instr, t); + strcpy(current_ctx, old_context); + break; + case func_call_type: + id = t->func_call.func_id->id.name; + tmp = get_symbol(table, current_ctx, id); + set_error_info(t->proto.id->pos_infos); + if (!tmp->is_init) + { + fatal_error("la fonction ‘~r%s~E‘ n'est pas initialisée", id); + exit(1); + } + + second_turn_semantic(t->func_call.func_id, t); + second_turn_semantic(t->func_call.params, t); + break; + case return_type: + second_turn_semantic(t->return_n.expr, t); + break; + case exp_list_type: + second_turn_semantic(t->exp_list.exp, t); + second_turn_semantic(t->exp_list.next, t); + break; + case io_type: + second_turn_semantic(t->io.expr, t); + break; + case array_access_type: + second_turn_semantic(t->arr_access.affect_expr, t); + second_turn_semantic(t->arr_access.id, t); + second_turn_semantic(t->arr_access.ind_expr, t); + break; + case alloc_type: + second_turn_semantic(t->alloc.expr, t); + second_turn_semantic(t->alloc.id, t); + break; + case proto_type: + id = t->proto.id->id.name; + tmp = get_symbol(table, current_ctx, id); + set_error_info(t->pos_infos); + if (!tmp->is_init && !tmp->is_checked) + { + warning("la fonction ‘~m%s~E‘ n'est pas initialisée", id); + } + tmp->is_checked = 1; + break; default: break; } @@ -116,7 +291,7 @@ void semantic_nb(ast *t) { if (t->nb.val > SHRT_MAX || t->nb.val < SHRT_MIN) { - set_error_info(t->lig, t->col); + set_error_info(t->pos_infos); warning("le nombre ~B%d~E dépasse la valeur maximale d'un entier", t->nb.val); } @@ -128,31 +303,15 @@ void semantic_nb(ast *t) void semantic_id(ast *t) -{ - /* - * Les actions dépendent du noeud parent et du contexte. - * Par exemple, l'id a dans l'expression 3 * a + 1 aura un codelen - * de 1 (il suffit simplement de faire un LOAD de la bonne adresse). - * En revanche, lors d'une déclaration, par exemple VAR x en dehors - * d'une fonction, il faudra en + augmenter le pointeur du tas (et - * réciproquement si la variable est déclarée dans une fonction il - * faudra mettre à jour la pile). - * - * - * On ne met que 1 ici car ces diverses opérations seront prises en - * compte dans le codelen des parents. - * - * semantic_id ne doit être appelé que lorsqu'il faut charger la - * valeur de l'identificateur. - * - */ - +{ /* On vérifie que l'identifiant existe */ - set_error_info(t->lig, t->col); + set_error_info(t->pos_infos); symbol *tmp = get_symbol(table, current_ctx, t->id.name); + tmp->is_used = 1; + - /* Un LOAD (les STORE seront gérés dans les affectations) */ - t->codelen = tmp->mem_zone == 's' ? 4 : 1; + if (tmp->type == array) t->codelen = tmp->mem_zone == 's' ? 2 : 1; + else t->codelen = tmp->mem_zone == 's' ? 4 : 1; } @@ -171,19 +330,19 @@ void semantic_b_op(ast *t) t->codelen += 5; break; - /* Tout ceux qui coûtent 6 instructions */ + /* Tout ceux qui coûtent 8 instructions */ case '<': case '>': case '=': case NE_OP: case OR_OP: - t->codelen += 6; + t->codelen += 8; break; - /* Ceux qui coûtent 7 instructions */ + /* Ceux qui coûtent 9 instructions */ case LE_OP: case GE_OP: - t->codelen += 7; + t->codelen += 9; break; default: /* Tous les autres ajoutent 4 instructions */ @@ -196,15 +355,41 @@ void semantic_b_op(ast *t) void semantic_u_op(ast *t) { - semantic(t->u_op.child); - t->codelen = t->u_op.child->codelen; - switch (t->u_op.ope) + u_op_node node = t->u_op; + semantic(node.child); + t->codelen = node.child->codelen; + + symbol *tmp; + switch (node.ope) { case NOT_OP: t->codelen += 4; break; case '-': t->codelen += 1; + break; + case '@': + set_error_info(node.child->pos_infos); + tmp = get_symbol(table, current_ctx, node.child->id.name); + + if (tmp->mem_zone == 's') + { + t->codelen = 2; + } + else t->codelen = 1; + break; + case '*': + set_error_info(node.child->pos_infos); + tmp = get_symbol(table, current_ctx, node.child->id.name); + + if (tmp->type != pointer) + { + warning("le symbole ‘~B%s~E‘ n'est pas un pointeur"); + } + + if (tmp->mem_zone == 's') t->codelen = 4; + else t->codelen = 1; + break; default: break; } @@ -223,7 +408,7 @@ void semantic_instr(ast *t) { if (node.next != NULL) { - set_error_info(t->lig, t->col); + set_error_info(node.instr->pos_infos); warning("les instructions situées apprès le ~BRETOURNER~E ne "\ "seront jamais exécutées"); } @@ -282,7 +467,7 @@ void semantic_for(ast *t) semantic(node.end_exp); semantic(node.list_instr); - set_error_info(t->lig, t->col); + set_error_info(node.id->pos_infos); symbol *tmp = get_symbol(table, current_ctx, node.id->id.name); int cost = tmp->mem_zone == 's' ? 6 : 3; @@ -299,7 +484,7 @@ void semantic_affect(ast *t) semantic(node.expr); semantic(node.id); - set_error_info(t->lig, t->col); + set_error_info(node.id->pos_infos); symbol *tmp = get_symbol(table, current_ctx, node.id->id.name); /* Si déjà init alors il est modifié */ @@ -308,6 +493,7 @@ void semantic_affect(ast *t) int cost = tmp->mem_zone == 's' ? 6 : 1; t->codelen = node.expr->codelen + cost; + if (node.is_deref && tmp->mem_zone == 's') t->codelen += 1; } @@ -335,9 +521,8 @@ void semantic_decla(ast *t) } - -void semantic_var_decla(ast *t) -{ +static void semantic_int_decla(ast *t) +{ var_decla_node node = t->var_decla; /* @@ -353,30 +538,153 @@ void semantic_var_decla(ast *t) int stack_instr = 0; if (zone == 'h') { - adr = node.id->mem_adr = HEAP_START + heap_rel_adr++; + adr = node.var->mem_adr = STATIC_START + static_rel_adr++; } else if (zone == 's') { - adr = node.id->mem_adr = stack_rel_adr++; + adr = node.var->mem_adr = stack_rel_adr++; stack_instr = 5; } + + char *id = node.var->id.name; - set_error_info(t->lig, t->col); - symbol *new_symb = init_symbol(node.id->id.name, adr, zone, integer); + set_error_info(node.var->pos_infos); + symbol *new_symb = init_symbol(id, adr, zone, integer); add_symbol(table, current_ctx, new_symb); + semantic(node.expr); semantic(node.next); - semantic(node.id); + semantic(node.var); + + new_symb->is_used = 0; if (node.expr != NULL) new_symb->is_init = 1; - /* + 2 pour le STORE et la MAJ du tas/pile */ if (node.expr != NULL) t->codelen += node.expr->codelen + 1 + stack_instr; if (node.next != NULL) t->codelen += node.next->codelen; } +static void semantic_ptr_decla(ast *t) +{ + var_decla_node node = t->var_decla; + char zone = strcmp(current_ctx, "global") == 0 ? 'h' : 's'; + + int adr; + if (zone == 'h') + { + adr = node.var->mem_adr = STATIC_START + static_rel_adr++; + } + else if (zone == 's') + { + adr = node.var->mem_adr = stack_rel_adr++; + } + + char *id = node.var->id.name; + set_error_info(node.var->pos_infos); + + symbol *new_symb = init_symbol(id, adr, zone, pointer); + add_symbol(table, current_ctx, new_symb); + + semantic(node.expr); + semantic(node.next); + semantic(node.var); + + new_symb->is_used = 0; + + t->codelen = 1; + if (node.next != NULL) t->codelen += node.next->codelen; +} + + +static void semantic_arr_decla(ast *t) +{ + var_decla_node node = t->var_decla; + array_decla_node arr_node = node.var->arr_decla; + + char zone = strcmp(current_ctx, "global") == 0 ? 'h' : 's'; + + int adr; + if (zone == 'h') + { + adr = node.var->mem_adr = STATIC_START + static_rel_adr; + static_rel_adr += arr_node.size > 1 ? arr_node.size : 1; + } + else if (zone == 's') + { + stack_rel_adr += arr_node.size > 1 ? arr_node.size : 1; + adr = node.var->mem_adr = stack_rel_adr; + } + + char *id = arr_node.id->id.name; + symbol *new_symb = init_symbol(id, adr, zone, node.type); + new_symb->size = arr_node.size; + add_symbol(table, current_ctx, new_symb); + + semantic(arr_node.list_expr); + semantic(arr_node.id); + semantic(node.next); + + new_symb->is_used = 0; + + + /* On compte le nombre d'éléments qui initialisent le tableau */ + int size = 0; + ast *aux = arr_node.list_expr; + while (aux != NULL) + { + size++; + aux = aux->exp_list.next; + } + + if (arr_node.size != -1 && (arr_node.size != size) && arr_node.list_expr != NULL) + { + set_error_info(arr_node.list_expr->pos_infos); + fatal_error("mauvaise taille lors de l'initialisation du tableau "\ + "~U%s~E: le tableau est de taille ~B%d~E (initialisé avec un tableau "\ + "de taille ~B%d~E)", id, arr_node.size, size); + exit(1); + } + + + t->codelen = 0; + if (new_symb->mem_zone == 's') t->codelen += 3; + else if (arr_node.list_expr == NULL) t->codelen += 3; + + if (arr_node.list_expr != NULL) + { + new_symb->is_init = 1; + t->codelen += arr_node.list_expr->codelen + 2 * size; // ! + } + + + if (node.next != NULL) t->codelen += node.next->codelen; +} + + + +void semantic_var_decla(ast *t) +{ + var_decla_node node = t->var_decla; + + switch (node.type) + { + case integer: + semantic_int_decla(t); + break; + case array: + semantic_arr_decla(t); + break; + case pointer: + semantic_ptr_decla(t); + break; + default: + break; + } +} + + /** * @brief @@ -389,13 +697,26 @@ void semantic_func_decla(ast *t) /* Le champs adr contiendra le nombre de paramètres */ int n = node.nb_params; + + + + + set_error_info(node.id->pos_infos); + /* + * On ajoute le nom de la fonction en tant qu'identifiant si nécessaire. + * En effet, il est peut-être déjà dans la table des symboles si un + * prototype a été mit. + */ + symbol *tmp = search_symbol(table, current_ctx, node.id->id.name); + if (tmp == NULL) + { + symbol *new_symb = init_symbol(node.id->id.name, 0, 'h', func); + new_symb->size = n; + new_symb->is_init = 1; + tmp = add_symbol(table, current_ctx, new_symb); + } + tmp->is_init = 1; - /* On ajoute l'id de la fonction au contexte actuel */ - set_error_info(t->lig, t->col); - symbol *new_symb = init_symbol(node.id->id.name, 0, 'h', func); - new_symb->size = n; - new_symb->is_init = 1; - add_symbol(table, current_ctx, new_symb); semantic(t->func_decla.id); /* On change le contexte qui devient le nom de la fonction */ @@ -416,28 +737,29 @@ void semantic_func_decla(ast *t) stack_rel_adr = strcmp(node.id->id.name, "PROGRAMME") == 0 ? 0 : 1; has_return_instr = 0; - + semantic(node.params); semantic(node.list_decl); semantic(node.list_instr); - new_symb->has_return = has_return_instr; + /* Si pas de "RETOURNER" spécifié on créé un noeud vide qui renverra 0 */ + if (!has_return_instr && strcmp(node.id->id.name, "PROGRAMME") != 0) + { + ast *return_n = create_return_node(NULL); + /* On l'ajoute à la fin des instructions et on réanalyse */ + node.list_instr = create_instr_node(return_n, node.list_instr); + semantic(node.list_instr); + } t->codelen = 0; - // if (node.params != NULL) t->codelen += node.params->codelen; if (node.list_decl != NULL) t->codelen += node.list_decl->codelen; if (node.list_instr != NULL) t->codelen += node.list_instr->codelen; + /* Au cas où ça change + tard (STOP pour PROGRAMME, JUMP pour les autre) */ if (strcmp(node.id->id.name, "PROGRAMME") == 0) t->codelen += 1; - else - { - t->codelen += 1; - t->codelen += 10 + 2; - if (!new_symb->has_return) t->codelen += 1; - } - + else t->codelen += 1; /* On revient au contexte précédent */ strcpy(current_ctx, old_context); @@ -449,6 +771,7 @@ void semantic_func_decla(ast *t) void semantic_func_call(ast *t) { func_call_node node = t->func_call; + semantic(node.func_id); /* On vérifie que la fonction existe */ symbol *tmp = get_symbol(table, current_ctx, node.func_id->id.name); @@ -465,7 +788,8 @@ void semantic_func_call(ast *t) /* On vérifie que le nombre de paramètre est le bon */ if (nb_params != tmp->size) { - set_error_info(t->lig, t->col); + if (node.params != NULL) set_error_info(node.params->pos_infos); + else set_error_info(node.func_id->pos_infos); fatal_error("nombre de paramètres incorrect (%d passés, %d attendus)", nb_params, tmp->size); exit(1); @@ -481,7 +805,8 @@ void semantic_func_call(ast *t) * On ajoute également le coût de la copie des paramètres dans la * pile. On va empiler les paramètres à la suite dans la pile. */ - t->codelen = 18 + node.params->codelen + 2 * nb_params; + t->codelen = 24 + 2 * nb_params; + if (node.params != NULL) t->codelen += node.params->codelen; } @@ -494,21 +819,24 @@ void semantic_return(ast *t) * On ne peut retourner que depuis une fonction autre que la * fonction principale PROGRAMME */ - if (strcmp(current_ctx, "global") == 0 - || strcmp(current_ctx, "PROGRAMME") == 0) + if (strcmp(current_ctx, "global") == 0) { - set_error_info(t->lig, t->col); + set_error_info(t->pos_infos); fatal_error("~BRETOURNER~E utilisé en dehors d'une fonction"); exit(1); } - t->codelen = 1; + if (node.expr != NULL) { semantic(node.expr); t->codelen = node.expr->codelen; - } + } + else t->codelen = 1; + + if (strcmp(current_ctx, "PROGRAMME") == 0) t->codelen += 1; + else t->codelen += 10 + PUSH_COST; } @@ -520,4 +848,75 @@ void semantic_exp_list(ast *t) t->codelen = node.exp->codelen; if (node.next != NULL) t->codelen += node.next->codelen; -} \ No newline at end of file +} + + + +void semantic_arr_access(ast *t) +{ + array_access_node node = t->arr_access; + semantic(node.id); + semantic(node.ind_expr); + semantic(node.affect_expr); + + set_error_info(node.id->pos_infos); + symbol *tmp = get_symbol(table, current_ctx, node.id->id.name); + if (tmp->type != array && tmp->type != pointer) + { + fatal_error("Impossible d'utiliser l'opérateur ~B[]~E"); + exit(1); + } + + if (node.affect_expr != NULL) tmp->is_init = 1; + + t->codelen = node.ind_expr->codelen; + if (tmp->type == array) t->codelen += (tmp->mem_zone == 's') ? 4 : 1; + else t->codelen += (tmp->mem_zone == 's') ? 5 : 3; + + if (node.affect_expr != NULL) t->codelen += node.affect_expr->codelen + 8; + else t->codelen += 1; +} + + +void semantic_alloc(ast *t) +{ + alloc_node node = t->alloc; + semantic(node.id); + semantic(node.expr); + + set_error_info(node.id->pos_infos); + symbol *tmp = get_symbol(table, current_ctx, node.id->id.name); + if (tmp->type != pointer) + { + symb_to_img(table, "table", "png"); + fatal_error("Impossible d'allouer de la mémoire à ~B%s~E "\ + "car ça n'est pas un pointeur.", node.id->id.name); + exit(1); + } + + tmp->is_init = 1; + + t->codelen = node.expr->codelen + 4; + if (tmp->mem_zone == 's') t->codelen += 5; + else t->codelen += 2; +} + + +void semantic_proto(ast *t) +{ + proto_node node = t->proto; + + set_error_info(node.id->pos_infos); + + /* Si déjà déclaré, on ignore */ + symbol *tmp = search_symbol(table, current_ctx, node.id->id.name); + if (tmp == NULL) + { + symbol *new_symb = init_symbol(node.id->id.name, 0, 'h', func); + new_symb->size = node.nb_params; + + /* Ajout dans la table des symboles */ + add_symbol(table, current_ctx, new_symb); + } +} + diff --git a/src/symbol_table.c b/src/symbol_table.c index f03080d..b64c8e7 100644 --- a/src/symbol_table.c +++ b/src/symbol_table.c @@ -62,10 +62,10 @@ symbol *init_symbol(const char *id, int adr, char zone, type_symb t) new_symb->size = 1; // ! pour fonctions new_symb->type = t; new_symb->mem_zone = zone; - new_symb->has_return = 0; /* Pour les fonctions */ new_symb->is_used = 0; /* Pour les warnings */ new_symb->is_modified = 0; new_symb->is_init = 0; + new_symb->is_checked = 0; strcpy(new_symb->id, id); return new_symb; @@ -131,7 +131,6 @@ symbol *get_symbol(symb_table table, const char *ctx, const char *id) } - res->is_used = 1; return res; } @@ -183,8 +182,6 @@ symbol *add_symbol(symb_table table, const char *c_name, symbol *s) - - context *add_context(symb_table table, const char *c_name) { if (table == NULL) return NULL; diff --git a/tests/all.algo b/tests/all.algo deleted file mode 100644 index e655fb8..0000000 --- a/tests/all.algo +++ /dev/null @@ -1,52 +0,0 @@ -# Ce programme fait la démonstration de toutes les fonctionnalités -# actuellement supportées par l'ASA - - -/* Déclaration de variables globales */ -VAR toto <- 5 * 3 + 2, titi <- 4 -VAR tutu <- 1 - -/* Déclaration de fonction */ -ALGO foo(a, b) -VAR res <- 0, tmp <- 1 -DEBUT - TQ a > 0 ET b > 0 FAIRE - SI a % 2 = 1 ET b % 2 = 1 ALORS - res <- res + 1 * tmp - FSI - a <- a / 2 - b <- b / 2 - tmp <- tmp * 2 - FTQ - - /* ToDo: retourner res */ -FIN - - -/* Autre variable globale */ -VAR tata <- tutu - - -/* Autre fonction */ -ALGO bar() -DEBUT - TQ tata > 0 FAIRE - SI tata % 2 = 0 ALORS - tata <- tata / 2 - SINON - tata <- tata - 1 - FSI - FTQ -FIN - - - -/* Programme principal */ -PROGRAMME() -VAR tutu, x -DEBUT - x <- 5 * (3 + 1) / (10 - 8) - tutu <- foo(x, 2) - toto <- titi * 2 + x - # NON 10 -FIN diff --git a/tests/alloc.algo b/tests/alloc.algo new file mode 100644 index 0000000..db5a78f --- /dev/null +++ b/tests/alloc.algo @@ -0,0 +1,21 @@ +/* Pour echanger et copie_tab */ +$ INCLURE utilitaires.algo + + +/* La bande doit contenir la taille du tableau puis les éléments du tableau */ +PROGRAMME() +VAR @tab, n, i, @cpy +DEBUT + n <- LIRE() + ALLOUER(tab, n) + POUR i DANS 0...n FAIRE + tab[i] <- LIRE() + FPOUR + + echanger(tab, 0, n - 1) + cpy <- copie_tab(tab, n) + + POUR i DANS 0...n FAIRE + ECRIRE(cpy[i]) + FPOUR +FIN diff --git a/tests/arithmetique.algo b/tests/arithmetique.algo index 84012c5..b9eee75 100644 --- a/tests/arithmetique.algo +++ b/tests/arithmetique.algo @@ -1,9 +1,13 @@ +/* Fonctionnalités arithmétiques de base */ PROGRAMME() -VAR x, y <- 5, z +VAR x, y, z DEBUT - # Test des expressions arithmétiques - /* Également un commentaire (et // aussi) */ - z <- 2 - x <- y * 3 + 5 - y <- x * 2 -FIN + x <- 2 + y <- 2 + 3 * -4 - - 1 + z <- x * y + 1 + + /* La bande de sortie doit-être [2, -9, -17] */ + ECRIRE(x) + ECRIRE(y) + ECRIRE(z) +FIN \ No newline at end of file diff --git a/tests/boucles.algo b/tests/boucles.algo index 8289945..52b0379 100644 --- a/tests/boucles.algo +++ b/tests/boucles.algo @@ -1,8 +1,9 @@ - - +/* Test des différentes boucles implémentées */ PROGRAMME() -VAR i <- 0, n <- 10 +VAR i <- 0, n DEBUT + n <- LIRE() // Attention la bande d'entrée ne doit pas être vide + /* Test de la boucle tant que (<=> while) */ TQ i <= n FAIRE i <- i + 1 diff --git a/tests/exemple1.algo b/tests/exemple1.algo index a2b94e8..8a8c95e 100644 --- a/tests/exemple1.algo +++ b/tests/exemple1.algo @@ -1,6 +1,4 @@ PROGRAMME() -VAR x DEBUT - # Ceci est un test Yohan va potentiellement tout casser - x <- 0 < (3 > 5) + (12+3)*4 - 7/2*(1-8) FIN \ No newline at end of file diff --git a/tests/exemple2.algo b/tests/exemple2.algo index 1a0c86a..dec40e4 100644 --- a/tests/exemple2.algo +++ b/tests/exemple2.algo @@ -1,14 +1,8 @@ -VAR x, y <- 10 -VAR z - -/* Test contextes */ -VAR a <- 1 - PROGRAMME() -VAR a <- 15, b +VAR toto, titi, tutu DEBUT - b <- 1 - x <- a - y - z <- x * 3 - (a % 2) - b <- NON (z <= y OU z < x) + toto <- 12 + titi <- 5 + tutu <- 2*(a-b) + toto <- (tutu < 0) FIN \ No newline at end of file diff --git a/tests/exemple3.algo b/tests/exemple3.algo index 418dfc2..71be920 100644 --- a/tests/exemple3.algo +++ b/tests/exemple3.algo @@ -6,4 +6,4 @@ DEBUT T[1] <- LIRE() somme <- T[0] + T[1] ECRIRE( somme ) -FIN +FIN \ No newline at end of file diff --git a/tests/fizzbuzz.algo b/tests/fizzbuzz.algo index 0574c76..55c4afe 100644 --- a/tests/fizzbuzz.algo +++ b/tests/fizzbuzz.algo @@ -1,8 +1,11 @@ -# Implémentation du célèbre jeu "FizzBuzz" -# Parcours les nombres jusqu'à une limite fixée, et si le nombre est -# divisible par 3, affiche "Fizz" (ici -3 car pas de strings) -# Si le nombre est divisible par 5, affiche "Buzz" (ici -5) -# S'il est divisible par 3 et 5, "FizzBuzz" (ici -15) +/* + * Implémentation du célèbre jeu "FizzBuzz" + * Parcours les nombres jusqu'à une limite fixée, et si le nombre est + * divisible par 3, affiche "Fizz" (ici -3 car pas de strings) + * Si le nombre est divisible par 5, affiche "Buzz" (ici -5) + * S'il est divisible par 3 et 5, "FizzBuzz" (ici -15) + */ + PROGRAMME() VAR n, i <- 1, flg DEBUT diff --git a/tests/fonctions.algo b/tests/fonctions.algo index 96125db..4f1dbb9 100644 --- a/tests/fonctions.algo +++ b/tests/fonctions.algo @@ -1,21 +1,25 @@ -ALGO foo(a, b) +/* Programme de test des fonctions */ +ALGO foo(a) DEBUT - RETOURNER a * b + 2 + RETOURNER a * 2 FIN - ALGO bar(a, b) -VAR x, y +VAR c DEBUT - x <- foo(a, b) - RETOURNER x + c <- a + foo(b) + RETOURNER c FIN +ALGO baz(a, b, c) +DEBUT + ECRIRE(bar(b, c) + a) +FIN -VAR toto <- 42 PROGRAMME() -VAR x <- 1, y <- 4 +VAR a <- 23, b <- 3 DEBUT - ECRIRE(bar(x, y)) -FIN + /* Doit afficher 42 */ + baz(a, 7, foo(b)) +FIN \ No newline at end of file diff --git a/tests/func.algo b/tests/func.algo deleted file mode 100644 index 157bf36..0000000 --- a/tests/func.algo +++ /dev/null @@ -1,13 +0,0 @@ -ALGO foo(a, b) -VAR x -DEBUT - x <- a + b - RETOURNER x -FIN - - -PROGRAMME() -VAR a <- 1, b <- 3 -DEBUT - ECRIRE(foo(a, b) * 2) -FIN \ No newline at end of file diff --git a/tests/inclure.algo b/tests/inclure.algo deleted file mode 100644 index 96d413b..0000000 --- a/tests/inclure.algo +++ /dev/null @@ -1,12 +0,0 @@ -// Avant -% INCLURE test_lib.algo -%INCLURE utilitaires.algo -// Après - - -PROGRAMME() -VAR x <- 7, y <- 25 -DEBUT - ECRIRE(min(x, y)) - ECRIRE(pgcd(x, y)) -FIN diff --git a/tests/io.algo b/tests/io.algo index 4b9c175..1235aeb 100644 --- a/tests/io.algo +++ b/tests/io.algo @@ -1,6 +1,7 @@ +/* Test des fonctions d'entrée/sortie. 1 valeur obligatoire dans la bande */ PROGRAMME() -VAR n, somme <- 4 +VAR n DEBUT n <- LIRE() - ECRIRE(n * somme + 1) + ECRIRE(n * n) FIN \ No newline at end of file diff --git a/tests/preproc.algo b/tests/preproc.algo new file mode 100644 index 0000000..f009d0a --- /dev/null +++ b/tests/preproc.algo @@ -0,0 +1,13 @@ +/* + * Test de l'inclusion de fichier (attention, il faut ajouter le flag + * `-I ./tests` à la compilation pour test_lib.algo) + */ +$ INCLURE test_lib.algo +$ INCLURE utilitaires.algo + +PROGRAMME() +VAR x <- 7, y <- 25 +DEBUT + ECRIRE(min(x, y)) + ECRIRE(pgcd(x, y)) +FIN diff --git a/tests/prototype.algo b/tests/prototype.algo new file mode 100644 index 0000000..9d15c2c --- /dev/null +++ b/tests/prototype.algo @@ -0,0 +1,25 @@ +/* Test des prototypes */ + +# Commentez pour qu'une erreur apparaisse +PROTO foo(a, b) + +ALGO bar(a, b) +VAR c <- a + 5 +DEBUT + RENVOYER foo(b, c) +FIN + + +ALGO foo(a, b) +DEBUT + RENVOYER a * b +FIN + +/* Décommentez pour qu'un warning apparaisse */ +// PROTO baz() + +PROGRAMME() +DEBUT + /* Doit afficher 42 */ + ECRIRE(bar(1, 7)) +FIN \ No newline at end of file diff --git a/tests/ptr.algo b/tests/ptr.algo new file mode 100644 index 0000000..876b4ae --- /dev/null +++ b/tests/ptr.algo @@ -0,0 +1,18 @@ +/* Programme de test des pointeurs */ + +ALGO echanger_int(@a, @b) +VAR c <- *a; +DEBUT + *a <- *b + *b <- c +FIN + + +PROGRAMME() +VAR x <- 10, y <- 2, @p +DEBUT + p <- @x + echanger_int(p, @y) + ECRIRE(x) + ECRIRE(y) +FIN \ No newline at end of file diff --git a/tests/recu.algo b/tests/recu.algo index 29aae68..1d633a8 100644 --- a/tests/recu.algo +++ b/tests/recu.algo @@ -1,3 +1,4 @@ +/* Test de la récursivité */ ALGO factorielle(n) VAR res <- 1 DEBUT @@ -8,9 +9,12 @@ DEBUT FIN +/* La bande de sortie doit-être: [1, 2, 6, 24, 120, 720, 5040] pour n = 7 */ PROGRAMME() -VAR n +VAR n, i DEBUT n <- LIRE() - ECRIRE(factorielle(n)) + POUR i DANS 1...n + 1 FAIRE + ECRIRE(factorielle(i)) + FPOUR FIN \ No newline at end of file diff --git a/tests/si.algo b/tests/si.algo index 172f047..c19bef7 100644 --- a/tests/si.algo +++ b/tests/si.algo @@ -3,7 +3,7 @@ VAR i <- 10 PROGRAMME() DEBUT // Test de la structure SI ALORS SINON - SI i = 0 ALORS + SI i <= 10 ET VRAI ALORS i <- 1 SINON i <- 0 diff --git a/tests/tab.algo b/tests/tab.algo new file mode 100644 index 0000000..5c1dc9c --- /dev/null +++ b/tests/tab.algo @@ -0,0 +1,10 @@ +/* + * Test des tableaux + */ + +PROGRAMME() +VAR a <- 1, toto[3] <- [1, 2, 3, 5], b <- 2, @t +DEBUT + ECRIRE(a + toto[toto[0] + 1]) + ECRIRE(toto) +FIN diff --git a/tests/test.algo b/tests/test.algo index 5ef6935..1d050de 100644 --- a/tests/test.algo +++ b/tests/test.algo @@ -1,18 +1,19 @@ -ALGO factorielle(n) -VAR res <- 1 -DEBUT - SI n > 1 ALORS - res <- n * factorielle(n - 1) - FSI - RETOURNER res -FIN - +VAR i, j, n +VAR cpt <- 0 PROGRAMME() -VAR n, i +# Somme des n premiers entiers n(n+1)/2 DEBUT n <- LIRE() - POUR i DANS 1...n + 1 FAIRE - ECRIRE(factorielle(i)) - FPOUR + i <- 0 + TQ i < n FAIRE + j <- i + TQ j < n FAIRE + cpt <- cpt + 1 + j <- j + 1 + FTQ + i <- i+1 + 1+1 + FTQ + ECRIRE(cpt) FIN \ No newline at end of file diff --git a/tests/test_lib.algo b/tests/test_lib.algo index 7d8c7ec..17b2ae1 100644 --- a/tests/test_lib.algo +++ b/tests/test_lib.algo @@ -1,3 +1,6 @@ +// Utilisé pour l'inclusion de fichier, n'a donc pas de PROGRAMME et ne doit +// PAS être compilé seul + /* Calcule le PGCD de a et de b */ ALGO pgcd(a, b) VAR tmp @@ -10,4 +13,9 @@ DEBUT RETOURNER a FIN -// \ No newline at end of file + + +ALGO ppcm(a, b) +DEBUT + RETOURNER a * b / pgcd(a, b) +FIN diff --git a/tests/tri_par_tas.algo b/tests/tri_par_tas.algo new file mode 100644 index 0000000..9c92472 --- /dev/null +++ b/tests/tri_par_tas.algo @@ -0,0 +1,81 @@ +/* + * Programme montrant à peu près toutes les possibilités offertes par le + * compilateur (toutes les constructions ne sont pas présentes). + * + * Tris par tas. + * La bande d'entrée doit contenir la taille du tableau puis les éléments du + * tableau. + */ + +/* Prototypes de fonctions */ +PROTO entasser(@tab, n) +PROTO tamiser(@tab, i_pere, i_fin) +PROTO echanger(@tab, i, j) + +/* Variables globales */ +VAR nb_comparaisons <- 0 +VAR nb_echanges <- 0 + + +ALGO tri_par_tas(@tab, n) +VAR k <- n - 1 +DEBUT + entasser(tab, n) + TQ k > 0 FAIRE + echanger(tab, 0, k) + tamiser(tab, 0, k) + k <- k - 1 + FTQ +FIN + + +ALGO entasser(@tab, n) +VAR i <- n / 2 +DEBUT + TQ i > 0 FAIRE + tamiser(tab, i, n) + i <- i - 1 + FTQ +FIN + +ALGO tamiser(@tab, i_pere, i_fin) +VAR i_fils +DEBUT + i_fils <- 2 * i_pere + 1 + SI i_fils < i_fin - 1 ET tab[i_fils + 1] > tab[i_fils] ALORS + i_fils <- i_fils + 1 + FSI + + SI i_fils < i_fin ET tab[i_pere] < tab[i_fils] ALORS + echanger(tab, i_pere, i_fils) + tamiser(tab, i_fils, i_fin) + FSI + + nb_comparaisons <- nb_comparaisons + 2 +FIN + +ALGO echanger(@tab, i, j) +VAR tmp +DEBUT + tmp <- tab[i] + tab[i] <- tab[j] + tab[j] <- tmp + nb_echanges <- nb_echanges + 1 +FIN + + +PROGRAMME() +VAR @tab, n, i +DEBUT + n <- LIRE() + ALLOUER(tab, n) + POUR i DANS 0...n FAIRE + tab[i] <- LIRE() + FPOUR + + tri_par_tas(tab, n) + + POUR i DANS 0...n FAIRE + ECRIRE(tab[i]) + FPOUR +FIN \ No newline at end of file diff --git a/tests/tri_rapide.algo b/tests/tri_rapide.algo new file mode 100644 index 0000000..93cac9e --- /dev/null +++ b/tests/tri_rapide.algo @@ -0,0 +1,82 @@ +/* + * Programme réunissant à peu près toutes les constructions possibles, en dehors + * des variables globales. + * + * Tris rapide. + * La bande d'entrée doit contenir la taille du tableau puis les éléments du + * tableau. + */ + +PROTO reculer(@tab, x, @j) +PROTO avancer(@tab, x, @i) +PROTO echanger(@tab, i, j) +PROTO partitionner(@tab, p, r) + +ALGO tri_rapide(@tab, p, r) +VAR q +DEBUT + SI p < r ALORS + q <- partitionner(tab, p, r) + tri_rapide(tab, p, q) + tri_rapide(tab, q + 1, r) + FSI +FIN + + +ALGO reculer(@tab, x, @j) +DEBUT + FAIRE + *j <- *j - 1 + TQ tab[*j] > x +FIN + + +ALGO avancer(@tab, x, @i) +DEBUT + FAIRE + *i <- *i + 1 + TQ tab[*i] < x +FIN + + +ALGO partitionner(@tab, p, r) +VAR i <- p, j <- r, x <- tab[p] +DEBUT + TQ tab[j] > x FAIRE + j <- j - 1 + FTQ + + TQ i < j FAIRE + echanger(tab, i, j) + reculer(tab, x, @j) + avancer(tab, x, @i) + FTQ + + RETOURNER j +FIN + + +ALGO echanger(@tab, i, j) +VAR tmp +DEBUT + tmp <- tab[i] + tab[i] <- tab[j] + tab[j] <- tmp +FIN + + +PROGRAMME() +VAR @tab, n, i +DEBUT + n <- LIRE() + ALLOUER(tab, n) + POUR i DANS 0...n FAIRE + tab[i] <- LIRE() + FPOUR + + tri_rapide(tab, 0, n - 1) + + POUR i DANS 0...n FAIRE + ECRIRE(tab[i]) + FPOUR +FIN \ No newline at end of file