Grammaire Lark

Ce document décrit la grammaire formelle d'AlgoLab telle qu'elle est définie dans src/algolab/grammar.lark. C'est la source de vérité de la syntaxe du langage.

AlgoLab utilise Lark, un parser Python qui supporte les grammaires EBNF. Le parser est en mode LALR avec un lexer contextuel.

Structure d'un programme

programme: preamble DEBUT instructions FIN
preamble: (fonction | declaration)*

Un programme AlgoLab est composé d'un préambule (déclarations de fonctions et de variables, dans n'importe quel ordre), suivi du bloc DEBUT ... FIN contenant les instructions.

Fonctions

fonction: FONCTION IDENTIFIER "(" params? ")" ":" type instructions RETOURNER expression FIN_FONCTION
params: param_group (("," | ";") param_group)*
param_group: identifiers ":" type

Une fonction a un nom, des paramètres optionnels (groupés par type), un type de retour, un corps d'instructions, et une expression de retour. Les groupes de paramètres peuvent être séparés par , ou ;.

Déclarations

declaration: VARIABLE decl_list
decl_list: decl_group (";" decl_group)*
decl_group: identifiers ":" type
identifiers: IDENTIFIER ("," IDENTIFIER)*
type: TYPE array_spec?
array_spec: "[" INT "]"

Les déclarations commencent par VARIABLE, suivies d'un ou plusieurs groupes nom:type séparés par ;. Chaque groupe peut lister plusieurs identifiants séparés par ,. Le type peut être suivi d'une spécification de tableau [taille].

Instructions

instructions: instruction*
instruction: affectation | si | tant_que | pour | lire | ecrire | appel_fonction

Le corps d'un programme (ou d'une fonction/boucle/condition) est une séquence d'instructions. Chaque instruction est l'un des 7 types listés.

Affectation

affectation: assignable ASSIGN expression
assignable: IDENTIFIER | array_access

L'affectation utilise l'opérateur <-. La cible peut être une variable simple ou un élément de tableau.

Structures conditionnelles

si: SI expression_booleenne ALORS instructions
    (SINON_SI expression_booleenne ALORS instructions)*
    (SINON instructions)?
    FIN_SI

Le Si peut être suivi de zéro ou plusieurs SinonSi, et d'un Sinon optionnel. Chaque branche contient un bloc d'instructions.

Boucles

tant_que: TANT_QUE expression_booleenne FAIRE instructions FIN_TANT_QUE

pour: POUR IDENTIFIER DE expression A expression (PAS expression)? FAIRE instructions FIN_POUR

La boucle Pour a une variable de contrôle, une borne de début (De), une borne de fin (A), un pas optionnel (Pas), et un corps. La boucle TantQue a une condition et un corps.

Entrées / Sorties

lire: LIRE lire_args
lire_args: assignable | "(" assignable ")"

ecrire: ECRIRE ecrire_args
ecrire_args: expression ("," expression)* | "(" expression ("," expression)* ")"

Les parenthèses sont optionnelles pour Lire et Ecrire. Ecrire accepte plusieurs expressions séparées par des virgules.

Appels de fonction

appel_fonction: IDENTIFIER "(" arguments? ")"
arguments: expression ("," expression)*

Un appel de fonction peut apparaître comme instruction ou dans une expression.

Expressions

expression: expression_arithmetique | expression_booleenne | chaine

expression_booleenne: expression_arithmetique comparateur expression_arithmetique
                    | expression_booleenne logique expression_booleenne
                    | NON expression_booleenne
                    | booleen

comparateur: COMP
logique: ET | OU

expression_arithmetique: terme ((PLUS | MINUS) terme)*
terme: facteur ((TIMES | DIV | MOD) facteur)*

facteur: nombre | array_access | IDENTIFIER | appel_fonction | "(" expression_arithmetique ")"

La priorité des opérateurs est définie par la structure de la grammaire. De la plus haute à la plus basse : facteur (nombres, variables, parenthèses, appels), puis terme (multiplication, division, modulo), puis expression_arithmetique (addition, soustraction), puis expression_booleenne (comparaisons, logique).

Littéraux

nombre: FLOAT | INT
booleen: VRAI | FAUX
chaine: STRING

Tokens (lexèmes)

Littéraux

INT: /-?[0-9]+/
FLOAT: /-?[0-9]+\.[0-9]+/
IDENTIFIER: /[a-zA-Z_][a-zA-Z0-9_]*/
COMP: /==|!=|<=|>=|<|>/
STRING: (importé de Lark : chaîne entre guillemets doubles avec échappement)

Opérateurs

ASSIGN.3: "<-"          // Priorité 3 (la plus haute)
PLUS: "+"
MINUS: "-"
TIMES: "*"
DIV: "/"
MOD: "%"

L'opérateur <- a une priorité de 3 pour éviter d'être confondu avec la séquence < suivie de -.

Mots-clés

Tous les mots-clés ont une priorité de 2 (.2) pour être reconnus avant les identifiants. Ils sont tous insensibles à la casse (flag /i).

DEBUT.2: /DEBUT/i
FIN.2: /FIN/i
VARIABLE.2: /VARIABLE/i
SI.2: /SI/i
ALORS.2: /ALORS/i
SINON.2: /SINON/i
SINON_SI.2: /SINON_?SI/i         // accepte SinonSi et Sinon_Si
FIN_SI.2: /FIN_?SI/i             // accepte FinSi et Fin_Si
TANT_QUE.2: /TANT_?QUE/i        // accepte TantQue et Tant_Que
FAIRE.2: /FAIRE/i
FIN_TANT_QUE.2: /FIN_?TANT_?QUE/i
POUR.2: /POUR/i
DE.2: /DE/i
A.2: /A/i
PAS.2: /PAS/i
FIN_POUR.2: /FIN_?POUR/i
LIRE.2: /LIRE/i
ECRIRE.2: /ECRIRE/i
NON.2: /NON/i
ET.2: /ET/i
OU.2: /OU/i
VRAI.2: /VRAI/i
FAUX.2: /FAUX/i
TYPE.2: /ENTIER|REEL|CARACTERE|BOOLEEN|CHAINE/i
FONCTION.2: /FONCTION/i
FIN_FONCTION.2: /FIN_?FONCTION/i
RETOURNER.2: /RETOURNER/i

Espaces blancs

%import common.ESCAPED_STRING -> STRING
%import common.WS
%ignore WS

Les espaces, tabulations et sauts de ligne sont ignorés. AlgoLab est un langage "free-form" : l'indentation n'est pas significative.

Notes pour les contributeurs

Si vous modifiez la grammaire, gardez en tête ces principes :

  • Testez : lancez pytest et vérifiez que les 24 tests passent toujours.
  • Priorités : les mots-clés doivent garder une priorité .2 pour ne pas être confondus avec des identifiants.
  • Compatibilité : les variantes avec/sans underscore (FinSi / Fin_Si) doivent rester supportées.
  • Insensibilité à la casse : tout nouveau mot-clé doit utiliser le flag /i.
  • Validez : utilisez scripts/validate_grammar.py pour vérifier la grammaire.
  • Visualisez : utilisez scripts/ast_viewer.py pour inspecter l'arbre généré par un programme.