Skip to main content

sigil_parser/plurality/
codegen.rs

1//! # Plurality Code Generation
2//!
3//! Code generation for plurality constructs. Transforms plurality AST nodes
4//! into runtime Rust code that integrates with the DAEMONIORUM game engine.
5//!
6//! ## Generated Code Patterns
7//!
8//! ### Alter Definitions
9//! ```text
10//! // Generated from: alter Abaddon: Council { ... }
11//! pub struct Abaddon {
12//!     pub archetype: Archetype,
13//!     pub preferred_reality: RealityLayer,
14//!     pub abilities: Vec<Ability>,
15//!     pub triggers: Vec<Trigger>,
16//!     pub anima: AnimaState,
17//!     pub state_machine: AlterStateMachine,
18//! }
19//!
20//! impl Alter for Abaddon {
21//!     fn category(&self) -> AlterCategory { AlterCategory::Council }
22//!     fn can_front(&self) -> bool { true }
23//!     // ...
24//! }
25//! ```
26//!
27//! ### Alter Blocks
28//! ```text
29//! // Generated from: alter Abaddon { ... }
30//! {
31//!     let _alter_guard = system.enter_alter::<Abaddon>();
32//!     // ... block contents ...
33//! } // AlterGuard drops, restores previous fronter
34//! ```
35//!
36//! ### Switch Expressions
37//! ```text
38//! // Generated from: switch to Beleth { ... }
39//! system.propose_switch(SwitchRequest {
40//!     target: AlterId::Beleth,
41//!     reason: SwitchReason::Combat,
42//!     urgency: 0.8,
43//!     requires: Consensus::Majority,
44//! }).then(|result| {
45//!     // then block
46//! }).otherwise(|result| {
47//!     // else block
48//! })
49//! ```
50
51use std::fmt::Write;
52
53use super::ast::*;
54use crate::ast::Visibility;
55
56// ============================================================================
57// CODE GENERATOR
58// ============================================================================
59
60/// Plurality code generator
61#[derive(Debug, Default)]
62pub struct PluralityCodeGen {
63    /// Output buffer
64    output: String,
65    /// Indentation level
66    indent: usize,
67    /// Module name for generated code (reserved for future use)
68    #[allow(dead_code)]
69    module_name: String,
70}
71
72impl PluralityCodeGen {
73    /// Create a new code generator
74    pub fn new(module_name: &str) -> Self {
75        Self {
76            output: String::new(),
77            indent: 0,
78            module_name: module_name.to_string(),
79        }
80    }
81
82    /// Generate code for a plurality item
83    pub fn generate_item(&mut self, item: &PluralityItem) -> String {
84        match item {
85            PluralityItem::Alter(def) => self.generate_alter_def(def),
86            PluralityItem::Headspace(def) => self.generate_headspace_def(def),
87            PluralityItem::Reality(def) => self.generate_reality_def(def),
88            PluralityItem::CoConChannel(channel) => self.generate_cocon_channel(channel),
89            PluralityItem::TriggerHandler(handler) => self.generate_trigger_handler(handler),
90        }
91    }
92
93    /// Generate code for an alter definition
94    pub fn generate_alter_def(&mut self, def: &AlterDef) -> String {
95        self.output.clear();
96
97        // Generate struct
98        self.write_visibility(&def.visibility);
99        self.writeln(&format!("struct {} {{", def.name.name));
100        self.indent += 1;
101
102        // Core fields
103        self.writeln("pub archetype: Option<Archetype>,");
104        self.writeln("pub preferred_reality: RealityLayer,");
105        self.writeln("pub abilities: Vec<Ability>,");
106        self.writeln("pub triggers: Vec<TriggerDef>,");
107        self.writeln("pub anima: AnimaState,");
108        self.writeln("pub state_machine: AlterStateMachine,");
109        self.writeln("pub state: AlterRuntimeState,");
110
111        self.indent -= 1;
112        self.writeln("}");
113        self.writeln("");
114
115        // Generate Default impl
116        self.generate_alter_default(def);
117
118        // Generate Alter trait impl
119        self.generate_alter_trait_impl(def);
120
121        // Generate methods
122        if !def.body.methods.is_empty() {
123            self.writeln(&format!("impl {} {{", def.name.name));
124            self.indent += 1;
125
126            for method in &def.body.methods {
127                self.generate_alter_method(method);
128            }
129
130            self.indent -= 1;
131            self.writeln("}");
132        }
133
134        self.output.clone()
135    }
136
137    /// Generate Default implementation for alter
138    fn generate_alter_default(&mut self, def: &AlterDef) {
139        self.writeln(&format!("impl Default for {} {{", def.name.name));
140        self.indent += 1;
141        self.writeln("fn default() -> Self {");
142        self.indent += 1;
143        self.writeln("Self {");
144        self.indent += 1;
145
146        // Archetype
147        if let Some(archetype) = &def.body.archetype {
148            self.writeln(&format!("archetype: Some({}),", expr_to_string(archetype)));
149        } else {
150            self.writeln("archetype: None,");
151        }
152
153        // Preferred reality
154        if let Some(reality) = &def.body.preferred_reality {
155            self.writeln(&format!("preferred_reality: {},", expr_to_string(reality)));
156        } else {
157            self.writeln("preferred_reality: RealityLayer::Grounded,");
158        }
159
160        // Abilities
161        self.write("abilities: vec![");
162        for (i, ability) in def.body.abilities.iter().enumerate() {
163            if i > 0 {
164                self.write(", ");
165            }
166            self.write(&expr_to_string(ability));
167        }
168        self.writeln("],");
169
170        // Triggers
171        self.write("triggers: vec![");
172        for (i, trigger) in def.body.triggers.iter().enumerate() {
173            if i > 0 {
174                self.write(", ");
175            }
176            self.write(&expr_to_string(trigger));
177        }
178        self.writeln("],");
179
180        // Anima
181        self.generate_anima_init(&def.body.anima);
182
183        // State machine
184        self.generate_state_machine_init(&def.body.states);
185
186        // Runtime state
187        self.writeln("state: AlterRuntimeState::Dormant,");
188
189        self.indent -= 1;
190        self.writeln("}");
191        self.indent -= 1;
192        self.writeln("}");
193        self.indent -= 1;
194        self.writeln("}");
195        self.writeln("");
196    }
197
198    /// Generate Alter trait implementation
199    fn generate_alter_trait_impl(&mut self, def: &AlterDef) {
200        self.writeln(&format!("impl Alter for {} {{", def.name.name));
201        self.indent += 1;
202
203        // Category
204        let category = match def.category {
205            AlterCategory::Council => "AlterCategory::Council",
206            AlterCategory::Servant => "AlterCategory::Servant",
207            AlterCategory::Fragment => "AlterCategory::Fragment",
208            AlterCategory::Hidden => "AlterCategory::Hidden",
209            AlterCategory::Persecutor => "AlterCategory::Persecutor",
210            AlterCategory::Custom => "AlterCategory::Custom",
211        };
212        self.writeln(&format!(
213            "fn category(&self) -> AlterCategory {{ {} }}",
214            category
215        ));
216
217        // Can front
218        let can_front = matches!(def.category, AlterCategory::Council);
219        self.writeln(&format!("fn can_front(&self) -> bool {{ {} }}", can_front));
220
221        // Name
222        self.writeln(&format!(
223            "fn name(&self) -> &'static str {{ \"{}\" }}",
224            def.name.name
225        ));
226
227        // Archetype
228        self.writeln("fn archetype(&self) -> Option<&Archetype> { self.archetype.as_ref() }");
229
230        // Preferred reality
231        self.writeln("fn preferred_reality(&self) -> RealityLayer { self.preferred_reality }");
232
233        // Abilities
234        self.writeln("fn abilities(&self) -> &[Ability] { &self.abilities }");
235
236        // Triggers
237        self.writeln("fn triggers(&self) -> &[TriggerDef] { &self.triggers }");
238
239        // Anima
240        self.writeln("fn anima(&self) -> &AnimaState { &self.anima }");
241        self.writeln("fn anima_mut(&mut self) -> &mut AnimaState { &mut self.anima }");
242
243        // State
244        self.writeln("fn state(&self) -> AlterRuntimeState { self.state }");
245        self.writeln("fn set_state(&mut self, state: AlterRuntimeState) { self.state = state; }");
246
247        self.indent -= 1;
248        self.writeln("}");
249        self.writeln("");
250    }
251
252    /// Generate anima initialization
253    fn generate_anima_init(&mut self, anima: &Option<AnimaConfig>) {
254        if let Some(config) = anima {
255            self.writeln("anima: AnimaState {");
256            self.indent += 1;
257
258            if let Some(arousal) = &config.base_arousal {
259                self.writeln(&format!("arousal: {},", expr_to_string(arousal)));
260            } else {
261                self.writeln("arousal: 0.5,");
262            }
263
264            if let Some(dominance) = &config.base_dominance {
265                self.writeln(&format!("dominance: {},", expr_to_string(dominance)));
266            } else {
267                self.writeln("dominance: 0.5,");
268            }
269
270            if let Some(expressiveness) = &config.expressiveness {
271                self.writeln(&format!("expressiveness: {},", expr_to_string(expressiveness)));
272            } else {
273                self.writeln("expressiveness: 0.5,");
274            }
275
276            if let Some(susceptibility) = &config.susceptibility {
277                self.writeln(&format!("susceptibility: {},", expr_to_string(susceptibility)));
278            } else {
279                self.writeln("susceptibility: 0.5,");
280            }
281
282            self.writeln("..Default::default()");
283            self.indent -= 1;
284            self.writeln("},");
285        } else {
286            self.writeln("anima: AnimaState::default(),");
287        }
288    }
289
290    /// Generate state machine initialization
291    fn generate_state_machine_init(&mut self, states: &Option<AlterStateMachine>) {
292        if let Some(sm) = states {
293            self.writeln("state_machine: AlterStateMachine {");
294            self.indent += 1;
295            self.writeln("transitions: vec![");
296            self.indent += 1;
297
298            for transition in &sm.transitions {
299                self.writeln("AlterTransitionDef {");
300                self.indent += 1;
301                self.writeln(&format!(
302                    "from: AlterRuntimeState::{:?},",
303                    transition.from
304                ));
305                self.writeln(&format!(
306                    "to: AlterRuntimeState::{:?},",
307                    transition.to
308                ));
309                self.writeln(&format!(
310                    "on: TriggerCondition::from({}),",
311                    expr_to_string(&transition.on)
312                ));
313
314                if let Some(guard) = &transition.guard {
315                    self.writeln(&format!(
316                        "guard: Some(Box::new(|ctx| {})),",
317                        expr_to_string(guard)
318                    ));
319                } else {
320                    self.writeln("guard: None,");
321                }
322
323                if transition.action.is_some() {
324                    self.writeln("action: Some(Box::new(|ctx| { /* action */ })),");
325                } else {
326                    self.writeln("action: None,");
327                }
328
329                self.indent -= 1;
330                self.writeln("},");
331            }
332
333            self.indent -= 1;
334            self.writeln("],");
335            self.indent -= 1;
336            self.writeln("},");
337        } else {
338            self.writeln("state_machine: AlterStateMachine::default(),");
339        }
340    }
341
342    /// Generate an alter method
343    fn generate_alter_method(&mut self, method: &AlterMethod) {
344        self.write_visibility(&method.visibility);
345
346        if method.is_async {
347            self.write("async ");
348        }
349
350        self.write(&format!("fn {}(&", method.name.name));
351        if method.params.iter().any(|p| is_self_pattern(&p.pattern)) {
352            self.write("mut self");
353        } else {
354            self.write("self");
355        }
356
357        for param in &method.params {
358            if !is_self_pattern(&param.pattern) {
359                self.write(&format!(", {}", pattern_to_string(&param.pattern)));
360                self.write(&format!(": {}", type_to_string(&param.ty)));
361            }
362        }
363        self.write(")");
364
365        if let Some(ret_ty) = &method.return_type {
366            self.write(&format!(" -> {}", type_to_string(ret_ty)));
367        }
368
369        if let Some(_body) = &method.body {
370            self.writeln(" {");
371            self.indent += 1;
372            self.writeln("// Method body");
373            self.indent -= 1;
374            self.writeln("}");
375        } else {
376            self.writeln(";");
377        }
378    }
379
380    /// Generate code for a headspace definition
381    pub fn generate_headspace_def(&mut self, def: &HeadspaceDef) -> String {
382        self.output.clear();
383
384        // Generate module
385        self.write_visibility(&def.visibility);
386        self.writeln(&format!("mod {} {{", def.name.name.to_lowercase()));
387        self.indent += 1;
388        self.writeln("use super::*;");
389        self.writeln("");
390
391        // Generate location structs
392        for location in &def.locations {
393            self.generate_location_struct(location);
394        }
395
396        // Generate headspace struct
397        self.writeln(&format!("pub struct {} {{", def.name.name));
398        self.indent += 1;
399        for location in &def.locations {
400            self.writeln(&format!(
401                "pub {}: {},",
402                location.name.name.to_lowercase(),
403                location.name.name
404            ));
405        }
406        self.indent -= 1;
407        self.writeln("}");
408        self.writeln("");
409
410        // Generate impl with methods
411        if !def.methods.is_empty() {
412            self.writeln(&format!("impl {} {{", def.name.name));
413            self.indent += 1;
414            for method in &def.methods {
415                self.generate_alter_method(method);
416            }
417            self.indent -= 1;
418            self.writeln("}");
419        }
420
421        self.indent -= 1;
422        self.writeln("}");
423
424        self.output.clone()
425    }
426
427    /// Generate a location struct
428    fn generate_location_struct(&mut self, location: &LocationDef) {
429        self.writeln(&format!("pub struct {} {{", location.name.name));
430        self.indent += 1;
431        self.writeln(&format!(
432            "pub location_type: {},",
433            location.location_type.name
434        ));
435
436        for (field_name, _) in &location.fields {
437            // Infer field type from field name or default to generic
438            self.writeln(&format!("pub {}: LocationField,", field_name.name));
439        }
440
441        if !location.connections.is_empty() {
442            self.writeln("pub connections: Vec<ConsciousnessStream>,");
443        }
444
445        if !location.hazards.is_empty() {
446            self.writeln("pub hazards: Vec<Hazard>,");
447        }
448
449        self.indent -= 1;
450        self.writeln("}");
451        self.writeln("");
452    }
453
454    /// Generate code for a reality definition
455    pub fn generate_reality_def(&mut self, def: &RealityDef) -> String {
456        self.output.clear();
457
458        // Generate superimposed entity struct
459        self.write_visibility(&def.visibility);
460        self.writeln(&format!("struct {} {{", def.name.name));
461        self.indent += 1;
462
463        for layer in &def.layers {
464            self.writeln(&format!(
465                "pub {}: {}Layer,",
466                layer.name.name.to_lowercase(),
467                layer.name.name
468            ));
469        }
470
471        self.indent -= 1;
472        self.writeln("}");
473        self.writeln("");
474
475        // Generate layer structs
476        for layer in &def.layers {
477            self.writeln(&format!("pub struct {}Layer {{", layer.name.name));
478            self.indent += 1;
479            for (field_name, _) in &layer.fields {
480                self.writeln(&format!("pub {}: RealityValue,", field_name.name));
481            }
482            self.indent -= 1;
483            self.writeln("}");
484            self.writeln("");
485        }
486
487        // Generate Superimposed trait impl
488        self.writeln(&format!("impl Superimposed for {} {{", def.name.name));
489        self.indent += 1;
490
491        self.writeln("fn current(&self, perception: &PerceptionState) -> &dyn RealityLayerView {");
492        self.indent += 1;
493        self.writeln("match perception.current_layer() {");
494        self.indent += 1;
495
496        for layer in &def.layers {
497            self.writeln(&format!(
498                "RealityLayer::{} => &self.{},",
499                layer.name.name,
500                layer.name.name.to_lowercase()
501            ));
502        }
503        self.writeln("_ => &self.grounded,");
504
505        self.indent -= 1;
506        self.writeln("}");
507        self.indent -= 1;
508        self.writeln("}");
509
510        // Generate transform logic
511        if !def.transforms.is_empty() {
512            self.writeln("");
513            self.writeln("fn check_transform(&self, perception: &PerceptionState) -> Option<RealityLayer> {");
514            self.indent += 1;
515
516            for transform in &def.transforms {
517                self.writeln(&format!(
518                    "if perception.current_layer() == RealityLayer::{} && ({}) {{",
519                    transform.from.name,
520                    expr_to_string(&transform.condition)
521                ));
522                self.indent += 1;
523                self.writeln(&format!(
524                    "return Some(RealityLayer::{});",
525                    transform.to.name
526                ));
527                self.indent -= 1;
528                self.writeln("}");
529            }
530
531            self.writeln("None");
532            self.indent -= 1;
533            self.writeln("}");
534        }
535
536        self.indent -= 1;
537        self.writeln("}");
538
539        self.output.clone()
540    }
541
542    /// Generate code for a co-con channel
543    pub fn generate_cocon_channel(&mut self, channel: &CoConChannel) -> String {
544        self.output.clear();
545
546        // Generate channel struct
547        self.writeln(&format!("pub struct {}Channel {{", channel.name.name));
548        self.indent += 1;
549        self.writeln("participants: Vec<AlterId>,");
550        self.writeln("active: bool,");
551        self.indent -= 1;
552        self.writeln("}");
553        self.writeln("");
554
555        // Generate impl
556        self.writeln(&format!("impl {}Channel {{", channel.name.name));
557        self.indent += 1;
558
559        // Constructor
560        self.writeln("pub fn new() -> Self {");
561        self.indent += 1;
562        self.writeln("Self {");
563        self.indent += 1;
564        self.write("participants: vec![");
565        for (i, p) in channel.participants.iter().enumerate() {
566            if i > 0 {
567                self.write(", ");
568            }
569            self.write(&format!("AlterId::{}", p.name));
570        }
571        self.writeln("],");
572        self.writeln("active: false,");
573        self.indent -= 1;
574        self.writeln("}");
575        self.indent -= 1;
576        self.writeln("}");
577        self.writeln("");
578
579        // Activate
580        self.writeln("pub fn activate(&mut self, system: &PluralSystem) -> Result<(), ChannelError> {");
581        self.indent += 1;
582        self.writeln("for &p in &self.participants {");
583        self.indent += 1;
584        self.writeln("if !system.is_cocon(p) {");
585        self.indent += 1;
586        self.writeln("return Err(ChannelError::NotCoConscious(p));");
587        self.indent -= 1;
588        self.writeln("}");
589        self.indent -= 1;
590        self.writeln("}");
591        self.writeln("self.active = true;");
592        self.writeln("Ok(())");
593        self.indent -= 1;
594        self.writeln("}");
595
596        self.indent -= 1;
597        self.writeln("}");
598
599        self.output.clone()
600    }
601
602    /// Generate code for a trigger handler
603    pub fn generate_trigger_handler(&mut self, handler: &TriggerHandler) -> String {
604        self.output.clear();
605
606        // Generate handler function
607        self.writeln(&format!(
608            "pub fn handle_{}(ctx: &mut TriggerContext) -> TriggerResult {{",
609            handler.pattern.trigger_type.name.to_lowercase()
610        ));
611        self.indent += 1;
612
613        // Destructure trigger
614        if !handler.pattern.fields.is_empty() {
615            self.write(&format!(
616                "let {} {{ ",
617                handler.pattern.trigger_type.name
618            ));
619            for (i, (field, binding)) in handler.pattern.fields.iter().enumerate() {
620                if i > 0 {
621                    self.write(", ");
622                }
623                if field.name != binding.name {
624                    self.write(&format!("{}: {}", field.name, binding.name));
625                } else {
626                    self.write(&binding.name);
627                }
628            }
629            self.writeln(" } = ctx.trigger();");
630        }
631
632        // Guard condition
633        if let Some(guard) = &handler.guard {
634            self.writeln(&format!("if !({}) {{", expr_to_string(guard)));
635            self.indent += 1;
636            self.writeln("return TriggerResult::Ignored;");
637            self.indent -= 1;
638            self.writeln("}");
639        }
640
641        // Handler body
642        self.writeln("// Handler body");
643        self.writeln("TriggerResult::Handled");
644
645        self.indent -= 1;
646        self.writeln("}");
647
648        self.output.clone()
649    }
650
651    /// Generate code for a switch expression
652    pub fn generate_switch_expr(&mut self, expr: &SwitchExpr) -> String {
653        let mut out = String::new();
654
655        if expr.forced {
656            write!(out, "system.force_switch(").unwrap();
657        } else {
658            write!(out, "system.propose_switch(").unwrap();
659        }
660
661        write!(out, "SwitchRequest {{").unwrap();
662        write!(out, " target: {},", alter_expr_to_string(&expr.target)).unwrap();
663
664        if let Some(reason) = &expr.config.reason {
665            write!(out, " reason: {},", expr_to_string(reason)).unwrap();
666        }
667
668        if let Some(urgency) = &expr.config.urgency {
669            write!(out, " urgency: {},", expr_to_string(urgency)).unwrap();
670        }
671
672        if let Some(requires) = &expr.config.requires {
673            write!(out, " requires: {},", expr_to_string(requires)).unwrap();
674        }
675
676        write!(out, " bypass_deliberation: {},", expr.config.bypass_deliberation).unwrap();
677        write!(out, " ..Default::default() }})").unwrap();
678
679        // Add then/else closures
680        if expr.config.then_block.is_some() || expr.config.else_block.is_some() {
681            write!(out, ".handle(|result| match result {{ ").unwrap();
682            if expr.config.then_block.is_some() {
683                write!(out, "SwitchResult::Success => {{ /* then block */ }}, ").unwrap();
684            }
685            if expr.config.else_block.is_some() {
686                write!(out, "SwitchResult::Denied(_) => {{ /* else block */ }}, ").unwrap();
687            }
688            if expr.config.emergency_block.is_some() {
689                write!(out, "SwitchResult::Emergency => {{ /* emergency block */ }}, ").unwrap();
690            }
691            write!(out, "_ => {{}} }})").unwrap();
692        }
693
694        out
695    }
696
697    /// Generate code for a split expression
698    pub fn generate_split_expr(&mut self, expr: &SplitExpr) -> String {
699        let mut out = String::new();
700
701        write!(out, "system.process_split(SplitRequest {{").unwrap();
702        write!(out, " parent: {},", alter_expr_to_string(&expr.parent)).unwrap();
703
704        if let Some(purpose) = &expr.config.purpose {
705            write!(out, " purpose: {},", expr_to_string(purpose)).unwrap();
706        }
707
708        if let Some(memories) = &expr.config.memories {
709            write!(out, " memories: {},", expr_to_string(memories)).unwrap();
710        }
711
712        if let Some(traits) = &expr.config.traits {
713            write!(out, " traits: {},", expr_to_string(traits)).unwrap();
714        }
715
716        write!(out, " ..Default::default() }})").unwrap();
717
718        out
719    }
720
721    /// Generate code for an alter block
722    pub fn generate_alter_block(&mut self, block: &AlterBlock) -> String {
723        let mut out = String::new();
724
725        write!(out, "{{ let _alter_guard = system.enter_alter::<").unwrap();
726        write!(out, "{}", alter_expr_to_string(&block.alter)).unwrap();
727        write!(out, ">();").unwrap();
728        write!(out, " /* block body */ }}").unwrap();
729
730        out
731    }
732
733    // Helper methods
734
735    fn write(&mut self, s: &str) {
736        self.output.push_str(s);
737    }
738
739    fn writeln(&mut self, s: &str) {
740        for _ in 0..self.indent {
741            self.output.push_str("    ");
742        }
743        self.output.push_str(s);
744        self.output.push('\n');
745    }
746
747    fn write_visibility(&mut self, vis: &Visibility) {
748        match vis {
749            Visibility::Public => self.write("pub "),
750            Visibility::Crate => self.write("pub(crate) "),
751            Visibility::Super => self.write("pub(super) "),
752            Visibility::Private => {}
753        }
754    }
755}
756
757// ============================================================================
758// HELPER FUNCTIONS
759// ============================================================================
760
761/// Convert expression to string (placeholder)
762fn expr_to_string(expr: &crate::ast::Expr) -> String {
763    format!("{:?}", expr).replace('"', "'")
764}
765
766/// Convert type expression to string (placeholder)
767fn type_to_string(ty: &crate::ast::TypeExpr) -> String {
768    format!("{:?}", ty)
769}
770
771/// Convert alter expression to string
772fn alter_expr_to_string(alter: &AlterExpr) -> String {
773    match alter {
774        AlterExpr::Named(ident) => ident.name.clone(),
775        AlterExpr::CurrentFronter(_) => "system.current_fronter()".to_string(),
776        AlterExpr::Expr(expr) => expr_to_string(expr),
777    }
778}
779
780/// Check if a pattern is the "self" identifier
781fn is_self_pattern(pattern: &crate::ast::Pattern) -> bool {
782    match pattern {
783        crate::ast::Pattern::Ident { name, .. } => name.name == "self",
784        _ => false,
785    }
786}
787
788/// Convert pattern to string (placeholder)
789fn pattern_to_string(pattern: &crate::ast::Pattern) -> String {
790    match pattern {
791        crate::ast::Pattern::Ident { mutable, name, .. } => {
792            if *mutable {
793                format!("mut {}", name.name)
794            } else {
795                name.name.clone()
796            }
797        }
798        crate::ast::Pattern::Tuple(patterns) => {
799            let inner: Vec<_> = patterns.iter().map(pattern_to_string).collect();
800            format!("({})", inner.join(", "))
801        }
802        _ => format!("{:?}", pattern),
803    }
804}
805
806// ============================================================================
807// TESTS
808// ============================================================================
809
810#[cfg(test)]
811mod tests {
812    use super::*;
813    use crate::ast::{Expr, Ident, Literal};
814    use crate::span::Span;
815
816    fn mock_ident(name: &str) -> Ident {
817        Ident {
818            name: name.to_string(),
819            evidentiality: None,
820            affect: None,
821            span: Span::default(),
822        }
823    }
824
825    #[test]
826    fn test_generate_alter_def() {
827        let def = AlterDef {
828            visibility: Visibility::Public,
829            attrs: Vec::new(),
830            name: mock_ident("Abaddon"),
831            category: AlterCategory::Council,
832            generics: None,
833            where_clause: None,
834            body: AlterBody {
835                archetype: Some(Expr::Path(crate::ast::TypePath {
836                    segments: vec![crate::ast::PathSegment {
837                        ident: mock_ident("Goetia"),
838                        generics: None,
839                    }],
840                })),
841                preferred_reality: None,
842                abilities: Vec::new(),
843                triggers: Vec::new(),
844                anima: None,
845                states: None,
846                special: Vec::new(),
847                methods: Vec::new(),
848                types: Vec::new(),
849            },
850            span: Span::default(),
851        };
852
853        let mut gen = PluralityCodeGen::new("test");
854        let output = gen.generate_alter_def(&def);
855
856        assert!(output.contains("struct Abaddon"));
857        assert!(output.contains("impl Alter for Abaddon"));
858        assert!(output.contains("AlterCategory::Council"));
859    }
860
861    #[test]
862    fn test_generate_switch_expr() {
863        let expr = SwitchExpr {
864            forced: false,
865            target: AlterExpr::Named(mock_ident("Beleth")),
866            config: SwitchConfig {
867                reason: Some(Expr::Literal(Literal::String("Combat".to_string()))),
868                urgency: Some(Expr::Literal(Literal::Float {
869                    value: "0.8".to_string(),
870                    suffix: None,
871                })),
872                requires: None,
873                then_block: None,
874                else_block: None,
875                emergency_block: None,
876                bypass_deliberation: false,
877            },
878            span: Span::default(),
879        };
880
881        let mut gen = PluralityCodeGen::new("test");
882        let output = gen.generate_switch_expr(&expr);
883
884        assert!(output.contains("propose_switch"));
885        assert!(output.contains("Beleth"));
886    }
887
888    #[test]
889    fn test_generate_alter_block() {
890        let block = AlterBlock {
891            alter: AlterExpr::Named(mock_ident("Abaddon")),
892            body: crate::ast::Block {
893                stmts: Vec::new(),
894                expr: None,
895            },
896            span: Span::default(),
897        };
898
899        let mut gen = PluralityCodeGen::new("test");
900        let output = gen.generate_alter_block(&block);
901
902        assert!(output.contains("enter_alter"));
903        assert!(output.contains("Abaddon"));
904    }
905}