Improve error messages and doc comments

This commit is contained in:
John 2023-08-23 02:15:10 -05:00
parent 7286d93e63
commit 8ba781bf69
5 changed files with 55 additions and 44 deletions

View File

@ -16,6 +16,8 @@ pub enum Error {
/// Produced by [Parser](crate::parser::Parser::parse<T>()) /// Produced by [Parser](crate::parser::Parser::parse<T>())
ParseError(parser::root::Root, Box<dyn std::error::Error + 'static>), ParseError(parser::root::Root, Box<dyn std::error::Error + 'static>),
Contextual(Context, Box<Self>), Contextual(Context, Box<Self>),
/// Produced by [Token] when the input is entirely unexpected.
UnexpectedSymbol(String),
/// Produced by [`TokenStream::expect`] when the next [Token] isn't the expected [Type] /// Produced by [`TokenStream::expect`] when the next [Token] isn't the expected [Type]
UnexpectedToken { UnexpectedToken {
expected: Type, expected: Type,
@ -45,7 +47,7 @@ pub enum Error {
RegisterTooHigh(u16), RegisterTooHigh(u16),
/// Produced by /// Produced by
/// [SecondaryOperand](parser::instruction::encoding::secondary_operand) /// [SecondaryOperand](parser::instruction::encoding::secondary_operand)
/// when the joke "secondary immediate" form is specified /// when the joke "secondary immediate" form is out of range 0..=1
FatSecondaryImmediate(isize), FatSecondaryImmediate(isize),
/// Produced by [Number](parser::instruction::encoding::number) when the number is too /// Produced by [Number](parser::instruction::encoding::number) when the number is too
/// wide to fit in 16 bits (outside the range `(-2^15) .. (2^16-1)` ) /// wide to fit in 16 bits (outside the range `(-2^15) .. (2^16-1)` )
@ -93,9 +95,10 @@ impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Error::Contextual(ctx, error) => write!(f, "{ctx}: {error}"), Error::Contextual(ctx, error) => write!(f, "{ctx}: {error}"),
Error::ParseError(_, error) => write!(f, "Error encountered while parsing:\n{error}"), Error::ParseError(_, error) => write!(f, "{error}"),
Error::UnexpectedSymbol(sym) => write!(f, "Unexpected item in bagging area: \"{sym}\""),
Error::UnexpectedToken { expected, got } => write!(f, "Expected {expected}, got {got}."), Error::UnexpectedToken { expected, got } => write!(f, "Expected {expected}, got {got}."),
Error::AllExpectationsFailed { expected, got } => write!(f, "Expected one of {expected}, got {got}."), Error::AllExpectationsFailed { expected, got } => write!(f, "Expected {expected}, got {got}."),
Error::UnexpectedDigits(number, radix) => write!(f, "Number `{number}` is not base {radix}."), Error::UnexpectedDigits(number, radix) => write!(f, "Number `{number}` is not base {radix}."),
Error::UnrecognizedOpcode(op) => write!(f, "{op} is not an opcode"), Error::UnrecognizedOpcode(op) => write!(f, "{op} is not an opcode"),
Error::NotARegister(reg) => write!(f, "{reg} is not a register"), Error::NotARegister(reg) => write!(f, "{reg} is not a register"),

View File

@ -1,5 +1,5 @@
// © 2023 John Breaux // © 2023 John Breaux
//! An assembler for the TI MSP430 //! A bare-bones toy assembler for the TI MSP430, for use in MicroCorruption
pub mod preamble { pub mod preamble {
use super::*; use super::*;
pub use error::Error; pub use error::Error;

View File

@ -19,7 +19,7 @@ pub trait Parsable {
match Self::parse(p, stream).map_err(|e| e.bare()) { match Self::parse(p, stream).map_err(|e| e.bare()) {
Ok(tt) => Ok(Some(tt)), Ok(tt) => Ok(Some(tt)),
Err(Error::UnexpectedToken { .. }) | Err(Error::AllExpectationsFailed { .. }) => Ok(None), Err(Error::UnexpectedToken { .. }) | Err(Error::AllExpectationsFailed { .. }) => Ok(None),
Err(e) => Err(e), Err(e) => Err(e.context(stream.context())),
} }
} }

View File

@ -6,14 +6,14 @@
// ✔ 1. Instructions // ✔ 1. Instructions
// ✔ 1. Instruction mnemonics /ad.../ // ✔ 1. Instruction mnemonics /ad.../
// ✔ 2. Byte/Word Mode Marker /(.\[bw\])?/ // ✔ 2. Byte/Word Mode Marker /(.\[bw\])?/
// ✔ 2. Src operands // ✔ 2. Operands
// ✔ 1. Registers /(r1[0-5]|r[0-9])/ // ✔ 1. Registers /(r1[0-5]|r[0-9])/
// ✔ 2. Immediate Values /#/ // ✔ 2. Immediate Values /#/
// ✔ 3. Absolute addresses /&/ // ✔ 3. Absolute addresses /&/
// ✔ 4. Numbers /[0-9A-Fa-f]+ // ✔ 4. Numbers /[0-9A-Fa-f]+
// ✔ 5. Jump Offsets: basically numbers /$?([+-]?[0-9A-Fa-f]{1,4})/ // ✔ 5. Jump Offsets: basically numbers /$?([+-]?[0-9A-Fa-f]{1,4})/
// ✔ 4. Label definitions /(^.*):/ // ✔ 3. Label definitions /(^.*):/
// ✔ 5. Comments (may be useful for debugging) // ✔ 4. Comments (may be useful for debugging)
pub mod context; pub mod context;
pub mod token; pub mod token;
@ -22,7 +22,7 @@ use crate::Error;
use context::Context; use context::Context;
use token::{Token, Type}; use token::{Token, Type};
/// Backtracking through bifurcated timelines /// A TokenStream is a specialized [Iterator] which produces [Tokens](Token)
pub trait TokenStream<'text>: Iterator<Item = Token<'text>> { pub trait TokenStream<'text>: Iterator<Item = Token<'text>> {
/// Gets this stream's [Context] /// Gets this stream's [Context]
fn context(&self) -> Context; fn context(&self) -> Context;
@ -50,7 +50,7 @@ pub trait TokenStream<'text>: Iterator<Item = Token<'text>> {
/// Ignores a [Token] of the expected [Type], discarding errors. /// Ignores a [Token] of the expected [Type], discarding errors.
fn allow(&mut self, expected: Type) { let _ = self.expect(expected); } fn allow(&mut self, expected: Type) { let _ = self.expect(expected); }
/// Runs a functor on each /// Runs a function on each
fn any_of<T, U>(&mut self, f: fn(&mut Self, Type) -> Result<U, Error>, expected: T) -> Result<U, Error> fn any_of<T, U>(&mut self, f: fn(&mut Self, Type) -> Result<U, Error>, expected: T) -> Result<U, Error>
where T: AsRef<[Type]> { where T: AsRef<[Type]> {
for &expected in expected.as_ref() { for &expected in expected.as_ref() {

View File

@ -1,8 +1,7 @@
// © 2023 John Breaux // © 2023 John Breaux
//! Defines the [Token] //! Defines the [Token]
//! //!
//! A [Token] represents all valid sequences of characters, //! A [Token] is a [semantically tagged](Type) sequence of characters
//! sorted by meaning
use crate::Error; use crate::Error;
use regex::Regex; use regex::Regex;
@ -57,6 +56,7 @@ impl<$t> From<&$t str> for $type {
}; };
} }
/// A [Token] is a [semantically tagged](Type) sequence of characters
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Token<'text> { pub struct Token<'text> {
/// The type of this token /// The type of this token
@ -96,12 +96,13 @@ impl<'text> Debug for Token<'text> {
impl<'text> Display for Token<'text> { impl<'text> Display for Token<'text> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.variant { match self.variant {
Type::Endl | Type::EndOfFile => write!(f, "{}", self.variant), Type::Endl | Type::EndOfFile | Type::Invalid => Display::fmt(&self.variant, f),
v => write!(f, "\"{}\" ({v})", self.lexeme), v => write!(f, "{v} \"{}\"", self.lexeme),
} }
} }
} }
/// A [token Type](Type) is a semantic tag for a sequence of characters
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Type { pub enum Type {
/// contiguous whitespace, excluding newline /// contiguous whitespace, excluding newline
@ -152,6 +153,8 @@ pub enum Type {
Separator, Separator,
/// End of File marker /// End of File marker
EndOfFile, EndOfFile,
/// Invalid token
Invalid,
} }
regex_impl! {<'text> Token<'text> { regex_impl! {<'text> Token<'text> {
@ -201,10 +204,10 @@ regex_impl! {<'text> Token<'text> {
pub fn expect_plus(text: &str) -> Option<Self> { pub fn expect_plus(text: &str) -> Option<Self> {
regex!(Type::Plus = r"^\+") regex!(Type::Plus = r"^\+")
} }
pub fn expect_open_idx(text: &str) -> Option<Self> { pub fn expect_l_paren(text: &str) -> Option<Self> {
regex!(Type::LParen = r"^\(") regex!(Type::LParen = r"^\(")
} }
pub fn expect_close_idx(text: &str) -> Option<Self> { pub fn expect_r_paren(text: &str) -> Option<Self> {
regex!(Type::RParen = r"^\)") regex!(Type::RParen = r"^\)")
} }
pub fn expect_indrect(text: &str) -> Option<Self> { pub fn expect_indrect(text: &str) -> Option<Self> {
@ -228,40 +231,44 @@ regex_impl! {<'text> Token<'text> {
pub fn expect_end_of_file(text: &str) -> Option<Self> { pub fn expect_end_of_file(text: &str) -> Option<Self> {
regex!(Type::EndOfFile = r"^$") regex!(Type::EndOfFile = r"^$")
} }
pub fn expect_anything(text: &str) -> Option<Self> {
regex!(Type::Invalid = r"^.*")
}
}} }}
impl Display for Type { impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::Space => write!(f, "space"), Self::Space => Display::fmt("space", f),
Self::Endl => write!(f, "newline"), Self::Endl => Display::fmt("newline", f),
Self::Comment => write!(f, "comment"), Self::Comment => Display::fmt("comment", f),
Self::Label => write!(f, "label definition"), Self::Label => Display::fmt("label definition", f),
Self::Insn => write!(f, "instruction mnemonic"), Self::Insn => Display::fmt("opcode", f),
Self::ByteWidth => write!(f, "byte-width marker"), Self::ByteWidth => Display::fmt("byte-width", f),
Self::WordWidth => write!(f, "word-width marker"), Self::WordWidth => Display::fmt("word-width", f),
Self::Register => write!(f, "register mnemonic"), Self::Register => Display::fmt("register", f),
Self::RadixMarkerDec => write!(f, "decimal radix marker"), Self::RadixMarkerDec => Display::fmt("decimal marker", f),
Self::RadixMarkerHex => write!(f, "hexadecimal radix marker"), Self::RadixMarkerHex => Display::fmt("hexadecimal marker", f),
Self::RadixMarkerOct => write!(f, "octal radix marker"), Self::RadixMarkerOct => Display::fmt("octal marker", f),
Self::RadixMarkerBin => write!(f, "binary radix marker"), Self::RadixMarkerBin => Display::fmt("binary marker", f),
Self::Number => write!(f, "number"), Self::Number => Display::fmt("number", f),
Self::Minus => write!(f, "minus sign"), Self::Minus => Display::fmt("minus sign", f),
Self::Plus => write!(f, "plus sign"), Self::Plus => Display::fmt("plus sign", f),
Self::LParen => write!(f, "left parenthesis"), Self::LParen => Display::fmt("left parenthesis", f),
Self::RParen => write!(f, "right parenthesis"), Self::RParen => Display::fmt("right parenthesis", f),
Self::Indirect => write!(f, "indirect mode marker"), Self::Indirect => Display::fmt("indirect", f),
Self::Absolute => write!(f, "absolute mode marker"), Self::Absolute => Display::fmt("absolute", f),
Self::Immediate => write!(f, "immediate mode marker"), Self::Immediate => Display::fmt("immediate", f),
Self::Identifier => write!(f, "identifier"), Self::Identifier => Display::fmt("identifier", f),
Self::Directive => write!(f, "directive"), Self::Directive => Display::fmt("directive", f),
Self::Separator => write!(f, "comma"), Self::Separator => Display::fmt("comma", f),
Self::EndOfFile => write!(f, "EOF"), Self::EndOfFile => Display::fmt("EOF", f),
Self::Invalid => Display::fmt("invalid token", f),
} }
} }
} }
/// Owned version of a token, which can outlive its parent buffer /// A [Token] which can outlive its parent buffer
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OwnedToken { pub struct OwnedToken {
/// The type of this token /// The type of this token
@ -285,6 +292,7 @@ impl From<Token<'_>> for OwnedToken {
} }
} }
/// [Types] are an owned array of [types](Type), with a custom [Display] implementation
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Types(Vec<Type>); pub struct Types(Vec<Type>);
@ -296,10 +304,10 @@ impl<T: AsRef<[Type]>> From<T> for Types {
impl Display for Types { impl Display for Types {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (idx, t) in self.0.iter().enumerate() { for (idx, t) in self.0.iter().enumerate() {
write!(f, "{t}")?; Display::fmt(t, f)?;
match idx { match idx {
i if i < self.0.len() - 2 => write!(f, ", ")?, i if i < self.0.len() - 2 => Display::fmt(", ", f)?,
i if i < self.0.len() - 1 => write!(f, " or ")?, i if i < self.0.len() - 1 => Display::fmt(" or ", f)?,
_ => (), _ => (),
} }
} }