decy_hir/lib.rs
1//! High-level Intermediate Representation for C-to-Rust transpilation.
2//!
3//! The HIR is a Rust-oriented representation that bridges C AST and Rust code generation.
4//!
5//! # Examples
6//!
7//! ```
8//! use decy_hir::{HirFunction, HirType, HirParameter};
9//!
10//! // Create a simple HIR function
11//! let func = HirFunction::new(
12//! "main".to_string(),
13//! HirType::Int,
14//! vec![],
15//! );
16//!
17//! assert_eq!(func.name(), "main");
18//! assert_eq!(func.return_type(), &HirType::Int);
19//! ```
20
21#![warn(missing_docs)]
22#![warn(clippy::all)]
23#![deny(unsafe_code)]
24
25/// Represents a C type in HIR.
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum HirType {
28 /// void type
29 Void,
30 /// C99 _Bool type (maps to bool in Rust)
31 Bool,
32 /// int type (maps to i32 in Rust)
33 Int,
34 /// unsigned int type (maps to u32 in Rust) - DECY-158
35 UnsignedInt,
36 /// float type (maps to f32 in Rust)
37 Float,
38 /// double type (maps to f64 in Rust)
39 Double,
40 /// char type (maps to u8 in Rust)
41 Char,
42 /// signed char type (maps to i8 in Rust) - DECY-250
43 SignedChar,
44 /// Pointer to another type
45 Pointer(Box<HirType>),
46 /// Boxed type (Rust `Box<T>`)
47 Box(Box<HirType>),
48 /// Vec type (Rust `Vec<T>`)
49 Vec(Box<HirType>),
50 /// Option type (Rust `Option<T>`) - for NULL pointer handling
51 Option(Box<HirType>),
52 /// Reference type (Rust `&T` or `&mut T`)
53 Reference {
54 /// Inner type
55 inner: Box<HirType>,
56 /// Whether the reference is mutable
57 mutable: bool,
58 },
59 /// Struct type (by name)
60 Struct(String),
61 /// Enum type (by name)
62 Enum(String),
63 /// Union type with named fields
64 Union(Vec<(String, HirType)>),
65 /// Array type with optional size (fixed-size or unsized)
66 Array {
67 /// Element type
68 element_type: Box<HirType>,
69 /// Optional size (None for unsized arrays like function parameters)
70 size: Option<usize>,
71 },
72 /// Function pointer type (Rust `fn` type)
73 FunctionPointer {
74 /// Parameter types
75 param_types: Vec<HirType>,
76 /// Return type
77 return_type: Box<HirType>,
78 },
79 /// String literal type (maps to &str in Rust)
80 StringLiteral,
81 /// Owned string type (maps to String in Rust)
82 OwnedString,
83 /// String reference type (maps to &str in Rust)
84 StringReference,
85 /// Type alias (typedef) - preserves the alias name for codegen
86 /// DECY-172: Used for size_t, ssize_t, ptrdiff_t, etc.
87 TypeAlias(String),
88}
89
90impl HirType {
91 /// Convert from parser AST type to HIR type.
92 ///
93 /// # Examples
94 ///
95 /// ```
96 /// use decy_hir::HirType;
97 /// use decy_parser::parser::Type;
98 ///
99 /// let hir_type = HirType::from_ast_type(&Type::Int);
100 /// assert_eq!(hir_type, HirType::Int);
101 /// ```
102 pub fn from_ast_type(ast_type: &decy_parser::parser::Type) -> Self {
103 use decy_parser::parser::Type;
104 match ast_type {
105 Type::Void => HirType::Void,
106 Type::Bool => HirType::Bool,
107 Type::Int => HirType::Int,
108 Type::UnsignedInt => HirType::UnsignedInt, // DECY-158
109 Type::Float => HirType::Float,
110 Type::Double => HirType::Double,
111 Type::Char => HirType::Char,
112 Type::SignedChar => HirType::SignedChar, // DECY-250
113 Type::Pointer(inner) => HirType::Pointer(Box::new(HirType::from_ast_type(inner))),
114 Type::Struct(name) => HirType::Struct(name.clone()),
115 Type::FunctionPointer {
116 param_types,
117 return_type,
118 } => HirType::FunctionPointer {
119 param_types: param_types.iter().map(HirType::from_ast_type).collect(),
120 return_type: Box::new(HirType::from_ast_type(return_type)),
121 },
122 Type::Array { element_type, size } => HirType::Array {
123 element_type: Box::new(HirType::from_ast_type(element_type)),
124 size: size.map(|s| s as usize),
125 },
126 // DECY-172: Preserve type aliases like size_t, ssize_t, ptrdiff_t
127 Type::TypeAlias(name) => HirType::TypeAlias(name.clone()),
128 }
129 }
130}
131
132/// Represents a struct field in HIR.
133#[derive(Debug, Clone, PartialEq, Eq)]
134pub struct HirStructField {
135 name: String,
136 field_type: HirType,
137}
138
139impl HirStructField {
140 /// Create a new struct field.
141 pub fn new(name: String, field_type: HirType) -> Self {
142 Self { name, field_type }
143 }
144
145 /// Get the field name.
146 pub fn name(&self) -> &str {
147 &self.name
148 }
149
150 /// Get the field type.
151 pub fn field_type(&self) -> &HirType {
152 &self.field_type
153 }
154}
155
156/// Represents a struct definition in HIR.
157#[derive(Debug, Clone, PartialEq, Eq)]
158pub struct HirStruct {
159 name: String,
160 fields: Vec<HirStructField>,
161}
162
163impl HirStruct {
164 /// Create a new struct.
165 pub fn new(name: String, fields: Vec<HirStructField>) -> Self {
166 Self { name, fields }
167 }
168
169 /// Get the struct name.
170 pub fn name(&self) -> &str {
171 &self.name
172 }
173
174 /// Get the struct fields.
175 pub fn fields(&self) -> &[HirStructField] {
176 &self.fields
177 }
178}
179
180/// Represents an enum variant in HIR.
181#[derive(Debug, Clone, PartialEq, Eq)]
182pub struct HirEnumVariant {
183 name: String,
184 value: Option<i32>,
185}
186
187impl HirEnumVariant {
188 /// Create a new enum variant.
189 pub fn new(name: String, value: Option<i32>) -> Self {
190 Self { name, value }
191 }
192
193 /// Get the variant name.
194 pub fn name(&self) -> &str {
195 &self.name
196 }
197
198 /// Get the variant value.
199 pub fn value(&self) -> Option<i32> {
200 self.value
201 }
202}
203
204/// Represents an enum definition in HIR.
205#[derive(Debug, Clone, PartialEq, Eq)]
206pub struct HirEnum {
207 name: String,
208 variants: Vec<HirEnumVariant>,
209}
210
211impl HirEnum {
212 /// Create a new enum.
213 pub fn new(name: String, variants: Vec<HirEnumVariant>) -> Self {
214 Self { name, variants }
215 }
216
217 /// Get the enum name.
218 pub fn name(&self) -> &str {
219 &self.name
220 }
221
222 /// Get the enum variants.
223 pub fn variants(&self) -> &[HirEnumVariant] {
224 &self.variants
225 }
226}
227
228/// Represents a typedef (type alias) in HIR.
229#[derive(Debug, Clone, PartialEq, Eq)]
230pub struct HirTypedef {
231 name: String,
232 underlying_type: HirType,
233}
234
235impl HirTypedef {
236 /// Create a new typedef.
237 ///
238 /// # Examples
239 ///
240 /// ```
241 /// use decy_hir::{HirTypedef, HirType};
242 ///
243 /// let typedef = HirTypedef::new("Integer".to_string(), HirType::Int);
244 /// assert_eq!(typedef.name(), "Integer");
245 /// assert_eq!(typedef.underlying_type(), &HirType::Int);
246 /// ```
247 pub fn new(name: String, underlying_type: HirType) -> Self {
248 Self {
249 name,
250 underlying_type,
251 }
252 }
253
254 /// Get the typedef name.
255 pub fn name(&self) -> &str {
256 &self.name
257 }
258
259 /// Get the underlying type.
260 pub fn underlying_type(&self) -> &HirType {
261 &self.underlying_type
262 }
263}
264
265/// Represents a constant declaration in HIR (from C #define or const).
266///
267/// # Examples
268///
269/// ```
270/// use decy_hir::{HirConstant, HirType, HirExpression};
271///
272/// let constant = HirConstant::new(
273/// "MAX".to_string(),
274/// HirType::Int,
275/// HirExpression::IntLiteral(100),
276/// );
277/// assert_eq!(constant.name(), "MAX");
278/// assert_eq!(constant.const_type(), &HirType::Int);
279/// ```
280#[derive(Debug, Clone, PartialEq)]
281pub struct HirConstant {
282 name: String,
283 const_type: HirType,
284 value: HirExpression,
285}
286
287impl HirConstant {
288 /// Create a new constant.
289 ///
290 /// # Examples
291 ///
292 /// ```
293 /// use decy_hir::{HirConstant, HirType, HirExpression};
294 ///
295 /// let constant = HirConstant::new(
296 /// "PI".to_string(),
297 /// HirType::Float,
298 /// HirExpression::IntLiteral(3), // Simplified for example
299 /// );
300 /// assert_eq!(constant.name(), "PI");
301 /// ```
302 pub fn new(name: String, const_type: HirType, value: HirExpression) -> Self {
303 Self {
304 name,
305 const_type,
306 value,
307 }
308 }
309
310 /// Get the constant name.
311 pub fn name(&self) -> &str {
312 &self.name
313 }
314
315 /// Get the constant type.
316 pub fn const_type(&self) -> &HirType {
317 &self.const_type
318 }
319
320 /// Get the constant value expression.
321 pub fn value(&self) -> &HirExpression {
322 &self.value
323 }
324}
325
326/// Represents a C macro definition in HIR.
327///
328/// C macros come in two forms:
329/// - Object-like: `#define MAX 100` (simple text substitution)
330/// - Function-like: `#define SQR(x) ((x) * (x))` (macro with parameters)
331///
332/// In Rust, these typically become:
333/// - Object-like → `const` declarations
334/// - Function-like → `fn` (inline functions) or `macro_rules!` (if truly necessary)
335///
336/// # Examples
337///
338/// ```
339/// use decy_hir::{HirMacroDefinition, HirType, HirExpression};
340///
341/// // Object-like macro: #define MAX 100
342/// let macro_def = HirMacroDefinition::new_object_like(
343/// "MAX".to_string(),
344/// "100".to_string(),
345/// );
346/// assert_eq!(macro_def.name(), "MAX");
347/// assert!(!macro_def.is_function_like());
348///
349/// // Function-like macro: #define SQR(x) ((x) * (x))
350/// let macro_def2 = HirMacroDefinition::new_function_like(
351/// "SQR".to_string(),
352/// vec!["x".to_string()],
353/// "((x) * (x))".to_string(),
354/// );
355/// assert_eq!(macro_def2.name(), "SQR");
356/// assert!(macro_def2.is_function_like());
357/// assert_eq!(macro_def2.parameters(), &["x"]);
358/// ```
359///
360/// Reference: K&R §4.11, ISO C99 §6.10.3
361#[derive(Debug, Clone, PartialEq, Eq)]
362pub struct HirMacroDefinition {
363 /// Macro name (e.g., "MAX", "SQR")
364 name: String,
365 /// Parameters for function-like macros (empty for object-like macros)
366 parameters: Vec<String>,
367 /// Macro body as a token string (unparsed)
368 /// This will be expanded during codegen
369 body: String,
370}
371
372impl HirMacroDefinition {
373 /// Create a new object-like macro (no parameters).
374 ///
375 /// # Examples
376 ///
377 /// ```
378 /// use decy_hir::HirMacroDefinition;
379 ///
380 /// // #define MAX 100
381 /// let macro_def = HirMacroDefinition::new_object_like(
382 /// "MAX".to_string(),
383 /// "100".to_string(),
384 /// );
385 /// assert_eq!(macro_def.name(), "MAX");
386 /// assert!(!macro_def.is_function_like());
387 /// ```
388 pub fn new_object_like(name: String, body: String) -> Self {
389 Self {
390 name,
391 parameters: vec![],
392 body,
393 }
394 }
395
396 /// Create a new function-like macro (with parameters).
397 ///
398 /// # Examples
399 ///
400 /// ```
401 /// use decy_hir::HirMacroDefinition;
402 ///
403 /// // #define SQR(x) ((x) * (x))
404 /// let macro_def = HirMacroDefinition::new_function_like(
405 /// "SQR".to_string(),
406 /// vec!["x".to_string()],
407 /// "((x) * (x))".to_string(),
408 /// );
409 /// assert_eq!(macro_def.name(), "SQR");
410 /// assert!(macro_def.is_function_like());
411 /// assert_eq!(macro_def.parameters(), &["x"]);
412 /// ```
413 pub fn new_function_like(name: String, parameters: Vec<String>, body: String) -> Self {
414 Self {
415 name,
416 parameters,
417 body,
418 }
419 }
420
421 /// Get the macro name.
422 pub fn name(&self) -> &str {
423 &self.name
424 }
425
426 /// Get the macro parameters (empty for object-like macros).
427 pub fn parameters(&self) -> &[String] {
428 &self.parameters
429 }
430
431 /// Get the macro body (unparsed token string).
432 pub fn body(&self) -> &str {
433 &self.body
434 }
435
436 /// Check if this is a function-like macro (has parameters).
437 pub fn is_function_like(&self) -> bool {
438 !self.parameters.is_empty()
439 }
440
441 /// Check if this is an object-like macro (no parameters).
442 pub fn is_object_like(&self) -> bool {
443 self.parameters.is_empty()
444 }
445}
446
447/// Represents a function parameter in HIR.
448#[derive(Debug, Clone, PartialEq, Eq)]
449pub struct HirParameter {
450 name: String,
451 param_type: HirType,
452 /// DECY-135: Whether the pointee type is const (for pointer params like `const char*`)
453 is_pointee_const: bool,
454}
455
456impl HirParameter {
457 /// Create a new HIR parameter.
458 ///
459 /// # Examples
460 ///
461 /// ```
462 /// use decy_hir::{HirParameter, HirType};
463 ///
464 /// let param = HirParameter::new("x".to_string(), HirType::Int);
465 /// assert_eq!(param.name(), "x");
466 /// ```
467 pub fn new(name: String, param_type: HirType) -> Self {
468 Self {
469 name,
470 param_type,
471 is_pointee_const: false,
472 }
473 }
474
475 /// Get the parameter name.
476 pub fn name(&self) -> &str {
477 &self.name
478 }
479
480 /// Get the parameter type.
481 pub fn param_type(&self) -> &HirType {
482 &self.param_type
483 }
484
485 /// DECY-135: Check if this parameter has a const-qualified pointee.
486 pub fn is_pointee_const(&self) -> bool {
487 self.is_pointee_const
488 }
489
490 /// DECY-135: Check if this is a const char* parameter (should become &str).
491 pub fn is_const_char_pointer(&self) -> bool {
492 self.is_pointee_const
493 && matches!(self.param_type, HirType::Pointer(ref inner) if **inner == HirType::Char)
494 }
495
496 /// Convert from parser AST parameter to HIR parameter.
497 pub fn from_ast_parameter(ast_param: &decy_parser::parser::Parameter) -> Self {
498 Self {
499 name: ast_param.name.clone(),
500 param_type: HirType::from_ast_type(&ast_param.param_type),
501 is_pointee_const: ast_param.is_pointee_const,
502 }
503 }
504
505 /// DECY-135: Create a new parameter with a transformed type but preserving is_pointee_const.
506 /// Use this when transforming parameter types (e.g., pointer to reference) to maintain
507 /// const char* → &str transformation capability.
508 pub fn with_type(&self, new_type: HirType) -> Self {
509 Self {
510 name: self.name.clone(),
511 param_type: new_type,
512 is_pointee_const: self.is_pointee_const,
513 }
514 }
515}
516
517/// Represents a function in HIR.
518#[derive(Debug, Clone, PartialEq)]
519pub struct HirFunction {
520 name: String,
521 return_type: HirType,
522 parameters: Vec<HirParameter>,
523 body: Option<Vec<HirStatement>>,
524}
525
526impl HirFunction {
527 /// Create a new HIR function.
528 ///
529 /// # Examples
530 ///
531 /// ```
532 /// use decy_hir::{HirFunction, HirType, HirParameter};
533 ///
534 /// let func = HirFunction::new(
535 /// "add".to_string(),
536 /// HirType::Int,
537 /// vec![
538 /// HirParameter::new("a".to_string(), HirType::Int),
539 /// HirParameter::new("b".to_string(), HirType::Int),
540 /// ],
541 /// );
542 ///
543 /// assert_eq!(func.name(), "add");
544 /// assert_eq!(func.parameters().len(), 2);
545 /// ```
546 pub fn new(name: String, return_type: HirType, parameters: Vec<HirParameter>) -> Self {
547 Self {
548 name,
549 return_type,
550 parameters,
551 body: None,
552 }
553 }
554
555 /// Get the function name.
556 pub fn name(&self) -> &str {
557 &self.name
558 }
559
560 /// Get the return type.
561 pub fn return_type(&self) -> &HirType {
562 &self.return_type
563 }
564
565 /// Get the parameters.
566 pub fn parameters(&self) -> &[HirParameter] {
567 &self.parameters
568 }
569
570 /// Convert from parser AST function to HIR function.
571 ///
572 /// # Examples
573 ///
574 /// ```
575 /// use decy_hir::HirFunction;
576 /// use decy_parser::parser::{Function, Type, Parameter};
577 ///
578 /// let ast_func = Function::new(
579 /// "test".to_string(),
580 /// Type::Void,
581 /// vec![],
582 /// );
583 ///
584 /// let hir_func = HirFunction::from_ast_function(&ast_func);
585 /// assert_eq!(hir_func.name(), "test");
586 /// ```
587 pub fn from_ast_function(ast_func: &decy_parser::parser::Function) -> Self {
588 let body = if ast_func.body.is_empty() {
589 None
590 } else {
591 Some(
592 ast_func
593 .body
594 .iter()
595 .map(HirStatement::from_ast_statement)
596 .collect(),
597 )
598 };
599
600 Self {
601 name: ast_func.name.clone(),
602 return_type: HirType::from_ast_type(&ast_func.return_type),
603 parameters: ast_func
604 .parameters
605 .iter()
606 .map(HirParameter::from_ast_parameter)
607 .collect(),
608 body,
609 }
610 }
611
612 /// Create a new HIR function with a body.
613 ///
614 /// # Examples
615 ///
616 /// ```
617 /// use decy_hir::{HirFunction, HirType, HirStatement, HirExpression};
618 ///
619 /// let func = HirFunction::new_with_body(
620 /// "test".to_string(),
621 /// HirType::Int,
622 /// vec![],
623 /// vec![
624 /// HirStatement::VariableDeclaration {
625 /// name: "x".to_string(),
626 /// var_type: HirType::Int,
627 /// initializer: Some(HirExpression::IntLiteral(5)),
628 /// },
629 /// HirStatement::Return(Some(HirExpression::Variable("x".to_string()))),
630 /// ],
631 /// );
632 ///
633 /// assert_eq!(func.name(), "test");
634 /// assert_eq!(func.body().len(), 2);
635 /// ```
636 pub fn new_with_body(
637 name: String,
638 return_type: HirType,
639 parameters: Vec<HirParameter>,
640 body: Vec<HirStatement>,
641 ) -> Self {
642 Self {
643 name,
644 return_type,
645 parameters,
646 body: Some(body),
647 }
648 }
649
650 /// Get the function body.
651 pub fn body(&self) -> &[HirStatement] {
652 self.body.as_deref().unwrap_or(&[])
653 }
654
655 /// DECY-190: Check if this function has a body (is a definition, not just a declaration).
656 /// Returns true for definitions, false for forward declarations/prototypes.
657 pub fn has_body(&self) -> bool {
658 self.body.is_some()
659 }
660}
661
662/// Unary operators for expressions.
663#[derive(Debug, Clone, Copy, PartialEq, Eq)]
664pub enum UnaryOperator {
665 /// Unary minus (-x)
666 Minus,
667 /// Logical NOT (!x)
668 LogicalNot,
669 /// Bitwise NOT (~x)
670 BitwiseNot,
671 /// Address-of (&x)
672 AddressOf,
673 /// Post-increment (x++)
674 /// Returns old value, then increments
675 /// Reference: ISO C99 §6.5.2.4, K&R §2.8
676 PostIncrement,
677 /// Post-decrement (x--)
678 /// Returns old value, then decrements
679 /// Reference: ISO C99 §6.5.2.4, K&R §2.8
680 PostDecrement,
681 /// Pre-increment (++x)
682 /// Increments first, then returns new value
683 /// Reference: ISO C99 §6.5.3.1, K&R §2.8
684 PreIncrement,
685 /// Pre-decrement (--x)
686 /// Decrements first, then returns new value
687 /// Reference: ISO C99 §6.5.3.1, K&R §2.8
688 PreDecrement,
689}
690
691/// Binary operators for expressions.
692#[derive(Debug, Clone, Copy, PartialEq, Eq)]
693pub enum BinaryOperator {
694 /// Addition (+)
695 Add,
696 /// Subtraction (-)
697 Subtract,
698 /// Multiplication (*)
699 Multiply,
700 /// Division (/)
701 Divide,
702 /// Modulo (%)
703 Modulo,
704 /// Equality (==)
705 Equal,
706 /// Inequality (!=)
707 NotEqual,
708 /// Less than (<)
709 LessThan,
710 /// Greater than (>)
711 GreaterThan,
712 /// Less than or equal (<=)
713 LessEqual,
714 /// Greater than or equal (>=)
715 GreaterEqual,
716 /// Logical AND (&&)
717 LogicalAnd,
718 /// Logical OR (||)
719 LogicalOr,
720 /// Left shift (<<)
721 LeftShift,
722 /// Right shift (>>)
723 RightShift,
724 /// Bitwise AND (&)
725 BitwiseAnd,
726 /// Bitwise OR (|)
727 BitwiseOr,
728 /// Bitwise XOR (^)
729 BitwiseXor,
730 /// Assignment (=) - for embedded assignments like (c=getchar())
731 Assign,
732 /// Comma operator (,) - DECY-224: for multi-expression statements
733 Comma,
734}
735
736/// Represents an expression in HIR.
737#[derive(Debug, Clone, PartialEq)]
738pub enum HirExpression {
739 /// Integer literal
740 IntLiteral(i32),
741 /// Float literal (stored as string to preserve precision)
742 FloatLiteral(String),
743 /// String literal (C: "hello" → Rust: "hello")
744 StringLiteral(String),
745 /// Character literal (C: 'a', '\0', '\n' → Rust: b'a', 0, b'\n')
746 CharLiteral(i8),
747 /// Variable reference
748 Variable(String),
749 /// Binary operation (left op right)
750 BinaryOp {
751 /// The operator
752 op: BinaryOperator,
753 /// Left operand
754 left: Box<HirExpression>,
755 /// Right operand
756 right: Box<HirExpression>,
757 },
758 /// Dereference operation (*ptr)
759 Dereference(Box<HirExpression>),
760 /// Address-of operation (&x)
761 AddressOf(Box<HirExpression>),
762 /// Unary operation (-x, !x)
763 UnaryOp {
764 /// The operator
765 op: UnaryOperator,
766 /// Operand
767 operand: Box<HirExpression>,
768 },
769 /// Post-increment expression (x++)
770 /// C semantics: returns old value, then increments
771 /// Rust: { let tmp = x; x += 1; tmp }
772 PostIncrement {
773 /// The operand to increment
774 operand: Box<HirExpression>,
775 },
776 /// Pre-increment expression (++x)
777 /// C semantics: increments first, then returns new value
778 /// Rust: { x += 1; x }
779 PreIncrement {
780 /// The operand to increment
781 operand: Box<HirExpression>,
782 },
783 /// Post-decrement expression (x--)
784 /// C semantics: returns old value, then decrements
785 /// Rust: { let tmp = x; x -= 1; tmp }
786 PostDecrement {
787 /// The operand to decrement
788 operand: Box<HirExpression>,
789 },
790 /// Pre-decrement expression (--x)
791 /// C semantics: decrements first, then returns new value
792 /// Rust: { x -= 1; x }
793 PreDecrement {
794 /// The operand to decrement
795 operand: Box<HirExpression>,
796 },
797 /// Function call (function_name(args...))
798 FunctionCall {
799 /// Function name
800 function: String,
801 /// Arguments
802 arguments: Vec<HirExpression>,
803 },
804 /// Field access (obj.field)
805 FieldAccess {
806 /// Object expression
807 object: Box<HirExpression>,
808 /// Field name
809 field: String,
810 },
811 /// Pointer field access (ptr->field)
812 PointerFieldAccess {
813 /// Pointer expression
814 pointer: Box<HirExpression>,
815 /// Field name
816 field: String,
817 },
818 /// Array indexing
819 ArrayIndex {
820 /// Array expression
821 array: Box<HirExpression>,
822 /// Index expression
823 index: Box<HirExpression>,
824 },
825 /// Safe slice indexing (DECY-069)
826 /// Represents safe, bounds-checked array access: `arr[index]`
827 /// Generated when pointer arithmetic can be transformed to safe indexing.
828 /// Unlike ArrayIndex (which may use unsafe), SliceIndex guarantees zero unsafe blocks.
829 SliceIndex {
830 /// Slice/array expression
831 slice: Box<HirExpression>,
832 /// Index expression
833 index: Box<HirExpression>,
834 /// Element type (for codegen type inference)
835 element_type: HirType,
836 },
837 /// Sizeof expression (sizeof(T) → `std::mem::size_of::<T>()`)
838 Sizeof {
839 /// Type name as a string (e.g., "int", "struct Data")
840 type_name: String,
841 },
842 /// NULL literal (NULL → None)
843 NullLiteral,
844 /// NULL check expression (p != NULL → is_some(), p == NULL → is_none())
845 IsNotNull(Box<HirExpression>),
846 /// calloc(count, sizeof(T)) → vec![0; count]
847 Calloc {
848 /// Number of elements to allocate
849 count: Box<HirExpression>,
850 /// Element type
851 element_type: Box<HirType>,
852 },
853 /// malloc(size) → Box::new(default) or Vec::with_capacity(n)
854 Malloc {
855 /// Size expression (typically sizeof(T) or n * sizeof(T))
856 size: Box<HirExpression>,
857 },
858 /// realloc(ptr, new_size) → Vec::resize or Vec::reserve
859 Realloc {
860 /// Pointer to reallocate
861 pointer: Box<HirExpression>,
862 /// New size expression (typically n * sizeof(T))
863 new_size: Box<HirExpression>,
864 },
865 /// String method call (e.g., s.len(), s.to_string(), s.clone_into(&mut dst))
866 StringMethodCall {
867 /// Receiver expression (e.g., s in s.len())
868 receiver: Box<HirExpression>,
869 /// Method name (e.g., "len", "to_string", "clone_into")
870 method: String,
871 /// Arguments to the method call
872 arguments: Vec<HirExpression>,
873 },
874 /// Cast expression (C: (int)x → Rust: x as i32)
875 ///
876 /// Represents a C-style cast that converts an expression to a target type.
877 /// Maps to Rust `as` operator for safe casts, or `transmute` for unsafe casts.
878 ///
879 /// # Sprint 19 Feature (DECY-059)
880 ///
881 /// Added in Sprint 19 to support C cast expressions.
882 Cast {
883 /// Target type to cast to
884 target_type: HirType,
885 /// Expression being cast
886 expr: Box<HirExpression>,
887 },
888 /// Compound literal (C: (struct Point){10, 20} → Rust: Point { x: 10, y: 20 })
889 ///
890 /// C99 compound literals create anonymous objects of a specified type.
891 /// Used for inline struct/array initialization.
892 ///
893 /// # Sprint 19 Feature (DECY-060)
894 ///
895 /// Added in Sprint 19 to support C99 compound literals.
896 CompoundLiteral {
897 /// Type of the compound literal (struct Point, int[], etc.)
898 literal_type: HirType,
899 /// Initializer expressions (values for struct fields or array elements)
900 initializers: Vec<HirExpression>,
901 },
902 /// Ternary/Conditional expression (C: cond ? then_val : else_val)
903 ///
904 /// The C ternary operator evaluates the condition and returns either
905 /// the then_val or else_val based on whether condition is truthy.
906 ///
907 /// Maps to Rust's `if cond { then_val } else { else_val }` expression.
908 ///
909 /// # DECY-192
910 ///
911 /// Added to support K&R Chapter 2.11 Conditional Expressions.
912 Ternary {
913 /// Condition expression (evaluated as boolean)
914 condition: Box<HirExpression>,
915 /// Value if condition is true
916 then_expr: Box<HirExpression>,
917 /// Value if condition is false
918 else_expr: Box<HirExpression>,
919 },
920}
921
922/// Represents a single case in a switch statement.
923#[derive(Debug, Clone, PartialEq)]
924pub struct SwitchCase {
925 /// Case value expression (None for default case)
926 pub value: Option<HirExpression>,
927 /// Statements to execute for this case
928 pub body: Vec<HirStatement>,
929}
930
931/// Represents a statement in HIR.
932#[derive(Debug, Clone, PartialEq)]
933pub enum HirStatement {
934 /// Variable declaration with optional initializer
935 VariableDeclaration {
936 /// Variable name
937 name: String,
938 /// Variable type
939 var_type: HirType,
940 /// Optional initializer expression
941 initializer: Option<HirExpression>,
942 },
943 /// Return statement with optional value
944 Return(Option<HirExpression>),
945 /// If statement with condition, then-block, and optional else-block
946 If {
947 /// Condition expression
948 condition: HirExpression,
949 /// Then block (statements to execute if condition is true)
950 then_block: Vec<HirStatement>,
951 /// Else block (optional statements to execute if condition is false)
952 else_block: Option<Vec<HirStatement>>,
953 },
954 /// While loop with condition and body
955 While {
956 /// Loop condition
957 condition: HirExpression,
958 /// Loop body (statements to execute while condition is true)
959 body: Vec<HirStatement>,
960 },
961 /// Break statement (exit loop)
962 Break,
963 /// Continue statement (skip to next iteration)
964 Continue,
965 /// Assignment statement (target = value)
966 Assignment {
967 /// Target variable name
968 target: String,
969 /// Value expression to assign
970 value: HirExpression,
971 },
972 /// For loop with optional init, condition, optional increment, and body
973 For {
974 /// Initialization statements (e.g., int i = 0, j = 10) - DECY-224
975 init: Vec<HirStatement>,
976 /// Loop condition expression (None = infinite loop, e.g. `for(;;)`)
977 condition: Option<HirExpression>,
978 /// Increment statements (e.g., i++, j--) - DECY-224
979 increment: Vec<HirStatement>,
980 /// Loop body (statements to execute while condition is true)
981 body: Vec<HirStatement>,
982 },
983 /// Switch statement with condition, cases, and optional default case
984 Switch {
985 /// Condition expression to match against
986 condition: HirExpression,
987 /// List of case statements
988 cases: Vec<SwitchCase>,
989 /// Optional default case body
990 default_case: Option<Vec<HirStatement>>,
991 },
992 /// Pointer dereference assignment (*ptr = value)
993 DerefAssignment {
994 /// Target expression to dereference
995 target: HirExpression,
996 /// Value expression to assign
997 value: HirExpression,
998 },
999 /// Array index assignment
1000 ArrayIndexAssignment {
1001 /// Array expression
1002 array: Box<HirExpression>,
1003 /// Index expression
1004 index: Box<HirExpression>,
1005 /// Value expression to assign
1006 value: HirExpression,
1007 },
1008 /// Field assignment (ptr->field = value or obj.field = value)
1009 FieldAssignment {
1010 /// Object/pointer expression
1011 object: HirExpression,
1012 /// Field name
1013 field: String,
1014 /// Value expression to assign
1015 value: HirExpression,
1016 },
1017 /// Free statement (free(ptr) → automatic drop via RAII)
1018 Free {
1019 /// Pointer expression to free
1020 pointer: HirExpression,
1021 },
1022 /// Expression statement (e.g., printf("Hello");, free(ptr);, i++;)
1023 ///
1024 /// Represents an expression used as a statement, typically for side effects.
1025 /// Common in C for function calls whose return values are discarded.
1026 ///
1027 /// # DECY-065
1028 ///
1029 /// Added to fix bug where function call statements (like printf) were being
1030 /// converted to Break placeholder, causing them to disappear during transpilation.
1031 Expression(HirExpression),
1032 /// Inline assembly statement (C: asm("...") or __asm__("..."))
1033 ///
1034 /// C inline assembly cannot be automatically transpiled to safe Rust.
1035 /// This variant preserves the assembly text for manual review.
1036 ///
1037 /// # DECY-197
1038 ///
1039 /// Added to handle inline assembly in C code. The codegen emits a
1040 /// review-required comment rather than attempting automatic translation.
1041 InlineAsm {
1042 /// The raw assembly text from the C source
1043 text: String,
1044 /// Whether the assembly might be translatable to Rust intrinsics
1045 translatable: bool,
1046 },
1047}
1048
1049impl HirStatement {
1050 /// Convert from parser AST statement to HIR statement.
1051 pub fn from_ast_statement(ast_stmt: &decy_parser::parser::Statement) -> Self {
1052 use decy_parser::parser::Statement;
1053 match ast_stmt {
1054 Statement::VariableDeclaration {
1055 name,
1056 var_type,
1057 initializer,
1058 } => HirStatement::VariableDeclaration {
1059 name: name.clone(),
1060 var_type: HirType::from_ast_type(var_type),
1061 initializer: initializer.as_ref().map(HirExpression::from_ast_expression),
1062 },
1063 Statement::Return(expr) => {
1064 HirStatement::Return(expr.as_ref().map(HirExpression::from_ast_expression))
1065 }
1066 Statement::Assignment { target, value } => HirStatement::Assignment {
1067 target: target.clone(),
1068 value: HirExpression::from_ast_expression(value),
1069 },
1070 Statement::If {
1071 condition,
1072 then_block,
1073 else_block,
1074 } => HirStatement::If {
1075 condition: HirExpression::from_ast_expression(condition),
1076 then_block: then_block
1077 .iter()
1078 .map(HirStatement::from_ast_statement)
1079 .collect(),
1080 else_block: else_block
1081 .as_ref()
1082 .map(|block| block.iter().map(HirStatement::from_ast_statement).collect()),
1083 },
1084 Statement::For {
1085 init,
1086 condition,
1087 increment,
1088 body,
1089 } => HirStatement::For {
1090 // DECY-224: Support multiple init statements
1091 init: init.iter().map(HirStatement::from_ast_statement).collect(),
1092 condition: condition
1093 .as_ref()
1094 .map(HirExpression::from_ast_expression),
1095 // DECY-224: Support multiple increment statements
1096 increment: increment
1097 .iter()
1098 .map(HirStatement::from_ast_statement)
1099 .collect(),
1100 body: body.iter().map(HirStatement::from_ast_statement).collect(),
1101 },
1102 Statement::While { condition, body } => HirStatement::While {
1103 condition: HirExpression::from_ast_expression(condition),
1104 body: body.iter().map(HirStatement::from_ast_statement).collect(),
1105 },
1106 Statement::DerefAssignment { target, value } => HirStatement::DerefAssignment {
1107 target: HirExpression::from_ast_expression(target),
1108 value: HirExpression::from_ast_expression(value),
1109 },
1110 Statement::ArrayIndexAssignment {
1111 array,
1112 index,
1113 value,
1114 } => HirStatement::ArrayIndexAssignment {
1115 array: Box::new(HirExpression::from_ast_expression(array)),
1116 index: Box::new(HirExpression::from_ast_expression(index)),
1117 value: HirExpression::from_ast_expression(value),
1118 },
1119 Statement::FieldAssignment {
1120 object,
1121 field,
1122 value,
1123 } => HirStatement::FieldAssignment {
1124 object: HirExpression::from_ast_expression(object),
1125 field: field.clone(),
1126 value: HirExpression::from_ast_expression(value),
1127 },
1128 Statement::Break => HirStatement::Break,
1129 Statement::Continue => HirStatement::Continue,
1130 // Increment/decrement statements are converted to assignments
1131 // ptr++ becomes ptr = ptr + 1
1132 Statement::PostIncrement { target } | Statement::PreIncrement { target } => {
1133 HirStatement::Assignment {
1134 target: target.clone(),
1135 value: HirExpression::BinaryOp {
1136 op: BinaryOperator::Add,
1137 left: Box::new(HirExpression::Variable(target.clone())),
1138 right: Box::new(HirExpression::IntLiteral(1)),
1139 },
1140 }
1141 }
1142 // ptr-- becomes ptr = ptr - 1
1143 Statement::PostDecrement { target } | Statement::PreDecrement { target } => {
1144 HirStatement::Assignment {
1145 target: target.clone(),
1146 value: HirExpression::BinaryOp {
1147 op: BinaryOperator::Subtract,
1148 left: Box::new(HirExpression::Variable(target.clone())),
1149 right: Box::new(HirExpression::IntLiteral(1)),
1150 },
1151 }
1152 }
1153 // Compound assignments like ptr += offset become ptr = ptr + offset
1154 Statement::CompoundAssignment { target, op, value } => HirStatement::Assignment {
1155 target: target.clone(),
1156 value: HirExpression::BinaryOp {
1157 op: convert_binary_operator(*op),
1158 left: Box::new(HirExpression::Variable(target.clone())),
1159 right: Box::new(HirExpression::from_ast_expression(value)),
1160 },
1161 },
1162 // DECY-185: Compound assignments to complex targets like *ptr *= 2, sb->capacity *= 2
1163 // These become DerefAssignment with a BinaryOp value
1164 Statement::DerefCompoundAssignment { target, op, value } => {
1165 let hir_target = HirExpression::from_ast_expression(target);
1166 HirStatement::DerefAssignment {
1167 target: hir_target.clone(),
1168 value: HirExpression::BinaryOp {
1169 op: convert_binary_operator(*op),
1170 left: Box::new(hir_target),
1171 right: Box::new(HirExpression::from_ast_expression(value)),
1172 },
1173 }
1174 }
1175 // Switch statement - convert cases and default
1176 Statement::Switch {
1177 condition,
1178 cases,
1179 default_case,
1180 } => HirStatement::Switch {
1181 condition: HirExpression::from_ast_expression(condition),
1182 cases: cases
1183 .iter()
1184 .map(|case| SwitchCase {
1185 value: case.value.as_ref().map(HirExpression::from_ast_expression),
1186 body: case
1187 .body
1188 .iter()
1189 .map(HirStatement::from_ast_statement)
1190 .collect(),
1191 })
1192 .collect(),
1193 default_case: default_case
1194 .as_ref()
1195 .map(|block| block.iter().map(HirStatement::from_ast_statement).collect()),
1196 },
1197 // Function call statement - convert to expression statement
1198 // DECY-065: Now properly supported with HirStatement::Expression variant
1199 Statement::FunctionCall {
1200 function,
1201 arguments,
1202 } => HirStatement::Expression(HirExpression::FunctionCall {
1203 function: function.clone(),
1204 arguments: arguments
1205 .iter()
1206 .map(HirExpression::from_ast_expression)
1207 .collect(),
1208 }),
1209 }
1210 }
1211}
1212
1213impl HirExpression {
1214 /// Convert from parser AST expression to HIR expression.
1215 pub fn from_ast_expression(ast_expr: &decy_parser::parser::Expression) -> Self {
1216 use decy_parser::parser::Expression;
1217 match ast_expr {
1218 Expression::IntLiteral(value) => HirExpression::IntLiteral(*value),
1219 Expression::FloatLiteral(value) => HirExpression::FloatLiteral(value.clone()),
1220 Expression::StringLiteral(value) => HirExpression::StringLiteral(value.clone()),
1221 Expression::CharLiteral(value) => HirExpression::CharLiteral(*value),
1222 Expression::Variable(name) => HirExpression::Variable(name.clone()),
1223 Expression::BinaryOp { op, left, right } => HirExpression::BinaryOp {
1224 op: convert_binary_operator(*op),
1225 left: Box::new(HirExpression::from_ast_expression(left)),
1226 right: Box::new(HirExpression::from_ast_expression(right)),
1227 },
1228 Expression::FunctionCall {
1229 function,
1230 arguments,
1231 } => HirExpression::FunctionCall {
1232 function: function.clone(),
1233 arguments: arguments
1234 .iter()
1235 .map(HirExpression::from_ast_expression)
1236 .collect(),
1237 },
1238 Expression::Dereference(inner) => {
1239 HirExpression::Dereference(Box::new(HirExpression::from_ast_expression(inner)))
1240 }
1241 Expression::UnaryOp { op, operand } => HirExpression::UnaryOp {
1242 op: convert_unary_operator(*op),
1243 operand: Box::new(HirExpression::from_ast_expression(operand)),
1244 },
1245 Expression::ArrayIndex { array, index } => HirExpression::ArrayIndex {
1246 array: Box::new(HirExpression::from_ast_expression(array)),
1247 index: Box::new(HirExpression::from_ast_expression(index)),
1248 },
1249 Expression::FieldAccess { object, field } => HirExpression::FieldAccess {
1250 object: Box::new(HirExpression::from_ast_expression(object)),
1251 field: field.clone(),
1252 },
1253 Expression::PointerFieldAccess { pointer, field } => {
1254 HirExpression::PointerFieldAccess {
1255 pointer: Box::new(HirExpression::from_ast_expression(pointer)),
1256 field: field.clone(),
1257 }
1258 }
1259 // DECY-139: Increment/decrement expressions in expression context
1260 // Properly preserve the increment/decrement for codegen to emit correct Rust
1261 Expression::PostIncrement { operand } => HirExpression::PostIncrement {
1262 operand: Box::new(HirExpression::from_ast_expression(operand)),
1263 },
1264 Expression::PreIncrement { operand } => HirExpression::PreIncrement {
1265 operand: Box::new(HirExpression::from_ast_expression(operand)),
1266 },
1267 Expression::PostDecrement { operand } => HirExpression::PostDecrement {
1268 operand: Box::new(HirExpression::from_ast_expression(operand)),
1269 },
1270 Expression::PreDecrement { operand } => HirExpression::PreDecrement {
1271 operand: Box::new(HirExpression::from_ast_expression(operand)),
1272 },
1273 Expression::Sizeof { type_name } => HirExpression::Sizeof {
1274 type_name: type_name.clone(),
1275 },
1276 Expression::Cast { target_type, expr } => HirExpression::Cast {
1277 target_type: HirType::from_ast_type(target_type),
1278 expr: Box::new(HirExpression::from_ast_expression(expr)),
1279 },
1280 Expression::CompoundLiteral {
1281 literal_type,
1282 initializers,
1283 } => HirExpression::CompoundLiteral {
1284 literal_type: HirType::from_ast_type(literal_type),
1285 initializers: initializers
1286 .iter()
1287 .map(HirExpression::from_ast_expression)
1288 .collect(),
1289 },
1290 // DECY-192: Ternary/conditional expression
1291 Expression::Ternary {
1292 condition,
1293 then_expr,
1294 else_expr,
1295 } => HirExpression::Ternary {
1296 condition: Box::new(HirExpression::from_ast_expression(condition)),
1297 then_expr: Box::new(HirExpression::from_ast_expression(then_expr)),
1298 else_expr: Box::new(HirExpression::from_ast_expression(else_expr)),
1299 },
1300 }
1301 }
1302}
1303
1304/// Convert parser UnaryOperator to HIR UnaryOperator
1305fn convert_unary_operator(op: decy_parser::parser::UnaryOperator) -> UnaryOperator {
1306 use decy_parser::parser::UnaryOperator as ParserOp;
1307 match op {
1308 ParserOp::Minus => UnaryOperator::Minus,
1309 ParserOp::LogicalNot => UnaryOperator::LogicalNot,
1310 ParserOp::BitwiseNot => UnaryOperator::BitwiseNot,
1311 ParserOp::AddressOf => UnaryOperator::AddressOf,
1312 }
1313}
1314
1315/// Convert parser BinaryOperator to HIR BinaryOperator
1316fn convert_binary_operator(op: decy_parser::parser::BinaryOperator) -> BinaryOperator {
1317 use decy_parser::parser::BinaryOperator as ParserOp;
1318 match op {
1319 ParserOp::Add => BinaryOperator::Add,
1320 ParserOp::Subtract => BinaryOperator::Subtract,
1321 ParserOp::Multiply => BinaryOperator::Multiply,
1322 ParserOp::Divide => BinaryOperator::Divide,
1323 ParserOp::Modulo => BinaryOperator::Modulo,
1324 ParserOp::Equal => BinaryOperator::Equal,
1325 ParserOp::NotEqual => BinaryOperator::NotEqual,
1326 ParserOp::LessThan => BinaryOperator::LessThan,
1327 ParserOp::GreaterThan => BinaryOperator::GreaterThan,
1328 ParserOp::LessEqual => BinaryOperator::LessEqual,
1329 ParserOp::GreaterEqual => BinaryOperator::GreaterEqual,
1330 ParserOp::LogicalAnd => BinaryOperator::LogicalAnd,
1331 ParserOp::LogicalOr => BinaryOperator::LogicalOr,
1332 // DECY-137: Bitwise and shift operators
1333 ParserOp::LeftShift => BinaryOperator::LeftShift,
1334 ParserOp::RightShift => BinaryOperator::RightShift,
1335 ParserOp::BitwiseAnd => BinaryOperator::BitwiseAnd,
1336 ParserOp::BitwiseOr => BinaryOperator::BitwiseOr,
1337 ParserOp::BitwiseXor => BinaryOperator::BitwiseXor,
1338 // DECY-195: Assignment operator for embedded assignments
1339 ParserOp::Assign => BinaryOperator::Assign,
1340 // DECY-224: Comma operator for multi-expression statements
1341 ParserOp::Comma => BinaryOperator::Comma,
1342 }
1343}
1344
1345#[cfg(test)]
1346#[path = "hir_tests.rs"]
1347mod hir_tests;
1348
1349#[cfg(test)]
1350#[path = "property_tests.rs"]
1351mod property_tests;
1352
1353#[cfg(test)]
1354#[path = "statement_tests.rs"]
1355mod statement_tests;
1356
1357#[cfg(test)]
1358#[path = "struct_tests.rs"]
1359mod struct_tests;
1360
1361#[cfg(test)]
1362#[path = "array_indexing_tests.rs"]
1363mod array_indexing_tests;
1364
1365#[cfg(test)]
1366#[path = "slice_index_tests.rs"]
1367mod slice_index_tests; // DECY-069
1368
1369#[cfg(test)]
1370#[path = "for_loop_tests.rs"]
1371mod for_loop_tests;
1372
1373#[cfg(test)]
1374#[path = "typedef_tests.rs"]
1375mod typedef_tests;
1376
1377#[cfg(test)]
1378#[path = "typedef_property_tests.rs"]
1379mod typedef_property_tests;
1380
1381#[cfg(test)]
1382#[path = "function_pointer_tests.rs"]
1383mod function_pointer_tests;
1384
1385#[cfg(test)]
1386#[path = "string_tests.rs"]
1387mod string_tests;
1388
1389#[cfg(test)]
1390#[path = "string_property_tests.rs"]
1391mod string_property_tests;
1392
1393#[cfg(test)]
1394#[path = "switch_tests.rs"]
1395mod switch_tests;
1396
1397#[cfg(test)]
1398#[path = "macro_definition_tests.rs"]
1399mod macro_definition_tests;
1400
1401#[cfg(test)]
1402#[path = "coverage_tests.rs"]
1403mod coverage_tests;