msp430-repl/src/parser.rs

212 lines
6.7 KiB
Rust

// © 2023 John Breaux
//! Parses [Tokens](crate::Token) into an [abstract syntax tree](Root)
use crate::{Error, Hash, TokenStream, Type};
use std::fmt::{Debug, Display, LowerHex};
pub mod preamble {
//! All the different AST node types
use super::*;
// Traits
pub use parsable::Parsable;
pub use comment::Comment;
pub use directive::Directive;
pub use identifier::Identifier;
pub use instruction::{
encoding::{
encoding_parser::EncodingParser, jump_target::JumpTarget, number::Number, primary_operand::PrimaryOperand,
register::Register, secondary_operand::SecondaryOperand, width::Width, Encoding,
},
opcode::Opcode,
Instruction,
};
pub use label::Label;
pub use line::Line;
pub use root::Root;
}
use preamble::*;
pub(crate) mod parsable;
pub(crate) mod comment;
pub(crate) mod directive;
pub(crate) mod identifier;
pub(crate) mod instruction;
pub(crate) mod label;
pub(crate) mod line {
// © 2023 John Breaux
use super::*;
/// A line is one of:
/// - [`Label`] (definition)
/// - [`Instruction`]
/// - [`Directive`]
/// - [`Comment`]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Line {
Empty,
Insn(Instruction),
Comment(Comment),
Directive(Directive),
Label(Label), // TODO: Label resolution
EndOfFile, // Expected end of file
}
impl Parsable for Line {
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error>
where T: TokenStream<'text> {
if let Ok(token) = stream.peek_expect_any_of([Type::Insn, Type::Comment, Type::Directive, Type::Identifier])
{
return Ok(match token.variant() {
Type::Insn => Self::Insn(Instruction::parse(p, stream)?),
Type::Comment => Self::Comment(Comment::parse(p, stream)?),
Type::Directive => Self::Directive(Directive::parse(p, stream)?),
Type::Identifier => Self::Label(Label::parse(p, stream)?),
_ => unreachable!(),
});
}
let token = stream.expect_any_of([Type::EndOfFile])?;
Ok(match token.variant() {
Type::EndOfFile => Self::EndOfFile,
_ => unreachable!(),
})
}
}
impl Display for Line {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Empty => writeln!(f, "\n"),
Self::Label(arg0) => Display::fmt(arg0, f),
Self::Insn(arg0) => Display::fmt(arg0, f),
Self::Directive(arg0) => Display::fmt(arg0, f),
Self::Comment(arg0) => Display::fmt(arg0, f),
Self::EndOfFile => write!(f, "; End of file."),
}
}
}
impl LowerHex for Line {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Line::Insn(arg0) => LowerHex::fmt(arg0, f),
_ => Ok(()),
}
}
}
}
pub(crate) mod root {
// © 2023 John Breaux
use super::*;
/// Contains the entire AST
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Root(pub Vec<Line>);
// TODO: Get data out of ParseTree
// TODO: Maybe implement some sort of follower
impl Parsable for Root {
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error>
where T: TokenStream<'text> {
let mut lines = vec![];
loop {
match Line::parse(p, stream) {
Ok(Line::EndOfFile) => break,
Ok(line) => lines.push(line),
Err(e) => {
let ret = Self(lines);
eprintln!("{ret}");
eprintln!("Error:{e}\n");
eprint!("Remaining:");
stream.for_each(|t| eprint!("{t}"));
eprintln!();
return Err(Error::ParseError(ret, Box::new(e)));
}
}
}
Ok(Root(lines))
}
}
impl Display for Root {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for line in self.0.iter() {
f.pad(&format!("{line} "))?;
}
Ok(())
}
}
impl LowerHex for Root {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for line in self.0.iter() {
LowerHex::fmt(line, f)?;
}
Ok(())
}
}
impl Debug for Root {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for line in self.0.iter() {
Display::fmt(line, f)?;
Debug::fmt(line, f)?;
}
Ok(())
}
}
}
/// The type for [Parser] callbacks
pub type EmitComment = Box<dyn FnMut(&str)>;
pub type DefineLabel = Box<dyn FnMut(&Identifier) -> Result<(), Error>>;
pub struct Parser {
radix: u32,
// TODO: callbacks for emitted token sequences?!
on_label: Option<DefineLabel>,
on_comment: Option<EmitComment>,
}
impl Parser {
pub fn parse_with<'t, T>(self, stream: &'t mut T) -> Result<Root, Error>
where T: TokenStream<'t> {
Root::parse(&self, &mut stream.ignore_spaces())
}
pub fn parse<T>(self, input: &T) -> Result<Root, Error>
where T: AsRef<str> + ?Sized {
Root::parse(&self, &mut super::Tokenizer::new(input).ignore_spaces())
}
pub fn parse_one<T>(self, input: &T) -> Result<Line, Error>
where T: AsRef<str> + ?Sized {
Line::parse(&self, &mut super::Tokenizer::new(input).ignore_spaces())
}
/// Sets the default radix for [Token](crate::tokenizer::token::Token) -> [Number]
/// conversion
pub fn radix(mut self, radix: u32) { self.radix = radix; }
/// Inform the caller of a new identifier definition
pub fn define_label(&mut self, l: &Identifier) -> Result<(), Error> {
match self.on_label.as_mut() {
Some(f) => f(l),
_ => Ok(()),
}
}
/// Inform the caller of an identifier being used
pub fn emit_comment(&mut self, d: &str) {
if let Some(f) = self.on_comment.as_mut() {
f(d)
}
}
}
impl Default for Parser {
fn default() -> Self { Self { radix: 16, on_label: None, on_comment: None } }
}
impl Debug for Parser {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Parser").field("radix", &self.radix).finish_non_exhaustive()
}
}