Constantes
Sommaire
- Pourquoi ce fichier ?
- Sources
- Sections
- REPL - Prompts (visual.toml)
- REPL - Couleurs (visual.toml)
- REPL - Messages
- REPL - Historique
- JIT - Configuration
- ND Recursion
- Broadcast
- Parsing
- Repr
- JIT - Pure Builtins
- VM - Configuration
- Weird Log
- Format - Defaults
- JIT - Inlining
- Benchmark
- Cache - Configuration
- Optimization - Niveaux
- Env vars, bit-packing et chemins XDG
- Python Module Paths
- Config Keys
- Config Validation
- Error Messages
- Boolean Parsing
- Architecture
- Utilisation
- Modification
Le fichier catnip_core/src/constants.rs centralise toutes les constantes par défaut du runtime Catnip en Rust.
Pourquoi ce fichier ?
Plutôt que d'avoir des valeurs magiques dispersées dans différents modules, tout est ici :
- Facile à trouver - un seul endroit pour tous les defaults
- Facile à modifier - changer un seuil, un prompt, un message
- Facile à maintenir - pas de duplication, source unique de vérité
Sources
Les constantes proviennent de deux sources :
constants.rs- Valeurs non-visuelles (messages, seuils, tailles)visual.toml- Couleurs et prompts (OKLCH), injectées viabuild.rsdanstheme_generated.rs
Les constantes non-visuelles vivent dans catnip_core (pure Rust, sans PyO3). Les constantes visuelles sont générées
par build.rs dans catnip_rs. Le fichier catnip_rs/src/constants.rs réexporte tout via
pub use catnip_core::constants::* + include!(), donc tous les modules accèdent à tout via crate::constants::*.
Sections
REPL - Prompts (visual.toml)
[prompts]
main = "▸ "
continuation = "▹ "
Définis dans visual.toml, convertis en constantes Rust par build.rs.
REPL - Couleurs (visual.toml)
[ui]
prompt = "oklch(0.6125 0.1566 191.6)"
error = "oklch(0.592 0.1973 28.59)"
info = "oklch(0.647 0.179 258.35)"
success = "oklch(0.662 0.2318 149.1)"
dim = "oklch(0.722 0 0)"
Toutes les couleurs sont en OKLCH dans visual.toml. Le build génère les codes ANSI correspondants. Deux palettes
(accent.dark / accent.light, base.dark / base.light) pour le syntax highlighting.
REPL - Messages
pub const REPL_WELCOME_TEMPLATE: &str = "Catnip REPL v{version}\nType /help for help, /exit to quit\n";
pub const REPL_EXIT_OK: &[&str] = &["state resolved.", "collapse complete.", ...];
pub const REPL_EXIT_ABORT: &[&str] = &["context destroyed.", "vm is dead.", ...];
Deux pools de messages de sortie selon le contexte (exit normal, abort). Un message est choisi au hasard à chaque fermeture.
REPL - Historique
pub const REPL_HISTORY_FILE: &str = "repl_history";
pub const REPL_MAX_HISTORY: usize = 1000;
JIT - Configuration
pub const JIT_DEFAULT_STANDALONE: bool = true;
pub const JIT_DEFAULT_PIPELINE: bool = false;
pub const JIT_THRESHOLD_DEFAULT: u32 = 100;
pub const JIT_MAX_RECURSION_DEPTH: usize = 10000;
pub const JIT_MAX_TRACE_OPS: usize = 10000;
Paramètres du JIT Cranelift (hot detection après 100 itérations/appels, fallback interpréteur au-delà de 10000 niveaux
de récursion). Deux défauts d'activation, un par monde : le binaire standalone et la REPL démarrent JIT actif (opt-out
--no-jit), le pipeline Python démarre JIT inactif — le mode auto laisse les pragmas de fichier (pragma("jit", ...))
l'activer script par script. Un seul point de définition par valeur.
ND Recursion
pub const ND_MAX_RECURSION_DEPTH: usize = 300;
Profondeur maximale de récursion ND (~~) côté pipeline PyO3. Chaque appel récursif via recur() consomme la stack C
partagée avec CPython (estimation historique ~16KB par frame ; le stack overflow survient autour de ~494 frames sur une
stack de 8MB). La limite à 300 garde ~40% de marge sous ce point (300 × 16KB = 4.8MB) ; ne la relever qu'après re-mesure
du coût par frame. Le guard est appliqué dans VMHost.execute_nd_recursion.
La VM pure (catnip_vm) est aussi bornée par la stack C — recur réentre dispatch via run_sync, une frame
native par niveau — mais elle ne compte pas la profondeur : elle fait croître la stack à la demande avec
stacker::maybe_grow (segments heap alloués quand la stack courante est presque pleine), donc ~~ va aussi profond que
la mémoire le permet sans SIGSEGV. Un backstop-compteur (ND_RUNAWAY_CAP = 10 000, dans
catnip_vm/src/vm/broadcast.rs) transforme une récursion sans cas de base en erreur catchable au lieu d'un OOM. Ce
n'est donc pas une borne de profondeur mais un garde-fou anti-emballement.
Broadcast
pub const BROADCAST_MAX_DEPTH: usize = 1000;
Profondeur maximale d'imbrication d'un broadcast (.[...]) côté pipeline PyO3. Les opérateurs de broadcast
(broadcast_map, broadcast_binary_op, fast-path VM broadcast_map_in_vm) récursent une frame native par niveau
d'imbrication ; sans garde, une liste assez profonde débordait la stack C et provoquait un SIGSEGV (~14 400 niveaux sur
une stack de 8MB, ~5 000 sur 2MB ; ~440 o par frame, bien plus légères que les frames ND). La garde lève un
RuntimeError catchable à 1000 — marge large même sur les petites stacks (thread pools), et aligne sur la limite de
récursion par défaut de CPython. Aucun broadcast réel n'approche cette profondeur. Dans la VM pure (catnip_vm), la
même constante borne aussi l'ND-map ~> (nd_map_apply), qui récurse de façon identique.
Parsing
pub const PARSE_MAX_DEPTH: usize = 1000;
Profondeur maximale d'imbrication parse-tree → IR, partagée par les deux frontends (extension PyO3 et binaires
catnip_vm) car le dispatcher transform() vit dans catnip_core. Un littéral écrit en source ([[[...]]],
((((...))))) fait récurser transform une frame native par niveau ; sans garde, la stack C débordait et provoquait un
SIGSEGV avant même qu'une valeur existe — un chemin compile-time distinct du broadcast runtime. La garde (RAII
thread-local, même forme que le broadcast) lève une erreur de transformation catchable à 1000. En amont, la lib
tree-sitter elle-même déborde au-delà de ~5 000-20 000 niveaux selon la stack ; ce chemin-là n'est pas gardé (voir
wip/TODO.md).
Repr
pub const REPR_MAX_DEPTH: usize = 200;
Profondeur maximale d'affichage (repr/display) dans la VM pure. Value::display_string et la variante registry-aware
display_value_repr récursent une frame native par niveau ; afficher une liste ou un struct trop imbriqués débordait la
stack. Le cap est bien plus bas que celui du broadcast (1000) car une frame de repr est bien plus lourde (~10× :
nombreuses branches, format!, allocations locales) : le crash mesuré est autour de ~500 niveaux sur une stack de 2MB
(workers async, threads de test), d'où 200 pour garder >2× de marge. Seule la VM pure en a besoin — catnip_rs reprend
via des objets CPython, protégés par le trashcan. La garde arrête la récursion et les points d'entrée
(str/repr/print, résultat du serveur MCP) lèvent un RuntimeError catchable.
JIT - Pure Builtins
pub const JIT_NATIVE_BUILTINS: &[&str] = &["abs", "bool", "int", "max", "min", "round"];
pub const JIT_CALLBACK_BUILTINS: &[&str] = &["float"];
pub const JIT_PURE_BUILTINS: &[&str] = &[
"abs", "all", "any", "bool", "complex", "dict", "divmod", /* ... 27 noms,
voir catnip_core/src/constants.rs */
];
Trois catégories de builtins purs pour le JIT :
- Native : codegen Cranelift direct (args entiers)
- Callback : appel extern C (int/float)
- Pure : union des deux + builtins sans side effects (pour
LoadGlobal)
JIT_PURE_BUILTINS est aussi la source de Context.KNOWN_PURE_FUNCTIONS (exposée via _rs) et de la table du linter :
gen_builtins.py la parse directement dans catnip_core/src/constants.rs.
VM - Configuration
pub const VM_FRAME_STACK_CAPACITY: usize = 32;
pub const VM_FRAME_STACK_INIT: usize = 64;
pub const VM_FRAME_POOL_SIZE: usize = 64;
Tailles initiales des structures internes de la VM. VM_FRAME_STACK_CAPACITY est la capacité du stack d'opérandes par
frame, VM_FRAME_STACK_INIT la capacité initiale du stack de frames (profondeur d'appels avant réallocation).
Weird Log
pub const WEIRD_LOG_MAX_DEFAULT: usize = 50;
Nombre maximum de crash logs conservés dans ~/.local/state/catnip/.
Format - Defaults
pub const FORMAT_INDENT_SIZE_DEFAULT: usize = 4;
pub const FORMAT_LINE_LENGTH_DEFAULT: usize = 120;
pub const FORMAT_ALIGN_DEFAULT: bool = true;
Valeurs par défaut du formatter (catnip format). Overridables via [format] dans catnip.toml.
JIT - Inlining
pub const JIT_MAX_INLINE_OPS: usize = 20;
pub const JIT_MAX_INLINE_DEPTH: usize = 2;
Limites de l'inliner JIT. MAX_INLINE_OPS contrôle la taille maximale d'une fonction inlinée, MAX_INLINE_DEPTH la
profondeur de l'inlining récursif.
Benchmark
pub const BENCH_DEFAULT_ITERATIONS: usize = 10;
Nombre d'itérations par défaut pour catnip bench.
Cache - Configuration
pub const CACHE_DISK_TTL_DEFAULT: u64 = 86400; // 24h
pub const CACHE_DISK_MAX_SIZE_MB_DEFAULT: u64 = 100;
Paramètres du système de cache (mémoire FIFO et disque LRU).
Optimization - Niveaux
pub const OPTIMIZATION_LEVEL_DEFAULT: u8 = 2;
pub const OPTIMIZE_MAX: i64 = 3;
pub const TCO_ENABLED_DEFAULT: bool = true;
pub const EXECUTOR_DEFAULT: &str = "vm";
Niveau d'optimisation (0-3), TCO et exécuteur par défaut. Le défaut s'arrête à 2 parce que OPTIMIZE_MAX est le
palier du tier inter-blocs CFG+SSA : il se demande. Les deux constantes sont lues ensemble par
Pipeline::set_optimize_level, seul endroit où les seuils sont dérivés — un hôte qui les réécrirait pourrait accepter
-o level:3 sans activer le tier. EXECUTOR_DEFAULT traverse DEFAULT_CONFIG jusqu'aux fallbacks Python
(catnip/config.py le reflète depuis Rust).
Env vars, bit-packing et chemins XDG
Les noms d'env vars partagés (ENV_STDLIB_PATH, ENV_THEME + THEME_VALUES/THEME_DEFAULT, ENV_CATNIP_PATH) vivent
dans catnip_core ; les constantes de bit-packing du bytecode (CALL_ARGS_SHIFT/CALL_ARGS_MASK, FOR_RANGE_*) dans
catnip_core/src/vm/mod.rs -- consommées par les deux compilateurs et les deux VMs, un seul layout. Les répertoires XDG
(get_cache_dir, get_config_dir, get_state_dir, get_data_dir) sont regroupés dans catnip_core/src/paths.rs avec
l'unique APP_DIR = "catnip". Les seuils du linter (LINT_MAX_*) ont leur source de vérité dans
catnip_tools/src/config.rs ; les paramètres Python correspondants sont Optional (None → défaut Rust).
Python Module Paths
pub const PY_MOD_RS: &str = "catnip._rs";
pub const PY_MOD_NODES: &str = "catnip.nodes";
pub const PY_MOD_CONTEXT: &str = "catnip.context";
pub const PY_MOD_LOADER: &str = "catnip.loader";
pub const PY_MOD_SEMANTIC: &str = "catnip.semantic";
// ... 12 constantes au total
Chemins py.import() utilisés par les modules Rust (via PyO3) pour accéder aux modules Python. Définis dans
catnip_rs/src/constants.rs (pas dans catnip_core, car ils dépendent de PyO3).
Config Keys
pub const CFG_NO_COLOR: &str = "no_color";
pub const CFG_JIT: &str = "jit";
pub const CFG_TCO: &str = "tco";
pub const CFG_OPTIMIZE: &str = "optimize";
pub const CFG_EXECUTOR: &str = "executor";
// ... 14 constantes au total
Clés de configuration utilisées par ConfigManager. Le type &'static str permet d'utiliser
HashMap<&'static str, ConfigValue> au lieu de HashMap<String, ConfigValue>, éliminant les allocations heap pour les
clés.
Config Validation
pub const CONFIG_VALID_KEYS: &[&str] = &[
"no_color", "jit", "tco", "optimize", "executor",
"cache_max_size_mb", "cache_ttl_seconds", "theme",
"memory_limit", "enable_cache", "log_weird_errors", "max_weird_logs",
];
pub const CONFIG_VALID_FORMAT_KEYS: &[&str] = &["indent_size", "line_length", "align"];
Listes exhaustives des clés valides, exposées à Python via valid_config_keys() / valid_format_keys(). Les frozensets
Python (VALID_KEYS, VALID_FORMAT_KEYS) en dérivent directement -- source unique en Rust.
Error Messages
pub fn format_name_error(name: &str) -> String;
pub fn extract_name_from_error(msg: &str) -> Option<&str>;
Format centralisé pour les NameError ("name 'x' is not defined"). Tous les sites Rust (scope, frame, registry, VM)
utilisent format_name_error. L'extraction est exposée à Python via extract_name_from_error() (PyO3), remplaçant le
regex côté compat.py.
Boolean Parsing
pub fn parse_bool_value(s: &str) -> Option<bool> {
match s {
"on" | "true" | "1" | "yes" => Some(true),
"off" | "false" | "0" | "no" => Some(false),
_ => None,
}
}
Parsing centralisé pour les valeurs booléennes textuelles (config, pragmas, options CLI).
Architecture
Deux fichiers de constantes :
catnip_core/src/constants.rs: constantes non-visuelles pures Rust (messages, seuils, tailles, builtins,parse_bool_value)catnip_rs/src/constants.rs: réexportecatnip_core::constants::*, ajoute les constantes PyO3 (PY_MOD_*,CFG_*), et inclut les constantes visuelles viabuild.rs
Tous les modules accèdent à tout via use crate::constants::*.
Utilisation
// Import de module Python
use crate::constants::PY_MOD_SEMANTIC;
let module = py.import(PY_MOD_SEMANTIC)?;
// Clé de config (zero-alloc)
use crate::constants::CFG_TCO;
self.values.insert(CFG_TCO, config_value);
// Parsing booléen
use crate::constants::parse_bool_value;
let enabled = parse_bool_value("on"); // Some(true)
Modification
Pour changer un default runtime :
- Modifier la constante dans
catnip_core/src/constants.rs - Recompiler :
make compile
Pour changer une couleur ou un prompt :
- Modifier
catnip_rs/visual.toml - Recompiler :
make compile(lebuild.rsrégénèretheme_generated.rs)
Aucun code dans les autres modules n'a besoin de changer.