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
pytestet vérifiez que les 24 tests passent toujours. - Priorités : les mots-clés doivent garder une priorité
.2pour 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.pypour vérifier la grammaire. - Visualisez : utilisez
scripts/ast_viewer.pypour inspecter l'arbre généré par un programme.