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