use nom::{IResult, Parser, branch::alt, bytes::complete::tag};
use crate::parser::ast::{Endianness, TypeKind};
pub fn parse_type_keyword(input: &str) -> IResult<&str, &str> {
alt((
alt((
tag("ubequad"),
tag("ulequad"),
tag("uquad"),
tag("bequad"),
tag("lequad"),
tag("quad"),
)),
alt((
tag("ubelong"),
tag("ulelong"),
tag("ulong"),
tag("belong"),
tag("lelong"),
tag("long"),
)),
alt((
tag("ubeshort"),
tag("uleshort"),
tag("ushort"),
tag("beshort"),
tag("leshort"),
tag("short"),
)),
alt((tag("ubyte"), tag("byte"))),
alt((
tag("bedouble"),
tag("ledouble"),
tag("double"),
tag("befloat"),
tag("lefloat"),
tag("float"),
)),
tag("string"),
))
.parse(input)
}
#[must_use]
pub fn type_keyword_to_kind(type_name: &str) -> TypeKind {
match type_name {
"byte" => TypeKind::Byte { signed: true },
"ubyte" => TypeKind::Byte { signed: false },
"short" => TypeKind::Short {
endian: Endianness::Native,
signed: true,
},
"ushort" => TypeKind::Short {
endian: Endianness::Native,
signed: false,
},
"leshort" => TypeKind::Short {
endian: Endianness::Little,
signed: true,
},
"uleshort" => TypeKind::Short {
endian: Endianness::Little,
signed: false,
},
"beshort" => TypeKind::Short {
endian: Endianness::Big,
signed: true,
},
"ubeshort" => TypeKind::Short {
endian: Endianness::Big,
signed: false,
},
"long" => TypeKind::Long {
endian: Endianness::Native,
signed: true,
},
"ulong" => TypeKind::Long {
endian: Endianness::Native,
signed: false,
},
"lelong" => TypeKind::Long {
endian: Endianness::Little,
signed: true,
},
"ulelong" => TypeKind::Long {
endian: Endianness::Little,
signed: false,
},
"belong" => TypeKind::Long {
endian: Endianness::Big,
signed: true,
},
"ubelong" => TypeKind::Long {
endian: Endianness::Big,
signed: false,
},
"quad" => TypeKind::Quad {
endian: Endianness::Native,
signed: true,
},
"uquad" => TypeKind::Quad {
endian: Endianness::Native,
signed: false,
},
"lequad" => TypeKind::Quad {
endian: Endianness::Little,
signed: true,
},
"ulequad" => TypeKind::Quad {
endian: Endianness::Little,
signed: false,
},
"bequad" => TypeKind::Quad {
endian: Endianness::Big,
signed: true,
},
"ubequad" => TypeKind::Quad {
endian: Endianness::Big,
signed: false,
},
"float" => TypeKind::Float {
endian: Endianness::Native,
},
"befloat" => TypeKind::Float {
endian: Endianness::Big,
},
"lefloat" => TypeKind::Float {
endian: Endianness::Little,
},
"double" => TypeKind::Double {
endian: Endianness::Native,
},
"bedouble" => TypeKind::Double {
endian: Endianness::Big,
},
"ledouble" => TypeKind::Double {
endian: Endianness::Little,
},
"string" => TypeKind::String { max_length: None },
_ => unreachable!("type_keyword_to_kind called with unknown type: {type_name}"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::ast::Endianness;
#[test]
fn test_parse_type_keyword_byte_variants() {
assert_eq!(parse_type_keyword("byte rest"), Ok((" rest", "byte")));
assert_eq!(parse_type_keyword("ubyte rest"), Ok((" rest", "ubyte")));
}
#[test]
fn test_parse_type_keyword_short_variants() {
let cases = [
("short", "short"),
("ushort", "ushort"),
("leshort", "leshort"),
("uleshort", "uleshort"),
("beshort", "beshort"),
("ubeshort", "ubeshort"),
];
for (input, expected) in cases {
let input_with_rest = format!("{input} rest");
let (rest, keyword) = parse_type_keyword(&input_with_rest).unwrap();
assert_eq!(keyword, expected, "Failed for input: {input}");
assert_eq!(rest, " rest", "Wrong remaining for input: {input}");
}
}
#[test]
fn test_parse_type_keyword_long_variants() {
let cases = ["long", "ulong", "lelong", "ulelong", "belong", "ubelong"];
for input in cases {
let input_with_rest = format!("{input} rest");
let (rest, keyword) = parse_type_keyword(&input_with_rest).unwrap();
assert_eq!(keyword, input, "Failed for: {input}");
assert_eq!(rest, " rest");
}
}
#[test]
fn test_parse_type_keyword_quad_variants() {
let cases = ["quad", "uquad", "lequad", "ulequad", "bequad", "ubequad"];
for input in cases {
let input_with_rest = format!("{input} rest");
let (rest, keyword) = parse_type_keyword(&input_with_rest).unwrap();
assert_eq!(keyword, input, "Failed for: {input}");
assert_eq!(rest, " rest");
}
}
#[test]
fn test_parse_type_keyword_string() {
assert_eq!(parse_type_keyword("string rest"), Ok((" rest", "string")));
}
#[test]
fn test_parse_type_keyword_unknown() {
assert!(parse_type_keyword("unknown rest").is_err());
}
#[test]
fn test_parse_type_keyword_empty() {
assert!(parse_type_keyword("").is_err());
}
#[test]
fn test_type_keyword_to_kind_byte() {
assert_eq!(
type_keyword_to_kind("byte"),
TypeKind::Byte { signed: true }
);
assert_eq!(
type_keyword_to_kind("ubyte"),
TypeKind::Byte { signed: false }
);
}
#[test]
fn test_type_keyword_to_kind_short_endianness() {
assert_eq!(
type_keyword_to_kind("short"),
TypeKind::Short {
endian: Endianness::Native,
signed: true
}
);
assert_eq!(
type_keyword_to_kind("leshort"),
TypeKind::Short {
endian: Endianness::Little,
signed: true
}
);
assert_eq!(
type_keyword_to_kind("beshort"),
TypeKind::Short {
endian: Endianness::Big,
signed: true
}
);
}
#[test]
fn test_type_keyword_to_kind_unsigned_variants() {
assert_eq!(
type_keyword_to_kind("ushort"),
TypeKind::Short {
endian: Endianness::Native,
signed: false
}
);
assert_eq!(
type_keyword_to_kind("ulong"),
TypeKind::Long {
endian: Endianness::Native,
signed: false
}
);
assert_eq!(
type_keyword_to_kind("uquad"),
TypeKind::Quad {
endian: Endianness::Native,
signed: false
}
);
}
#[test]
fn test_type_keyword_to_kind_signed_defaults() {
assert_eq!(
type_keyword_to_kind("long"),
TypeKind::Long {
endian: Endianness::Native,
signed: true
}
);
assert_eq!(
type_keyword_to_kind("quad"),
TypeKind::Quad {
endian: Endianness::Native,
signed: true
}
);
}
#[test]
fn test_type_keyword_to_kind_string() {
assert_eq!(
type_keyword_to_kind("string"),
TypeKind::String { max_length: None }
);
}
#[test]
fn test_roundtrip_all_keywords() {
let keywords = [
"byte", "ubyte", "short", "ushort", "leshort", "uleshort", "beshort", "ubeshort",
"long", "ulong", "lelong", "ulelong", "belong", "ubelong", "quad", "uquad", "lequad",
"ulequad", "bequad", "ubequad", "float", "befloat", "lefloat", "double", "bedouble",
"ledouble", "string",
];
for keyword in keywords {
let (rest, parsed) = parse_type_keyword(keyword).unwrap();
assert_eq!(rest, "", "Keyword {keyword} should consume all input");
let _ = type_keyword_to_kind(parsed);
}
}
}