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
7use serde::{Deserialize, Serialize};
8
9/// Unique identifier for AST nodes, used for source mapping
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub struct NodeId(pub u64);
12
13impl NodeId {
14    pub fn new(id: u64) -> Self {
15        Self(id)
16    }
17}
18
19impl std::fmt::Display for NodeId {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        write!(f, "#{}", self.0)
22    }
23}
24
25/// Core Forms - canonical S-expressions after expansion
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub enum CoreForm {
28    // Literals
29    Symbol {
30        id: NodeId,
31        name: String,
32    },
33    Number {
34        id: NodeId,
35        value: i64,
36    },
37    String {
38        id: NodeId,
39        value: String,
40    },
41
42    // Compound forms
43    List {
44        id: NodeId,
45        elements: Vec<CoreForm>,
46    },
47
48    // Core language constructs (to be expanded)
49    DefineFunc {
50        id: NodeId,
51        name: String,
52        params: Vec<String>,
53        body: Box<CoreForm>,
54    },
55
56    IfExpr {
57        id: NodeId,
58        condition: Box<CoreForm>,
59        then_branch: Box<CoreForm>,
60        else_branch: Option<Box<CoreForm>>,
61    },
62
63    MatchExpr {
64        id: NodeId,
65        scrutinee: Box<CoreForm>,
66        arms: Vec<MatchArm>,
67    },
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct MatchArm {
72    pub pattern: CoreForm,
73    pub body: CoreForm,
74}
75
76impl CoreForm {
77    pub fn node_id(&self) -> NodeId {
78        match self {
79            CoreForm::Symbol { id, .. } => *id,
80            CoreForm::Number { id, .. } => *id,
81            CoreForm::String { id, .. } => *id,
82            CoreForm::List { id, .. } => *id,
83            CoreForm::DefineFunc { id, .. } => *id,
84            CoreForm::IfExpr { id, .. } => *id,
85            CoreForm::MatchExpr { id, .. } => *id,
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn test_node_id() {
96        let id = NodeId::new(42);
97        assert_eq!(id.0, 42);
98    }
99
100    #[test]
101    fn test_core_form_node_id() {
102        let form = CoreForm::Number { id: NodeId::new(1), value: 42 };
103        assert_eq!(form.node_id().0, 1);
104    }
105
106    #[test]
107    fn test_node_id_display() {
108        let id = NodeId::new(123);
109        assert_eq!(format!("{}", id), "#123");
110    }
111
112    #[test]
113    fn test_node_id_equality() {
114        let id1 = NodeId::new(42);
115        let id2 = NodeId::new(42);
116        let id3 = NodeId::new(43);
117        assert_eq!(id1, id2);
118        assert_ne!(id1, id3);
119    }
120
121    #[test]
122    fn test_symbol_node_id() {
123        let form = CoreForm::Symbol { id: NodeId::new(10), name: "foo".to_string() };
124        assert_eq!(form.node_id().0, 10);
125    }
126
127    #[test]
128    fn test_string_node_id() {
129        let form = CoreForm::String { id: NodeId::new(20), value: "hello".to_string() };
130        assert_eq!(form.node_id().0, 20);
131    }
132
133    #[test]
134    fn test_list_node_id() {
135        let form = CoreForm::List { id: NodeId::new(30), elements: vec![] };
136        assert_eq!(form.node_id().0, 30);
137    }
138
139    #[test]
140    fn test_define_func_node_id() {
141        let form = CoreForm::DefineFunc {
142            id: NodeId::new(40),
143            name: "test".to_string(),
144            params: vec![],
145            body: Box::new(CoreForm::Number { id: NodeId::new(41), value: 0 }),
146        };
147        assert_eq!(form.node_id().0, 40);
148    }
149
150    #[test]
151    fn test_if_expr_node_id() {
152        let form = CoreForm::IfExpr {
153            id: NodeId::new(50),
154            condition: Box::new(CoreForm::Symbol { id: NodeId::new(51), name: "true".to_string() }),
155            then_branch: Box::new(CoreForm::Number { id: NodeId::new(52), value: 1 }),
156            else_branch: None,
157        };
158        assert_eq!(form.node_id().0, 50);
159    }
160
161    #[test]
162    fn test_match_expr_node_id() {
163        let form = CoreForm::MatchExpr {
164            id: NodeId::new(60),
165            scrutinee: Box::new(CoreForm::Symbol { id: NodeId::new(61), name: "x".to_string() }),
166            arms: vec![],
167        };
168        assert_eq!(form.node_id().0, 60);
169    }
170
171    #[test]
172    fn test_match_arm_creation() {
173        let arm = MatchArm {
174            pattern: CoreForm::Symbol { id: NodeId::new(70), name: "pattern".to_string() },
175            body: CoreForm::Number { id: NodeId::new(71), value: 42 },
176        };
177        assert_eq!(arm.pattern.node_id().0, 70);
178        assert_eq!(arm.body.node_id().0, 71);
179    }
180}