agp: algorithmique et...
TRANSCRIPT
- p. 1/64
AGP: Algorithmique et programmation
Tanguy Risset, Stéphane Ubé[email protected], Stéphane.Ubé[email protected]
Lab CITI, INSA de LyonVersion du November 20, 2007
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 2/64
Plan
■ Compilation des procédures■ La pile d’exécution■ Paradigmes de programmation■ Grammaires■ Lex et Yacc
■ Sources: Claude Evéquoz, Claude Jard, Educnet
Introduction
Compilation des procédures
● Procédures
● Pile
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 3/64
Notion de procédure
■ Procédures: unités de base pour les compilateurs.■ Trois abstractions importantes:
◆ Abstraction du contrôle: passage de paramètres etrésultats.
◆ Abstraction de l’espace des noms: portée des variables.◆ Interface externe: signature de la procédure
■ En C, Les fonctions sont toutes définies au même niveau.■ Beaucoup de langage permettent de définir des fonction à
l’intérieur d’autres fonctions.
Introduction
Compilation des procédures
● Procédures
● Pile
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 4/64
Transfert de contrôle
■ Mécanisme de transfert de contrôle entre les procédures:◆ lorsqu’on appelle une procédure, le contrôle est donné à
la procédure appelée;◆ lorsque cette procédure appelée se termine, le contrôle
est redonné à la procédure appelante.◆ Deux appels à une même procédure créent donc deux
instances (ou invocations) indépendantes.■ Trois représentations graphiques utiles:
◆ Arbres de liens statiques◆ Le graphe d’appel: représente les informations écrites
dans le programme.◆ L’arbre d’appel: représente une exécution particulière.
Introduction
Compilation des procédures
● Procédures
● Pile
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 5/64
Exemple: arbre de liens statique (Pascal)
procedure calc;begin { calc}
...end;procedure call1;
var y...procedure call2
var z: ...procedure call3;
var y....begin { call3}
x:=...calc;
end;begin { call2}
z:=1;calc;call3;
end;begin { call1}
call2;...
end;
arbre de liens statiques:
main
Call3
Call1
Call2
Calc
Introduction
Compilation des procédures
● Procédures
● Pile
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 6/64
Graphe d’appel
procedure calc;begin { calc}
...end;procedure call1;
var y...procedure call2
var z: ...procedure call3;
var y....begin { call3}
x:=...calc;
end;begin { call2}
z:=1;calc;call3;
end;begin { call1}
call2;...
end;
Graphe d’appel:
Call1
main
Call2
Call3
Calc
Introduction
Compilation des procédures
● Procédures
● Pile
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 7/64
Un arbre d’appel
procedure calc;begin { calc}
...end;procedure call1;
var y...procedure call2
var z: ...procedure call3;
var y....begin { call3}
x:=...calc;
end;begin { call2}
z:=1;calc;call3;
end;begin { call1}
call2;...
end;
Arbre d’appeld’une exécutionparticulière:
main
Call3
CalcCalc
Call1
Call2
main appelle call1
call1 appelle call2
call2 appelle calc
calc revient à call2
call2 appelle call3
call3 appelle calc
calc revient à call3
call3 revient à call2
call2 revient à call1
call1 revient à main
Introduction
Compilation des procédures
● Procédures
● Pile
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 8/64
Pile d’exécution
■ Le mécanisme de transfert de contrôle entre les procéduresest implémenté grâce à la pile d’exécution.
■ Le programmeur à cette vision de la mémoire virtuelle:
Code static Tas PileMemoire libre
0(petites adresses)
100000(grandes adresses)
■ Le tas (heap) est utilisé pour l’allocation dynamique.■ La pile (stack) est utilisée pour la gestion des contextes des
procédures (variable locales, etc.)
Introduction
Compilation des procédures
● Procédures
● Pile
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 9/64
Enregistrement d’activation
■ Appel d’une procédure: empilement de l’enregistrementd’activation (AR pour activation record).
■ L’AR permet de mettre en place le contexte de la procédure.■ Cet AR contient
◆ L’espace pour les variables locales déclarées dans laprocédure
◆ Des informations pour la restauration du contexte de laprocédure appelante:■ Pointeur sur l’AR de la procédure appelante (ARP ou FP
pour frame pointeur).■ Adresse de l’instruction de retour (instruction suivant
l’appel de la procédure appelante).■ Éventuellement sauvegarde de l’état des registres au
moment de l’appel.
Introduction
Compilation des procédures
● Procédures
● Pile
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 10/64
Appel de procédure: état de la pile
avant l’appel après l’appel
ARP
SP
AR procédure
appelante
⇒
SP
ARP
AR procédure
appelante
AR procédure
appelée
Introduction
Compilation des procédures
● Procédures
● Pile
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 11/64
Contenu de l’AR
SP
ARP
AR procédure
appelante
AR procédure
appelée
Parametres
Sauvegarde des registres
Résultat
ARP appelant
Adresse de retour
Variables locales
Introduction
Compilation des procédures
● Procédures
● Pile
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 12/64
Retour de procédure: état de la pile
avant le retour après le retour
SP
ARP
AR procédure
appelante
AR procédure
appelée
⇒
ARP
SP
AR procédure
appelante
Introduction
Compilation des procédures
● Procédures
● Pile
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 13/64
Lien statique et Lien dynamique
■ Considérons une procédure employeur qui appelle uneprocédure fils .
■ Dans l’AR de fils , l’ARP appelant pointe sur l’AR deemployeur .
■ Ce pointeur l’ARP est quelquefois appelé le lien dynamique, ilpointe sur l’environnement de la procédure appelante (iciemployeur ).
■ Considérons maintenant la procédure mère dans laquellefils à été déclarée.
■ Dans certains langages comme Pascal, la procédure filspeut accéder aux variables de mère
■ Pour cela on a besoin d’un lien statique qui est un pointeursur l’environnement de la procédure ou l’on a été déclaré.
Introduction
Compilation des procédures
● Procédures
● Pile
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 14/64
Exemple: arbre d’appel
procedure main();var y...procedure mere()
var z: ...procedure fils();
begin { fils()}...
end; { fils()}procedure employeur();
var y....begin { employeur()}
x:=...fils();
end;begin { mere()}
z:=1;fils();employeur();
end;begin { main()}
mere();...
end;
Arbred’appel:
main
mere
fils
fils
employeur
Pile ( lors du 2eme appel de fils):
SP
ARP
AR procédure
AR procédure
AR procédure
fils
employeur
mere
ARP appelant
(dynamic link)
ARP appelant
(dynamic link)
Lien statique
(SL: static link)
Lien statique(SL: static link)
Introduction
Compilation des procédures
● Procédures
● Pile
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 15/64
Démo
■ Didacticiel disponible enhttp://ina2.eivd.ch/ina/Collaborateurs/cez/LangPara digm(Claude Evéquoz)
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 16/64
Paradigmes de programmation
■ Paradigme: un paradigme de programmation est un modèlede référence, une façon d’aborder un problème, une manièrede penser, c’est la concrétisation d’une philosophie deprogrammation
■ Pas de paradigme universel■ Les plus connus:
◆ Paradigme impératif Le programme agit de manièreséquentielle sur la mémoire de l’ordinateur (Ada83,Pascal, C, etc.).
◆ Paradigme objet L’exécution d’un programme est unesuccession d’échanges de messages entre objets. ( C++,Smaltalk, Java, Eiffel, etc)
■ Langage : Un langage de programmation est uneabstraction des données et des opérations réalisées par unordinateur. Un langage est habituellement dominé par unparadigme à partir duquel sa structure a été conçue.
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 17/64
Langages de programmation: la technologie
■ Abstraction des données◆ types de base: entiers, caractères, etc.◆ structures: tableaux, enregistrements, équivalence◆ unités: classes, modules, paquetages
■ Abstraction de contrôles◆ énoncés: affectations,◆ séquencements: boucles, branchements conditionnels◆ traitement des exceptions
■ Techniques de définitions des langages◆ syntaxe (c.-à-d. qu’est-ce une expression légale?)◆ sémantique (c.-à-d., quel est le résultat de l’exécution?)
■ Techniques d’exécution des programmes◆ Compilation, édition des liens◆ Interprétation◆ Machine virtuelle
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 18/64
Paradigme impératif
■ Combinaison de trois composants fondamentaux:◆ la séquence (l’ordre dans lequel les instructions sont
exécutées),◆ la décision (exécution d’instructions sous condition vraie
ou fausse),◆ la répétition (répétition contrôlée d’un bloc d’instructions).
■ Point faible: programmation pas très haut niveau.Procedure imprimerFacture(FACTURE: Facture)
début
Si connectionImprimante()
alors
imprimer(facture.entete)
Pour i <- 1 à facture.nblignes
imprimer(facture.ligne[i]) / * ordonnancement explicite
FinPour
imprimer(facture.pied)
finImpression()
FinSi
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 19/64
Paradigme fonctionnel
■ Décomposer un programme en sous-programmes(fonctions), eux-mêmes décomposés en sous-programmes,etc.
■ Difficile d’ajouter des fonctionnalités
fonction imprimerLignes(lignes)
Debut
Si lignes!=NULL alors
imprimer(premier(lignes));
imprimerLignes(suivantes(lignes))
FinSi
Fin
fonction imprimerFacture(facture : Facture)
début
Si connectionImprimante() alors
débutImpression()
imprimer(entete(facture))
imprimerLignes(lignes(facture))
imprimer(pied(facture))
finImpression()
FinSi
fin
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 20/64
Exemple programmation fonctionnelle: LISP
■ Utile pour les manipulation symboliques (ex: AST).defun factorial (n)
"Calcule la factorielle de l’entier n."(if (<= n 1)
1( * n (factorial (- n 1)))))
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 21/64
Paradigme de décomposition objet
■ un programme est constitué d’objets collaborant entre euxpar envois de messages.
■ Ancêtre: Simula (1967). Smalltalk (1970, Rank-Xerox - PaloAlto), puis Eiffel (années 1980, Bertrand Meyer), C++(années 1980, Bjarne Stroustrup, AT&T Bell), Java (années1990, Sun) et C# (années 2000, Microsoft).
■ Un objet est une instance d’une classe.■ L’objet est plus qu’un conteneur de données sur lesquelles
sont définies des opérations, il a en charge une partie desresponsabilités de contrôle.
■ Tout l’art de l’analyse et de la conception objet consiste àidentifier les objets pertinents et à faire en sorte qu’ilscollaborent, en s’assurant de leur capacité à être réutilisésdans d’autres contextes applicatifs.
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 22/64
Exemple de décomposition objet
■ Facture est une classe possédant la méthode imprimer.méthode Facture::imprimer(impr : Imprimante)début/ * impr est un objet qui désigne la
responsabilité « impression » * /impr.print(entete)Pour chaque ligne de moi.Lignes Faire
impr.print(moi.ligne)FinPourimpr.print(pied)
fin
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 23/64
Paradigme Model-View-Controller
■ Séparer une application en trois parties :◆ le modèle, qui représente la source d’information (souvent
UML),◆ la vue, qui fournit une à plusieurs représentations du
modèle,◆ le contrôleur, qui synchronise les actions et définit une
sémantique.■ Tendances actuelles: Java (J2EE) et architecture .NET
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 24/64
Paradigmes: conclusion
■ Tous les langages compilés utilisent les même mécanismesde compilation (pile d’exécution).
■ Les langages de haut niveau permettent un nommage desdonnées et des actions plus adaptés que l’assembleur auxdéveloppement d’applications complexes.
■ Les paradigmes correspondent à une réorganisation del’espace des noms.
■ l’orientation objet est une réorganisation de l’espace desnoms du programme d’un schéma centré sur les procéduresvers un schéma centré sur les données.
■ Les langages interprétés (Java, Matlab, etc.) requièrent unmécanisme plus complexe à l’exécution.
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 25/64
Définition d’un langage
■ Langage = Syntaxe + Sémantique■ Sémantique égale, syntaxe différente:
◆ en C: if (x == 0) a = 1;◆ en Pascal: if x = 0 then begin a := 1; end;
■ Sémantique différente, syntaxe égale : 4 + 1 / 2 vaut :◆ 4 en C (opérations entières)◆ 9/2 en Mathematica
■ La syntaxe d’un langage définit l’ensemble des règles quidécrivent la structure de ce langage.
■ elle est définie par une grammaire qui elle même se décritpar un langage appelé métalangage.
■ Un métalangage est un langage qui permet de décrire unautre langage.
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 26/64
Notation BNF
La notion Backus-Naur Form (BNF) est le métalangage le pluscouramment utilisé pour décrire la syntaxe des langages deprogrammation.
■ Les symboles <, >, et ::=sont ceux du métalangageet n’appartiennent pas aulangage décrit.
■ Le symbole ::= signifie«est défini par».
■ Les symboles placés entreles chevrons < et > sontdes symbolesnon-terminaux (ilsappartiennent aumétalangage)
■ Les autres symboles sontappelés les symbolesterminaux.
<terme> ::= <terme> * <chiffre><terme> ::= <chiffre><chiffre> ::= 0<chiffre> ::= 1<chiffre> ::= 2<chiffre> ::= 3<chiffre> ::= 4<chiffre> ::= 5<chiffre> ::= 6<chiffre> ::= 7<chiffre> ::= 8<chiffre> ::= 9
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 27/64
Grammaire et langage
■ Notation simplifiée pour le même langage:<terme> ::= <terme> * <chiffre> | <chiffre><chiffre> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
■ En partant du non-terminal terme , on peut décrire lesmultiplications d’un nombre quelconque de chiffres, parexemple 0* 9* 7 ou 8* 8* 8* 8. Ce sont des mots du langagedéfinit par la grammaire.
■ Les règles ci dessus sont appelées règles de productions dela grammaire.
■ Pour définir complètement une grammaire, il faut unquadruplet: (S,T,N,P)◆ S est le symbole de départ,◆ T est l’ensemble des terminaux◆ N est l’ensemble des non-terminaux◆ S est l’ensemble des règles de production
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 28/64
Exemple: TP2
■ Syntaxe des fichiers de log2003-12-20 11:13:20 DROP SRC=134.214.0.1 DEST=134.214.1 00.6 PROTO=ICMP SPORT=8
■ Grammaire (S,T,N,P),
■ S={ LOG},
■ T= { SRC, DST, DROP , REJECT , ACCEPT, TCP ,
ICMP , UDP, ’ ’, \t, \n, 0, 1, 2, 3, 4, 5, 6,
6, 8, 9, -, ., : },
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 29/64
Exemple: TP2
2003-12-20 11:13:20 DROP SRC=134.214.0.1 DEST=134.214.1 00.6 PROTO=ICMP SPORT=8 DPORT=8
P={<LOG> ::= <LIGNE> \n <LOG> | <LIGNE>
<LIGNE> ::=<DATE> <SEP> <HEURE> <SEP> <ACTION> <SEP> <SOURCE> \
<SEP> <DESTINATION> <SEP> <FINLIGNE> \
<PROTOCOLE> <SEP> <SOURCEPORT> <SEP> <DESTPORT>
<DATE> ::= <ENTIER> - <ENTIER> - <ENTIER>
<HEURE> ::= <ENTIER> : <ENTIER> : <ENTIER>
<SOURCE> ::= SRC = <ADRESSE>
<DESTINATION> ::= DEST = <ADRESSE>
<SOURCEPORT>::= SPORT = <ENTIER>
<DESTPORT> ::= DPORT = <ENTIER>
<ADRESSE> :: <ENTIER> . <ENTIER> . <ENTIER> . <ENTIER>
<ACTION> ::= DROP | REJECT | ACCEPT
<PROTOCOLE> ::= TCP | ICMP | UDP
<SEP> ::= <CARSEP> <SEP> | <SEP>
<ENTIER> ::= <CHIFFRE> <ENTIER> | <CHIFFRE>
<CARSEP> ::= ’ ’ | \t
<CHIFFRE> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 30/64
Arbre de dérivation
■ Soit la grammaire suivante:<expression> ::= <expression> + <expression> |
<expression> * <expression> |<chiffre>
<chiffre> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
■ Le mot 4+3* 2 fait partie du langage de cette grammaire, ilcorrespond à la dérivation:
<expression> ==> <expression> + <expression>==> <expression> + <expression> * <expression>==> <chiffre> + <expression> * <expression>==> <chiffre> + <chiffre> * <expression>==> <chiffre> + <chiffre> * <chiffre>==> 4 + <chiffre> * <chiffre>==> 4 + 3 * <chiffre>==> 4 + 3 * 2
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 31/64
Arbre de dérivation pour 4+3*2
■ Deux arbres possibles: la grammaire est ambiguë.
<expression>
<expression>
<chiffre>
<expression>
<expression>
<chiffre>
4 3+ * 2
<chiffre>
<expression>
<expression>
<expression>
<chiffre> <chiffre>
<expression>
<expression>
<expression>
<chiffre>
4 + * 23
■ Deux dérivations sont équivalentes si elles ont le mêmearbre de dérivation (seul l’ordre dans lequel on a choisit lesrègles peut changer).
■ Pour nous, l’expression n’est pas ambiguë, il s’agit bien de4+(3 * 2)
■ Les grammaires manipulées doivent, autant que possible,être non-ambiguës.
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 32/64
Solution: modifier la grammaire
■ ... Sans modifier le langage:<expression> ::= <expression> + <expression> |
<terme><terme> ::= <terme> * <terme> | <chiffre><chiffre> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
■ ⇒ un seul arbre de dérivation possible.■ On peut faire d’autres améliorations pour l’associativité des
opérateurs et les parenthèses. Une grammaire courammentutilisée pour les expressions arithmétiques est la suivante:
<expression> ::= <expression> + <terme> | <terme><terme> ::= <terme> * <facteur> | <facteur><facteur> ::= ( <expression> ) | <chiffre><chiffre> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 33/64
Classification de Chomski
■ Grammaires de type 3 : régulière.◆ Les productions ont la forme :<A> ::= t <B>◆ Reconnaissent les langages réguliers: le même ensemble
de langages que les expressions régulières (cf TD).■ Grammaires de type 2 : indépendantes du contexte.
◆ Les productions ont la forme :<A> ::= liste de terminaux et non-terminaux
◆ Plus grande classe que les langages réguliers (ex:{an
bn|n ∈ ZZ})
◆ Grammaires utilisées pour décrire les langages deprogrammation, correspond à la notation BNF.
■ Grammaires de type 1 : dépendante de contexte.◆ Les productions ont la forme : a <A> b ::= a B b où a
et b sont des listes, de terminaux et non-terminaux, quipeuvent être vides et B est soit un terminal, soit unnon-terminal. a et b sont les contextes de la règle.
■ Grammaire de type 0 : libre, aucune restriction sur lesproductions.
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 34/64
Parsing
■ Le parsing consiste à essayer de reconnaître un mot commefaisant partie du langage décrit par la grammaire.
■ Il existe deux techniques:◆ Parsing top-down: on part du symbole racine et on essaye
de construire l’arbre de dérivation en fonction de ce quel’on rencontre en lisant le mot. Utilisé lors de l’écrituremanuelle du parseur (moins efficace mais plus simple)
◆ Parsing bottom-up: on reconstruit l’arbre à partir desfeuilles pour arriver à la racine. Plus efficace grâce àl’invention des grammaires LR(1) (Knuth), utilisé par lescompilateurs de compilateurs.
■ Toutes les techniques utilisent un automate pour reconnaîtreles mots.
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 35/64
Compilateur de compilateur
Compilateur de
fich.c
a.out
CCompilateur de
fich.x
a.out
X
Grammaire
de X
Compilateur de
compilateur
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 36/64
Exemple du TP4
■ Génération d’AST à partirde l’expression régulière
■ But: transformer unechaîne représentant uneexpression régulière en unAST.
■ NOTE: Le TP est pris pourillustrer Lex et Yacc, ce quisuit ne doit pas vous servirpour le TP (il ne faut paschanger le parsing utilisédans le TP).
Pour ((a+b).c) * .# , onvoudrait obtenir:
1 b
4 *
3 .
2 +
1 a
1 c
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 37/64
Structure de l’AST
■ L’utilisateur définit la structure de donnée dans laquelle ilveut mettre l’arbre de syntaxe abstrait (fichier tree_agp.h ):
#define NODE_VAR 1
#define NODE_OR 2
#define NODE_AND 3
#define NODE_STAR 4
typedef struct node {
int type_node ; / * type de noeud * /
char var; / * variable * /
struct node * fg; / * fils gauche * /
struct node * fd; / * fils droit * /
} NODE ;
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 38/64
Lex et Yacc
■ La génération sera faite en deux temps:■ Analyse lexicale
◆ outil lex ou flex : fast lexical analyser◆ lit les caractères du programme source et reconnaît les
constituants syntaxiques qu’ils représentent (les "unitéslexicales").
■ Analyse syntaxique:◆ structure la suite d’unités lexicales en catégories
"grammaticales";◆ on présente généralement cette structure à l’aide d’un
arbre, l’arbre syntaxique (AST).◆ outil yacc (Yet Another Compiler Compiler), ou bison
(version récente de yacc ).
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 39/64
Analyse lexicale■ Passe de ((a+b).c) * .#
■ à un suite de composants syntaxiques (tokens):<’(’> <’(’> <’a’,VARIABLE> <’+’,OR> <’b’,VARIABLE> <’)’>
<’.’,AND> <’c’,VARIABLE> <’)’> <’ * ’,STAR>
■ Fichier de commande flex (fichier auto.l ):%{
#include "auto.tab.h" //déclaration des tokens (fichier g énéré par yacc)
%}
%%
[a-zA-Z] {yylval.c=yytext;
return VARIABLE;}
"(" {return ’(’;}
")" {return ’)’;}
"#" {yylval.c=yytext;
return SHARP;}
" * " {yylval.c=yytext;
return STAR;}
"+" {yylval.c=yytext;
return OR;}
"." {yylval.c=yytext;
return AND;}
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 40/64
Analyse lexicale
■ Commande: flex auto.l
■ Génère un fichier lex.yy.c qui contient la définition d’unautomate permettant de reconnaître les unités lexicales
flex
auto.l
lex.yy.c a.out
((a+b).c).#
<’(’> <’(’> <’a’,VARIABLE> ...
gcc
yacc
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 41/64
Analyse syntaxique
■ Passe de<’(’> <’(’> <’a’,VARIABLE> <’+’,OR> <’b’,VARIABLE> <’)’>
<’.’,AND> <’c’,VARIABLE> <’)’> <’ * ’,STAR>
à la structure désirée■ Grammaire utilisée:
<mot> ::= <expr> . #<expr> ::= <VARIABLE> |
( <expr> ) |<expr> <OR> <expr> |<expr> <AND> <expr> |<expr> <STAR>
■ yacc associe des actions à chaque règle, ces actions vontpermettre de construire l’AST basé sur l’arbre de dérivation
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 42/64
Arbre de dérivation et AST
a + b
<VARIABLE>
<expr>
<VARIABLE>
<expr>
<expr>
(
<expr>
<expr>
c
<expr>
<VARIABLE>
) .( )
1 b
4 *
3 .
2 +
1 a
1 c
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 43/64
■ Fichier de commande yacc (auto.y ):ok_ext : ok
{root=$1;YYACCEPT;}
ok : expr AND SHARP
{$$=creerNODE(NODE_AND,"",$1,creerNODE(NODE_VAR,$3, 0,0));}
expr : VARIABLE
{ $$=creerNODE(NODE_VAR,$1,0,0);}
| ’(’ expr ’)’
{$$=$2;}
| expr OR expr
{$$=creerNODE(NODE_OR,"",$1,$3);}
| expr AND expr
{$$=creerNODE(NODE_AND,"",$1,$3);}
| expr STAR
{$$=creerNODE(NODE_STAR,"",$1,0);;}
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 44/64
Lex et yacc combiné
flex
lex.yy.c
1 b
4 *
3 .
2 +
1 a
1 c
auto.l
a.out
((a+b).c).#
gcc
yacc
gccyacc a.out
yacc
auto.y
auto.tab.c
<’(’> <’(’> <’a’,VARIABLE> ...
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 45/64
Vue de l’utilisateur
flex
lex.yy.c
1 b
4 *
3 .
2 +
1 a
1 c
auto.l
auto.tab.c
yacc
auto.y
((a+b).c).#
gcc a.out
tree_agp.h
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 46/64
Lex et Yacc en résumé
■ Outils pour produire des parseurs■ Utiles pour traiter les fichiers de données ou pour analyser
des formats simples.■ Outils open source (GNU) extrêmement solides et portables
(produisent du C travaillant sur les E/S standard).
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 47/64
Plan
■ Pointeurs de fonctions■ Les erreurs courantes en C (source
http://nicolasj.developpez.com/articles/erreurs/par exemple)
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 48/64
Utilité des pointeurs de fonction
■ Mécanismes dynamiques◆ plug-in◆ Modifier une fonctionnalité sans arrêter le programme◆ ajouter de nouvelles fonctionnalités
■ Exemple: fonction de décodage de trame niveau 2:dépendant de l’interface connectée (ethernet, wifi, etc.)
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 49/64
Un premier exemple
#include <stdio.h>
#include <stdlib.h>
//declaration de fonction
int fonct1(int a)
{
fprintf(stdout,"Je suis fonct1(%d)\n",a);
return(0);
}
int main()
{// declaration de pointeur de fonction
int ( * foncPtr)(int a);
foncPtr=&fonct1;
( * foncPtr)(10);
return(0);
}
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 50/64
Comprendre les déclarations
■ Déclaration d’une variable: int * q[3]◆ [] plus prioritaire que * , donc:
int * q[3] ⇔ int ( * (q[3]))◆ l’expression ( * (q[3])) est de type int◆ l’expression q[3] est de type pointeur vers un int◆ l’expression (i.e. la variable) q est de type tableau de
pointeur vers un int
■ Déclaration d’une fonction:int fonct1(int a)◆ l’expression fonct1(int a) est de type int◆ l’expression (i.e. la variable) fonct1 est de type fonction
qui prend un int et renvoie un int◆ Les parenthèses après un symbole indique que le
symbole est une fonction (de même que les crochetsaprès un symbole indique que le symbole est un tableau).
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 51/64
Déclaration d’un pointeur de fonction
■ Déclaration d’un pointeur de fonction:int ( * foncPtr)(int a)◆ l’expression ( * foncPtr)(int a) est de type int◆ l’expression ( * foncPtr) est de type fonction qui prend
un int est renvoie un int◆ l’expression (i.e. la variable) foncPtr est de type pointeur
vers une fonction qui prend un int et renvoie un int
■ lors de l’utilisation, (presque) tout se passe comme si lafonction était une Lvalue:◆ On peut affecter une adresse de fonction au pointeur de
fonction: foncPtr=&fonct1;◆ Si on déréférence le pointeur de fonction, on obtient une
fonction: l’exécution de ( * foncPtr)(10); affiche:Je suis la fonction fonct1(10)
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 52/64
En fait, c’est un peu plus compliqué...
■ En C, une fonction est automatiquement castée en pointeurde fonction (et inversement):foncPtr=&fonct1 ⇔ foncPtr=fonct1( * foncPtr)(10); ⇔ (foncPtr)(10)
■ Tout comme pour les tableaux:tab ⇔ &tab
■ Pour les fonctions et les tableaux qui ne sont pas desL-values (on les appelle quelquefois des labels) lecompilateur identifie a et &a
■ On peut donc écrire:int ( * foncPtr)(int a);
foncPtr=fonct1;foncPtr(10);
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 53/64
On peut donc ecrire:
#include <stdio.h>
#include <stdlib.h>
//declaration de fonction
int fonct1(int a)
{
fprintf(stdout,"Je suis fonct1(%d)\n",a);
return(0);
}
int main()
{// declaration de pointeur de fonction
int ( * foncPtr)(int a);
foncPtr=fonct1;
foncPtr(10);
return(0);
}
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 54/64
Un autre exemple
//comparaison de deux entiers
int croissant(int i, int j)
{
if (i<=j) return 0;
else return 1;
}
int decroissant(int i, int j)
{
if (i<=j) return 1;
else return 0;
}
int main(void)
{
int i,t[6]={1,5,2,3,6,4};
trie(t, 6, croissant);
for(i=0;i<6;i++)
fprintf(stdout," %d ",t[i]);
fprintf(stdout,"\n");
trie(t, 6, decroissant);
for(i=0;i<6;i++)
fprintf(stdout," %d ",t[i]);
fprintf(stdout,"\n");
return 0;
}
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 55/64
... la fonction tri
void trie(int tableau[], int taille, int (fcomp)(int, int) )
{
int i,j,min;
//tri par permuation avec fcomp comme fonction de comparais on
for (i=0;i<taille;i++)
{
min=tableau[i];
for (j=i+1;j<taille;j++)
if (fcomp(tableau[i],tableau[j]))
{
min = tableau[j];
tableau[j]=tableau[i];
tableau[i]=min;
}
}
return ;
}
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 56/64
Passage de fonction par référence
■ Comme pour une variable normale, on peut modifier unpointeur de fonction en le passant en paramêtre parréférénce.
■ Pour avoir une fonction qui modifie un pointeur de fonction(i.e. qui modifie la qui fonction appelée lorsque le pointeurest invoqué), il faut que son paramêtre soit un pointeur surun pointeur de fonction.
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 57/64
Passage de fonction par référencechangeOrdre(int ( ** fcomp1)(int, int), int ( * fcomp2)(int, int))
{
* fcomp1=fcomp2;
}
int main(void)
{
int i,t[6]={1,5,2,3,6,4};
int ( * fcomp)(int,int);
fcomp=croissant;
trie(t, 6, fcomp);
for(i=0;i<6;i++)
fprintf(stdout," %d ",t[i]);
fprintf(stdout,"\n");
changeOrdre(&fcomp,decroissant);
trie(t, 6, fcomp);
for(i=0;i<6;i++)
fprintf(stdout," %d ",t[i]);
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 58/64
Les erreurs courantes en C
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 59/64
Confusion entre == et =
■ À ne pas faire:if (size = 0) ....
■ Détection: l’option -Wall du compilateur avertit leprogrammeur (warning )
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 60/64
Confusion entre opérateurs logiques et binaires
■ Le ET logique (&&) qui retourne 0 ou 1 (en s’arrêtant aupremier argument s’il est faux)
■ Le ET binaire (&) évalue ses deux opérandes et effectue leET bit à bit entre ses deux opérandes.
■ Le OU logique (|| ) qui retourne 0 ou 1 (en s’arrêtant aupremier argument s’il est vrai)
■ Le OU binaire (| ) évalue ses deux opérandes et effectue leOU bit à bit entre ses deux opérandes.
■ Impossible à détecter à la compilation.
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 61/64
Problèmes de macros
■ On n’écrit pas#define MAX 10;◆ A[MAX] devient A[10;]
■ On n’écrit pas#define MAX=10◆ Erreur détectée à la compilation, mais lors de l’utilisation
de MAX(la ligne référencée n’est pas celle de la définitionde MAX).
■ On écrit#define MAX 10
■ En cas de doute, on peut utiliser gcc -E pour vérifierl’expansion correcte des macros.
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 62/64
Fonctions retournant un caractère getc ...
char c;while ( (c = getchar ()) != EOF)...■ La fonction getchar retourne un entier
■ Les cast implicites effectués sont:while ( (int)(c = (char)getchar ()) != EOF)
■ le caractère EOF (qui marque la fin d’un fichier : End Of File)est un caractère invalide généralement égal à -1 mais il peutparfaitement être égal à 128, dans ce cas on dépasse lacapacité de stockage d’un char et l’on se retrouve avec unrésultat égal à -128 : la condition du while sera toujoursfausse, le programme boucle indéfiniment!
■ Il faut écrire:int cInt;char c;while ( (cInt = getchar ()) != EOF)
{c=(char)cInt;
...
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 63/64
Erreurs avec if et for
■ point-virgule mal placéif (a < b) ;
a = b;for (i=0; i<N; i++);
printf("%d", i);■ Le mauvais else
if (a < b)if (b < c) then b = c;
elseb = a;
■ Ce qu’il fallait faire:if (a < b)
{ if (b < c) then b = c; }else
b = a;
Introduction
Compilation des procédures
Paradigmes de programmation
Grammaires
Lex et Yacc
Introduction
Pointeur de fonctions
Erreurs courante en C
- p. 64/64
Et les plus courantes...
■ Mauvaise indentation■ Pas de makefile (ou pas de cible clean dans le makefile)■ exemple de fichier configuration vi:
syntax on " coloration syntaxiqueset autoindent " identationset cindent " pour Cset nopasteset ts=4 " tabulation a 4 caracteresset sw=4
■ Sous vi:◆ == pour indenter la ligne courante,◆ =Gpour identer tout le fichier: