1use std::fmt;
2use thiserror::Error;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub struct Position {
6 pub line: u32,
7 pub column: u32,
8 pub offset: u32,
9}
10
11impl Position {
12 pub fn new(line: u32, column: u32, offset: u32) -> Self {
13 Self {
14 line,
15 column,
16 offset,
17 }
18 }
19}
20
21impl fmt::Display for Position {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 write!(f, "{}:{}", self.line, self.column)
24 }
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct Span {
29 pub start: Position,
30 pub end: Position,
31}
32
33impl Span {
34 pub fn new(start: Position, end: Position) -> Self {
35 Self { start, end }
36 }
37
38 pub fn single(pos: Position) -> Self {
39 Self {
40 start: pos,
41 end: pos,
42 }
43 }
44}
45
46impl fmt::Display for Span {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 if self.start.line == self.end.line {
49 write!(f, "{}:{}", self.start.line, self.start.column)
50 } else {
51 write!(f, "{}-{}", self.start, self.end)
52 }
53 }
54}
55
56#[derive(Error, Debug)]
57pub enum ParseError {
58 #[error("Lexical error at {span}: {message}")]
59 LexicalError { span: Span, message: String },
60
61 #[error("Syntax error at {span}: {message}")]
62 SyntaxError { span: Span, message: String },
63
64 #[error("Unexpected token at {span}: expected {expected}, found {found}")]
65 UnexpectedToken {
66 span: Span,
67 expected: String,
68 found: String,
69 },
70
71 #[error("Unexpected end of input")]
72 UnexpectedEndOfInput,
73
74 #[error("Invalid number literal at {span}: {message}")]
75 InvalidNumberLiteral { span: Span, message: String },
76}
77
78impl ParseError {
79 pub fn lexical_error(span: Span, message: impl Into<String>) -> Self {
80 Self::LexicalError {
81 span,
82 message: message.into(),
83 }
84 }
85
86 pub fn syntax_error(span: Span, message: impl Into<String>) -> Self {
87 Self::SyntaxError {
88 span,
89 message: message.into(),
90 }
91 }
92
93 pub fn unexpected_token(
94 span: Span,
95 expected: impl Into<String>,
96 found: impl Into<String>,
97 ) -> Self {
98 Self::UnexpectedToken {
99 span,
100 expected: expected.into(),
101 found: found.into(),
102 }
103 }
104
105 pub fn unexpected_end_of_input() -> Self {
106 Self::UnexpectedEndOfInput
107 }
108
109 pub fn invalid_number_literal(span: Span, message: impl Into<String>) -> Self {
110 Self::InvalidNumberLiteral {
111 span,
112 message: message.into(),
113 }
114 }
115}
116
117#[derive(Error, Debug)]
118pub enum SemanticError {
119 #[error("Empty program")]
120 EmptyProgram,
121 #[error("Dice count cannot be zero")]
122 DiceCountZero,
123 #[error("Dice faces cannot be zero")]
124 DiceFacesZero,
125}
126
127#[derive(Error, Debug)]
128pub enum RuntimeError {
129 #[error("Invalid stack state")]
130 InvalidStackState,
131 #[error("Stack underflow")]
132 StackUnderflow,
133 #[error("Stack overflow")]
134 StackOverflow,
135 #[error("Division by zero")]
136 DivisionByZero,
137 #[error("Invalid instruction pointer: {0}")]
138 InvalidInstructionPointer(usize),
139 #[error("Invalid opcode: {0}")]
140 InvalidOpcode(u8),
141 #[error(
142 "Unknown constant pool tag: {tag} at index {index}. Valid tags: 1=Utf8, 3=Integer, 4=Float, 5=Long, 6=Double, 7=Class, 8=String, 9=Fieldref, 10=Methodref, 11=InterfaceMethodref, 12=NameAndType, 15=MethodHandle, 16=MethodType, 18=InvokeDynamic"
143 )]
144 UnknownConstantPoolTag { tag: u8, index: u16 },
145 #[error("Call stack overflow")]
146 CallStackOverflow,
147 #[error("Call stack underflow")]
148 CallStackUnderflow,
149}