From 683ff3095e9a24eda93db7f872a63ef2cc690a76 Mon Sep 17 00:00:00 2001 From: Ross Andrews Date: Tue, 15 Mar 2022 22:50:19 -0500 Subject: [PATCH] Big refactoring, invalid assembly is now unrepresentable assembly --- vasm/src/ast.rs | 145 ++++++----------------- vasm/src/lib.rs | 7 ++ vasm/src/main.rs | 11 -- vasm/src/parse_error.rs | 3 +- vasm/src/vasm.pest | 29 +++-- vasm/src/vasm_parser.rs | 250 +++++++++++++++++++++++++--------------- vcore/src/cpu.rs | 14 ++- vcore/src/word.rs | 8 +- 8 files changed, 228 insertions(+), 239 deletions(-) create mode 100644 vasm/src/lib.rs delete mode 100644 vasm/src/main.rs diff --git a/vasm/src/ast.rs b/vasm/src/ast.rs index c1b7d62..8aca42d 100644 --- a/vasm/src/ast.rs +++ b/vasm/src/ast.rs @@ -1,12 +1,10 @@ -use std::convert::TryFrom; use std::str::FromStr; use pest::iterators::Pair; use vcore::opcodes::Opcode; -use crate::parse_error::ParseError; -use crate::vasm_parser::{Rule, VASMParser}; +use crate::vasm_parser::Rule; /// A non-opcode directive to the assembler #[derive(Debug, PartialEq, Copy, Clone)] @@ -28,26 +26,6 @@ impl From> for Directive { } } -/// Either an opcode or a directive; something that lives in the -/// instruction column of a line -#[derive(Debug, PartialEq, Copy, Clone)] -pub enum Instruction { - Directive(Directive), - Opcode(Opcode), -} - -impl<'a> TryFrom> for Instruction { - type Error = ParseError<'a>; - - fn try_from(pair: Pair<'a, Rule>) -> Result { - match pair.as_rule() { - Rule::opcode => Ok(Instruction::Opcode(Opcode::try_from(pair.as_str())?)), - Rule::directive => Ok(Instruction::Directive(Directive::from(pair))), - _ => Err(ParseError::InvalidInstruction(pair.as_str())), - } - } -} - /// One of the five arithmetical operators #[derive(Debug, PartialEq, Copy, Clone)] pub enum Operator { @@ -96,12 +74,12 @@ impl<'a> Node<'a> { for inner in pair.into_inner() { let string_inner = inner.as_str(); match string_inner { - "\\t" => string.push_str("\t"), - "\\r" => string.push_str("\r"), - "\\n" => string.push_str("\n"), - "\\0" => string.push_str("\0"), - "\\\\" => string.push_str("\\"), - "\\\"" => string.push_str("\""), + "\\t" => string.push('\t'), + "\\r" => string.push('\r'), + "\\n" => string.push('\n'), + "\\0" => string.push('\0'), + "\\\\" => string.push('\\'), + "\\\"" => string.push('\"'), _ => string.push_str(string_inner), } } @@ -125,102 +103,55 @@ impl<'a> Node<'a> { } } -impl<'a> TryFrom> for Node<'a> { - type Error = ParseError<'a>; - - /// Attempt to parse a given pair into the `Node` it represents. The result of this is a +impl<'a> From> for Node<'a> { + /// Parse a given pair into the `Node` it represents. The result of this is a /// `Node` that might contain a borrow of part of the original code, but which in no way /// depends on the original pair's object model. - fn try_from(pair: Pair<'a, Rule>) -> Result { + fn from(pair: Pair<'a, Rule>) -> Self { match pair.as_rule() { Rule::expr | Rule::term => { let mut iter = pair.into_inner(); - let first = Node::try_from(iter.next().unwrap())?; + let first = Node::from(iter.next().unwrap()); let mut rest = Vec::<(Operator, Node)>::new(); while let Some(operator) = iter.next() { let rhs = iter.next().unwrap(); let op = Operator::from(operator); - let node = Node::try_from(rhs)?; + let node = Node::from(rhs); rest.push((op, node)); } - Ok(Node::Expr(Box::from(first), rest)) + Node::Expr(Box::from(first), rest) } - Rule::fact | Rule::number => Ok(Node::try_from(pair.into_inner().next().unwrap())?), + Rule::fact | Rule::number => Node::from(pair.into_inner().next().unwrap()), Rule::dec_number | Rule::dec_zero | Rule::neg_number => { - Ok(Node::Number(i32::from_str(pair.as_str()).unwrap())) + Node::Number(i32::from_str(pair.as_str()).unwrap()) } - Rule::hex_number => Ok(Node::Number( - i32::from_str_radix(pair.as_str().get(2..).unwrap(), 16).unwrap(), - )), - Rule::bin_number => Ok(Node::Number( - i32::from_str_radix(pair.as_str().get(2..).unwrap(), 2).unwrap(), - )), - Rule::oct_number => Ok(Node::Number( - i32::from_str_radix(pair.as_str().get(2..).unwrap(), 8).unwrap(), - )), - Rule::string => Ok(Node::string(pair)), - Rule::label => Ok(Node::Label(pair.as_str())), - Rule::relative_label => Ok(Node::RelativeLabel(pair.as_str().get(1..).unwrap())), - Rule::absolute_line_offset | Rule::relative_line_offset => Ok(Node::line_offset(pair)), - _ => todo!(), + Rule::hex_number => { + Node::Number(i32::from_str_radix(pair.as_str().get(2..).unwrap(), 16).unwrap()) + } + Rule::bin_number => { + Node::Number(i32::from_str_radix(pair.as_str().get(2..).unwrap(), 2).unwrap()) + } + Rule::oct_number => { + Node::Number(i32::from_str_radix(pair.as_str().get(2..).unwrap(), 8).unwrap()) + } + Rule::string => Node::string(pair), + Rule::label => Node::Label(pair.as_str()), + Rule::relative_label => Node::RelativeLabel(pair.as_str().get(1..).unwrap()), + Rule::absolute_line_offset | Rule::relative_line_offset => Node::line_offset(pair), + _ => unreachable!(), } } } #[derive(Debug, PartialEq, Clone)] -pub struct VASMLine<'a> { - label: Option<&'a str>, - instruction: Option, - argument: Option>, -} - -impl<'a> VASMLine<'a> { - pub fn blank() -> VASMLine<'a> { - VASMLine { - label: None, - instruction: None, - argument: None, - } - } - - pub fn with_instruction(self, instruction: Instruction) -> VASMLine<'a> { - VASMLine { - label: self.label, - instruction: Some(instruction), - argument: self.argument, - } - } - - pub fn with_opcode(self, opcode: Opcode) -> VASMLine<'a> { - VASMLine { - label: self.label, - instruction: Some(Instruction::Opcode(opcode)), - argument: self.argument, - } - } - - pub fn with_directive(self, directive: Directive) -> VASMLine<'a> { - VASMLine { - label: self.label, - instruction: Some(Instruction::Directive(directive)), - argument: self.argument, - } - } - - pub fn with_label(self, label: &'a str) -> Self { - VASMLine { - label: Some(label), - instruction: self.instruction, - argument: self.argument, - } - } - - pub fn with_argument(self, argument: Node<'a>) -> Self { - VASMLine { - label: self.label, - instruction: self.instruction, - argument: Some(argument), - } - } +pub struct Label<'a>(pub &'a str); + +#[derive(Debug, PartialEq, Clone)] +pub enum VASMLine<'a> { + Instruction(Option>, Opcode, Option>), + Db(Option>, Node<'a>), + Org(Option>, Node<'a>), + Equ(Label<'a>, Node<'a>), + LabelDef(Label<'a>), } diff --git a/vasm/src/lib.rs b/vasm/src/lib.rs new file mode 100644 index 0000000..b7a14f4 --- /dev/null +++ b/vasm/src/lib.rs @@ -0,0 +1,7 @@ +extern crate pest; +#[macro_use] +extern crate pest_derive; + +pub mod ast; +pub mod parse_error; +pub mod vasm_parser; diff --git a/vasm/src/main.rs b/vasm/src/main.rs deleted file mode 100644 index ce35f9c..0000000 --- a/vasm/src/main.rs +++ /dev/null @@ -1,11 +0,0 @@ -extern crate pest; -#[macro_use] -extern crate pest_derive; - -mod vasm_parser; -mod parse_error; -mod ast; - -fn main() { - println!("Hello, world!"); -} diff --git a/vasm/src/parse_error.rs b/vasm/src/parse_error.rs index db35edc..974a469 100644 --- a/vasm/src/parse_error.rs +++ b/vasm/src/parse_error.rs @@ -17,10 +17,9 @@ impl<'a> From> for ParseError<'a> { impl<'a> Display for ParseError<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { use ParseError::*; - use crate::parse_error::ParseError::LineParseFailure; match self { LineParseFailure => write!(f, "Failed to parse line"), - InvalidInstruction(p) => write!(f, "Cannot parse {} as instruction", p) + InvalidInstruction(p) => write!(f, "Cannot parse {} as instruction", p), } } } diff --git a/vasm/src/vasm.pest b/vasm/src/vasm.pest index 7a5eaba..c779eae 100644 --- a/vasm/src/vasm.pest +++ b/vasm/src/vasm.pest @@ -24,6 +24,7 @@ number = { dec_number | hex_number | bin_number | oct_number | dec_zero | neg_nu // a digit: label_char = { ASCII_ALPHA_LOWER | ASCII_ALPHA_UPPER | "_" } label = @{ label_char ~ (label_char | ASCII_DIGIT | "$")* } +label_def = { label ~ ":" } // To make relative jumps easier, we'll also allow an '@' at the start of a label, and interpret // that as meaning "relative to the first byte of this instruction:" @@ -35,13 +36,7 @@ absolute_line_offset = { "$" ~ sign ~ dec_number } // And the relative form of that, @+nnn and @-nnn: relative_line_offset = { "@" ~ sign ~ dec_number } -opcode = @{ ASCII_ALPHA_LOWER+ } // TODO: replace with actual list - -// The assembler will support some directives: -// - .org to set the current address -// - .db to embed some data -// - .equ to define some constants -directive = { ".org" | ".db" | ".equ" } +opcode = @{ ASCII_ALPHA_LOWER+ } // The .equ directive isn't much use without the ability to have expressions based on // symbols, so, a quick arithmetic expression parser: @@ -61,14 +56,16 @@ string = ${ "\"" ~ string_inner* ~ "\"" } // Parsing a line // Normally an assembly line will be a sequence of "label, opcode, argument, comment." -// However, most of these elements are optional. An opcode is only required if an -// argument exists. Comments are already handled by the COMMENT pattern - -// Some sub-patterns for the portions of a line: -label_group = { label ~ ":" } - -// An opcode might be an actual opcode, or a directive -instruction_group = { (opcode | directive) ~ (expr | string)? } +// However, only some combinations of these are valid. Comments are already handled by +// the COMMENT pattern. +// Also, a line might be an actual instruction, or the assembler will support some directives: +// - .org to set the current address +// - .db to embed some data +// - .equ to define some constants +instruction = { label_def? ~ opcode ~ expr? } +db_directive = { label_def? ~ ".db" ~ (expr | string) } +org_directive = { label_def? ~ ".org" ~ expr } +equ_directive = { label_def ~ ".equ" ~ expr } // Finally the entire pattern for an assembly line: -line = { SOI ~ label_group? ~ instruction_group? ~ EOI } \ No newline at end of file +line = { SOI ~ (db_directive | org_directive | equ_directive | instruction | label_def) ~ EOI } \ No newline at end of file diff --git a/vasm/src/vasm_parser.rs b/vasm/src/vasm_parser.rs index 107041f..dcff99f 100644 --- a/vasm/src/vasm_parser.rs +++ b/vasm/src/vasm_parser.rs @@ -1,18 +1,36 @@ use std::convert::TryFrom; -use std::str::FromStr; -use pest::iterators::Pair; use pest::Parser; use vcore::opcodes::Opcode; -use crate::ast::{Directive, Instruction, Node, Operator, VASMLine}; +use crate::ast::{Label, Node, VASMLine}; use crate::parse_error::ParseError; #[derive(Parser)] #[grammar = "vasm.pest"] pub struct VASMParser; +type Pair<'a> = pest::iterators::Pair<'a, Rule>; + +trait Children { + fn first(self) -> Self; + fn only(self) -> Self; +} + +impl<'a> Children for Pair<'a> { + fn first(self) -> Self { + self.into_inner().next().unwrap() + } + + fn only(self) -> Pair<'a> { + let mut iter = self.into_inner(); + let child = iter.next().unwrap(); + debug_assert_eq!(iter.next(), None); + child + } +} + /// Perform tree-shaking on a `Node` by recursively removing single-`Node` exprs. fn shake(node: Node) -> Node { match node { @@ -29,79 +47,81 @@ fn shake(node: Node) -> Node { } } +fn optional_label(pair: Option) -> Option