OR-1 dataflow CPU sketch
1"""Structured error types for OR1 assembler.
2
3Provides error categories, the AssemblyError dataclass, and utilities for
4formatting errors with source context in Rust style.
5"""
6
7from dataclasses import dataclass, field
8from enum import Enum
9
10from asm.ir import SourceLoc
11
12
13class ErrorCategory(Enum):
14 """Classification of assembly errors."""
15 PARSE = "parse"
16 NAME = "name"
17 SCOPE = "scope"
18 PLACEMENT = "placement"
19 RESOURCE = "resource"
20 ARITY = "arity"
21 PORT = "port"
22 UNREACHABLE = "unreachable"
23 VALUE = "value"
24
25
26@dataclass(frozen=True)
27class AssemblyError:
28 """Structured error with source location and context.
29
30 Attributes:
31 loc: Source location where the error occurred
32 category: Error classification (see ErrorCategory)
33 message: Human-readable error message
34 suggestions: Optional list of suggestions for fixing the error
35 context_lines: Optional source lines for context
36 """
37 loc: SourceLoc
38 category: ErrorCategory
39 message: str
40 suggestions: list[str] = field(default_factory=list)
41 context_lines: list[str] = field(default_factory=list)
42
43
44def format_error(error: AssemblyError, source: str) -> str:
45 """Format an error with source context in Rust style.
46
47 Produces output like:
48 error[SCOPE]: Duplicate label '&add' in function '$main'
49 --> line 5, column 3
50 |
51 5 | &add <| sub
52 | ^^^
53 = help: First defined at line 2
54
55 Args:
56 error: The AssemblyError to format
57 source: The original source text
58
59 Returns:
60 Formatted error string with source context
61 """
62 lines = source.split('\n')
63
64 # Build the header
65 result = f"error[{error.category.name}]: {error.message}\n"
66 result += f" --> line {error.loc.line}, column {error.loc.column}\n"
67
68 # Extract and display the source line
69 if 0 < error.loc.line <= len(lines):
70 source_line = lines[error.loc.line - 1]
71
72 # Compute gutter width based on line number
73 gutter_width = len(str(error.loc.line))
74
75 result += " " * (gutter_width + 1) + "|\n"
76 result += f"{error.loc.line:>{gutter_width}} | {source_line}\n"
77
78 # Add carets pointing to the error column(s)
79 caret_col = error.loc.column
80 caret_end_col = error.loc.end_column if error.loc.end_column else caret_col + 1
81 caret_count = max(1, caret_end_col - caret_col)
82 carets = "^" * caret_count
83
84 result += " " * (gutter_width + 1) + "| " + " " * (caret_col - 1) + carets + "\n"
85
86 # Add suggestions
87 for suggestion in error.suggestions:
88 result += f" = help: {suggestion}\n"
89
90 # Add context lines if provided
91 for line in error.context_lines:
92 result += f" |\n | {line}\n"
93
94 return result