From 4256e332a83f618dc2f001e2dc94e3f9bd57e6f5 Mon Sep 17 00:00:00 2001 From: Collin MacDonald Date: Fri, 8 Aug 2025 13:54:32 -0500 Subject: [PATCH] [odin/en] Add Odin.md (#5346) * Create odin.md * Add link to Odin book * Improve build-int data structure section * Fix os.open() example and remove SIMD --- odin.md | 575 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 575 insertions(+) create mode 100644 odin.md diff --git a/odin.md b/odin.md new file mode 100644 index 00000000..d7c01e03 --- /dev/null +++ b/odin.md @@ -0,0 +1,575 @@ +--- +name: Odin +contributors: + - ["Collin MacDonald", "https://github.com/CollinEMac"] +filename: learnodin.odin +--- + +Odin was created by Bill "gingerBill" Hall. It is a general-purpose systems +programming language that emphasizes simplicity, readability, and performance +without garbage collection. Odin bills itself as "the C alternative for the +joy of programming." + +```odin +// Single line comments start with two slashes. + +/* + Multiline comments start with slash-star, + and end with star-slash. They can be nested! + /* + Like this! + */ +*/ + +// This is the classic "hello world" program in Odin. +package main + +import "core:fmt" + +main :: proc() { + fmt.println("Hellope!") +} + +//////////////////////////////////////////////////// +## 1. Basic Data Types and Operators +//////////////////////////////////////////////////// + +// Integers - Odin has explicit sized integer types +x: i32 = 42 // 32-bit signed integer +y: u64 = 100 // 64-bit unsigned integer +z: int = 123 // Platform-dependent integer (usually i64) + +// You can use underscores for readability in numbers +big_number := 1_000_000 + +// Floating point numbers +pi: f32 = 3.14159 // 32-bit float +e: f64 = 2.71828 // 64-bit float (default for float literals) + +// Boolean +is_true: bool = true +is_false: bool = false + +// Rune (Unicode character) +letter: rune = 'A' +emoji: rune = '🚀' + +// Strings +name: string = "Odin Programming" +raw_string := `C:\Windows\System32` // Raw string with backticks + +// String length (in bytes, not characters!) +length := len(name) + +// Arithmetic operators work as you'd expect +result := 10 + 5 // 15 +diff := 10 - 5 // 5 +product := 10 * 5 // 50 +quotient := 10 / 5 // 2 +remainder := 10 % 3 // 1 + +// Comparison operators +is_equal := 10 == 10 // true +not_equal := 10 != 5 // true +greater := 10 > 5 // true +less_equal := 5 <= 10 // true + +// Logical operators +and_result := true && false // false +or_result := true || false // true +not_result := !true // false + +// Bitwise operators +bit_and := 0b1010 & 0b1100 // 0b1000 +bit_or := 0b1010 | 0b1100 // 0b1110 +bit_xor := 0b1010 ~ 0b1100 // 0b0110 (note: ~ is XOR in Odin) +bit_not := ~0b1010 // bitwise NOT + +//////////////////////////////////////////////////// +## 2. Variables and Constants +//////////////////////////////////////////////////// + +// Variable declaration with type inference +some_number := 42 // Type inferred as int +some_text := "Hello" // Type inferred as string + +// Explicit type declaration +explicit_int: int = 42 +explicit_float: f64 = 3.14 + +// Uninitialized variables are zero-initialized +uninitialized_int: int // Defaults to 0 +uninitialized_bool: bool // Defaults to false +uninitialized_string: string // Defaults to "" + +// Constants are defined with :: +PI :: 3.14159 +MESSAGE :: "This is a constant" + +// Typed constants +TYPED_CONSTANT : f32 : 2.71828 + +// Multiple assignment +a, b := 10, 20 +a, b = b, a // Swap values + +//////////////////////////////////////////////////// +## 3. Arrays and Slices +//////////////////////////////////////////////////// + +// Fixed-size arrays +numbers: [5]int = {1, 2, 3, 4, 5} +chars: [3]rune = {'A', 'B', 'C'} + +// Array with inferred size +inferred := [..]int{10, 20, 30, 40} + +// Zero-initialized array +zeros: [10]int // All elements are 0 + +// Accessing array elements +first := numbers[0] // 1 +last := numbers[4] // 5 +array_length := len(numbers) // 5 + +// Slices - dynamic views into arrays +slice: []int = {1, 2, 3, 4, 5} // Slice literal +array_slice := numbers[1:4] // Slice of array from index 1 to 3 +full_slice := numbers[:] // Slice of entire array + +// Dynamic arrays - can grow and shrink +dynamic_array: [dynamic]int +append(&dynamic_array, 1) +append(&dynamic_array, 2, 3, 4) // Append multiple elements + +// Remember to clean up dynamic arrays +defer delete(dynamic_array) + +//////////////////////////////////////////////////// +## 4. Control Flow +//////////////////////////////////////////////////// + +// If statements +age := 25 +if age >= 18 { + fmt.println("Adult") +} else if age >= 13 { + fmt.println("Teenager") +} else { + fmt.println("Child") +} + +// For loops - Odin's only loop construct +// C-style for loop +for i := 0; i < 10; i += 1 { + fmt.println(i) +} + +// While-style loop +counter := 0 +for counter < 5 { + fmt.println(counter) + counter += 1 +} + +// Infinite loop +for { + // This runs forever (until break) + break // Exit the loop +} + +// Iterating over arrays/slices with index +numbers_array := [3]int{10, 20, 30} +for value, index in numbers_array { + fmt.printf("Index %d: Value %d\n", index, value) +} + +// Iterating over just values +for value in numbers_array { + fmt.println(value) +} + +// Switch statements +day := "Monday" +switch day { +case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday": + fmt.println("Weekday") +case "Saturday", "Sunday": + fmt.println("Weekend") +case: // Default case + fmt.println("Unknown day") +} + +// Switch with no condition (like if-else chain) +switch { +case age < 13: + fmt.println("Child") +case age < 20: + fmt.println("Teenager") +case: + fmt.println("Adult") +} + +//////////////////////////////////////////////////// +## 5. Procedures (Functions) +//////////////////////////////////////////////////// + +// Basic procedure definition +add :: proc(a: int, b: int) -> int { + return a + b +} + +// Procedure with multiple return values +divide :: proc(a: int, b: int) -> (int, bool) { + if b == 0 { + return 0, false // Division by zero + } + return a / b, true +} + +// Using the procedure +sum := add(5, 3) // 8 +quotient, ok := divide(10, 2) // 5, true +quotient_bad, ok_bad := divide(10, 0) // 0, false + +// Procedure with default parameters (using overloading) +greet :: proc(name: string) { + fmt.printf("Hello, %s!\n", name) +} + +greet :: proc() { + greet("World") +} + +// Variadic procedures (variable number of arguments) +sum_all :: proc(numbers: ..int) -> int { + total := 0 + for number in numbers { + total += number + } + return total +} + +result_sum := sum_all(1, 2, 3, 4, 5) // 15 + +//////////////////////////////////////////////////// +## 6. Structs +//////////////////////////////////////////////////// + +// Struct definition +Person :: struct { + name: string, + age: int, + height: f32, +} + +// Creating struct instances +person1 := Person{ + name = "Alice", + age = 30, + height = 5.6, +} + +// Partial initialization (remaining fields are zero-initialized) +person2 := Person{ + name = "Bob", + // age and height default to 0 +} + +// Accessing struct fields +fmt.printf("%s is %d years old\n", person1.name, person1.age) + +// Modifying struct fields +person1.age = 31 + +// Procedure that works with structs +celebrate_birthday :: proc(person: ^Person) { // ^ means pointer + person.age += 1 + fmt.printf("Happy birthday! %s is now %d\n", person.name, person.age) +} + +celebrate_birthday(&person1) // Pass address with & + +//////////////////////////////////////////////////// +## 7. Enums and Unions +//////////////////////////////////////////////////// + +// Enums +Color :: enum { + RED, + GREEN, + BLUE, + YELLOW, +} + +my_color := Color.RED + +// Enums with explicit values +Status :: enum u8 { + OK = 0, + ERROR = 1, + WARNING = 2, +} + +// Unions (tagged unions) +Shape :: union { + Circle: struct { radius: f32 }, + Rectangle: struct { width, height: f32 }, + Triangle: struct { base, height: f32 }, +} + +my_shape := Shape(Circle{{radius = 5.0}}) + +// Pattern matching with unions +switch shape in my_shape { +case Circle: + fmt.printf("Circle with radius %.2f\n", shape.radius) +case Rectangle: + fmt.printf("Rectangle %.2f x %.2f\n", shape.width, shape.height) +case Triangle: + fmt.printf("Triangle base %.2f, height %.2f\n", shape.base, + shape.height) +} + +//////////////////////////////////////////////////// +## 8. Maps +//////////////////////////////////////////////////// + +// Map declaration +scores: map[string]int + +// Initialize map +scores = make(map[string]int) +defer delete(scores) // Clean up when done + +// Add key-value pairs +scores["Alice"] = 95 +scores["Bob"] = 87 +scores["Charlie"] = 92 + +// Access values +alice_score := scores["Alice"] // 95 + +// Check if key exists +bob_score, exists := scores["Bob"] +if exists { + fmt.printf("Bob's score: %d\n", bob_score) +} + +// Iterate over map +for name, score in scores { + fmt.printf("%s: %d\n", name, score) +} + +// Map literal +ages := map[string]int{ + "Alice" = 30, + "Bob" = 25, + "Charlie" = 35, +} +defer delete(ages) + +//////////////////////////////////////////////////// +## 9. Pointers and Memory Management +//////////////////////////////////////////////////// + +// Pointers +number := 42 +number_ptr := &number // Get address of number +value := number_ptr^ // Dereference pointer (get value) + +fmt.printf("Value: %d, Address: %p\n", value, number_ptr) + +// Dynamic memory allocation +// new() allocates and returns a pointer +int_ptr := new(int) +int_ptr^ = 100 +defer free(int_ptr) // Clean up memory + +// make() for complex types +my_slice := make([]int, 5) // Slice with length 5 +defer delete(my_slice) + +//////////////////////////////////////////////////// +## 10. Error Handling +//////////////////////////////////////////////////// + +// Odin uses multiple return values for error handling +read_file :: proc(filename: string) -> (string, bool) { + // Simulate file reading + if filename == "" { + return "", false // Error case + } + return "file contents", true // Success case +} + +// Using the error-returning procedure +content, success := read_file("myfile.txt") +if !success { + fmt.println("Failed to read file") +} else { + fmt.printf("File content: %s\n", content) +} + +// Common pattern with or_return +parse_number :: proc(s: string) -> (int, bool) { + // This is a simplified example + if s == "42" { + return 42, true + } + return 0, false +} + +example_with_error_handling :: proc() -> bool { + // or_return automatically returns false if the second value is false + num := parse_number("42") or_return + fmt.printf("Parsed number: %d\n", num) + return true +} + +//////////////////////////////////////////////////// +## 11. Packages and Imports +//////////////////////////////////////////////////// + +// Every .odin file starts with a package declaration +// package main // (Already declared at the top) + +// Import from core library +import "core:fmt" +import "core:strings" +import "core:os" + +// Import with alias +import str "core:strings" + +// Using imported procedures +text := "Hello, World!" +upper_text := strings.to_upper(text) +fmt.println(upper_text) + +// Import from vendor packages (external libraries) +// import "vendor:raylib" + +//////////////////////////////////////////////////// +## 12. Compile-time Features +//////////////////////////////////////////////////// + +// Compile-time conditionals +when ODIN_OS == .Windows { + // Windows-specific code + fmt.println("Running on Windows") +} else when ODIN_OS == .Linux { + // Linux-specific code + fmt.println("Running on Linux") +} else { + // Other platforms + fmt.println("Running on other platform") +} + +// Compile-time constants +ODIN_DEBUG :: #config(DEBUG, false) + +when ODIN_DEBUG { + fmt.println("Debug mode enabled") +} + +// Generics (Parametric polymorphism) +Generic_Array :: struct($T: typeid) { + data: []T, +} + +max :: proc(a: $T, b: T) -> T { + return a if a > b else b +} + +max_int := max(10, 20) // T becomes int +max_float := max(3.14, 2.71) // T becomes f64 + +//////////////////////////////////////////////////// +## 13. Built-in Data Structures +//////////////////////////////////////////////////// + +// Bit sets for flags +File_Mode :: enum { + READ, + WRITE, + EXECUTE, +} + +permissions: bit_set[File_Mode] +permissions |= {.READ, .WRITE} // Set multiple flags +permissions &~= {.WRITE} // Remove flag +has_read := .READ in permissions // Check flag +is_readonly := permissions == {.READ} // Compare sets + +// Complex numbers +z1 := complex64(3 + 4i) +z2 := complex64(1 - 2i) +sum := z1 + z2 // (4 + 2i) +magnitude := abs(z1) // 5.0 + +// Matrices for linear algebra +transform := matrix[3, 3]f32{ + 1, 0, 5, // Translation X = 5 + 0, 1, 3, // Translation Y = 3 + 0, 0, 1, // Homogeneous coordinate +} + +point := [3]f32{10, 20, 1} +transformed := transform * point // Matrix multiplication + +// Quaternions for 3D rotations +identity_rot := quaternion128{0, 0, 0, 1} // No rotation +rotation_90_z := quaternion128{0, 0, 0.707, 0.707} // 90° around Z + +//////////////////////////////////////////////////// +## 14. Context System and Defer +//////////////////////////////////////////////////// + +// Odin has an implicit context system for threading allocators, +// loggers, and other utilities through your program + +example_with_context :: proc() { + // Save current context + old_allocator := context.allocator + + // Use a different allocator temporarily + temp_allocator := context.temp_allocator + context.allocator = temp_allocator + + // All allocations in this scope use temp_allocator + temp_data := make([]int, 100) + // No need to delete temp_data - it's automatically cleaned up + + // Restore original allocator + context.allocator = old_allocator +} + +// defer ensures cleanup happens when scope exits +resource_management_example :: proc() { + file_handle := os.open("example.txt", os.O_RDONLY, 0) or_return + defer os.close(file_handle) // Always closed when function exits + + buffer := make([]u8, 1024) + defer delete(buffer) // Always freed when function exits + + // Use file_handle and buffer... + // They're automatically cleaned up even if we return early +} +``` + +## Further Reading + +The [Odin Programming Language website](https://odin-lang.org/) provides +excellent documentation and examples. + +The [overview](https://odin-lang.org/docs/overview/) covers much of the +language in detail. + +The [GitHub examples repository](https://github.com/odin-lang/examples) +contains idiomatic Odin code examples. + +For learning resources: +- ["Understanding the Odin Programming Language" by Karl Zylinski](https://odinbook.com) +- [Odin Discord](https://discord.gg/sVBPHEv) for community support +- [FAQ](https://odin-lang.org/docs/faq/) for common questions