1use std::ffi::OsStr;
3use std::fmt::{self, Display, Formatter};
4use std::io::Error as IoError;
5use std::path::PathBuf;
6
7use base::prelude::Address;
8
9use super::collections::OneOrMore;
10use super::memorymap::RcWordSource;
11use super::source::Source;
12use super::source::{LineAndColumn, WithLocation};
13use super::span::{Span, Spanned};
14use super::symbol::SymbolName;
15
16#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
28pub struct BlockIdentifier(usize);
29
30impl From<usize> for BlockIdentifier {
31 fn from(value: usize) -> Self {
32 BlockIdentifier(value)
33 }
34}
35
36impl From<BlockIdentifier> for usize {
37 fn from(value: BlockIdentifier) -> usize {
38 value.0
39 }
40}
41
42impl BlockIdentifier {
43 pub fn previous_block(self) -> Option<BlockIdentifier> {
44 self.0.checked_sub(1).map(BlockIdentifier)
45 }
46}
47
48impl Display for BlockIdentifier {
49 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
50 write!(f, "block {}", self.0)
51 }
52}
53
54#[derive(Debug, PartialEq, Eq, Clone)]
56pub enum MachineLimitExceededFailure {
57 RanOutOfIndexRegisters(Span, SymbolName),
63 BlockTooLarge {
69 span: Span,
70 block_id: BlockIdentifier,
71 offset: usize,
72 },
73}
74
75impl Spanned for MachineLimitExceededFailure {
76 fn span(&self) -> Span {
77 match self {
78 MachineLimitExceededFailure::RanOutOfIndexRegisters(span, _)
79 | MachineLimitExceededFailure::BlockTooLarge { span, .. } => *span,
80 }
81 }
82}
83
84impl Display for MachineLimitExceededFailure {
85 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
86 match self {
87 MachineLimitExceededFailure::RanOutOfIndexRegisters(_span, name) => {
88 write!(
89 f,
90 "there are not enough index registers to assign one as the default for the symbol {name}"
91 )
92 }
93 MachineLimitExceededFailure::BlockTooLarge {
94 span: _,
95 block_id,
96 offset,
97 } => {
98 write!(
99 f,
100 "{block_id} is too large; offset {offset} does not fit in physical memory"
101 )
102 }
103 }
104 }
105}
106
107#[derive(Debug, PartialEq, Eq)]
113pub enum ProgramError {
114 InconsistentTag {
117 name: SymbolName,
118 span: Span,
119 msg: String,
120 },
121 SymbolDefinitionLoop {
126 symbol_names: OneOrMore<SymbolName>,
127 span: Span,
128 },
129
130 SyntaxError { msg: String, span: Span },
134
135 BlockTooLong(Span, MachineLimitExceededFailure),
137
138 RcBlockTooLong(RcWordSource),
141
142 FailedToAssignIndexRegister(Span, SymbolName),
144}
145impl Spanned for ProgramError {
151 fn span(&self) -> Span {
152 match self {
153 ProgramError::RcBlockTooLong(RcWordSource { span, .. })
154 | ProgramError::FailedToAssignIndexRegister(span, _)
155 | ProgramError::BlockTooLong(span, _)
156 | ProgramError::InconsistentTag { span, .. }
157 | ProgramError::SymbolDefinitionLoop { span, .. }
158 | ProgramError::SyntaxError { span, .. } => *span,
159 }
160 }
161}
162
163impl Display for ProgramError {
164 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
165 match self {
166 ProgramError::RcBlockTooLong(RcWordSource { kind, .. }) => {
167 write!(f, "RC-word block grew too long to allocate {kind}")
168 }
169 ProgramError::FailedToAssignIndexRegister(_span, symbol_name) => {
170 write!(
171 f,
172 "there are not enough index registers available to assign one for {symbol_name}"
173 )
174 }
175 ProgramError::BlockTooLong(_span, mle) => {
176 write!(f, "program block contains too many machine words: {mle}")
177 }
178 ProgramError::InconsistentTag { name, span: _, msg } => {
179 write!(f, "inconsistent definitions for tag {name}: {msg}")
180 }
181 ProgramError::SymbolDefinitionLoop {
182 symbol_names,
183 span: _,
184 } => {
185 let (head, tail) = symbol_names.as_parts();
186 write!(
187 f,
188 "symbol {head} is undefined because its definition forms a loop"
189 )?;
190 if !tail.is_empty() {
191 write!(f, ": {head}")?;
192 for name in tail {
193 write!(f, "->{name}")?;
194 }
195 }
196 Ok(())
197 }
198 ProgramError::SyntaxError { span: _, msg } => {
199 write!(f, "{msg}")
200 }
201 }
202 }
203}
204
205impl ProgramError {
206 pub(crate) fn into_assembler_failure(self, source_file_body: &Source<'_>) -> AssemblerFailure {
207 let span: Span = self.span();
208 let location: LineAndColumn = source_file_body.location_of(span.start);
209 AssemblerFailure::BadProgram(OneOrMore::new(WithLocation {
210 location,
211 inner: self,
212 }))
213 }
214}
215
216#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
218pub enum IoAction {
219 Read,
220 Write,
221}
222
223impl Display for IoAction {
224 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
225 f.write_str(match self {
226 IoAction::Read => "read",
227 IoAction::Write => "write",
228 })
229 }
230}
231
232#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
234pub enum IoTarget {
235 File(PathBuf),
236}
237
238impl Display for IoTarget {
239 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
240 match self {
241 IoTarget::File(file_name) => {
242 f.write_str("file ")?;
243 write_os_string(f, file_name.as_os_str())
244 }
245 }
246 }
247}
248
249#[derive(Debug)]
251pub struct IoFailed {
252 pub action: IoAction,
254 pub target: IoTarget,
256 pub error: IoError, }
259
260impl Display for IoFailed {
261 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
262 let IoFailed {
263 action,
264 target,
265 error,
266 } = self;
267 write!(f, "I/O error: {action} failed on {target}: {error}")
268 }
269}
270
271impl PartialEq<IoFailed> for IoFailed {
272 fn eq(&self, other: &IoFailed) -> bool {
274 self.action == other.action
275 && self.target == other.target
276 && self.error.to_string() == other.error.to_string()
277 }
278}
279
280impl Eq for IoFailed {}
281
282#[derive(Debug, PartialEq, Eq)]
286pub enum AssemblerFailure {
287 InternalError(String),
289 BadTapeBlock {
293 address: Address,
294 length: usize,
295 msg: String,
296 },
297 Io(IoFailed), BadProgram(OneOrMore<WithLocation<ProgramError>>),
301 MachineLimitExceeded(MachineLimitExceededFailure),
303}
304
305fn write_os_string(f: &mut Formatter<'_>, s: &OsStr) -> Result<(), fmt::Error> {
311 match s.to_str() {
312 Some(unicode_name) => f.write_str(unicode_name),
313 None => write!(
314 f,
315 "{} (some non-Unicode characters changed to make it printable)",
316 s.to_string_lossy(),
317 ),
318 }
319}
320
321impl Display for AssemblerFailure {
322 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
323 match self {
324 AssemblerFailure::BadProgram(errors) => {
325 for e in errors.iter() {
326 write!(f, "error in user program: {e}")?;
327 }
328 Ok(())
329 }
330 AssemblerFailure::InternalError(msg) => {
331 write!(f, "internal error: {msg}")
332 }
333 AssemblerFailure::BadTapeBlock {
334 address,
335 length,
336 msg,
337 } => {
338 write!(
339 f,
340 "bad tape block of length {length} words at address {address}: {msg}"
341 )
342 }
343 AssemblerFailure::Io(e) => write!(f, "{e}"),
344 AssemblerFailure::MachineLimitExceeded(fail) => {
345 write!(f, "machine limit exceeded: {fail}")
346 }
347 }
348 }
349}