212 lines
6.7 KiB
Rust
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()
|
|
}
|
|
}
|