From 6bec93765a9fff22c250d455cad984f4b68ba311 Mon Sep 17 00:00:00 2001 From: nat Date: Tue, 17 Dec 2024 16:24:27 -0800 Subject: [PATCH] feat: add macro for ad hoc parsing rules --- src/bin/ponderosa-asm.rs | 72 +++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/src/bin/ponderosa-asm.rs b/src/bin/ponderosa-asm.rs index 9459417..7c0047c 100644 --- a/src/bin/ponderosa-asm.rs +++ b/src/bin/ponderosa-asm.rs @@ -7,6 +7,20 @@ use std::collections::{HashMap, BTreeMap, VecDeque}; use ponderosa::state::{Land, Rule}; +macro_rules! ad_hoc_rule { + ($tokens:expr, $argument_count:expr, $body:block) => { + if $tokens.len() < $argument_count { + Err("Too few arguments".to_owned()) + } else if $tokens.len() > $argument_count { + Err("Too many arguments".to_owned()) + } else { + #[allow(unused)] + let parse_rule = |tokens: &Vec<&str>| -> Result {$body}; + parse_rule($tokens) + } + }; +} + macro_rules! assert_argument_count { ($tokens:expr, $count:expr) => { if $tokens.len() < $count { @@ -17,20 +31,17 @@ macro_rules! assert_argument_count { return Err("Too many arguments".to_owned()); } }; - - ($tokens:expr, $count:expr, $body:block) => { - if $tokens.len() < $count { - Err("Too few arguments".to_owned()); - } else if $tokens.len() > $count { - Err("Too many arguments".to_owned()); - } else $body - }; } fn parse_number(string: &str) -> u16 { string.parse::().unwrap() } +fn parse_safely(string: &str) -> Result { + string.parse::() + .map_err(|_| format!("Cannot parse number: {t}", t=string)) +} + fn stack_bit(token: &str) -> Result { let c = token.as_bytes()[0] as char; match c { @@ -54,11 +65,8 @@ fn parse_binary_op(tokens: &Vec<&str>, opcode: u16, signed: bool) -> Result, opcode: u16, signed: bool) -> Result { assert_argument_count!(tokens, 4); - let signed_immediate = tokens[2].parse::() - .map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?; - - let unsigned_immediate = tokens[2].parse::() - .map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?; + let signed_immediate = parse_safely::(tokens[2])?; + let unsigned_immediate = parse_safely::(tokens[2])?; if signed { if signed_immediate < -32 || signed_immediate > 31 { @@ -82,11 +90,8 @@ fn parse_push(tokens: &Vec<&str>, opcode: u16, signed: bool) -> Result() - .map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?; - - let unsigned_immediate = tokens[2].parse::() - .map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?; + let signed_immediate = parse_safely::(tokens[2])?; + let unsigned_immediate = parse_safely::(tokens[2])?; let immediate = if signed { signed_immediate as u16 } else { unsigned_immediate }; @@ -111,11 +116,8 @@ fn parse_forestry_op(tokens: &Vec<&str>, opcode: u16) -> Result { assert_argument_count!(tokens, 4); let stack = stack_bit(tokens[1])?; - let relative_x = tokens[2].parse::() - .map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?; - - let relative_y = tokens[3].parse::() - .map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?; + let relative_x = parse_safely::(tokens[2])?; + let relative_y = parse_safely::(tokens[3])?; if relative_x > 15 || relative_x < -16 { Err(format!("Cannot reach cell at {relative_x} along the X-axis; trees may only access cells in the range of -16 to 15")) @@ -210,7 +212,7 @@ fn main() -> io::Result<()> { "mult" => parse_binary_op(&tokens, 0b00111, true), "div" => parse_binary_op(&tokens, 0b01000, true), "slt" => parse_binary_op(&tokens, 0b01001, true), - "not" => assert_argument_count!(tokens, 3, { + "not" => ad_hoc_rule!(&tokens, 3, { let opcode = 0b10010; let (src, dest) = (stack_bit(tokens[1])?, stack_bit(tokens[2])?); Ok(opcode<<11 & src<<9 & dest<<8) @@ -244,23 +246,20 @@ fn main() -> io::Result<()> { "swap" => parse_stack_op(&tokens, 0b01111), "rotate" => parse_stack_op(&tokens, 0b10000), "flip" => parse_stack_op(&tokens, 0b10001), - "clear" => { + "clear" => ad_hoc_rule!(&tokens, 2, { let opcode = 0b10010; - assert_argument_count!(tokens, 2); let to_clear = stack_bit(tokens[1])?; Ok(opcode<<11 | to_clear<<10) - }, + }), - "skip" => { + "skip" => ad_hoc_rule!(&tokens, 2, { let opcode = 0b10011; - assert_argument_count!(tokens, 2); - Ok(opcode<<11 | (tokens[1].parse::() & 0b111111111)) - }, - "skim" => { + Ok(opcode<<11 | (parse_safely::(tokens[1])? & 0b111111111)) + }), + "skim" => ad_hoc_rule!(&tokens, 2, { let opcode = 0b10100; - assert_argument_count!(tokens, 2); Ok(opcode<<11 | stack_bit(tokens[1])? << 9) - }, + }), "sow" => parse_forestry_op(&tokens, 0b10101), "reap" => parse_forestry_op(&tokens, 0b10110), @@ -268,11 +267,10 @@ fn main() -> io::Result<()> { "replant" => parse_forestry_op(&tokens, 0b11000), "yield" => Ok(0b11001_0_00000_00000), "compost" => Ok(0b11010_0_00000_00000), - "define" => { + "define" => ad_hoc_rule!(&tokens, 2, { let opcode = 0b11011; - assert_argument_count!(tokens, 2); Ok(opcode<<11 | stack_bit(tokens[1])? << 10) - }, + }), "wait" => parse_forestry_op(&tokens, 0b11100), "signal" => parse_forestry_op(&tokens, 0b11101),