Skip to main content

cambridge_asm/exec/
error.rs

1// Copyright (c) 2021 Saadi Save
2// This Source Code Form is subject to the terms of the Mozilla Public
3// License, v. 2.0. If a copy of the MPL was not distributed with this
4// file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
6#![allow(clippy::module_name_repetitions)]
7
8use std::{
9    fmt::{Debug, Display, Formatter, Result as FmtResult},
10    ops::Deref,
11};
12use thiserror::Error;
13
14/// Represents all possible runtime errors
15#[derive(Debug, Error)]
16pub enum RtError {
17    #[error("{0}")]
18    Other(String),
19    #[error("Unexpected I/O error, caused by: {0}")]
20    IoError(#[from] std::io::Error),
21    #[error("#x{0:X} is not a valid UTF-8 byte.")]
22    InvalidUtf8Byte(usize),
23    #[error("Operand is not a memory address, register, or literal")]
24    InvalidOperand,
25    #[error("No operand needed")]
26    NoOpInst,
27    #[error("Operand missing")]
28    NoOperand,
29    #[error("Invalid memory address `{0}`")]
30    InvalidAddr(usize),
31    #[error("Invalid indirect access address {redirect} at memory address {src}")]
32    InvalidIndirectAddr { src: usize, redirect: usize },
33    #[error("Invalid indexed access address `{}` from {src} + {offset}", .src +.offset)]
34    InvalidIndexedAddr { src: usize, offset: usize },
35    #[error("Invalid operand sequence")]
36    InvalidMultiOp,
37}
38
39impl From<&'static str> for RtError {
40    fn from(value: &'static str) -> Self {
41        Self::Other(value.to_string())
42    }
43}
44
45impl From<String> for RtError {
46    fn from(value: String) -> Self {
47        Self::Other(value)
48    }
49}
50
51pub type RtResult<T = ()> = Result<T, RtError>;
52
53/// Stores original source code during execution
54#[derive(Debug, Default, Clone)]
55#[repr(transparent)]
56pub struct Source(Vec<String>);
57
58impl Source {
59    pub fn handle_err(
60        &self,
61        write: &mut impl std::io::Write,
62        err: &RtError,
63        pos: usize,
64    ) -> std::io::Result<()> {
65        writeln!(write, "Runtime Error:")?;
66        writeln!(write)?;
67
68        if self.0.is_empty() {
69            writeln!(write, "(source empty, error at position {pos})")?;
70            return writeln!(write, "message: {err}");
71        }
72
73        for (i, s) in self.0.iter().enumerate() {
74            if pos == i {
75                if let Some(prev) = self.0.get(i - 1) {
76                    writeln!(write, "{num:>w$}    {prev}", num = i, w = self.whitespace())?;
77                }
78
79                writeln!(
80                    write,
81                    "{num:>w$}    {s} <-",
82                    num = i + 1,
83                    w = self.whitespace()
84                )?;
85
86                if let Some(next) = self.0.get(i + 1) {
87                    writeln!(
88                        write,
89                        "{num:>w$}    {next}",
90                        num = i + 2,
91                        w = self.whitespace()
92                    )?;
93                }
94
95                writeln!(write)?;
96                writeln!(write, "message: {err}")?;
97                break;
98            }
99        }
100        writeln!(write)
101    }
102
103    fn whitespace(&self) -> usize {
104        self.0.len().to_string().len()
105    }
106}
107
108impl<T: Deref<Target = str>> From<T> for Source {
109    fn from(s: T) -> Self {
110        Source(
111            s.to_string()
112                .lines()
113                .filter(|&el| !el.starts_with("//"))
114                .map(String::from)
115                .collect(),
116        )
117    }
118}
119
120impl Display for Source {
121    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
122        for inst in &self.0 {
123            writeln!(f, "    {inst}")?;
124        }
125
126        Ok(())
127    }
128}