├── .gitignore ├── assets └── logo.webp ├── tamago ├── tamacro │ ├── README.md │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── Cargo.toml ├── LICENSE.txt ├── README.md └── src │ ├── formatter.rs │ ├── lib.rs │ ├── typedef.rs │ ├── union.rs │ ├── scope.rs │ ├── variable.rs │ ├── structs.rs │ ├── block.rs │ ├── types.rs │ ├── enums.rs │ └── comment.rs ├── Cargo.toml ├── README.md ├── LICENSE.txt ├── src ├── semantic_analyzer │ ├── mod.rs │ ├── resolver.rs │ └── type_checker.rs ├── main.rs └── lexer.rs ├── example.clla └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /assets/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bichanna/castella/HEAD/assets/logo.webp -------------------------------------------------------------------------------- /tamago/tamacro/README.md: -------------------------------------------------------------------------------- 1 | # tamacro 2 | 3 | This is not intended to be used directly by any other crates other than `tamago`. 4 | -------------------------------------------------------------------------------- /tamago/tamacro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tamacro" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | description = "tamacro" 7 | license = "MIT" 8 | authors = ["Nobuharu Shimazu "] 9 | homepage = "https://github.com/bichanna/castella/tree/main/tamago/tamacro" 10 | readme = "README.md" 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies] 16 | proc-macro2 = "1.0.93" 17 | quote = "1.0.38" 18 | syn = { version = "2.0.98", features = ["full"] } 19 | -------------------------------------------------------------------------------- /tamago/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tamago" 3 | version = "0.2.0" 4 | edition = "2024" 5 | 6 | description = "Library for generating C code" 7 | license = "MIT" 8 | license-file = "LICENSE.txt" 9 | authors = ["Nobuharu Shimazu "] 10 | homepage = "https://github.com/bichanna/castella/tree/main/tamago" 11 | repository = "https://github.com/bichanna/castella/tree/main/tamago" 12 | readme = "README.md" 13 | keywords = ["code", "generator", "code_generator"] 14 | 15 | [dependencies] 16 | tamacro = { path = "./tamacro", version = "0.1.0" } 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "castella" 3 | version = "0.0.0" 4 | edition = "2024" 5 | 6 | description = "Basically the C programming language peppered with my preferences" 7 | license = "MIT" 8 | license-file = "LICENSE.txt" 9 | categories = ["compilers"] 10 | keywords = ["language", "programming_language"] 11 | authors = ["Nobuharu Shimazu "] 12 | repository = "https://github.com/bichanna/castella" 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | tamago = { path = "tamago" } 17 | tamacro = { path = "tamago/tamacro" } 18 | snailquote = "~0.3" 19 | logos = "~0.15" 20 | colored = "~3.0" 21 | 22 | [workspace] 23 | members = [ 24 | "tamago", 25 | "tamago/tamacro" 26 | ] 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Castella 2 | 3 |
4 | Castella's logo 5 |
6 | 7 | Castella is essentially a reimagined version of C, infused with personal preferences for both semantics and syntax. Castella uses the [tamago](https://crates.io/crates/tamago) crate to generate its C output. 8 | 9 | ## Goals 10 | 11 | - Basically C with a little bit nicer syntax 12 | - Transpiles to standard C code, ensuring compatibility 13 | - Full manual memory management, just like in C, but with some niceties 14 | - Built-in implementations of dynamic arrays, hash maps, and other useful things 15 | - Use functions, types, and other stuff from C without too much fuss 16 | - Generics! 17 | - Run some pretty awesome code at compile-time (inspired by [this post](https://0x44.xyz/blog/comptime-1/)) 18 | - Not too many features being introduced (except for the extensive compile-time capabilities) 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2025 Nobuharu Shimazu 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /tamago/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2025 Nobuharu Shimazu 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /src/semantic_analyzer/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod resolver; 2 | pub mod type_checker; 3 | 4 | use crate::parser::Span; 5 | 6 | /// Could be either a warning or an error 7 | type Message = (Span, String); 8 | 9 | /// Implements the Damerau-Levenshtein distance 10 | pub fn edit_distance(x: &str, y: &str) -> usize { 11 | let x_chars = x.chars().collect::>(); 12 | let y_chars = y.chars().collect::>(); 13 | 14 | let x_len = x.chars().count(); 15 | let y_len = y.chars().count(); 16 | 17 | let mut dp = vec![vec![0; y_len + 1]; x_len + 1]; 18 | 19 | for i in 0..=x_len { 20 | dp[i][0] = i; 21 | } 22 | 23 | for j in 0..=y_len { 24 | dp[0][j] = j; 25 | } 26 | 27 | for i in 1..=x_len { 28 | for j in 1..=y_len { 29 | let cost = if x_chars[i - 1] == y_chars[j - 1] { 30 | 0 31 | } else { 32 | 1 33 | }; 34 | 35 | let deletion = dp[i - 1][j] + 1; 36 | let insertion = dp[i][j - 1] + 1; 37 | let substitution = dp[i - 1][j - 1] + cost; 38 | 39 | dp[i][j] = deletion.min(insertion).min(substitution); 40 | 41 | if i > 1 42 | && j > 1 43 | && x_chars[i - 1] == y_chars[j - 2] 44 | && x_chars[i - 2] == y_chars[j - 1] 45 | { 46 | dp[i][j] = dp[i][j].min(dp[i - 2][j - 2] + 1); 47 | } 48 | } 49 | } 50 | 51 | dp[x_len][y_len] 52 | } 53 | 54 | #[cfg(test)] 55 | mod tests { 56 | use super::*; 57 | 58 | #[test] 59 | fn test_damerau_levenshtein_distance() { 60 | assert_eq!(edit_distance("acb", "abc"), 1); 61 | assert_eq!(edit_distance("teh", "the"), 1); 62 | assert_eq!(edit_distance("apple", "aple"), 1); 63 | assert_eq!(edit_distance("aple", "apple"), 1); 64 | assert_eq!(edit_distance("abc", "acb"), 1); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tamago/README.md: -------------------------------------------------------------------------------- 1 | # Tamago 2 | 3 | Tamago is a code generator library for C, written in Rust. It is designed to simplify the process of generating C code programmatically, leveraging Rust's safety and expressiveness. This crate makes heavy use of the builder pattern to provide a pretty API (I hope) for constructing C code structures. 4 | 5 | Tamago is primarily developed as a core component for the [Castella transpiler](https://github.com/bichanna/castella), but it is designed to be reusable for any project that needs to generate C code dynamically. 6 | 7 | ## Features 8 | 9 | - Generate C code programmatically with a type-safe Rust API. 10 | - Builder pattern for ergonomic and readable code generation. 11 | - Lightweight and focused on simplicity. 12 | 13 | ## Installation 14 | 15 | Add `tamago` to your project by including it in your `Cargo.toml`: 16 | 17 | ```toml 18 | [dependencies] 19 | tamago = "0.1.0" # Replace with the actual version 20 | ``` 21 | 22 | ## Usage 23 | ```rust 24 | use tamago::*; 25 | 26 | let scope = ScopeBuilder::new() 27 | .global_statement(GlobalStatement::Struct( 28 | StructBuilder::new_with_str("Person") 29 | .doc( 30 | DocCommentBuilder::new() 31 | .line_str("Represents a person") 32 | .build(), 33 | ) 34 | .field( 35 | FieldBuilder::new_with_str( 36 | "name", 37 | Type::new(BaseType::Char) 38 | .make_pointer() 39 | .make_const() 40 | .build(), 41 | ) 42 | .doc( 43 | DocCommentBuilder::new() 44 | .line_str("The name of the person") 45 | .build(), 46 | ) 47 | .build(), 48 | ) 49 | .field( 50 | FieldBuilder::new_with_str("age", Type::new(BaseType::Int).build()) 51 | .doc( 52 | DocCommentBuilder::new() 53 | .line_str("The age of the person") 54 | .build(), 55 | ) 56 | .build(), 57 | ) 58 | .build(), 59 | )) 60 | .new_line() 61 | .global_statement(GlobalStatement::TypeDef( 62 | TypeDefBuilder::new_with_str( 63 | Type::new(BaseType::Struct("Person".to_string())).build(), 64 | "Person", 65 | ) 66 | .build(), 67 | )) 68 | .build(); 69 | 70 | println!("{}", scope.to_string()); 71 | ``` 72 | And here's output: 73 | ```c 74 | /// Represents a person 75 | struct Person { 76 | /// The name of the person 77 | const char* name; 78 | /// The age of the person 79 | int age; 80 | }; 81 | 82 | typedef struct Person Person; 83 | ``` 84 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use colored::{ColoredString, Colorize}; 2 | use logos::Logos; 3 | 4 | mod lexer; 5 | mod parser; 6 | mod semantic_analyzer; 7 | 8 | type Message = (parser::Span, String); 9 | 10 | fn get_line_number(source: &str, span: parser::Span) -> usize { 11 | source[0..span.start].chars().filter(|&c| c == '\n').count() + 1 12 | } 13 | 14 | #[inline] 15 | fn show_message( 16 | source: &str, 17 | source_path: &str, 18 | span: parser::Span, 19 | pre: ColoredString, 20 | msg: String, 21 | ) { 22 | eprintln!( 23 | "{}({}) {}: {}", 24 | source_path, 25 | get_line_number(source, span), 26 | pre, 27 | msg 28 | ); 29 | } 30 | 31 | fn show_errors(source: &str, source_path: &str, errors: Vec) { 32 | for e in errors { 33 | show_message(source, source_path, e.0, "Error".red(), e.1); 34 | } 35 | } 36 | 37 | fn show_warnings(source: &str, source_path: &str, warnings: Vec) { 38 | for w in warnings { 39 | show_message(source, source_path, w.0, "Warning".yellow(), w.1); 40 | } 41 | } 42 | 43 | fn compile(source: &str) -> (Vec, Result<(), Vec>) { 44 | let mut warnings: Vec = vec![]; 45 | 46 | let lexer = lexer::Token::lexer(source); 47 | 48 | let ast: Vec; 49 | match parser::Parser::new(lexer).parse() { 50 | Ok(res) => ast = res, 51 | Err(errs) => return (warnings, Err(errs)), 52 | } 53 | 54 | match semantic_analyzer::resolver::Resolver::new(&ast).resolve() { 55 | (mut w, Ok(())) => warnings.append(&mut w), 56 | (mut w, Err(errs)) => { 57 | warnings.append(&mut w); 58 | return (warnings, Err(errs)); 59 | } 60 | } 61 | 62 | match semantic_analyzer::type_checker::TypeChecker::new(&ast).check() { 63 | (mut w, Ok(())) => warnings.append(&mut w), 64 | (mut w, Err(errs)) => { 65 | warnings.append(&mut w); 66 | return (warnings, Err(errs)); 67 | } 68 | } 69 | 70 | todo!() 71 | } 72 | 73 | fn main() { 74 | // println!("filepath(line) Error: "); 75 | 76 | // let source = "func main(): void { return (1 + 1.0) * 3; }"; 77 | // let source = "func main(): void { age; Person { name = \"Nobu\", age = 18 }; }"; 78 | let source = "func abc(): void {} func main(): void { free dhdbc; }"; 79 | // let source = "enum Week { Mon; Tue; Wed; Thur=4; Fri; }"; 80 | // let source = "alias Age = int; union Person { name: str; age: Age; }"; 81 | 82 | let lexer = lexer::Token::lexer(source); 83 | let res = parser::Parser::new(lexer).parse(); 84 | if let Err(err) = res { 85 | show_errors(source, "main.clla", err); 86 | return; 87 | } 88 | 89 | let a = semantic_analyzer::resolver::Resolver::new(&res.unwrap()).resolve(); 90 | 91 | if let (w, Err(err)) = a { 92 | show_warnings(source, "main.clla", w); 93 | show_errors(source, "main.clla", err); 94 | } else { 95 | let a = a.0; 96 | show_warnings(source, "main.clla", a); 97 | } 98 | 99 | // println!("{:#?}", a); 100 | } 101 | -------------------------------------------------------------------------------- /tamago/src/formatter.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Nobuharu Shimazu 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | //! This module provides a formatter implementation that emits a string of C code with the right 22 | //! indentation. 23 | 24 | use std::fmt::{self, Write}; 25 | 26 | const DEFAULT_INDENT: usize = 2; 27 | 28 | pub trait Format { 29 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result; 30 | } 31 | 32 | /// Formatter for a scope 33 | #[derive(Debug)] 34 | pub struct Formatter<'a> { 35 | /// The buffer for the formatter 36 | dst: &'a mut String, 37 | 38 | /// The current identation level 39 | pub spaces: usize, 40 | 41 | /// The current scope 42 | scope: Vec, 43 | 44 | /// Indentation level 45 | pub indent: usize, 46 | } 47 | 48 | impl<'a> Formatter<'a> { 49 | pub fn new(dst: &'a mut String) -> Self { 50 | Self { 51 | dst, 52 | spaces: 0, 53 | scope: vec![], 54 | indent: DEFAULT_INDENT, 55 | } 56 | } 57 | 58 | pub fn scope(&mut self, name: &str, f: F) -> R 59 | where 60 | F: FnOnce(&mut Self) -> R, 61 | { 62 | self.scope.push(name.to_string()); 63 | let res = f(self); 64 | self.scope.pop(); 65 | res 66 | } 67 | 68 | pub fn block(&mut self, f: F) -> fmt::Result 69 | where 70 | F: FnOnce(&mut Self) -> fmt::Result, 71 | { 72 | if !self.is_start_of_line() { 73 | write!(self, " ")?; 74 | } 75 | 76 | writeln!(self, "{{")?; 77 | self.indent(f)?; 78 | write!(self, "}}")?; 79 | 80 | Ok(()) 81 | } 82 | 83 | pub fn indent(&mut self, f: F) -> R 84 | where 85 | F: FnOnce(&mut Self) -> R, 86 | { 87 | self.spaces += self.indent; 88 | let res = f(self); 89 | self.spaces -= self.indent; 90 | res 91 | } 92 | 93 | pub fn is_start_of_line(&self) -> bool { 94 | self.dst.is_empty() || self.dst.ends_with('\n') 95 | } 96 | 97 | fn push_spaces(&mut self) { 98 | self.dst.push_str(&" ".repeat(self.spaces)) 99 | } 100 | } 101 | 102 | impl Write for Formatter<'_> { 103 | fn write_str(&mut self, s: &str) -> fmt::Result { 104 | let mut should_indent = self.is_start_of_line(); 105 | 106 | for (idx, line) in s.lines().enumerate() { 107 | if idx != 0 { 108 | self.dst.push('\n'); 109 | } 110 | 111 | if should_indent && !line.is_empty() && line.as_bytes()[0] != b'\n' { 112 | self.push_spaces(); 113 | } 114 | 115 | should_indent = true; 116 | 117 | self.dst.push_str(line); 118 | } 119 | 120 | if s.as_bytes().last() == Some(&b'\n') { 121 | self.dst.push('\n'); 122 | } 123 | 124 | Ok(()) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /tamago/tamacro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | use syn::{Data, DeriveInput, Expr, Lit, Meta, Variant}; 4 | 5 | #[proc_macro_derive(DisplayFromFormat)] 6 | pub fn derive_display_from_format(input: TokenStream) -> TokenStream { 7 | let input = syn::parse(input).unwrap_or_else(|_| { 8 | panic!("Failed to parse"); 9 | }); 10 | let DeriveInput { ident, .. } = input; 11 | 12 | let output = quote! { 13 | impl std::fmt::Display for #ident { 14 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 15 | let mut res = String::new(); 16 | self.format(&mut Formatter::new(&mut res))?; 17 | write!(f, "{res}") 18 | } 19 | } 20 | }; 21 | 22 | output.into() 23 | } 24 | 25 | #[proc_macro_derive(DisplayFromConstSymbol, attributes(symbol))] 26 | pub fn derive_display_from_const_symbol(input: TokenStream) -> TokenStream { 27 | let input = syn::parse(input).unwrap_or_else(|_| { 28 | panic!("Failed to parse"); 29 | }); 30 | let DeriveInput { 31 | ident: enum_name, 32 | data, 33 | .. 34 | } = input; 35 | 36 | let variants = match data { 37 | Data::Enum(data_enum) => data_enum.variants, 38 | _ => panic!("DisplayFromConstSymbol derive macro can only be used on enums"), 39 | }; 40 | 41 | let match_arms = variants.iter().map(|variant| { 42 | let variant_name = &variant.ident; 43 | let symbol = get_symbol_attr(variant).unwrap_or_else(|| { 44 | panic!("Each variant of {enum_name} must have a #[symbol = \"...\"] attribute"); 45 | }); 46 | 47 | quote! { 48 | #enum_name::#variant_name => write!(fmt, #symbol), 49 | } 50 | }); 51 | 52 | let output = quote! { 53 | impl std::fmt::Display for #enum_name { 54 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 55 | use #enum_name::*; 56 | match self { 57 | #(#match_arms)* 58 | } 59 | } 60 | } 61 | }; 62 | 63 | output.into() 64 | } 65 | 66 | #[proc_macro_derive(FormatFromConstSymbol, attributes(symbol))] 67 | pub fn derive_format_from_const_symbol(input: TokenStream) -> TokenStream { 68 | let input = syn::parse(input).unwrap_or_else(|_| { 69 | panic!("Failed to parse"); 70 | }); 71 | let DeriveInput { 72 | ident: enum_name, 73 | data, 74 | .. 75 | } = input; 76 | 77 | let variants = match data { 78 | Data::Enum(data_enum) => data_enum.variants, 79 | _ => panic!("FormatFromConstSymbol derive macro can only be used on enums"), 80 | }; 81 | 82 | let match_arms = variants.iter().map(|variant| { 83 | let variant_name = &variant.ident; 84 | let symbol = get_symbol_attr(variant).unwrap_or_else(|| { 85 | panic!("Each variant of {enum_name} must have a #[symbol = \"...\"] attribute"); 86 | }); 87 | 88 | quote! { 89 | #enum_name::#variant_name => write!(fmt, #symbol), 90 | } 91 | }); 92 | 93 | let output = quote! { 94 | impl crate::formatter::Format for #enum_name { 95 | fn format(&self, fmt: &mut crate::formatter::Formatter<'_>) -> fmt::Result { 96 | use #enum_name::*; 97 | match self { 98 | #(#match_arms)* 99 | } 100 | } 101 | } 102 | }; 103 | 104 | output.into() 105 | } 106 | 107 | fn get_symbol_attr(variant: &Variant) -> Option { 108 | for attr in &variant.attrs { 109 | if attr.path().is_ident("symbol") { 110 | if let Meta::NameValue(name_value) = &attr.meta { 111 | if let Expr::Lit(lit) = &name_value.value { 112 | if let Lit::Str(lit_str) = &lit.lit { 113 | return Some(lit_str.value()); 114 | } 115 | } 116 | } 117 | } 118 | } 119 | None 120 | } 121 | -------------------------------------------------------------------------------- /example.clla: -------------------------------------------------------------------------------- 1 | import rl "opt/raylib" 2 | import "fmt" 3 | 4 | const TICK_RATE = 0.13; 5 | const WINDOW_SIZE = 1000; 6 | const GRID_LENGTH = 20; 7 | const CELL_SIZE = 16; 8 | const MAX_SNAKE_LENGTH = GRID_LENGTH * GRID_LENGTH; 9 | const CANVAS_SIZE = GRID_LENGTH * CELL_SIZE; 10 | 11 | alias Vec2i: [2]int; 12 | 13 | let snake: [MAX_SNAKE_LENGTH]Vec2i; 14 | let snake_len: uint; 15 | let move_dir: Vec2i = [0, 1]; 16 | let tick_timer: float = TICK_RATE; 17 | let game_over: bool = false; 18 | let food_pos: Vec2i; 19 | 20 | func place_food() { 21 | let occupied: [GRID_LENGTH][GRID_LENGTH]bool; 22 | for (i in 0.. 0) { 37 | let random_cell_idx = rl::get_random_value(0, int32(len(free_cells) - 1)); 38 | food_pos = free_cells[random_cell_idx]; 39 | } 40 | } 41 | 42 | func restart() { 43 | let start_head_pos = Vec2i { GRID_LENGTH / 2, GRID_LENGTH / 2 }; 44 | snake[0] = tstart_head_pos; 45 | snake[1] = start_head_pos - [0, 1]; 46 | snake[2] = start_head_pos - [0, 2]; 47 | snake_len = 3; 48 | move_dir = [0, 1]; 49 | game_over = false; 50 | place_food(); 51 | } 52 | 53 | func main() : int { 54 | rl::SetConfigFlags(rl::Flag.VSYNC_HINT); 55 | rl::InitWindow(WINDOW_SIZE, WINDOW_SIZE, "Snake"); 56 | 57 | restart(); 58 | 59 | while (not rl::window_should_close()) { 60 | if (rl::is_key_down(rl::Key.UP)) { 61 | move_dir = [0, -1]; 62 | } else if (rl::is_key_down(rl::Key.DOWN)) { 63 | move_dir = [0, 1]; 64 | } else if (rl::is_key_down(rl::Key.LEFT)) { 65 | move_dir = [-1, 0]; 66 | } else if (rl::is_key_down(rl::Key.RIGHT)) { 67 | move_dir = [1, 0]; 68 | } 69 | 70 | if (game_over) { 71 | if (rl::is_key_down(rl::Key.ENTER)) 72 | restart(); 73 | } else { 74 | tick_timer -= rl::GetFrameTime(); 75 | } 76 | 77 | if (tick_timer <= 0) { 78 | let next_part_pos = snake[0]; 79 | snake[0] = snake[0] - move_dir; 80 | let head_pos = snake[0]; 81 | 82 | if (head_pos[0] < 0 or head_pos[1] < 0 or head_pos[0] >= GRID_LENGTH or head_pos[1] >= GRID_LENGTH) { 83 | game_over = true; 84 | } 85 | 86 | for (i in 1.. for LexError { 12 | fn from(value: ParseIntError) -> Self { 13 | Self { 14 | msg: value.to_string(), 15 | } 16 | } 17 | } 18 | 19 | impl From for LexError { 20 | fn from(value: ParseFloatError) -> Self { 21 | Self { 22 | msg: value.to_string(), 23 | } 24 | } 25 | } 26 | 27 | impl From for LexError { 28 | fn from(value: UnescapeError) -> Self { 29 | Self { 30 | msg: value.to_string(), 31 | } 32 | } 33 | } 34 | 35 | impl Default for LexError { 36 | fn default() -> Self { 37 | Self { 38 | msg: "".to_string(), 39 | } 40 | } 41 | } 42 | 43 | #[derive(Logos, Clone, Debug, PartialEq)] 44 | #[logos(error = LexError)] 45 | pub enum Token { 46 | #[regex(r"[A-Za-z_][A-Za-z1-9_]*", |lex| lex.slice().to_string(), priority = 1)] 47 | Ident(String), 48 | 49 | // TODO: Char! 50 | /// Regex inspired by 51 | #[regex(r#""([^"\\]|\\["\\bnfrt]|u[a-fA-F0-9]{4})*""#, |lex| unescape(lex.slice()))] 52 | Str(String), 53 | 54 | #[regex(r"-?(?:0|[1-9]\d*)(?:[eE]?\d+)?", |lex| lex.slice().parse::())] 55 | Int(i64), 56 | 57 | #[regex(r"-?(?:0|[1-9]\d*)\.\d+(?:[eE][+-]?\d+)?", |lex| lex.slice().parse::())] 58 | Double(f64), 59 | 60 | #[regex(r"[ \n\t\f]+", skip)] 61 | #[regex(r"//[^\n]*\n?", skip)] 62 | #[regex(r"/\*(?:[^*]|\*[^/])*\*/", skip)] // Can't be nested 63 | #[token("(")] 64 | LeftParen, 65 | 66 | #[token(")")] 67 | RightParen, 68 | 69 | #[token("{")] 70 | LeftBrace, 71 | 72 | #[token("}")] 73 | RightBrace, 74 | 75 | #[token("[")] 76 | LeftBrak, 77 | 78 | #[token("]")] 79 | RightBrak, 80 | 81 | #[token("&")] 82 | Ampersand, 83 | 84 | #[token("^")] 85 | Caret, 86 | 87 | #[token(".")] 88 | Dot, 89 | 90 | #[token("..")] 91 | DDot, 92 | 93 | #[token("+")] 94 | Plus, 95 | 96 | #[token("+=")] 97 | PlusEq, 98 | 99 | #[token("-")] 100 | Minus, 101 | 102 | #[token("-=")] 103 | MinusEq, 104 | 105 | #[token("*")] 106 | Mul, 107 | 108 | #[token("*=")] 109 | MulEq, 110 | 111 | #[token("/")] 112 | Div, 113 | 114 | #[token("/=")] 115 | DivEq, 116 | 117 | #[token("%")] 118 | Mod, 119 | 120 | #[token("%=")] 121 | ModEq, 122 | 123 | #[token(",")] 124 | Comma, 125 | 126 | #[token("->")] 127 | RightArrow, 128 | 129 | #[token(">")] 130 | GT, 131 | 132 | #[token(">=")] 133 | GE, 134 | 135 | #[token("<")] 136 | LT, 137 | 138 | #[token("<=")] 139 | LE, 140 | 141 | #[token("!=")] 142 | NotEq, 143 | 144 | #[token("=")] 145 | Eq, 146 | 147 | #[token("==")] 148 | DEq, 149 | 150 | #[token(";")] 151 | SemiColon, 152 | 153 | #[token(":")] 154 | Colon, 155 | 156 | #[token("::")] 157 | DColon, 158 | 159 | // Keywords 160 | #[token("import")] 161 | Import, 162 | 163 | #[token("let")] 164 | Let, 165 | 166 | #[token("const")] 167 | Const, 168 | 169 | #[token("func")] 170 | Func, 171 | 172 | #[token("if")] 173 | If, 174 | 175 | #[token("and")] 176 | And, 177 | 178 | #[token("or")] 179 | Or, 180 | 181 | #[token("not")] 182 | Not, 183 | 184 | #[token("else")] 185 | Else, 186 | 187 | #[token("true")] 188 | True, 189 | 190 | #[token("false")] 191 | False, 192 | 193 | #[token("for")] 194 | For, 195 | 196 | #[token("while")] 197 | While, 198 | 199 | #[token("do")] 200 | Do, 201 | 202 | #[token("in")] 203 | In, 204 | 205 | #[token("return")] 206 | Return, 207 | 208 | #[token("defer")] 209 | Defer, 210 | 211 | #[token("make")] 212 | Make, 213 | 214 | #[token("destroy")] 215 | Destroy, 216 | 217 | #[token("new")] 218 | New, 219 | 220 | #[token("free")] 221 | Free, 222 | 223 | #[token("enum")] 224 | Enum, 225 | 226 | #[token("struct")] 227 | Struct, 228 | 229 | #[token("union")] 230 | Union, 231 | 232 | #[token("alias")] 233 | Alias, 234 | 235 | #[token("break")] 236 | Break, 237 | 238 | #[token("continue")] 239 | Continue, 240 | 241 | #[token("void")] 242 | TVoid, 243 | 244 | #[token("double")] 245 | TDouble, 246 | 247 | #[token("float")] 248 | TFloat, 249 | 250 | #[token("char")] 251 | TChar, 252 | 253 | #[token("str")] 254 | TStr, 255 | 256 | #[token("i8")] 257 | TInt8, 258 | 259 | #[token("i16")] 260 | TInt16, 261 | 262 | #[token("i32")] 263 | TInt32, 264 | 265 | #[token("i64")] 266 | TInt64, 267 | 268 | #[token("u8")] 269 | TUInt8, 270 | 271 | #[token("u16")] 272 | TUInt16, 273 | 274 | #[token("u32")] 275 | TUInt32, 276 | 277 | #[token("u64")] 278 | TUInt64, 279 | 280 | #[token("bool")] 281 | TBool, 282 | } 283 | 284 | impl std::fmt::Display for Token { 285 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 286 | use Token::*; 287 | 288 | match self { 289 | Ident(ident) => write!(f, "identifier '{}'", ident), 290 | Str(string) => write!(f, "string literal '{}'", string), 291 | Int(integer) => write!(f, "integer literal '{}'", integer), 292 | Double(double) => write!(f, "double literal '{}'", double), 293 | LeftParen => write!(f, "'('"), 294 | RightParen => write!(f, "')'"), 295 | LeftBrace => write!(f, "{{"), 296 | RightBrace => write!(f, "}}"), 297 | LeftBrak => write!(f, "'['"), 298 | RightBrak => write!(f, "']'"), 299 | Caret => write!(f, "'^'"), 300 | Dot => write!(f, "'.'"), 301 | DDot => write!(f, "'..'"), 302 | Plus => write!(f, "'+'"), 303 | PlusEq => write!(f, "'+='"), 304 | Minus => write!(f, "'-'"), 305 | MinusEq => write!(f, "'-='"), 306 | Mul => write!(f, "'*'"), 307 | MulEq => write!(f, "'*='"), 308 | Div => write!(f, "'/'"), 309 | DivEq => write!(f, "'/='"), 310 | Mod => write!(f, "'%'"), 311 | ModEq => write!(f, "'%='"), 312 | Comma => write!(f, "'"), 313 | RightArrow => write!(f, "'->'"), 314 | GT => write!(f, "'>'"), 315 | GE => write!(f, "'>='"), 316 | LT => write!(f, "'<'"), 317 | LE => write!(f, "'<='"), 318 | NotEq => write!(f, "'!='"), 319 | Eq => write!(f, "'='"), 320 | DEq => write!(f, "'=='"), 321 | Ampersand => writeln!(f, "'&'"), 322 | SemiColon => write!(f, "';'"), 323 | Colon => write!(f, "':'"), 324 | Not => write!(f, "'not'"), 325 | DColon => write!(f, "'::'"), 326 | Import => write!(f, "'import'"), 327 | Let => write!(f, "'let'"), 328 | Const => write!(f, "'const'"), 329 | Func => write!(f, "'func'"), 330 | Break => write!(f, "'break'"), 331 | Continue => write!(f, "'continue'"), 332 | If => write!(f, "'if'"), 333 | And => write!(f, "'and'"), 334 | Or => write!(f, "'or'"), 335 | Else => write!(f, "'else'"), 336 | True => write!(f, "'true'"), 337 | False => write!(f, "'false'"), 338 | For => write!(f, "'for'"), 339 | While => write!(f, "'while'"), 340 | Do => write!(f, "'do'"), 341 | In => write!(f, "'in'"), 342 | Return => write!(f, "'return'"), 343 | Defer => write!(f, "'defer'"), 344 | Make => write!(f, "'make'"), 345 | Destroy => write!(f, "'destroy'"), 346 | New => write!(f, "'new'"), 347 | Free => write!(f, "'free'"), 348 | Enum => write!(f, "'enum'"), 349 | Struct => write!(f, "'struct'"), 350 | Union => write!(f, "'union'"), 351 | Alias => write!(f, "'alias'"), 352 | 353 | // Types 354 | TVoid => write!(f, "'void'"), 355 | TDouble => write!(f, "'double'"), 356 | TFloat => write!(f, "'float'"), 357 | TChar => write!(f, "'char'"), 358 | TStr => write!(f, "'str'"), 359 | TInt8 => write!(f, "'i8'"), 360 | TInt16 => write!(f, "'i16'"), 361 | TInt32 => write!(f, "'i32'"), 362 | TInt64 => write!(f, "'i64'"), 363 | TUInt8 => write!(f, "'u8'"), 364 | TUInt16 => write!(f, "'u16'"), 365 | TUInt32 => write!(f, "'u32'"), 366 | TUInt64 => write!(f, "'u64'"), 367 | TBool => write!(f, "'bool'"), 368 | } 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /tamago/src/typedef.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Nobuharu Shimazu 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | //! This module provides functionality for generating C-style typedef declarations. 22 | //! 23 | //! A typedef in C creates an alias for a type, allowing developers to use more 24 | //! descriptive or shorter names for complex types. This module allows programmers 25 | //! to programmatically generate such typedef declarations with proper formatting. 26 | 27 | use std::fmt::{self, Write}; 28 | 29 | use crate::{BaseType, Format, Formatter, Type}; 30 | use tamacro::DisplayFromFormat; 31 | 32 | /// Represents a C-style `typedef` declaration. 33 | /// 34 | /// A `TypeDef` encapsulates both the original type being aliased and the new alias name. 35 | /// It provides methods for creating and manipulating typedef declarations and can be 36 | /// formatted as valid C code. 37 | /// 38 | /// # Examples 39 | /// 40 | /// Basic typedef for a struct: 41 | /// ```rust 42 | /// // Creates a typedef equivalent to: typedef struct Person Person; 43 | /// let person_type = Type::new(BaseType::Struct("Person".to_string())).build(); 44 | /// let typedef = TypeDef::new(person_type, "Person".to_string()).build(); 45 | /// println!("{}", typedef); // Outputs: typedef struct Person Person; 46 | /// ``` 47 | /// 48 | /// Creating a typedef for a function pointer: 49 | /// ```rust 50 | /// // Creates a typedef for a callback function 51 | /// let callback_type = Type::new(BaseType::FunctionPointer(/* function pointer details */)).build(); 52 | /// let typedef = TypeDef::new(callback_type, "Callback".to_string()).build(); 53 | /// // Outputs something like: typedef void (*Callback)(int, void*); 54 | /// ``` 55 | #[derive(Debug, Clone, DisplayFromFormat)] 56 | pub struct TypeDef { 57 | /// The type to be aliased. 58 | pub t: Type, 59 | 60 | /// The name of the new type alias. 61 | pub name: String, 62 | } 63 | 64 | impl TypeDef { 65 | /// Creates and returns a new `TypeDefBuilder` to construct a `TypeDef` using the builder pattern. 66 | /// 67 | /// This method initializes a builder with the provided original type and alias name. 68 | /// The builder pattern allows for a clear and flexible way to construct `TypeDef` instances. 69 | /// 70 | /// # Parameters 71 | /// 72 | /// * `t` - The original `Type` that will be aliased by the typedef. 73 | /// * `name` - The name for the new type alias as a `String`. 74 | /// 75 | /// # Returns 76 | /// 77 | /// Returns a `TypeDefBuilder` instance configured with the provided type and name. 78 | /// 79 | /// # Examples 80 | /// 81 | /// ```rust 82 | /// let original_type = Type::new(BaseType::Int).build(); 83 | /// let typedef_builder = TypeDef::new(original_type, "Integer".to_string()); 84 | /// let typedef = typedef_builder.build(); 85 | /// 86 | /// assert_eq!(typedef.to_string(), "typedef int Integer;\n"); 87 | /// ``` 88 | pub fn new(t: Type, name: String) -> TypeDefBuilder { 89 | TypeDefBuilder::new(t, name) 90 | } 91 | 92 | /// Converts this typedef declaration to a `Type` instance. 93 | /// 94 | /// This method creates a new `Type` representing a reference to this typedef. 95 | /// This is useful when you want to use the typedef name in other type declarations. 96 | /// 97 | /// # Returns 98 | /// 99 | /// Returns a new `Type` instance that references this typedef by name. 100 | /// 101 | /// # Examples 102 | /// 103 | /// ```rust 104 | /// // Create a typedef for a struct 105 | /// let struct_type = Type::new(BaseType::Struct("Point".to_string())).build(); 106 | /// let typedef = TypeDef::new(struct_type, "Point".to_string()).build(); 107 | /// 108 | /// // Use the typedef in another declaration 109 | /// let point_type = typedef.to_type(); 110 | /// let pointer_to_point = Type::new(BaseType::Pointer(Box::new(point_type))).build(); 111 | /// 112 | /// // This would represent: Point* pointPtr; 113 | /// ``` 114 | pub fn to_type(&self) -> Type { 115 | Type::new(BaseType::TypeDef(self.name.clone())).build() 116 | } 117 | } 118 | 119 | impl Format for TypeDef { 120 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 121 | write!(fmt, "typedef ")?; 122 | self.t.format(fmt)?; 123 | writeln!(fmt, " {};", self.name) 124 | } 125 | } 126 | 127 | /// A builder for constructing a `TypeDef` instance. 128 | /// 129 | /// This builder provides a clear and concise way to create `TypeDef` instances. 130 | /// It allows setting the original type and alias name before building the final typedef. 131 | /// 132 | /// # Examples 133 | /// 134 | /// ```rust 135 | /// // Using the builder directly 136 | /// let builder = TypeDefBuilder::new_with_str( 137 | /// Type::new(BaseType::Char).build(), 138 | /// "Byte" 139 | /// ); 140 | /// let typedef = builder.build(); 141 | /// 142 | /// // Or using the TypeDef::new method 143 | /// let typedef = TypeDef::new( 144 | /// Type::new(BaseType::Double).build(), 145 | /// "Real".to_string() 146 | /// ).build(); 147 | /// ``` 148 | pub struct TypeDefBuilder { 149 | t: Type, 150 | name: String, 151 | } 152 | 153 | impl TypeDefBuilder { 154 | /// Creates a new `TypeDefBuilder` with the specified type and name. 155 | /// 156 | /// # Parameters 157 | /// 158 | /// * `t` - The original `Type` that will be aliased by the typedef. 159 | /// * `name` - The name for the new type alias as a `String`. 160 | /// 161 | /// # Returns 162 | /// 163 | /// Returns a new `TypeDefBuilder` configured with the provided type and name. 164 | /// 165 | /// # Examples 166 | /// 167 | /// ```rust 168 | /// let original_type = Type::new(BaseType::Long).build(); 169 | /// let builder = TypeDefBuilder::new(original_type, "BigInt".to_string()); 170 | /// let typedef = builder.build(); 171 | /// ``` 172 | pub fn new(t: Type, name: String) -> Self { 173 | Self { t, name } 174 | } 175 | 176 | /// Creates a new `TypeDefBuilder` with the specified type and a string slice name. 177 | /// 178 | /// This is a convenience method that converts the string slice to a `String` internally. 179 | /// 180 | /// # Parameters 181 | /// 182 | /// * `t` - The original `Type` that will be aliased by the typedef. 183 | /// * `name` - The name for the new type alias as a string slice. 184 | /// 185 | /// # Returns 186 | /// 187 | /// Returns a new `TypeDefBuilder` configured with the provided type and name. 188 | /// 189 | /// # Examples 190 | /// 191 | /// ```rust 192 | /// let original_type = Type::new(BaseType::Unsigned(BaseType::Int)).build(); 193 | /// let builder = TypeDefBuilder::new_with_str(original_type, "UInt"); 194 | /// let typedef = builder.build(); 195 | /// ``` 196 | pub fn new_with_str(t: Type, name: &str) -> Self { 197 | Self::new(t, name.to_string()) 198 | } 199 | 200 | /// Consumes the builder and returns a fully constructed `TypeDef` instance. 201 | /// 202 | /// # Returns 203 | /// 204 | /// Returns a new `TypeDef` with the type and name configured in this builder. 205 | /// 206 | /// # Examples 207 | /// 208 | /// ```rust 209 | /// let builder = TypeDefBuilder::new_with_str( 210 | /// Type::new(BaseType::Void).build(), 211 | /// "Nothing" 212 | /// ); 213 | /// let typedef = builder.build(); 214 | /// 215 | /// assert_eq!(typedef.to_string(), "typedef void Nothing;\n"); 216 | /// ``` 217 | pub fn build(self) -> TypeDef { 218 | TypeDef { 219 | t: self.t, 220 | name: self.name, 221 | } 222 | } 223 | } 224 | 225 | #[cfg(test)] 226 | mod tests { 227 | use super::*; 228 | use crate::*; 229 | 230 | #[test] 231 | fn typedef() { 232 | let t = TypeDefBuilder::new_with_str( 233 | TypeBuilder::new(BaseType::Struct("Person".to_string())).build(), 234 | "Person", 235 | ) 236 | .build(); 237 | let res = "typedef struct Person Person;\n"; 238 | 239 | assert_eq!(t.to_string(), res); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /tamago/src/union.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Nobuharu Shimazu 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | //! This module provides tools for creating and managing C unions in Rust. 22 | //! 23 | //! It allows for programmatic generation of C union declarations with support 24 | //! for fields, documentation, and conversion to a `Type`. This is particularly 25 | //! useful for generating C code or bindings from Rust. 26 | 27 | use std::fmt::{self, Write}; 28 | 29 | use crate::{BaseType, DocComment, Field, Format, Formatter, Type}; 30 | use tamacro::DisplayFromFormat; 31 | 32 | /// Represents a C union with its fields and attributes. 33 | /// 34 | /// The `Union` struct enables the creation of complete C union declarations, 35 | /// supporting a name, a list of fields, and optional documentation comments. 36 | /// Unions in C allow multiple fields to share the same memory location. 37 | /// 38 | /// # Examples 39 | /// 40 | /// Basic union declaration: 41 | /// ```c 42 | /// union Data { 43 | /// int i; 44 | /// float f; 45 | /// }; 46 | /// ``` 47 | /// 48 | /// Union with array field: 49 | /// ```c 50 | /// union Buffer { 51 | /// char data[16]; 52 | /// int status; 53 | /// }; 54 | /// ``` 55 | #[derive(Debug, Clone, DisplayFromFormat)] 56 | pub struct Union { 57 | /// The name of the union. 58 | pub name: String, 59 | 60 | /// The fields of the union. 61 | pub fields: Vec, 62 | 63 | /// The optional documentation comment for the union. 64 | pub doc: Option, 65 | } 66 | 67 | impl Union { 68 | /// Creates and returns a new `UnionBuilder` to construct a `Union` using the builder pattern. 69 | /// 70 | /// This method simplifies the process of creating complex union declarations 71 | /// by providing an interface for defining all union properties. 72 | /// 73 | /// # Parameters 74 | /// 75 | /// * `name` - The name of the union 76 | /// 77 | /// # Returns 78 | /// 79 | /// A `UnionBuilder` instance that can be used to configure and build a `Union` 80 | /// 81 | /// # Examples 82 | /// 83 | /// ```rust 84 | /// let union = Union::new("Value".to_string()) 85 | /// .field(FieldBuilder::new_with_str("x", Type::new(BaseType::Int).build()).build()) 86 | /// .field(FieldBuilder::new_with_str("y", Type::new(BaseType::Float).build()).build()) 87 | /// .build(); 88 | /// ``` 89 | pub fn new(name: String) -> UnionBuilder { 90 | UnionBuilder::new(name) 91 | } 92 | 93 | /// Converts the union to a `Type` for use in other declarations. 94 | /// 95 | /// # Returns 96 | /// 97 | /// A `Type` instance representing the union as `union ` 98 | /// 99 | /// # Examples 100 | /// 101 | /// ```rust 102 | /// let union = Union::new("Data".to_string()).build(); 103 | /// let union_type = union.to_type(); 104 | /// assert_eq!(union_type.to_string(), "union Data"); 105 | /// ``` 106 | pub fn to_type(&self) -> Type { 107 | Type::new(BaseType::Union(self.name.clone())).build() 108 | } 109 | } 110 | 111 | impl Format for Union { 112 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 113 | if let Some(doc) = &self.doc { 114 | doc.format(fmt)?; 115 | } 116 | 117 | write!(fmt, "union {}", self.name)?; 118 | 119 | if !self.fields.is_empty() { 120 | fmt.block(|fmt| { 121 | for field in &self.fields { 122 | field.format(fmt)?; 123 | } 124 | Ok(()) 125 | })?; 126 | } 127 | 128 | writeln!(fmt, ";") 129 | } 130 | } 131 | 132 | /// A builder for constructing a `Union` instance with a fluent interface. 133 | /// 134 | /// The `UnionBuilder` provides a step-by-step approach to creating complex C unions 135 | /// by allowing incremental configuration of the union's name, fields, and documentation. 136 | pub struct UnionBuilder { 137 | name: String, 138 | fields: Vec, 139 | doc: Option, 140 | } 141 | 142 | impl UnionBuilder { 143 | /// Creates and returns a new `UnionBuilder` with the specified name. 144 | /// 145 | /// # Parameters 146 | /// 147 | /// * `name` - The name of the union as a String 148 | /// 149 | /// # Returns 150 | /// 151 | /// A new `UnionBuilder` instance with default values for fields and documentation 152 | /// 153 | /// # Examples 154 | /// 155 | /// ```rust 156 | /// let builder = UnionBuilder::new("Status".to_string()); 157 | /// ``` 158 | pub fn new(name: String) -> Self { 159 | Self { 160 | name, 161 | fields: vec![], 162 | doc: None, 163 | } 164 | } 165 | 166 | /// Creates and returns a new `UnionBuilder` using a string slice for the name. 167 | /// 168 | /// This is a convenience method that converts the provided string slice to a String 169 | /// before delegating to the standard `new` method. 170 | /// 171 | /// # Parameters 172 | /// 173 | /// * `name` - The name of the union as a string slice 174 | /// 175 | /// # Returns 176 | /// 177 | /// A new `UnionBuilder` instance 178 | /// 179 | /// # Examples 180 | /// 181 | /// ```rust 182 | /// let builder = UnionBuilder::new_with_str("Info"); 183 | /// ``` 184 | pub fn new_with_str(name: &str) -> Self { 185 | Self::new(name.to_string()) 186 | } 187 | 188 | /// Sets the documentation comment for the union being built. 189 | /// 190 | /// # Parameters 191 | /// 192 | /// * `doc` - The documentation comment to attach to the union 193 | /// 194 | /// # Returns 195 | /// 196 | /// The builder instance for method chaining 197 | /// 198 | /// # Examples 199 | /// 200 | /// ```rust 201 | /// let builder = UnionBuilder::new_with_str("Result") 202 | /// .doc(DocComment::new("Represents a computation result").build()); 203 | /// ``` 204 | pub fn doc(mut self, doc: DocComment) -> Self { 205 | self.doc = Some(doc); 206 | self 207 | } 208 | 209 | /// Adds a field to the union being built. 210 | /// 211 | /// # Parameters 212 | /// 213 | /// * `field` - The field to add to the union 214 | /// 215 | /// # Returns 216 | /// 217 | /// The builder instance for method chaining 218 | /// 219 | /// # Examples 220 | /// 221 | /// ```rust 222 | /// let builder = UnionBuilder::new_with_str("Data") 223 | /// .field(FieldBuilder::new_with_str("id", Type::new(BaseType::Int).build()).build()); 224 | /// ``` 225 | pub fn field(mut self, field: Field) -> Self { 226 | self.fields.push(field); 227 | self 228 | } 229 | 230 | /// Sets the complete list of fields for the union being built. 231 | /// 232 | /// This method replaces any previously added fields with the provided list. 233 | /// 234 | /// # Parameters 235 | /// 236 | /// * `fields` - A vector of `Field` objects representing the union's fields 237 | /// 238 | /// # Returns 239 | /// 240 | /// The builder instance for method chaining 241 | /// 242 | /// # Examples 243 | /// 244 | /// ```rust 245 | /// let fields = vec![ 246 | /// FieldBuilder::new_with_str("x", Type::new(BaseType::Int).build()).build(), 247 | /// FieldBuilder::new_with_str("y", Type::new(BaseType::Float).build()).build() 248 | /// ]; 249 | /// let builder = UnionBuilder::new_with_str("Point").fields(fields); 250 | /// ``` 251 | pub fn fields(mut self, fields: Vec) -> Self { 252 | self.fields = fields; 253 | self 254 | } 255 | 256 | /// Finalizes the union definition and returns a fully constructed `Union`. 257 | /// 258 | /// # Returns 259 | /// 260 | /// A fully constructed `Union` instance 261 | /// 262 | /// # Examples 263 | /// 264 | /// ```rust 265 | /// let union = UnionBuilder::new_with_str("Value") 266 | /// .field(FieldBuilder::new_with_str("a", Type::new(BaseType::Int).build()).build()) 267 | /// .build(); 268 | /// ``` 269 | pub fn build(self) -> Union { 270 | Union { 271 | name: self.name, 272 | fields: self.fields, 273 | doc: self.doc, 274 | } 275 | } 276 | } 277 | 278 | #[cfg(test)] 279 | mod tests { 280 | use super::*; 281 | use crate::*; 282 | 283 | #[test] 284 | fn union() { 285 | let u = UnionBuilder::new_with_str("some_union") 286 | .fields(vec![ 287 | FieldBuilder::new_with_str( 288 | "a", 289 | TypeBuilder::new(BaseType::Char).make_array(20).build(), 290 | ) 291 | .build(), 292 | FieldBuilder::new_with_str("b", TypeBuilder::new(BaseType::Int).build()).build(), 293 | FieldBuilder::new_with_str("c", TypeBuilder::new(BaseType::Bool).build()).build(), 294 | ]) 295 | .build(); 296 | let res = r#"union some_union { 297 | char a[20]; 298 | int b; 299 | bool c; 300 | }; 301 | "#; 302 | 303 | assert_eq!(u.to_string(), res); 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /src/semantic_analyzer/resolver.rs: -------------------------------------------------------------------------------- 1 | use std::collections::hash_map::Entry; 2 | use std::collections::HashMap; 3 | 4 | use crate::parser::*; 5 | use crate::semantic_analyzer::*; 6 | 7 | #[derive(Debug, Default)] 8 | struct Scope<'ast> { 9 | names: HashMap<&'ast str, (Span, bool)>, 10 | enclosing: Option>>, 11 | } 12 | 13 | #[derive(Debug)] 14 | pub struct Resolver<'ast> { 15 | ast: &'ast Vec, 16 | scope: Scope<'ast>, 17 | 18 | errors: Vec, 19 | warnings: Vec, 20 | } 21 | 22 | impl<'ast> Resolver<'ast> { 23 | pub fn new(ast: &'ast Vec) -> Self { 24 | Self { 25 | ast, 26 | scope: Scope::new(), 27 | errors: vec![], 28 | warnings: vec![], 29 | } 30 | } 31 | 32 | pub fn resolve(mut self) -> (Vec, Result<(), Vec>) { 33 | for stmt in self.ast { 34 | self.resolve_global_stmt(stmt); 35 | } 36 | 37 | for (name, (span, used)) in self.scope.names { 38 | if !used && name != "main" { 39 | self.warnings.push((span, format!("'{name}' is not used"))); 40 | } 41 | } 42 | 43 | if self.errors.is_empty() { 44 | (self.warnings, Ok(())) 45 | } else { 46 | (self.warnings, Err(self.errors)) 47 | } 48 | } 49 | 50 | fn resolve_global_stmt(&mut self, stmt: &'ast LocatedGlobalStmt) { 51 | use GlobalStmt::*; 52 | 53 | let Located { node: stmt, span } = stmt; 54 | 55 | match stmt { 56 | Enum { name, .. } 57 | | Struct { name, .. } 58 | | Union { name, .. } 59 | | Variable { name, .. } 60 | | Constant { name, .. } 61 | | Alias { name, .. } => { 62 | if let Err(err) = self.scope.declare(&name, span.clone()) { 63 | self.errors.push(err); 64 | } 65 | } 66 | Function { 67 | name, 68 | params, 69 | ret: _, 70 | body, 71 | } => self.resolve_func(span, name, params, body), 72 | Import { name, path } => self.resolve_import(span, name, path), 73 | } 74 | } 75 | 76 | fn resolve_func( 77 | &mut self, 78 | span: &Span, 79 | name: &'ast String, 80 | params: &'ast Vec<(String, LocatedType)>, 81 | body: &'ast Vec, 82 | ) { 83 | if let Err(err) = self.scope.declare(&name, span.clone()) { 84 | self.errors.push(err); 85 | } 86 | 87 | let old_scope = std::mem::take(&mut self.scope); 88 | self.scope = Scope::new_with_scope(old_scope); 89 | 90 | for (name, t) in params { 91 | if let Err(err) = self.scope.declare(name, t.span.clone()) { 92 | self.errors.push(err); 93 | } 94 | } 95 | 96 | for stmt in body { 97 | self.resolve_stmt(stmt); 98 | } 99 | 100 | self.scope = *std::mem::take(&mut self.scope.enclosing).unwrap(); 101 | } 102 | 103 | fn resolve_import(&mut self, span: &Span, name: &String, path: &String) { 104 | // TODO: Handle whether the module being imported even exists or not 105 | todo!() 106 | } 107 | 108 | fn resolve_stmt(&mut self, stmt: &'ast LocatedStmt) { 109 | use crate::parser::Stmt::*; 110 | 111 | let Located { node: stmt, span } = stmt; 112 | 113 | match stmt { 114 | Variable { name, value, .. } => { 115 | if let Err(err) = self.scope.has(&name, span.clone()) { 116 | self.errors.push(err); 117 | } 118 | if let Some(value) = value { 119 | self.resolve_expr(value); 120 | } 121 | } 122 | Expression { expr } 123 | | Return { value: Some(expr) } 124 | | Destroy { expr } 125 | | Free { expr } => { 126 | self.resolve_expr(expr); 127 | } 128 | If { cond, then, other } => self.resolve_if(cond, then, other), 129 | While { cond, body, .. } => self.resolve_while(cond, body), 130 | Defer { body } => { 131 | for stmt in body { 132 | self.resolve_stmt(stmt); 133 | } 134 | } 135 | Break | Continue | Return { value: None } => {} 136 | } 137 | } 138 | 139 | fn resolve_if( 140 | &mut self, 141 | cond: &'ast LocatedExpr, 142 | then: &'ast Vec, 143 | other: &'ast Option>, 144 | ) { 145 | self.resolve_expr(cond); 146 | 147 | for stmt in then { 148 | self.resolve_stmt(stmt); 149 | } 150 | 151 | if let Some(other) = other { 152 | for stmt in other { 153 | self.resolve_stmt(stmt); 154 | } 155 | } 156 | } 157 | 158 | fn resolve_while(&mut self, cond: &'ast LocatedExpr, body: &'ast Vec) { 159 | self.resolve_expr(cond); 160 | 161 | for stmt in body { 162 | self.resolve_stmt(stmt); 163 | } 164 | } 165 | 166 | fn resolve_expr(&mut self, expr: &'ast LocatedExpr) { 167 | use crate::parser::Expr::*; 168 | 169 | let Located { node: expr, span } = expr; 170 | 171 | match expr { 172 | Int(_) 173 | | Double(_) 174 | | Bool(_) 175 | | Char(_) 176 | | Str(_) 177 | | Sizeof { .. } 178 | | Make { .. } 179 | | New { .. } => {} 180 | Ident(name) => { 181 | if let Err(err) = self.scope.has(&name, span.clone()) { 182 | self.errors.push(err); 183 | } 184 | } 185 | Binary { left, right, .. } => { 186 | self.resolve_expr(left); 187 | self.resolve_expr(right); 188 | } 189 | Parenthesized { expr } 190 | | Unary { expr, .. } 191 | | MemAccess { expr, .. } 192 | | Cast { expr, .. } => { 193 | self.resolve_expr(expr); 194 | } 195 | Assign { lvalue, value, .. } => { 196 | self.resolve_assign(lvalue, value); 197 | } 198 | Ternary { cond, lexpr, rexpr } => { 199 | self.resolve_expr(cond); 200 | self.resolve_expr(lexpr); 201 | self.resolve_expr(rexpr); 202 | } 203 | FnCall { name, args } => { 204 | self.resolve_expr(name); 205 | for arg in args { 206 | self.resolve_expr(arg); 207 | } 208 | } 209 | EnumVarAccess { ident, .. } => { 210 | if let Err(err) = self.scope.has(&ident, span.clone()) { 211 | self.errors.push(err); 212 | } 213 | } 214 | ArrIndex { arr, idx } => { 215 | self.resolve_expr(arr); 216 | self.resolve_expr(idx); 217 | } 218 | InitArr { elems } | InitArrDesignated { elems, .. } => { 219 | for elem in elems { 220 | self.resolve_expr(elem); 221 | } 222 | } 223 | InitStruct { ident, args } => { 224 | if let Err(err) = self.scope.has(&ident, span.clone()) { 225 | self.errors.push(err); 226 | } 227 | for arg in args { 228 | self.resolve_expr(&arg.1); 229 | } 230 | } 231 | } 232 | } 233 | 234 | fn resolve_assign(&mut self, lvalue: &'ast Box, value: &'ast Box) { 235 | use crate::parser::Expr::*; 236 | 237 | let Located { 238 | node: lvalue, 239 | span: lspan, 240 | } = &**lvalue; 241 | 242 | match lvalue { 243 | Ident(var) => { 244 | if let Err(err) = self.scope.has(&var, lspan.clone()) { 245 | self.errors.push(err); 246 | } 247 | } 248 | // Should I allow Expr::Assign to be a lvalue? 249 | MemAccess { expr, .. } => { 250 | self.resolve_expr(&expr); 251 | } 252 | ArrIndex { arr, idx } => { 253 | self.resolve_expr(&arr); 254 | self.resolve_expr(&idx); 255 | } 256 | _ => self 257 | .errors 258 | .push((lspan.clone(), format!("Invalid lvalue for assignment"))), 259 | } 260 | 261 | self.resolve_expr(value); 262 | } 263 | } 264 | 265 | impl<'ast> Scope<'ast> { 266 | pub fn new() -> Self { 267 | Self { 268 | names: HashMap::new(), 269 | enclosing: None, 270 | } 271 | } 272 | 273 | pub fn new_with_scope(scope: Scope<'ast>) -> Self { 274 | Self { 275 | names: HashMap::new(), 276 | enclosing: Some(Box::new(scope)), 277 | } 278 | } 279 | 280 | pub fn declare(&mut self, name: &'ast str, span: Span) -> Result<(), Message> { 281 | match self.names.entry(name) { 282 | Entry::Occupied(_) => Err((span, format!("'{name}' is already declared"))), 283 | Entry::Vacant(entry) => { 284 | entry.insert((span, false)); 285 | Ok(()) 286 | } 287 | } 288 | } 289 | 290 | pub fn has(&mut self, name: &'ast str, span: Span) -> Result<(), Message> { 291 | if self.names.contains_key(name) { 292 | *self.names.get_mut(&name).unwrap() = (span, true); 293 | Ok(()) 294 | } else if let Some(scope) = &mut self.enclosing { 295 | scope.has(name, span) 296 | } else { 297 | let threshold = 1; 298 | for n in self.names.keys() { 299 | if threshold >= edit_distance(n, name) { 300 | return Err((span, format!("By '{name}', did you mean '{n}'?"))); 301 | } 302 | } 303 | Err((span, format!("'{name}' is not declared"))) 304 | } 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /tamago/src/scope.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Nobuharu Shimazu 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | //! This module provides structures and builders for creating and managing C code blocks, 22 | //! such as function bodies, loop bodies, and conditional branches. 23 | //! 24 | //! It defines the primary representation of a C program through the `Scope` struct, 25 | //! which contains global statements and serves as the root container for generated code. 26 | //! The module follows a builder pattern approach for constructing C code structures. 27 | 28 | use std::fmt::{self, Write}; 29 | 30 | use crate::*; 31 | use tamacro::DisplayFromFormat; 32 | 33 | /// Represents a global scope in C, serving as the root container for all C code elements. 34 | /// 35 | /// The global scope can contain various global statements like function definitions, 36 | /// variable declarations, struct definitions, and preprocessor directives. 37 | /// 38 | /// # Examples 39 | /// ```c 40 | /// int number = 0; // Global variable declaration 41 | /// 42 | /// int main(void) { 43 | /// // Function body block 44 | /// } 45 | /// ``` 46 | #[derive(Debug, Clone, DisplayFromFormat)] 47 | pub struct Scope { 48 | /// Optional documentation comment associated with the scope. 49 | pub doc: Option, 50 | 51 | /// The collection of global statements contained within this scope. 52 | pub global_stmts: Vec, 53 | } 54 | 55 | impl Scope { 56 | /// Creates and returns a new `ScopeBuilder` to construct a `Scope` using the builder pattern. 57 | /// 58 | /// This is the recommended way to create a new `Scope` instance. 59 | /// 60 | /// # Returns 61 | /// A new `ScopeBuilder` instance that can be used to build a `Scope`. 62 | /// 63 | /// # Examples 64 | /// ```rust 65 | /// let scope = Scope::new() 66 | /// .global_statement(GlobalStatement::Include(include)) 67 | /// .new_line() 68 | /// .global_statement(GlobalStatement::Function(function)) 69 | /// .build(); 70 | /// ``` 71 | /// 72 | pub fn new() -> ScopeBuilder { 73 | ScopeBuilder::new() 74 | } 75 | } 76 | 77 | impl Format for Scope { 78 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 79 | if let Some(doc) = &self.doc { 80 | doc.format(fmt)?; 81 | } 82 | 83 | for stmt in &self.global_stmts { 84 | stmt.format(fmt)?; 85 | } 86 | 87 | Ok(()) 88 | } 89 | } 90 | 91 | /// A builder for constructing a `Scope` instance using the builder pattern. 92 | /// 93 | /// This builder provides methods to add various elements to a scope and finally build 94 | /// the complete `Scope` instance. 95 | pub struct ScopeBuilder { 96 | doc: Option, 97 | global_stmts: Vec, 98 | } 99 | 100 | impl ScopeBuilder { 101 | /// Creates and returns a new `ScopeBuilder` to construct a `Scope`. 102 | /// 103 | /// # Returns 104 | /// A new `ScopeBuilder` instance with default (empty) values. 105 | /// 106 | /// # Examples 107 | /// ```rust 108 | /// let scope = ScopeBuilder::new() 109 | /// .global_statement(GlobalStatement::Include(include)) 110 | /// .new_line() 111 | /// .build(); 112 | /// ``` 113 | pub fn new() -> Self { 114 | Self { 115 | doc: None, 116 | global_stmts: vec![], 117 | } 118 | } 119 | 120 | /// Sets the documentation comment for the scope being built. 121 | /// 122 | /// # Parameters 123 | /// * `doc` - The documentation comment to associate with the scope. 124 | /// 125 | /// # Returns 126 | /// The builder instance for method chaining. 127 | pub fn doc(mut self, doc: DocComment) -> Self { 128 | self.doc = Some(doc); 129 | self 130 | } 131 | 132 | /// Sets all the global statements of the scope at once, replacing any existing statements. 133 | /// 134 | /// # Parameters 135 | /// * `global_stmts` - A vector of global statements to include in the scope. 136 | /// 137 | /// # Returns 138 | /// The builder instance for method chaining. 139 | pub fn global_statements(mut self, global_stmts: Vec) -> Self { 140 | self.global_stmts = global_stmts; 141 | self 142 | } 143 | 144 | /// Appends a single global statement to the scope. 145 | /// 146 | /// # Parameters 147 | /// * `global_stmt` - The global statement to append to the scope. 148 | /// 149 | /// # Returns 150 | /// The builder instance for method chaining. 151 | pub fn global_statement(mut self, global_stmt: GlobalStatement) -> Self { 152 | self.global_stmts.push(global_stmt); 153 | self 154 | } 155 | 156 | /// Appends a new line to the scope for better formatting of the generated code. 157 | /// 158 | /// This is equivalent to adding `GlobalStatement::NewLine` to the scope. 159 | /// 160 | /// # Returns 161 | /// The builder instance for method chaining. 162 | pub fn new_line(self) -> Self { 163 | self.global_statement(GlobalStatement::NewLine) 164 | } 165 | 166 | /// Consumes the builder and returns the constructed `Scope` instance. 167 | /// 168 | /// # Returns 169 | /// A `Scope` instance containing the documentation and global statements 170 | /// configured in this builder. 171 | pub fn build(self) -> Scope { 172 | Scope { 173 | doc: self.doc, 174 | global_stmts: self.global_stmts, 175 | } 176 | } 177 | } 178 | 179 | /// Represents a global statement in C that can appear at the top level of a C file. 180 | /// 181 | /// Global statements include function definitions, variable declarations, 182 | /// type definitions, preprocessor directives, and more. 183 | /// 184 | /// # Examples 185 | /// ```c 186 | /// int number = 0; // Variable declaration 187 | /// 188 | /// struct Person { // Struct definition 189 | /// char* name; 190 | /// int age; 191 | /// }; 192 | /// ``` 193 | #[derive(Debug, Clone, DisplayFromFormat)] 194 | pub enum GlobalStatement { 195 | /// A comment in the code. 196 | Comment(Comment), 197 | 198 | /// An enum definition (e.g., `enum Color { RED, GREEN, BLUE };`). 199 | Enum(Enum), 200 | 201 | /// A struct definition (e.g., `struct Person { char* name; int age; };`). 202 | Struct(Struct), 203 | 204 | /// A function declaration or definition. 205 | Function(Function), 206 | 207 | /// A union definition (e.g., `union Data { int i; float f; };`). 208 | Union(Union), 209 | 210 | /// A variable declaration or definition. 211 | Variable(Variable), 212 | 213 | /// A typedef statement 214 | TypeDef(TypeDef), 215 | 216 | /// An error preprocessor directive (e.g., `#error "Not supported"`). 217 | ErrorDirective(ErrorDirective), 218 | 219 | /// An ifdef preprocessor directive (e.g., `#ifdef DEBUG`). 220 | IfDefDirective(IfDefDirective), 221 | 222 | /// An if preprocessor directive (e.g., `#if PLATFORM == WINDOWS`). 223 | IfDirective(IfDirective), 224 | 225 | /// An include preprocessor directive (e.g., `#include `). 226 | Include(Include), 227 | 228 | /// A line preprocessor directive (e.g., `#line 50 "file.c"`). 229 | LineDirective(LineDirective), 230 | 231 | /// A macro definition (e.g., `#define MAX(a, b) ((a) > (b) ? (a) : (b))`). 232 | Macro(Macro), 233 | 234 | /// A pragma preprocessor directive (e.g., `#pragma once`). 235 | PragmaDirective(PragmaDirective), 236 | 237 | /// A warning preprocessor directive (e.g., `#warning "Deprecated feature"`). 238 | WarningDirective(WarningDirective), 239 | 240 | /// A raw piece of code inserted directly without processing. 241 | Raw(String), 242 | 243 | /// A new line for formatting purposes. 244 | NewLine, 245 | } 246 | 247 | impl Format for GlobalStatement { 248 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 249 | use GlobalStatement::*; 250 | match self { 251 | Comment(c) => c.format(fmt), 252 | Enum(e) => e.format(fmt), 253 | Struct(s) => s.format(fmt), 254 | Function(f) => f.format(fmt), 255 | Union(u) => u.format(fmt), 256 | Variable(v) => v.format(fmt), 257 | TypeDef(t) => t.format(fmt), 258 | ErrorDirective(e) => e.format(fmt), 259 | IfDefDirective(i) => i.format(fmt), 260 | IfDirective(i) => i.format(fmt), 261 | Include(i) => i.format(fmt), 262 | LineDirective(l) => l.format(fmt), 263 | Macro(m) => m.format(fmt), 264 | PragmaDirective(p) => p.format(fmt), 265 | WarningDirective(w) => w.format(fmt), 266 | Raw(r) => writeln!(fmt, "{r}"), 267 | NewLine => writeln!(fmt), 268 | } 269 | } 270 | } 271 | 272 | #[cfg(test)] 273 | mod tests { 274 | use super::*; 275 | 276 | #[test] 277 | fn scope() { 278 | let s = ScopeBuilder::new() 279 | .global_statement(GlobalStatement::WarningDirective( 280 | WarningDirectiveBuilder::new_with_str("some warning").build(), 281 | )) 282 | .new_line() 283 | .global_statement(GlobalStatement::Include( 284 | IncludeBuilder::new_system_with_str("stdio.h").build(), 285 | )) 286 | .build(); 287 | let res = r#"#warning "some warning" 288 | 289 | #include 290 | "#; 291 | 292 | assert_eq!(s.to_string(), res); 293 | 294 | let s = ScopeBuilder::new() 295 | .global_statements(vec![ 296 | GlobalStatement::Comment(CommentBuilder::new().comment_with_str("Hello").build()), 297 | GlobalStatement::NewLine, 298 | GlobalStatement::Function( 299 | FunctionBuilder::new_with_str("some_func", Type::new(BaseType::Bool).build()) 300 | .body( 301 | BlockBuilder::new() 302 | .statement(Statement::Return(None)) 303 | .build(), 304 | ) 305 | .doc( 306 | DocCommentBuilder::new() 307 | .line_str("this is a function") 308 | .build(), 309 | ) 310 | .build(), 311 | ), 312 | ]) 313 | .build(); 314 | let res = r#"// Hello 315 | 316 | /// this is a function 317 | bool some_func(void) { 318 | return; 319 | } 320 | "#; 321 | 322 | assert_eq!(s.to_string(), res); 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /tamago/src/variable.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Nobuharu Shimazu 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | //! This module provides tools for creating and managing C variables in Rust. 22 | //! 23 | //! It allows for programmatic generation of C variable declarations with support 24 | //! for various attributes such as `static` and `extern`, initial values, types, 25 | //! and documentation. This is particularly useful for generating C code or bindings 26 | //! from Rust. 27 | 28 | use std::fmt::{self, Write}; 29 | 30 | use crate::{DocComment, Expr, Format, Formatter, Type}; 31 | use tamacro::DisplayFromFormat; 32 | 33 | /// Represents a C variable with its properties and attributes. 34 | /// 35 | /// The `Variable` struct enables the creation of complete C variable declarations, 36 | /// supporting type definitions, initial values, static and extern modifiers, and 37 | /// documentation comments. 38 | /// 39 | /// # Examples 40 | /// 41 | /// Basic variable declaration: 42 | /// ```c 43 | /// int counter; 44 | /// ``` 45 | /// 46 | /// Initialized static variable: 47 | /// ```c 48 | /// static float value = 3.14; 49 | /// ``` 50 | /// 51 | /// External variable declaration: 52 | /// ```c 53 | /// extern char* message; 54 | /// ``` 55 | #[derive(Debug, Clone, DisplayFromFormat)] 56 | pub struct Variable { 57 | /// The name of the variable 58 | pub name: String, 59 | 60 | /// The type of the variable 61 | pub t: Type, 62 | 63 | /// The optional initial value of the variable 64 | pub value: Option, 65 | 66 | /// Whether the variable is declared with the `static` keyword 67 | pub is_static: bool, 68 | 69 | /// Whether the variable is declared with the `extern` keyword 70 | pub is_extern: bool, 71 | 72 | /// The optional documentation comment for the variable 73 | pub doc: Option, 74 | } 75 | 76 | impl Variable { 77 | /// Creates and returns a new `VariableBuilder` to construct a `Variable` using the builder pattern. 78 | /// 79 | /// This method simplifies the process of creating complex variable declarations 80 | /// by providing an interface for defining all variable properties. 81 | /// 82 | /// # Parameters 83 | /// 84 | /// * `name` - The name of the variable 85 | /// * `t` - The type of the variable 86 | /// 87 | /// # Returns 88 | /// 89 | /// A `VariableBuilder` instance that can be used to configure and build a `Variable` 90 | /// 91 | /// # Examples 92 | /// 93 | /// ```rust 94 | /// let var = Variable::new("message".to_string(), Type::new(BaseType::Char).make_pointer().build()) 95 | /// .value(Expr::Str("Hello".to_string())) 96 | /// .build(); 97 | /// ``` 98 | pub fn new(name: String, t: Type) -> VariableBuilder { 99 | VariableBuilder::new(name, t) 100 | } 101 | 102 | /// Returns the type of the variable. 103 | /// 104 | /// # Returns 105 | /// 106 | /// A clone of the variable's `Type` 107 | /// 108 | /// # Examples 109 | /// 110 | /// ```rust 111 | /// let var = Variable::new("x".to_string(), Type::new(BaseType::Int).build()).build(); 112 | /// let t = var.to_type(); 113 | /// assert_eq!(t.to_string(), "int"); 114 | /// ``` 115 | pub fn to_type(&self) -> Type { 116 | self.t.clone() 117 | } 118 | } 119 | 120 | impl Format for Variable { 121 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 122 | if let Some(doc) = &self.doc { 123 | doc.format(fmt)?; 124 | } 125 | 126 | if self.is_extern { 127 | write!(fmt, "extern ")?; 128 | } 129 | 130 | if self.is_static { 131 | write!(fmt, "static ")?; 132 | } 133 | 134 | self.t.format(fmt)?; 135 | write!(fmt, " {}", self.name)?; 136 | 137 | if self.t.is_array() { 138 | write!(fmt, "[{}]", self.t.array)?; 139 | } 140 | 141 | if !self.is_extern { 142 | if let Some(value) = &self.value { 143 | write!(fmt, " = ")?; 144 | value.format(fmt)?; 145 | } 146 | } 147 | 148 | Ok(()) 149 | } 150 | } 151 | 152 | /// A builder for constructing a `Variable` instance with a fluent interface. 153 | /// 154 | /// The `VariableBuilder` provides a step-by-step approach to creating complex C variables 155 | /// by allowing incremental configuration of all variable attributes and properties. 156 | pub struct VariableBuilder { 157 | name: String, 158 | t: Type, 159 | value: Option, 160 | is_static: bool, 161 | is_extern: bool, 162 | doc: Option, 163 | } 164 | 165 | impl VariableBuilder { 166 | /// Creates and returns a new `VariableBuilder` with the specified name and type. 167 | /// 168 | /// # Parameters 169 | /// 170 | /// * `name` - The name of the variable as a String 171 | /// * `t` - The type of the variable 172 | /// 173 | /// # Returns 174 | /// 175 | /// A new `VariableBuilder` instance with default values for other properties 176 | /// 177 | /// # Examples 178 | /// 179 | /// ```rust 180 | /// let builder = VariableBuilder::new("count".to_string(), Type::new(BaseType::Int).build()); 181 | /// ``` 182 | pub fn new(name: String, t: Type) -> Self { 183 | Self { 184 | name, 185 | t, 186 | value: None, 187 | is_static: false, 188 | is_extern: false, 189 | doc: None, 190 | } 191 | } 192 | 193 | /// Creates and returns a new `VariableBuilder` using a string slice for the name. 194 | /// 195 | /// This is a convenience method that converts the provided string slice to a String 196 | /// before delegating to the standard `new` method. 197 | /// 198 | /// # Parameters 199 | /// 200 | /// * `name` - The name of the variable as a string slice 201 | /// * `t` - The type of the variable 202 | /// 203 | /// # Returns 204 | /// 205 | /// A new `VariableBuilder` instance 206 | /// 207 | /// # Examples 208 | /// 209 | /// ```rust 210 | /// let builder = VariableBuilder::new_with_str("flag", Type::new(BaseType::Bool).build()); 211 | /// ``` 212 | pub fn new_with_str(name: &str, t: Type) -> Self { 213 | Self::new(name.to_string(), t) 214 | } 215 | 216 | /// Sets the initial value for the variable being built. 217 | /// 218 | /// # Parameters 219 | /// 220 | /// * `value` - The expression to initialize the variable with 221 | /// 222 | /// # Returns 223 | /// 224 | /// The builder instance for method chaining 225 | /// 226 | /// # Examples 227 | /// 228 | /// ```rust 229 | /// let builder = VariableBuilder::new_with_str("pi", Type::new(BaseType::Float).build()) 230 | /// .value(Expr::Float(3.14)); 231 | /// ``` 232 | pub fn value(mut self, value: Expr) -> Self { 233 | self.value = Some(value); 234 | self 235 | } 236 | 237 | /// Sets the documentation comment for the variable being built. 238 | /// 239 | /// # Parameters 240 | /// 241 | /// * `doc` - The documentation comment to attach to the variable 242 | /// 243 | /// # Returns 244 | /// 245 | /// The builder instance for method chaining 246 | /// 247 | /// # Examples 248 | /// 249 | /// ```rust 250 | /// let builder = VariableBuilder::new_with_str("size", Type::new(BaseType::Int).build()) 251 | /// .doc(DocComment::new("The size of the buffer").build()); 252 | /// ``` 253 | pub fn doc(mut self, doc: DocComment) -> Self { 254 | self.doc = Some(doc); 255 | self 256 | } 257 | 258 | /// Makes the variable static. 259 | /// 260 | /// In C, the `static` keyword limits the variable's scope to the file it is defined in 261 | /// and preserves its value between function calls if local. 262 | /// 263 | /// # Returns 264 | /// 265 | /// The builder instance for method chaining 266 | /// 267 | /// # Examples 268 | /// 269 | /// ```rust 270 | /// let builder = VariableBuilder::new_with_str("counter", Type::new(BaseType::Int).build()) 271 | /// .make_static(); 272 | /// ``` 273 | pub fn make_static(mut self) -> Self { 274 | self.is_static = true; 275 | self 276 | } 277 | 278 | /// Makes the variable extern. 279 | /// 280 | /// In C, the `extern` keyword indicates that the variable is defined elsewhere, 281 | /// typically in another compilation unit. 282 | /// 283 | /// # Returns 284 | /// 285 | /// The builder instance for method chaining 286 | /// 287 | /// # Examples 288 | /// 289 | /// ```rust 290 | /// let builder = VariableBuilder::new_with_str("global_flag", Type::new(BaseType::Bool).build()) 291 | /// .make_extern(); 292 | /// ``` 293 | pub fn make_extern(mut self) -> Self { 294 | self.is_extern = true; 295 | self 296 | } 297 | 298 | /// Sets the initial value of the variable using a raw string. 299 | /// 300 | /// This is a convenience method for setting the value without constructing an `Expr` manually. 301 | /// 302 | /// # Parameters 303 | /// 304 | /// * `value` - The raw string value to initialize the variable with 305 | /// 306 | /// # Returns 307 | /// 308 | /// The builder instance for method chaining 309 | /// 310 | /// # Examples 311 | /// 312 | /// ```rust 313 | /// let builder = VariableBuilder::new_with_str("name", Type::new(BaseType::Char).make_pointer().build()) 314 | /// .raw_value("\"John\"".to_string()); 315 | /// ``` 316 | pub fn raw_value(mut self, value: String) -> Self { 317 | self.value = Some(Expr::Raw(value)); 318 | self 319 | } 320 | 321 | /// Finalizes the variable definition and returns a fully constructed `Variable`. 322 | /// 323 | /// # Returns 324 | /// 325 | /// A fully constructed `Variable` instance 326 | /// 327 | /// # Examples 328 | /// 329 | /// ```rust 330 | /// let var = VariableBuilder::new_with_str("id", Type::new(BaseType::Int).build()) 331 | /// .value(Expr::Int(42)) 332 | /// .build(); 333 | /// assert_eq!(var.to_string(), "int id = 42"); 334 | /// ``` 335 | pub fn build(self) -> Variable { 336 | Variable { 337 | name: self.name, 338 | t: self.t, 339 | value: self.value, 340 | is_static: self.is_static, 341 | is_extern: self.is_extern, 342 | doc: self.doc, 343 | } 344 | } 345 | } 346 | 347 | #[cfg(test)] 348 | mod tests { 349 | use super::*; 350 | use crate::*; 351 | 352 | #[test] 353 | fn var() { 354 | let var = VariableBuilder::new_with_str( 355 | "some_var", 356 | TypeBuilder::new(BaseType::Char) 357 | .make_pointer() 358 | .make_const() 359 | .build(), 360 | ) 361 | .value(Expr::Str("Hello, world".to_string())) 362 | .build(); 363 | 364 | let res = "const char* some_var = \"Hello, world\""; 365 | 366 | assert_eq!(var.to_string(), res); 367 | 368 | let another_var = 369 | VariableBuilder::new_with_str("another_var", TypeBuilder::new(BaseType::Bool).build()) 370 | .make_static() 371 | .build(); 372 | 373 | let another_res = "static bool another_var"; 374 | 375 | assert_eq!(another_var.to_string(), another_res); 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /tamago/src/structs.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Nobuharu Shimazu 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | //! This module provides means to create C structs programmatically in Rust. 22 | //! 23 | //! It offers a builder pattern approach for defining C-compatible struct types 24 | //! with their fields, documentation, and other attributes. This is particularly 25 | //! useful for generating C header files or FFI bindings. 26 | //! 27 | //! For now, nested anonymous structs are not supported, but this might change in the future. 28 | 29 | use std::fmt::{self, Write}; 30 | 31 | use crate::{BaseType, DocComment, Format, Formatter, Type}; 32 | use tamacro::DisplayFromFormat; 33 | 34 | /// Represents a struct in C. 35 | /// 36 | /// This struct holds all the information needed to represent a C struct, 37 | /// including its name, fields, and documentation. 38 | /// 39 | /// # Examples 40 | /// 41 | /// A C struct representation: 42 | /// ```c 43 | /// struct Person { 44 | /// char* name; 45 | /// int age; 46 | /// }; 47 | /// ``` 48 | /// 49 | /// Creating this struct using the builder pattern: 50 | /// ```rust 51 | /// use crate::{Struct, Field, Type, BaseType, DocComment}; 52 | /// 53 | /// let person = Struct::new("Person".to_string()) 54 | /// .field(Field::new("name".to_string(), Type::new(BaseType::Char).make_pointer().build())) 55 | /// .field(Field::new("age".to_string(), Type::new(BaseType::Int).build())) 56 | /// .build(); 57 | /// ``` 58 | #[derive(Debug, Clone, DisplayFromFormat)] 59 | pub struct Struct { 60 | /// The name of the struct 61 | name: String, 62 | 63 | /// The fields of the struct 64 | fields: Vec, 65 | 66 | /// The doc comment of the struct 67 | doc: Option, 68 | } 69 | 70 | impl Struct { 71 | /// Creates and returns a new `StructBuilder` to construct a `Struct` using the builder 72 | /// pattern. 73 | /// 74 | /// # Parameters 75 | /// * `name` - The name to be given to the struct 76 | /// 77 | /// # Returns 78 | /// A new `StructBuilder` instance initialized with the given name 79 | /// 80 | /// # Examples 81 | /// ```rust 82 | /// let person_struct = Struct::new("Person".to_string()) 83 | /// .field(Field::new("name".to_string(), Type::new(BaseType::Char).make_pointer().build())) 84 | /// .field(Field::new("age".to_string(), Type::new(BaseType::Int).build())) 85 | /// .build(); 86 | /// 87 | /// println!("{}", person_struct); 88 | /// // Outputs: 89 | /// // struct Person { 90 | /// // char* name; 91 | /// // int age; 92 | /// // }; 93 | /// ``` 94 | pub fn new(name: String) -> StructBuilder { 95 | StructBuilder::new(name) 96 | } 97 | 98 | /// Returns the type representation of the struct. 99 | /// 100 | /// This allows using a struct definition as a type for fields or function parameters. 101 | /// 102 | /// # Returns 103 | /// A `Type` instance representing this struct type 104 | /// 105 | /// # Examples 106 | /// ```rust 107 | /// let person_struct = Struct::new("Person".to_string()) 108 | /// .field(Field::new("name".to_string(), Type::new(BaseType::Char).make_pointer().build())) 109 | /// .build(); 110 | /// 111 | /// // Now use this struct as a type for another field 112 | /// let person_field = Field::new("person".to_string(), person_struct.to_type()) 113 | /// .build(); 114 | /// ``` 115 | pub fn to_type(&self) -> Type { 116 | Type::new(BaseType::Struct(self.name.clone())).build() 117 | } 118 | } 119 | 120 | impl Format for Struct { 121 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 122 | if let Some(doc) = &self.doc { 123 | doc.format(fmt)?; 124 | } 125 | 126 | write!(fmt, "struct {}", self.name)?; 127 | 128 | if !self.fields.is_empty() { 129 | fmt.block(|fmt| { 130 | for field in &self.fields { 131 | field.format(fmt)?; 132 | } 133 | Ok(()) 134 | })?; 135 | } 136 | 137 | writeln!(fmt, ";") 138 | } 139 | } 140 | 141 | /// A builder for constructing a `Struct` instance. 142 | /// 143 | /// This builder implements the builder pattern for creating struct 144 | /// definitions with a fluent interface. 145 | pub struct StructBuilder { 146 | name: String, 147 | fields: Vec, 148 | doc: Option, 149 | } 150 | 151 | impl StructBuilder { 152 | /// Creates and returns a new `StructBuilder` to construct a `Struct` using the builder 153 | /// pattern. 154 | /// 155 | /// # Parameters 156 | /// * `name` - The name to be given to the struct 157 | /// 158 | /// # Returns 159 | /// A new `StructBuilder` instance initialized with the given name 160 | /// 161 | /// # Examples 162 | /// ```rust 163 | /// let builder = StructBuilder::new("Person".to_string()); 164 | /// let person_struct = builder 165 | /// .field(Field::new("name".to_string(), Type::new(BaseType::Char).make_pointer().build())) 166 | /// .field(Field::new("age".to_string(), Type::new(BaseType::Int).build())) 167 | /// .build(); 168 | /// ``` 169 | pub fn new(name: String) -> Self { 170 | Self { 171 | name, 172 | fields: vec![], 173 | doc: None, 174 | } 175 | } 176 | 177 | /// Creates and returns a new `StructBuilder` construct a `Struct` with the given name string 178 | /// slice using the builder pattern. 179 | /// 180 | /// This is a convenience method that converts a string slice to a `String`. 181 | /// 182 | /// # Parameters 183 | /// * `name` - The name of the struct as a string slice 184 | /// 185 | /// # Returns 186 | /// A new `StructBuilder` instance initialized with the given name 187 | /// 188 | /// # Examples 189 | /// ```rust 190 | /// let person_struct = StructBuilder::new_with_str("Person") 191 | /// .field(Field::new_with_str("name", Type::new(BaseType::Char).make_pointer().build())) 192 | /// .field(Field::new_with_str("age", Type::new(BaseType::Int).build())) 193 | /// .build(); 194 | /// ``` 195 | pub fn new_with_str(name: &str) -> Self { 196 | Self::new(name.to_string()) 197 | } 198 | 199 | /// Sets the optional doc comment for the struct and returns the builder for more chaining. 200 | /// 201 | /// # Parameters 202 | /// * `doc` - A `DocComment` instance to be associated with the struct 203 | /// 204 | /// # Returns 205 | /// The builder instance for method chaining 206 | pub fn doc(mut self, doc: DocComment) -> Self { 207 | self.doc = Some(doc); 208 | self 209 | } 210 | 211 | /// Appends a struct field to the struct being built and returns the builder for more chaining. 212 | /// 213 | /// # Parameters 214 | /// * `field` - A `Field` instance to be added to the struct 215 | /// 216 | /// # Returns 217 | /// The builder instance for method chaining 218 | /// 219 | /// # Examples 220 | /// ```rust 221 | /// let name_field = Field::new_with_str("name", Type::new(BaseType::Char).make_pointer().build()); 222 | /// let age_field = Field::new_with_str("age", Type::new(BaseType::Int).build()); 223 | /// 224 | /// let person_struct = StructBuilder::new_with_str("Person") 225 | /// .field(name_field) 226 | /// .field(age_field) 227 | /// .build(); 228 | /// ``` 229 | pub fn field(mut self, field: Field) -> Self { 230 | self.fields.push(field); 231 | self 232 | } 233 | 234 | /// Sets the struct fields of the struct being built and returns the builder for more chaining. 235 | /// 236 | /// This method replaces any existing fields with the provided vector of fields. 237 | /// 238 | /// # Parameters 239 | /// * `fields` - A vector of `Field` instances to be added to the struct 240 | /// 241 | /// # Returns 242 | /// The builder instance for method chaining 243 | /// 244 | /// # Examples 245 | /// ```rust 246 | /// let fields = vec![ 247 | /// Field::new_with_str("name", Type::new(BaseType::Char).make_pointer().build()), 248 | /// Field::new_with_str("age", Type::new(BaseType::Int).build()) 249 | /// ]; 250 | /// 251 | /// let person_struct = StructBuilder::new_with_str("Person") 252 | /// .fields(fields) 253 | /// .build(); 254 | /// ``` 255 | pub fn fields(mut self, fields: Vec) -> Self { 256 | self.fields = fields; 257 | self 258 | } 259 | 260 | /// Consumes the builder and returns a `Struct` containing all the fields. 261 | /// 262 | /// # Returns 263 | /// A fully constructed `Struct` instance 264 | pub fn build(self) -> Struct { 265 | Struct { 266 | name: self.name, 267 | fields: self.fields, 268 | doc: self.doc, 269 | } 270 | } 271 | } 272 | 273 | /// Represents a struct field in C. 274 | /// 275 | /// This struct holds all the information needed to represent a field 276 | /// within a C struct, including its name, type, bitfield width (if any), 277 | /// and documentation. 278 | #[derive(Debug, Clone, DisplayFromFormat)] 279 | pub struct Field { 280 | /// The name of the field 281 | pub name: String, 282 | 283 | /// The type of the field 284 | pub t: Type, 285 | 286 | /// The number of bits in the bitfield, if this is a bitfield 287 | pub width: Option, 288 | 289 | /// The doc comment 290 | pub doc: Option, 291 | } 292 | 293 | impl Field { 294 | /// Creates and returns a new `FieldBuilder` to construct a `Field` using the builder pattern. 295 | /// 296 | /// # Parameters 297 | /// * `name` - The name of the field 298 | /// * `t` - The type of the field 299 | /// 300 | /// # Returns 301 | /// A new `FieldBuilder` instance initialized with the given name and type 302 | /// 303 | /// # Examples 304 | /// ```rust 305 | /// let name_field = Field::new("name".to_string(), Type::new(BaseType::Char).make_pointer().build()) 306 | /// .build(); 307 | /// 308 | /// let age_field = Field::new("age".to_string(), Type::new(BaseType::Int).build()) 309 | /// .build(); 310 | /// ``` 311 | pub fn new(name: String, t: Type) -> FieldBuilder { 312 | FieldBuilder::new(name, t) 313 | } 314 | 315 | /// Returns the type of the field. 316 | /// 317 | /// # Returns 318 | /// A clone of the field's type 319 | /// 320 | /// # Examples 321 | /// ```rust 322 | /// let field = Field::new("count".to_string(), Type::new(BaseType::Int).build()) 323 | /// .build(); 324 | /// 325 | /// let field_type = field.to_type(); 326 | /// assert_eq!(field_type.to_string(), "int"); 327 | /// ``` 328 | pub fn to_type(&self) -> Type { 329 | self.t.clone() 330 | } 331 | } 332 | 333 | impl Format for Field { 334 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 335 | if let Some(doc) = &self.doc { 336 | doc.format(fmt)?; 337 | } 338 | 339 | self.t.format(fmt)?; 340 | write!(fmt, " {}", self.name)?; 341 | 342 | if let Some(w) = self.width { 343 | write!(fmt, " : {w}")?; 344 | } 345 | 346 | if self.t.is_array() { 347 | write!(fmt, "[{}]", self.t.array)?; 348 | } 349 | writeln!(fmt, ";") 350 | } 351 | } 352 | 353 | /// A builder for constructing a `Field` instance. 354 | /// 355 | /// This builder implements the builder pattern for creating struct 356 | /// field definitions with a fluent interface. 357 | pub struct FieldBuilder { 358 | name: String, 359 | t: Type, 360 | width: Option, 361 | doc: Option, 362 | } 363 | 364 | impl FieldBuilder { 365 | /// Creates and returns a new `FieldBuilder` to construct a `Field` using the builder pattern. 366 | /// 367 | /// # Parameters 368 | /// * `name` - The name of the field 369 | /// * `t` - The type of the field 370 | /// 371 | /// # Returns 372 | /// A new `FieldBuilder` instance initialized with the given name and type 373 | /// 374 | /// # Examples 375 | /// ```rust 376 | /// let builder = FieldBuilder::new("name".to_string(), Type::new(BaseType::Char).make_pointer().build()); 377 | /// let name_field = builder.build(); 378 | /// 379 | /// // Or in a single chain: 380 | /// let age_field = FieldBuilder::new("age".to_string(), Type::new(BaseType::Int).build()) 381 | /// .build(); 382 | /// ``` 383 | pub fn new(name: String, t: Type) -> Self { 384 | Self { 385 | name, 386 | t, 387 | width: None, 388 | doc: None, 389 | } 390 | } 391 | 392 | /// Creates and returns a new `FieldBuilder` to construct a `Field` with the given name string 393 | /// slice using the builder pattern. 394 | /// 395 | /// This is a convenience method that converts a string slice to a `String`. 396 | /// 397 | /// # Parameters 398 | /// * `name` - The name of the field as a string slice 399 | /// * `t` - The type of the field 400 | /// 401 | /// # Returns 402 | /// A new `FieldBuilder` instance initialized with the given name and type 403 | /// 404 | /// # Examples 405 | /// ```rust 406 | /// let name_field = FieldBuilder::new_with_str("name", Type::new(BaseType::Char).make_pointer().build()) 407 | /// .build(); 408 | /// 409 | /// let age_field = FieldBuilder::new_with_str("age", Type::new(BaseType::Int).build()) 410 | /// .build(); 411 | /// ``` 412 | pub fn new_with_str(name: &str, t: Type) -> Self { 413 | Self::new(name.to_string(), t) 414 | } 415 | 416 | /// Sets the optional doc comment for the field and returns the builder for more chaining. 417 | /// 418 | /// # Parameters 419 | /// * `doc` - A `DocComment` instance to be associated with the field 420 | /// 421 | /// # Returns 422 | /// The builder instance for method chaining 423 | pub fn doc(mut self, doc: DocComment) -> Self { 424 | self.doc = Some(doc); 425 | self 426 | } 427 | 428 | /// Sets the optional bit width for the field and returns the builder for more chaining. 429 | /// 430 | /// When specified, this indicates that the field is a bitfield with the given width. 431 | /// 432 | /// # Parameters 433 | /// * `width` - The number of bits to allocate for this bitfield 434 | /// 435 | /// # Returns 436 | /// The builder instance for method chaining 437 | /// 438 | /// # Examples 439 | /// ```rust 440 | /// // Create a 1-bit flag field 441 | /// let flag_field = FieldBuilder::new_with_str("is_active", Type::new(BaseType::Bool).build()) 442 | /// .bitfield_width(1) 443 | /// .build(); 444 | /// 445 | /// // Create a 4-bit enum field that can store values 0-15 446 | /// let type_field = FieldBuilder::new_with_str("type", Type::new(BaseType::UInt8).build()) 447 | /// .bitfield_width(4) 448 | /// .build(); 449 | /// ``` 450 | pub fn bitfield_width(mut self, width: u8) -> Self { 451 | self.width = Some(width); 452 | self 453 | } 454 | 455 | /// Consumes the builder and returns a `Field` containing all the information. 456 | /// 457 | /// # Returns 458 | /// A fully constructed `Field` instance 459 | /// 460 | /// # Examples 461 | /// ```rust 462 | /// let field = FieldBuilder::new_with_str("name", Type::new(BaseType::Char).make_pointer().build()) 463 | /// .doc(DocComment::new().line_str("The person's full name").build()) 464 | /// .build(); 465 | /// 466 | /// println!("{}", field); 467 | /// // Output: 468 | /// // /// The person's full name 469 | /// // char* name; 470 | /// ``` 471 | pub fn build(self) -> Field { 472 | Field { 473 | name: self.name, 474 | t: self.t, 475 | width: self.width, 476 | doc: self.doc, 477 | } 478 | } 479 | } 480 | 481 | #[cfg(test)] 482 | mod tests { 483 | use super::*; 484 | use crate::*; 485 | 486 | #[test] 487 | fn field() { 488 | let f = FieldBuilder::new_with_str("some_field", Type::new(BaseType::Char).build()) 489 | .doc(DocComment::new().line_str("Hello").build()) 490 | .build(); 491 | let res = r#"/// Hello 492 | char some_field; 493 | "#; 494 | 495 | assert_eq!(f.to_string(), res); 496 | 497 | let f2 = FieldBuilder::new_with_str("another_field", Type::new(BaseType::Bool).build()) 498 | .bitfield_width(1) 499 | .build(); 500 | let res2 = "bool another_field : 1;\n"; 501 | 502 | assert_eq!(f2.to_string(), res2); 503 | } 504 | 505 | #[test] 506 | fn structs() { 507 | let s = StructBuilder::new_with_str("Person") 508 | .fields(vec![ 509 | FieldBuilder::new_with_str( 510 | "name", 511 | Type::new(BaseType::Char).make_pointer().build(), 512 | ) 513 | .build(), 514 | FieldBuilder::new_with_str("age", Type::new(BaseType::UInt8).build()).build(), 515 | ]) 516 | .build(); 517 | let res = r#"struct Person { 518 | char* name; 519 | uint8_t age; 520 | }; 521 | "#; 522 | 523 | assert_eq!(s.to_string(), res); 524 | } 525 | } 526 | -------------------------------------------------------------------------------- /tamago/src/block.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Nobuharu Shimazu 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | //! # Block Module 22 | //! 23 | //! This module provides functionality for creating and managing code blocks in C programs. 24 | //! Code blocks represent structured segments of code such as function bodies, loop bodies, 25 | //! conditional branches, and other scoped statements. 26 | //! 27 | //! The primary components in this module include: 28 | //! - `Block`: Represents a sequence of statements within curly braces in C 29 | //! - `BlockBuilder`: Facilitates constructing blocks using the builder pattern 30 | //! - `Statement`: Enumerates the various statement types that can appear within a block 31 | //! 32 | //! Use this module to programmatically generate well-structured C code with proper 33 | //! scoping and nesting of statements. 34 | 35 | use std::fmt::{self, Write}; 36 | 37 | use crate::{ 38 | Comment, DoWhile, ErrorDirective, Expr, For, Format, Formatter, If, IfDefDirective, 39 | IfDirective, Include, LineDirective, Macro, PragmaDirective, Switch, Variable, 40 | WarningDirective, While, 41 | }; 42 | use tamacro::DisplayFromFormat; 43 | 44 | /// Represents a scoped block of code in C, delimited by curly braces `{}`. 45 | /// A `Block` contains a sequence of statements that are executed together 46 | /// within the same lexical scope. 47 | /// 48 | /// Blocks are fundamental to C's structure and are used in various contexts: 49 | /// - Function bodies 50 | /// - Loop bodies (for, while, do-while) 51 | /// - Conditional branches (if, else, switch cases) 52 | /// 53 | /// ## Memory Management 54 | /// 55 | /// In C, blocks create a new scope for variables, meaning variables declared within 56 | /// a block are destroyed when execution leaves the block, unless they're declared with 57 | /// `static` storage duration. 58 | /// 59 | /// ## Examples 60 | /// 61 | /// ### Function Body 62 | /// ```c 63 | /// int calculate_sum(int a, int b) { 64 | /// int result; // Variable declaration 65 | /// result = a + b; // Assignment expression 66 | /// return result; // Return statement 67 | /// } 68 | /// ``` 69 | /// 70 | /// ### Conditional Branch 71 | /// ```c 72 | /// if (condition) { 73 | /// perform_action(); 74 | /// update_state(); 75 | /// } 76 | /// ``` 77 | #[derive(Debug, Clone, DisplayFromFormat)] 78 | pub struct Block { 79 | /// Collection of statements that make up the block's content. 80 | /// These statements are executed sequentially when the block is entered. 81 | pub stmts: Vec, 82 | } 83 | 84 | impl Block { 85 | /// Creates and returns a new `BlockBuilder` to construct a `Block` using the builder pattern. 86 | /// 87 | /// This method is the recommended entry point for creating blocks as it provides 88 | /// a fluent API for adding statements and other content. 89 | /// 90 | /// ## Example 91 | /// 92 | /// ```rust 93 | /// // Create a block with multiple statements 94 | /// let block = Block::new() 95 | /// .statement(Statement::Variable(var_declaration)) 96 | /// .statement(Statement::Expr(some_expression)) 97 | /// .statement(Statement::Return(Some(return_value))) 98 | /// .build(); 99 | /// ``` 100 | pub fn new() -> BlockBuilder { 101 | BlockBuilder::new() 102 | } 103 | } 104 | 105 | impl Format for Block { 106 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 107 | for stmt in &self.stmts { 108 | stmt.format(fmt)?; 109 | } 110 | 111 | Ok(()) 112 | } 113 | } 114 | 115 | /// A builder for constructing `Block` instances in a fluent, chainable manner. 116 | /// 117 | /// The builder pattern provides a convenient way to incrementally construct blocks 118 | /// by adding statements one at a time or in batches. It also provides utility methods 119 | /// for common operations like adding blank lines or merging blocks. 120 | /// 121 | /// ## Usage Pattern 122 | /// 123 | /// 1. Create a new builder with `BlockBuilder::new()` or `Block::new()` 124 | /// 2. Add statements using the various builder methods 125 | /// 3. Call `build()` to create the final `Block` instance 126 | pub struct BlockBuilder { 127 | stmts: Vec, 128 | } 129 | 130 | impl BlockBuilder { 131 | /// Creates and returns a new `BlockBuilder` instance. 132 | /// 133 | /// The new builder starts with an empty collection of statements. 134 | /// 135 | /// ## Example 136 | /// 137 | /// ```rust 138 | /// // Create a new block builder 139 | /// let builder = BlockBuilder::new(); 140 | /// 141 | /// // Add statements and build the block 142 | /// let block = builder 143 | /// .statement(Statement::Expr(Expr::UInt(42))) 144 | /// .statement(Statement::Return(None)) 145 | /// .build(); 146 | /// ``` 147 | pub fn new() -> Self { 148 | Self { stmts: vec![] } 149 | } 150 | 151 | /// Appends a single statement to the block being built. 152 | /// 153 | /// This method consumes the builder and returns it, allowing for method chaining. 154 | /// 155 | /// ## Parameters 156 | /// 157 | /// - `stmt`: The statement to add to the block 158 | /// 159 | /// ## Returns 160 | /// 161 | /// The builder instance for method chaining 162 | /// 163 | /// ## Example 164 | /// 165 | /// ```rust 166 | /// let block = BlockBuilder::new() 167 | /// .statement(Statement::Expr(Expr::UInt(1))) 168 | /// .statement(Statement::Break) 169 | /// .build(); 170 | /// ``` 171 | pub fn statement(mut self, stmt: Statement) -> Self { 172 | self.stmts.push(stmt); 173 | self 174 | } 175 | 176 | /// Replaces all existing statements with the provided collection of statements. 177 | /// 178 | /// This method is useful when you want to set multiple statements at once, 179 | /// potentially overwriting any statements that were previously added. 180 | /// 181 | /// ## Parameters 182 | /// 183 | /// - `stmts`: A vector of statements to set as the block's content 184 | /// 185 | /// ## Returns 186 | /// 187 | /// The builder instance for method chaining 188 | /// 189 | /// ## Example 190 | /// 191 | /// ```rust 192 | /// let predefined_statements = vec![ 193 | /// Statement::Variable(var_decl), 194 | /// Statement::Expr(expr), 195 | /// Statement::Return(Some(return_val)) 196 | /// ]; 197 | /// 198 | /// let block = BlockBuilder::new() 199 | /// .statements(predefined_statements) 200 | /// .build(); 201 | /// ``` 202 | pub fn statements(mut self, stmts: Vec) -> Self { 203 | self.stmts = stmts; 204 | self 205 | } 206 | 207 | /// Adds an empty line (newline character) to the block. 208 | /// 209 | /// This method is useful for formatting purposes, allowing you to add 210 | /// vertical spacing between statements for improved readability. 211 | /// 212 | /// ## Returns 213 | /// 214 | /// The builder instance for method chaining 215 | /// 216 | /// ## Example 217 | /// 218 | /// ```rust 219 | /// let block = BlockBuilder::new() 220 | /// .statement(Statement::Variable(var_decl)) 221 | /// .new_line() // Add vertical space for readability 222 | /// .statement(Statement::Expr(some_calculation)) 223 | /// .build(); 224 | /// ``` 225 | pub fn new_line(self) -> Self { 226 | self.statement(Statement::NewLine) 227 | } 228 | 229 | /// Merges the statements from another `Block` into this builder. 230 | /// The statements from the other block are appended after any existing statements. 231 | /// 232 | /// This method is useful for combining blocks or for reusing parts of existing blocks. 233 | /// 234 | /// ## Parameters 235 | /// 236 | /// - `other`: Another block whose statements will be merged into this builder 237 | /// 238 | /// ## Returns 239 | /// 240 | /// The builder instance for method chaining 241 | /// 242 | /// ## Example 243 | /// 244 | /// ```rust 245 | /// // Create a common initialization block 246 | /// let init_block = Block::new() 247 | /// .statement(Statement::Variable(var_init1)) 248 | /// .statement(Statement::Variable(var_init2)) 249 | /// .build(); 250 | /// 251 | /// // Reuse the initialization block in another context 252 | /// let full_block = BlockBuilder::new() 253 | /// .merge(init_block) 254 | /// .statement(Statement::Expr(main_logic)) 255 | /// .statement(Statement::Return(Some(result))) 256 | /// .build(); 257 | /// ``` 258 | pub fn merge(mut self, mut other: Block) -> Self { 259 | self.stmts.append(&mut other.stmts); 260 | self 261 | } 262 | 263 | /// Consumes the builder and creates a new `Block` instance. 264 | /// 265 | /// This method finalizes the building process and returns the constructed block 266 | /// containing all the statements that were added to the builder. 267 | /// 268 | /// ## Returns 269 | /// 270 | /// A new `Block` instance containing the statements added to the builder 271 | /// 272 | /// ## Example 273 | /// 274 | /// ```rust 275 | /// // Build a complete function body 276 | /// let function_body = BlockBuilder::new() 277 | /// .statement(Statement::Variable(local_var)) 278 | /// .statement(Statement::Expr(calculation)) 279 | /// .statement(Statement::Return(Some(result))) 280 | /// .build(); 281 | /// ``` 282 | pub fn build(self) -> Block { 283 | Block { stmts: self.stmts } 284 | } 285 | } 286 | 287 | /// Represents the various types of statements and preprocessor directives 288 | /// that can appear within a C code block. 289 | /// 290 | /// This enum covers the full range of C language constructs, including: 291 | /// - Basic statements (expressions, returns, breaks, continues) 292 | /// - Control flow statements (if, switch, loops) 293 | /// - Variable declarations 294 | /// - Labels and gotos 295 | /// - Preprocessor directives 296 | /// - Comments and raw code insertion 297 | /// 298 | /// Each variant corresponds to a specific type of C statement or directive, 299 | /// with associated data where necessary. 300 | #[derive(Debug, Clone, DisplayFromFormat)] 301 | pub enum Statement { 302 | /// A C-style comment (either line comment `//` or block comment `/* */`) 303 | Comment(Comment), 304 | 305 | /// A variable declaration statement, which may include initialization 306 | /// 307 | /// Example: `int counter = 0;` 308 | Variable(Variable), 309 | 310 | /// An expression statement, typically ending with a semicolon 311 | /// 312 | /// Examples: 313 | /// - `func();` 314 | /// - `a = b + c;` 315 | /// - `counter++;` 316 | Expr(Expr), 317 | 318 | /// A return statement, optionally with an expression to return 319 | /// 320 | /// Examples: 321 | /// - `return;` (represented as `Return(None)`) 322 | /// - `return value;` (represented as `Return(Some(expr))`) 323 | Return(Option), 324 | 325 | /// A break statement to exit a loop or switch 326 | /// 327 | /// Example: `break;` 328 | Break, 329 | 330 | /// A continue statement to skip to the next iteration of a loop 331 | /// 332 | /// Example: `continue;` 333 | Continue, 334 | 335 | /// A goto statement that jumps to a labeled statement 336 | /// 337 | /// Example: `goto error_handler;` 338 | GoTo(String), 339 | 340 | /// A label declaration that can be targeted by goto statements 341 | /// 342 | /// Example: `some_label:` 343 | Label(String), 344 | 345 | /// An if statement with optional else and else if branches 346 | /// 347 | /// Example: `if (condition) { ... } else if (another) { ... } else { ... }` 348 | If(If), 349 | 350 | /// A switch statement with multiple case branches 351 | /// 352 | /// Example: `switch (value) { case 1: ... break; default: ... }` 353 | Switch(Switch), 354 | 355 | /// A while loop 356 | /// 357 | /// Example: `while (condition) { ... }` 358 | While(While), 359 | 360 | /// A do-while loop 361 | /// 362 | /// Example: `do { ... } while (condition);` 363 | DoWhile(DoWhile), 364 | 365 | /// A for loop 366 | /// 367 | /// Example: `for (int i = 0; i < 10; i++) { ... }` 368 | For(For), 369 | 370 | /// A `#error` preprocessor directive that causes compilation to fail with a message 371 | /// 372 | /// Example: `#error "This platform is not supported"` 373 | ErrorDirective(ErrorDirective), 374 | 375 | /// A `#ifdef`, `#ifndef`, or `#elifdef` preprocessor directive for conditional compilation 376 | /// 377 | /// Example: `#ifdef DEBUG ... #endif` 378 | IfDefDirective(IfDefDirective), 379 | 380 | /// A `#if`, `#elif`, or `#else` preprocessor directive for conditional compilation 381 | /// 382 | /// Example: `#if PLATFORM == WINDOWS ... #else ... #endif` 383 | IfDirective(IfDirective), 384 | 385 | /// An `#include` directive to include a header file 386 | /// 387 | /// Examples: 388 | /// - `#include ` 389 | /// - `#include "myheader.h"` 390 | Include(Include), 391 | 392 | /// A `#line` preprocessor directive to control line numbering in error messages 393 | /// 394 | /// Example: `#line 50 "some_file.c"` 395 | LineDirective(LineDirective), 396 | 397 | /// A macro definition 398 | /// 399 | /// Examples: 400 | /// - `#define MAX_SIZE 100` 401 | /// - `#define SUM(a, b) ((a) + (b))` 402 | Macro(Macro), 403 | 404 | /// A `#pragma` directive for implementation-specific behaviors 405 | /// 406 | /// Example: `#pragma once` 407 | PragmaDirective(PragmaDirective), 408 | 409 | /// A `#warning` directive that emits a compiler warning 410 | /// 411 | /// Example: `#warning "This code is deprecated!"` 412 | WarningDirective(WarningDirective), 413 | 414 | /// Raw C code inserted verbatim without processing 415 | /// 416 | /// Useful for edge cases not covered by other statement types 417 | Raw(String), 418 | 419 | /// A standalone newline for formatting purposes 420 | /// 421 | /// Adds vertical whitespace between statements for improved readability 422 | NewLine, 423 | } 424 | 425 | impl Format for Statement { 426 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 427 | use Statement::*; 428 | match self { 429 | Comment(comment) => comment.format(fmt), 430 | Variable(variable) => { 431 | variable.format(fmt)?; 432 | writeln!(fmt, ";") 433 | } 434 | Expr(expr) => { 435 | expr.format(fmt)?; 436 | writeln!(fmt, ";") 437 | } 438 | Return(None) => writeln!(fmt, "return;"), 439 | Return(Some(expr)) => { 440 | write!(fmt, "return ")?; 441 | expr.format(fmt)?; 442 | writeln!(fmt, ";") 443 | } 444 | Break => writeln!(fmt, "break;"), 445 | Continue => writeln!(fmt, "continue;"), 446 | GoTo(s) => writeln!(fmt, "goto {s};"), 447 | Label(s) => writeln!(fmt, "{s}:"), 448 | If(i) => i.format(fmt), 449 | Switch(s) => s.format(fmt), 450 | While(w) => w.format(fmt), 451 | DoWhile(w) => w.format(fmt), 452 | For(f) => f.format(fmt), 453 | ErrorDirective(e) => e.format(fmt), 454 | IfDefDirective(i) => i.format(fmt), 455 | IfDirective(i) => i.format(fmt), 456 | Include(i) => i.format(fmt), 457 | LineDirective(l) => l.format(fmt), 458 | Macro(m) => m.format(fmt), 459 | PragmaDirective(p) => p.format(fmt), 460 | WarningDirective(w) => w.format(fmt), 461 | Raw(s) => writeln!(fmt, "{s}"), 462 | NewLine => writeln!(fmt), 463 | } 464 | } 465 | } 466 | 467 | #[cfg(test)] 468 | mod tests { 469 | use super::*; 470 | use crate::*; 471 | 472 | #[test] 473 | fn statement() { 474 | let mut s = Statement::Comment(Comment::new().comment_with_str("Hello").build()); 475 | assert_eq!(s.to_string(), "// Hello\n"); 476 | 477 | let t = Type::new(BaseType::Size) 478 | .make_const() 479 | .make_pointer() 480 | .build(); 481 | s = Statement::Variable(VariableBuilder::new_with_str("abc", t).build()); 482 | assert_eq!(s.to_string(), "const size_t* abc;\n"); 483 | 484 | s = Statement::Return(None); 485 | assert_eq!(s.to_string(), "return;\n"); 486 | s = Statement::Return(Some(Expr::UInt(123))); 487 | assert_eq!(s.to_string(), "return 123;\n"); 488 | 489 | s = Statement::Break; 490 | assert_eq!(s.to_string(), "break;\n"); 491 | 492 | s = Statement::Continue; 493 | assert_eq!(s.to_string(), "continue;\n"); 494 | 495 | s = Statement::GoTo("some_label".to_string()); 496 | assert_eq!(s.to_string(), "goto some_label;\n"); 497 | 498 | s = Statement::Label("some_label".to_string()); 499 | assert_eq!(s.to_string(), "some_label:\n"); 500 | } 501 | 502 | #[test] 503 | fn blocks() { 504 | let b1 = Block::new() 505 | .statement(Statement::Raw("abc;".to_string())) 506 | .new_line() 507 | .new_line() 508 | .statement(Statement::Expr(Expr::FnCall { 509 | name: Box::new(Expr::new_ident_with_str("some_func")), 510 | args: vec![], 511 | })) 512 | .build(); 513 | 514 | assert_eq!(b1.stmts.len(), 4); 515 | assert_eq!(b1.to_string(), "abc;\n\n\nsome_func();\n"); 516 | 517 | let b2 = Block::new() 518 | .statements(vec![Statement::Raw("something else".to_string())]) 519 | .merge(b1) 520 | .build(); 521 | 522 | assert_eq!(b2.to_string(), "something else\nabc;\n\n\nsome_func();\n"); 523 | } 524 | } 525 | -------------------------------------------------------------------------------- /tamago/src/types.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Nobuharu Shimazu 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | //! This module provides tools for expressing and managing C types in Rust. 22 | //! 23 | //! It defines structures and utilities to represent C base types, type qualifiers, 24 | //! and complex type constructions such as pointers and arrays. This is particularly 25 | //! useful for generating C code or bindings programmatically from Rust. 26 | 27 | use std::fmt::{self, Write}; 28 | 29 | use crate::{Format, Formatter}; 30 | use tamacro::DisplayFromFormat; 31 | 32 | /// Represents all base types used in C. 33 | /// 34 | /// The `BaseType` enum encapsulates the fundamental types in C, including primitive 35 | /// types like `int` and `float`, sized integer types from `stdint.h`, and aggregate 36 | /// types like `enum`, `struct`, and `union`. It serves as the foundation for building 37 | /// more complex types. 38 | /// 39 | /// # Examples 40 | /// 41 | /// Basic integer type: 42 | /// ```c 43 | /// int 44 | /// ``` 45 | /// 46 | /// Sized unsigned integer: 47 | /// ```c 48 | /// uint32_t 49 | /// ``` 50 | /// 51 | /// Struct type: 52 | /// ```c 53 | /// struct Point 54 | /// ``` 55 | #[derive(Debug, Clone, DisplayFromFormat)] 56 | pub enum BaseType { 57 | /// Represents the `void` type. 58 | Void, 59 | 60 | /// Represents the `double` type, double precision floating point number. 61 | Double, 62 | 63 | /// Represents the `float` type, single precision floating point number. 64 | Float, 65 | 66 | /// Represents the `char` type, a single byte character. 67 | Char, 68 | 69 | /// Represents the `int` type. 70 | Int, 71 | 72 | /// Represents the `uint8_t` type from `stdint.h`, an unsigned 8-bit integer. 73 | UInt8, 74 | 75 | /// Represents the `uint16_t` type from `stdint.h`, an unsigned 16-bit integer. 76 | UInt16, 77 | 78 | /// Represents the `uint32_t` type from `stdint.h`, an unsigned 32-bit integer. 79 | UInt32, 80 | 81 | /// Represents the `uint64_t` type from `stdint.h`, an unsigned 64-bit integer. 82 | UInt64, 83 | 84 | /// Represents the `int8_t` type from `stdint.h`, a signed 8-bit integer. 85 | Int8, 86 | 87 | /// Represents the `int16_t` type from `stdint.h`, a signed 16-bit integer. 88 | Int16, 89 | 90 | /// Represents the `int32_t` type from `stdint.h`, a signed 32-bit integer. 91 | Int32, 92 | 93 | /// Represents the `int64_t` type from `stdint.h`, a signed 64-bit integer. 94 | Int64, 95 | 96 | /// Represents the `size_t` type from `stddef.h`. 97 | Size, 98 | 99 | /// Represents the `uintptr_t` type from `stdint.h`. 100 | UIntPtr, 101 | 102 | /// Represents the `bool` type from `stdbool.h`. 103 | Bool, 104 | 105 | /// An enumeration type. 106 | Enum(String), 107 | 108 | /// A struct type. 109 | Struct(String), 110 | 111 | /// A union type. 112 | Union(String), 113 | 114 | /// `typedef` 115 | TypeDef(String), 116 | } 117 | 118 | impl BaseType { 119 | /// Creates a new unsigned integer with the given bit size. 120 | /// 121 | /// This method maps the provided bit size to the corresponding unsigned integer type: 122 | /// - 8 -> `UInt8` 123 | /// - 16 -> `UInt16` 124 | /// - 32 -> `UInt32` 125 | /// - 64 -> `UInt64` 126 | /// Any other value defaults to `UInt64`. 127 | /// 128 | /// # Parameters 129 | /// 130 | /// * `bits` - The size of the unsigned integer in bits 131 | /// 132 | /// # Returns 133 | /// 134 | /// A `BaseType` instance representing the specified unsigned integer type 135 | /// 136 | /// # Examples 137 | /// 138 | /// ```rust 139 | /// let uint_type = BaseType::new_uint(32); 140 | /// assert_eq!(uint_type.to_string(), "uint32_t"); 141 | /// ``` 142 | pub fn new_uint(bits: u8) -> Self { 143 | use BaseType::*; 144 | match bits { 145 | 8 => UInt8, 146 | 16 => UInt16, 147 | 32 => UInt32, 148 | 64 => UInt64, 149 | _ => UInt64, 150 | } 151 | } 152 | 153 | /// Creates a new signed integer with the given bit size. 154 | /// 155 | /// This method maps the provided bit size to the corresponding signed integer type: 156 | /// - 8 -> `Int8` 157 | /// - 16 -> `Int16` 158 | /// - 32 -> `Int32` 159 | /// - 64 -> `Int64` 160 | /// Any other value defaults to `Int64`. 161 | /// 162 | /// # Parameters 163 | /// 164 | /// * `bits` - The size of the signed integer in bits 165 | /// 166 | /// # Returns 167 | /// 168 | /// A `BaseType` instance representing the specified signed integer type 169 | /// 170 | /// # Examples 171 | /// 172 | /// ```rust 173 | /// let int_type = BaseType::new_int(16); 174 | /// assert_eq!(int_type.to_string(), "int16_t"); 175 | /// ``` 176 | pub fn new_int(bits: u8) -> Self { 177 | use BaseType::*; 178 | match bits { 179 | 8 => Int8, 180 | 16 => Int16, 181 | 32 => Int32, 182 | 64 => Int64, 183 | _ => Int64, 184 | } 185 | } 186 | 187 | /// Checks whether the type is an integer type. 188 | /// 189 | /// This includes all signed and unsigned integer types, `char`, `size_t`, `uintptr_t`, and `bool`. 190 | /// 191 | /// # Returns 192 | /// 193 | /// `true` if the type is an integer type, `false` otherwise 194 | /// 195 | /// # Examples 196 | /// 197 | /// ```rust 198 | /// let int_type = BaseType::Int; 199 | /// assert!(int_type.is_integer()); 200 | /// let float_type = BaseType::Float; 201 | /// assert!(!float_type.is_integer()); 202 | /// ``` 203 | pub fn is_integer(&self) -> bool { 204 | use BaseType::*; 205 | matches!( 206 | self, 207 | Int | UInt8 208 | | UInt16 209 | | UInt32 210 | | UInt64 211 | | Int8 212 | | Int16 213 | | Int32 214 | | Int64 215 | | Size 216 | | UIntPtr 217 | | Bool 218 | | Char 219 | ) 220 | } 221 | 222 | /// Checks whether the type is a tag type (`enum`, `struct`, `union`, or `typedef`). 223 | /// 224 | /// # Returns 225 | /// 226 | /// `true` if the type is a tag type, `false` otherwise 227 | /// 228 | /// # Examples 229 | /// 230 | /// ```rust 231 | /// let struct_type = BaseType::Struct("Point".to_string()); 232 | /// assert!(struct_type.is_tag_type()); 233 | /// let int_type = BaseType::Int; 234 | /// assert!(!int_type.is_tag_type()); 235 | /// ``` 236 | pub fn is_tag_type(&self) -> bool { 237 | use BaseType::*; 238 | matches!(self, Enum(_) | Struct(_) | Union(_) | TypeDef(_)) 239 | } 240 | } 241 | 242 | impl Format for BaseType { 243 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 244 | use BaseType::*; 245 | match self { 246 | Void => write!(fmt, "void"), 247 | Double => write!(fmt, "double"), 248 | Float => write!(fmt, "float"), 249 | Char => write!(fmt, "char"), 250 | Int => write!(fmt, "int"), 251 | UInt8 => write!(fmt, "uint8_t"), 252 | UInt16 => write!(fmt, "uint16_t"), 253 | UInt32 => write!(fmt, "uint32_t"), 254 | UInt64 => write!(fmt, "uint64_t"), 255 | Int8 => write!(fmt, "int8_t"), 256 | Int16 => write!(fmt, "int16_t"), 257 | Int32 => write!(fmt, "int32_t"), 258 | Int64 => write!(fmt, "int64_t"), 259 | Size => write!(fmt, "size_t"), 260 | UIntPtr => write!(fmt, "uintptr_t"), 261 | Bool => write!(fmt, "bool"), 262 | Enum(s) => write!(fmt, "enum {s}"), 263 | Struct(s) => write!(fmt, "struct {s}"), 264 | Union(s) => write!(fmt, "union {s}"), 265 | TypeDef(s) => write!(fmt, "{s}"), 266 | } 267 | } 268 | } 269 | 270 | /// Represents type qualifiers in C. 271 | /// 272 | /// The `TypeQualifier` enum encapsulates C's type qualifiers `const` and `volatile`, 273 | /// which modify the behavior of variables or pointers in a C program. 274 | /// 275 | /// # Examples 276 | /// 277 | /// Const qualifier: 278 | /// ```c 279 | /// const int 280 | /// ``` 281 | /// 282 | /// Volatile qualifier: 283 | /// ```c 284 | /// volatile char 285 | /// ``` 286 | #[derive(Debug, Clone, Copy, DisplayFromFormat)] 287 | pub enum TypeQualifier { 288 | /// The `volatile` keyword, indicating the variable may change unexpectedly. 289 | Volatile, 290 | 291 | /// The `const` keyword, indicating the variable's value cannot be modified after initialization. 292 | Const, 293 | } 294 | 295 | impl Format for TypeQualifier { 296 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 297 | use TypeQualifier::*; 298 | match self { 299 | Volatile => write!(fmt, "volatile"), 300 | Const => write!(fmt, "const"), 301 | } 302 | } 303 | } 304 | 305 | /// Represents a complete type in C, including base type, qualifiers, pointers, and arrays. 306 | /// 307 | /// The `Type` struct combines a `BaseType` with optional qualifiers, pointer levels, 308 | /// and array sizes to fully describe a C type as it would appear in a declaration. 309 | /// 310 | /// # Examples 311 | /// 312 | /// Pointer to const char: 313 | /// ```c 314 | /// const char* 315 | /// ``` 316 | /// 317 | /// Array of integers: 318 | /// ```c 319 | /// int[10] 320 | /// ``` 321 | /// 322 | /// Double pointer to volatile float: 323 | /// ```c 324 | /// volatile float** 325 | /// ``` 326 | #[derive(Debug, Clone, DisplayFromFormat)] 327 | pub struct Type { 328 | /// The base type used to construct a type. 329 | pub base: BaseType, 330 | 331 | /// All the qualifiers for the type. 332 | pub qualifiers: Vec, 333 | 334 | /// Pointers 335 | pub pointers: u8, 336 | 337 | /// Array 338 | pub array: usize, 339 | } 340 | 341 | impl Type { 342 | /// Creates and returns a new `TypeBuilder` to construct a `Type` using the builder pattern. 343 | /// 344 | /// This method provides a fluent interface for defining complex C types incrementally. 345 | /// 346 | /// # Parameters 347 | /// 348 | /// * `base` - The base type to start with 349 | /// 350 | /// # Returns 351 | /// 352 | /// A `TypeBuilder` instance for configuring and building a `Type` 353 | /// 354 | /// # Examples 355 | /// 356 | /// ```rust 357 | /// let t = Type::new(BaseType::Char) 358 | /// .make_const() 359 | /// .make_pointer() 360 | /// .build(); 361 | /// assert_eq!(t.to_string(), "const char*"); 362 | /// ``` 363 | pub fn new(base: BaseType) -> TypeBuilder { 364 | TypeBuilder::new(base) 365 | } 366 | 367 | /// Checks whether the type is an array. 368 | /// 369 | /// # Returns 370 | /// 371 | /// `true` if the type is an array (array size > 0), `false` otherwise 372 | /// 373 | /// # Examples 374 | /// 375 | /// ```rust 376 | /// let array_type = Type::new(BaseType::Int).make_array(5).build(); 377 | /// assert!(array_type.is_array()); 378 | /// let simple_type = Type::new(BaseType::Int).build(); 379 | /// assert!(!simple_type.is_array()); 380 | /// ``` 381 | pub fn is_array(&self) -> bool { 382 | self.array != 0 383 | } 384 | } 385 | 386 | impl Format for Type { 387 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 388 | for q in &self.qualifiers { 389 | q.format(fmt)?; 390 | write!(fmt, " ")?; 391 | } 392 | 393 | self.base.format(fmt)?; 394 | 395 | write!(fmt, "{}", "*".repeat(self.pointers.into()))?; 396 | 397 | Ok(()) 398 | } 399 | } 400 | 401 | /// A builder for constructing a `Type` instance with a fluent interface. 402 | /// 403 | /// The `TypeBuilder` allows incremental configuration of a C type's properties, 404 | /// such as qualifiers, pointers, and array sizes, before finalizing the type. 405 | pub struct TypeBuilder { 406 | base: BaseType, 407 | qualifiers: Vec, 408 | pointers: u8, 409 | array: usize, 410 | } 411 | 412 | impl TypeBuilder { 413 | /// Creates and returns a new `TypeBuilder` to construct a `Type`. 414 | /// 415 | /// # Parameters 416 | /// 417 | /// * `base` - The base type to start with 418 | /// 419 | /// # Returns 420 | /// 421 | /// A new `TypeBuilder` instance with default values for qualifiers, pointers, and array size 422 | /// 423 | /// # Examples 424 | /// 425 | /// ```rust 426 | /// let builder = TypeBuilder::new(BaseType::Int); 427 | /// ``` 428 | pub fn new(base: BaseType) -> Self { 429 | Self { 430 | base, 431 | qualifiers: vec![], 432 | pointers: 0, 433 | array: 0, 434 | } 435 | } 436 | 437 | /// Adds a type qualifier to the type being built. 438 | /// 439 | /// # Parameters 440 | /// 441 | /// * `q` - The type qualifier to add 442 | /// 443 | /// # Returns 444 | /// 445 | /// The builder instance for method chaining 446 | /// 447 | /// # Examples 448 | /// 449 | /// ```rust 450 | /// let builder = TypeBuilder::new(BaseType::Float) 451 | /// .type_qualifier(TypeQualifier::Const); 452 | /// ``` 453 | pub fn type_qualifier(mut self, q: TypeQualifier) -> Self { 454 | self.qualifiers.push(q); 455 | self 456 | } 457 | 458 | /// Makes the type volatile. 459 | /// 460 | /// Adds the `volatile` qualifier to the type. 461 | /// 462 | /// # Returns 463 | /// 464 | /// The builder instance for method chaining 465 | /// 466 | /// # Examples 467 | /// 468 | /// ```rust 469 | /// let builder = TypeBuilder::new(BaseType::Int).make_volatile(); 470 | /// assert_eq!(builder.build().to_string(), "volatile int"); 471 | /// ``` 472 | pub fn make_volatile(self) -> Self { 473 | self.type_qualifier(TypeQualifier::Volatile) 474 | } 475 | 476 | /// Makes the type const. 477 | /// 478 | /// Adds the `const` qualifier to the type. 479 | /// 480 | /// # Returns 481 | /// 482 | /// The builder instance for method chaining 483 | /// 484 | /// # Examples 485 | /// 486 | /// ```rust 487 | /// let builder = TypeBuilder::new(BaseType::Char).make_const(); 488 | /// assert_eq!(builder.build().to_string(), "const char"); 489 | /// ``` 490 | pub fn make_const(self) -> Self { 491 | self.type_qualifier(TypeQualifier::Const) 492 | } 493 | 494 | /// Makes the type a pointer. 495 | /// 496 | /// Increases the pointer level by one. 497 | /// 498 | /// # Returns 499 | /// 500 | /// The builder instance for method chaining 501 | /// 502 | /// # Examples 503 | /// 504 | /// ```rust 505 | /// let builder = TypeBuilder::new(BaseType::Void).make_pointer(); 506 | /// assert_eq!(builder.build().to_string(), "void*"); 507 | /// ``` 508 | pub fn make_pointer(mut self) -> Self { 509 | self.pointers += 1; 510 | self 511 | } 512 | 513 | /// Makes the type an array with the given size. 514 | /// 515 | /// # Parameters 516 | /// 517 | /// * `size` - The size of the array 518 | /// 519 | /// # Returns 520 | /// 521 | /// The builder instance for method chaining 522 | /// 523 | /// # Examples 524 | /// 525 | /// ```rust 526 | /// let builder = TypeBuilder::new(BaseType::Int).make_array(10); 527 | /// ``` 528 | pub fn make_array(mut self, size: usize) -> Self { 529 | self.array = size; 530 | self 531 | } 532 | 533 | /// Finalizes the type definition and returns a fully constructed `Type`. 534 | /// 535 | /// # Returns 536 | /// 537 | /// A fully constructed `Type` instance 538 | /// 539 | /// # Examples 540 | /// 541 | /// ```rust 542 | /// let t = TypeBuilder::new(BaseType::Double) 543 | /// .make_const() 544 | /// .make_pointer() 545 | /// .build(); 546 | /// assert_eq!(t.to_string(), "const double*"); 547 | /// ``` 548 | pub fn build(self) -> Type { 549 | Type { 550 | base: self.base, 551 | qualifiers: self.qualifiers, 552 | pointers: self.pointers, 553 | array: self.array, 554 | } 555 | } 556 | } 557 | 558 | #[cfg(test)] 559 | mod tests { 560 | use super::*; 561 | 562 | #[test] 563 | fn base_type() { 564 | use BaseType::*; 565 | assert_eq!(Void.to_string(), "void"); 566 | assert_eq!(Double.to_string(), "double"); 567 | assert_eq!(Float.to_string(), "float"); 568 | assert_eq!(Char.to_string(), "char"); 569 | assert_eq!(Int.to_string(), "int"); 570 | assert_eq!(UInt8.to_string(), "uint8_t"); 571 | assert_eq!(UInt16.to_string(), "uint16_t"); 572 | assert_eq!(UInt32.to_string(), "uint32_t"); 573 | assert_eq!(UInt64.to_string(), "uint64_t"); 574 | assert_eq!(Int8.to_string(), "int8_t"); 575 | assert_eq!(Int16.to_string(), "int16_t"); 576 | assert_eq!(Int32.to_string(), "int32_t"); 577 | assert_eq!(Int64.to_string(), "int64_t"); 578 | assert_eq!(Size.to_string(), "size_t"); 579 | assert_eq!(UIntPtr.to_string(), "uintptr_t"); 580 | assert_eq!(Bool.to_string(), "bool"); 581 | assert_eq!(Enum("abc".to_string()).to_string(), "enum abc"); 582 | assert_eq!(Struct("abc".to_string()).to_string(), "struct abc"); 583 | assert_eq!(Union("abc".to_string()).to_string(), "union abc"); 584 | assert_eq!(TypeDef("abc".to_string()).to_string(), "abc"); 585 | } 586 | 587 | #[test] 588 | fn t() { 589 | use BaseType::*; 590 | let mut t = Type::new(Void).build(); 591 | assert_eq!(t.to_string(), "void"); 592 | 593 | t = Type::new(Void).make_pointer().make_pointer().build(); 594 | assert_eq!(t.to_string(), "void**"); 595 | 596 | t = Type::new(Void) 597 | .make_pointer() 598 | .make_pointer() 599 | .make_const() 600 | .build(); 601 | assert_eq!(t.to_string(), "const void**"); 602 | 603 | t = Type::new(Void) 604 | .make_pointer() 605 | .make_const() 606 | .make_array(10) 607 | .build(); 608 | assert_eq!(t.to_string(), "const void*"); 609 | assert!(t.is_array()) 610 | } 611 | } 612 | -------------------------------------------------------------------------------- /tamago/src/enums.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Nobuharu Shimazu 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | // furnished to do so, subject to the following conditions: 8 | // 9 | // The above copyright notice and this permission notice shall be included in all 10 | // copies or substantial portions of the Software. 11 | // 12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | // SOFTWARE. 19 | 20 | //! # Enum Definition Module 21 | //! 22 | //! This module provides abstractions and builders for creating C-style enum definitions. 23 | //! It enables programmatic generation of complete enum declarations with variants, 24 | //! optional documentation comments, and explicit values for enum constants. 25 | //! 26 | //! The module contains: 27 | //! - `Enum`: Represents a complete C enum declaration 28 | //! - `Variant`: Represents an individual enum variant with optional value 29 | //! 30 | //! Both structures come with corresponding builder patterns to facilitate construction 31 | //! of enum declarations. 32 | 33 | use std::fmt::{self, Write}; 34 | 35 | use crate::{BaseType, DocComment, Format, Formatter, Type}; 36 | use tamacro::DisplayFromFormat; 37 | 38 | /// Represents a complete enum declaration in C programming language. 39 | /// 40 | /// An enum in C is a user-defined type consisting of a set of named integer constants. 41 | /// This structure models the complete enum definition, including its name, variants, 42 | /// and optional documentation. 43 | /// 44 | /// # Examples 45 | /// 46 | /// Basic enum with sequential values: 47 | /// ```rust 48 | /// let enum_def = Enum::new("Color".to_string()) 49 | /// .variant(Variant::new("RED".to_string()).build()) 50 | /// .variant(Variant::new("GREEN".to_string()).build()) 51 | /// .variant(Variant::new("BLUE".to_string()).build()) 52 | /// .build(); 53 | /// ``` 54 | /// 55 | /// This would generate C code like: 56 | /// ```c 57 | /// enum Color { 58 | /// RED, 59 | /// GREEN, 60 | /// BLUE, 61 | /// }; 62 | /// ``` 63 | /// 64 | /// Enum with explicit values: 65 | /// ```rust 66 | /// let enum_def = Enum::new("HttpStatus".to_string()) 67 | /// .variant(Variant::new("OK".to_string()).value(200).build()) 68 | /// .variant(Variant::new("NOT_FOUND".to_string()).value(404).build()) 69 | /// .variant(Variant::new("SERVER_ERROR".to_string()).value(500).build()) 70 | /// .build(); 71 | /// ``` 72 | /// 73 | /// This would generate C code like: 74 | /// ```c 75 | /// enum HttpStatus { 76 | /// OK = 200, 77 | /// NOT_FOUND = 404, 78 | /// SERVER_ERROR = 500, 79 | /// }; 80 | /// ``` 81 | #[derive(Debug, Clone, DisplayFromFormat)] 82 | pub struct Enum { 83 | /// The identifier name for the enum type 84 | pub name: String, 85 | 86 | /// The collection of enum variants (constants) defined within this enum 87 | pub variants: Vec, 88 | 89 | /// Optional documentation comment for the enum 90 | pub doc: Option, 91 | } 92 | 93 | impl Enum { 94 | /// Creates and returns a new `EnumBuilder` to construct an enum using the builder pattern. 95 | /// 96 | /// This method provides a convenient entry point to start building an enum declaration. 97 | /// The returned builder allows for fluent and clear construction of enum types. 98 | /// 99 | /// # Parameters 100 | /// - `name`: The identifier name for the enum type 101 | /// 102 | /// # Returns 103 | /// A new `EnumBuilder` instance initialized with the given name 104 | /// 105 | /// # Examples 106 | /// ```rust 107 | /// let enum_def = Enum::new("Direction".to_string()) 108 | /// .variant(Variant::new("NORTH".to_string()).build()) 109 | /// .variant(Variant::new("EAST".to_string()).build()) 110 | /// .variant(Variant::new("SOUTH".to_string()).build()) 111 | /// .variant(Variant::new("WEST".to_string()).build()) 112 | /// .build(); 113 | /// ``` 114 | pub fn new(name: String) -> EnumBuilder { 115 | EnumBuilder::new(name) 116 | } 117 | 118 | /// Converts this enum definition into its corresponding type representation. 119 | /// 120 | /// This method creates a Type instance that represents the enum type, which can be 121 | /// used when declaring variables or parameters of this enum type. 122 | /// 123 | /// # Returns 124 | /// A `Type` instance representing this enum type 125 | /// 126 | /// # Examples 127 | /// ```rust 128 | /// let enum_def = Enum::new("Color".to_string()) 129 | /// .variant(Variant::new("RED".to_string()).build()) 130 | /// .variant(Variant::new("GREEN".to_string()).build()) 131 | /// .variant(Variant::new("BLUE".to_string()).build()) 132 | /// .build(); 133 | /// 134 | /// let color_type = enum_def.to_type(); 135 | /// ``` 136 | pub fn to_type(&self) -> Type { 137 | Type::new(BaseType::Enum(self.name.clone())).build() 138 | } 139 | } 140 | 141 | impl Format for Enum { 142 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 143 | if let Some(doc) = &self.doc { 144 | doc.format(fmt)?; 145 | } 146 | 147 | write!(fmt, "enum {}", self.name)?; 148 | 149 | fmt.block(|fmt| { 150 | for variant in &self.variants { 151 | variant.format(fmt)?; 152 | writeln!(fmt, ",")?; 153 | } 154 | 155 | Ok(()) 156 | })?; 157 | 158 | writeln!(fmt, ";") 159 | } 160 | } 161 | 162 | /// A builder for constructing an `Enum` instance incrementally. 163 | /// 164 | /// This builder provides an API for creating enum declarations with multiple 165 | /// variants and optional documentation. It follows the builder pattern to make 166 | /// the construction process more readable and easier to maintain. 167 | pub struct EnumBuilder { 168 | name: String, 169 | variants: Vec, 170 | doc: Option, 171 | } 172 | 173 | impl EnumBuilder { 174 | /// Creates and returns a new `EnumBuilder` to construct an enum declaration. 175 | /// 176 | /// This method initializes a builder with just the enum name and no variants. 177 | /// 178 | /// # Parameters 179 | /// - `name`: The identifier name for the enum type 180 | /// 181 | /// # Returns 182 | /// A new `EnumBuilder` instance initialized with the given name 183 | /// 184 | /// # Examples 185 | /// ```rust 186 | /// let builder = EnumBuilder::new("Direction".to_string()); 187 | /// // Further configure the builder... 188 | /// let enum_def = builder 189 | /// .variant(Variant::new("NORTH".to_string()).build()) 190 | /// .build(); 191 | /// ``` 192 | pub fn new(name: String) -> Self { 193 | Self { 194 | name, 195 | variants: vec![], 196 | doc: None, 197 | } 198 | } 199 | 200 | /// Creates and returns a new `EnumBuilder` with a string slice as the enum name. 201 | /// 202 | /// This is a convenience constructor that converts the string slice to a `String` internally. 203 | /// 204 | /// # Parameters 205 | /// - `name`: String slice containing the identifier name for the enum type 206 | /// 207 | /// # Returns 208 | /// A new `EnumBuilder` instance initialized with the given name 209 | /// 210 | /// # Examples 211 | /// ```rust 212 | /// let builder = EnumBuilder::new_with_str("Direction"); 213 | /// // Further configure the builder... 214 | /// let enum_def = builder 215 | /// .variant(Variant::new_with_str("NORTH").build()) 216 | /// .build(); 217 | /// ``` 218 | pub fn new_with_str(name: &str) -> Self { 219 | Self { 220 | name: name.to_string(), 221 | variants: vec![], 222 | doc: None, 223 | } 224 | } 225 | 226 | /// Sets the documentation comment for the enum declaration. 227 | /// 228 | /// This method specifies an optional documentation comment that will be included 229 | /// before the enum declaration in the generated code. 230 | /// 231 | /// # Parameters 232 | /// - `doc`: The documentation comment to associate with the enum 233 | /// 234 | /// # Returns 235 | /// `self` for method chaining 236 | /// 237 | /// # Examples 238 | /// ```rust 239 | /// use crate::DocComment; 240 | /// 241 | /// let enum_def = EnumBuilder::new_with_str("LogLevel") 242 | /// .doc(DocComment::new("Log severity levels for the application".to_string()).build()) 243 | /// .variant(Variant::new_with_str("DEBUG").build()) 244 | /// .variant(Variant::new_with_str("INFO").build()) 245 | /// .variant(Variant::new_with_str("WARNING").build()) 246 | /// .variant(Variant::new_with_str("ERROR").build()) 247 | /// .build(); 248 | /// ``` 249 | pub fn doc(mut self, doc: DocComment) -> Self { 250 | self.doc = Some(doc); 251 | self 252 | } 253 | 254 | /// Adds a single variant to the enum declaration. 255 | /// 256 | /// This method appends a new variant to the list of enum variants. 257 | /// 258 | /// # Parameters 259 | /// - `variant`: The enum variant to add 260 | /// 261 | /// # Returns 262 | /// `self` for method chaining 263 | /// 264 | /// # Examples 265 | /// ```rust 266 | /// let enum_def = EnumBuilder::new_with_str("Direction") 267 | /// .variant(Variant::new_with_str("NORTH").build()) 268 | /// .variant(Variant::new_with_str("EAST").build()) 269 | /// .variant(Variant::new_with_str("SOUTH").build()) 270 | /// .variant(Variant::new_with_str("WEST").build()) 271 | /// .build(); 272 | /// ``` 273 | pub fn variant(mut self, variant: Variant) -> Self { 274 | self.variants.push(variant); 275 | self 276 | } 277 | 278 | /// Sets all the variants for the enum declaration at once. 279 | /// 280 | /// This method replaces any existing variants with the provided collection. 281 | /// 282 | /// # Parameters 283 | /// - `variants`: A vector of variants to include in the enum 284 | /// 285 | /// # Returns 286 | /// `self` for method chaining 287 | /// 288 | /// # Examples 289 | /// ```rust 290 | /// let month_variants = vec![ 291 | /// Variant::new_with_str("JAN").build(), 292 | /// Variant::new_with_str("FEB").build(), 293 | /// Variant::new_with_str("MAR").build(), 294 | /// // ... other months 295 | /// Variant::new_with_str("DEC").build(), 296 | /// ]; 297 | /// 298 | /// let enum_def = EnumBuilder::new_with_str("Month") 299 | /// .variants(month_variants) 300 | /// .build(); 301 | /// ``` 302 | pub fn variants(mut self, variants: Vec) -> Self { 303 | self.variants = variants; 304 | self 305 | } 306 | 307 | /// Finalizes the building process and returns the constructed `Enum` declaration. 308 | /// 309 | /// This method consumes the builder and produces an `Enum` instance with all 310 | /// the properties configured during the building process. 311 | /// 312 | /// # Returns 313 | /// A new `Enum` instance with the configured properties 314 | /// 315 | /// # Examples 316 | /// ```rust 317 | /// let enum_def = EnumBuilder::new_with_str("Boolean") 318 | /// .variant(Variant::new_with_str("FALSE").value(0).build()) 319 | /// .variant(Variant::new_with_str("TRUE").value(1).build()) 320 | /// .build(); 321 | /// ``` 322 | pub fn build(self) -> Enum { 323 | Enum { 324 | name: self.name, 325 | variants: self.variants, 326 | doc: self.doc, 327 | } 328 | } 329 | } 330 | 331 | /// Represents an individual enum variant (constant) within a C enum declaration. 332 | /// 333 | /// Each variant has a name and can optionally have an explicit integer value. 334 | /// If no explicit value is provided, C will automatically assign sequential 335 | /// values starting from 0 or continuing from the last explicit value. 336 | /// 337 | /// # Examples 338 | /// 339 | /// Basic variant without explicit value: 340 | /// ```rust 341 | /// let variant = Variant::new("RED".to_string()).build(); 342 | /// ``` 343 | /// 344 | /// Variant with explicit value: 345 | /// ```rust 346 | /// let variant = Variant::new("NOT_FOUND".to_string()).value(404).build(); 347 | /// ``` 348 | /// 349 | /// Variant with documentation: 350 | /// ```rust 351 | /// use crate::DocComment; 352 | /// 353 | /// let variant = Variant::new("SUCCESS".to_string()) 354 | /// .value(0) 355 | /// .doc(DocComment::new("Operation completed successfully".to_string()).build()) 356 | /// .build(); 357 | /// ``` 358 | #[derive(Debug, Clone, DisplayFromFormat)] 359 | pub struct Variant { 360 | /// The identifier name for this enum constant 361 | pub name: String, 362 | 363 | /// Optional explicit integer value for this enum constant 364 | pub value: Option, 365 | 366 | /// Optional documentation comment for this variant 367 | pub doc: Option, 368 | } 369 | 370 | impl Variant { 371 | /// Creates and returns a new `VariantBuilder` to construct an enum variant. 372 | /// 373 | /// This method provides a convenient entry point to start building an enum variant. 374 | /// The returned builder allows for fluent and clear construction of enum constants. 375 | /// 376 | /// # Parameters 377 | /// - `name`: The identifier name for the enum constant 378 | /// 379 | /// # Returns 380 | /// A new `VariantBuilder` instance initialized with the given name 381 | /// 382 | /// # Examples 383 | /// ```rust 384 | /// let variant = Variant::new("ERROR".to_string()) 385 | /// .value(-1) 386 | /// .build(); 387 | /// ``` 388 | pub fn new(name: String) -> VariantBuilder { 389 | VariantBuilder::new(name) 390 | } 391 | } 392 | 393 | impl Format for Variant { 394 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 395 | if let Some(doc) = &self.doc { 396 | doc.format(fmt)?; 397 | } 398 | 399 | write!(fmt, "{}", self.name)?; 400 | 401 | if let Some(value) = self.value { 402 | write!(fmt, " = {value}")?; 403 | } 404 | 405 | Ok(()) 406 | } 407 | } 408 | 409 | /// A builder for constructing a `Variant` instance incrementally. 410 | /// 411 | /// This builder provides a fluent API for creating enum variants with optional 412 | /// explicit values and documentation. It follows the builder pattern to make 413 | /// the construction process more readable and easier to maintain. 414 | pub struct VariantBuilder { 415 | name: String, 416 | value: Option, 417 | doc: Option, 418 | } 419 | 420 | impl VariantBuilder { 421 | /// Creates and returns a new `VariantBuilder` to construct an enum variant. 422 | /// 423 | /// This method initializes a builder with just the variant name and no explicit value. 424 | /// 425 | /// # Parameters 426 | /// - `name`: The identifier name for the enum constant 427 | /// 428 | /// # Returns 429 | /// A new `VariantBuilder` instance initialized with the given name 430 | /// 431 | /// # Examples 432 | /// ```rust 433 | /// let builder = VariantBuilder::new("SUCCESS".to_string()); 434 | /// // Further configure the builder... 435 | /// let variant = builder 436 | /// .value(0) 437 | /// .build(); 438 | /// ``` 439 | pub fn new(name: String) -> Self { 440 | Self { 441 | name, 442 | value: None, 443 | doc: None, 444 | } 445 | } 446 | 447 | /// Creates and returns a new `VariantBuilder` with a string slice as the variant name. 448 | /// 449 | /// This is a convenience constructor that converts the string slice to a `String` internally. 450 | /// 451 | /// # Parameters 452 | /// - `name`: String slice containing the identifier name for the enum constant 453 | /// 454 | /// # Returns 455 | /// A new `VariantBuilder` instance initialized with the given name 456 | /// 457 | /// # Examples 458 | /// ```rust 459 | /// let builder = VariantBuilder::new_with_str("SUCCESS"); 460 | /// // Further configure the builder... 461 | /// let variant = builder 462 | /// .value(0) 463 | /// .build(); 464 | /// ``` 465 | pub fn new_with_str(name: &str) -> Self { 466 | Self { 467 | name: name.to_string(), 468 | value: None, 469 | doc: None, 470 | } 471 | } 472 | 473 | /// Sets the documentation comment for the enum variant. 474 | /// 475 | /// This method specifies an optional documentation comment that will be included 476 | /// before the variant declaration in the generated code. 477 | /// 478 | /// # Parameters 479 | /// - `doc`: The documentation comment to associate with the variant 480 | /// 481 | /// # Returns 482 | /// `self` for method chaining 483 | /// 484 | /// # Examples 485 | /// ```rust 486 | /// use crate::DocComment; 487 | /// 488 | /// let variant = VariantBuilder::new_with_str("SUCCESS") 489 | /// .doc(DocComment::new("Operation completed successfully".to_string()).build()) 490 | /// .value(0) 491 | /// .build(); 492 | /// ``` 493 | pub fn doc(mut self, doc: DocComment) -> Self { 494 | self.doc = Some(doc); 495 | self 496 | } 497 | 498 | /// Sets the explicit integer value for the enum variant. 499 | /// 500 | /// In C, enum variants can have explicit values assigned. If not specified, 501 | /// C will automatically assign sequential values starting from 0 or continuing 502 | /// from the last explicit value. 503 | /// 504 | /// # Parameters 505 | /// - `value`: The explicit integer value to assign to this enum constant 506 | /// 507 | /// # Returns 508 | /// `self` for method chaining 509 | /// 510 | /// # Examples 511 | /// ```rust 512 | /// let variant = VariantBuilder::new_with_str("NOT_FOUND") 513 | /// .value(404) 514 | /// .build(); 515 | /// ``` 516 | pub fn value(mut self, value: i64) -> Self { 517 | self.value = Some(value); 518 | self 519 | } 520 | 521 | /// Finalizes the building process and returns the constructed `Variant`. 522 | /// 523 | /// This method consumes the builder and produces a `Variant` instance with all 524 | /// the properties configured during the building process. 525 | /// 526 | /// # Returns 527 | /// A new `Variant` instance with the configured properties 528 | /// 529 | /// # Examples 530 | /// ```rust 531 | /// let variant = VariantBuilder::new_with_str("INTERNAL_ERROR") 532 | /// .value(500) 533 | /// .build(); 534 | /// ``` 535 | pub fn build(self) -> Variant { 536 | Variant { 537 | name: self.name, 538 | value: self.value, 539 | doc: self.doc, 540 | } 541 | } 542 | } 543 | 544 | #[cfg(test)] 545 | mod tests { 546 | use super::*; 547 | 548 | #[test] 549 | fn enum_stmt() { 550 | let e = EnumBuilder::new_with_str("Weekday") 551 | .variants(vec![ 552 | VariantBuilder::new_with_str("MONDAY").build(), 553 | VariantBuilder::new_with_str("TUESDAY").build(), 554 | VariantBuilder::new_with_str("WEDNESDAY").build(), 555 | VariantBuilder::new_with_str("THURSDAY").build(), 556 | VariantBuilder::new_with_str("FRIDAY").build(), 557 | ]) 558 | .build(); 559 | let res = r#"enum Weekday { 560 | MONDAY, 561 | TUESDAY, 562 | WEDNESDAY, 563 | THURSDAY, 564 | FRIDAY, 565 | }; 566 | "#; 567 | assert_eq!(e.to_string(), res); 568 | } 569 | } 570 | -------------------------------------------------------------------------------- /tamago/src/comment.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Nobuharu Shimazu 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | //! This module provides functionality for generating and formatting different types of comments 22 | //! in C code. It supports standard C-style comments, heading comments for section organization, 23 | //! and documentation comments typically used before functions, structs, and other declarations. 24 | //! 25 | //! The components in this module are: 26 | //! - `Comment`: Represents regular C-style comments (single-line or multi-line) 27 | //! - `CommentBuilder`: Facilitates constructing comments using the builder pattern 28 | //! - `DocComment`: Represents documentation comments in C (using `///` style) 29 | //! - `DocCommentBuilder`: Facilitates constructing documentation comments 30 | //! 31 | //! Use this module to add properly formatted comments to generated C code for improved 32 | //! readability, documentation, and code organization. 33 | 34 | use std::fmt::{self, Write}; 35 | 36 | use crate::{Format, Formatter}; 37 | use tamacro::DisplayFromFormat; 38 | 39 | /// Represents a C-style comment in generated code. This struct supports both standard 40 | /// single-line comments and special heading-style comments that are surrounded by 41 | /// separator lines for visual emphasis. 42 | /// 43 | /// ## Comment Types 44 | /// 45 | /// This struct can represent two types of comments: 46 | /// 1. **Standard Comments**: Normal comments prefixed with `//` 47 | /// 2. **Heading Comments**: Comments surrounded by horizontal lines of slashes to create 48 | /// visual separation between code sections 49 | /// 50 | /// ## Usage 51 | /// 52 | /// Comments are used for: 53 | /// - Explaining code behavior and intent 54 | /// - Marking sections of code 55 | /// - Providing context for implementation details 56 | /// - Temporarily disabling code (commenting out) 57 | /// 58 | /// ## Examples 59 | /// 60 | /// ### Standard Comment 61 | /// ```c 62 | /// // This is a standard comment 63 | /// // It can span multiple lines 64 | /// ``` 65 | /// 66 | /// ### Heading Comment 67 | /// ```c 68 | /// //////////////////////////////////////////////////////////////////////////////// 69 | /// // This is a heading comment used to mark a major section of code 70 | /// //////////////////////////////////////////////////////////////////////////////// 71 | /// ``` 72 | #[derive(Debug, Clone, DisplayFromFormat)] 73 | pub struct Comment { 74 | /// The text content of the comment, which may contain multiple lines. 75 | /// Each line will be prefixed with `//` during formatting. 76 | pub comment: String, 77 | 78 | /// Controls whether the comment is formatted as a heading comment. 79 | /// When true, the comment will be surrounded by lines of slashes. 80 | pub is_heading: bool, 81 | } 82 | 83 | impl Comment { 84 | /// Creates and returns a new `CommentBuilder` to construct a `Comment` using the builder pattern. 85 | /// 86 | /// This method is the recommended entry point for creating comments as it provides 87 | /// a fluent API for setting comment text and style. 88 | /// 89 | /// ## Example 90 | /// 91 | /// ```rust 92 | /// // Create a standard comment 93 | /// let standard_comment = Comment::new() 94 | /// .comment_with_str("This explains the following code") 95 | /// .build(); 96 | /// 97 | /// // Create a heading comment 98 | /// let heading_comment = Comment::new() 99 | /// .comment_with_str("INITIALIZATION SECTION") 100 | /// .heading(true) 101 | /// .build(); 102 | /// ``` 103 | pub fn new() -> CommentBuilder { 104 | CommentBuilder::new() 105 | } 106 | 107 | /// Internal helper method to add horizontal separator lines for heading comments. 108 | /// 109 | /// This method generates a line of slashes that spans most of the available width, 110 | /// taking into account the current indentation level. 111 | /// 112 | /// ## Parameters 113 | /// 114 | /// - `fmt`: The formatter to write to 115 | /// 116 | /// ## Returns 117 | /// 118 | /// A `fmt::Result` indicating success or failure of the write operation 119 | fn push_heading(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 120 | if self.is_heading { 121 | writeln!(fmt, "{}", "/".repeat(80 - fmt.spaces))?; 122 | } 123 | 124 | Ok(()) 125 | } 126 | } 127 | 128 | impl Format for Comment { 129 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 130 | self.push_heading(fmt)?; 131 | for line in self.comment.lines() { 132 | writeln!(fmt, "// {line}")?; 133 | } 134 | self.push_heading(fmt) 135 | } 136 | } 137 | 138 | /// A builder for constructing `Comment` instances in a fluent, chainable manner. 139 | /// 140 | /// This builder provides methods for setting the comment text and controlling 141 | /// whether it's formatted as a heading comment. It follows the builder pattern 142 | /// to allow method chaining for a more readable and flexible API. 143 | pub struct CommentBuilder { 144 | comment: String, 145 | is_heading: bool, 146 | } 147 | 148 | impl CommentBuilder { 149 | /// Creates and returns a new `CommentBuilder` instance with default settings. 150 | /// 151 | /// The new builder starts with an empty comment and is not set as a heading comment. 152 | /// 153 | /// ## Example 154 | /// 155 | /// ```rust 156 | /// let builder = CommentBuilder::new(); 157 | /// let comment = builder 158 | /// .comment_with_str("Some explanation here") 159 | /// .build(); 160 | /// ``` 161 | pub fn new() -> Self { 162 | Self { 163 | comment: String::new(), 164 | is_heading: false, 165 | } 166 | } 167 | 168 | /// Creates a new `CommentBuilder` initialized with the provided comment text. 169 | /// 170 | /// This is a convenience method that creates a builder with pre-filled comment text, 171 | /// but still defaults to a standard (non-heading) comment style. 172 | /// 173 | /// ## Parameters 174 | /// 175 | /// - `comment`: The initial text content for the comment 176 | /// 177 | /// ## Returns 178 | /// 179 | /// A new `CommentBuilder` with the provided text 180 | /// 181 | /// ## Example 182 | /// 183 | /// ```rust 184 | /// let comment = CommentBuilder::new_with_str("Important note about this code") 185 | /// .build(); 186 | /// ``` 187 | pub fn new_with_str(comment: &str) -> Self { 188 | Self { 189 | comment: comment.to_string(), 190 | is_heading: false, 191 | } 192 | } 193 | 194 | /// Sets the comment text for the builder using a String. 195 | /// 196 | /// This method overwrites any previously set comment text. 197 | /// 198 | /// ## Parameters 199 | /// 200 | /// - `comment`: The text content for the comment as a String 201 | /// 202 | /// ## Returns 203 | /// 204 | /// The builder instance for method chaining 205 | /// 206 | /// ## Example 207 | /// 208 | /// ```rust 209 | /// let dynamic_text = format!("Count: {}", some_value); 210 | /// let comment = CommentBuilder::new() 211 | /// .comment(dynamic_text) 212 | /// .build(); 213 | /// ``` 214 | pub fn comment(mut self, comment: String) -> Self { 215 | self.comment = comment; 216 | self 217 | } 218 | 219 | /// Sets the comment text for the builder using a string slice. 220 | /// 221 | /// This is a convenience method that converts the string slice to a String 222 | /// and then sets it as the comment text. 223 | /// 224 | /// ## Parameters 225 | /// 226 | /// - `comment`: The text content for the comment as a string slice 227 | /// 228 | /// ## Returns 229 | /// 230 | /// The builder instance for method chaining 231 | /// 232 | /// ## Example 233 | /// 234 | /// ```rust 235 | /// let comment = CommentBuilder::new() 236 | /// .comment_with_str("This function calculates the average") 237 | /// .build(); 238 | /// ``` 239 | pub fn comment_with_str(self, comment: &str) -> Self { 240 | self.comment(comment.to_string()) 241 | } 242 | 243 | /// Controls whether the comment should be formatted as a heading comment. 244 | /// 245 | /// Heading comments are surrounded by lines of slashes for visual emphasis, 246 | /// making them useful for marking major sections of code. 247 | /// 248 | /// ## Parameters 249 | /// 250 | /// - `b`: Set to true for a heading comment, false for a standard comment 251 | /// 252 | /// ## Returns 253 | /// 254 | /// The builder instance for method chaining 255 | /// 256 | /// ## Example 257 | /// 258 | /// ```rust 259 | /// let section_header = CommentBuilder::new() 260 | /// .comment_with_str("HELPER FUNCTIONS") 261 | /// .heading(true) 262 | /// .build(); 263 | /// ``` 264 | pub fn heading(mut self, b: bool) -> Self { 265 | self.is_heading = b; 266 | self 267 | } 268 | 269 | /// Consumes the builder and returns a new `Comment` instance. 270 | /// 271 | /// This method finalizes the building process and returns the constructed comment 272 | /// with the text and style settings configured during the build process. 273 | /// 274 | /// ## Returns 275 | /// 276 | /// A new `Comment` instance with the configured settings 277 | /// 278 | /// ## Example 279 | /// 280 | /// ```rust 281 | /// let comment = CommentBuilder::new() 282 | /// .comment_with_str("Important implementation detail") 283 | /// .build(); 284 | /// ``` 285 | pub fn build(self) -> Comment { 286 | Comment { 287 | comment: self.comment, 288 | is_heading: self.is_heading, 289 | } 290 | } 291 | } 292 | 293 | /// Represents a documentation comment in C code, using the `///` style syntax. 294 | /// 295 | /// Documentation comments are special comments typically placed before function declarations, 296 | /// struct definitions, typedefs, and other code elements to document their purpose, parameters, 297 | /// return values, and other important information. 298 | /// 299 | /// ## Usage 300 | /// 301 | /// Doc comments are typically used to document: 302 | /// - Function signatures (parameters, return values, exceptions) 303 | /// - Data structures and their fields 304 | /// - Global variables and constants 305 | /// - Type definitions 306 | /// - File/module purpose 307 | /// 308 | /// ## Example 309 | /// 310 | /// ```c 311 | /// /// Calculates the square of a number 312 | /// /// 313 | /// /// @param value The input number to square 314 | /// /// @return The square of the input value 315 | /// int square(int value); 316 | /// ``` 317 | #[derive(Debug, Clone, DisplayFromFormat)] 318 | pub struct DocComment { 319 | /// Lines of documentation comment text. 320 | /// Each line will be prefixed with `///` during formatting. 321 | pub docs: Vec, 322 | } 323 | 324 | impl DocComment { 325 | /// Creates and returns a new `DocCommentBuilder` to construct a `DocComment` using the builder pattern. 326 | /// 327 | /// This method is the recommended entry point for creating documentation comments as it provides 328 | /// a fluent API for building multi-line documentation. 329 | /// 330 | /// ## Example 331 | /// 332 | /// ```rust 333 | /// let doc_comment = DocComment::new() 334 | /// .line_str("Calculates the factorial of a number") 335 | /// .line_str("") 336 | /// .line_str("@param n The input number") 337 | /// .line_str("@return The factorial of n") 338 | /// .build(); 339 | /// ``` 340 | pub fn new() -> DocCommentBuilder { 341 | DocCommentBuilder::new() 342 | } 343 | } 344 | 345 | impl Format for DocComment { 346 | fn format(&self, fmt: &mut Formatter<'_>) -> fmt::Result { 347 | for line in &self.docs { 348 | writeln!(fmt, "/// {line}")?; 349 | } 350 | Ok(()) 351 | } 352 | } 353 | 354 | /// A builder for constructing `DocComment` instances in a fluent, chainable manner. 355 | /// 356 | /// This builder provides methods for building documentation comments line by line 357 | /// or from larger text blocks. It follows the builder pattern to allow method chaining 358 | /// for a more readable and flexible API. 359 | pub struct DocCommentBuilder { 360 | docs: Vec, 361 | } 362 | 363 | impl DocCommentBuilder { 364 | /// Creates and returns a new `DocCommentBuilder` instance with an empty set of documentation lines. 365 | /// 366 | /// ## Example 367 | /// 368 | /// ```rust 369 | /// let builder = DocCommentBuilder::new(); 370 | /// let doc = builder 371 | /// .line_str("Function description here") 372 | /// .line_str("@param name Description of parameter") 373 | /// .build(); 374 | /// ``` 375 | pub fn new() -> Self { 376 | Self { docs: vec![] } 377 | } 378 | 379 | /// Appends a single line of documentation text to the comment using a String. 380 | /// 381 | /// ## Parameters 382 | /// 383 | /// - `line`: The line of text to add as a String 384 | /// 385 | /// ## Returns 386 | /// 387 | /// The builder instance for method chaining 388 | /// 389 | /// ## Example 390 | /// 391 | /// ```rust 392 | /// let param_desc = format!("@param count Maximum number (default: {})", DEFAULT_COUNT); 393 | /// let doc = DocCommentBuilder::new() 394 | /// .line(param_desc) 395 | /// .build(); 396 | /// ``` 397 | pub fn line(self, line: String) -> Self { 398 | self.line_str(&line) 399 | } 400 | 401 | /// Appends a single line of documentation text to the comment using a string slice. 402 | /// 403 | /// This method handles empty lines by preserving them in the documentation comment. 404 | /// Empty lines are useful for separating sections within documentation. 405 | /// 406 | /// ## Parameters 407 | /// 408 | /// - `line`: The line of text to add as a string slice 409 | /// 410 | /// ## Returns 411 | /// 412 | /// The builder instance for method chaining 413 | /// 414 | /// ## Example 415 | /// 416 | /// ```rust 417 | /// let doc = DocCommentBuilder::new() 418 | /// .line_str("Parses a configuration file") 419 | /// .line_str("") // Empty line for visual separation 420 | /// .line_str("@param path Path to the configuration file") 421 | /// .build(); 422 | /// ``` 423 | pub fn line_str(mut self, line: &str) -> Self { 424 | self.docs.push(if line.is_empty() { 425 | String::new() 426 | } else { 427 | line.to_string() 428 | }); 429 | self 430 | } 431 | 432 | /// Adds a multi-line block of text to the documentation comment using a String. 433 | /// 434 | /// This method splits the input text by newlines and adds each line separately. 435 | /// It also handles line wrapping for long lines to maintain readability. 436 | /// 437 | /// ## Parameters 438 | /// 439 | /// - `text`: The multi-line text block as a String 440 | /// 441 | /// ## Returns 442 | /// 443 | /// The builder instance for method chaining 444 | /// 445 | /// ## Example 446 | /// 447 | /// ```rust 448 | /// let description = format!( 449 | /// "Processes data according to the specified algorithm.\n\n\ 450 | /// The processing has a complexity of O(n) where n is {}.", 451 | /// complexity_description 452 | /// ); 453 | /// let doc = DocCommentBuilder::new() 454 | /// .text(description) 455 | /// .build(); 456 | /// ``` 457 | pub fn text(self, text: String) -> Self { 458 | self.text_str(&text) 459 | } 460 | 461 | /// Adds a multi-line block of text to the documentation comment using a string slice. 462 | /// 463 | /// This method processes the text block and adds it to the documentation comment with 464 | /// intelligent line handling: 465 | /// 1. Splits the input by newlines 466 | /// 2. Preserves empty lines 467 | /// 3. Performs line wrapping for lines longer than 80 characters 468 | /// 469 | /// ## Parameters 470 | /// 471 | /// - `text`: The multi-line text block as a string slice 472 | /// 473 | /// ## Returns 474 | /// 475 | /// The builder instance for method chaining 476 | /// 477 | /// ## Example 478 | /// 479 | /// ```rust 480 | /// let doc = DocCommentBuilder::new() 481 | /// .text_str( 482 | /// "This function implements the algorithm described in:\n\ 483 | /// Smith, J. (2023). Efficient Algorithms for Data Processing.\n\n\ 484 | /// Note: The implementation uses recursion and may cause stack overflow\ 485 | /// with extremely large inputs." 486 | /// ) 487 | /// .build(); 488 | /// ``` 489 | pub fn text_str(self, text: &str) -> Self { 490 | let mut res = self; 491 | for line in text.lines() { 492 | if line.is_empty() || line == "\n" { 493 | res = res.line_str(""); 494 | continue; 495 | } 496 | 497 | let mut start = 0; 498 | let mut end = 0; 499 | for (offset, c) in line.chars().enumerate() { 500 | if c == ' ' && (offset - start) > 80 { 501 | res = res.line_str(&line[start..=end]); 502 | start = end; 503 | } 504 | end = offset; 505 | } 506 | 507 | if start == end { 508 | res = res.line_str(""); 509 | } else { 510 | res = res.line_str(&line[start..=end]); 511 | } 512 | } 513 | 514 | res 515 | } 516 | 517 | /// Consumes the builder and returns a new `DocComment` instance. 518 | /// 519 | /// This method finalizes the building process and returns the constructed documentation 520 | /// comment containing all the lines added during the building process. 521 | /// 522 | /// ## Returns 523 | /// 524 | /// A new `DocComment` instance with the configured documentation lines 525 | /// 526 | /// ## Example 527 | /// 528 | /// ```rust 529 | /// let complete_doc = DocCommentBuilder::new() 530 | /// .line_str("Encrypts data using the specified algorithm") 531 | /// .line_str("") 532 | /// .line_str("@param data The data to encrypt") 533 | /// .line_str("@param key The encryption key") 534 | /// .line_str("@return The encrypted data or NULL on failure") 535 | /// .build(); 536 | /// ``` 537 | pub fn build(self) -> DocComment { 538 | DocComment { docs: self.docs } 539 | } 540 | } 541 | 542 | #[cfg(test)] 543 | mod tests { 544 | use super::*; 545 | 546 | #[test] 547 | fn comment() { 548 | let mut c = CommentBuilder::new_with_str("Hello, world").build(); 549 | assert_eq!(c.to_string(), "// Hello, world\n"); 550 | 551 | c = CommentBuilder::new_with_str("abc").heading(true).build(); 552 | assert_eq!(c.to_string(), "////////////////////////////////////////////////////////////////////////////////\n// abc\n////////////////////////////////////////////////////////////////////////////////\n"); 553 | } 554 | 555 | #[test] 556 | fn doc_comment() { 557 | let mut c = DocComment::new().text_str("Hello\nworld").build(); 558 | assert_eq!(c.to_string(), "/// Hello\n/// world\n"); 559 | 560 | c = DocComment::new().line_str("ABC").build(); 561 | assert_eq!(c.to_string(), "/// ABC\n"); 562 | } 563 | } 564 | -------------------------------------------------------------------------------- /src/semantic_analyzer/type_checker.rs: -------------------------------------------------------------------------------- 1 | use std::collections::hash_map::Entry; 2 | use std::collections::HashMap; 3 | 4 | use tamago::{AssignOp, BinOp, UnaryOp}; 5 | 6 | use crate::parser::*; 7 | use crate::semantic_analyzer::*; 8 | 9 | type ReturnType = Option<(Type, bool)>; 10 | 11 | #[derive(Debug)] 12 | enum UserDefinedType<'ast> { 13 | Enum { 14 | span: &'ast Span, 15 | variants: &'ast Vec<(String, Option)>, 16 | }, 17 | Struct { 18 | span: &'ast Span, 19 | fields: &'ast Vec<(String, LocatedType)>, 20 | }, 21 | Union { 22 | span: &'ast Span, 23 | fields: &'ast Vec<(String, LocatedType)>, 24 | }, 25 | Function { 26 | span: &'ast Span, 27 | params: &'ast Vec<(String, LocatedType)>, 28 | ret: &'ast LocatedType, 29 | }, 30 | Alias { 31 | span: &'ast Span, 32 | t: &'ast LocatedType, 33 | }, 34 | Import { 35 | span: &'ast Span, 36 | name: &'ast str, 37 | }, 38 | } 39 | 40 | #[derive(Debug, Default)] 41 | struct Types<'ast> { 42 | types: HashMap<&'ast str, LocatedType>, 43 | enclosing: Option>>, 44 | } 45 | 46 | #[derive(Debug)] 47 | pub struct TypeChecker<'ast> { 48 | ast: &'ast Vec, 49 | types: Types<'ast>, 50 | user_def_types: HashMap<&'ast str, UserDefinedType<'ast>>, 51 | 52 | errors: Vec, 53 | warnings: Vec, 54 | } 55 | 56 | impl<'ast> TypeChecker<'ast> { 57 | pub fn new(ast: &'ast Vec) -> Self { 58 | Self { 59 | ast, 60 | types: Types::new(), 61 | user_def_types: HashMap::new(), 62 | errors: vec![], 63 | warnings: vec![], 64 | } 65 | } 66 | 67 | pub fn check(mut self) -> (Vec, Result<(), Vec>) { 68 | for stmt in self.ast { 69 | self.check_global_stmt(stmt); 70 | } 71 | 72 | if self.errors.is_empty() { 73 | (self.warnings, Ok(())) 74 | } else { 75 | (self.warnings, Err(self.errors)) 76 | } 77 | } 78 | 79 | fn check_global_stmt(&mut self, stmt: &'ast LocatedGlobalStmt) { 80 | use GlobalStmt::*; 81 | 82 | let Located { node: gstmt, span } = stmt; 83 | 84 | match gstmt { 85 | Enum { name, .. } 86 | | Struct { name, .. } 87 | | Union { name, .. } 88 | | Alias { name, .. } 89 | | Import { name, .. } => { 90 | let _ = self.define_user_type(name, stmt); 91 | } 92 | 93 | Function { 94 | name, 95 | params, 96 | ret, 97 | body, 98 | } => { 99 | if let Err(w) = self.types.declare( 100 | name, 101 | Located { 102 | node: Type::UserDefinedType(name.to_string()), 103 | span: span.clone(), 104 | }, 105 | ) { 106 | self.warnings.push(w); 107 | } 108 | 109 | let _ = self.define_user_type(name, stmt); 110 | 111 | let old_types = std::mem::take(&mut self.types); 112 | self.types = Types::new_with_types(old_types); 113 | 114 | for (name, t) in params { 115 | if let Err(err) = self.types.declare(name, t.clone()) { 116 | self.errors.push(err); 117 | } 118 | } 119 | 120 | self.check_func_body(ret, body); 121 | } 122 | 123 | Variable { 124 | name, 125 | t, 126 | value: Some(value), 127 | .. 128 | } => { 129 | let var_t: Type; 130 | if let Some(t) = t { 131 | var_t = t.clone(); 132 | let given_t: Type; 133 | match self.check_expr(value) { 134 | Err(err) => { 135 | self.errors.push(err); 136 | return; 137 | } 138 | Ok(t) => given_t = t, 139 | } 140 | 141 | if var_t != given_t { 142 | self.errors.push(( 143 | span.clone(), 144 | format!("Expected '{var_t}' but got '{given_t}'"), 145 | )); 146 | return; 147 | } 148 | } else { 149 | match self.check_expr(value) { 150 | Err(err) => { 151 | self.errors.push(err); 152 | return; 153 | } 154 | Ok(t) => var_t = t, 155 | } 156 | } 157 | 158 | if let Err(err) = self.types.declare( 159 | name, 160 | Located { 161 | node: var_t, 162 | span: span.clone(), 163 | }, 164 | ) { 165 | self.errors.push(err); 166 | } 167 | } 168 | 169 | Variable { 170 | name, 171 | t, 172 | value: None, 173 | .. 174 | } => { 175 | if let Some(t) = t { 176 | if let Err(err) = self.types.declare( 177 | name, 178 | Located { 179 | node: t.clone(), 180 | span: span.clone(), 181 | }, 182 | ) { 183 | self.errors.push(err); 184 | } 185 | } else { 186 | self.errors.push(( 187 | span.clone(), 188 | format!("Expected an explicit type but got nothing"), 189 | )); 190 | } 191 | } 192 | 193 | Constant { name, t, value, .. } => { 194 | todo!() // TODO: Only allow compile-time expressions 195 | } 196 | } 197 | } 198 | 199 | fn check_func_body(&mut self, ret: &'ast LocatedType, body: &'ast Vec) { 200 | todo!() 201 | } 202 | 203 | /// Some(true) -> full return 204 | /// Some(false) -> partial return 205 | /// None -> no return (same as void) 206 | fn check_stmt( 207 | &mut self, 208 | expected_ret: &'ast LocatedType, 209 | stmt: &'ast LocatedStmt, 210 | ) -> Result, Message> { 211 | use Stmt::*; 212 | 213 | let Located { node: s, span } = stmt; 214 | 215 | match s { 216 | Variable { 217 | name, 218 | t, 219 | value: Some(value), 220 | .. 221 | } => { 222 | let var_t: Type; 223 | if let Some(t) = t { 224 | var_t = t.clone(); 225 | let given_t = self.check_expr(value)?; 226 | 227 | if var_t != given_t { 228 | return Err(( 229 | span.clone(), 230 | format!("Expected '{var_t}' but got '{given_t}'"), 231 | )); 232 | } 233 | } else { 234 | var_t = self.check_expr(value)?; 235 | } 236 | 237 | self.types.declare( 238 | name, 239 | Located { 240 | node: var_t, 241 | span: span.clone(), 242 | }, 243 | )?; 244 | 245 | Ok(None) 246 | } 247 | 248 | Variable { 249 | name, 250 | t, 251 | value: None, 252 | .. 253 | } => { 254 | if let Some(t) = t { 255 | self.types.declare( 256 | name, 257 | Located { 258 | node: t.clone(), 259 | span: span.clone(), 260 | }, 261 | )?; 262 | 263 | Ok(None) 264 | } else { 265 | Err(( 266 | span.clone(), 267 | format!("Expected an explicit type but got nothing"), 268 | )) 269 | } 270 | } 271 | 272 | Expression { expr } => { 273 | self.check_expr(expr)?; 274 | Ok(None) 275 | } 276 | 277 | Return { value } => { 278 | if let Some(val) = value { 279 | let t = self.check_expr(val)?; 280 | if expected_ret.node == t { 281 | Ok(Some(true)) 282 | } else { 283 | Err(( 284 | span.clone(), 285 | format!( 286 | "Expected {} as return type but got {}", 287 | expected_ret.node, t 288 | ), 289 | )) 290 | } 291 | } else { 292 | if expected_ret.node == Type::Void { 293 | Ok(Some(true)) 294 | } else { 295 | Err(( 296 | span.clone(), 297 | format!( 298 | "Expected {} as return type but got {}", 299 | expected_ret.node, 300 | Type::Void 301 | ), 302 | )) 303 | } 304 | } 305 | } 306 | 307 | Break | Continue => Ok(None), 308 | 309 | If { cond, then, other } => { 310 | let cond_t = self.check_expr(cond)?; 311 | if cond_t != Type::Bool { 312 | return Err(( 313 | span.clone(), 314 | format!("If condition must be boolean but got {}", cond_t), 315 | )); 316 | } 317 | 318 | let then_returns = self.check_branch(expected_ret, then)?; 319 | 320 | let mut else_returns = false; 321 | if let Some(other) = other { 322 | else_returns = self.check_branch(expected_ret, other)?; 323 | } 324 | 325 | if then_returns && else_returns { 326 | // both then & else have a return stmt 327 | Ok(Some(true)) 328 | } else if !then_returns && !else_returns { 329 | // neither then or else have a return stmt 330 | Ok(None) 331 | } else { 332 | // either then or else has a return stmt 333 | Ok(Some(false)) 334 | } 335 | } 336 | 337 | While { cond, body, .. } => { 338 | let cond_t = self.check_expr(cond)?; 339 | if cond_t != Type::Bool { 340 | return Err(( 341 | span.clone(), 342 | format!("While condition must be boolean but got {}", cond_t), 343 | )); 344 | } 345 | 346 | if self.check_branch(expected_ret, body)? { 347 | Ok(Some(false)) 348 | } else { 349 | Ok(None) 350 | } 351 | } 352 | 353 | Defer { body } => { 354 | for stmt in body { 355 | if matches!(self.check_stmt(expected_ret, stmt)?, Some(..)) { 356 | return Err(( 357 | stmt.span.clone(), 358 | "No return statement is allowed in defer body".to_string(), 359 | )); 360 | } 361 | } 362 | Ok(None) 363 | } 364 | 365 | Destroy { expr } => { 366 | let t = self.check_expr(expr); 367 | Ok(None) 368 | } 369 | 370 | Free { expr } => { 371 | let t = self.check_expr(expr); 372 | Ok(None) 373 | } 374 | } 375 | } 376 | 377 | fn check_expr(&self, expr: &'ast LocatedExpr) -> Result { 378 | use Expr::*; 379 | 380 | let Located { node: e, span } = expr; 381 | 382 | match e { 383 | Int(..) => Ok(Type::Int32), 384 | Double(..) => Ok(Type::Double), 385 | Bool(..) => Ok(Type::Bool), 386 | Char(..) => Ok(Type::Char), 387 | Str(..) => Ok(Type::Str), 388 | Ident(name) => todo!(), 389 | Binary { left, op, right } => self.check_binary(span, left, op, right), 390 | Parenthesized { expr } => self.check_expr(expr), 391 | Unary { op, expr } => self.check_unary(span, op, expr), 392 | Assign { lvalue, op, value } => self.check_assign(span, lvalue, op, value), 393 | Ternary { cond, lexpr, rexpr } => self.check_ternary(span, lexpr, rexpr), 394 | FnCall { name, args } => self.check_fn_call(span, name, args), 395 | MemAccess { expr, member } => self.check_mem_access(span, expr, member), 396 | EnumVarAccess { ident, variant } => self.check_enum_var_access(span, ident, variant), 397 | ArrIndex { arr, idx } => self.check_arr_index(span, arr, idx), 398 | Cast { t, expr } => self.check_cast(span, t, expr), 399 | Sizeof { t } => self.check_sizeof(span, t), 400 | InitArr { elems } => self.check_init_arr(span, elems), 401 | InitArrDesignated { idxs, elems } => self.check_init_arr_designated(span, idxs, elems), 402 | InitStruct { ident, args } => self.check_init_struct(span, ident, args), 403 | Make { t } => self.check_make(span, t), 404 | New { t } => self.check_new(span, t), 405 | } 406 | } 407 | 408 | fn check_new(&self, span: &'ast Span, t: &'ast Type) -> Result { 409 | todo!() 410 | } 411 | 412 | fn check_make(&self, span: &'ast Span, t: &'ast Type) -> Result { 413 | todo!() 414 | } 415 | 416 | fn check_init_struct( 417 | &self, 418 | span: &'ast Span, 419 | ident: &'ast String, 420 | args: &'ast Vec<(String, LocatedExpr)>, 421 | ) -> Result { 422 | todo!() 423 | } 424 | 425 | fn check_init_arr_designated( 426 | &self, 427 | span: &'ast Span, 428 | idx: &'ast Vec, 429 | elems: &'ast Vec, 430 | ) -> Result { 431 | todo!() 432 | } 433 | 434 | fn check_init_arr( 435 | &self, 436 | span: &'ast Span, 437 | elems: &'ast Vec, 438 | ) -> Result { 439 | todo!() 440 | } 441 | 442 | fn check_sizeof(&self, span: &'ast Span, t: &'ast Type) -> Result { 443 | todo!() 444 | } 445 | 446 | fn check_cast( 447 | &self, 448 | span: &'ast Span, 449 | t: &'ast LocatedType, 450 | expr: &'ast LocatedExpr, 451 | ) -> Result { 452 | todo!() 453 | } 454 | 455 | fn check_arr_index( 456 | &self, 457 | span: &'ast Span, 458 | args: &'ast LocatedExpr, 459 | idx: &'ast LocatedExpr, 460 | ) -> Result { 461 | todo!() 462 | } 463 | 464 | fn check_enum_var_access( 465 | &self, 466 | span: &'ast Span, 467 | ident: &'ast String, 468 | variant: &'ast String, 469 | ) -> Result { 470 | todo!() 471 | } 472 | 473 | fn check_mem_access( 474 | &self, 475 | span: &'ast Span, 476 | expr: &'ast LocatedExpr, 477 | member: &'ast String, 478 | ) -> Result { 479 | todo!() 480 | } 481 | 482 | fn check_fn_call( 483 | &self, 484 | span: &'ast Span, 485 | name: &'ast LocatedExpr, 486 | args: &'ast Vec, 487 | ) -> Result { 488 | todo!() 489 | } 490 | 491 | fn check_ternary( 492 | &self, 493 | span: &'ast Span, 494 | lexpr: &'ast LocatedExpr, 495 | rexpr: &'ast LocatedExpr, 496 | ) -> Result { 497 | todo!() 498 | } 499 | 500 | fn check_binary( 501 | &self, 502 | span: &'ast Span, 503 | left: &'ast LocatedExpr, 504 | op: &'ast BinOp, 505 | right: &'ast LocatedExpr, 506 | ) -> Result { 507 | todo!() 508 | } 509 | 510 | fn check_unary( 511 | &self, 512 | span: &'ast Span, 513 | op: &'ast UnaryOp, 514 | expr: &'ast LocatedExpr, 515 | ) -> Result { 516 | todo!() 517 | } 518 | 519 | fn check_assign( 520 | &self, 521 | span: &'ast Span, 522 | lvalue: &'ast LocatedExpr, 523 | op: &'ast AssignOp, 524 | value: &'ast LocatedExpr, 525 | ) -> Result { 526 | todo!() 527 | } 528 | 529 | /// true -> the branch has a return 530 | /// false -> the branch doesn't have a return 531 | fn check_branch( 532 | &mut self, 533 | expected_ret: &'ast LocatedType, 534 | branch: &'ast Vec, 535 | ) -> Result { 536 | let mut result: Result = Ok(false); 537 | for stmt in branch { 538 | if result != Ok(false) { 539 | self.warnings 540 | .push((stmt.span.clone(), format!("Unreachable code after return"))); 541 | break; 542 | } 543 | 544 | match self.check_stmt(expected_ret, stmt)? { 545 | Some(p) if p => { 546 | result = Ok(true); 547 | } 548 | _ => {} 549 | } 550 | } 551 | 552 | result 553 | } 554 | 555 | fn define_user_type( 556 | &mut self, 557 | name: &'ast str, 558 | stmt: &'ast LocatedGlobalStmt, 559 | ) -> Result<(), Message> { 560 | let span = stmt.span.clone(); 561 | let t: UserDefinedType<'ast>; 562 | 563 | if let Ok(ty) = UserDefinedType::<'ast>::try_from(stmt) { 564 | t = ty; 565 | } else { 566 | todo!(); // TODO: probably just ignore? 567 | // return Err((span, format!(""))); 568 | } 569 | 570 | match self.user_def_types.entry(name) { 571 | Entry::Occupied(_) => Err((span, format!("Type '{name}' is already declared"))), 572 | Entry::Vacant(entry) => { 573 | entry.insert(t); 574 | Ok(()) 575 | } 576 | } 577 | } 578 | } 579 | 580 | impl<'ast> Types<'ast> { 581 | pub fn new() -> Self { 582 | Self { 583 | types: HashMap::new(), 584 | enclosing: None, 585 | } 586 | } 587 | 588 | pub fn new_with_types(types: Types<'ast>) -> Self { 589 | Self { 590 | types: HashMap::new(), 591 | enclosing: Some(Box::new(types)), 592 | } 593 | } 594 | 595 | pub fn declare(&mut self, name: &'ast str, t: LocatedType) -> Result<(), Message> { 596 | match self.types.entry(name) { 597 | Entry::Occupied(_) => Err((t.span.clone(), format!("'{name}' is already declared"))), 598 | Entry::Vacant(entry) => { 599 | entry.insert(t); 600 | Ok(()) 601 | } 602 | } 603 | } 604 | 605 | pub fn is_declared(&mut self, name: &'ast str, span: Span) -> Result<(), Message> { 606 | if self.types.contains_key(name) { 607 | Ok(()) 608 | } else if let Some(types) = &mut self.enclosing { 609 | types.is_declared(name, span) 610 | } else { 611 | Err((span, format!("'{name}' is not declared"))) 612 | } 613 | } 614 | } 615 | 616 | impl<'ast> TryFrom<&'ast LocatedGlobalStmt> for UserDefinedType<'ast> { 617 | type Error = (); 618 | 619 | fn try_from(value: &'ast LocatedGlobalStmt) -> Result { 620 | use GlobalStmt::*; 621 | 622 | let Located { span, node: value } = value; 623 | 624 | match value { 625 | Enum { variants, .. } => Ok(UserDefinedType::Enum { span, variants }), 626 | Struct { fields, .. } => Ok(UserDefinedType::Struct { span, fields }), 627 | Union { fields, .. } => Ok(UserDefinedType::Union { span, fields }), 628 | Function { params, ret, .. } => Ok(UserDefinedType::Function { span, params, ret }), 629 | Alias { t, .. } => Ok(UserDefinedType::Alias { span, t }), 630 | Import { name, .. } => Ok(UserDefinedType::Import { span, name }), 631 | _ => Err(()), 632 | } 633 | } 634 | } 635 | --------------------------------------------------------------------------------