agp: algorithmique et...

32
- p. 1/64 AGP: Algorithmique et programmation Tanguy Risset, Stéphane Ubéba [email protected], Stéphane.Ubé[email protected] Lab CITI, INSA de Lyon Version 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

Upload: duongngoc

Post on 14-Sep-2018

243 views

Category:

Documents


0 download

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: