oxur_lang/
core_forms.rs

1//! Core Forms - The Intermediate Representation (IR)
2//!
3//! Core Forms are canonical S-expressions that serve as the stable contract
4//! between compilation stages. After macro expansion and desugaring, all Oxur
5//! code is represented in these forms.
6
7// Re-export NodeId from oxur-smap for source mapping
8pub use oxur_smap::NodeId;
9
10/// Core Forms - canonical S-expressions after expansion
11#[derive(Debug, Clone)]
12pub enum CoreForm {
13    // Literals
14    Symbol {
15        id: NodeId,
16        name: String,
17    },
18    Number {
19        id: NodeId,
20        value: i64,
21    },
22    String {
23        id: NodeId,
24        value: String,
25    },
26
27    // Compound forms
28    List {
29        id: NodeId,
30        elements: Vec<CoreForm>,
31    },
32
33    // Core language constructs (to be expanded)
34    DefineFunc {
35        id: NodeId,
36        name: String,
37        params: Vec<String>,
38        body: Box<CoreForm>,
39    },
40
41    IfExpr {
42        id: NodeId,
43        condition: Box<CoreForm>,
44        then_branch: Box<CoreForm>,
45        else_branch: Option<Box<CoreForm>>,
46    },
47
48    MatchExpr {
49        id: NodeId,
50        scrutinee: Box<CoreForm>,
51        arms: Vec<MatchArm>,
52    },
53}
54
55#[derive(Debug, Clone)]
56pub struct MatchArm {
57    pub pattern: CoreForm,
58    pub body: CoreForm,
59}
60
61impl CoreForm {
62    pub fn node_id(&self) -> NodeId {
63        match self {
64            CoreForm::Symbol { id, .. } => *id,
65            CoreForm::Number { id, .. } => *id,
66            CoreForm::String { id, .. } => *id,
67            CoreForm::List { id, .. } => *id,
68            CoreForm::DefineFunc { id, .. } => *id,
69            CoreForm::IfExpr { id, .. } => *id,
70            CoreForm::MatchExpr { id, .. } => *id,
71        }
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn test_node_id() {
81        let id = NodeId::from_raw(42);
82        assert_eq!(id.as_raw(), 42);
83    }
84
85    #[test]
86    fn test_core_form_node_id() {
87        let form = CoreForm::Number { id: NodeId::from_raw(1), value: 42 };
88        assert_eq!(form.node_id().as_raw(), 1);
89    }
90
91    #[test]
92    fn test_node_id_display() {
93        let id = NodeId::from_raw(123);
94        assert_eq!(format!("{}", id), "NodeId(123)");
95    }
96
97    #[test]
98    fn test_node_id_equality() {
99        let id1 = NodeId::from_raw(42);
100        let id2 = NodeId::from_raw(42);
101        let id3 = NodeId::from_raw(43);
102        assert_eq!(id1, id2);
103        assert_ne!(id1, id3);
104    }
105
106    #[test]
107    fn test_symbol_node_id() {
108        let form = CoreForm::Symbol { id: NodeId::from_raw(10), name: "foo".to_string() };
109        assert_eq!(form.node_id().as_raw(), 10);
110    }
111
112    #[test]
113    fn test_string_node_id() {
114        let form = CoreForm::String { id: NodeId::from_raw(20), value: "hello".to_string() };
115        assert_eq!(form.node_id().as_raw(), 20);
116    }
117
118    #[test]
119    fn test_list_node_id() {
120        let form = CoreForm::List { id: NodeId::from_raw(30), elements: vec![] };
121        assert_eq!(form.node_id().as_raw(), 30);
122    }
123
124    #[test]
125    fn test_define_func_node_id() {
126        let form = CoreForm::DefineFunc {
127            id: NodeId::from_raw(40),
128            name: "test".to_string(),
129            params: vec![],
130            body: Box::new(CoreForm::Number { id: NodeId::from_raw(41), value: 0 }),
131        };
132        assert_eq!(form.node_id().as_raw(), 40);
133    }
134
135    #[test]
136    fn test_if_expr_node_id() {
137        let form = CoreForm::IfExpr {
138            id: NodeId::from_raw(50),
139            condition: Box::new(CoreForm::Symbol {
140                id: NodeId::from_raw(51),
141                name: "true".to_string(),
142            }),
143            then_branch: Box::new(CoreForm::Number { id: NodeId::from_raw(52), value: 1 }),
144            else_branch: None,
145        };
146        assert_eq!(form.node_id().as_raw(), 50);
147    }
148
149    #[test]
150    fn test_match_expr_node_id() {
151        let form = CoreForm::MatchExpr {
152            id: NodeId::from_raw(60),
153            scrutinee: Box::new(CoreForm::Symbol {
154                id: NodeId::from_raw(61),
155                name: "x".to_string(),
156            }),
157            arms: vec![],
158        };
159        assert_eq!(form.node_id().as_raw(), 60);
160    }
161
162    #[test]
163    fn test_match_arm_creation() {
164        let arm = MatchArm {
165            pattern: CoreForm::Symbol { id: NodeId::from_raw(70), name: "pattern".to_string() },
166            body: CoreForm::Number { id: NodeId::from_raw(71), value: 42 },
167        };
168        assert_eq!(arm.pattern.node_id().as_raw(), 70);
169        assert_eq!(arm.body.node_id().as_raw(), 71);
170    }
171}