• Aucun résultat trouvé

IFT313 Introduction aux langages formels

N/A
N/A
Protected

Academic year: 2022

Partager "IFT313 Introduction aux langages formels"

Copied!
18
0
0

Texte intégral

(1)

IFT313

Introduction aux langages formels

Froduald Kabanza

Département d’informatique Université de Sherbrooke

Java CUP

Générateur d’analyseurs LALR

(2)

Sujets

• Java CUP

Introduction

Actions sémantiques Gérer des conflits

Recouvrement d’erreur

(3)

Références

[2] Appel, A. and Palsberg. J. Modern Compiler Implementation in Java.

Second Edition. Cambridge, 2004.

Section 3.4 à 3.5

[4] Aho, A., Lam, M., Sethi R., Ullman J. Compilers: Principles, Techniques, and Tools, 2nd Edition. Addison Wesley, 2007.

Section 4.8 à 4.9

(4)

Java CUP

– CUP signifie « Construction of Useful Parser »

– C’est un générateur d’analyseurs LALR(1). Il est écrit en Java. Il génère des analyseurs en Java.

– Écrit originalement par Scott Hudson, Frank Flannery, C. Scott Ananian (University of Princeton)

– Maintenant maintenu par l’University of Munich.

http://www2.cs.tum.edu/projects/cup/

– Nous voyions une veille version (2006), alignée sur la syntaxe de Yacc (Yet Another Compiler-Compiler). Yacc est un générateur d’analyseurs syntaxiques écrit en C pour les environnements UNIX.

(5)

Exemple (calcsyntax1/parser.cup)

/* Terminals (tokens returned by the scanner). */

terminal PLUS, TIMES, LPAREN, RPAREN;

terminal Integer NUMBER;

/* Non terminals */

non terminal E, T, F;

/* The grammar */

E ::= E PLUS T | T ; T ::= T TIMES F | F ;

F ::= LPAREN E RPAREN | NUMBER ;

(6)

Commande

Commande : java –jar java-cup-11a.jar grammaire.cup Options

- donne toutes les options

-parser Parser spécifie le nom de classe du parser (déf: parser.java) -symbols Sym spécifie le nom de classe types de symboles (def

sym.java)

Il y en a d’autres : voir le manuel.

(7)

Génération d’un analyseur

Java CUP

Spécification Java CUP (parser.cup)

Spécification JFlex

(scanner.jflex)

Parser.java

(analyseur syntaxique)

Sym.java

(types symboles)

JFlex

javac

Scanner.java

(analyseur lexical) Parser.class Scanner.class

Code source

Sym.class

AST en mémoire Ou inerprétation

(8)

Attributs synthétiques dans Java CUP

Dans Java CUP les attributs sont optionnels.

Chaque symbole dans la partie droite d’une production peut être optionnellement étiqueté par un autre symbole (un attribut).

L’étiquette apparaît juste après le symbole, les deux étant séparés de « : »

Les étiquettes pour une production doivent être uniques, et peuvent être utilisés dans l’action sémantique associée à la production pour référer aux valeurs des symboles correspondants.

Le symbole dans la partie gauche est implicitement étiqueté par RESULT.

Les valeurs des attributs par les terminaux sont fournies par le scanner. Les autres sont calculés par les actions sémantiques.

(9)

Exemple

E ::= E:x PLUS T:y

{: RESULT = new Integer(x.intValue() + y.intValue()); :}

– Le symbole E dans la partie droite est étiqueté par x et le symbole T par y.

– RESULT est la valeur pour le symbole E dans la partie gauche de la pro- duction.

– Chaque symbole dans une règle de production est représenté par un objet (classe Symbol par défaut) sur la pile. Les étiquètes réfèrent aux valeurs de ces objets.

x et y réfèrent à des objets de la classe Integer parce que les symboles cor- respondants ont été déclarés comme étant de la classe Integer.

– Il en va de même pour RESULT.

(10)

Gérer des conflits

– Si une grammaire n’est pas LALR(1), normalement Java CUP va générer des conflits (shift/reduce ou reduce/reduce)

– En particulier, ce sera le cas si la grammaire est ambiguë (vu qu’elle ne peut pas être LR (k) quelque soit le cas; elle donne lieu à

plusieurs arbres d’analyse).

– Toutefois, on peut utiliser une grammaire non LALR (1), voire ambiguë, à condition de spécifier des règles de désambiguïsation de sorte qu’il y ait un seul arbre d’analyse :

– Ces règles indiquent comment gérer les conflits (shift/reduce, re- duce/reduce).

• Cela permet de travailler avec des grammaires plus simples et plus facile à comprendre, ou plus expressives que les grammaires LALR(1).

(11)

Règles par défaut

Par défaut Java CUP résout les conflits dans la table d’analyse LALR(1) en utilisant les règles suivantes:

1. Un conflit reduce/reduce est résolu en choisissant l’élément correspondant à la règle de production qui apparaît en premier lieu dans la spécification de la grammaire.

2. Un conflit shift/reduce est résolu en faveur du shift.

Ceci permet entre autres de résoudre correctement le conflit shift/reduce généré par l’ambiguïté sous-jacente à l’instruction if-then-else.

Il revient au programmeur de s’assurer que les règles par défaut correspondent à ce qu’il veut. Si ce n’est pas le cas, il a deux choix : (a) spécifier des règles de

désambiguïsation; (b) utiliser une grammaire équivalente non ambiguë.

(12)

Règles spécifiées explicitement

– On peut spécifier des règles de précédence et d’associativité comme suit :

precedence left terminal [, terminal...];

precedence right terminal [, terminal...];

precedence nonassoc terminal [, terminal...];

(13)

Règles spécifiées explicitement

L’ordre de précédence (priorité), du plus élevé au moins élevé, va du bas vers le haut. Ainsi, les déclarations suivantes indiquent que :

l’addition et la soustraction ont la même priorité;

la multiplication et la division ont la mêmes priorités;

et ces derniers ont une priorité plus élevée que l’addition et la soustraction:

precedence left ADD, MINUS;

precedence left TIMES, DIVIDE;

La précédence résout des conflit shift/reduce.

Par exemple, avec les déclarations précédentes, étant donné l’entrée 3 + 4 * 8,

L’analyseur doit déterminer s’il faut réduire ‘3 + 4’ ou avancer (shift) '*' sur la pile. Puisque '*' a une plus grande priorité que '+', il va être mis sur la pile, de sorte

(14)

Règles spécifiées explicitement

– Java CUP assigne une priorité à chaque terminal, en se basant sur l’ordre de leurs déclarations dans les spécification ‘precedence’, dans l’ordre in- verse de leur apparition.

– Java CUP assigne aussi une priorité à chaque production : c’est la priorité du dernier terminal dans la partie droite de la production.

– Par exemple, expr ::= expr TIMES expr a la même précédence que TIMES.

(15)

Règles spécifiées explicitement

Lorsqu’on a un conflit shift/reduce :

Si le terminal (avant le point) a une plus grande priorité que la production correspondant à l’élément reduce, on fait shift.

S’ils ont la même priorité, l’associativité du terminal détermine ce qu’on fait:

Si l’associativité du terminal avant le point est left, on fait reduce. Par exemple avec les déclarations précédentes, avec une entrée du genre 3 + 4 + 5, l’analyseur va toujours faire reduce de gauche à droite, en commençant par 3 + 4.

Si l’associativité du terminal avant le point est right, on fait shift. Ainsi les réduc- tions se feront de droite à gauche. Par exemple si on avait déclaré PLUS comme étant associatif à droite, dans l’exemple précédent 4 + 5 serait réduit avant d’addi- tionner avec 3.

Sinon on fait reduce.

Lorsqu’on a un conflit reduce/reduce, on réduit avec la production ayant la plus grande prior- ité.

Dans les situations où le terminal le plus à droite dans une production ne donne pas la priorité souhaitée, on peut spécifier la bonne priorité en utilisant %prec <terminal>. Ainsi la priorité de la règle sera comme celle du terminal (qui doit dans ce cas être défini dans la section de

(16)

Règles spécifiées explicitement

– Si des terminaux sont déclarés nonassoc, deux occurrences successifs de terminaux de même priorité génèrent une erreur.

– Par exemple, si '= =' est declaré nonassoc une erreur serait générée avec l’entrée 6 = = 7 = = 8 = = 9.

– Tous les terminaux non déclarés dans les spécifications

précédence/associativité ont une priorité inférieure aux autres.

– Les productions sans terminaux ont aussi un priorité moindre inférieure aux autres

– Si un conflit shift/reduce ou reduce/reduce implique de tels terminaux il est reporté.

(17)

Recouvrement d’erreur

– Java CUP utilise un token spécial error pour spécifier des recouvrements d’erreurs.

– Par exemple, on pourrait avoir des productions du genre:

stmt ::= expr SEMI | while_stmt SEMI | if_stmt SEMI | ... | error SEMI ;

– Ceci signifie que si aucune des production normales pour stmt ne corre- spond à l’entrée, une erreur syntaxique devrait être signalée. Le recou- vrement se fera en sautant les tokens erronés (ceci revient à les scanner et les réduire par error) jusqu’au point où l’analyse peut se poursuivre cor- rectement en lisant un point-virgule (SEMI).

Références

Documents relatifs

• Deux états dans d’un AFD M sont équivalents (indistinguable) si et seulement si pour chaque mot u, lorsque une exécution de M sur l’entrée u commence dans l’un ou

- Pour analyser la syntaxe d’une grammaire hors-contexte, nous ajoutons une pile à un automate fini pour obtenir un modèle de programmes plus puissant connu sous le nom de

 Pour ce faire la partie gauche de la production (un non-terminal) est enlevée de la pile et remplacée par la partie droite de la production. (q, ε, A)  (q, a) pour chaque

- On vient de voir qu’en calculant First(S) ou First(XYS) on doit tenir compte des non-terminaux qui pourraient dériver la chaîne vide et de ceux qui pourraient les suivre dans

 Parce que les deux règles ont la même fonction d’analyse (c-à-d., la fonction correspondant au non terminal dans la partie gauche de chaque production).. Si elle partagent le

– Si le symbole est un non terminal, l’attribut pourrait être une donnée calculée en utilisant les actions sémantiques. – Une grammaire avec des attributs est appelée

• Pour aller au-delà, il faut utiliser d’autres outils ou des méthodes adhoc pour évaluer les attributs (avec l’aide

• Pouvoir programmer un analyseur syntaxique récursif pour une grammaire donnée. • Connaître les fondements d’un générateur d’analyseur syntaxique LL tel