import "@std/mem" import "@std/arraylist" Span :: struct { start u32 end u32 } DiagnosticId :: distinct u32 no_diagnostic :: maxval!(DiagnosticId) Code :: enum { invalid_character, float_must_end_with_digit, unterminated_string, unexpected_token, duplicate_name, unknown_name, expected_type, type_not_value, invalid_void_type, type_mismatch, invalid_operand, integer_division, not_callable, argument_count_mismatch, cannot_infer_type, missing_return, return_type_mismatch, condition_not_bool, invalid_assignment_target, missing_entry, invalid_entry, literal_out_of_range, invalid_escape, read_before_initialization, write_before_initialization, initialization_cycle, integer_overflow, } Diagnostic :: struct { code Code span Span } #! owns diagnostic storage and retains its allocator; release with deinit. Store :: struct { list arraylist.ArrayList(Diagnostic) } CapacityError :: enum { diagnostic_limit_reached } #! retains `allocator` for added diagnostics; the caller owns the returned Store. init proc(allocator mem.Allocator) Store { return Store{ list = arraylist.init(allocator) } } #! releases diagnostic storage with the retained allocator. deinit proc(store @mut Store) void { arraylist.deinit(&store.list) } #! allocates from the retained allocator; the returned ID is valid until deinit. add proc(store @mut Store, code Code, span Span) DiagnosticId ! (mem.AllocError | CapacityError) { if (store.list.items.len >= usize(no_diagnostic)) return .diagnostic_limit_reached id :: DiagnosticId(store.list.items.len) try arraylist.append(&store.list, Diagnostic{ code = code, span = span }) return id } get proc(store @Store, id DiagnosticId) ?Diagnostic { if (usize(id) >= store.list.items.len) return null return store.list.items[usize(id)] } count proc(store @Store) usize { return store.list.items.len } message proc(code Code) []u8 { return match code { .invalid_character: "invalid character" .float_must_end_with_digit: "float literal must end with a digit" .unterminated_string: "unterminated string literal" .unexpected_token: "unexpected token" .duplicate_name: "duplicate name or forbidden shadowing" .unknown_name: "unknown name" .expected_type: "annotation must name a type" .type_not_value: "type name cannot be used as a value" .invalid_void_type: "void cannot be stored or used as a parameter type" .type_mismatch: "incompatible value type" .invalid_operand: "operator requires compatible operand types" .integer_division: "division requires float operands" .not_callable: "value is not callable" .argument_count_mismatch: "argument count does not match the function signature" .cannot_infer_type: "cannot infer a type without a type anchor" .missing_return: "nonvoid function can finish without returning a value" .return_type_mismatch: "return does not match the function result type" .condition_not_bool: "condition must have type bool" .invalid_assignment_target: "assignment requires a mutable local binding" .missing_entry: "program requires a main function" .invalid_entry: "main must be a function with no parameters" .literal_out_of_range: "numeric literal is outside its runtime type's range" .invalid_escape: "invalid string escape" .read_before_initialization: "local value was read before initialization" .write_before_initialization: "local value was assigned before initialization" .initialization_cycle: "global initialization cycle" .integer_overflow: "integer arithmetic overflow" } }