2022-03-12 15:39:29 -06:00
|
|
|
use std::convert::TryFrom;
|
|
|
|
|
|
2022-03-14 01:04:08 -05:00
|
|
|
use pest::Parser;
|
2022-03-13 15:44:48 -05:00
|
|
|
|
2022-03-14 01:04:08 -05:00
|
|
|
use vcore::opcodes::Opcode;
|
2022-03-13 15:44:48 -05:00
|
|
|
|
2022-03-15 22:50:19 -05:00
|
|
|
use crate::ast::{Label, Node, VASMLine};
|
2022-03-14 19:13:14 -05:00
|
|
|
use crate::parse_error::ParseError;
|
2022-03-13 17:01:43 -05:00
|
|
|
|
2022-03-14 01:04:08 -05:00
|
|
|
#[derive(Parser)]
|
|
|
|
|
#[grammar = "vasm.pest"]
|
|
|
|
|
pub struct VASMParser;
|
|
|
|
|
|
2022-03-15 22:50:19 -05:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-14 01:04:08 -05:00
|
|
|
/// Perform tree-shaking on a `Node` by recursively removing single-`Node` exprs.
|
|
|
|
|
fn shake(node: Node) -> Node {
|
|
|
|
|
match node {
|
|
|
|
|
Node::Expr(first, rest) => {
|
|
|
|
|
let shaken_car = shake(*first);
|
|
|
|
|
if rest.is_empty() {
|
|
|
|
|
shaken_car
|
|
|
|
|
} else {
|
|
|
|
|
let shaken_cdr = rest.into_iter().map(|(op, n)| (op, shake(n))).collect();
|
|
|
|
|
Node::Expr(Box::from(shaken_car), shaken_cdr)
|
2022-03-13 15:44:48 -05:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-14 19:13:14 -05:00
|
|
|
_ => node,
|
2022-03-13 15:44:48 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-15 22:50:19 -05:00
|
|
|
fn optional_label(pair: Option<Pair>) -> Option<Label> {
|
|
|
|
|
pair.map(|label| Label(label.only().as_str()))
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-14 01:04:08 -05:00
|
|
|
pub fn parse_vasm_line(line: &str) -> Result<VASMLine, ParseError<'_>> {
|
2022-03-15 22:50:19 -05:00
|
|
|
let line = VASMParser::parse(Rule::line, line)
|
2022-03-13 15:44:48 -05:00
|
|
|
.map_err(|_| ParseError::LineParseFailure)?
|
2022-03-12 15:39:29 -06:00
|
|
|
.next()
|
|
|
|
|
.unwrap()
|
2022-03-15 22:50:19 -05:00
|
|
|
.first();
|
2022-03-12 15:39:29 -06:00
|
|
|
|
2022-03-15 22:50:19 -05:00
|
|
|
match line.as_rule() {
|
|
|
|
|
Rule::instruction => {
|
|
|
|
|
let mut iter = line.into_inner().peekable();
|
|
|
|
|
let label = optional_label(iter.next_if(|pair| pair.as_rule() == Rule::label_def));
|
|
|
|
|
let opcode = Opcode::try_from(
|
|
|
|
|
iter.next_if(|pair| pair.as_rule() == Rule::opcode)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.as_str(),
|
|
|
|
|
)?;
|
|
|
|
|
let argument = iter
|
|
|
|
|
.next_if(|pair| pair.as_rule() == Rule::expr)
|
|
|
|
|
.map(|expr| shake(Node::from(expr)));
|
|
|
|
|
return Ok(VASMLine::Instruction(label, opcode, argument));
|
|
|
|
|
}
|
|
|
|
|
Rule::db_directive => {
|
|
|
|
|
let mut iter = line.into_inner().peekable();
|
|
|
|
|
let label = optional_label(iter.next_if(|pair| pair.as_rule() == Rule::label_def));
|
|
|
|
|
let argument = shake(Node::from(iter.next().unwrap()));
|
|
|
|
|
return Ok(VASMLine::Db(label, argument));
|
|
|
|
|
}
|
|
|
|
|
Rule::org_directive => {
|
|
|
|
|
let mut iter = line.into_inner().peekable();
|
|
|
|
|
let label = optional_label(iter.next_if(|pair| pair.as_rule() == Rule::label_def));
|
|
|
|
|
let argument = shake(Node::from(iter.next().unwrap()));
|
|
|
|
|
return Ok(VASMLine::Org(label, argument));
|
|
|
|
|
}
|
|
|
|
|
Rule::equ_directive => {
|
|
|
|
|
let mut iter = line.into_inner();
|
|
|
|
|
let label = Label(iter.next().unwrap().only().as_str());
|
|
|
|
|
let argument = shake(Node::from(iter.next().unwrap()));
|
|
|
|
|
return Ok(VASMLine::Equ(label, argument));
|
2022-03-12 15:39:29 -06:00
|
|
|
}
|
2022-03-15 22:50:19 -05:00
|
|
|
Rule::label_def => {
|
|
|
|
|
return Ok(VASMLine::LabelDef(Label(line.only().as_str())));
|
|
|
|
|
}
|
|
|
|
|
_ => unreachable!(),
|
2022-03-12 15:39:29 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod test {
|
2022-03-14 01:04:08 -05:00
|
|
|
use vcore::opcodes::Opcode::*;
|
|
|
|
|
|
2022-03-14 19:13:14 -05:00
|
|
|
use super::*;
|
2022-03-15 22:50:19 -05:00
|
|
|
use crate::ast::Operator;
|
2022-03-13 15:44:48 -05:00
|
|
|
|
2022-03-15 22:50:19 -05:00
|
|
|
fn number(number: i32) -> Node<'static> {
|
|
|
|
|
Node::Number(number)
|
|
|
|
|
}
|
2022-03-13 15:44:48 -05:00
|
|
|
|
2022-03-15 22:50:19 -05:00
|
|
|
fn string(s: &str) -> Node<'static> {
|
|
|
|
|
Node::String(s.to_string())
|
2022-03-13 15:44:48 -05:00
|
|
|
}
|
2022-03-12 15:39:29 -06:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_parse() {
|
2022-03-15 22:50:19 -05:00
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("add"),
|
|
|
|
|
Ok(VASMLine::Instruction(None, Add, None))
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("sub"),
|
|
|
|
|
Ok(VASMLine::Instruction(None, Sub, None))
|
|
|
|
|
);
|
2022-03-14 19:13:14 -05:00
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("blah"),
|
|
|
|
|
Err(ParseError::InvalidInstruction("blah"))
|
|
|
|
|
);
|
2022-03-13 15:44:48 -05:00
|
|
|
assert_eq!(parse_vasm_line("47"), Err(ParseError::LineParseFailure));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_numbers() {
|
2022-03-15 22:50:19 -05:00
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("add 45"),
|
|
|
|
|
Ok(VASMLine::Instruction(None, Add, Some(number(45))))
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("blah: add 45"),
|
|
|
|
|
Ok(VASMLine::Instruction(
|
|
|
|
|
Some(Label("blah")),
|
|
|
|
|
Add,
|
|
|
|
|
Some(number(45))
|
|
|
|
|
))
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("add 0"),
|
|
|
|
|
Ok(VASMLine::Instruction(None, Add, Some(number(0))))
|
|
|
|
|
);
|
2022-03-14 19:13:14 -05:00
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("add 0x10"),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::Instruction(None, Add, Some(number(16))))
|
2022-03-14 19:13:14 -05:00
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("add 0b1111"),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::Instruction(None, Add, Some(number(15))))
|
2022-03-14 19:13:14 -05:00
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("add 0o377"),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::Instruction(None, Add, Some(number(255))))
|
2022-03-14 19:13:14 -05:00
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("add -17"),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::Instruction(None, Add, Some(number(-17))))
|
2022-03-14 19:13:14 -05:00
|
|
|
);
|
2022-03-13 15:44:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2022-03-15 22:50:19 -05:00
|
|
|
fn test_dbs() {
|
2022-03-14 19:13:14 -05:00
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line(".db \"blah\""),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::Db(None, string("blah")))
|
2022-03-14 19:13:14 -05:00
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line(".db \"blah\\twith escapes\\0\""),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::Db(None, string("blah\twith escapes\0")))
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("foo: .db 47"),
|
|
|
|
|
Ok(VASMLine::Db(Some(Label("foo")), number(47)))
|
|
|
|
|
);
|
2022-03-12 15:39:29 -06:00
|
|
|
}
|
|
|
|
|
|
2022-03-13 17:01:43 -05:00
|
|
|
#[test]
|
|
|
|
|
fn test_exprs() {
|
2022-03-14 19:13:14 -05:00
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("add 2 + 3 * (4 - 5) + 6"),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::Instruction(
|
|
|
|
|
None,
|
|
|
|
|
Add,
|
|
|
|
|
Some(Node::Expr(
|
|
|
|
|
Box::from(Node::Number(2)),
|
|
|
|
|
vec![
|
|
|
|
|
(
|
|
|
|
|
Operator::Add,
|
|
|
|
|
Node::Expr(
|
|
|
|
|
Box::from(Node::Number(3)),
|
|
|
|
|
vec![(
|
|
|
|
|
Operator::Mul,
|
|
|
|
|
Node::Expr(
|
|
|
|
|
Box::from(Node::Number(4)),
|
|
|
|
|
vec![(Operator::Sub, Node::Number(5))],
|
|
|
|
|
)
|
|
|
|
|
)],
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
(Operator::Add, Node::Number(6))
|
|
|
|
|
],
|
|
|
|
|
))
|
|
|
|
|
))
|
2022-03-14 19:13:14 -05:00
|
|
|
);
|
2022-03-13 17:01:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_expr_labels() {
|
2022-03-14 19:13:14 -05:00
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("loadw foo"),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::Instruction(None, Loadw, Some(Node::Label("foo"))))
|
2022-03-14 19:13:14 -05:00
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("brz @blah"),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::Instruction(
|
|
|
|
|
None,
|
|
|
|
|
Brz,
|
|
|
|
|
Some(Node::RelativeLabel("blah"))
|
|
|
|
|
))
|
2022-03-14 19:13:14 -05:00
|
|
|
)
|
2022-03-13 17:01:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_expr_offsets() {
|
2022-03-14 19:13:14 -05:00
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("jmp $-2"),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::Instruction(
|
|
|
|
|
None,
|
|
|
|
|
Jmp,
|
|
|
|
|
Some(Node::AbsoluteOffset(-2))
|
|
|
|
|
))
|
2022-03-14 19:13:14 -05:00
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("brz @+3"),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::Instruction(
|
|
|
|
|
None,
|
|
|
|
|
Brz,
|
|
|
|
|
Some(Node::RelativeOffset(3))
|
|
|
|
|
))
|
2022-03-14 19:13:14 -05:00
|
|
|
)
|
2022-03-13 17:01:43 -05:00
|
|
|
}
|
|
|
|
|
|
2022-03-12 15:39:29 -06:00
|
|
|
#[test]
|
|
|
|
|
fn test_parse_labels() {
|
2022-03-14 19:13:14 -05:00
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("foo: add"),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::Instruction(Some(Label("foo")), Add, None))
|
2022-03-14 19:13:14 -05:00
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("bar:"),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::LabelDef(Label("bar")))
|
2022-03-14 19:13:14 -05:00
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_vasm_line("foo: add 43"),
|
2022-03-15 22:50:19 -05:00
|
|
|
Ok(VASMLine::Instruction(
|
|
|
|
|
Some(Label("foo")),
|
|
|
|
|
Add,
|
|
|
|
|
Some(number(43))
|
|
|
|
|
))
|
2022-03-14 19:13:14 -05:00
|
|
|
);
|
2022-03-12 15:39:29 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_parse_directives() {
|
2022-03-14 19:13:14 -05:00
|
|
|
assert_eq!(
|
2022-03-15 22:50:19 -05:00
|
|
|
parse_vasm_line("foo: .equ 47"),
|
|
|
|
|
Ok(VASMLine::Equ(Label("foo"), number(47)))
|
2022-03-14 19:13:14 -05:00
|
|
|
);
|
|
|
|
|
assert_eq!(
|
2022-03-15 22:50:19 -05:00
|
|
|
parse_vasm_line(".org 0x400"),
|
|
|
|
|
Ok(VASMLine::Org(None, number(1024)))
|
2022-03-14 19:13:14 -05:00
|
|
|
);
|
2022-03-12 15:39:29 -06:00
|
|
|
}
|
|
|
|
|
}
|