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!(
271                    "expressiveness: {},",
272                    expr_to_string(expressiveness)
273                ));
274            } else {
275                self.writeln("expressiveness: 0.5,");
276            }
277
278            if let Some(susceptibility) = &config.susceptibility {
279                self.writeln(&format!(
280                    "susceptibility: {},",
281                    expr_to_string(susceptibility)
282                ));
283            } else {
284                self.writeln("susceptibility: 0.5,");
285            }
286
287            self.writeln("..Default::default()");
288            self.indent -= 1;
289            self.writeln("},");
290        } else {
291            self.writeln("anima: AnimaState::default(),");
292        }
293    }
294
295    /// Generate state machine initialization
296    fn generate_state_machine_init(&mut self, states: &Option<AlterStateMachine>) {
297        if let Some(sm) = states {
298            self.writeln("state_machine: AlterStateMachine {");
299            self.indent += 1;
300            self.writeln("transitions: vec![");
301            self.indent += 1;
302
303            for transition in &sm.transitions {
304                self.writeln("AlterTransitionDef {");
305                self.indent += 1;
306                self.writeln(&format!("from: AlterRuntimeState::{:?},", transition.from));
307                self.writeln(&format!("to: AlterRuntimeState::{:?},", transition.to));
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(
513                "fn check_transform(&self, perception: &PerceptionState) -> Option<RealityLayer> {",
514            );
515            self.indent += 1;
516
517            for transform in &def.transforms {
518                self.writeln(&format!(
519                    "if perception.current_layer() == RealityLayer::{} && ({}) {{",
520                    transform.from.name,
521                    expr_to_string(&transform.condition)
522                ));
523                self.indent += 1;
524                self.writeln(&format!(
525                    "return Some(RealityLayer::{});",
526                    transform.to.name
527                ));
528                self.indent -= 1;
529                self.writeln("}");
530            }
531
532            self.writeln("None");
533            self.indent -= 1;
534            self.writeln("}");
535        }
536
537        self.indent -= 1;
538        self.writeln("}");
539
540        self.output.clone()
541    }
542
543    /// Generate code for a co-con channel
544    pub fn generate_cocon_channel(&mut self, channel: &CoConChannel) -> String {
545        self.output.clear();
546
547        // Generate channel struct
548        self.writeln(&format!("pub struct {}Channel {{", channel.name.name));
549        self.indent += 1;
550        self.writeln("participants: Vec<AlterId>,");
551        self.writeln("active: bool,");
552        self.indent -= 1;
553        self.writeln("}");
554        self.writeln("");
555
556        // Generate impl
557        self.writeln(&format!("impl {}Channel {{", channel.name.name));
558        self.indent += 1;
559
560        // Constructor
561        self.writeln("pub fn new() -> Self {");
562        self.indent += 1;
563        self.writeln("Self {");
564        self.indent += 1;
565        self.write("participants: vec![");
566        for (i, p) in channel.participants.iter().enumerate() {
567            if i > 0 {
568                self.write(", ");
569            }
570            self.write(&format!("AlterId::{}", p.name));
571        }
572        self.writeln("],");
573        self.writeln("active: false,");
574        self.indent -= 1;
575        self.writeln("}");
576        self.indent -= 1;
577        self.writeln("}");
578        self.writeln("");
579
580        // Activate
581        self.writeln(
582            "pub fn activate(&mut self, system: &PluralSystem) -> Result<(), ChannelError> {",
583        );
584        self.indent += 1;
585        self.writeln("for &p in &self.participants {");
586        self.indent += 1;
587        self.writeln("if !system.is_cocon(p) {");
588        self.indent += 1;
589        self.writeln("return Err(ChannelError::NotCoConscious(p));");
590        self.indent -= 1;
591        self.writeln("}");
592        self.indent -= 1;
593        self.writeln("}");
594        self.writeln("self.active = true;");
595        self.writeln("Ok(())");
596        self.indent -= 1;
597        self.writeln("}");
598
599        self.indent -= 1;
600        self.writeln("}");
601
602        self.output.clone()
603    }
604
605    /// Generate code for a trigger handler
606    pub fn generate_trigger_handler(&mut self, handler: &TriggerHandler) -> String {
607        self.output.clear();
608
609        // Generate handler function
610        self.writeln(&format!(
611            "pub fn handle_{}(ctx: &mut TriggerContext) -> TriggerResult {{",
612            handler.pattern.trigger_type.name.to_lowercase()
613        ));
614        self.indent += 1;
615
616        // Destructure trigger
617        if !handler.pattern.fields.is_empty() {
618            self.write(&format!("let {} {{ ", handler.pattern.trigger_type.name));
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!(
677            out,
678            " bypass_deliberation: {},",
679            expr.config.bypass_deliberation
680        )
681        .unwrap();
682        write!(out, " ..Default::default() }})").unwrap();
683
684        // Add then/else closures
685        if expr.config.then_block.is_some() || expr.config.else_block.is_some() {
686            write!(out, ".handle(|result| match result {{ ").unwrap();
687            if expr.config.then_block.is_some() {
688                write!(out, "SwitchResult::Success => {{ /* then block */ }}, ").unwrap();
689            }
690            if expr.config.else_block.is_some() {
691                write!(out, "SwitchResult::Denied(_) => {{ /* else block */ }}, ").unwrap();
692            }
693            if expr.config.emergency_block.is_some() {
694                write!(
695                    out,
696                    "SwitchResult::Emergency => {{ /* emergency block */ }}, "
697                )
698                .unwrap();
699            }
700            write!(out, "_ => {{}} }})").unwrap();
701        }
702
703        out
704    }
705
706    /// Generate code for a split expression
707    pub fn generate_split_expr(&mut self, expr: &SplitExpr) -> String {
708        let mut out = String::new();
709
710        write!(out, "system.process_split(SplitRequest {{").unwrap();
711        write!(out, " parent: {},", alter_expr_to_string(&expr.parent)).unwrap();
712
713        if let Some(purpose) = &expr.config.purpose {
714            write!(out, " purpose: {},", expr_to_string(purpose)).unwrap();
715        }
716
717        if let Some(memories) = &expr.config.memories {
718            write!(out, " memories: {},", expr_to_string(memories)).unwrap();
719        }
720
721        if let Some(traits) = &expr.config.traits {
722            write!(out, " traits: {},", expr_to_string(traits)).unwrap();
723        }
724
725        write!(out, " ..Default::default() }})").unwrap();
726
727        out
728    }
729
730    /// Generate code for an alter block
731    pub fn generate_alter_block(&mut self, block: &AlterBlock) -> String {
732        let mut out = String::new();
733
734        write!(out, "{{ let _alter_guard = system.enter_alter::<").unwrap();
735        write!(out, "{}", alter_expr_to_string(&block.alter)).unwrap();
736        write!(out, ">();").unwrap();
737        write!(out, " /* block body */ }}").unwrap();
738
739        out
740    }
741
742    // Helper methods
743
744    fn write(&mut self, s: &str) {
745        self.output.push_str(s);
746    }
747
748    fn writeln(&mut self, s: &str) {
749        for _ in 0..self.indent {
750            self.output.push_str("    ");
751        }
752        self.output.push_str(s);
753        self.output.push('\n');
754    }
755
756    fn write_visibility(&mut self, vis: &Visibility) {
757        match vis {
758            Visibility::Public => self.write("pub "),
759            Visibility::Crate => self.write("pub(crate) "),
760            Visibility::Super => self.write("pub(super) "),
761            Visibility::Private => {}
762        }
763    }
764}
765
766// ============================================================================
767// HELPER FUNCTIONS
768// ============================================================================
769
770/// Convert expression to string (placeholder)
771fn expr_to_string(expr: &crate::ast::Expr) -> String {
772    format!("{:?}", expr).replace('"', "'")
773}
774
775/// Convert type expression to string (placeholder)
776fn type_to_string(ty: &crate::ast::TypeExpr) -> String {
777    format!("{:?}", ty)
778}
779
780/// Convert alter expression to string
781fn alter_expr_to_string(alter: &AlterExpr) -> String {
782    match alter {
783        AlterExpr::Named(ident) => ident.name.clone(),
784        AlterExpr::CurrentFronter(_) => "system.current_fronter()".to_string(),
785        AlterExpr::Expr(expr) => expr_to_string(expr),
786    }
787}
788
789/// Check if a pattern is the "self" identifier
790fn is_self_pattern(pattern: &crate::ast::Pattern) -> bool {
791    match pattern {
792        crate::ast::Pattern::Ident { name, .. } => name.name == "self",
793        _ => false,
794    }
795}
796
797/// Convert pattern to string (placeholder)
798fn pattern_to_string(pattern: &crate::ast::Pattern) -> String {
799    match pattern {
800        crate::ast::Pattern::Ident { mutable, name, .. } => {
801            if *mutable {
802                format!("mut {}", name.name)
803            } else {
804                name.name.clone()
805            }
806        }
807        crate::ast::Pattern::Tuple(patterns) => {
808            let inner: Vec<_> = patterns.iter().map(pattern_to_string).collect();
809            format!("({})", inner.join(", "))
810        }
811        _ => format!("{:?}", pattern),
812    }
813}
814
815// ============================================================================
816// TESTS
817// ============================================================================
818
819#[cfg(test)]
820mod tests {
821    use super::*;
822    use crate::ast::{Expr, Ident, Literal};
823    use crate::span::Span;
824
825    fn mock_ident(name: &str) -> Ident {
826        Ident {
827            name: name.to_string(),
828            evidentiality: None,
829            affect: None,
830            span: Span::default(),
831        }
832    }
833
834    #[test]
835    fn test_generate_alter_def() {
836        let def = AlterDef {
837            visibility: Visibility::Public,
838            attrs: Vec::new(),
839            name: mock_ident("Abaddon"),
840            category: AlterCategory::Council,
841            generics: None,
842            where_clause: None,
843            body: AlterBody {
844                archetype: Some(Expr::Path(crate::ast::TypePath {
845                    segments: vec![crate::ast::PathSegment {
846                        ident: mock_ident("Goetia"),
847                        generics: None,
848                    }],
849                })),
850                preferred_reality: None,
851                abilities: Vec::new(),
852                triggers: Vec::new(),
853                anima: None,
854                states: None,
855                special: Vec::new(),
856                methods: Vec::new(),
857                types: Vec::new(),
858            },
859            span: Span::default(),
860        };
861
862        let mut gen = PluralityCodeGen::new("test");
863        let output = gen.generate_alter_def(&def);
864
865        assert!(output.contains("struct Abaddon"));
866        assert!(output.contains("impl Alter for Abaddon"));
867        assert!(output.contains("AlterCategory::Council"));
868    }
869
870    #[test]
871    fn test_generate_switch_expr() {
872        let expr = SwitchExpr {
873            forced: false,
874            target: AlterExpr::Named(mock_ident("Beleth")),
875            config: SwitchConfig {
876                reason: Some(Expr::Literal(Literal::String("Combat".to_string()))),
877                urgency: Some(Expr::Literal(Literal::Float {
878                    value: "0.8".to_string(),
879                    suffix: None,
880                })),
881                requires: None,
882                then_block: None,
883                else_block: None,
884                emergency_block: None,
885                bypass_deliberation: false,
886            },
887            span: Span::default(),
888        };
889
890        let mut gen = PluralityCodeGen::new("test");
891        let output = gen.generate_switch_expr(&expr);
892
893        assert!(output.contains("propose_switch"));
894        assert!(output.contains("Beleth"));
895    }
896
897    #[test]
898    fn test_generate_alter_block() {
899        let block = AlterBlock {
900            alter: AlterExpr::Named(mock_ident("Abaddon")),
901            body: crate::ast::Block {
902                stmts: Vec::new(),
903                expr: None,
904            },
905            span: Span::default(),
906        };
907
908        let mut gen = PluralityCodeGen::new("test");
909        let output = gen.generate_alter_block(&block);
910
911        assert!(output.contains("enter_alter"));
912        assert!(output.contains("Abaddon"));
913    }
914}