:: Enseignements :: Master :: M2 :: 2011-2012 :: Machine Virtuelle (et bazard autour ...) ::
[LOGO]

Quelques notes sur la grammaire du language Foo


Avant propos

Le language Foo n'est pas un vrai langage, mais à un language simple avec les élements communs avec les langages Python, Ruby ou Javascript et une syntax proche du C.
Il n'a donc aucune autre prétention que de calculer des valeurs de Fibonacci.

La grammaire

La grammaire du langage définie plus de constructions syntaxiques que nécessaire pour réaliser les premiers labs. Ici, seuls les productions pour ces premiers labs sont décrites.
La description de la grammaire est sous la forme d'une EBNF. Les terminaux sont entre quote ('id' est un terminal) et le non-terminaux non pas de quote (expr est un non-terminal). De plus, foo? veut dire que, foo est optionel. foo+ indique qu'il peut y avoir un ou plusieurs foo répétés, foo* indique qu'il peut y avoir zéro ou plusieurs foo répétés. Enfin il existe une construction qui permet de définir lors d'une répétition un terminal de séparation, foo/'bar'+ veut dire une serie de foo séparés par des 'bar'. Les noms entre '{' et '}' corresponde au nom des productions que l'on retrouvera dans le code des visiteurs.

Un script

Un script est composé de plusieurs fonctions et d'un block de code qui est le code à exécuter au début du script, une sorte de main.
Une fonction se déclare comme en C mais avec le mot clé 'fun' devant et les types des paramétres et le type de retour sont optionnels.
Les instructions d'une fonction sont définies dans un block c'est à dire entre '{' et '}'.

 script = member* block                                       { script }
       ;        
       
 member = func                                                { member_func }
        | ...
        ;

 func = 'fun' 'type'? 'id' '(' parameter/'comma'* ')' block   { func }
      ; 
          
 parameter = 'type'? 'id'                                     { parameter }
           ; 
      
 block = '{' instr* '}'                                       { block }
       ; 

Instructions

Le langage foo fait la distinction entre les instructions et les expressions, les expressions ayant une valeur, et donc repésentent un calcul alors que les instructions n'ont pas de valeur et représentent plus une commande. Contrairement au C et comme en Javascript, les instructions peuvent se terminer par un retour à la ligne à la place d'un point virgule ';' avec les mêmes rêgles qu'en Javascript (Automatic Semicolon Insertion ou ASI).

Pour des questions de clareté, les instructions ont été découpé en plusieurs sections.

Declaration et assignation

Les variables sont déclarés avec le mot-clé var comme en Javascript mais avec la porté d'une déclaration en C (la portée des variables en Javascript est bizarre). Contrairement au C, mais comme en C++ ou en Java, la déclaration de variable est une instruction et donc peut se retrouver au milieu d'autres instructions. Il n'est pas possible de déclarer une variable sans expression d'initialisation, cela évite de se demander si les variables sont initilialisés ou non avant leur utilisation qui est une contrainte requise par le bytecode Java.
Contrairement au C, l'assignation est une instruction, donc il ne peut pas y avoir plusieurs assignations pour une instruction.

 instr = decl eoln                     { instr_declaration }
       | assignment eoln               { instr_assignment }
       | ...

 decl = 'var' 'type'? 'id' '=' expr    { decl }
      ;

 assignment = 'id' '=' expr            { assignment }
            ;

Boucle

Il n'y a qu'une seul syntaxe pour les boucles, la boucle for. Comme en C, les instructions break et continue existent et peuvent avoir un label.
Comme en C++ ou en Java, il est possible de déclarer une variable dans la partie initialisation du for.

 instr = ...
       | label? 'for' '(' forinit? ';' expr? ';' forupdate? ')' instr  { instr_for } 
       | 'break' 'id'?  eoln                                           { instr_break }
       | 'continue' 'id'?  eoln                                        { instr_continue } 
       | ... 
       
 forinit = decl           { forinit_decl }
         | assignment     { forinit_assignment }
         ;
         
 forupdate = assignment   { forupdate_assignment }
           | expr         { forupdate_expr }
           ;
 
 label = 'id' ':'         { label }
       ;

Appel de fonction

Un appel de fonction est une instruction et une expression et les arguments qui sont des expressions sont séparés par des virgules.

 instr = ...
       | funcall eoln                  { instr_funcall }
       | ...

 primary = ...
         | funcall                     { primary_funcall }
         | ...
      
 funcall =  'id' '(' arg/','* ')'      { funcall }
         ; 
         
 arg = expr                            { arg_expr } 
     | ...     
     ;

Autres instructions

La grammaire définie aussi les instructions if et if/else ainsi que l'instruction return.

 instr = decl eoln                                                     { instr_declaration }
       | assignment eoln                                               { instr_assignment }
       | funcall eoln                                                  { instr_funcall }
       | block                                                         { instr_block }
       | 'if' '(' expr ')' instr               [else]                  { instr_if }
       | 'if' '(' expr ')' instr 'else' instr                          { instr_if_else }
       | label? 'for' '(' forinit? ';' expr? ';' forupdate? ')' instr  { instr_for } 
       | 'return' expr? eoln                                           { instr_return }
       | 'break' 'id'?  eoln                                           { instr_break }
       | 'continue' 'id'?  eoln                                        { instr_continue } 
       ;  

Les expressions

Les expressions sont soit des constantes, des variables ou des combinaisons utilisant les opéraeurs binaires +, -, *, /, %, ==, !=, <, <=, >, >=, && et || avec leur précédence habituelle. Les constantes sont true, false, des entiers sur 32 bits, des valeurs flottantes 64bits (IEEE 764), des chaines de caractères séparé par des " " et null.

      
 primary = constant                   { primary_constant }
      | 'id'                          { primary_id }
      | '(' expr ')'                  { primary_parens }
      | funcall                       { primary_funcall }
      ;
      
 expr = primary                                { expr_primary }
      | expr '+' expr              [plus]      { expr_add }
      | expr '-' expr              [plus]      { expr_sub }
      | expr '*' expr              [star]      { expr_mul }
      | expr '/' expr              [star]      { expr_div }
      | expr '%' expr              [star]      { expr_mod }
      | expr '==' expr             [eq]        { expr_eq }
      | expr '!=' expr             [eq]        { expr_neq }
      | expr '<' expr              [eq]        { expr_lt }
      | expr '<=' expr             [eq]        { expr_le }
      | expr '>' expr              [eq]        { expr_gt }
      | expr '>=' expr             [eq]        { expr_ge }
      | expr '&&' expr             [and]       { expr_and }
      | expr '||' expr             [and]       { expr_or }
      ;
      
 constant = 'boolean_cst'          { constant_boolean }
          | 'integer_cst'          { constant_integer }
          | 'double_cst'           { constant_double }
          | 'string_cst'           { constant_string }
          | 'null'                 { constant_null }
          ;