0.2.0: Feature update and Refactor

- Each major module (lexer, parser, assembler) has its own error type
  - These error types are somewhat interconnected, but their
    dependency relationships are one-way and well defined
- The AST is no longer responsible for assembling itself
  - The Assembler (assembler::Assembler) will now visit every AST node
    and accumulate words
  - Words are assumed to be little-endian.
- There are now a set of assembler directives that affect the
  generated output:
  - .word <Number>: inserts a single word in the output
  - .words [<Number>,*]: inserts multiple words in the output
  - .byte <Number>: Alias for .word
  - .bytes [<Number>,*]: Alias for .words
  - .string "String": inserts a null-terminated UTF-8 encoded string
  - .strings ["String",*]: "" multiple strings
  - Data is always word-aligned at the moment.
- There are now assembler directives that affect the AST during
  parsing:
  - .include "path/to/file": Parses the contents of a file directly
    into the AST
    - Included files have their own defines, but *share* labels.
      This is because .defines are a tokenizer construct, and
      including a file creates a new buffer and tokenizer.
    - Circular includes are NOT checked for at the moment.
      It is very easy to exhaust the stack.
- General cleanup of several functions, comments, TODOs, etc.
- main.rs was moved to make room for upcoming improvements to the UI

TODO:
- REPL mode is only partially compatible with .define directive
- Branching to a label will branch to the data AT the label,
  not the label itself. I doubt this is correct behavior.
  - In case br <label> is meant to use the absolute address,
    I've created a .org directive (currently unimplemented)
    for specifying the load address of the program.
This commit is contained in:
John 2023-09-05 01:54:50 -05:00
parent f79d7716c5
commit 417ef03e41
36 changed files with 1160 additions and 738 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "msp430-asm" name = "msp430-asm"
version = "0.1.0" version = "0.2.0"
edition = "2021" edition = "2021"
authors = ["John Breaux"] authors = ["John Breaux"]
publish = false publish = false

197
src/assembler.rs Normal file
View File

@ -0,0 +1,197 @@
// © 2023 John Breaux
//! Traverses an AST, assembling instructions.
//!
//! [Assembler] carries *some* state
use crate::parser::preamble::*;
use error::AssemblyError;
use std::collections::HashMap;
use std::path::Path;
pub mod error;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum IdentType {
Word,
Jump,
}
/// Takes in an AST's [Root], and outputs a sequence of bytes
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Assembler {
out: Vec<u16>,
/// A map from Labels' [Identifier]s to their location in the binary
labels: HashMap<Identifier, usize>,
/// A list of all referenced [Identifier]s in the binary, and their locations
identifiers: Vec<(usize, Identifier, IdentType)>,
}
impl Assembler {
pub fn assemble(r: &Root) -> Result<Vec<u16>, AssemblyError> {
let mut out = Self::default();
out.visit_root(r)?;
Ok(out.out)
}
pub fn load(&mut self, r: &Root) -> Result<(), AssemblyError> { self.visit_root(r) }
pub fn out(self) -> Vec<u16> { self.out }
fn last_mut(&mut self) -> Result<&mut u16, AssemblyError> { self.out.last_mut().ok_or(AssemblyError::EmptyBuffer) }
fn push_default(&mut self) -> usize {
self.out.push(Default::default());
self.out.len() - 1
}
}
impl Assembler {
/// Visits the [Root] node of a parse tree
fn visit_root(&mut self, r: &Root) -> Result<(), AssemblyError> {
// Visit the entire tree
for (num, line) in r.lines() {
self.visit_line(line).map_err(|e| e.ctx(r.file().unwrap_or(Path::new("stdin")), *num))?;
}
// Link identifiers
for (idx, id, id_type) in self.identifiers.iter() {
let Some(&num) = self.labels.get(id) else { return Err(AssemblyError::UnresolvedIdentifier(id.clone())) };
let offset = (num as isize - *idx as isize) * 2;
*self.out.get_mut(*idx).expect("idx should be a valid index into out") |= match id_type {
IdentType::Word => offset as u16,
IdentType::Jump => JumpTarget::squish(offset)?,
};
}
Ok(())
}
/// visit a [Line]
fn visit_line(&mut self, line: &Line) -> Result<(), AssemblyError> {
match line {
Line::Insn(insn) => self.visit_instruction(insn),
Line::Label(label) => self.visit_label(label),
Line::Directive(d) => self.visit_directive(d),
_ => Ok(()),
}
}
/// Visits a [Directive]
fn visit_directive(&mut self, node: &Directive) -> Result<(), AssemblyError> {
match node {
Directive::Org(_) => todo!("{node}"),
Directive::Define(..) => (),
Directive::Include(r) => self.visit_root(r)?,
Directive::Byte(word) | Directive::Word(word) => self.out.push((*word).into()),
Directive::Bytes(words) | Directive::Words(words) => {
for word in words {
self.out.push((*word).into());
}
}
Directive::String(s) => self.visit_string(s)?,
Directive::Strings(strs) => {
for s in strs {
self.visit_string(s)?;
}
}
};
Ok(())
}
/// Visits a [Label]
fn visit_label(&mut self, node: &Label) -> Result<(), AssemblyError> {
// Register the label
match self.labels.insert(node.0.to_owned(), self.out.len()) {
Some(_) => Err(AssemblyError::RedefinedLabel(node.0.to_owned())),
_ => Ok(()),
}
}
/// Visits an [Instruction]
fn visit_instruction(&mut self, insn: &Instruction) -> Result<(), AssemblyError> {
self.push_default();
self.visit_opcode(insn.opcode())?;
self.visit_encoding(insn.encoding())?;
Ok(())
}
/// Visits an [Opcode]
fn visit_opcode(&mut self, node: &Opcode) -> Result<(), AssemblyError> {
*self.last_mut()? |= *node as u16;
Ok(())
}
/// Visits an [Encoding]
fn visit_encoding(&mut self, node: &Encoding) -> Result<(), AssemblyError> {
*self.last_mut()? |= node.word();
match node {
Encoding::Single { dst, .. } => {
self.visit_primary_operand(dst)?;
}
Encoding::Jump { target } => {
self.visit_jump_target(target)?;
}
Encoding::Double { src, dst, .. } => {
self.visit_primary_operand(src)?;
self.visit_secondary_operand(dst)?;
}
}
Ok(())
}
/// Visits a [JumpTarget]
fn visit_jump_target(&mut self, node: &JumpTarget) -> Result<(), AssemblyError> {
match node {
JumpTarget::Number(num) => self.visit_number(num),
JumpTarget::Identifier(id) => {
self.visit_identifier(id, self.out.len() - 1, IdentType::Jump)?;
Ok(())
}
}
}
/// Visits a [SecondaryOperand]
fn visit_secondary_operand(&mut self, node: &SecondaryOperand) -> Result<(), AssemblyError> {
use SecondaryOperand as O;
if let O::Indexed(_, num) | O::Absolute(num) = node {
self.push_default();
self.visit_number(num)?;
}
Ok(())
}
/// Visits a [PrimaryOperand]
fn visit_primary_operand(&mut self, node: &PrimaryOperand) -> Result<(), AssemblyError> {
use PrimaryOperand as O;
match node {
O::Indexed(_, num) | O::Absolute(num) | O::Immediate(num) => {
self.push_default();
self.visit_number(num)?;
}
O::Relative(id) => {
let addr = self.push_default();
self.visit_identifier(id, addr, IdentType::Word)?;
}
_ => (),
}
Ok(())
}
/// Visits a number and writes it into the last index
fn visit_number(&mut self, node: &Number) -> Result<(), AssemblyError> {
*self.last_mut()? |= u16::from(*node);
Ok(())
}
/// Visits a number and appends it to the output buffer
fn visit_string(&mut self, node: &str) -> Result<(), AssemblyError> {
for (idx, byte) in node.bytes().chain([0u8].into_iter()).enumerate() {
if idx % 2 == 0 {
self.push_default();
}
*self.last_mut()? |= (byte as u16) << (8 * (idx % 2));
}
Ok(())
}
/// Visits an [Identifier], and registers it to the identifier list
fn visit_identifier(&mut self, node: &Identifier, addr: usize, ty: IdentType) -> Result<(), AssemblyError> {
self.identifiers.push((addr, node.clone(), ty));
Ok(())
}
}

56
src/assembler/error.rs Normal file
View File

@ -0,0 +1,56 @@
// © 2023 John Breauxs
use crate::parser::{error::ParseError, preamble::*};
use std::{
fmt::Display,
path::{Path, PathBuf},
};
#[derive(Debug)]
pub enum AssemblyError {
UnresolvedIdentifier(Identifier),
RedefinedLabel(Identifier),
JumpedTooFar(Identifier, isize),
ParseError(ParseError),
// TODO: This, better'
Context(Box<AssemblyError>, PathBuf, usize),
EmptyBuffer,
}
impl AssemblyError {
pub(super) fn ctx<P: AsRef<Path> + ?Sized>(self, file: &P, line: usize) -> Self {
Self::Context(self.into(), file.as_ref().into(), line)
}
}
impl From<ParseError> for AssemblyError {
fn from(value: ParseError) -> Self { Self::ParseError(value) }
}
impl Display for AssemblyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::UnresolvedIdentifier(id) => {
write!(f, "Identifier {id} is undefined, but referenced anyway.")
}
Self::RedefinedLabel(id) => {
write!(f, "Redefined label '{id}'.")
}
Self::JumpedTooFar(id, num) => {
write!(f, "Label '{id}' is too far away. ({num} is outside range -0x400..=0x3fe)")
}
Self::ParseError(e) => Display::fmt(e, f),
Self::Context(e, file, line) => write!(f, "{}:{line}:\n\t{e}", file.display()),
Self::EmptyBuffer => Display::fmt("Tried to get last element of output buffer, but buffer was empty", f),
}
}
}
impl std::error::Error for AssemblyError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::ParseError(e) => Some(e),
Self::Context(e, ..) => Some(e),
_ => None,
}
}
}

View File

@ -0,0 +1,61 @@
//! Simple frontend for the assembler
use msp430_asm::preamble::*;
use std::error::Error;
use std::io::Read;
fn main() -> Result<(), Box<dyn Error>> {
let mut repl = true;
for arg in std::env::args() {
match arg.as_str() {
"-" | "-f" | "--file" => repl = false,
_ => (),
}
}
let mut buf = String::new();
if repl {
let mut line = String::new();
while let Ok(len) = std::io::stdin().read_line(&mut line) {
match len {
0 => break, // No newline (reached EOF)
1 => continue, // Line is empty
_ => {
// Try to parse this line in isolation (this restricts preprocessing)
match Parser::default().parse(&line) {
Ok(_) => {
buf += &line;
}
Err(error) => println!("{error}"),
}
line.clear();
}
}
}
match Assembler::assemble(&Parser::default().parse(&buf)?) {
Err(error) => println!("{error}"),
Ok(out) => {
for word in out {
print!("{:04x} ", word.swap_bytes())
}
}
}
println!();
} else {
std::io::stdin().lock().read_to_string(&mut buf)?;
let tree = Parser::default().parse(&buf);
match &tree {
Ok(tree) => {
//msp430_asm::linker::Printer::default().visit_root(tree);
for insn in msp430_asm::assembler::Assembler::assemble(tree)? {
print!("{:04x} ", insn.swap_bytes())
}
println!();
}
Err(error) => eprintln!("{error}"),
}
}
Ok(())
}

View File

@ -1,126 +1,39 @@
// © 2023 John Breauxs // © 2023 John Breauxs
//! Common error type for [msp430-asm](crate) errors //! Common error type for [msp430-asm](crate) errors
use super::*;
use std::fmt::Display; use std::fmt::Display;
use super::{
lexer::token::{OwnedToken, Types},
*,
};
// TODO: Store more error context in error. for example:
// Error {ExpectationFailed{...}, WhileParsing(Register)}
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// Produced by [Parser](crate::parser::Parser::parse<T>()) /// Produced by [lexer]
ParseError(parser::root::Root, Box<dyn std::error::Error + 'static>), LexError(lexer::error::LexError),
/// Any other error, tagged with [Context]. Created by [`Error::context()`] /// Produced by [parser]
Contextual(Context, Box<Self>), ParseError(parser::error::ParseError),
/// Produced by [Token] when the input is entirely unexpected. /// Produced by [assembler]
UnexpectedSymbol(String), AssemblyError(assembler::error::AssemblyError),
/// Produced by [`TokenStream::expect`] when the next [Token] isn't the expected [Type]
UnexpectedToken {
expected: Type,
got: OwnedToken,
},
/// Produced by [`TokenStream::expect_any_of`] when the next [Token] isn't any of the expected
/// [Types](Type)
AllExpectationsFailed {
expected: Types,
got: OwnedToken,
},
/// Produced by
/// [Number](parser::preamble::Number)[::parse()](parser::parsable::Parsable::parse())
/// when the parsed number contains digits too high for the specified radix
UnexpectedDigits(String, u32),
/// Produced by
/// [Opcode](parser::preamble::Opcode)[::parse()](parser::parsable::Parsable::parse())
/// when the opcode passed lexing but did not match recognized opcodes.
///
/// This should be interpreted as a failure in lexing.
UnrecognizedOpcode(String),
/// Produced by [Register](parser::preamble::Register)
/// when attempting to convert from a [str] that isn't a register (pc, sp, sr, cg, or r{number})
NotARegister(String),
/// Produced by [Register](parser::preamble::Register)
/// when attempting to convert from a [u16] that isn't in the range 0-15
RegisterTooHigh(u16),
/// Produced by
/// [SecondaryOperand](parser::preamble::SecondaryOperand)
/// when the joke "secondary immediate" form is out of range 0..=1
FatSecondaryImmediate(isize),
/// Produced by [Number](parser::preamble::Number) when the number is too
/// wide to fit in 16 bits (outside the range `(-2^15) .. (2^16-1)` )
NumberTooWide(isize),
/// Produced by [JumpTarget](parser::preamble::JumpTarget)
/// when the jump offset is outside the range (-0x3ff..0x3fc)
JumpedTooFar(isize),
/// Produced by [JumpTarget](parser::preamble::JumpTarget)
JumpedOdd(isize),
EndOfFile,
} }
impl Error { impl Error {}
pub fn context(self, c: Context) -> Self {
match self {
Self::Contextual(..) => self,
_ => Self::Contextual(c, Box::new(self)),
}
}
// Extracts the root of the error tree impl From<lexer::error::LexError> for Error {
pub fn bare(self) -> Self { fn from(value: lexer::error::LexError) -> Self { Self::LexError(value) }
match self { }
Self::Contextual(_, bare) => bare.bare(),
_ => self,
}
}
pub fn swap(mut self, other: Self) -> Self { impl From<parser::error::ParseError> for Error {
if let Self::Contextual(_, err) = &mut self { fn from(value: parser::error::ParseError) -> Self { Self::ParseError(value) }
_ = std::mem::replace(err.as_mut(), other) }
}
self
}
pub fn expected<E: AsRef<[Type]>, T: Into<OwnedToken>>(expected: E, got: T) -> Self { impl From<assembler::error::AssemblyError> for Error {
match expected.as_ref().len() { fn from(value: assembler::error::AssemblyError) -> Self { Self::AssemblyError(value) }
1 => Self::UnexpectedToken { expected: expected.as_ref()[0], got: got.into() },
_ => Self::AllExpectationsFailed { expected: expected.as_ref().into(), got: got.into() },
}
}
pub fn mask_expectation(mut self, expected: Type) -> Self {
match self {
Error::UnexpectedToken { got, .. } => self = Error::UnexpectedToken { expected, got },
Error::AllExpectationsFailed { got, .. } => self = Error::UnexpectedToken { expected, got },
Error::Contextual(context, err) => {
self = Error::Contextual(context, Box::new(err.mask_expectation(expected)))
}
_ => (),
}
self
}
} }
impl Display for Error { 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::LexError(e) => Display::fmt(e, f),
Error::ParseError(_, error) => write!(f, "{error}"), Error::ParseError(e) => Display::fmt(e, f),
Error::UnexpectedSymbol(sym) => write!(f, "Unexpected item in bagging area: \"{sym}\""), Error::AssemblyError(e) => Display::fmt(e, f),
Error::UnexpectedToken { expected, got } => write!(f, "Expected {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::UnrecognizedOpcode(op) => write!(f, "{op} is not an opcode"),
Error::NotARegister(reg) => write!(f, "{reg} is not a register"),
Error::RegisterTooHigh(reg) => write!(f, "r{reg} is not a register"),
Error::FatSecondaryImmediate(num) => write!(f, "Secondary immediate must be #0 or #1, not #{num}"),
Error::NumberTooWide(num) => write!(f, "{num} does not fit in 16 bits"),
Error::JumpedTooFar(num) => write!(f, "{num} is too far away: must be in range (`-1022..=1024`.)"),
Error::JumpedOdd(num) => write!(f, "Jump targets only encode even numbers: {num} must not be odd."),
Error::EndOfFile => write!(f, "Unexpected end of file"),
} }
} }
} }
@ -128,8 +41,9 @@ impl Display for Error {
impl std::error::Error for Error { impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self { match self {
Error::ParseError(_, e) => Some(e.as_ref()), Error::LexError(e) => Some(e),
_ => None, Error::ParseError(e) => Some(e),
Error::AssemblyError(e) => Some(e),
} }
} }
} }

View File

@ -1,14 +1,19 @@
// © 2023 John Breaux // © 2023 John Breaux
//! Convenience functions and traits for dealing with hashable data //! Convenience functions and traits for dealing with hashable data
pub type Hash = u64; pub type Hash = u64;
pub trait FromHash: From<Hash> {
/// Hashes anything that implements [type@Hash] using the [DefaultHasher](std::collections::hash_map::DefaultHasher) /// Calculates a hash using Rust hashmap's default hasher.
fn hash<T: std::hash::Hash>(hashable: T) -> Hash { pub fn hash<T: std::hash::Hash>(hashable: T) -> Hash {
use std::hash::Hasher; use std::hash::Hasher;
let mut hasher = std::collections::hash_map::DefaultHasher::new(); let mut hasher = std::collections::hash_map::DefaultHasher::new();
hashable.hash(&mut hasher); hashable.hash(&mut hasher);
hasher.finish() hasher.finish()
} }
pub trait FromHash: From<Hash> {
/// Hashes anything that implements [type@Hash] using the
/// [DefaultHasher](std::collections::hash_map::DefaultHasher)
fn hash<T: std::hash::Hash>(hashable: T) -> Hash { hash(hashable) }
fn from_hash<T: std::hash::Hash>(hashable: T) -> Self fn from_hash<T: std::hash::Hash>(hashable: T) -> Self
where Self: Sized { where Self: Sized {
Self::from(Self::hash(hashable)) Self::from(Self::hash(hashable))

View File

@ -1,28 +1,15 @@
// © 2023 John Breaux // © 2023 John Breaux
//! Iterates over [`&str`](str), producing [`Token`s](Token) //! Iterates over [`&str`](str), producing [`Token`s](Token)
// Things we need:
// ✔ 1. Lexer/Tokenizer
// ✔ 1. Instructions
// ✔ 1. Instruction mnemonics /ad.../
// ✔ 2. Byte/Word Mode Marker /(.\[bw\])?/
// ✔ 2. Operands
// ✔ 1. Registers /(r1[0-5]|r[0-9])/
// ✔ 2. Immediate Values /#/
// ✔ 3. Absolute addresses /&/
// ✔ 4. Numbers /[0-9A-Fa-f]+
// ✔ 5. Jump Offsets: basically numbers /$?([+-]?[0-9A-Fa-f]{1,4})/
// ✔ 3. Label definitions /(^.*):/
// ✔ 4. Comments (may be useful for debugging)
pub mod context; pub mod context;
pub mod error;
pub mod ignore; pub mod ignore;
pub mod preprocessed; pub mod preprocessed;
pub mod token; pub mod token;
pub mod token_stream; pub mod token_stream;
use crate::Error;
use context::Context; use context::Context;
use error::LexError;
use token::{Token, Type}; use token::{Token, Type};
use token_stream::TokenStream; use token_stream::TokenStream;
@ -70,13 +57,13 @@ impl<'text> TokenStream<'text> for Tokenizer<'text> {
// Tokenizer has access to the source buffer, and can implement expect and peek without cloning // Tokenizer has access to the source buffer, and can implement expect and peek without cloning
// itself. This can go wrong, of course, if an [Identifier] is expected, since all instructions and // itself. This can go wrong, of course, if an [Identifier] is expected, since all instructions and
// registers are valid identifiers. // registers are valid identifiers.
fn expect(&mut self, expected: Type) -> Result<Self::Item, Error> { fn expect(&mut self, expected: Type) -> Result<Self::Item, LexError> {
let token = Token::expect(&self.text[self.idx..], expected).map_err(|e| e.context(self.context()))?; let token = Token::expect(&self.text[self.idx..], expected).map_err(|e| e.context(self.context()))?;
self.count(&token); self.count(&token);
Ok(token) Ok(token)
} }
fn peek(&mut self) -> Self::Item { Token::from(&self.text[self.idx..]) } fn peek(&mut self) -> Self::Item { Token::from(&self.text[self.idx..]) }
fn peek_expect(&mut self, expected: Type) -> Result<Self::Item, Error> { fn peek_expect(&mut self, expected: Type) -> Result<Self::Item, LexError> {
Token::expect(&self.text[self.idx..], expected) Token::expect(&self.text[self.idx..], expected).map_err(|e| e.context(self.context()))
} }
} }

68
src/lexer/error.rs Normal file
View File

@ -0,0 +1,68 @@
// © 2023 John Breauxs
use super::{
context::Context,
token::{OwnedToken, *},
};
use std::fmt::Display;
#[derive(Debug)]
pub enum LexError {
/// Any other error, tagged with [Context]. Created by [`Error::context()`]
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]
UnexpectedToken { expected: Type, got: OwnedToken },
/// Produced by [`TokenStream::expect_any_of`] when the next [Token] isn't any of the
/// expected [Types](Type)
AllExpectationsFailed { expected: Types, got: OwnedToken },
}
impl LexError {
pub fn context(self, c: Context) -> Self {
match self {
Self::Contextual(..) => self,
_ => Self::Contextual(c, Box::new(self)),
}
}
// Extracts the root of the error tree
pub fn bare(self) -> Self {
match self {
Self::Contextual(_, bare) => bare.bare(),
_ => self,
}
}
pub fn expected<E: AsRef<[Type]>, T: Into<OwnedToken>>(expected: E, got: T) -> Self {
match expected.as_ref().len() {
1 => Self::UnexpectedToken { expected: expected.as_ref()[0], got: got.into() },
_ => Self::AllExpectationsFailed { expected: expected.as_ref().into(), got: got.into() },
}
}
pub fn mask_expectation(mut self, expected: Type) -> Self {
match self {
LexError::UnexpectedToken { got, .. } => self = LexError::UnexpectedToken { expected, got },
LexError::AllExpectationsFailed { got, .. } => self = LexError::UnexpectedToken { expected, got },
LexError::Contextual(context, err) => {
self = LexError::Contextual(context, Box::new(err.mask_expectation(expected)))
}
_ => (),
}
self
}
}
impl Display for LexError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LexError::Contextual(ctx, error) => write!(f, "{ctx}: {error}"),
LexError::UnexpectedSymbol(sym) => write!(f, "Unexpected item in bagging area: \"{sym}\""),
LexError::UnexpectedToken { expected, got } => write!(f, "Expected {expected}, got {got}."),
LexError::AllExpectationsFailed { expected, got } => write!(f, "Expected {expected}, got {got}."),
}
}
}
impl std::error::Error for LexError {}

View File

@ -38,7 +38,7 @@ impl<'t, T> TokenStream<'t> for Ignore<'t, T>
where T: TokenStream<'t> where T: TokenStream<'t>
{ {
fn context(&self) -> Context { self.inner.context() } fn context(&self) -> Context { self.inner.context() }
fn expect(&mut self, expected: Type) -> Result<Self::Item, Error> { fn expect(&mut self, expected: Type) -> Result<Self::Item, LexError> {
self.inner.allow(self.ignore); self.inner.allow(self.ignore);
self.inner.expect(expected) self.inner.expect(expected)
} }
@ -48,7 +48,7 @@ where T: TokenStream<'t>
self.inner.peek() self.inner.peek()
} }
fn peek_expect(&mut self, expected: Type) -> Result<Self::Item, Error> { fn peek_expect(&mut self, expected: Type) -> Result<Self::Item, LexError> {
self.inner.allow(self.ignore); self.inner.allow(self.ignore);
self.inner.peek_expect(expected) self.inner.peek_expect(expected)
} }

View File

@ -1,6 +1,6 @@
// © 2023 John Breaux // © 2023 John Breaux
//! Preprocesses a [`TokenStream`], substituting tokens for earlier tokens based on in-band ".define" //! Preprocesses a [`TokenStream`], substituting tokens for earlier tokens based on in-band
//! rules //! ".define" rules
use super::*; use super::*;
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
@ -47,21 +47,27 @@ impl<'t, T: TokenStream<'t>> Preprocessed<'t, T> {
/// Gets a mutable reference to the inner [TokenStream] /// Gets a mutable reference to the inner [TokenStream]
pub fn inner_mut(&mut self) -> &mut T { self.inner } pub fn inner_mut(&mut self) -> &mut T { self.inner }
fn define(&mut self, token: Token<'t>) -> Result<(), Error> { /// Preserve the next token in the queue
fn enqueue(&mut self, token: Token<'t>) -> Token<'t> {
self.queue.push_back(token);
token
}
/// Process .define directives in the preprocessor
fn define(&mut self, token: Token<'t>) -> Result<(), LexError> {
if !(token.is_variant(Type::Directive) && token.lexeme().starts_with(".define")) { if !(token.is_variant(Type::Directive) && token.lexeme().starts_with(".define")) {
return Ok(()); return Ok(());
} }
// Tokenize the subdocument // Tokenize the subdocument
self.allow(Type::Directive); self.allow(Type::Directive);
self.allow(Type::Space);
self.require(Type::Space).map_err(|e| e.context(self.context()))?;
let Some(k) = self.inner.next() else { return Ok(()) }; let Some(k) = self.inner.next() else { return Ok(()) };
if !self.sub_types.contains(&k.variant()) { if !self.sub_types.contains(&k.variant()) {
self.sub_types.push(k.variant()); self.sub_types.push(k.variant());
}; };
self.require(Type::Space).map_err(|e| e.context(self.context()))?; self.allow(Type::Space);
let mut replacement = vec![]; let mut replacement = vec![];
loop { loop {
@ -71,7 +77,10 @@ impl<'t, T: TokenStream<'t>> Preprocessed<'t, T> {
// ignore comments // ignore comments
self.inner.next(); self.inner.next();
} }
_ => replacement.push(self.inner.next().unwrap()), _ => {
let next = self.inner.next().unwrap();
replacement.push(self.enqueue(next));
}
} }
} }
self.sub_table.insert(k, replacement); self.sub_table.insert(k, replacement);
@ -92,10 +101,10 @@ where T: TokenStream<'t>
{ {
fn context(&self) -> Context { self.inner.context() } fn context(&self) -> Context { self.inner.context() }
fn expect(&mut self, expected: Type) -> Result<Self::Item, Error> { fn expect(&mut self, expected: Type) -> Result<Self::Item, LexError> {
match self.queue.front() { match self.queue.front() {
Some(&token) if token.is_variant(expected) => Ok(self.queue.pop_front().unwrap_or_default()), Some(&token) if token.is_variant(expected) => Ok(self.queue.pop_front().unwrap_or_default()),
Some(&token) => Err(Error::expected([expected], token).context(self.context())), Some(&token) => Err(LexError::expected([expected], token).context(self.context())),
None => { None => {
// Only resolve defines when expecting, otherwise you'll run into issues. // Only resolve defines when expecting, otherwise you'll run into issues.
if let Ok(next) = self.inner.expect(expected) { if let Ok(next) = self.inner.expect(expected) {
@ -109,10 +118,9 @@ where T: TokenStream<'t>
} }
return if self.queue.is_empty() { self.inner.expect(expected) } else { self.expect(expected) }; return if self.queue.is_empty() { self.inner.expect(expected) } else { self.expect(expected) };
} }
Err(Error::expected([expected], self.inner.peek())) Err(LexError::expected([expected], self.inner.peek()).context(self.context()))
} }
} }
// TODO: preprocessor step
} }
fn peek(&mut self) -> Self::Item { fn peek(&mut self) -> Self::Item {
@ -130,10 +138,10 @@ where T: TokenStream<'t>
} }
} }
fn peek_expect(&mut self, expected: Type) -> Result<Self::Item, Error> { fn peek_expect(&mut self, expected: Type) -> Result<Self::Item, LexError> {
match self.queue.front() { match self.queue.front() {
Some(&token) if token.is_variant(expected) => Ok(token), Some(&token) if token.is_variant(expected) => Ok(token),
Some(&token) => Err(Error::expected([expected], token).context(self.context())), Some(&token) => Err(LexError::expected([expected], token).context(self.context())),
None => { None => {
if let Ok(next) = self.inner.peek_expect(expected) { if let Ok(next) = self.inner.peek_expect(expected) {
return Ok(next); return Ok(next);
@ -146,7 +154,7 @@ where T: TokenStream<'t>
self.peek_expect(expected) self.peek_expect(expected)
}; };
} }
Err(Error::expected([expected], self.inner.peek())) Err(LexError::expected([expected], self.inner.peek()))
} }
} }
} }

View File

@ -1,7 +1,9 @@
// © 2023 John Breaux // © 2023 John Breaux
//! A [Token] is a [semantically tagged](Type) sequence of characters //! A [Token] is a [semantically tagged](Type) sequence of characters.
//!
//! Token, and the tokenizer, intend to copy as little as possible.
use crate::Error; use super::error::LexError;
use regex::Regex; use regex::Regex;
use std::{ use std::{
fmt::{Debug, Display}, fmt::{Debug, Display},
@ -21,10 +23,10 @@ impl<$t> $type {
/// Lexes a token only for the expected `variant` /// Lexes a token only for the expected `variant`
/// ///
/// Warning: This bypasses precedence rules. Only use for specific patterns. /// Warning: This bypasses precedence rules. Only use for specific patterns.
pub fn expect(text: &$t str, expected: Type) -> Result<Self, Error> { pub fn expect(text: &$t str, expected: Type) -> Result<Self, LexError> {
match expected {$( match expected {$(
$out => Self::$func(text), $out => Self::$func(text),
)*}.ok_or(Error::UnexpectedToken { )*}.ok_or(LexError::UnexpectedToken {
expected, expected,
got: Self::from(text).into(), got: Self::from(text).into(),
}) })
@ -137,6 +139,10 @@ pub enum Type {
LParen, LParen,
/// Close-Indexed-Mode marker /// Close-Indexed-Mode marker
RParen, RParen,
/// Open Square Bracket
LBracket,
/// Closed Square Bracket
RBracket,
/// Indirect mode marker /// Indirect mode marker
Indirect, Indirect,
/// absolute address marker /// absolute address marker
@ -145,6 +151,8 @@ pub enum Type {
Immediate, Immediate,
/// Valid identifier. Identifiers must start with a Latin alphabetic character or underline /// Valid identifier. Identifiers must start with a Latin alphabetic character or underline
Identifier, Identifier,
/// A string, encased in "quotes"
String,
/// Assembler directive /// Assembler directive
Directive, Directive,
/// Separator (comma) /// Separator (comma)
@ -209,6 +217,12 @@ regex_impl! {<'text> Token<'text> {
pub fn expect_r_paren(text: &str) -> Option<Self> { pub fn expect_r_paren(text: &str) -> Option<Self> {
regex!(Type::RParen = r"^\)") regex!(Type::RParen = r"^\)")
} }
pub fn expect_l_bracket(text: &str) -> Option<Self> {
regex!(Type::LBracket = r"^\[")
}
pub fn expect_r_bracket(text: &str) -> Option<Self> {
regex!(Type::RBracket = r"^]")
}
pub fn expect_indrect(text: &str) -> Option<Self> { pub fn expect_indrect(text: &str) -> Option<Self> {
regex!(Type::Indirect = r"^@") regex!(Type::Indirect = r"^@")
} }
@ -218,8 +232,11 @@ regex_impl! {<'text> Token<'text> {
pub fn expect_immediate(text: &str) -> Option<Self> { pub fn expect_immediate(text: &str) -> Option<Self> {
regex!(Type::Immediate = r"^#") regex!(Type::Immediate = r"^#")
} }
pub fn expect_string(text: &str) -> Option<Self> {
regex!(Type::String = r#"^"[^"]*""#)
}
pub fn expect_directive(text: &str) -> Option<Self> { pub fn expect_directive(text: &str) -> Option<Self> {
regex!(Type::Directive = r"^\.\S+") regex!(Type::Directive = r"^\.\w+")
} }
pub fn expect_identifier(text: &str) -> Option<Self> { pub fn expect_identifier(text: &str) -> Option<Self> {
regex!(Type::Identifier = r"^[A-Za-z_]\w*") regex!(Type::Identifier = r"^[A-Za-z_]\w*")
@ -255,10 +272,13 @@ impl Display for Type {
Self::Plus => Display::fmt("plus sign", f), Self::Plus => Display::fmt("plus sign", f),
Self::LParen => Display::fmt("left parenthesis", f), Self::LParen => Display::fmt("left parenthesis", f),
Self::RParen => Display::fmt("right parenthesis", f), Self::RParen => Display::fmt("right parenthesis", f),
Self::LBracket => Display::fmt("left bracket", f),
Self::RBracket => Display::fmt("right bracket", f),
Self::Indirect => Display::fmt("indirect", f), Self::Indirect => Display::fmt("indirect", f),
Self::Absolute => Display::fmt("absolute", f), Self::Absolute => Display::fmt("absolute", f),
Self::Immediate => Display::fmt("immediate", f), Self::Immediate => Display::fmt("immediate", f),
Self::Identifier => Display::fmt("identifier", f), Self::Identifier => Display::fmt("identifier", f),
Self::String => Display::fmt("string", f),
Self::Directive => Display::fmt("directive", f), Self::Directive => Display::fmt("directive", f),
Self::Separator => Display::fmt("comma", f), Self::Separator => Display::fmt("comma", f),
Self::EndOfFile => Display::fmt("EOF", f), Self::EndOfFile => Display::fmt("EOF", f),

View File

@ -11,12 +11,14 @@ pub trait TokenStream<'text>: Iterator<Item = Token<'text>> + std::fmt::Debug {
fn context(&self) -> Context; fn context(&self) -> Context;
/// Creates an iterator that skips [Type::Space] in the input /// Creates an iterator that skips [Type::Space] in the input
#[inline]
fn ignore(&'text mut self, variant: Type) -> Ignore<'text, Self> fn ignore(&'text mut self, variant: Type) -> Ignore<'text, Self>
where Self: Sized { where Self: Sized {
Ignore::new(variant, self) Ignore::new(variant, self)
} }
/// Creates a [TokenStream] that performs live substitution of the input /// Creates a [TokenStream] that performs live substitution of the input
#[inline]
fn preprocessed(&'text mut self) -> Preprocessed<'text, Self> fn preprocessed(&'text mut self) -> Preprocessed<'text, Self>
where Self: Sized { where Self: Sized {
Preprocessed::new(self) Preprocessed::new(self)
@ -26,51 +28,57 @@ pub trait TokenStream<'text>: Iterator<Item = Token<'text>> + std::fmt::Debug {
fn peek(&mut self) -> Self::Item; fn peek(&mut self) -> Self::Item;
/// Returns the next [Token] if it is of the expected [Type], without advancing /// Returns the next [Token] if it is of the expected [Type], without advancing
fn peek_expect(&mut self, expected: Type) -> Result<Self::Item, Error>; fn peek_expect(&mut self, expected: Type) -> Result<Self::Item, LexError>;
/// Consumes and returns a [Token] if it is the expected [Type] /// Consumes and returns a [Token] if it is the expected [Type]
/// ///
/// Otherwise, does not consume a [Token] /// Otherwise, does not consume a [Token]
fn expect(&mut self, expected: Type) -> Result<Self::Item, Error>; fn expect(&mut self, expected: Type) -> Result<Self::Item, LexError>;
/// Ignores a [Token] of the expected [Type], propegating errors. /// Ignores a [Token] of the expected [Type], propegating errors.
fn require(&mut self, expected: Type) -> Result<(), Error> { self.expect(expected).map(|_| ()) } #[inline]
fn require(&mut self, expected: Type) -> Result<(), LexError> { self.expect(expected).map(|_| ()) }
/// Ignores a [Token] of the expected [Type], discarding errors. /// Ignores a [Token] of the expected [Type], discarding errors.
#[inline]
fn allow(&mut self, expected: Type) { let _ = self.expect(expected); } fn allow(&mut self, expected: Type) { let _ = self.expect(expected); }
/// Runs a function 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, LexError>, expected: T) -> Result<U, LexError>
where T: AsRef<[Type]> { where T: AsRef<[Type]> {
for &expected in expected.as_ref() { for &expected in expected.as_ref() {
match f(self, expected).map_err(|e| e.bare()) { match f(self, expected).map_err(|e| e.bare()) {
Ok(t) => return Ok(t), Ok(t) => return Ok(t),
Err(Error::UnexpectedToken { .. }) => continue, Err(LexError::UnexpectedToken { .. }) => continue,
Err(e) => return Err(e.context(self.context())), Err(e) => return Err(e.context(self.context())),
} }
} }
Err(Error::expected(expected, self.peek()).context(self.context())) Err(LexError::expected(expected, self.peek()).context(self.context()))
} }
/// Returns the next [Token] if it is of the expected [Types](Type), without advancing /// Returns the next [Token] if it is of the expected [Types](Type), without advancing
fn peek_expect_any_of<T>(&mut self, expected: T) -> Result<Self::Item, Error> #[inline]
fn peek_expect_any_of<T>(&mut self, expected: T) -> Result<Self::Item, LexError>
where T: AsRef<[Type]> { where T: AsRef<[Type]> {
self.any_of(Self::peek_expect, expected) self.any_of(Self::peek_expect, expected)
} }
/// Consumes and returns a [Token] if it matches any of the expected [Types](Type) /// Consumes and returns a [Token] if it matches any of the expected [Types](Type)
/// ///
/// Otherwise, does not consume a [Token] /// Otherwise, does not consume a [Token]
fn expect_any_of<T>(&mut self, expected: T) -> Result<Self::Item, Error> #[inline]
fn expect_any_of<T>(&mut self, expected: T) -> Result<Self::Item, LexError>
where T: AsRef<[Type]> { where T: AsRef<[Type]> {
self.any_of(Self::expect, expected) self.any_of(Self::expect, expected)
} }
/// Ignores a [Token] of any expected [Type], discarding errors. /// Ignores a [Token] of any expected [Type], discarding errors.
#[inline]
fn allow_any_of<T>(&mut self, expected: T) fn allow_any_of<T>(&mut self, expected: T)
where T: AsRef<[Type]> { where T: AsRef<[Type]> {
let _ = self.expect_any_of(expected); let _ = self.expect_any_of(expected);
} }
/// Ignores a [Token] of any expected [Type], propegating errors. /// Ignores a [Token] of any expected [Type], propegating errors.
fn require_any_of<T>(&mut self, expected: T) -> Result<(), Error> #[inline]
fn require_any_of<T>(&mut self, expected: T) -> Result<(), LexError>
where T: AsRef<[Type]> { where T: AsRef<[Type]> {
self.any_of(Self::require, expected) self.any_of(Self::require, expected)
} }

View File

@ -27,6 +27,7 @@
//! │ └─ Encoding::Single //! │ └─ Encoding::Single
//! │ ├─ Width //! │ ├─ Width
//! │ └─ PrimaryOperand //! │ └─ PrimaryOperand
//! │ ├─ Identifier // Label, for relative-addressed data/code
//! │ ├─ Register // Direct, indexed, indirect or indirect-post-increment register. //! │ ├─ Register // Direct, indexed, indirect or indirect-post-increment register.
//! │ └─ Number // Index, absolute address or immediate value. //! │ └─ Number // Index, absolute address or immediate value.
//! ├─ Line //! ├─ Line
@ -35,9 +36,11 @@
//! │ └─ Encoding::Double //! │ └─ Encoding::Double
//! │ ├─ Width //! │ ├─ Width
//! │ ├─ PrimaryOperand //! │ ├─ PrimaryOperand
//! │ ├─ Identifier // Label, for relative-addressed data/code
//! │ │ ├─ Register // Direct, indexed, indirect or indirect-post-increment register. //! │ │ ├─ Register // Direct, indexed, indirect or indirect-post-increment register.
//! │ │ └─ Number // Index, absolute address or immediate value. //! │ │ └─ Number // Index, absolute address or immediate value.
//! │ └─ SecondaryOperand //! │ └─ SecondaryOperand
//! │ ├─ Identifier // Label, for relative-addressed data/code
//! │ ├─ Register // Direct or indexed register //! │ ├─ Register // Direct or indexed register
//! │ └─ Number // Index or absolute address //! │ └─ Number // Index or absolute address
//! ├─ Line //! ├─ Line
@ -45,6 +48,7 @@
//! │ ├─ Opcode //! │ ├─ Opcode
//! │ └─ Encoding::Jump //! │ └─ Encoding::Jump
//! │ └─ JumpTarget //! │ └─ JumpTarget
//! │ ├─ Identifier // Label
//! │ └─ Number // Even, PC-relative offset in range (-1024..=1022) //! │ └─ Number // Even, PC-relative offset in range (-1024..=1022)
//! └─ Line //! └─ Line
//! └─ EndOfFile //! └─ EndOfFile
@ -53,21 +57,20 @@
pub mod preamble { pub mod preamble {
//! Common imports for msp430-asm //! Common imports for msp430-asm
use super::*; use super::*;
pub use assembler::Assembler;
pub use error::Error; pub use error::Error;
pub use hash::{FromHash, Hash};
pub use lexer::{ pub use lexer::{
context::Context, context::Context,
token::{Token, Type}, token::{Token, Type},
token_stream::TokenStream, token_stream::TokenStream,
Tokenizer, Tokenizer,
}; };
pub use linker::{Linker, Visitor};
pub use parser::Parser; pub use parser::Parser;
} }
use preamble::*; use preamble::*;
pub mod error; pub mod error;
pub mod hash;
pub mod assembler;
pub mod lexer; pub mod lexer;
pub mod linker;
pub mod parser; pub mod parser;

View File

@ -1,20 +0,0 @@
// © 2023 John Breaux
/// TODO: tree traversal and label resolution
use crate::parser::preamble::*;
pub trait Visitor<T> {
// visit_node for all nodes
fn visit_register(&mut self, r: &Register) -> T;
fn visit_number(&mut self, n: &Number) -> T;
fn visit_width(&mut self, w: &Width) -> T;
fn visit_primary_operand(&mut self, p: &PrimaryOperand) -> T;
fn visit_secondary_operand(&mut self, d: &SecondaryOperand) -> T;
fn visit_jump_target(&mut self, t: &JumpTarget) -> T;
fn visit_encoding(&mut self, e: &Encoding) -> T;
fn visit_opcode(&mut self, o: &Opcode) -> T;
fn visit_instruction(&mut self, i: &Instruction) -> T;
fn visit_directive(&mut self, d: &Directive) -> T;
// the most important one: resolve identifiers
fn visit_identifier(&mut self, i: &Identifier) -> T;
}
/// TODO: [Linker]
pub struct Linker;

View File

@ -1,42 +0,0 @@
//! Simple frontend for the assembler
use msp430_asm::preamble::*;
use std::io::Read;
fn main() -> Result<(), Error> {
let mut repl = true;
for arg in std::env::args() {
match arg.as_str() {
"-" | "-f" | "--file" => repl = false,
_ => (),
}
}
let mut buf = String::new();
if repl {
while let Ok(len) = std::io::stdin().read_line(&mut buf) {
match len {
0 => break, // No newline (reached EOF)
1 => {
// create a token steam
match Parser::default().parse(&buf) {
Ok(tree) => println!("{tree:x}"),
Err(error) => println!("{error}"),
}
buf.clear(); // Reuse buf's allocation
continue;
} // Line is empty
_ => (),
}
}
} else {
std::io::stdin().lock().read_to_string(&mut buf).map_err(|_| Error::EndOfFile)?;
let tree = Parser::default().parse(&buf);
match &tree {
Ok(tree) => println!("{tree:x}"),
Err(error) => eprintln!("{error}"),
}
}
Ok(())
}

View File

@ -1,15 +1,20 @@
// © 2023 John Breaux // © 2023 John Breaux
//! Parses [`Tokens`](crate::Token) into an [abstract syntax tree](Root) //! Parses [`Tokens`](crate::Token) into an [abstract syntax tree](Root)
use crate::{Error, Hash, TokenStream, Type}; use crate::{TokenStream, Type};
use std::fmt::{Debug, Display, LowerHex}; use error::ParseError;
use preamble::*;
use std::{
fmt::{Debug, Display},
path::Path,
};
pub mod preamble { pub mod preamble {
//! All the different AST node types //! All the different AST node types
use super::*; use super::*;
// Traits // Traits
pub use parsable::Parsable; pub use parsable::Parsable;
// Nodes
pub use comment::Comment; pub use comment::Comment;
pub use directive::Directive; pub use directive::Directive;
pub use identifier::Identifier; pub use identifier::Identifier;
@ -24,173 +29,38 @@ pub mod preamble {
pub use label::Label; pub use label::Label;
pub use line::Line; pub use line::Line;
pub use root::Root; pub use root::Root;
// Error
pub use error::ParseError;
} }
use preamble::*;
pub mod parsable; pub mod parsable;
pub mod comment; pub mod comment;
pub mod directive; pub mod directive;
pub mod error;
pub mod identifier; pub mod identifier;
pub mod instruction; pub mod instruction;
pub mod label; pub mod label;
pub mod line;
pub mod line { pub mod root;
// © 2023 John Breaux
//! [`Line`] contains a single subcomponent of the document. Multiple instructions on the same document line will be treated as if they took up multiple [`Line`s](Line).
//!
//! A line contains one of:
//! - [`Label`]
//! - [`Instruction`]
//! - [`Directive`]
//! - [`Comment`]
//! - [Nothing](Line::Empty)
use super::*;
/// A line contains any one of:
/// - [`Label`] (definition)
/// - [`Instruction`]
/// - [`Directive`]
/// - [`Comment`]
/// - Nothing at all
#[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> {
Ok(
match stream
.peek_expect_any_of([
Type::Endl,
Type::Insn,
Type::Comment,
Type::Directive,
Type::Identifier,
Type::EndOfFile,
])?
.variant()
{
Type::Endl => {
stream.next();
Self::Empty
}
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)?),
Type::EndOfFile => {
stream.next();
Self::EndOfFile
}
_ => unreachable!("stream.peek_expect_any_of should return Err for unmatched inputs"),
},
)
}
}
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 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) => {
return Err(Error::ParseError(Self(lines), 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 { pub struct Parser {
radix: u32, radix: u32,
// TODO: callbacks for emitted token sequences?!
on_label: Option<DefineLabel>,
on_comment: Option<EmitComment>,
} }
impl Parser { impl Parser {
pub fn parse_with<'t>(self, stream: &'t mut impl TokenStream<'t>) -> Result<Root, Error> { pub fn parse_with<'t>(self, stream: &'t mut impl TokenStream<'t>) -> Result<Root, ParseError> {
Root::parse(&self, &mut stream.ignore(Type::Space)) Root::parse(&self, &mut stream.ignore(Type::Space))
} }
pub fn parse<T>(self, input: &T) -> Result<Root, Error> pub fn parse<T>(self, input: &T) -> Result<Root, ParseError>
where T: AsRef<str> + ?Sized { where T: AsRef<str> + ?Sized {
Root::parse(&self, &mut super::Tokenizer::new(input).preprocessed().ignore(Type::Space)) Root::parse(&self, &mut super::Tokenizer::new(input).preprocessed().ignore(Type::Space))
} }
pub fn parse_one<T>(self, input: &T) -> Result<Line, Error> pub fn parse_file<P>(self, path: &P) -> Result<Root, ParseError>
where P: AsRef<Path> + ?Sized {
self.parse(&std::fs::read_to_string(path.as_ref())?).map(|r| r.set_file(path.as_ref().into()))
}
pub fn parse_one<T>(self, input: &T) -> Result<Line, ParseError>
where T: AsRef<str> + ?Sized { where T: AsRef<str> + ?Sized {
Line::parse(&self, &mut super::Tokenizer::new(input).preprocessed().ignore(Type::Space)) Line::parse(&self, &mut super::Tokenizer::new(input).preprocessed().ignore(Type::Space))
} }
@ -198,24 +68,10 @@ impl Parser {
/// Sets the default radix for [Token](crate::lexer::token::Token) -> [Number] /// Sets the default radix for [Token](crate::lexer::token::Token) -> [Number]
/// conversion /// conversion
pub fn radix(mut self, radix: u32) { self.radix = radix; } 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 { impl Default for Parser {
fn default() -> Self { Self { radix: 16, on_label: None, on_comment: None } } fn default() -> Self { Self { radix: 16 } }
} }
impl Debug for Parser { impl Debug for Parser {

View File

@ -5,10 +5,9 @@ use super::*;
pub struct Comment(pub String); pub struct Comment(pub String);
impl Parsable for Comment { impl Parsable for Comment {
fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, Error> fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, ParseError>
where T: TokenStream<'text> { where T: TokenStream<'text> {
let token = stream.expect(Type::Comment)?; Ok(Self(stream.expect(Type::Comment)?.lexeme().to_string()))
Ok(Self(token.lexeme().to_string()))
} }
} }
impl Display for Comment { impl Display for Comment {

View File

@ -1,32 +1,90 @@
// © 2023 John Breaux // © 2023 John Breaux
//! A [`Directive`] issues commands directly to the [`Tokenizer`](crate::Tokenizer) and //! A [`Directive`] issues commands directly to the [`Tokenizer`](crate::Tokenizer) and
//! [Linker](crate::Linker) //! [Linker](crate::Linker)
use std::path::PathBuf;
use super::*; use super::*;
use crate::hash::FromHash; use crate::lexer::token::OwnedToken;
// TODO: Parse each kind of *postprocessor* directive into an AST node
// - .org 8000: Directive::Org { base: Number }
// - .define ident tt... Directive::Define { } ; should this be in the AST? How do I put this
// in the AST?
// - .include "<filename>" Directive::Include { Root } ; should this include an entire AST in
// the AST?
// - .word 8000 Directive::Word(Number)
// - .words dead beef Directive::Words(Vec<u16>|Vec<Number>)
// - .byte ff Directive::Byte(Number)
// - .bytes de, ad, be, ef Directive::Bytes(Vec<u8>)
// - .string "string" Directive::String(String)
// - .ascii "string" Directive::Ascii(Vec<u8>)
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Directive(pub Hash, pub String); pub enum Directive {
Org(Number),
impl Directive { Define(Vec<OwnedToken>),
fn str<S: ToString>(mut self, s: S) -> Self { Include(Root), // TODO: create and parse an entire AST, and stick it in Include
self.1 = s.to_string(); Byte(Number),
self Bytes(Vec<Number>),
} Word(Number),
Words(Vec<Number>),
String(String),
Strings(Vec<String>),
} }
impl From<Hash> for Directive { impl Directive {}
fn from(value: Hash) -> Self { Self(value, String::new()) }
}
impl Parsable for Directive { impl Parsable for Directive {
fn parse<'text, T>(_p: &Parser, stream: &mut T) -> Result<Self, Error> fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, ParseError>
where T: TokenStream<'text> { where T: TokenStream<'text> {
// expect a directive
let d = stream.expect(Type::Directive)?; let d = stream.expect(Type::Directive)?;
// send the directive to the listener // match on the directive
Ok(Self::from_hash(d.lexeme()).str(d.lexeme())) Ok(match d.lexeme() {
".org" => Self::Org(Number::parse(p, stream)?),
".define" => {
let mut tokens = vec![];
loop {
match stream.peek().variant() {
Type::Endl | Type::EndOfFile => break,
_ => tokens.push(stream.next().unwrap_or_default().into()),
}
}
Self::Define(tokens)
}
".include" => {
// Try to get path
Self::Include(Parser::default().parse_file(&PathBuf::parse(p, stream)?)?)
}
".byte" => Self::Byte(Number::parse(p, stream)?),
".bytes" => Self::Bytes(Vec::<Number>::parse(p, stream)?),
".word" => Self::Word(Number::parse(p, stream)?),
".words" => Self::Words(Vec::<Number>::parse(p, stream)?),
".string" => Self::String(String::parse(p, stream)?),
".strings" => Self::Strings(Vec::<String>::parse(p, stream)?),
e => Err(ParseError::UnrecognizedDirective(e.into()))?,
})
} }
} }
impl Display for Directive { impl Display for Directive {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.1) } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Directive::Org(num) => write!(f, ".org {num}"),
Directive::Define(rep) => {
write!(f, ".define")?;
for t in rep {
write!(f, " {t}")?;
}
Ok(())
}
Directive::Include(r) => Display::fmt(r, f),
Directive::Byte(num) => write!(f, ".org {num}"),
Directive::Bytes(v) => write!(f, ".bytes {v:?}"),
Directive::Word(num) => write!(f, ".org {num}"),
Directive::Words(v) => write!(f, ".bytes {v:?}"),
Directive::String(s) => write!(f, ".string \"{s}\""),
Directive::Strings(s) => write!(f, ".string \"{s:?}\""),
}
}
} }

74
src/parser/error.rs Normal file
View File

@ -0,0 +1,74 @@
// © 2023 John Breauxs
use super::*;
use crate::lexer::error::LexError;
#[derive(Debug)]
pub enum ParseError {
/// Produced by [lexer](crate::lexer)
LexError(LexError),
/// Produced by [std::io]
IoError(std::io::Error),
/// Produced by [Number](Number)[::parse()](Parsable::parse())
/// when the parsed number contains digits too high for the specified radix
UnexpectedDigits(String, u32),
/// Produced by [Opcode](Opcode)[::parse()](Parsable::parse())
/// when the opcode passed lexing but did not match recognized opcodes.
///
/// This is always a lexer bug.
UnrecognizedOpcode(String),
/// Produced by [Directive](Directive)[::parse()](Parsable::parse())
/// when an unknown or unimplemented directive is used
UnrecognizedDirective(String),
/// Produced by [Register] when attempting to convert from a [str]
/// that isn't a register (pc, sp, sr, cg, or r{number})
NotARegister(String),
/// Produced by [Register] when the r{number} is outside the range 0-15
RegisterTooHigh(u16),
/// Produced by [SecondaryOperand] when the joke "secondary immediate" form
/// is out of range 0..=1
FatSecondaryImmediate(isize),
/// Produced by a [Number] too wide to fit in 16 bits
/// (outside the range `(-2^15) .. (2^16-1)` )
NumberTooWide(isize),
/// Produced by [JumpTarget](parser::preamble::JumpTarget)
/// when the jump offset is outside the range (-0x3ff..0x3fc)
JumpedTooFar(isize),
/// Produced by [JumpTarget](parser::preamble::JumpTarget)
JumpedOdd(isize),
}
impl From<LexError> for ParseError {
fn from(value: LexError) -> Self { Self::LexError(value) }
}
impl From<std::io::Error> for ParseError {
fn from(value: std::io::Error) -> Self { Self::IoError(value) }
}
impl Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::LexError(error) => Display::fmt(error, f),
Self::IoError(error) => Display::fmt(error, f),
Self::UnexpectedDigits(number, radix) => write!(f, "Number `{number}` is not base {radix}."),
Self::UnrecognizedOpcode(op) => write!(f, "{op} is not an opcode"),
Self::UnrecognizedDirective(d) => write!(f, "{d} is not a directive."),
Self::NotARegister(reg) => write!(f, "{reg} is not a register"),
Self::RegisterTooHigh(reg) => write!(f, "r{reg} is not a register"),
Self::FatSecondaryImmediate(num) => write!(f, "Secondary immediate must be #0 or #1, not #{num}"),
Self::NumberTooWide(num) => write!(f, "{num} does not fit in 16 bits"),
Self::JumpedTooFar(num) => write!(f, "{num} is too far away: must be in range (`-1022..=1024`.)"),
Self::JumpedOdd(num) => {
write!(f, "Jump targets only encode even numbers: {num} must not be odd.")
}
}
}
}
impl std::error::Error for ParseError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::LexError(e) => Some(e),
Self::IoError(e) => Some(e),
_ => None,
}
}
}

View File

@ -1,22 +1,18 @@
// © 2023 John Breaux // © 2023 John Breaux
//! An [Identifier] stores the name of an identifier //! An [Identifier] stores the hash of an identifier
use super::*; use super::*;
use std::rc::Rc;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Identifier { pub struct Identifier {
Hash(Hash), str: Rc<str>,
Str(String),
} }
impl Identifier { impl Identifier {
fn str<T: AsRef<str>>(s: T) -> Self { Self::Str(s.as_ref().into()) } fn str<T: AsRef<str>>(s: T) -> Self { Self { str: s.as_ref().to_owned().into() } }
}
impl From<Hash> for Identifier {
fn from(value: Hash) -> Self { Self::Hash(value) }
} }
impl Parsable for Identifier { impl Parsable for Identifier {
fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, Error> fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, ParseError>
where T: TokenStream<'text> { where T: TokenStream<'text> {
let token = stream.expect(Type::Identifier)?; let token = stream.expect(Type::Identifier)?;
match token.variant() { match token.variant() {
@ -26,10 +22,5 @@ impl Parsable for Identifier {
} }
} }
impl Display for Identifier { impl Display for Identifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(&self.str, f) }
match self {
Identifier::Hash(_) => Display::fmt("Unresolved", f),
Identifier::Str(s) => Display::fmt(s, f),
}
}
} }

View File

@ -15,7 +15,7 @@ pub mod encoding;
pub mod opcode; pub mod opcode;
/// Contains the [Opcode] and [Encoding] information for a single msp430 instruction /// Contains the [Opcode] and [Encoding] information for a single msp430 instruction
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instruction(Opcode, Encoding); pub struct Instruction(Opcode, Encoding);
impl Instruction { impl Instruction {
@ -24,11 +24,11 @@ impl Instruction {
/// Gets the Instruction as a [u16] /// Gets the Instruction as a [u16]
pub fn word(&self) -> u16 { self.0 as u16 | self.1.word() } pub fn word(&self) -> u16 { self.0 as u16 | self.1.word() }
/// Gets the [extension words] /// Gets the [extension words]
pub fn ext_words(&self) -> (Option<u16>, Option<u16>) { self.1.extwords() } pub fn ext_words(&self) -> [Option<u16>; 2] { self.1.extwords() }
} }
impl Parsable for Instruction { impl Parsable for Instruction {
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error> fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, ParseError>
where where
Self: Sized, Self: Sized,
T: crate::TokenStream<'text>, T: crate::TokenStream<'text>,
@ -50,17 +50,3 @@ impl From<Instruction> for u16 {
impl Display for Instruction { impl Display for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}{}", self.0, self.1) } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}{}", self.0, self.1) }
} }
impl LowerHex for Instruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (word, (ext_src, ext_dst)) = (self.word(), self.ext_words());
write!(f, "{:04x} ", word.swap_bytes())?;
if let Some(e) = ext_src {
write!(f, "{:04x} ", e.swap_bytes())?
}
if let Some(e) = ext_dst {
write!(f, "{:04x} ", e.swap_bytes())?
}
Ok(())
}
}

View File

@ -32,7 +32,7 @@ use encoding_parser::EncodingParser;
/// // Print the Encoding /// // Print the Encoding
/// println!("{encoding}"); /// println!("{encoding}");
/// ``` /// ```
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Encoding { pub enum Encoding {
Single { width: Width, dst: PrimaryOperand }, Single { width: Width, dst: PrimaryOperand },
Jump { target: JumpTarget }, Jump { target: JumpTarget },
@ -52,20 +52,20 @@ impl Encoding {
pub fn reflexive() -> ReflexiveBuilder { Default::default() } pub fn reflexive() -> ReflexiveBuilder { Default::default() }
/// ///
pub fn word(&self) -> u16 { pub fn word(&self) -> u16 {
match *self { match self {
Encoding::Single { width, dst } => u16::from(width) | dst.mode() | dst.register() as u16, Encoding::Single { width, dst } => u16::from(*width) | dst.mode() | dst.register() as u16,
Encoding::Jump { target } => target.word(), Encoding::Jump { target } => target.word().unwrap_or_default(),
Encoding::Double { width, src, dst } => { Encoding::Double { width, src, dst } => {
u16::from(width) | src.mode() | dst.mode() | dst.register() as u16 | ((src.register() as u16) << 8) u16::from(*width) | src.mode() | dst.mode() | dst.register() as u16 | ((src.register() as u16) << 8)
} }
} }
} }
/// Returns extwords for instruction /// Returns extwords for instruction
pub fn extwords(&self) -> (Option<u16>, Option<u16>) { pub fn extwords(&self) -> [Option<u16>; 2] {
match self { match self {
Encoding::Double { src, dst, .. } => (src.ext_word(), dst.ext_word()), Encoding::Double { src, dst, .. } => [src.ext_word(), dst.ext_word()],
Encoding::Single { dst, .. } => (dst.ext_word(), None), Encoding::Single { dst, .. } => [dst.ext_word(), None],
Encoding::Jump { .. } => (None, None), Encoding::Jump { .. } => [None, None],
} }
} }
} }

View File

@ -17,7 +17,7 @@ impl SingleBuilder {
self self
} }
/// Build /// Build
pub fn end(&self) -> EncodingParser { EncodingParser::Single { width: self.width, dst: self.dst } } pub fn end(self) -> EncodingParser { EncodingParser::Single { width: self.width, dst: self.dst } }
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -29,7 +29,7 @@ impl JumpBuilder {
self.target = Some(target); self.target = Some(target);
self self
} }
pub fn end(&self) -> EncodingParser { EncodingParser::Jump { target: self.target } } pub fn end(self) -> EncodingParser { EncodingParser::Jump { target: self.target } }
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -54,7 +54,7 @@ impl DoubleBuilder {
self.dst = Some(dst); self.dst = Some(dst);
self self
} }
pub fn end(&self) -> EncodingParser { EncodingParser::Double { width: self.width, src: self.src, dst: self.dst } } pub fn end(self) -> EncodingParser { EncodingParser::Double { width: self.width, src: self.src, dst: self.dst } }
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -72,5 +72,5 @@ impl ReflexiveBuilder {
self.reg = Some(reg); self.reg = Some(reg);
self self
} }
pub fn end(&self) -> EncodingParser { EncodingParser::Reflexive { width: self.width, reg: self.reg } } pub fn end(self) -> EncodingParser { EncodingParser::Reflexive { width: self.width, reg: self.reg } }
} }

View File

@ -2,7 +2,7 @@
//! An [`EncodingParser`] builds an [`Encoding`] from a [`TokenStream`] //! An [`EncodingParser`] builds an [`Encoding`] from a [`TokenStream`]
use super::*; use super::*;
#[derive(Debug)] #[derive(Clone, Debug)]
/// Builds an [Encoding] using [Tokens](crate::Token) from an input [TokenStream] /// Builds an [Encoding] using [Tokens](crate::Token) from an input [TokenStream]
pub enum EncodingParser { pub enum EncodingParser {
Single { width: Option<Width>, dst: Option<PrimaryOperand> }, Single { width: Option<Width>, dst: Option<PrimaryOperand> },
@ -10,29 +10,27 @@ pub enum EncodingParser {
Double { width: Option<Width>, src: Option<PrimaryOperand>, dst: Option<SecondaryOperand> }, Double { width: Option<Width>, src: Option<PrimaryOperand>, dst: Option<SecondaryOperand> },
Reflexive { width: Option<Width>, reg: Option<SecondaryOperand> }, Reflexive { width: Option<Width>, reg: Option<SecondaryOperand> },
} }
impl EncodingParser { impl EncodingParser {
/// Constructs an [Encoding] from this [EncodingParser], filling holes /// Constructs an [Encoding] from this [EncodingParser], filling holes
/// with the tokenstream /// with the tokenstream
pub fn parse<'text, T>(&self, p: &Parser, stream: &mut T) -> Result<Encoding, Error> pub fn parse<'text, T>(self, p: &Parser, stream: &mut T) -> Result<Encoding, ParseError>
where T: crate::TokenStream<'text> { where T: crate::TokenStream<'text> {
Ok(match self { Ok(match self {
Self::Single { width, dst } => { Self::Single { width, dst } => Encoding::Single {
let width = width.unwrap_or_else(|| Width::parse_or_default(p, stream)); width: width.unwrap_or_else(|| Width::parse_or_default(p, stream)),
let dst = if let Some(dst) = dst { *dst } else { PrimaryOperand::parse(p, stream)? }; dst: if let Some(dst) = dst { dst } else { PrimaryOperand::parse(p, stream)? },
Encoding::Single { width, dst } },
}
Self::Jump { target } => Encoding::Jump { target: target.unwrap_or(JumpTarget::parse(p, stream)?) }, Self::Jump { target } => Encoding::Jump { target: target.unwrap_or(JumpTarget::parse(p, stream)?) },
Self::Double { width, src, dst } => { Self::Double { width, src, dst } => Encoding::Double {
let width = width.unwrap_or_else(|| Width::parse_or_default(p, stream)); width: width.unwrap_or_else(|| Width::parse_or_default(p, stream)),
let src = if let Some(src) = src { *src } else { PrimaryOperand::parse(p, stream)? }; src: if let Some(src) = src { src } else { PrimaryOperand::parse(p, stream)? },
let dst = if let Some(dst) = dst { *dst } else { SecondaryOperand::parse(p, stream)? }; dst: if let Some(dst) = dst { dst } else { SecondaryOperand::parse(p, stream)? },
},
Encoding::Double { width, src, dst }
}
Self::Reflexive { width, reg } => { Self::Reflexive { width, reg } => {
let width = width.unwrap_or_else(|| Width::parse(p, stream).unwrap_or_default()); let width = width.unwrap_or_else(|| Width::parse(p, stream).unwrap_or_default());
let reg = if let Some(reg) = reg { *reg } else { SecondaryOperand::parse(p, stream)? }; let reg = if let Some(reg) = reg { reg } else { SecondaryOperand::parse(p, stream)? };
Encoding::Double { width, src: reg.into(), dst: reg } Encoding::Double { width, src: reg.clone().into(), dst: reg }
} }
}) })
} }

View File

@ -5,37 +5,54 @@ use super::*;
/// Contains the [pc-relative offset](Number) or [label](Identifier) /// Contains the [pc-relative offset](Number) or [label](Identifier)
/// for a [Jump](Encoding::Jump) [Instruction] /// for a [Jump](Encoding::Jump) [Instruction]
// TODO: Allow identifiers in JumpTarget #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum JumpTarget {
pub struct JumpTarget(Number); Number(Number),
Identifier(Identifier),
}
impl JumpTarget { impl JumpTarget {
pub fn word(&self) -> u16 { u16::from(self.0) & 0x3ff } pub fn word(&self) -> Option<u16> {
match self {
JumpTarget::Number(n) => Some(u16::from(*n) & 0x3ff),
JumpTarget::Identifier(_) => None,
}
}
pub fn squish(value: isize) -> Result<u16, ParseError> {
match value {
i if i % 2 != 0 => Err(ParseError::JumpedOdd(i))?,
i if (-1024..=1022).contains(&(i - 2)) => Ok(((value >> 1) - 1) as u16 & 0x3ff),
i => Err(ParseError::JumpedTooFar(i))?,
}
}
pub fn unsquish(value: u16) -> isize { (value as isize + 1) << 1 }
} }
impl Parsable for JumpTarget { impl Parsable for JumpTarget {
/// - Identifier // - Identifier
/// - Number // - Number
/// - Negative fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, ParseError>
/// - Number
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error>
where T: crate::TokenStream<'text> { where T: crate::TokenStream<'text> {
// Try to parse a number // Try to parse a number
let target = Number::parse(p, stream)?; if let Some(num) = Number::try_parse(p, stream)? {
match target.into() { Self::try_from(num)
i if i % 2 != 0 => Err(Error::JumpedOdd(i).context(stream.context()))?, } else {
i if (-1024..=1022).contains(&(i - 2)) => Ok(Self((target - 2) >> 1)), // if that fails, try to parse an identifier instead
i => Err(Error::JumpedTooFar(i).context(stream.context()))?, Ok(Self::Identifier(Identifier::parse(p, stream)?))
} }
} }
} }
impl From<JumpTarget> for u16 { impl TryFrom<Number> for JumpTarget {
fn from(value: JumpTarget) -> Self { value.0.into() } type Error = ParseError;
fn try_from(value: Number) -> Result<Self, Self::Error> { Ok(Self::Number(Self::squish(value.into())?.into())) }
} }
impl Display for JumpTarget { impl Display for JumpTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", (1 + isize::from(self.0)) << 1) match self {
Self::Number(num) => write!(f, "{:x}", Self::unsquish(u16::from(*num))),
Self::Identifier(id) => write!(f, "{id}"),
}
} }
} }

View File

@ -2,56 +2,54 @@
//! A [`Number`] represents a 16-bit signed or unsigned word //! A [`Number`] represents a 16-bit signed or unsigned word
use super::*; use super::*;
// TODO: Allow identifiers/expressions in place of numbers
// - Dependency inversion in TokenStream to allow swapping the parser mid-parse?
// - Oh my god, not relying on std::iter::Iterator allows for so many more parsing options
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Number(isize, u32); // (value, radix) pub struct Number(isize, u32); // (value, radix)
impl Parsable for Number { impl Parsable for Number {
// A number is: // A number is:
// RadixMarker[Hex|Oct|Bin]? // [Minus|Plus]? RadixMarker[Hex|Dec|Oct|Bin]? Number
// - Number fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, ParseError>
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error>
where T: TokenStream<'text> { where T: TokenStream<'text> {
use Type::*; use Type as Ty;
// The number is negative when it begins with a Minus, but Plus is also acceptable. // The number is negative when it begins with a Minus, but Plus is also acceptable.
let negative = stream.expect_any_of([Minus, Plus]).map_or(false, |t| t.is_variant(Minus)); let negative = stream.expect_any_of([Ty::Minus, Ty::Plus]).map_or(false, |t| t.is_variant(Ty::Minus));
let radix = match stream let radix = match stream
.expect_any_of([RadixMarkerHex, RadixMarkerDec, RadixMarkerOct, RadixMarkerBin]) .expect_any_of([Ty::RadixMarkerHex, Ty::RadixMarkerDec, Ty::RadixMarkerOct, Ty::RadixMarkerBin])
.ok() .ok()
.map(|t| t.variant()) .map(|t| t.variant())
{ {
Some(RadixMarkerHex) => 16, Some(Ty::RadixMarkerHex) => 16,
Some(RadixMarkerDec) => 10, Some(Ty::RadixMarkerDec) => 10,
Some(RadixMarkerOct) => 8, Some(Ty::RadixMarkerOct) => 8,
Some(RadixMarkerBin) => 2, Some(Ty::RadixMarkerBin) => 2,
_ => p.radix, _ => p.radix,
}; };
let number = stream.expect(Number)?; let number = stream.expect(Ty::Number)?;
// TODO: Reintroduce error context
let number = isize::from_str_radix(number.lexeme(), radix) let number = isize::from_str_radix(number.lexeme(), radix)
.map_err(|_| Error::UnexpectedDigits(number.lexeme().into(), radix).context(stream.context()))? .map_err(|_| ParseError::UnexpectedDigits(number.lexeme().into(), radix))?
* if negative { -1 } else { 1 }; * if negative { -1 } else { 1 };
// Ensure number fits within a *signed or unsigned* 16-bit int (it will be truncated to fit) // Ensure number fits within a *signed or unsigned* 16-bit int (it will be truncated to fit)
Ok(Self( Ok(Self(
if (-0x8000..0x10000).contains(&number) { if (-0x8000..0x10000).contains(&number) { number } else { Err(ParseError::NumberTooWide(number))? },
number
} else {
Err(Error::NumberTooWide(number).context(stream.context()))?
},
radix, radix,
)) ))
} }
} }
impl From<isize> for Number {
fn from(value: isize) -> Self { Self(value, 16) }
}
impl From<Number> for isize { impl From<Number> for isize {
fn from(value: Number) -> Self { value.0 as Self } fn from(value: Number) -> Self { value.0 as Self }
} }
impl From<Number> for i32 {
fn from(value: Number) -> Self { value.0 as Self } impl From<u16> for Number {
fn from(value: u16) -> Self { Self(value as isize, 16) }
} }
impl From<Number> for u16 { impl From<Number> for u16 {
/// Converts this type from the input type.
fn from(value: Number) -> Self { value.0 as Self } fn from(value: Number) -> Self { value.0 as Self }
} }
@ -72,5 +70,12 @@ impl std::ops::Shr<usize> for Number {
} }
impl std::fmt::Display for Number { impl std::fmt::Display for Number {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:x}", self.0) } fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.1 {
2 => std::fmt::Binary::fmt(&self.0, f),
8 => std::fmt::Octal::fmt(&self.0, f),
16 => std::fmt::LowerHex::fmt(&self.0, f),
_ => std::fmt::Display::fmt(&self.0, f),
}
}
} }

View File

@ -5,12 +5,13 @@ use super::*;
/// Contains the first [Register], addressing mode, and Extension Word for a /// Contains the first [Register], addressing mode, and Extension Word for a
/// [one-operand](Encoding::Single) or [two-operand](Encoding::Double) [Instruction] /// [one-operand](Encoding::Single) or [two-operand](Encoding::Double) [Instruction]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PrimaryOperand { pub enum PrimaryOperand {
Direct(Register), Direct(Register),
Indirect(Register), Indirect(Register),
PostInc(Register), PostInc(Register),
Indexed(Register, Number), Indexed(Register, Number),
Relative(Identifier),
Absolute(Number), Absolute(Number),
Immediate(Number), Immediate(Number),
Four, Four,
@ -27,7 +28,7 @@ impl PrimaryOperand {
use PrimaryOperand::*; use PrimaryOperand::*;
match self { match self {
Direct(_) | Zero => 0, Direct(_) | Zero => 0,
Indexed(_, _) | Absolute(_) | One => 1 << 4, Indexed(_, _) | Relative(_) | Absolute(_) | One => 1 << 4,
Indirect(_) | Two | Four => 2 << 4, Indirect(_) | Two | Four => 2 << 4,
PostInc(_) | Immediate(_) | MinusOne | Eight => 3 << 4, PostInc(_) | Immediate(_) | MinusOne | Eight => 3 << 4,
} }
@ -37,7 +38,7 @@ impl PrimaryOperand {
use PrimaryOperand::*; use PrimaryOperand::*;
match self { match self {
Direct(r) | Indexed(r, _) | Indirect(r) | PostInc(r) => *r, Direct(r) | Indexed(r, _) | Indirect(r) | PostInc(r) => *r,
Immediate(_) => Register::pc, Immediate(_) | Relative(_) => Register::pc,
Absolute(_) | Four | Eight => Register::sr, Absolute(_) | Four | Eight => Register::sr,
Zero | One | Two | MinusOne => Register::cg, Zero | One | Two | MinusOne => Register::cg,
} }
@ -53,21 +54,8 @@ impl PrimaryOperand {
} }
impl Parsable for PrimaryOperand { impl Parsable for PrimaryOperand {
// - Register fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, ParseError>
// - Indirect
// - Register
// - PostInc?
// - Number
// - OpenIdx
// - Register
// - CloseIdx
// - Absolute
// - Number
// - Immediate
// - Number
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error>
where T: crate::TokenStream<'text> { where T: crate::TokenStream<'text> {
use PrimaryOperand::*;
// Try parsing as Register (Direct) // Try parsing as Register (Direct)
if let Some(r) = Register::try_parse(p, stream)? { if let Some(r) = Register::try_parse(p, stream)? {
return Ok(Self::Direct(r)); return Ok(Self::Direct(r));
@ -79,33 +67,43 @@ impl Parsable for PrimaryOperand {
stream.expect(Type::RParen)?; stream.expect(Type::RParen)?;
return Ok(Self::Indexed(reg, idx)); return Ok(Self::Indexed(reg, idx));
} }
// Try parsing as Identifier (Relative, label mode)
if let Some(id) = Identifier::try_parse(p, stream)? {
return Ok(Self::Relative(id));
}
// Or directly match any of the valid prefix markers // Or directly match any of the valid prefix markers
// Type::Register and Type::Number are included here to make error messages clearer. // Register, Number, and Identifier are included here to make error messages clearer.
// their inclusion will cause a negligible slowdown when the next token is not a prefix marker // their inclusion will cause a negligible slowdown when the next token is not a prefix marker
// (a failure condition) // (a failure condition)
let token = let token = stream.expect_any_of([
stream.expect_any_of([Type::Indirect, Type::Absolute, Type::Immediate, Type::Register, Type::Number])?; Type::Indirect,
Type::Absolute,
Type::Immediate,
Type::Register,
Type::Number,
Type::Identifier,
])?;
Ok(match token.variant() { Ok(match token.variant() {
Type::Indirect => { Type::Indirect => {
let reg = Register::parse(p, stream)?; let reg = Register::parse(p, stream)?;
match stream.expect(Type::Plus) { match stream.expect(Type::Plus) {
Ok(_) => PostInc(reg), Ok(_) => Self::PostInc(reg),
Err(_) => Indirect(reg), Err(_) => Self::Indirect(reg),
} }
} }
Type::Absolute => Absolute(Number::parse(p, stream)?), Type::Absolute => Self::Absolute(Number::parse(p, stream)?),
Type::Immediate => { Type::Immediate => {
let number = Number::parse(p, stream)?; let number = Number::parse(p, stream)?;
match number.into() { match number.into() {
// There are two representations for the all-ones constant, since Number preserves // There are two representations for the all-ones constant, since Number preserves
// signedness. // signedness.
-1 | 0xffff => MinusOne, -1_isize | 0xffff => Self::MinusOne,
0 => Zero, 0 => Self::Zero,
1 => One, 1 => Self::One,
2 => Two, 2 => Self::Two,
4 => Four, 4 => Self::Four,
8 => Eight, 8 => Self::Eight,
_ => Immediate(number), _ => Self::Immediate(number),
} }
} }
_ => unreachable!("Token {token:?} passed expectation but failed match!"), _ => unreachable!("Token {token:?} passed expectation but failed match!"),
@ -119,6 +117,7 @@ impl From<SecondaryOperand> for PrimaryOperand {
SecondaryOperand::Direct(r) => Self::Direct(r), SecondaryOperand::Direct(r) => Self::Direct(r),
SecondaryOperand::Indexed(r, n) => Self::Indexed(r, n), SecondaryOperand::Indexed(r, n) => Self::Indexed(r, n),
SecondaryOperand::Absolute(n) => Self::Absolute(n), SecondaryOperand::Absolute(n) => Self::Absolute(n),
SecondaryOperand::Relative(id) => Self::Relative(id),
SecondaryOperand::Zero => Self::Zero, SecondaryOperand::Zero => Self::Zero,
SecondaryOperand::One => Self::One, SecondaryOperand::One => Self::One,
} }
@ -133,6 +132,7 @@ impl Display for PrimaryOperand {
Self::Indirect(r) => write!(f, "@{r}"), Self::Indirect(r) => write!(f, "@{r}"),
Self::PostInc(r) => write!(f, "@{r}+"), Self::PostInc(r) => write!(f, "@{r}+"),
Self::Indexed(r, idx) => write!(f, "{idx}({r})"), Self::Indexed(r, idx) => write!(f, "{idx}({r})"),
Self::Relative(id) => Display::fmt(id, f),
Self::Absolute(n) => write!(f, "&{n}"), Self::Absolute(n) => write!(f, "&{n}"),
Self::Immediate(n) => write!(f, "#{n}"), Self::Immediate(n) => write!(f, "#{n}"),
Self::Four => Display::fmt("#4", f), Self::Four => Display::fmt("#4", f),

View File

@ -30,14 +30,9 @@ pub enum Register {
} }
impl Parsable for Register { impl Parsable for Register {
fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, Error> fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, ParseError>
where T: crate::TokenStream<'text> { where T: crate::TokenStream<'text> {
stream stream.expect(Type::Register)?.lexeme().parse()
.expect(Type::Register)
.map_err(|e: Error| e.context(stream.context()))?
.lexeme()
.parse()
.map_err(|e: Error| e.context(stream.context()))
} }
} }
@ -46,7 +41,7 @@ impl From<Register> for u16 {
} }
impl TryFrom<u16> for Register { impl TryFrom<u16> for Register {
type Error = Error; type Error = ParseError;
fn try_from(value: u16) -> Result<Self, Self::Error> { fn try_from(value: u16) -> Result<Self, Self::Error> {
use Register::*; use Register::*;
Ok(match value { Ok(match value {
@ -66,13 +61,13 @@ impl TryFrom<u16> for Register {
13 => r13, 13 => r13,
14 => r14, 14 => r14,
15 => r15, 15 => r15,
_ => return Err(Error::RegisterTooHigh(value)), _ => return Err(ParseError::RegisterTooHigh(value)),
}) })
} }
} }
impl FromStr for Register { impl FromStr for Register {
type Err = Error; type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
use Register::*; use Register::*;
@ -81,7 +76,9 @@ impl FromStr for Register {
"sp" => Ok(sp), "sp" => Ok(sp),
"sr" => Ok(sr), "sr" => Ok(sr),
"cg" => Ok(cg), "cg" => Ok(cg),
_ => str::parse::<u16>(&s[1..]).map_err(|_| -> Self::Err { Error::NotARegister(s.into()) })?.try_into(), _ => {
str::parse::<u16>(&s[1..]).map_err(|_| -> Self::Err { ParseError::NotARegister(s.into()) })?.try_into()
}
} }
} }
} }

View File

@ -4,28 +4,31 @@
use super::*; use super::*;
/// The destination of a [Double](Encoding::Double) /// The destination of a [Double](Encoding::Double)
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SecondaryOperand { pub enum SecondaryOperand {
Direct(Register), Direct(Register),
Indexed(Register, Number), Indexed(Register, Number),
Relative(Identifier),
Absolute(Number), Absolute(Number),
// Joke encodings? // Joke encodings?
Zero, Zero,
One, One,
} }
use SecondaryOperand as So;
impl SecondaryOperand { impl SecondaryOperand {
pub fn mode(&self) -> u16 { pub fn mode(&self) -> u16 {
use SecondaryOperand::*;
match self { match self {
Direct(_) | Zero => 0, So::Direct(_) | So::Zero => 0,
Indexed(_, _) | Absolute(_) | One => 1 << 7, So::Indexed(_, _) | So::Relative(_) | So::Absolute(_) | So::One => 1 << 7,
} }
} }
pub fn register(&self) -> Register { pub fn register(&self) -> Register {
use SecondaryOperand::*; use SecondaryOperand::*;
match self { match self {
Direct(r) | Indexed(r, _) => *r, Direct(r) | Indexed(r, _) => *r,
Relative(_) => Register::pc,
Absolute(_) => Register::sr, Absolute(_) => Register::sr,
Zero | One => Register::cg, Zero | One => Register::cg,
} }
@ -51,7 +54,7 @@ impl Parsable for SecondaryOperand {
// - Number // - Number
// - Immediate // - Immediate
// - Number == 0, 1 // - Number == 0, 1
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, crate::Error> fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, ParseError>
where T: crate::TokenStream<'text> { where T: crate::TokenStream<'text> {
use SecondaryOperand::*; use SecondaryOperand::*;
stream.allow(Type::Separator); stream.allow(Type::Separator);
@ -66,16 +69,22 @@ impl Parsable for SecondaryOperand {
stream.expect(Type::RParen)?; stream.expect(Type::RParen)?;
return Ok(Self::Indexed(reg, idx)); return Ok(Self::Indexed(reg, idx));
} }
// Type::Register and Type::Number are included here to make error messages clearer. // Try parsing as Identifier (Relative, label mode)
if let Some(id) = Identifier::try_parse(p, stream)? {
return Ok(Self::Relative(id));
}
// Register, Number, and Identifier are included here to make error messages clearer.
// their inclusion will cause a negligible slowdown when the next token is not a prefix marker // their inclusion will cause a negligible slowdown when the next token is not a prefix marker
// (a failure condition) but should not match a token // (a failure condition) but should not match a token
let token = stream.expect_any_of([Type::Absolute, Type::Immediate, Type::Register, Type::Number])?; let token =
stream.expect_any_of([Type::Absolute, Type::Immediate, Type::Register, Type::Number, Type::Identifier])?;
Ok(match token.variant() { Ok(match token.variant() {
Type::Absolute => Absolute(Number::parse(p, stream)?), Type::Absolute => Absolute(Number::parse(p, stream)?),
// TODO: Reintroduce error context
Type::Immediate => match Number::parse(p, stream)?.into() { Type::Immediate => match Number::parse(p, stream)?.into() {
0 => Zero, 0 => Zero,
1 => One, 1 => One,
n => Err(Error::FatSecondaryImmediate(n as isize).context(stream.context()))?, n => Err(ParseError::FatSecondaryImmediate(n))?,
}, },
_ => unreachable!("Token {token:?} passed expectation but failed match!"), _ => unreachable!("Token {token:?} passed expectation but failed match!"),
}) })
@ -87,6 +96,7 @@ impl Display for SecondaryOperand {
match self { match self {
Self::Direct(r) => Display::fmt(r, f), Self::Direct(r) => Display::fmt(r, f),
Self::Indexed(r, idx) => write!(f, "{idx}({r})"), Self::Indexed(r, idx) => write!(f, "{idx}({r})"),
Self::Relative(id) => Display::fmt(id, f),
Self::Absolute(n) => write!(f, "&{n}"), Self::Absolute(n) => write!(f, "&{n}"),
Self::Zero => Display::fmt("#0", f), Self::Zero => Display::fmt("#0", f),
Self::One => Display::fmt("#1", f), Self::One => Display::fmt("#1", f),

View File

@ -10,7 +10,7 @@ use super::*;
pub struct Width(bool); pub struct Width(bool);
impl Parsable for Width { impl Parsable for Width {
fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, Error> fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, ParseError>
where T: TokenStream<'text> { where T: TokenStream<'text> {
let Ok(token) = stream.expect_any_of([Type::ByteWidth, Type::WordWidth]) else { let Ok(token) = stream.expect_any_of([Type::ByteWidth, Type::WordWidth]) else {
return Ok(Self(false)); return Ok(Self(false));

View File

@ -71,195 +71,189 @@ pub enum Opcode {
} }
impl Opcode { impl Opcode {
pub fn takes_width(&self) -> bool {
use Opcode::*;
match self {
Rrc => true,
Swpb => false,
Rra => true,
Sxt => false,
Push => true,
Call | Reti => false,
Jnz | Jz | Jnc | Jc | Jn | Jge | Jl | Jmp => false,
Mov | Add | Addc | Subc | Sub | Cmp | Dadd | Bit | Bic | Bis | Xor | And => true,
Nop | Pop | Br | Ret | Clrc | Setc | Clrz | Setz | Clrn | Setn | Dint | Eint | Rla | Rlc | Inv | Clr
| Tst | Dec | Decd | Inc | Incd | Adc | Dadc | Sbc => true,
}
}
/// Resolve an Opcode into an [Opcode] and an [EncodingParser] /// Resolve an Opcode into an [Opcode] and an [EncodingParser]
pub fn resolve(self) -> (Opcode, EncodingParser) { pub fn resolve(self) -> (Opcode, EncodingParser) {
use super::Encoding as Enc; use super::Encoding as Enc;
use Opcode::*; use Register as Reg;
use Register::*;
use {PrimaryOperand as Src, SecondaryOperand as Dst}; use {PrimaryOperand as Src, SecondaryOperand as Dst};
match self { match self {
Rrc | Swpb | Rra | Sxt | Push | Call | Reti => (self, Enc::single().end()), Self::Rrc | Self::Swpb | Self::Rra | Self::Sxt | Self::Push | Self::Call | Self::Reti => {
Jnz | Jz | Jnc | Jc | Jn | Jge | Jl | Jmp => (self, Enc::jump().end()), (self, Enc::single().end())
Mov | Add | Addc | Subc | Sub | Cmp | Dadd | Bit | Bic | Bis | Xor | And => (self, Enc::double().end()), }
Nop => (Mov, Enc::double().src(Src::Zero).dst(Dst::Zero).end()), Self::Jnz | Self::Jz | Self::Jnc | Self::Jc | Self::Jn | Self::Jge | Self::Jl | Self::Jmp => {
Pop => (Mov, Enc::double().src(Src::PostInc(sp)).end()), (self, Enc::jump().end())
Br => (Mov, Enc::double().dst(Dst::Direct(pc)).end()), }
Ret => (Mov, Enc::double().src(Src::PostInc(sp)).dst(Dst::Direct(pc)).end()), Self::Mov
Clrc => (Bic, Enc::double().src(Src::One).dst(Dst::Direct(sr)).end()), | Self::Add
Setc => (Bis, Enc::double().src(Src::One).dst(Dst::Direct(sr)).end()), | Self::Addc
Clrz => (Bic, Enc::double().src(Src::Two).dst(Dst::Direct(sr)).end()), | Self::Subc
Setz => (Bis, Enc::double().src(Src::Two).dst(Dst::Direct(sr)).end()), | Self::Sub
Clrn => (Bic, Enc::double().src(Src::Four).dst(Dst::Direct(sr)).end()), | Self::Cmp
Setn => (Bis, Enc::double().src(Src::Four).dst(Dst::Direct(sr)).end()), | Self::Dadd
Dint => (Bic, Enc::double().src(Src::Eight).dst(Dst::Direct(sr)).end()), | Self::Bit
Eint => (Bis, Enc::double().src(Src::Eight).dst(Dst::Direct(sr)).end()), | Self::Bic
Rla => (Add, Enc::reflexive().end()), | Self::Bis
Rlc => (Addc, Enc::reflexive().end()), | Self::Xor
Inv => (Xor, Enc::double().src(Src::MinusOne).end()), | Self::And => (self, Enc::double().end()),
Clr => (Mov, Enc::double().src(Src::Zero).end()), Self::Nop => (Self::Mov, Enc::double().src(Src::Zero).dst(Dst::Zero).end()),
Tst => (Cmp, Enc::double().src(Src::Zero).end()), Self::Pop => (Self::Mov, Enc::double().src(Src::PostInc(Reg::sp)).end()),
Dec => (Sub, Enc::double().src(Src::One).end()), Self::Br => (Self::Mov, Enc::double().dst(Dst::Direct(Reg::pc)).end()),
Decd => (Sub, Enc::double().src(Src::Two).end()), Self::Ret => (Self::Mov, Enc::double().src(Src::PostInc(Reg::sp)).dst(Dst::Direct(Reg::pc)).end()),
Inc => (Add, Enc::double().src(Src::One).end()), Self::Clrc => (Self::Bic, Enc::double().src(Src::One).dst(Dst::Direct(Reg::sr)).end()),
Incd => (Add, Enc::double().src(Src::Two).end()), Self::Setc => (Self::Bis, Enc::double().src(Src::One).dst(Dst::Direct(Reg::sr)).end()),
Adc => (Addc, Enc::double().src(Src::Zero).end()), Self::Clrz => (Self::Bic, Enc::double().src(Src::Two).dst(Dst::Direct(Reg::sr)).end()),
Dadc => (Dadd, Enc::double().src(Src::Zero).end()), Self::Setz => (Self::Bis, Enc::double().src(Src::Two).dst(Dst::Direct(Reg::sr)).end()),
Sbc => (Subc, Enc::double().src(Src::Zero).end()), Self::Clrn => (Self::Bic, Enc::double().src(Src::Four).dst(Dst::Direct(Reg::sr)).end()),
Self::Setn => (Self::Bis, Enc::double().src(Src::Four).dst(Dst::Direct(Reg::sr)).end()),
Self::Dint => (Self::Bic, Enc::double().src(Src::Eight).dst(Dst::Direct(Reg::sr)).end()),
Self::Eint => (Self::Bis, Enc::double().src(Src::Eight).dst(Dst::Direct(Reg::sr)).end()),
Self::Rla => (Self::Add, Enc::reflexive().end()),
Self::Rlc => (Self::Addc, Enc::reflexive().end()),
Self::Inv => (Self::Xor, Enc::double().src(Src::MinusOne).end()),
Self::Clr => (Self::Mov, Enc::double().src(Src::Zero).end()),
Self::Tst => (Self::Cmp, Enc::double().src(Src::Zero).end()),
Self::Dec => (Self::Sub, Enc::double().src(Src::One).end()),
Self::Decd => (Self::Sub, Enc::double().src(Src::Two).end()),
Self::Inc => (Self::Add, Enc::double().src(Src::One).end()),
Self::Incd => (Self::Add, Enc::double().src(Src::Two).end()),
Self::Adc => (Self::Addc, Enc::double().src(Src::Zero).end()),
Self::Dadc => (Self::Dadd, Enc::double().src(Src::Zero).end()),
Self::Sbc => (Self::Subc, Enc::double().src(Src::Zero).end()),
} }
} }
} }
impl Parsable for Opcode { impl Parsable for Opcode {
fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, Error> fn parse<'text, T>(_: &Parser, stream: &mut T) -> Result<Self, ParseError>
where T: TokenStream<'text> { where T: TokenStream<'text> {
stream.expect(Type::Insn)?.parse().map_err(|e: Error| e.context(stream.context())) // TODO: Reintroduce error context
stream.expect(Type::Insn)?.parse()
} }
} }
impl FromStr for Opcode { impl FromStr for Opcode {
type Err = Error; type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
use Opcode::*;
//TODO: Reduce allocations here? //TODO: Reduce allocations here?
let s = s.to_ascii_lowercase(); let s = s.to_ascii_lowercase();
Ok(match s.as_str() { Ok(match s.as_str() {
"rrc" => Rrc, "rrc" => Self::Rrc,
"swpb" => Swpb, "swpb" => Self::Swpb,
"rra" => Rra, "rra" => Self::Rra,
"sxt" => Sxt, "sxt" => Self::Sxt,
"push" => Push, "push" => Self::Push,
"call" => Call, "call" => Self::Call,
"reti" => Reti, "reti" => Self::Reti,
"jne" | "jnz" => Jnz, "jne" | "jnz" => Self::Jnz,
"jeq" | "jz" => Jz, "jeq" | "jz" => Self::Jz,
"jnc" | "jlo" => Jnc, "jnc" | "jlo" => Self::Jnc,
"jc" | "jhs" => Jc, "jc" | "jhs" => Self::Jc,
"jn" => Jn, "jn" => Self::Jn,
"jge" => Jge, "jge" => Self::Jge,
"jl" => Jl, "jl" => Self::Jl,
"jmp" => Jmp, "jmp" => Self::Jmp,
"mov" => Mov, "mov" => Self::Mov,
"add" => Add, "add" => Self::Add,
"addc" => Addc, "addc" => Self::Addc,
"subc" => Subc, "subc" => Self::Subc,
"sub" => Sub, "sub" => Self::Sub,
"cmp" => Cmp, "cmp" => Self::Cmp,
"dadd" => Dadd, "dadd" => Self::Dadd,
"bit" => Bit, "bit" => Self::Bit,
"bic" => Bic, "bic" => Self::Bic,
"bis" => Bis, "bis" => Self::Bis,
"xor" => Xor, "xor" => Self::Xor,
"and" => And, "and" => Self::And,
"nop" => Nop, "nop" => Self::Nop,
"pop" => Pop, "pop" => Self::Pop,
"br" => Br, "br" => Self::Br,
"ret" => Ret, "ret" => Self::Ret,
"clrc" => Clrc, "clrc" => Self::Clrc,
"setc" => Setc, "setc" => Self::Setc,
"clrz" => Clrz, "clrz" => Self::Clrz,
"setz" => Setz, "setz" => Self::Setz,
"clrn" => Clrn, "clrn" => Self::Clrn,
"setn" => Setn, "setn" => Self::Setn,
"dint" => Dint, "dint" => Self::Dint,
"eint" => Eint, "eint" => Self::Eint,
"rla" => Rla, "rla" => Self::Rla,
"rlc" => Rlc, "rlc" => Self::Rlc,
"inv" => Inv, "inv" => Self::Inv,
"clr" => Clr, "clr" => Self::Clr,
"tst" => Tst, "tst" => Self::Tst,
"dec" => Dec, "dec" => Self::Dec,
"decd" => Decd, "decd" => Self::Decd,
"inc" => Inc, "inc" => Self::Inc,
"incd" => Incd, "incd" => Self::Incd,
"adc" => Adc, "adc" => Self::Adc,
"dadc" => Dadc, "dadc" => Self::Dadc,
"sbc" => Sbc, "sbc" => Self::Sbc,
_ => Err(Error::UnrecognizedOpcode(s))?, _ => Err(ParseError::UnrecognizedOpcode(s))?,
}) })
} }
} }
impl Display for Opcode { impl Display for Opcode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Opcode::*;
write!( write!(
f, f,
"{}", "{}",
match self { match self {
Nop => "nop", Self::Nop => "nop",
Pop => "pop", Self::Pop => "pop",
Br => "br", Self::Br => "br",
Ret => "ret", Self::Ret => "ret",
Clrc => "clrc", Self::Clrc => "clrc",
Setc => "setc", Self::Setc => "setc",
Clrz => "clrz", Self::Clrz => "clrz",
Setz => "setz", Self::Setz => "setz",
Clrn => "clrn", Self::Clrn => "clrn",
Setn => "setn", Self::Setn => "setn",
Dint => "dint", Self::Dint => "dint",
Eint => "eint", Self::Eint => "eint",
Rla => "rla", Self::Rla => "rla",
Rlc => "rlc", Self::Rlc => "rlc",
Inv => "inv", Self::Inv => "inv",
Clr => "clr", Self::Clr => "clr",
Tst => "tst", Self::Tst => "tst",
Dec => "dec", Self::Dec => "dec",
Decd => "decd", Self::Decd => "decd",
Inc => "inc", Self::Inc => "inc",
Incd => "incd", Self::Incd => "incd",
Adc => "adc", Self::Adc => "adc",
Dadc => "dadc", Self::Dadc => "dadc",
Sbc => "sbc", Self::Sbc => "sbc",
Rrc => "rrc", Self::Rrc => "rrc",
Swpb => "swpb", Self::Swpb => "swpb",
Rra => "rra", Self::Rra => "rra",
Sxt => "sxt", Self::Sxt => "sxt",
Push => "push", Self::Push => "push",
Call => "call", Self::Call => "call",
Reti => "reti", Self::Reti => "reti",
Jnz => "jnz", Self::Jnz => "jnz",
Jz => "jz", Self::Jz => "jz",
Jnc => "jnc", Self::Jnc => "jnc",
Jc => "jc", Self::Jc => "jc",
Jn => "jn", Self::Jn => "jn",
Jge => "jge", Self::Jge => "jge",
Jl => "jl", Self::Jl => "jl",
Jmp => "jmp", Self::Jmp => "jmp",
Mov => "mov", Self::Mov => "mov",
Add => "add", Self::Add => "add",
Addc => "addc", Self::Addc => "addc",
Subc => "subc", Self::Subc => "subc",
Sub => "sub", Self::Sub => "sub",
Cmp => "cmp", Self::Cmp => "cmp",
Dadd => "dadd", Self::Dadd => "dadd",
Bit => "bit", Self::Bit => "bit",
Bic => "bic", Self::Bic => "bic",
Bis => "bis", Self::Bis => "bis",
Xor => "xor", Self::Xor => "xor",
And => "and", Self::And => "and",
} }
) )
} }
} }
impl LowerHex for Opcode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:04x}", *self as u16) }
}

View File

@ -7,13 +7,12 @@ use super::*;
pub struct Label(pub Identifier); pub struct Label(pub Identifier);
impl Parsable for Label { impl Parsable for Label {
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error> fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, ParseError>
where T: TokenStream<'text> { where T: TokenStream<'text> {
Ok(Self( Ok(Self(Identifier::parse(p, stream).and_then(|t| {
Identifier::parse(p, stream) stream.require(Type::Label)?;
.and_then(|t| stream.require(Type::Label).and(Ok(t))) Ok(t)
.map_err(|e| e.context(stream.context()))?, })?))
))
} }
} }

72
src/parser/line.rs Normal file
View File

@ -0,0 +1,72 @@
// © 2023 John Breaux
//! [`Line`] contains a single subcomponent of the document. Multiple instructions on the same
//! document line will be treated as if they took up multiple [`Line`s](Line).
//!
//! A line contains one of:
//! - [`Label`]
//! - [`Instruction`]
//! - [`Directive`]
//! - [`Comment`]
//! - [Nothing](Line::Empty)
use super::*;
/// A line contains any one of:
/// - [`Label`] (definition)
/// - [`Instruction`]
/// - [`Directive`]
/// - [`Comment`]
/// - Nothing at all
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Line {
Empty,
Insn(Instruction),
Comment(Comment),
Directive(Directive),
Label(Label),
EndOfFile, // Expected end of file
}
impl Parsable for Line {
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, ParseError>
where T: TokenStream<'text> {
Ok(
match stream
.peek_expect_any_of([
Type::Endl,
Type::Insn,
Type::Comment,
Type::Directive,
Type::Identifier,
Type::EndOfFile,
])?
.variant()
{
Type::Endl => {
stream.next();
Self::Empty
}
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)?),
Type::EndOfFile => {
stream.next();
Self::EndOfFile
}
_ => unreachable!("stream.peek_expect_any_of should return Err for unmatched inputs"),
},
)
}
}
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."),
}
}
}

View File

@ -4,7 +4,7 @@ use super::*;
/// Parses tokens from [stream](TokenStream) into Self node /// Parses tokens from [stream](TokenStream) into Self node
pub trait Parsable { pub trait Parsable {
/// Parses tokens from [TokenStream](TokenStream) into Self nodes /// Parses tokens from [TokenStream](TokenStream) into Self nodes
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, Error> fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, ParseError>
where where
Self: Sized, Self: Sized,
T: TokenStream<'text>; T: TokenStream<'text>;
@ -12,19 +12,23 @@ pub trait Parsable {
/// Attempts to parse tokens from [stream](TokenStream) into Self nodes. /// Attempts to parse tokens from [stream](TokenStream) into Self nodes.
/// ///
/// Masks failed expectations. /// Masks failed expectations.
fn try_parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Option<Self>, Error> fn try_parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Option<Self>, ParseError>
where where
Self: Sized, Self: Sized,
T: TokenStream<'text>, T: TokenStream<'text>,
{ {
match Self::parse(p, stream).map_err(|e| e.bare()) { match Self::parse(p, stream) {
Ok(tt) => Ok(Some(tt)), Ok(some) => Ok(Some(some)),
Err(Error::UnexpectedToken { .. }) | Err(Error::AllExpectationsFailed { .. }) => Ok(None), Err(ParseError::LexError(_)) => Ok(None),
Err(e) => Err(e.context(stream.context())), Err(e) => Err(e),
} }
} }
fn parse_and<'text, T, R>(p: &Parser, stream: &mut T, f: fn(p: &Parser, &mut T) -> R) -> Result<(Self, R), Error> fn parse_and<'text, T, R>(
p: &Parser,
stream: &mut T,
f: fn(p: &Parser, &mut T) -> R,
) -> Result<(Self, R), ParseError>
where where
Self: Sized, Self: Sized,
T: TokenStream<'text>, T: TokenStream<'text>,
@ -43,3 +47,39 @@ pub trait Parsable {
Self::parse(p, stream).unwrap_or_default() Self::parse(p, stream).unwrap_or_default()
} }
} }
macro_rules! parsable_str_types {
($($t:ty),*$(,)?) => {$(
impl Parsable for $t {
fn parse<'text, T>(_p: &Parser, stream: &mut T) -> Result<Self, ParseError>
where T: TokenStream<'text> {
Ok(stream.expect(Type::String)?.lexeme().trim_matches('"').into())
}
}
)*};
}
use std::{path::PathBuf, rc::Rc};
parsable_str_types![String, Rc<str>, Box<str>, PathBuf];
/// Vectors of arbitrary parsables are cool
impl<P: Parsable> Parsable for Vec<P> {
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, ParseError>
where T: TokenStream<'text> {
// [dead beef]
// [A, B,]
// [c d e f]
// [ something
// else ]
stream.require(Type::LBracket)?;
stream.allow(Type::Endl);
let mut out = vec![];
while let Some(t) = P::try_parse(p, stream)? {
out.push(t);
stream.allow(Type::Separator);
stream.allow(Type::Endl);
}
stream.require(Type::RBracket)?;
Ok(out)
}
}

51
src/parser/root.rs Normal file
View File

@ -0,0 +1,51 @@
use std::path::{Path, PathBuf};
// © 2023 John Breaux
use super::*;
/// Contains the entire AST
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Root(Option<PathBuf>, Vec<(usize, Line)>);
// pub struct Root { pub path: PathBuf, pub lines: Vec<Line> }
impl Root {
pub fn file(&self) -> Option<&Path> { self.0.as_deref() }
pub(crate) fn set_file(mut self, path: PathBuf) -> Self {
self.0 = Some(path);
self
}
pub fn lines(&self) -> &[(usize, Line)] { &self.1 }
}
impl Parsable for Root {
fn parse<'text, T>(p: &Parser, stream: &mut T) -> Result<Self, ParseError>
where T: TokenStream<'text> {
let mut lines = vec![];
loop {
let number = stream.context().line();
match Line::parse(p, stream)? {
Line::EndOfFile => break,
line => lines.push((number, line)),
}
}
Ok(Root(None, lines))
}
}
impl Display for Root {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (num, line) in &self.1 {
f.pad(&format!("{num:3}: {line} "))?;
}
Ok(())
}
}
impl Debug for Root {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for line in self.0.iter() {
Debug::fmt(line, f)?;
}
Ok(())
}
}

View File

@ -2,6 +2,16 @@
; examples of valid assembly ; examples of valid assembly
; ;
; testing labels
jmp main
; testing directives
.string "ABA"
.string "ABAB"
.word 0b0101101001011010
.words [dead beef]
main:
; testing defines ; testing defines
.define asdfgh #1000 .define asdfgh #1000
.define qwerty @sp+ .define qwerty @sp+
@ -132,7 +142,7 @@ mov #beef, sp
mov #beef, sr mov #beef, sr
mov #beef, cg mov #beef, cg
; jmp _register_mode ; TODO: msp430_asm currently has no support for jump labels. jmp _register_mode
jmp 3fe jmp 3fe
jmp -3fc jmp -3fc
ret ret