diff --git a/vasm/src/vasm_parser.rs b/vasm/src/vasm_parser.rs index e07da73..f4bd92a 100644 --- a/vasm/src/vasm_parser.rs +++ b/vasm/src/vasm_parser.rs @@ -47,10 +47,27 @@ impl<'a> TryFrom> for Instruction { #[derive(Debug, PartialEq, Copy, Clone)] enum Operator { Add, Sub, Mul, Div, Mod } +impl From<&str> for Operator { + fn from(s: &str) -> Self { + use Operator::*; + match s { + "+" => Add, + "-" => Sub, + "*" => Mul, + "/" => Div, + "%" => Mod, + _ => unreachable!() + } + } +} + #[derive(Debug, PartialEq, Clone)] enum Node<'a>{ Number(i32), Label(&'a str), + RelativeLabel(&'a str), + AbsoluteOffset(i32), + RelativeOffset(i32), String(String), Expr(Box>, Vec<(Operator, Node<'a>)>) } @@ -76,6 +93,19 @@ impl<'a> Node<'a> { } Node::String(string) } + + pub fn line_offset(pair: Pair<'a, Rule>) -> Node<'a> { + let outer = pair.as_rule(); + let mut inner = pair.into_inner(); + let sign = inner.next().unwrap().as_str(); + let mut num = i32::from_str(inner.next().unwrap().as_str()).unwrap(); + if sign == "-" { num *= -1 } + match outer { + Rule::relative_line_offset => Node::RelativeOffset(num), + Rule::absolute_line_offset => Node::AbsoluteOffset(num), + _ => unreachable!() + } + } } impl<'a> TryFrom> for Node<'a> { @@ -84,8 +114,17 @@ impl<'a> TryFrom> for Node<'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![])) + let mut iter = pair.into_inner(); + let first = Node::try_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.as_str()); + let node = Node::try_from(rhs)?; + rest.push((op, node)); + } + Ok(Node::Expr(Box::from(first), rest)) } Rule::fact | Rule::number => { Ok(Node::try_from(pair.into_inner().next().unwrap())?) @@ -97,6 +136,9 @@ impl<'a> TryFrom> for Node<'a> { 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!() } } @@ -179,11 +221,7 @@ fn parse_vasm_line(line: &str) -> Result> { 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!() - } + vasm_line = vasm_line.with_argument(Node::try_from(argument_group)?) } } @@ -202,7 +240,19 @@ mod test { } fn op_number(opcode: Opcode, argument: i32) -> VASMLine<'a> { - Self::blank().with_opcode(opcode).with_argument(Node::simple_expr(Node::Number(argument))) + Self::blank().with_opcode(opcode).with_argument(Node::simple_expr(Node::simple_expr(Node::Number(argument)))) + } + + fn op_label(opcode: Opcode, argument: &'a str) -> VASMLine<'a> { + Self::blank().with_opcode(opcode).with_argument(Node::simple_expr(Node::simple_expr(Node::Label(argument)))) + } + + fn op_rel_label(opcode: Opcode, argument: &'a str) -> VASMLine<'a> { + Self::blank().with_opcode(opcode).with_argument(Node::simple_expr(Node::simple_expr(Node::RelativeLabel(argument)))) + } + + fn op_off(opcode: Opcode, argument: Node<'a>) -> VASMLine<'a> { + Self::blank().with_opcode(opcode).with_argument(Node::simple_expr(Node::simple_expr(argument))) } pub fn dir_str(directive: Directive, string: &str) -> VASMLine<'a> { @@ -234,6 +284,43 @@ mod test { assert_eq!(parse_vasm_line(".db \"blah\\twith escapes\\0\""), Ok(VASMLine::dir_str(Db, "blah\twith escapes\0"))) } + #[test] + fn test_exprs() { + assert_eq!(parse_vasm_line("add 2 + 3 * (4 - 5) + 6"), Ok( + VASMLine::op(Add) + .with_argument( + Node::Expr( + Box::from(Node::simple_expr(Node::Number(2))), + vec![ + ( + Operator::Add, + Node::Expr( + Box::from(Node::Number(3)), + vec![ + ( + Operator::Mul, + Node::Expr( + Box::from(Node::simple_expr(Node::Number(4))), + vec![(Operator::Sub, Node::simple_expr(Node::Number(5)))], + ))],)), + ( + Operator::Add, + Node::simple_expr(Node::Number(6)) + )],)))); + } + + #[test] + fn test_expr_labels() { + assert_eq!(parse_vasm_line("loadw foo"), Ok(VASMLine::op_label(Loadw, "foo"))); + assert_eq!(parse_vasm_line("brz @blah"), Ok(VASMLine::op_rel_label(Brz, "blah"))) + } + + #[test] + fn test_expr_offsets() { + assert_eq!(parse_vasm_line("jmp $-2"), Ok(VASMLine::op_off(Jmp, Node::AbsoluteOffset(-2)))); + assert_eq!(parse_vasm_line("brz @+3"), Ok(VASMLine::op_off(Brz, Node::RelativeOffset(3)))) + } + #[test] fn test_parse_labels() { assert_eq!(parse_vasm_line("foo: add"), Ok(VASMLine::op(Add).with_label("foo")));