update instruction set

from bf60db11a1
This commit is contained in:
phyto 2023-05-06 10:24:15 +00:00
parent 0825db17ea
commit dac63b0fe2
Signed by: phyto
SSH Key Fingerprint: SHA256:FJdIUDW+Q/c/oLlaNI7vrxCrv0VMxMgT6usJ+7A/wo0
3 changed files with 679 additions and 676 deletions

View File

@ -1,245 +1,245 @@
use std::collections::HashSet;
use crate::{
span::{Span, Spanned},
token::{ScopedIdentifier, Statement},
walker::Definitions,
Error, Warning,
span::{Span, Spanned},
token::{ScopedIdentifier, Statement},
walker::Definitions,
Error, Warning,
};
const LIT: u8 = 0x80;
const LIT2: u8 = 0x20;
const LIT2: u8 = 0xa0;
struct Binary {
data: [u8; 256 * 256 - 256],
pointer: u16,
length: u16,
data: [u8; 256 * 256 - 256],
pointer: u16,
length: u16,
}
impl Binary {
pub fn new() -> Self {
Self {
data: [0; 256 * 256 - 256],
pointer: 256,
length: 256,
}
}
pub fn new() -> Self {
Self {
data: [0; 256 * 256 - 256],
pointer: 256,
length: 256,
}
}
pub fn push_byte(&mut self, byte: u8) {
self.data[self.pointer as usize - 256] = byte;
self.increment_pointer(1);
self.length = self.pointer;
}
pub fn push_byte(&mut self, byte: u8) {
self.data[self.pointer as usize - 256] = byte;
self.increment_pointer(1);
self.length = self.pointer;
}
pub fn push_short(&mut self, short: u16) {
self.push_byte(((short >> 8) & 0xff) as u8);
self.push_byte((short & 0x00ff) as u8);
}
pub fn push_short(&mut self, short: u16) {
self.push_byte(((short >> 8) & 0xff) as u8);
self.push_byte((short & 0x00ff) as u8);
}
pub fn set_pointer(&mut self, to: u16) {
self.pointer = to;
}
pub fn set_pointer(&mut self, to: u16) {
self.pointer = to;
}
pub fn increment_pointer(&mut self, by: u16) {
self.pointer += by;
}
pub fn increment_pointer(&mut self, by: u16) {
self.pointer += by;
}
pub fn get_pointer(&self) -> u16 {
self.pointer
}
pub fn get_pointer(&self) -> u16 {
self.pointer
}
}
impl From<Binary> for Vec<u8> {
fn from(binary: Binary) -> Self {
binary.data[0..binary.length as usize - 256].into()
}
fn from(binary: Binary) -> Self {
binary.data[0..binary.length as usize - 256].into()
}
}
pub(crate) fn emit(
statements: Vec<Spanned<Statement>>,
definitions: Definitions,
statements: Vec<Spanned<Statement>>,
definitions: Definitions,
) -> Result<(Vec<u8>, Vec<Warning>), (Vec<Error>, Vec<Warning>)> {
let mut errors: Vec<Error> = Vec::new();
let mut warnings: Vec<Warning> = Vec::new();
let mut errors: Vec<Error> = Vec::new();
let mut warnings: Vec<Warning> = Vec::new();
let mut unused_labels: HashSet<&ScopedIdentifier> = definitions.labels.keys().collect();
let mut unused_labels: HashSet<&ScopedIdentifier> = definitions.labels.keys().collect();
let mut binary = Binary::new();
let mut binary = Binary::new();
for statement in statements {
match statement {
Spanned {
node: Statement::Instruction(instruction),
..
} => {
let opcode = instruction.instruction_kind as u8
| ((instruction.short as u8) << 5)
| ((instruction.r#return as u8) << 6)
| ((instruction.keep as u8) << 7);
binary.push_byte(opcode);
}
Spanned {
node: Statement::PadAbsolute(value),
..
} => {
binary.set_pointer(value as u16);
}
Spanned {
node: Statement::PadRelative(value),
..
} => {
binary.increment_pointer(value as u16);
}
Spanned {
node: Statement::LiteralZeroPageAddress(scoped_identifier),
span,
} => match find_address(&scoped_identifier, &definitions, &span) {
Ok((address, _)) => {
unused_labels.remove(&scoped_identifier);
if address <= 0xff {
binary.push_byte(LIT);
binary.push_byte((address & 0xff) as u8);
} else {
errors.push(Error::AddressNotZeroPage {
address,
identifier: scoped_identifier.to_string(),
span: span.into(),
});
binary.increment_pointer(2);
}
}
Err(err) => {
errors.push(err);
binary.increment_pointer(2);
}
},
Spanned {
node: Statement::LiteralRelativeAddress(scoped_identifier),
span,
} => match find_address(&scoped_identifier, &definitions, &span) {
Ok((address, other_span)) => {
unused_labels.remove(&scoped_identifier);
let offset = address as isize - binary.get_pointer() as isize - 3;
if offset < -126 || offset > 126 {
errors.push(Error::AddressTooFar {
distance: offset.abs() as usize,
identifier: scoped_identifier.to_string(),
span: span.into(),
other_span: other_span.into(),
});
binary.increment_pointer(2);
} else {
binary.push_byte(LIT);
binary.push_byte(offset as u8);
}
}
Err(err) => {
errors.push(err);
binary.increment_pointer(2);
}
},
Spanned {
node: Statement::LiteralAbsoluteAddress(scoped_identifier),
span,
} => match find_address(&scoped_identifier, &definitions, &span) {
Ok((address, _)) => {
unused_labels.remove(&scoped_identifier);
binary.push_byte(LIT2);
binary.push_short(address);
}
Err(err) => {
errors.push(err);
binary.increment_pointer(3);
}
},
Spanned {
node: Statement::RawAddress(scoped_identifier),
span,
} => match find_address(&scoped_identifier, &definitions, &span) {
Ok((address, _)) => {
unused_labels.remove(&scoped_identifier);
binary.push_short(address);
}
Err(err) => {
errors.push(err);
binary.increment_pointer(2);
}
},
Spanned {
node: Statement::LiteralHexByte(value),
..
} => {
binary.push_byte(LIT);
binary.push_byte(value);
}
Spanned {
node: Statement::LiteralHexShort(value),
..
} => {
binary.push_byte(LIT2);
binary.push_short(value);
}
Spanned {
node: Statement::RawHexByte(value),
..
} => {
binary.push_byte(value);
}
Spanned {
node: Statement::RawHexShort(value),
..
} => {
binary.push_short(value);
}
Spanned {
node: Statement::RawChar(value),
..
} => {
binary.push_byte(value);
}
Spanned {
node: Statement::RawWord(word),
..
} => {
for byte in word {
binary.push_byte(byte);
}
}
}
}
for statement in statements {
match statement {
Spanned {
node: Statement::Instruction(instruction),
..
} => {
let opcode = instruction.instruction_kind as u8
| ((instruction.short as u8) << 5)
| ((instruction.r#return as u8) << 6)
| ((instruction.keep as u8) << 7);
binary.push_byte(opcode);
}
Spanned {
node: Statement::PadAbsolute(value),
..
} => {
binary.set_pointer(value as u16);
}
Spanned {
node: Statement::PadRelative(value),
..
} => {
binary.increment_pointer(value as u16);
}
Spanned {
node: Statement::LiteralZeroPageAddress(scoped_identifier),
span,
} => match find_address(&scoped_identifier, &definitions, &span) {
Ok((address, _)) => {
unused_labels.remove(&scoped_identifier);
if address <= 0xff {
binary.push_byte(LIT);
binary.push_byte((address & 0xff) as u8);
} else {
errors.push(Error::AddressNotZeroPage {
address,
identifier: scoped_identifier.to_string(),
span: span.into(),
});
binary.increment_pointer(2);
}
}
Err(err) => {
errors.push(err);
binary.increment_pointer(2);
}
},
Spanned {
node: Statement::LiteralRelativeAddress(scoped_identifier),
span,
} => match find_address(&scoped_identifier, &definitions, &span) {
Ok((address, other_span)) => {
unused_labels.remove(&scoped_identifier);
let offset = address as isize - binary.get_pointer() as isize - 3;
if offset < -126 || offset > 126 {
errors.push(Error::AddressTooFar {
distance: offset.abs() as usize,
identifier: scoped_identifier.to_string(),
span: span.into(),
other_span: other_span.into(),
});
binary.increment_pointer(2);
} else {
binary.push_byte(LIT);
binary.push_byte(offset as u8);
}
}
Err(err) => {
errors.push(err);
binary.increment_pointer(2);
}
},
Spanned {
node: Statement::LiteralAbsoluteAddress(scoped_identifier),
span,
} => match find_address(&scoped_identifier, &definitions, &span) {
Ok((address, _)) => {
unused_labels.remove(&scoped_identifier);
binary.push_byte(LIT2);
binary.push_short(address);
}
Err(err) => {
errors.push(err);
binary.increment_pointer(3);
}
},
Spanned {
node: Statement::RawAddress(scoped_identifier),
span,
} => match find_address(&scoped_identifier, &definitions, &span) {
Ok((address, _)) => {
unused_labels.remove(&scoped_identifier);
binary.push_short(address);
}
Err(err) => {
errors.push(err);
binary.increment_pointer(2);
}
},
Spanned {
node: Statement::LiteralHexByte(value),
..
} => {
binary.push_byte(LIT);
binary.push_byte(value);
}
Spanned {
node: Statement::LiteralHexShort(value),
..
} => {
binary.push_byte(LIT2);
binary.push_short(value);
}
Spanned {
node: Statement::RawHexByte(value),
..
} => {
binary.push_byte(value);
}
Spanned {
node: Statement::RawHexShort(value),
..
} => {
binary.push_short(value);
}
Spanned {
node: Statement::RawChar(value),
..
} => {
binary.push_byte(value);
}
Spanned {
node: Statement::RawWord(word),
..
} => {
for byte in word {
binary.push_byte(byte);
}
}
}
}
for unused_label_name in unused_labels
.into_iter()
.filter(|scoped_identifier| !scoped_identifier.is_captital())
{
let (_, span) = definitions.labels[&unused_label_name];
warnings.push(Warning::LabelUnused {
name: unused_label_name.to_string(),
span: span.into(),
});
}
for unused_label_name in unused_labels
.into_iter()
.filter(|scoped_identifier| !scoped_identifier.is_captital())
{
let (_, span) = definitions.labels[&unused_label_name];
warnings.push(Warning::LabelUnused {
name: unused_label_name.to_string(),
span: span.into(),
});
}
if errors.is_empty() {
Ok((binary.into(), warnings))
} else {
Err((errors, warnings))
}
if errors.is_empty() {
Ok((binary.into(), warnings))
} else {
Err((errors, warnings))
}
}
fn find_address(
scoped_identifier: &ScopedIdentifier,
definitions: &Definitions,
span: &Span,
scoped_identifier: &ScopedIdentifier,
definitions: &Definitions,
span: &Span,
) -> Result<(u16, Span), Error> {
match definitions.labels.get(scoped_identifier) {
Some((address, span)) => {
return Ok((*address, *span));
}
None => {
return Err(Error::LabelUndefined {
name: scoped_identifier.to_string(),
span: (*span).into(),
});
}
}
match definitions.labels.get(scoped_identifier) {
Some((address, span)) => {
return Ok((*address, *span));
}
None => {
return Err(Error::LabelUndefined {
name: scoped_identifier.to_string(),
span: (*span).into(),
});
}
}
}

View File

@ -1,47 +1,49 @@
#[derive(Debug, Copy, Clone)]
pub(crate) struct Instruction {
pub(crate) instruction_kind: InstructionKind,
pub(crate) keep: bool,
pub(crate) r#return: bool,
pub(crate) short: bool,
pub(crate) instruction_kind: InstructionKind,
pub(crate) keep: bool,
pub(crate) r#return: bool,
pub(crate) short: bool,
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum InstructionKind {
// Stack
BreakOrLiteral = 0x00,
Increment,
Pop,
Duplicate,
NoOperation,
Swap,
Over,
Rotate = 0x07,
// Logic
Equal = 0x08,
NotEqual,
GreaterThan,
LesserThan,
Jump,
JumpCondition,
JumpStash,
Stash = 0x0f,
// Memory
LoadZeroPage = 0x10,
StoreZeroPage,
LoadRelative,
StoreRelative,
LoadAbsolute,
StoreAbsolute,
DeviceIn,
DeviceOut = 0x17,
// Arithmetic
Add = 0x18,
Subtract,
Multiply,
Divide,
And,
Or,
ExclusiveOr,
Shift = 0x1f,
// Stack
Break = 0x00,
Increment,
Pop,
Nip,
Swap,
Rotate,
Duplicate,
Over = 0x07,
// Logic
Equal = 0x08,
NotEqual,
GreaterThan,
LesserThan,
Jump,
JumpCondition,
JumpStash,
Stash = 0x0f,
// Memory
LoadZeroPage = 0x10,
StoreZeroPage,
LoadRelative,
StoreRelative,
LoadAbsolute,
StoreAbsolute,
DeviceIn,
DeviceOut = 0x17,
// Arithmetic
Add = 0x18,
Subtract,
Multiply,
Divide,
And,
Or,
ExclusiveOr,
Shift = 0x1f,
// Special
Literal = 0x80,
}

View File

@ -8,464 +8,465 @@ mod hex_number;
#[derive(Clone)]
pub(crate) enum Word {
Fine {
token: Spanned<Token>,
warnings: Vec<Warning>,
},
Faulty {
errors: Vec<Error>,
warnings: Vec<Warning>,
},
Fine {
token: Spanned<Token>,
warnings: Vec<Warning>,
},
Faulty {
errors: Vec<Error>,
warnings: Vec<Warning>,
},
}
impl Word {
pub(crate) fn new(symbols: &[Spanned<u8>]) -> Self {
debug_assert!({
const WHITESPACES: [u8; 6] = [b' ', b'\t', b'\n', 0x0b, 0x0c, b'\r'];
pub(crate) fn new(symbols: &[Spanned<u8>]) -> Self {
debug_assert!({
const WHITESPACES: [u8; 6] = [b' ', b'\t', b'\n', 0x0b, 0x0c, b'\r'];
let chars: Vec<u8> = symbols.iter().map(|Spanned { node: ch, .. }| *ch).collect();
WHITESPACES.iter().all(|ch| !chars.contains(ch))
});
let chars: Vec<u8> = symbols.iter().map(|Spanned { node: ch, .. }| *ch).collect();
WHITESPACES.iter().all(|ch| !chars.contains(ch))
});
match tokenize(symbols) {
Ok((token, warnings)) => Self::Fine { token, warnings },
Err(error) => Self::Faulty {
errors: vec![error],
warnings: Vec::new(),
},
}
}
match tokenize(symbols) {
Ok((token, warnings)) => Self::Fine { token, warnings },
Err(error) => Self::Faulty {
errors: vec![error],
warnings: Vec::new(),
},
}
}
}
fn tokenize(word: &[Spanned<u8>]) -> Result<(Spanned<Token>, Vec<Warning>), Error> {
match word.first().cloned().unwrap() {
Spanned { node: b'[', span } => {
return Ok((Token::OpeningBracket.spanning(span), Vec::new()))
}
Spanned { node: b']', span } => {
return Ok((Token::ClosingBracket.spanning(span), Vec::new()))
}
Spanned { node: b'{', span } => {
return Ok((Token::OpeningBrace.spanning(span), Vec::new()))
}
Spanned { node: b'}', span } => {
return Ok((Token::ClosingBrace.spanning(span), Vec::new()))
}
Spanned { node: b'%', span } => match parse_macro(span, &word[1..]) {
Ok(name) => {
return Ok((
Token::MacroDefine(name).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
Err(err) => Err(err),
},
Spanned { node: b'|', span } => hex_number::parse_hex_number_unconstrained(&word[1..])
.map_err(|err| match err {
hex_number::Error2::DigitExpected => Error::HexNumberExpected { span: span.into() },
hex_number::Error2::DigitInvalid { digit, span } => Error::HexDigitInvalid {
digit,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: span.into(),
},
hex_number::Error2::TooLong { length } => Error::HexNumberTooLong {
length,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: to_span(&word[1..]).unwrap().into(),
},
})
.map(|value| Token::PadAbsolute(value))
.map(|token| (token.spanning(to_span(word).unwrap()), Vec::new())),
Spanned { node: b'$', span } => hex_number::parse_hex_number_unconstrained(&word[1..])
.map_err(|err| match err {
hex_number::Error2::DigitExpected => Error::HexNumberExpected { span: span.into() },
hex_number::Error2::DigitInvalid { digit, span } => Error::HexDigitInvalid {
digit,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: span.into(),
},
hex_number::Error2::TooLong { length } => Error::HexNumberTooLong {
length,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: to_span(&word[1..]).unwrap().into(),
},
})
.map(|value| Token::PadRelative(value))
.map(|token| (token.spanning(to_span(word).unwrap()), Vec::new())),
Spanned { node: b'@', span } => {
if !word[1..].is_empty() {
if word[1].node != b'&' {
if let Some(position) = word[1..]
.iter()
.map(|Spanned { node: ch, .. }| *ch)
.position(|c| c == b'/')
{
Err(Error::SlashInLabelOrSublabel {
span: word[1 + position].span.into(),
})
} else {
Ok((
Token::LabelDefine(to_string(&word[1..]))
.spanning(to_span(word).unwrap()),
Vec::new(),
))
}
} else {
Err(Error::AmpersandAtTheStartOfLabel {
span: word[1].span.into(),
})
}
} else {
Err(Error::LabelExpected { span: span.into() })
}
}
Spanned { node: b'&', span } => {
if !word[1..].is_empty() {
if let Some(position) = word[1..]
.iter()
.map(|Spanned { node: ch, .. }| *ch)
.position(|c| c == b'/')
{
Err(Error::SlashInLabelOrSublabel {
span: word[1 + position].span.into(),
})
} else {
Ok((
Token::SublabelDefine(to_string(&word[1..]))
.spanning(to_span(word).unwrap()),
Vec::new(),
))
}
} else {
Err(Error::LabelExpected { span: span.into() })
}
}
Spanned { node: b'#', span } => match hex_number::parse_hex_number(&word[1..]) {
Ok(hex_number::HexNumber::Byte(value)) => Ok(Token::LiteralHexByte(value)),
Ok(hex_number::HexNumber::Short(value)) => Ok(Token::LiteralHexShort(value)),
Err(hex_number::Error::DigitExpected) => {
Err(Error::HexNumberOrCharacterExpected { span: span.into() })
}
Err(hex_number::Error::DigitInvalid { digit, span }) => Err(Error::HexDigitInvalid {
digit,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: span.into(),
}),
Err(hex_number::Error::UnevenLength { length: 1 }) => {
Ok(Token::LiteralHexByte(word[1].node as u8))
}
Err(hex_number::Error::UnevenLength { length }) => Err(Error::HexNumberUnevenLength {
length,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: to_span(&word[1..]).unwrap().into(),
}),
Err(hex_number::Error::TooLong { length }) => Err(Error::HexNumberTooLong {
length,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: to_span(&word[1..]).unwrap().into(),
}),
}
.map(|token| (token.spanning(to_span(word).unwrap()), Vec::new())),
Spanned { node: b'.', span } => match parse_identifier(span, &word[1..]) {
Ok(name) => {
return Ok((
Token::LiteralZeroPageAddress(name).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
Err(err) => Err(err),
},
Spanned { node: b',', span } => match parse_identifier(span, &word[1..]) {
Ok(name) => {
return Ok((
Token::LiteralRelativeAddress(name).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
Err(err) => Err(err),
},
Spanned { node: b';', span } => match parse_identifier(span, &word[1..]) {
Ok(name) => {
return Ok((
Token::LiteralAbsoluteAddress(name).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
Err(err) => Err(err),
},
Spanned { node: b':', span } => match parse_identifier(span, &word[1..]) {
Ok(name) => {
return Ok((
Token::RawAddress(name).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
Err(err) => Err(err),
},
Spanned { node: b'\'', span } => {
let bytes: Vec<u8> = to_string(&word[1..]);
match bytes.len() {
0 => Err(Error::CharacterExpected { span: span.into() }),
1 => Ok((
Token::RawChar(bytes[0]).spanning(Span::combine(&span, &word[1].span)),
Vec::new(),
)),
_ => {
let span = to_span(&word[1..]).unwrap();
Err(Error::MoreThanOneByteFound {
bytes,
span: span.into(),
})
}
}
}
Spanned { node: b'"', .. } => {
return Ok((
Token::RawWord(to_string(&word[1..])).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
_ => {
if let Ok(hex_number) = hex_number::parse_hex_number(word) {
return Ok((
match hex_number {
hex_number::HexNumber::Byte(value) => Token::RawHexByte(value),
hex_number::HexNumber::Short(value) => Token::RawHexShort(value),
}
.spanning(to_span(word).unwrap()),
Vec::new(),
));
};
if let Some((instruction, new_warnings)) = parse_instruction(word) {
return Ok((
Token::Instruction(instruction).spanning(to_span(word).unwrap()),
new_warnings,
));
};
return Ok((
Token::MacroInvoke(to_string(word)).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
}
match word.first().cloned().unwrap() {
Spanned { node: b'[', span } => {
return Ok((Token::OpeningBracket.spanning(span), Vec::new()))
}
Spanned { node: b']', span } => {
return Ok((Token::ClosingBracket.spanning(span), Vec::new()))
}
Spanned { node: b'{', span } => {
return Ok((Token::OpeningBrace.spanning(span), Vec::new()))
}
Spanned { node: b'}', span } => {
return Ok((Token::ClosingBrace.spanning(span), Vec::new()))
}
Spanned { node: b'%', span } => match parse_macro(span, &word[1..]) {
Ok(name) => {
return Ok((
Token::MacroDefine(name).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
Err(err) => Err(err),
},
Spanned { node: b'|', span } => hex_number::parse_hex_number_unconstrained(&word[1..])
.map_err(|err| match err {
hex_number::Error2::DigitExpected => Error::HexNumberExpected { span: span.into() },
hex_number::Error2::DigitInvalid { digit, span } => Error::HexDigitInvalid {
digit,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: span.into(),
},
hex_number::Error2::TooLong { length } => Error::HexNumberTooLong {
length,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: to_span(&word[1..]).unwrap().into(),
},
})
.map(|value| Token::PadAbsolute(value))
.map(|token| (token.spanning(to_span(word).unwrap()), Vec::new())),
Spanned { node: b'$', span } => hex_number::parse_hex_number_unconstrained(&word[1..])
.map_err(|err| match err {
hex_number::Error2::DigitExpected => Error::HexNumberExpected { span: span.into() },
hex_number::Error2::DigitInvalid { digit, span } => Error::HexDigitInvalid {
digit,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: span.into(),
},
hex_number::Error2::TooLong { length } => Error::HexNumberTooLong {
length,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: to_span(&word[1..]).unwrap().into(),
},
})
.map(|value| Token::PadRelative(value))
.map(|token| (token.spanning(to_span(word).unwrap()), Vec::new())),
Spanned { node: b'@', span } => {
if !word[1..].is_empty() {
if word[1].node != b'&' {
if let Some(position) = word[1..]
.iter()
.map(|Spanned { node: ch, .. }| *ch)
.position(|c| c == b'/')
{
Err(Error::SlashInLabelOrSublabel {
span: word[1 + position].span.into(),
})
} else {
Ok((
Token::LabelDefine(to_string(&word[1..]))
.spanning(to_span(word).unwrap()),
Vec::new(),
))
}
} else {
Err(Error::AmpersandAtTheStartOfLabel {
span: word[1].span.into(),
})
}
} else {
Err(Error::LabelExpected { span: span.into() })
}
}
Spanned { node: b'&', span } => {
if !word[1..].is_empty() {
if let Some(position) = word[1..]
.iter()
.map(|Spanned { node: ch, .. }| *ch)
.position(|c| c == b'/')
{
Err(Error::SlashInLabelOrSublabel {
span: word[1 + position].span.into(),
})
} else {
Ok((
Token::SublabelDefine(to_string(&word[1..]))
.spanning(to_span(word).unwrap()),
Vec::new(),
))
}
} else {
Err(Error::LabelExpected { span: span.into() })
}
}
Spanned { node: b'#', span } => match hex_number::parse_hex_number(&word[1..]) {
Ok(hex_number::HexNumber::Byte(value)) => Ok(Token::LiteralHexByte(value)),
Ok(hex_number::HexNumber::Short(value)) => Ok(Token::LiteralHexShort(value)),
Err(hex_number::Error::DigitExpected) => {
Err(Error::HexNumberOrCharacterExpected { span: span.into() })
}
Err(hex_number::Error::DigitInvalid { digit, span }) => Err(Error::HexDigitInvalid {
digit,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: span.into(),
}),
Err(hex_number::Error::UnevenLength { length: 1 }) => {
Ok(Token::LiteralHexByte(word[1].node as u8))
}
Err(hex_number::Error::UnevenLength { length }) => Err(Error::HexNumberUnevenLength {
length,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: to_span(&word[1..]).unwrap().into(),
}),
Err(hex_number::Error::TooLong { length }) => Err(Error::HexNumberTooLong {
length,
number: String::from_utf8_lossy(&to_string(&word[1..])).into_owned(),
span: to_span(&word[1..]).unwrap().into(),
}),
}
.map(|token| (token.spanning(to_span(word).unwrap()), Vec::new())),
Spanned { node: b'.', span } => match parse_identifier(span, &word[1..]) {
Ok(name) => {
return Ok((
Token::LiteralZeroPageAddress(name).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
Err(err) => Err(err),
},
Spanned { node: b',', span } => match parse_identifier(span, &word[1..]) {
Ok(name) => {
return Ok((
Token::LiteralRelativeAddress(name).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
Err(err) => Err(err),
},
Spanned { node: b';', span } => match parse_identifier(span, &word[1..]) {
Ok(name) => {
return Ok((
Token::LiteralAbsoluteAddress(name).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
Err(err) => Err(err),
},
Spanned { node: b':', span } => match parse_identifier(span, &word[1..]) {
Ok(name) => {
return Ok((
Token::RawAddress(name).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
Err(err) => Err(err),
},
Spanned { node: b'\'', span } => {
let bytes: Vec<u8> = to_string(&word[1..]);
match bytes.len() {
0 => Err(Error::CharacterExpected { span: span.into() }),
1 => Ok((
Token::RawChar(bytes[0]).spanning(Span::combine(&span, &word[1].span)),
Vec::new(),
)),
_ => {
let span = to_span(&word[1..]).unwrap();
Err(Error::MoreThanOneByteFound {
bytes,
span: span.into(),
})
}
}
}
Spanned { node: b'"', .. } => {
return Ok((
Token::RawWord(to_string(&word[1..])).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
_ => {
if let Ok(hex_number) = hex_number::parse_hex_number(word) {
return Ok((
match hex_number {
hex_number::HexNumber::Byte(value) => Token::RawHexByte(value),
hex_number::HexNumber::Short(value) => Token::RawHexShort(value),
}
.spanning(to_span(word).unwrap()),
Vec::new(),
));
};
if let Some((instruction, new_warnings)) = parse_instruction(word) {
return Ok((
Token::Instruction(instruction).spanning(to_span(word).unwrap()),
new_warnings,
));
};
return Ok((
Token::MacroInvoke(to_string(word)).spanning(to_span(word).unwrap()),
Vec::new(),
));
}
}
}
fn to_string(symbols: &[Spanned<u8>]) -> Vec<u8> {
symbols.iter().map(|Spanned { node: ch, .. }| *ch).collect()
symbols.iter().map(|Spanned { node: ch, .. }| *ch).collect()
}
fn to_span(symbols: &[Spanned<u8>]) -> Option<Span> {
Some(Span::combine(&symbols.first()?.span, &symbols.last()?.span))
Some(Span::combine(&symbols.first()?.span, &symbols.last()?.span))
}
fn parse_macro(rune_span: Span, symbols: &[Spanned<u8>]) -> Result<Vec<u8>, Error> {
if symbols.is_empty() {
return Err(Error::MacroNameExpected {
span: rune_span.into(),
});
}
if symbols.is_empty() {
return Err(Error::MacroNameExpected {
span: rune_span.into(),
});
}
if let Ok(_) = hex_number::parse_hex_number(symbols) {
return Err(Error::MacroCannotBeAHexNumber {
span: to_span(symbols).unwrap().into(),
number: String::from_utf8_lossy(&to_string(symbols)).into_owned(),
});
}
if let Some(_) = parse_instruction(symbols) {
return Err(Error::MacroCannotBeAnInstruction {
span: to_span(symbols).unwrap().into(),
instruction: String::from_utf8_lossy(&to_string(symbols)).into_owned(),
});
}
if let Ok(_) = hex_number::parse_hex_number(symbols) {
return Err(Error::MacroCannotBeAHexNumber {
span: to_span(symbols).unwrap().into(),
number: String::from_utf8_lossy(&to_string(symbols)).into_owned(),
});
}
if let Some(_) = parse_instruction(symbols) {
return Err(Error::MacroCannotBeAnInstruction {
span: to_span(symbols).unwrap().into(),
instruction: String::from_utf8_lossy(&to_string(symbols)).into_owned(),
});
}
Ok(to_string(symbols))
Ok(to_string(symbols))
}
fn parse_identifier(rune_span: Span, symbols: &[Spanned<u8>]) -> Result<Identifier, Error> {
if symbols.is_empty() {
return Err(Error::IdentifierExpected {
span: rune_span.into(),
});
}
if symbols.is_empty() {
return Err(Error::IdentifierExpected {
span: rune_span.into(),
});
}
if let Some(Spanned { node: b'&', span }) = symbols.first() {
let rune_span = Span::combine(&rune_span, &span);
if symbols[1..].is_empty() {
return Err(Error::SublabelExpected {
span: rune_span.into(),
});
}
return Ok(Identifier::Sublabel(to_string(&symbols[1..])));
}
if let Some(Spanned { node: b'&', span }) = symbols.first() {
let rune_span = Span::combine(&rune_span, &span);
if symbols[1..].is_empty() {
return Err(Error::SublabelExpected {
span: rune_span.into(),
});
}
return Ok(Identifier::Sublabel(to_string(&symbols[1..])));
}
match symbols
.iter()
.map(|Spanned { node: ch, .. }| *ch)
.position(|c| c == b'/')
{
Some(position) => {
if let Some(second_position) = symbols[position + 1..]
.iter()
.map(|Spanned { node: ch, .. }| *ch)
.position(|c| c == b'/')
{
return Err(Error::MoreThanOneSlashInIdentifier {
span: symbols[position + 1 + second_position].span.into(),
});
}
match symbols
.iter()
.map(|Spanned { node: ch, .. }| *ch)
.position(|c| c == b'/')
{
Some(position) => {
if let Some(second_position) = symbols[position + 1..]
.iter()
.map(|Spanned { node: ch, .. }| *ch)
.position(|c| c == b'/')
{
return Err(Error::MoreThanOneSlashInIdentifier {
span: symbols[position + 1 + second_position].span.into(),
});
}
let label = {
let label_symbols = &symbols[..position];
if label_symbols.is_empty() {
return Err(Error::LabelExpected {
span: rune_span.into(),
});
}
to_string(label_symbols)
};
let sublabel = {
let sublabel_symbols = &symbols[position + 1..];
if sublabel_symbols.is_empty() {
return Err(Error::SublabelExpected {
span: symbols[position].span.into(),
});
}
to_string(sublabel_symbols)
};
Ok(Identifier::Path(label, sublabel))
}
None => {
if symbols.is_empty() {
return Err(Error::LabelExpected {
span: rune_span.into(),
});
}
Ok(Identifier::Label(to_string(symbols)))
}
}
let label = {
let label_symbols = &symbols[..position];
if label_symbols.is_empty() {
return Err(Error::LabelExpected {
span: rune_span.into(),
});
}
to_string(label_symbols)
};
let sublabel = {
let sublabel_symbols = &symbols[position + 1..];
if sublabel_symbols.is_empty() {
return Err(Error::SublabelExpected {
span: symbols[position].span.into(),
});
}
to_string(sublabel_symbols)
};
Ok(Identifier::Path(label, sublabel))
}
None => {
if symbols.is_empty() {
return Err(Error::LabelExpected {
span: rune_span.into(),
});
}
Ok(Identifier::Label(to_string(symbols)))
}
}
}
/// `symbols` must not be empty.
fn parse_instruction(symbols: &[Spanned<u8>]) -> Option<(Instruction, Vec<Warning>)> {
if symbols.len() < 3 {
return None;
}
if symbols.len() < 3 {
return None;
}
let instruction_kind = match to_string(&symbols[..3]).as_slice() {
b"BRK" | b"LIT" => Some(InstructionKind::BreakOrLiteral),
b"INC" => Some(InstructionKind::Increment),
b"NIP" => Some(InstructionKind::NoOperation),
b"POP" => Some(InstructionKind::Pop),
b"DUP" => Some(InstructionKind::Duplicate),
b"SWP" => Some(InstructionKind::Swap),
b"OVR" => Some(InstructionKind::Over),
b"ROT" => Some(InstructionKind::Rotate),
b"EQU" => Some(InstructionKind::Equal),
b"NEQ" => Some(InstructionKind::NotEqual),
b"GTH" => Some(InstructionKind::GreaterThan),
b"LTH" => Some(InstructionKind::LesserThan),
b"JMP" => Some(InstructionKind::Jump),
b"JCN" => Some(InstructionKind::JumpCondition),
b"JSR" => Some(InstructionKind::JumpStash),
b"STH" => Some(InstructionKind::Stash),
b"LDZ" => Some(InstructionKind::LoadZeroPage),
b"STZ" => Some(InstructionKind::StoreZeroPage),
b"LDR" => Some(InstructionKind::LoadRelative),
b"STR" => Some(InstructionKind::StoreRelative),
b"LDA" => Some(InstructionKind::LoadAbsolute),
b"STA" => Some(InstructionKind::StoreAbsolute),
b"DEI" => Some(InstructionKind::DeviceIn),
b"DEO" => Some(InstructionKind::DeviceOut),
b"ADD" => Some(InstructionKind::Add),
b"SUB" => Some(InstructionKind::Subtract),
b"MUL" => Some(InstructionKind::Multiply),
b"DIV" => Some(InstructionKind::Divide),
b"AND" => Some(InstructionKind::And),
b"ORA" => Some(InstructionKind::Or),
b"EOR" => Some(InstructionKind::ExclusiveOr),
b"SFT" => Some(InstructionKind::Shift),
_ => None,
}?;
let instruction_kind = match to_string(&symbols[..3]).as_slice() {
b"BRK" => Some(InstructionKind::Break),
b"LIT" => Some(InstructionKind::Literal),
b"INC" => Some(InstructionKind::Increment),
b"POP" => Some(InstructionKind::Pop),
b"Nip" => Some(InstructionKind::Nip),
b"SWP" => Some(InstructionKind::Swap),
b"ROT" => Some(InstructionKind::Rotate),
b"DUP" => Some(InstructionKind::Duplicate),
b"OVR" => Some(InstructionKind::Over),
b"EQU" => Some(InstructionKind::Equal),
b"NEQ" => Some(InstructionKind::NotEqual),
b"GTH" => Some(InstructionKind::GreaterThan),
b"LTH" => Some(InstructionKind::LesserThan),
b"JMP" => Some(InstructionKind::Jump),
b"JCN" => Some(InstructionKind::JumpCondition),
b"JSR" => Some(InstructionKind::JumpStash),
b"STH" => Some(InstructionKind::Stash),
b"LDZ" => Some(InstructionKind::LoadZeroPage),
b"STZ" => Some(InstructionKind::StoreZeroPage),
b"LDR" => Some(InstructionKind::LoadRelative),
b"STR" => Some(InstructionKind::StoreRelative),
b"LDA" => Some(InstructionKind::LoadAbsolute),
b"STA" => Some(InstructionKind::StoreAbsolute),
b"DEI" => Some(InstructionKind::DeviceIn),
b"DEO" => Some(InstructionKind::DeviceOut),
b"ADD" => Some(InstructionKind::Add),
b"SUB" => Some(InstructionKind::Subtract),
b"MUL" => Some(InstructionKind::Multiply),
b"DIV" => Some(InstructionKind::Divide),
b"AND" => Some(InstructionKind::And),
b"ORA" => Some(InstructionKind::Or),
b"EOR" => Some(InstructionKind::ExclusiveOr),
b"SFT" => Some(InstructionKind::Shift),
_ => None,
}?;
let mut keep: Option<Span> = None;
let mut r#return: Option<Span> = None;
let mut short: Option<Span> = None;
let mut warnings = Vec::new();
let mut keep: Option<Span> = None;
let mut r#return: Option<Span> = None;
let mut short: Option<Span> = None;
let mut warnings = Vec::new();
for Spanned { node: ch, span } in &symbols[3..] {
match ch {
b'k' => {
if let Some(other_span) = keep {
warnings.push(Warning::InstructionModeDefinedMoreThanOnce {
instruction_mode: 'k',
instruction: String::from_utf8_lossy(&to_string(&symbols[..3]))
.into_owned(),
span: (*span).into(),
other_span: other_span.into(),
});
}
keep = Some(*span);
}
b'r' => {
if let Some(other_span) = r#return {
warnings.push(Warning::InstructionModeDefinedMoreThanOnce {
instruction_mode: 'r',
instruction: String::from_utf8_lossy(&to_string(&symbols[..3]))
.into_owned(),
span: (*span).into(),
other_span: other_span.into(),
});
}
r#return = Some(*span);
}
b'2' => {
if let Some(other_span) = short {
warnings.push(Warning::InstructionModeDefinedMoreThanOnce {
instruction_mode: '2',
instruction: String::from_utf8_lossy(&to_string(&symbols[..3]))
.into_owned(),
span: (*span).into(),
other_span: other_span.into(),
});
}
short = Some(*span);
}
_ => {
return None;
}
}
}
for Spanned { node: ch, span } in &symbols[3..] {
match ch {
b'k' => {
if let Some(other_span) = keep {
warnings.push(Warning::InstructionModeDefinedMoreThanOnce {
instruction_mode: 'k',
instruction: String::from_utf8_lossy(&to_string(&symbols[..3]))
.into_owned(),
span: (*span).into(),
other_span: other_span.into(),
});
}
keep = Some(*span);
}
b'r' => {
if let Some(other_span) = r#return {
warnings.push(Warning::InstructionModeDefinedMoreThanOnce {
instruction_mode: 'r',
instruction: String::from_utf8_lossy(&to_string(&symbols[..3]))
.into_owned(),
span: (*span).into(),
other_span: other_span.into(),
});
}
r#return = Some(*span);
}
b'2' => {
if let Some(other_span) = short {
warnings.push(Warning::InstructionModeDefinedMoreThanOnce {
instruction_mode: '2',
instruction: String::from_utf8_lossy(&to_string(&symbols[..3]))
.into_owned(),
span: (*span).into(),
other_span: other_span.into(),
});
}
short = Some(*span);
}
_ => {
return None;
}
}
}
return Some((
Instruction {
instruction_kind,
keep: keep.is_some(),
r#return: r#return.is_some(),
short: short.is_some(),
},
warnings,
));
return Some((
Instruction {
instruction_kind,
keep: keep.is_some(),
r#return: r#return.is_some(),
short: short.is_some(),
},
warnings,
));
}
impl fmt::Debug for Word {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Word::Fine { token, warnings } => {
let mut debug_struct = f.debug_struct("Fine");
debug_struct.field("token", token);
if !warnings.is_empty() {
debug_struct.field("warnings", warnings);
}
debug_struct.finish()
}
Word::Faulty { errors, warnings } => {
let mut debug_struct = f.debug_struct("Faulty");
debug_struct.field("errors", errors);
if !warnings.is_empty() {
debug_struct.field("warnings", warnings);
}
debug_struct.finish()
}
}
}
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Word::Fine { token, warnings } => {
let mut debug_struct = f.debug_struct("Fine");
debug_struct.field("token", token);
if !warnings.is_empty() {
debug_struct.field("warnings", warnings);
}
debug_struct.finish()
}
Word::Faulty { errors, warnings } => {
let mut debug_struct = f.debug_struct("Faulty");
debug_struct.field("errors", errors);
if !warnings.is_empty() {
debug_struct.field("warnings", warnings);
}
debug_struct.finish()
}
}
}
}