feat: add macro for ad hoc parsing rules

This commit is contained in:
Nat 2024-12-17 16:24:27 -08:00
parent 551b4468aa
commit 6bec93765a
1 changed files with 35 additions and 37 deletions

View File

@ -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<u16, String> {$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::<u16>().unwrap()
}
fn parse_safely<T: std::str::FromStr>(string: &str) -> Result<T, String> {
string.parse::<T>()
.map_err(|_| format!("Cannot parse number: {t}", t=string))
}
fn stack_bit(token: &str) -> Result<u16, String> {
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<u16,
fn parse_binary_immediate_op(tokens: &Vec<&str>, opcode: u16, signed: bool) -> Result<u16, String> {
assert_argument_count!(tokens, 4);
let signed_immediate = tokens[2].parse::<i16>()
.map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?;
let unsigned_immediate = tokens[2].parse::<u16>()
.map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?;
let signed_immediate = parse_safely::<i16>(tokens[2])?;
let unsigned_immediate = parse_safely::<u16>(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<u16, Stri
assert_argument_count!(tokens, 3);
let dest = stack_bit(tokens[1])?;
let signed_immediate = tokens[2].parse::<i16>()
.map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?;
let unsigned_immediate = tokens[2].parse::<u16>()
.map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?;
let signed_immediate = parse_safely::<i16>(tokens[2])?;
let unsigned_immediate = parse_safely::<u16>(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<u16, String> {
assert_argument_count!(tokens, 4);
let stack = stack_bit(tokens[1])?;
let relative_x = tokens[2].parse::<i16>()
.map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?;
let relative_y = tokens[3].parse::<i16>()
.map_err(|_| format!("Cannot parse number: {t}", t=tokens[2]))?;
let relative_x = parse_safely::<i16>(tokens[2])?;
let relative_y = parse_safely::<i16>(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::<u16>() & 0b111111111))
},
"skim" => {
Ok(opcode<<11 | (parse_safely::<u16>(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),