use std::cmp::Ordering;
use crate::directive_def::Arg;
use crate::directive_def::Directive;
use crate::expression::parse_expression_ws;
use crate::options::HhhArgs;
use trivet::errors::syntax_error;
use trivet::errors::ParseResult;
use trivet::parse_from_string;
use trivet::Parser;
pub fn number_to_bytes(number: i64, width: Option<usize>, little_endian: bool) -> Vec<u8> {
let mut bytes = if little_endian {
number.to_le_bytes().to_vec()
} else {
number.to_be_bytes().to_vec()
};
if let Some(width) = width {
let length = bytes.len();
match width.cmp(&length) {
Ordering::Less => {
if little_endian {
bytes = bytes[0..width].to_vec();
} else {
bytes = bytes[length - width..length].to_vec();
}
bytes
}
Ordering::Greater => {
for _ in length..width {
if little_endian {
bytes.push(0u8);
} else {
bytes.insert(0, 0u8);
}
}
bytes
}
Ordering::Equal => bytes,
}
} else {
bytes
}
}
pub fn parse_directive(parser: &mut Parser, config: &mut HhhArgs) -> ParseResult<Directive> {
let mut name = parser.take_while(|ch| ch.is_alphabetic() || ch == '_' || ch == '-');
name += &parser.take_while(|ch| ch.is_alphanumeric() || ch == '_' || ch == '-');
parser.consume_ws();
name = name.replace('-', "_");
if !parser.peek_and_consume('(') {
parser.consume_ws();
return Ok(Directive {
name,
..Default::default()
});
}
parser.consume_ws();
let mut arguments = vec![];
let mut first = true;
while !parser.peek_and_consume(')') {
if first {
first = false;
} else if parser.peek_and_consume(',') {
parser.consume_ws();
} else {
return Err(syntax_error(
parser.loc(),
"Arguments to a directive must be comma-separated.",
));
}
match parser.peek() {
'"' => {
let value = parser.parse_string_match_delimiter_ws()?;
arguments.push(Arg::String(value));
}
'[' => {
parser.consume();
parser.consume_ws();
let number = parse_expression_ws(parser, config)?;
if !parser.peek_and_consume(']') {
return Err(syntax_error(
parser.loc(),
"Missing closing square bracket for expression.",
));
}
if parser.peek_and_consume('/') {
let digits = parser.take_while_unless(|ch| ch.is_ascii_digit(), |ch| ch == '_');
let loc = parser.loc();
let width = match digits.parse::<u8>() {
Ok(value) => value,
Err(error) => {
return Err(syntax_error(loc, &error.to_string()));
}
} as usize;
let next = parser.peek_n(2).to_uppercase();
let little_endian = match next.as_str() {
"LE" => {
parser.consume_n(2);
true
}
"BE" => {
parser.consume_n(2);
false
}
_ => config.little_endian,
};
let bytes = number_to_bytes(number, Some(width), little_endian);
arguments.push(Arg::Bytes(bytes));
} else {
arguments.push(Arg::Number(number));
}
}
',' => {
parser.consume();
return Err(syntax_error(
parser.loc(),
"Expected an argument but did not find one.",
));
}
_ => {
let value = parse_expression_ws(parser, config)?;
arguments.push(Arg::Number(value));
}
}
parser.consume_ws();
}
if arguments.is_empty() {
Ok(Directive {
name,
..Default::default()
})
} else {
Ok(Directive { name, arguments })
}
}
pub fn parse_and_do_directive(text: &str, config: &mut HhhArgs) -> ParseResult<Option<String>> {
let mut parser = parse_from_string(text);
let directive = parse_directive(&mut parser, config)?;
Ok(directive.execute(config))
}
#[cfg(test)]
mod test {
use trivet::{parse_from_string, parser::ParseResult};
use crate::{
directive_def::{Arg, Directive},
options::HhhArgs,
};
use super::{number_to_bytes, parse_directive};
#[test]
fn number_to_bytes_test() {
let mut bytes = number_to_bytes(0, Some(1), false);
assert_eq!(bytes, vec![0u8]);
bytes = number_to_bytes(0, Some(0), false);
assert_eq!(bytes, vec![]);
bytes = number_to_bytes(-1, Some(1), false);
assert_eq!(bytes, vec![0xff]);
bytes = number_to_bytes(0, Some(8), false);
assert_eq!(bytes, vec![0u8; 8]);
bytes = number_to_bytes(0, Some(36), false);
assert_eq!(bytes, vec![0u8; 36]);
bytes = number_to_bytes(0, Some(36), true);
assert_eq!(bytes, vec![0u8; 36]);
bytes = number_to_bytes(-1, Some(8), false);
assert_eq!(bytes, vec![0xff; 8]);
bytes = number_to_bytes(0, None, false);
assert_eq!(bytes, vec![0u8; 8]);
bytes = number_to_bytes(-1, None, false);
assert_eq!(bytes, vec![0xff; 8]);
bytes = number_to_bytes(0x218e4436, Some(6), false);
assert_eq!(bytes, vec![0x00, 0x00, 0x21, 0x8e, 0x44, 0x36]);
bytes = number_to_bytes(0x218e4436, None, false);
assert_eq!(bytes, vec![0x00, 0x00, 0x00, 0x00, 0x21, 0x8e, 0x44, 0x36]);
bytes = number_to_bytes(0x218e4436, Some(6), true);
assert_eq!(bytes, vec![0x36, 0x44, 0x8e, 0x21, 0x00, 0x00]);
bytes = number_to_bytes(0x218e4436, None, true);
assert_eq!(bytes, vec![0x36, 0x44, 0x8e, 0x21, 0x00, 0x00, 0x00, 0x00]);
bytes = number_to_bytes(-0x218e4436, Some(6), true);
assert_eq!(
bytes,
vec![
0xff - 0x36 + 1,
0xff - 0x44,
0xff - 0x8e,
0xff - 0x21,
0xff - 0x00,
0xff - 0x00
]
);
bytes = number_to_bytes(-0x218e4436, None, true);
assert_eq!(
bytes,
vec![
0xff - 0x36 + 1,
0xff - 0x44,
0xff - 0x8e,
0xff - 0x21,
0xff - 0x00,
0xff - 0x00,
0xff - 0x00,
0xff - 0x00
]
);
}
#[test]
fn parse_directive_test() -> ParseResult<()> {
let mut config = HhhArgs::default();
let mut parser = parse_from_string("simple");
let mut result = parse_directive(&mut parser, &mut config)?;
assert_eq!(
result,
Directive {
name: "simple".to_string(),
arguments: vec![],
}
);
config.set_variable("dig", &[0x7f, 0xfe, 0xee, 0x0c, 0xde, 0xad, 0xbe, 0xef]);
parser = parse_from_string("f(1,2,[3/3],[52]/2,$dig,\"dig\")");
result = parse_directive(&mut parser, &mut config)?;
assert_eq!(
result,
Directive {
name: "f".to_string(),
arguments: vec![
Arg::Number(1),
Arg::Number(2),
Arg::Number(1),
Arg::Bytes(vec![0, 52]),
Arg::Number(0x7ffeee0cdeadbeef),
Arg::String("dig".to_string())
],
}
);
parser = parse_from_string("f( [ 6 * 2 ]/3le , [ 2 * 6 ]/4be , ( ( 2 * 3 ) ** 2 ) >> 1 )");
result = parse_directive(&mut parser, &mut config)?;
assert_eq!(
result,
Directive {
name: "f".to_string(),
arguments: vec![
Arg::Bytes(vec![12, 0, 0]),
Arg::Bytes(vec![0, 0, 0, 12]),
Arg::Number(18)
],
}
);
parser = parse_from_string("f()");
result = parse_directive(&mut parser, &mut config)?;
assert_eq!(
result,
Directive {
name: "f".to_string(),
arguments: vec![],
}
);
parser = parse_from_string("f(1");
assert!(parse_directive(&mut parser, &mut config).is_err());
parser = parse_from_string("f(1,");
assert!(parse_directive(&mut parser, &mut config).is_err());
parser = parse_from_string("f(1,\"");
assert!(parse_directive(&mut parser, &mut config).is_err());
parser = parse_from_string("f(1,$");
assert!(parse_directive(&mut parser, &mut config).is_err());
parser = parse_from_string("f(1,$moo, [2*6)");
assert!(parse_directive(&mut parser, &mut config).is_err());
parser = parse_from_string("f(1,$moo,,)");
assert!(parse_directive(&mut parser, &mut config).is_err());
parser = parse_from_string("f([2]/256)");
assert!(parse_directive(&mut parser, &mut config).is_err());
parser = parse_from_string("f([2");
assert!(parse_directive(&mut parser, &mut config).is_err());
parser = parse_from_string("f(,");
assert!(parse_directive(&mut parser, &mut config).is_err());
Ok(())
}
}