From 643e8d5749f5329935c4f4d3864e261e9409d17c Mon Sep 17 00:00:00 2001 From: Ross Andrews Date: Sun, 13 Mar 2022 15:44:48 -0500 Subject: [PATCH] More parser implementation --- vasm/src/main.rs | 1 - vasm/src/parse_error.rs | 26 ++++- vasm/src/vasm.pest | 7 +- vasm/src/vasm_line.rs | 61 ------------ vasm/src/vasm_parser.rs | 214 +++++++++++++++++++++++++++++++++++++--- vcore/src/opcodes.rs | 11 ++- 6 files changed, 231 insertions(+), 89 deletions(-) delete mode 100644 vasm/src/vasm_line.rs diff --git a/vasm/src/main.rs b/vasm/src/main.rs index f86d3b9..6993c44 100644 --- a/vasm/src/main.rs +++ b/vasm/src/main.rs @@ -3,7 +3,6 @@ extern crate pest; extern crate pest_derive; mod parse_error; -mod vasm_line; mod vasm_parser; fn main() { diff --git a/vasm/src/parse_error.rs b/vasm/src/parse_error.rs index bf914b0..2f60671 100644 --- a/vasm/src/parse_error.rs +++ b/vasm/src/parse_error.rs @@ -1,10 +1,26 @@ -use vcore::opcodes::InvalidOpcode; +use vcore::opcodes::{InvalidMnemonic}; +use pest::iterators::Pair; +use std::fmt::{Display, Formatter}; +use crate::vasm_parser::Rule; #[derive(Debug, PartialEq)] -pub struct ParseError<'a>(pub &'a str); +pub enum ParseError<'a> { + LineParseFailure, + InvalidInstruction(&'a str) +} -impl<'a> From for ParseError<'a> { - fn from(_: InvalidOpcode) -> Self { - Self("Invalid opcode") +impl<'a> From> for ParseError<'a> { + fn from(err: InvalidMnemonic<'a>) -> Self { + Self::InvalidInstruction(err.0) } } + +impl<'a> Display for ParseError<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + use ParseError::*; + match self { + LineParseFailure => write!(f, "Failed to parse line"), + InvalidInstruction(p) => write!(f, "Cannot parse {} as instruction", p) + } + } +} \ No newline at end of file diff --git a/vasm/src/vasm.pest b/vasm/src/vasm.pest index d2be18c..7a5eaba 100644 --- a/vasm/src/vasm.pest +++ b/vasm/src/vasm.pest @@ -16,8 +16,9 @@ dec_number = @{ ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* } neg_number = ${ "-" ~ dec_number } hex_number = ${ "0x" ~ ASCII_HEX_DIGIT+ } bin_number = ${ "0b" ~ ASCII_BIN_DIGIT+ } +oct_number = ${ "0o" ~ ASCII_OCT_DIGIT+ } dec_zero = @{ "0" } -number = { dec_number | hex_number | bin_number | dec_zero | neg_number } +number = { dec_number | hex_number | bin_number | oct_number | dec_zero | neg_number } // A label can be any sequence of C-identifier-y characters, as long as it doesn't start with // a digit: @@ -63,13 +64,11 @@ string = ${ "\"" ~ string_inner* ~ "\"" } // 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 ~ ":" } -argument_group = { expr | string } // An opcode might be an actual opcode, or a directive -instruction_group = { (opcode | directive) ~ argument_group? } +instruction_group = { (opcode | directive) ~ (expr | string)? } // Finally the entire pattern for an assembly line: line = { SOI ~ label_group? ~ instruction_group? ~ EOI } \ No newline at end of file diff --git a/vasm/src/vasm_line.rs b/vasm/src/vasm_line.rs deleted file mode 100644 index b118fc2..0000000 --- a/vasm/src/vasm_line.rs +++ /dev/null @@ -1,61 +0,0 @@ -use vcore::opcodes::Opcode; - -#[derive(Debug, PartialEq, Copy, Clone)] -pub enum Directive { Db, Equ, Org } - -impl From<&str> for Directive { - fn from(value: &str) -> Self { - use Directive::*; - match value { - ".db" => Db, - ".equ" => Equ, - ".org" => Org, - _ => unreachable!() - } - } -} - -#[derive(Debug, PartialEq, Copy, Clone)] -pub struct VASMLine<'a> { - label: Option<&'a str>, - opcode: Option, - directive: Option -} - -impl<'a> VASMLine<'a> { - pub fn blank() -> VASMLine<'a> { - VASMLine { - label: None, - opcode: None, - directive: None - } - } - - pub fn for_opcode(opcode: Opcode) -> VASMLine<'a> { - Self::blank().with_opcode(opcode) - } - - pub fn with_opcode(self, opcode: Opcode) -> VASMLine<'a> { - VASMLine { - label: self.label, - opcode: Some(opcode), - directive: self.directive - } - } - - pub fn with_directive(self, directive: Directive) -> VASMLine<'a> { - VASMLine { - label: self.label, - opcode: self.opcode, - directive: Some(directive) - } - } - - pub fn with_label(self, label: &'a str) -> Self { - VASMLine { - label: Some(label), - opcode: self.opcode, - directive: self.directive - } - } -} diff --git a/vasm/src/vasm_parser.rs b/vasm/src/vasm_parser.rs index 23950cd..e07da73 100644 --- a/vasm/src/vasm_parser.rs +++ b/vasm/src/vasm_parser.rs @@ -1,18 +1,167 @@ use vcore::opcodes::Opcode; use crate::parse_error::ParseError; -use crate::vasm_line::{Directive, VASMLine}; use pest::{Parser}; use std::convert::TryFrom; +use pest::iterators::Pair; +use std::str::FromStr; #[derive(Parser)] #[grammar = "vasm.pest"] struct VASMParser; +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum Directive { Db, Equ, Org } + +impl From<&str> for Directive { + fn from(value: &str) -> Self { + use Directive::*; + match value { + ".db" => Db, + ".equ" => Equ, + ".org" => Org, + _ => unreachable!() + } + } +} + +#[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.as_str()))), + _ => Err(ParseError::InvalidInstruction(pair.as_str())) + } + } +} + +#[derive(Debug, PartialEq, Copy, Clone)] +enum Operator { Add, Sub, Mul, Div, Mod } + +#[derive(Debug, PartialEq, Clone)] +enum Node<'a>{ + Number(i32), + Label(&'a str), + String(String), + Expr(Box>, Vec<(Operator, Node<'a>)>) +} + +impl<'a> Node<'a> { + pub fn simple_expr(node: Node<'a>) -> Node<'a> { + Node::Expr(Box::from(node), vec![]) + } + + pub fn string(pair: Pair<'a, Rule>) -> Node<'a> { + let mut string = String::with_capacity(pair.as_str().len()); + 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("\""), + _ => string.push_str(string_inner) + } + } + Node::String(string) + } +} + +impl<'a> TryFrom> for Node<'a> { + type Error = ParseError<'a>; + + fn try_from(pair: Pair<'a, Rule>) -> Result { + match pair.as_rule() { + Rule::expr | Rule::term => { + let first = Node::try_from(pair.into_inner().next().unwrap())?; + Ok(Node::Expr(Box::from(first), vec![])) + } + Rule::fact | Rule::number => { + Ok(Node::try_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())) + } + 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)) } + _ => todo!() + } + } +} + +#[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) + } + } +} + fn parse_vasm_line(line: &str) -> Result> { let mut pairs = VASMParser::parse(Rule::line, line) - .map_err(|_| ParseError("Failed to parse line"))? + .map_err(|_| ParseError::LineParseFailure)? .next() .unwrap() .into_inner() @@ -26,13 +175,16 @@ fn parse_vasm_line(line: &str) -> Result> { } if let Some(instruction_group) = pairs.next_if(|pair| pair.as_rule() == Rule::instruction_group) { - let instruction_field = instruction_group.into_inner().next().unwrap(); - vasm_line = match instruction_field.as_rule() { - Rule::opcode => vasm_line.with_opcode(Opcode::try_from(instruction_field.as_str())?), - Rule::directive => vasm_line.with_directive(Directive::from(instruction_field.as_str())), - _ => unreachable!() + let mut pairs = instruction_group.into_inner(); + vasm_line = vasm_line.with_instruction(Instruction::try_from(pairs.next().unwrap())?); + + if let Some(argument_group) = pairs.next() { + vasm_line = match argument_group.as_rule() { + Rule::string => vasm_line.with_argument(Node::try_from(argument_group)?), + Rule::expr => vasm_line.with_argument(Node::try_from(argument_group.into_inner().next().unwrap())?), + _ => unreachable!() + } } - // something something argument here } Ok(vasm_line) @@ -41,23 +193,57 @@ fn parse_vasm_line(line: &str) -> Result> { #[cfg(test)] mod test { use super::*; + use Opcode::*; + use Directive::*; + + impl<'a> VASMLine<'a> { + pub fn op(opcode: Opcode) -> VASMLine<'a> { + Self::blank().with_opcode(opcode) + } + + fn op_number(opcode: Opcode, argument: i32) -> VASMLine<'a> { + Self::blank().with_opcode(opcode).with_argument(Node::simple_expr(Node::Number(argument))) + } + + pub fn dir_str(directive: Directive, string: &str) -> VASMLine<'a> { + Self::blank().with_directive(directive).with_argument(Node::String(string.to_string())) + } + } #[test] fn test_parse() { - assert_eq!(parse_vasm_line("add"), Ok(VASMLine::for_opcode(Opcode::Add))); - assert_eq!(parse_vasm_line("sub"), Ok(VASMLine::for_opcode(Opcode::Sub))); - assert_eq!(parse_vasm_line("blah"), Err(ParseError("Invalid opcode"))); + assert_eq!(parse_vasm_line("add"), Ok(VASMLine::op(Add))); + assert_eq!(parse_vasm_line("sub"), Ok(VASMLine::op(Sub))); + assert_eq!(parse_vasm_line("blah"), Err(ParseError::InvalidInstruction("blah"))); + assert_eq!(parse_vasm_line("47"), Err(ParseError::LineParseFailure)); + } + + #[test] + fn test_numbers() { + assert_eq!(parse_vasm_line("add 45"), Ok(VASMLine::op_number(Add, 45))); + assert_eq!(parse_vasm_line("add 0"), Ok(VASMLine::op_number(Add, 0))); + assert_eq!(parse_vasm_line("add 0x10"), Ok(VASMLine::op_number(Add, 16))); + assert_eq!(parse_vasm_line("add 0b1111"), Ok(VASMLine::op_number(Add, 15))); + assert_eq!(parse_vasm_line("add 0o377"), Ok(VASMLine::op_number(Add, 255))); + assert_eq!(parse_vasm_line("add -17"), Ok(VASMLine::op_number(Add, -17))); + } + + #[test] + fn test_strings() { + assert_eq!(parse_vasm_line(".db \"blah\""), Ok(VASMLine::dir_str(Db, "blah"))); + assert_eq!(parse_vasm_line(".db \"blah\\twith escapes\\0\""), Ok(VASMLine::dir_str(Db, "blah\twith escapes\0"))) } #[test] fn test_parse_labels() { - assert_eq!(parse_vasm_line("foo: add"), Ok(VASMLine::for_opcode(Opcode::Add).with_label("foo"))); + assert_eq!(parse_vasm_line("foo: add"), Ok(VASMLine::op(Add).with_label("foo"))); assert_eq!(parse_vasm_line("bar:"), Ok(VASMLine::blank().with_label("bar"))); + assert_eq!(parse_vasm_line("foo: add 43"), Ok(VASMLine::op_number(Add, 43).with_label("foo"))); } #[test] fn test_parse_directives() { - assert_eq!(parse_vasm_line("foo: .equ"), Ok(VASMLine::blank().with_label("foo").with_directive(Directive::Equ))); - assert_eq!(parse_vasm_line(".db"), Ok(VASMLine::blank().with_directive(Directive::Db))); + assert_eq!(parse_vasm_line("foo: .equ"), Ok(VASMLine::blank().with_label("foo").with_directive(Equ))); + assert_eq!(parse_vasm_line(".db"), Ok(VASMLine::blank().with_directive(Db))); } } diff --git a/vcore/src/opcodes.rs b/vcore/src/opcodes.rs index f4ada51..c4fdc52 100644 --- a/vcore/src/opcodes.rs +++ b/vcore/src/opcodes.rs @@ -59,6 +59,9 @@ impl Display for InvalidOpcode { impl std::error::Error for InvalidOpcode {} +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct InvalidMnemonic<'a>(pub &'a str); + impl Display for Opcode { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { @@ -163,10 +166,10 @@ impl TryFrom for Opcode { } } -impl TryFrom<&str> for Opcode { - type Error = InvalidOpcode; +impl<'a> TryFrom<&'a str> for Opcode { + type Error = InvalidMnemonic<'a>; - fn try_from(value: &str) -> Result { + fn try_from(value: &'a str) -> Result { use Opcode::*; Ok(match value { "nop" => Nop, @@ -212,7 +215,7 @@ impl TryFrom<&str> for Opcode { "popr" => Popr, "peekr" => Peekr, "debug" => Debug, - _ => return Err(InvalidOpcode(255)) + _ => return Err(InvalidMnemonic(value)) }) } }