Support binary literals with binary format (#5149)

* 4924 Support binary literals with binary format

* 4924 Support automatic padding for binary literals
This commit is contained in:
merkrafter 2022-04-11 09:58:57 +02:00 committed by GitHub
parent 625e807a35
commit a30930324d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 99 additions and 21 deletions

View file

@ -1074,26 +1074,40 @@ pub fn parse_binary(
working_set: &mut StateWorkingSet, working_set: &mut StateWorkingSet,
span: Span, span: Span,
) -> (Expression, Option<ParseError>) { ) -> (Expression, Option<ParseError>) {
pub fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> { let (hex_value, err) = parse_binary_with_base(working_set, span, 16, 2, b"0x[", b"]");
(0..s.len()) if err.is_some() {
.step_by(2) return parse_binary_with_base(working_set, span, 2, 8, b"0b[", b"]");
.map(|i| u8::from_str_radix(&s[i..i + 2], 16))
.collect()
} }
(hex_value, err)
}
fn parse_binary_with_base(
working_set: &mut StateWorkingSet,
span: Span,
base: u32,
min_digits_per_byte: usize,
prefix: &[u8],
suffix: &[u8],
) -> (Expression, Option<ParseError>) {
let token = working_set.get_span_contents(span); let token = working_set.get_span_contents(span);
if let Some(token) = token.strip_prefix(b"0x[") { if let Some(token) = token.strip_prefix(prefix) {
if let Some(token) = token.strip_suffix(b"]") { if let Some(token) = token.strip_suffix(suffix) {
let (lexed, err) = lex(token, span.start + 3, &[b',', b'\r', b'\n'], &[], true); let (lexed, err) = lex(
token,
span.start + prefix.len(),
&[b',', b'\r', b'\n'],
&[],
true,
);
let mut hex_value = vec![]; let mut binary_value = vec![];
for token in lexed { for token in lexed {
match token.contents { match token.contents {
TokenContents::Item => { TokenContents::Item => {
let contents = working_set.get_span_contents(token.span); let contents = working_set.get_span_contents(token.span);
hex_value.extend_from_slice(contents); binary_value.extend_from_slice(contents);
} }
TokenContents::Pipe => { TokenContents::Pipe => {
return ( return (
@ -1105,20 +1119,21 @@ pub fn parse_binary(
} }
} }
if hex_value.len() % 2 != 0 { let required_padding = (min_digits_per_byte - binary_value.len() % min_digits_per_byte)
return ( % min_digits_per_byte;
garbage(span),
Some(ParseError::IncorrectValue( if required_padding != 0 {
"incomplete binary".into(), binary_value = {
span, let mut tail = binary_value;
"number of binary digits needs to be a multiple of 2".into(), let mut binary_value: Vec<u8> = vec![b'0'; required_padding];
)), binary_value.append(&mut tail);
); binary_value
};
} }
let str = String::from_utf8_lossy(&hex_value).to_string(); let str = String::from_utf8_lossy(&binary_value).to_string();
match decode_hex(&str) { match decode_with_base(&str, base, min_digits_per_byte) {
Ok(v) => { Ok(v) => {
return ( return (
Expression { Expression {
@ -1149,6 +1164,13 @@ pub fn parse_binary(
) )
} }
fn decode_with_base(s: &str, base: u32, digits_per_byte: usize) -> Result<Vec<u8>, ParseIntError> {
(0..s.len())
.step_by(digits_per_byte)
.map(|i| u8::from_str_radix(&s[i..i + digits_per_byte], base))
.collect()
}
pub fn parse_int(token: &[u8], span: Span) -> (Expression, Option<ParseError>) { pub fn parse_int(token: &[u8], span: Span) -> (Expression, Option<ParseError>) {
if let Some(token) = token.strip_prefix(b"0x") { if let Some(token) = token.strip_prefix(b"0x") {
if let Ok(v) = i64::from_str_radix(&String::from_utf8_lossy(token), 16) { if let Ok(v) = i64::from_str_radix(&String::from_utf8_lossy(token), 16) {

View file

@ -61,6 +61,62 @@ pub fn parse_int() {
)) ))
} }
#[test]
pub fn parse_binary_with_hex_format() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let (block, err) = parse(&mut working_set, None, b"0x[13]", true, &[]);
assert!(err.is_none());
assert!(block.len() == 1);
let expressions = &block[0];
assert!(expressions.len() == 1);
assert_eq!(expressions[0].expr, Expr::Binary(vec![0x13]))
}
#[test]
pub fn parse_binary_with_incomplete_hex_format() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let (block, err) = parse(&mut working_set, None, b"0x[3]", true, &[]);
assert!(err.is_none());
assert!(block.len() == 1);
let expressions = &block[0];
assert!(expressions.len() == 1);
assert_eq!(expressions[0].expr, Expr::Binary(vec![0x03]))
}
#[test]
pub fn parse_binary_with_binary_format() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let (block, err) = parse(&mut working_set, None, b"0b[1010 1000]", true, &[]);
assert!(err.is_none());
assert!(block.len() == 1);
let expressions = &block[0];
assert!(expressions.len() == 1);
assert_eq!(expressions[0].expr, Expr::Binary(vec![0b10101000]))
}
#[test]
pub fn parse_binary_with_incomplete_binary_format() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let (block, err) = parse(&mut working_set, None, b"0b[10]", true, &[]);
assert!(err.is_none());
assert!(block.len() == 1);
let expressions = &block[0];
assert!(expressions.len() == 1);
assert_eq!(expressions[0].expr, Expr::Binary(vec![0b00000010]))
}
#[test] #[test]
pub fn parse_call() { pub fn parse_call() {
let engine_state = EngineState::new(); let engine_state = EngineState::new();