use trivet::{errors::syntax_error, parser::ParseResult, Parser};
use crate::options::HhhArgs;
fn parse_primitive_ws(parser: &mut Parser, config: &mut HhhArgs) -> ParseResult<i64> {
let is_neg = parser.peek_and_consume('-');
let value = if parser.peek_and_consume('(') {
parser.consume_ws();
let value = parse_top_expr_ws(parser, config)?;
if !parser.peek_and_consume_ws(')') {
return Err(syntax_error(parser.loc(), "Missing closing parenthesis."));
}
value
} else if parser.peek_and_consume('$') {
let name = parser.take_while(|ch| ch.is_alphanumeric() || ch == '_');
let value = match config.get_variable(&name) {
Some(bytes) => {
let len = bytes.len();
let end = bytes.len().min((usize::BITS / 8) as usize);
let start = 0;
let mut newbytes = [0u8; 8];
newbytes[8 - end..8 - start].copy_from_slice(&bytes[len - end..len - start]);
Ok(i64::from_be_bytes(newbytes))
}
None => Err(syntax_error(
parser.loc(),
&format!(
"The variable ${} does not have a value at this point.",
name
),
)),
}?;
parser.consume_ws();
value
} else {
parser.parse_i64_ws()?
};
Ok(if is_neg { -value } else { value })
}
fn parse_exp_expr_ws(parser: &mut Parser, config: &mut HhhArgs) -> ParseResult<i64> {
let mut left = parse_primitive_ws(parser, config)?;
loop {
left = if parser.peek_and_consume_ws('^') {
left ^ parse_primitive_ws(parser, config)?
} else if parser.peek_and_consume_ws('|') {
left | parse_primitive_ws(parser, config)?
} else if parser.peek_and_consume_ws('&') {
left & parse_primitive_ws(parser, config)?
} else if parser.peek_and_consume_str_ws("**") {
let right = parse_exp_expr_ws(parser, config)?;
if right > 0x7fff_ffff {
return Err(syntax_error(
parser.loc(),
&format!(
"Power {} is too large; must be no larger than {}.",
right, 0x7fff_ffff
),
));
}
if right < 0 {
return Err(syntax_error(parser.loc(), "Powers must be nonnegative."));
}
left.saturating_pow(right as u32)
} else if parser.peek_and_consume_str_ws(">>") {
left >> parse_primitive_ws(parser, config)?
} else if parser.peek_and_consume_str_ws("<<") {
left << parse_primitive_ws(parser, config)?
} else {
break;
}
}
Ok(left)
}
fn parse_product_expr_ws(parser: &mut Parser, config: &mut HhhArgs) -> ParseResult<i64> {
let mut left = parse_exp_expr_ws(parser, config)?;
loop {
left = if parser.peek_and_consume_ws('*') {
left * parse_exp_expr_ws(parser, config)?
} else if parser.peek_and_consume_ws('/') {
left / parse_exp_expr_ws(parser, config)?
} else if parser.peek_and_consume_ws('%') {
left % parse_exp_expr_ws(parser, config)?
} else {
break;
}
}
Ok(left)
}
fn parse_top_expr_ws(parser: &mut Parser, config: &mut HhhArgs) -> ParseResult<i64> {
let mut left = parse_product_expr_ws(parser, config)?;
loop {
left = if parser.peek_and_consume_ws('+') {
left + parse_product_expr_ws(parser, config)?
} else if parser.peek_and_consume_ws('-') {
left - parse_product_expr_ws(parser, config)?
} else {
break;
};
}
Ok(left)
}
pub fn parse_expression_ws(parser: &mut Parser, config: &mut HhhArgs) -> ParseResult<i64> {
parser.consume_ws();
parse_top_expr_ws(parser, config)
}
#[cfg(test)]
mod test {
use trivet::{parse_from_string, parser::ParseResult};
use crate::options::HhhArgs;
use super::parse_expression_ws;
#[test]
fn number_test() -> ParseResult<()> {
let mut config = HhhArgs::default();
let mut parser = parse_from_string("0b1110");
let mut result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 0b1110);
parser = parse_from_string("0");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 0);
parser = parse_from_string("1");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 1);
parser = parse_from_string("-1");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, -1);
parser = parse_from_string("(0)");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 0);
parser = parse_from_string("(-1)");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, -1);
parser = parse_from_string("-(1)");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, -1);
parser = parse_from_string("65_535");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 65_535);
parser = parse_from_string("0xffff");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 0xffff);
parser = parse_from_string("");
assert!(parse_expression_ws(&mut parser, &mut config).is_err());
Ok(())
}
#[test]
fn add_sub_test() -> ParseResult<()> {
let mut config = HhhArgs::default();
let mut parser = parse_from_string(" 1 + 1 ");
let mut result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 2);
parser = parse_from_string(" 1 - 1 + 2 - 2 + 3 - 3 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 0);
Ok(())
}
#[test]
fn mul_div_mod_test() -> ParseResult<()> {
let mut config = HhhArgs::default();
let mut parser = parse_from_string(" 1 * 1 ");
let mut result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 1);
parser = parse_from_string(" 1 / 1 * 2 / 2 * 3 / 3 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 1);
parser = parse_from_string(" 1 / 1 * 2 / 2 * 3 / 3 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 1);
parser = parse_from_string(" ( 8 / 2 * 3 ) % 11 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 1);
parser = parse_from_string(" ( 3 % 2 ) * ( 6 % 4 ) ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 2);
Ok(())
}
#[test]
fn pow_test() -> ParseResult<()> {
let mut config = HhhArgs::default();
let mut parser = parse_from_string(" 1 ** 1 ");
let mut result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 1);
parser = parse_from_string(" 1 ** 1 ** 2 ** 3 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 1);
parser = parse_from_string(" (2 ** 3) ** 4 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 4096);
parser = parse_from_string(" 2 ** 2 ** 3 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 256);
parser = parse_from_string(" (2 ** 2) ** 3 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 64);
Ok(())
}
#[test]
fn shift_test() -> ParseResult<()> {
let mut config = HhhArgs::default();
let mut parser = parse_from_string(" 1 << 1 ");
let mut result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 2);
parser = parse_from_string(" 1 << 2 << 3 << 4 >> 4 >> 3 >> 2 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 1);
parser = parse_from_string(" 17 << 2 >> 1 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 34);
Ok(())
}
#[test]
fn bitwise_test() -> ParseResult<()> {
let mut config = HhhArgs::default();
let mut parser = parse_from_string(" 1 ^ 1 ");
let mut result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 0);
parser = parse_from_string(" 0x657463 ^ 0x650460 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 0x007003);
parser = parse_from_string(" 0x21 | 2 & 3 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 3);
Ok(())
}
#[test]
fn pemdas_test() -> ParseResult<()> {
let mut config = HhhArgs::default();
let mut parser = parse_from_string(" 1 + 2 * 3 ");
let mut result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 7);
parser = parse_from_string(" 2 * 3 ** 2 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 18);
parser = parse_from_string(" 2 ** 3 - 1 * 6 ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 2);
Ok(())
}
#[test]
fn error_test() {
let mut config = HhhArgs::default();
let mut parser = parse_from_string(" ");
assert!(parse_expression_ws(&mut parser, &mut config).is_err());
parser = parse_from_string(" ) ");
assert!(parse_expression_ws(&mut parser, &mut config).is_err());
parser = parse_from_string(" ( 1 + ) ");
assert!(parse_expression_ws(&mut parser, &mut config).is_err());
parser = parse_from_string(" 1 ** 2 ** 3 ** 4 ** 5 ");
assert!(parse_expression_ws(&mut parser, &mut config).is_err());
parser = parse_from_string(" 1 ** -6 ");
assert!(parse_expression_ws(&mut parser, &mut config).is_err());
}
#[test]
fn missing_test() {
let mut config = HhhArgs::default();
let mut parser = parse_from_string(" 2 * ");
assert!(parse_expression_ws(&mut parser, &mut config).is_err());
}
#[test]
fn variable_test() -> ParseResult<()> {
let mut config = HhhArgs::default();
config.set_variable("one", &[1]);
config.set_variable("two", &[0, 0, 0, 0, 0, 0, 0, 2]);
config.set_variable("three", &[0, 0, 0, 3]);
let mut parser = parse_from_string(" $three ");
let mut result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 3);
parser = parse_from_string(" $one + $two ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 3);
parser = parse_from_string(" $one + $two * $three ");
result = parse_expression_ws(&mut parser, &mut config)?;
assert_eq!(result, 7);
parser = parse_from_string(" 1 ** $unknown ");
assert!(parse_expression_ws(&mut parser, &mut config).is_err());
parser = parse_from_string(" ( ");
assert!(parse_expression_ws(&mut parser, &mut config).is_err());
parser = parse_from_string(" (7 ");
assert!(parse_expression_ws(&mut parser, &mut config).is_err());
Ok(())
}
}