Documentation Technique : Grammaire & Parseur SoCQ Julien Deflaux 1 er août 2008 1
Table des matières 1 Introduction 3 2 Grammaire SoCQ 4 2.1 Description des données..................... 4 2.2 Étendue de la grammaire.................... 4 2.3 Grammaire SoCQ (Oracle like)................. 4 3 Parseur SoCQ 11 3.1 Présentation de JavaCC..................... 11 3.2 ParseTree............................. 11 3.3 SemanticVisitor.......................... 12 3.4 La classe Parser......................... 13 3.5 ParseException & SemanticException............. 14 4 FAQ 14 4.1 Comment installer javacc?................... 14 4.2 Comment utiliser le parseur?.................. 15 4.3 Comment enlever les erreurs dans le package fr.cnrs.liris.socq.model.extended.parser?............ 15 4.4 Limitations connues....................... 15 2
1 Introduction Ce document permet de présenter la grammaire SoCQ ainsi que le parseur java qui permet de créer les objets adéquats et vérifier la syntaxe et la sémantique de requêtes issues de cette grammaire. La sémantique concernant la structure des données sort du cadre de ce document. Il sera juste question de l utiliser. Ce document présente en premier lieu la grammaire SoCQ c est-à-dire fournit un document concernant sa description complète comme utilisée dans le projet SoCQ GUI. La description de la grammaire a été basée sur une représentation proche de la grammaire Oracle. En second lieu le lien est fait entre cette grammaire et la façon de l utiliser de la parser. La finalité du parseur était d être intégré au reste du projet SoCQ initialement développé en JAVA sous forme de plugin Eclipse RCP. Il a donc été choisi d utiliser l outil javacc fourni par la communauté Eclipse pour créer le parseur simplement. Le parseur permet de vérifier la syntaxe et la sémantique d une requête et de créer à partir de cette requête les objets adéquats. 3
2 Grammaire SoCQ 2.1 Description des données La grammaire SoCQ permet de représenter et manipuler différents types de données. Les modèles à manipuler sont les suivants : Table SchemaSoCQ BindingPattern Attribute Pour une description de ces données au sens JAVA on pourra se référer au package fr.cnrs.liris.socq.model.extended.data. 2.2 Étendue de la grammaire La grammaire qui permet de manipuler ces données se décompose en trois parties : DDLStatement : permet de définir et décrire les données. DMLStatement : permet de d ajouter des tuples à une Table. SFW Select From Where : permet de sélectionner des tuples à l aide de requêtes proches du SQL. 2.3 Grammaire SoCQ (Oracle like) Figure 1 Légende de la grammaire SoCQ 4
Query Query DDL Statement DML Statement SFW Query DDL Statement DDLStatement BindingPatterns CREATE SchemaSoCQ ; DDL Statement : SchemaSoCQ SchemaSoCQ RELATION STREAM Name ( Attributes ) Attributes Attribute 5
Attribute VIRTUAL Name AttributeType Boolean Attributetype Real String Service Integer Binary Char DDL Statement : BindingPatterns BindingPatterns USING ( BindingPattern ) BINDING PATTERN BindingPattern Name [ ServiceID ] ( ) : ( OutputParams ) InputParams InputParams & OutputParams ParamName 6
SFW : Select From Where SFW SELECT SelList FROM FromList ; WHERE ConditionList INVOKING BPList SFW : Select List SelList SFWAttribute Joker SFWAttribute Name Alias. SFW : From List FromList DataSource DataSource Name Alias [ Window ] 7
Window Integer SFW : Condition List ConditionList OpCondition Condition OpCondition AND Condition AttCondition Operator AttCondition AttCondition String SFWAttribute Integer Character IS Operator = < > 8
SFW : BindingPatterns List BPList SFWBindingPattern SFWBindingPattern Name Alias. DML Statement DML Statement ( ) INSERT INTO TableName VALUES Values ; ( Columns ) DML : Columns Columns Columns Column Identifier 9
DML : Values Values Value Joker Value String Real Integer Shared Management Integer Digit Joker * Letter _ Digit a-z 0-9 A-Z Identifier Name & ParamName & Alias & ServiceID Letter Digit Letter 10
3 Parseur SoCQ Le parseur a été développé en JAVA pour pouvoir être intégré au reste du projet SoCQ. Il est contenu dans le projet ExtendedData. Tout ce qui est relatif au parseur a pour racine de packages : fr.cnrs.liris.socq.model.extended.parser Les seules classes nécessaires à son utilisation se trouvent dans ce package. Il n est pas nécessaire d utiliser ses sous-packages ils sont là pour implémenter le parseur. 3.1 Présentation de JavaCC Le parseur de requêtes est développé à l aide du plugin Eclipse javacc 1. Toutes les classes liées au parseur javacc sont dans le package : fr.cnrs.liris.socq.model.extended.parser.queryparser javacc s occupe de générer les différentes classes dont il a besoin le seul fichier a manipuler est QueryParser.jtb. Ce plugin permet de définir la grammaire à parser de manière analogue à ce qui est fait dans la section 2. Par exemple pour définir la grammaire concernant une requête ( SFW cf section 2) le code est le suivant : void SFWQuery():{} { <SELECT> SelList() <FROM> FromList() [ <WHERE> ConditionList() ] [ <INVOKING> BPList() ] ";" } De plus on peut définir une liste des expression qui ne seront pas parsées ( bloc SKIP du code ) ainsi que la façon dont sont interprétées chaque expressions ( appelées TOKEN ) de la requêtes. Pour lancer la vérification à l aide du parseur QueryParser il faut lancer la méthode parse(string toread). Celle-ci renvoie une ParseException si une erreur sur la grammaire est trouvée sur la chaîne toread analysée et sinon stocke localement le ParseTree généré automatiquement par javacc. Pour une documentation complète sur javacc on se référera à sa documentation en ligne[1]. 3.2 ParseTree Le QueryParser fait plusieurs actions : 1. javacc WebSite : https://javacc.dev.java.net/ 11
Il vérifie la syntaxe d une String et renvoie une ParseException en cas d erreur. Il renvoie ensuite un ParseTree ( arbre syntaxique ) qui contient la requête sous forme d arbre hiérarchique ( cf figure2). Figure 2 Exemple de ParseTree Chaque noeud de l arbre correspond à une classe générée par javacc et portant le nom définit dans la grammaire. Par exemple on pourra retrouver une classe SFWQuery une classe SelList etc... Toutes ces classes sont dans le package : fr.cnrs.liris.socq.model.extended.parser.queryparser.syntaxtree 3.3 SemanticVisitor javacc propose plusieurs façons de parcourir une String en fonction de l extension du QueryParser dans tous les cas la grammaire est vérifiée et peut renvoyer une ParseException :.jj : Le parsetree n est pas généré automatiquement mais chacune des Expresison peut renvoyer un objet java ( au lieu de renvoyer void comme dans l exemple ). Cet avantage est devenu très vite un inconvénient pour le parseur SoCQ car cela complique le code car il faut vérifier la sémantique en même temps et cela rend moins maintenable celui-ci..jtb : Permet de créer un ParseTree automatiquement. Par contre aucune expression ne peut renvoyer d objet JAVA. Il faut ensuite vérifier la sémantique du ParseTree en le parcourant à l aide des visiteurs qui héritent de classes générées par javacc..jjt : Identique aux jtb sauf que l utilisation des visiteurs est plus complexe car il faut redéfinir toutes leurs méthodes. Le QueryParser est donc un fichier.jtb. Le ParseTree est parcouru par des SoCQVisitors qui vérifient la sémantique et créent les objets correspondants à la requête. En cas d erreur de sémantique ils renvoient une SemanticException. 12
Les SoCQVisitors sont dans le package : fr.cnrs.liris.socq.model.extended.parser.queryparser.socqvisitor On est obligé d implémenter les visiteurs dont le fonctionnement est le suivant : Le Visitor est lancé pour visiter un noeud. La méthode visit fait accepter au noeud d être parcouru par ce Visitor. Le noeud est parcouru et appelle la méthode visit du Visitor sur chacun de ses fils. Cependant au final il ne faut implémenter que les Visitor. On ne peut redéfinir les Noeuds il faut donc ne redéfinir que les méthodes visit du Visitor dont le noeud nous intéresse pour la vérification sémantique. Ces méthodes sont du type : public void visit(tablename node) { this.tablenode = node.f0.f0; } TableName correspond au nom du noeud à parcourir. On peut donc ainsi récupérer le tablenode de cette requête grâce à cette méthode. Chacun des SoCQVisitor contient une méthode public excecute() qui permet de lancer la vérification sémantique et de créer les objets voulus. Une approfondissement n est pas nécessaire dans ce document il s agit d algorithmie. 3.4 La classe Parser La classe Parser permet de regrouper le QueryParser et les SoCQVisitor dans une seule et même classe. Cela permet de n appeler qu une seule méthode parse(string toread) qui permet de faire la vérification syntaxique sémantique et construire les objets requis par la requête. Ces objets sont ensuite accessibles via diverses méthodes. Voici une explication des différentes méthodes : isstringvalid(string toread) : appelle le QueryParser et lui fait vérifier si toread est bien une String au sens SoCQ(voir section 2 page4). isidentifiervalid(string toread) : appelle le QueryParser et lui fait vérifier si toread est bien une Identifier au sens SoCQ. parse(string toread) : permet de vérifier uniquement la syntaxe d une requête. Appelle la méthode parse(toreadnull). parse(string toread Vector<Table> localtables) permet de parser une requête et d en vérifier la syntaxe et la sémantique. La sémantique est vérifiée si localtables n est pas null. C est cette méthode qui se charge d appeler le parseur et d appeler en fonction du type de 13
requêtes rencontrées le bon SoCQVisitor. L attribut localtables correspond aux tables déjà présentes sur le Scheme et dont il faut se baser pour vérifier la sémantique. A chaque rencontre d une requête de DDL la nouvelle Table est ajoutée à cette liste. Il faut que cette méthode ai été exécutée pour qu une des méthodes suivantes soit significative. hasddlstatement() Retourne un booléen qui indique si la requête contient une ou plusieurs requêtes de DDL. getddlstatements() permet de récupérer le vecteur de tables associé à la requête. ( Vérifier avec hasddlstatement() si ce vecteur existe). hasdmlstatement Retourne un booléen qui indique si la requête contient une ou plusieurs requêtes de DML. getdmlstatement() permet de récupérer un vecteur de tuples listant tous les tuples de la requête dans l ordre rencontré dans la requête. getdmlpositions() Retourne un vecteur d Integer où chaque entier correspond à la position du DMLStatement dans le vecteur de tables obtenu via DDLStatement(). Les positions sont rentrées dans l ordre d apparition dans la requête et permettent de définir à quelle table appartient chaque Tuple obtenu grâce à getdmlstatement(). Cette classe Parser émet des ParseException ou SemanticException en fonction du type d erreurs rencontrées. 3.5 ParseException & SemanticException Ces exceptions sont rencontrées dans le Parser. Ces classes sont présentes dans le package : fr.cnrs.liris.socq.model.extended.parser Elles héritent de la classe Exception. La classe ParseException ne récupère que le message des ParseException rencontrées dans le Parser. La ParseException du QueryParser es une classe différentes qui contient des informations non utiles hors du contexte du QueryParser. La classe SemanticException prend en paramètre d entrée un NodeToken ( c est à dire un noeud du ParseTree ) et un message optionnel. Le message d erreur est alors formatté et sera affiché par sa méthode tostring(). 4 FAQ 4.1 Comment installer javacc? Pout installer le plugin javacc on se réferera à la documentation en ligne sur javacc : https://javacc.dev.java.net/ 14
4.2 Comment utiliser le parseur? Il suffit d avoir accès uniquement au package : fr.cnrs.liris.socq.model.extended.parser La classe Parser permet d utiliser pleinement le parseur ( cf section 3 page 11). Les deux types d exceptions que peut renvoyer un parsing sont ParseException et SemanticException contenues dans ce package (cf. section 3.5 page 14). 4.3 Comment enlever les erreurs dans le package fr.cnrs.liris.socq.model.extended.parser? javacc construit des fichiers qui ont comme nom de package.syntaxtree.* ou.visitors.* par défaut. IL faut lui spécifier où créer ces packages. Pour cela : Cliquer sur le projet et choisir dans le menu Properties Aller dans la catégorie "Options javacc" Aller dans "Options JTB" Dans l option JTB_OPTIONS ( packages par défaut ) mettre la valeur : "-p=fr.cnrs.liris.socq.model.extended.parser.queryparser". 4.4 Limitations connues Il n est pas possible de modifier/ supprimer des requêtes via le parseur Il faut gérer la transformation du ParseTree d une RequêteSFW dans le SemanticSFWVisitor pour la transformer en requête algébrique. Il faut gérer les Real : actuellement un nombre du type 3.3 n est pas reconnu comme Real. Références [1] javacc documentation 2007. https://javacc.dev.java.net/doc/ docindex.html. 15