More parser implementation
This commit is contained in:
@@ -3,7 +3,6 @@ extern crate pest;
|
|||||||
extern crate pest_derive;
|
extern crate pest_derive;
|
||||||
|
|
||||||
mod parse_error;
|
mod parse_error;
|
||||||
mod vasm_line;
|
|
||||||
mod vasm_parser;
|
mod vasm_parser;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
+21
-5
@@ -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)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct ParseError<'a>(pub &'a str);
|
pub enum ParseError<'a> {
|
||||||
|
LineParseFailure,
|
||||||
|
InvalidInstruction(&'a str)
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> From<InvalidOpcode> for ParseError<'a> {
|
impl<'a> From<InvalidMnemonic<'a>> for ParseError<'a> {
|
||||||
fn from(_: InvalidOpcode) -> Self {
|
fn from(err: InvalidMnemonic<'a>) -> Self {
|
||||||
Self("Invalid opcode")
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-4
@@ -16,8 +16,9 @@ dec_number = @{ ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* }
|
|||||||
neg_number = ${ "-" ~ dec_number }
|
neg_number = ${ "-" ~ dec_number }
|
||||||
hex_number = ${ "0x" ~ ASCII_HEX_DIGIT+ }
|
hex_number = ${ "0x" ~ ASCII_HEX_DIGIT+ }
|
||||||
bin_number = ${ "0b" ~ ASCII_BIN_DIGIT+ }
|
bin_number = ${ "0b" ~ ASCII_BIN_DIGIT+ }
|
||||||
|
oct_number = ${ "0o" ~ ASCII_OCT_DIGIT+ }
|
||||||
dec_zero = @{ "0" }
|
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 label can be any sequence of C-identifier-y characters, as long as it doesn't start with
|
||||||
// a digit:
|
// a digit:
|
||||||
@@ -63,13 +64,11 @@ string = ${ "\"" ~ string_inner* ~ "\"" }
|
|||||||
// However, most of these elements are optional. An opcode is only required if an
|
// However, most of these elements are optional. An opcode is only required if an
|
||||||
// argument exists. Comments are already handled by the COMMENT pattern
|
// argument exists. Comments are already handled by the COMMENT pattern
|
||||||
|
|
||||||
|
|
||||||
// Some sub-patterns for the portions of a line:
|
// Some sub-patterns for the portions of a line:
|
||||||
label_group = { label ~ ":" }
|
label_group = { label ~ ":" }
|
||||||
argument_group = { expr | string }
|
|
||||||
|
|
||||||
// An opcode might be an actual opcode, or a directive
|
// 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:
|
// Finally the entire pattern for an assembly line:
|
||||||
line = { SOI ~ label_group? ~ instruction_group? ~ EOI }
|
line = { SOI ~ label_group? ~ instruction_group? ~ EOI }
|
||||||
@@ -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<Opcode>,
|
|
||||||
directive: Option<Directive>
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+199
-13
@@ -1,18 +1,167 @@
|
|||||||
use vcore::opcodes::Opcode;
|
use vcore::opcodes::Opcode;
|
||||||
|
|
||||||
use crate::parse_error::ParseError;
|
use crate::parse_error::ParseError;
|
||||||
use crate::vasm_line::{Directive, VASMLine};
|
|
||||||
|
|
||||||
use pest::{Parser};
|
use pest::{Parser};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
use pest::iterators::Pair;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[grammar = "vasm.pest"]
|
#[grammar = "vasm.pest"]
|
||||||
struct VASMParser;
|
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<Pair<'a, Rule>> for Instruction {
|
||||||
|
type Error = ParseError<'a>;
|
||||||
|
|
||||||
|
fn try_from(pair: Pair<'a, Rule>) -> Result<Self, Self::Error> {
|
||||||
|
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<Node<'a>>, 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<Pair<'a, Rule>> for Node<'a> {
|
||||||
|
type Error = ParseError<'a>;
|
||||||
|
|
||||||
|
fn try_from(pair: Pair<'a, Rule>) -> Result<Self, Self::Error> {
|
||||||
|
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<Instruction>,
|
||||||
|
argument: Option<Node<'a>>
|
||||||
|
}
|
||||||
|
|
||||||
|
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<VASMLine, ParseError<'_>> {
|
fn parse_vasm_line(line: &str) -> Result<VASMLine, ParseError<'_>> {
|
||||||
let mut pairs = VASMParser::parse(Rule::line, line)
|
let mut pairs = VASMParser::parse(Rule::line, line)
|
||||||
.map_err(|_| ParseError("Failed to parse line"))?
|
.map_err(|_| ParseError::LineParseFailure)?
|
||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_inner()
|
.into_inner()
|
||||||
@@ -26,13 +175,16 @@ fn parse_vasm_line(line: &str) -> Result<VASMLine, ParseError<'_>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(instruction_group) = pairs.next_if(|pair| pair.as_rule() == Rule::instruction_group) {
|
if let Some(instruction_group) = pairs.next_if(|pair| pair.as_rule() == Rule::instruction_group) {
|
||||||
let instruction_field = instruction_group.into_inner().next().unwrap();
|
let mut pairs = instruction_group.into_inner();
|
||||||
vasm_line = match instruction_field.as_rule() {
|
vasm_line = vasm_line.with_instruction(Instruction::try_from(pairs.next().unwrap())?);
|
||||||
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())),
|
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!()
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
// something something argument here
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(vasm_line)
|
Ok(vasm_line)
|
||||||
@@ -41,23 +193,57 @@ fn parse_vasm_line(line: &str) -> Result<VASMLine, ParseError<'_>> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn test_parse() {
|
fn test_parse() {
|
||||||
assert_eq!(parse_vasm_line("add"), Ok(VASMLine::for_opcode(Opcode::Add)));
|
assert_eq!(parse_vasm_line("add"), Ok(VASMLine::op(Add)));
|
||||||
assert_eq!(parse_vasm_line("sub"), Ok(VASMLine::for_opcode(Opcode::Sub)));
|
assert_eq!(parse_vasm_line("sub"), Ok(VASMLine::op(Sub)));
|
||||||
assert_eq!(parse_vasm_line("blah"), Err(ParseError("Invalid opcode")));
|
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]
|
#[test]
|
||||||
fn test_parse_labels() {
|
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("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]
|
#[test]
|
||||||
fn test_parse_directives() {
|
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("foo: .equ"), Ok(VASMLine::blank().with_label("foo").with_directive(Equ)));
|
||||||
assert_eq!(parse_vasm_line(".db"), Ok(VASMLine::blank().with_directive(Directive::Db)));
|
assert_eq!(parse_vasm_line(".db"), Ok(VASMLine::blank().with_directive(Db)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ impl Display for InvalidOpcode {
|
|||||||
|
|
||||||
impl std::error::Error 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 {
|
impl Display for Opcode {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
@@ -163,10 +166,10 @@ impl TryFrom<u8> for Opcode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&str> for Opcode {
|
impl<'a> TryFrom<&'a str> for Opcode {
|
||||||
type Error = InvalidOpcode;
|
type Error = InvalidMnemonic<'a>;
|
||||||
|
|
||||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
||||||
use Opcode::*;
|
use Opcode::*;
|
||||||
Ok(match value {
|
Ok(match value {
|
||||||
"nop" => Nop,
|
"nop" => Nop,
|
||||||
@@ -212,7 +215,7 @@ impl TryFrom<&str> for Opcode {
|
|||||||
"popr" => Popr,
|
"popr" => Popr,
|
||||||
"peekr" => Peekr,
|
"peekr" => Peekr,
|
||||||
"debug" => Debug,
|
"debug" => Debug,
|
||||||
_ => return Err(InvalidOpcode(255))
|
_ => return Err(InvalidMnemonic(value))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user