Glossaire Catnip
Ce document définit les concepts clés et la terminologie utilisée dans le projet Catnip, afin d'éviter les ambiguïtés dans la documentation.
Architecture et Composants
HostApp (Application Hôte)
L'application hôte est le programme Python qui intègre et utilise Catnip comme langage embarqué. Elle fournit:
- Le contexte d'exécution
- Les fonctions et objets Python accessibles depuis Catnip
- L'environnement d'intégration
Exemple: Une application web Flask qui utilise Catnip pour les scripts de configuration utilisateur.
Runtime
L'ensemble des composants nécessaires à l'exécution du code Catnip:
- Parser
- Semantic analyzer
- Registry
- Executor
- Context
Registry
Le registre est le composant central qui:
- Gère le mapping entre opérations (opcodes) et leurs implémentations
- Maintient le contexte d'exécution (scopes, variables)
- Fournit l'interface entre l'AST et les opérations concrètes
Modules: registry.py, core/registry_core.pyx
Context
Le contexte d'exécution contient:
- Les scopes (portées) avec leurs variables locales/globales
- Le résultat de la dernière expression
- L'accès aux builtins Python
- L'état d'exécution (pile de scopes)
Fichier: catnip/context.py
Phases d'Exécution
Parsing
Transformation du code source Catnip en Abstract Syntax Tree (AST) via Lark.
Parser utilisé : Earley (gère les ambiguïtés, flexible mais lent)
Composants:
parser.py: Classe Parser basée sur Larkgrammar.lark: Grammaire du langagetransformer/: Transformation de l'arbre Lark en IR
Performance : Le parsing Earley est relativement lent. Le cache est essentiel en production.
Semantic Analysis
Transformation de l'AST en Op nodes (opérations exécutables):
- Résolution des références
- Optimisation (constant folding, etc.)
- Détection des tail-calls
- Application des pragmas
Module: catnip/semantic/analyzer.py
Execution
Exécution des Op nodes via le Registry, par interprétation directe de l'arbre.
Module: executor.py
OpCode
Code d'opération numérique (enum) utilisé pour identifier les opérations de manière efficace.
Avantages : - Comparaisons O(1) (entiers vs strings) - Lookups rapides dans les dictionnaires - Moins de mémoire - Unifié : IR et Op utilisent le même enum
Exemples :
OpCode.ADD = 1
OpCode.SUB = 2
OpCode.OP_IF = 10 # Préfixe OP_ pour mots-clés Cython
OpCode.OP_WHILE = 11
Convention : Les opcodes correspondant à des mots-clés Cython (if, while, for, etc.) sont préfixés par OP_.
Module : catnip/semantic/opcode.py
Trampoline
Technique d'optimisation TCO qui transforme les appels récursifs en une boucle avec rebinding de paramètres.
Principe : Au lieu d'empiler des frames, la fonction retourne un signal TailCall qui indique au trampoline de rebinder les paramètres et recommencer.
Résultat : O(1) stack space au lieu de O(n).
La fonction rebondit sur elle-même sans jamais vraiment s'appeler.
Structures de Données
IR (Intermediate Representation)
Nœud intermédiaire produit par le transformer:
IR(ident=OpCode.ADD, args=(left, right), kwargs={})
Op (Operation)
Nœud exécutable produit par l'analyse sémantique:
Op(ident=OpCode.ADD, args=(left, right), kwargs={})
Points communs : IR et Op utilisent le même enum OpCode pour identifier les opérations.
Différence : IR est la sortie brute du parser (avant optimisation), Op est optimisé et annoté (tail calls, etc.).
Scope (Portée)
Une portée est un dictionnaire de symboles (variables) avec un parent optionnel:
- Global scope: Portée racine
- Local scope: Portée créée par fonction/lambda/bloc
- Lookup: Recherche en cascade (local → parent → global)
Module: core/scope.pyx (optimisé Cython)
Node
Classes pour les objets Catnip exécutables:
Op: Nœud d'opération (cdef class Cython)IR: Nœud intermédiaire (hérite de Op)Function: Fonction nommée (cdef class Cython)Lambda: Fonction anonyme (cdef class Cython)Pattern*: Patterns de pattern matching
Module: core/op_core.pyx (Op), core/nodes_core.pyx (Function, Lambda), nodes.py (réexports et autres)
Optimisations
TCO (Tail-Call Optimization)
Optimisation des appels terminaux: Les fonctions qui se terminent par un appel récursif sont optimisées pour ne pas consommer de stack Python.
Implémentation: Trampoline pattern avec détection de self-recursion.
Pragma: pragma("tco", True) / pragma("tco", False)
Constant Folding
Résolution anticipée: Les expressions constantes sont évaluées au moment de l'analyse sémantique plutôt qu'à l'exécution.
Exemple: 2 + 3 * 4 → 14 (au parse-time)
Cython Optimization
Modules critiques implémentés en Cython pour performance maximale:
core/: Modules core (scope, registry, nodes, broadcast)transformer/: Transformation parse tree → IRsemantic/: Analyse sémantique et optimisations
Strength Reduction
Réduction de force : Remplacement d'opérations coûteuses par des équivalentes moins coûteuses.
Exemple : x ** 2 → x * x (multiplication plus rapide que puissance)
Dead / Blunt Code Elimination
Élimination de code mort : Suppression du code inaccessible ou jamais exécuté.
Exemple : if (true) { a } else { b } → { a } (branche else supprimée)
Pragmas
Pragma
Directive de compilation qui contrôle le comportement du compilateur/runtime.
Syntaxe: pragma("directive", value) où value peut être un booléen, string ou nombre
Directives supportées:
tco: Tail-call optimization (True/False)optimize: Niveau d'optimisation (0-3 ou "none"/"full")cache: Cache de parsing (True/False)debug: Mode debug (True/False)feature: Charger un module Catnip ou Python (pur ou binaire) par nom ou chemin (pragma("feature", "module", "alias"))
Exemples:
pragma("tco", True)
pragma("optimize", "3")
pragma("feature", "json")
pragma("feature", "pandas", "pd")
pragma("feature", "./toolbox.cat", "tools")
Module: pragma.py
Pattern Matching
Pattern
Structure utilisée pour le matching dans match/case:
- PatternLiteral: Match une valeur exacte (
1,"text") - PatternVar: Capture la valeur dans une variable (
x) - PatternWildcard: Match tout (
_) - PatternOr: Match une alternative (
a | b | c)
Guard
Condition ajoutée à un pattern: n if n > 10
Fonctions et Lambdas
Function (Fonction Nommée)
Fonction définie avec un nom:
factorial = (n) => { … }
Lambda (Fonction Anonyme)
Fonction sans nom, souvent inline:
numbers.[() => { x * 2 }]
Variadic Parameters
Paramètres qui acceptent un nombre variable d'arguments:
sum = (*args) => { … }
Syntaxe: Préfixe * devant le nom du paramètre.
Closure (Fermeture)
Fonction qui capture les variables de son scope parent.
Exemple :
make_adder = (n) => {
(x) => { x + n } # Capture 'n' du scope parent
}
add5 = make_adder(5)
add5(10) # → 15
Unpacking (Destructuring)
Décomposition d'une structure de données en plusieurs variables.
Syntaxe :
(a, b) = tuple(1, 2)
(first, *rest) = list(1, 2, 3, 4, 5)
(x, (y, z)) = list(1, tuple(2, 3))
Support : Tuples, listes, imbrication, star operator (*)
Broadcasting
Broadcasting Operation
Application d'une opération, étendue naturellement à toutes les dimensions internes.
La même expression fonctionne quel que soit le "niveau" : scalaire, liste, matrice, colonne, tenseur, ou structure hôte arbitraire.
Syntaxe: collection.[operation]
Types:
- Binaire:
.[+ 10],.[* 2] - Unaire:
.[abs],.[not] - Fonction:
.[(x) => { x ** 2 }] - Filtre:
.[(x) => { x % 2 == 0 }]
Catnip applique toujours l’opération "au bon endroit"”", même quand la dimension n’est pas connue à l’avance.
Termes Généraux
Builtin
Fonction ou objet Python accessible depuis Catnip sans import.
Exemples: range, list, dict
Pure Function
Fonction sans effets de bord qui retourne toujours le même résultat pour les mêmes arguments.
Utilisé pour: Optimisations de broadcasting, mémoization.
Décorateur : @pure en Python
Tail Position
Une expression est en position terminale si sa valeur est immédiatement retournée sans autre calcul.
Important pour: TCO (Tail-Call Optimization)
Cache
Système de mémorisation pour éviter de re-parser ou re-compiler du code déjà traité.
Types :
- Parse cache : Cache des AST parsés (évite le parsing)
- Compilation cache : Cache des Op nodes compilés
- Function cache : Mémoization des résultats de fonctions pures
Backends : Mémoire, disque, Redis
Activation :
catnip = Catnip(enable_cache=True)
Pragma :
pragma("cache", True)
Module : cachesys.py
Sandbox (Bac à sable)
Environnement d'exécution isolé qui limite les actions possibles du code.
Objectif : Permettre l'exécution de code utilisateur sans risque pour le système hôte.
Catnip peut s'exécuter dans un contexte restreint où seules certaines fonctions Python sont accessibles.
Fichiers et Structure
.cat
Extension de fichier pour les scripts Catnip.
Interfaces et Outils
REPL (Read-Eval-Print Loop)
Mode interactif de Catnip accessible via catnip sans arguments.
Fonctionnalités :
- Lecture d'une expression utilisateur
- Évaluation immédiate
- Affichage du résultat
- Contexte persistant entre les commandes
Exemple :
$ catnip
🌿> x = 10
🌿> x * 2
20
🌿> factorial = (n) => { if (n <= 1) { 1 } else { n * factorial(n-1) } }
🌿> factorial(5)
120
Fichier : __main__.py
CLI (Command Line Interface)
Interface en ligne de commande pour exécuter Catnip.
Modes d'exécution :
- Script :
catnip script.cat(fallback automatique) - Script explicite :
catnip -- script.cat(avec séparateur) - Commande :
catnip -c "expression" - Pipe :
echo "2 + 3" | catnip - REPL :
catnip(interactif, défaut) - Sous-commandes :
catnip format script.cat,catnip check script.cat(futur)
Options :
-c, --command: Exécute une commande unique-p, --parsing: Niveau de parsing (0-3)-o, --optimize: Options d'optimisation (ex:tco:on)-l, --load: Charge un module Python--as: Nom personnalisé pour le module--inject: Injecte les fonctions directement dans globals
Exemples :
catnip script.cat # Exécute (fallback)
catnip -- script.cat # Exécute (explicite)
catnip -c "2 + 3" # Évalue
catnip -o tco:on script.cat # Avec TCO
echo "10 * 2" | catnip # Depuis stdin
catnip format script.cat # Sous-commande (futur)
Séparateur -- : Force l'interprétation de l'argument suivant comme un fichier, levant toute ambiguïté avec les sous-commandes.
Fichier : __main__.py
JIT (Just-In-Time Compilation)
Compilation à la volée du code Catnip (fonctionnalité expérimentale).
État actuel : Partiellement implémenté, pas encore de gestion de cache intégrée.
Activation :
ctx.jit_enabled = True
Pragma :
pragma("jit", True)
Objectif : Compiler les fonctions fréquemment appelées en bytecode optimisé pour améliorer les performances d'exécution.
Le JIT détecte les hot paths et les compile. Actuellement, il ne cache pas encore les compilations, ce qui limite son efficacité.
Acronymes
- AST: Abstract Syntax Tree (Arbre Syntaxique Abstrait)
- IR: Intermediate Representation (Représentation Intermédiaire)
- TCO: Tail-Call Optimization (Optimisation des Appels Terminaux)
- VM: Virtual Machine (Machine Virtuelle)
- REPL: Read-Eval-Print Loop (Boucle Lecture-Évaluation-Affichage)
- CLI: Command Line Interface (Interface en Ligne de Commande)
- JIT: Just-In-Time Compilation (Compilation à la Volée)
- Op: Operation (Opération exécutable)
- CSE: Common Subexpression Elimination (Élimination des Sous-Expressions Communes)
Conventions de Nommage
Fonctions Internes
- Préfixe
_: Fonction privée (ex:_execute()) - Préfixe
visit_: Méthode de visitor pattern (ex:visit_if()) - Préfixe
exec_: Méthode d'exécution (ex:exec_stmt())
Modules Cython
- Suffixe
_core: Module core optimisé (ex:nodes_core.pyx) - Suffixe
_opt: Module optimisé (ex:semantic_opt/)
Classes
- PascalCase:
PatternLiteral,Function,Lambda - Suffixe
Mixin: Classe mixin (ex:ControlFlowMixin)
Relations Entre Concepts
HostApp
└─> Runtime
├─> Parser
│ └─> IR(OpCode) - sortie du transformer
├─> Optimizer
│ └─> IR optimisé
├─> Semantic Analyzer
│ └─> Op(OpCode) - nœuds exécutables
└─> Executor
├─> Registry (résolution des opérations)
└─> Context (scopes, variables, fonctions, JIT, cache)
Exemples d'Usage
Intégration dans une HostApp
from catnip import Catnip
# Créer l'interpréteur
cat = Catnip()
# Injecter des objets Python
cat.context.locals['db'] = my_database
cat.context.locals['config'] = app_config
# Exécuter du code Catnip
result = cat.parse('db.query("SELECT * FROM users")').execute()
Pragma
pragma("tco", True)
pragma("optimize", "3")
pragma("feature", "json")
factorial = (n, acc=1) => {
if n <= 1 { acc } else { factorial(n - 1, n * acc) }
}
# Utiliser le module chargé
data = dict(("x", 10))
json_str = json.dumps(data)
Broadcasting
numbers = list(1, 2, 3, 4, 5)
doubled = numbers.[* 2] # [2, 4, 6, 8, 10]
squares = numbers.[(x) => { x ** 2 }] # [1, 4, 9, 16, 25]