use nom::{
IResult,
bytes::complete::{tag, take_while1},
Parser,
sequence::{preceded, Tuple},
character::complete::{char, space1},
combinator::opt
};
use super::{
Preamble,
comment::many1_comments_or_whitespace,
parse_name
};
#[derive(Debug, PartialEq)]
pub(super) struct Version<'a> {
major: &'a str,
minor: &'a str,
}
#[derive(Debug, PartialEq)]
pub(super) struct Target<'a> {
target: &'a str,
}
#[derive(Debug, PartialEq)]
pub(super) struct AddressSize<'a> {
size: &'a str,
}
pub(super) fn parse_preamble(input: &str) -> IResult<&str, Preamble> {
(
preceded(
opt(many1_comments_or_whitespace),
parse_version
),
preceded(
many1_comments_or_whitespace,
parse_target
),
preceded(
many1_comments_or_whitespace,
parse_address_size
)
)
.parse(input)
.map(
|(input, (version, target, address_size))|
(input, Preamble { version, target, address_size })
)
}
fn parse_version(input: &str) -> IResult<&str, Version> {
(
preceded(
tag(".version").and(space1),
take_while1(char::is_numeric)
),
preceded(
char('.'),
take_while1(char::is_numeric)
)
)
.parse(input)
.map(
|(input, (major, minor))|
(input, Version { major, minor })
)
}
fn parse_target(input: &str) -> IResult<&str, Target> {
preceded(
tag(".target").and(space1),
parse_name
.map(|target| Target { target })
)(input)
}
fn parse_address_size(input: &str) -> IResult<&str, AddressSize> {
preceded(
tag(".address_size").and(space1),
parse_name
.map(|size| AddressSize { size })
)(input)
}
#[cfg(test)]
mod test_parse_version {
use super::*;
#[test]
fn no_whitespace() {
assert_eq!(
parse_version(".version 1.0"),
Ok(("", Version { major: "1", minor: "0" }))
);
}
#[test]
fn leading_whitespace() {
assert!(
parse_version(" .version 1.0").is_err()
);
}
#[test]
fn trailing_whitespace() {
assert_eq!(
parse_version(".version 1.0 "),
Ok((" ", Version { major: "1", minor: "0" }))
);
}
#[test]
fn immediate_comment() {
assert_eq!(
parse_version(".version 1.0// This is a comment"),
Ok(("// This is a comment", Version { major: "1", minor: "0" }))
);
}
}
#[cfg(test)]
mod test_parse_target {
use super::{parse_target, Target};
#[test]
fn no_whitespace() {
assert_eq!(
parse_target(".target sm_30"),
Ok(("", Target { target: "sm_30" }))
);
}
#[test]
fn leading_whitespace() {
assert!(
parse_target(" .target sm_30").is_err()
);
}
#[test]
fn trailing_whitespace() {
assert_eq!(
parse_target(".target sm_30 "),
Ok((" ", Target { target: "sm_30" }))
);
}
#[test]
fn immediate_comment() {
assert_eq!(
parse_target(".target sm_30// This is a comment"),
Ok(("// This is a comment", Target { target: "sm_30" }))
);
}
}
#[cfg(test)]
mod test_parse_address_size {
use super::{parse_address_size, AddressSize};
#[test]
fn no_whitespace() {
assert_eq!(
parse_address_size(".address_size 64"),
Ok(("", AddressSize { size: "64" }))
);
}
#[test]
fn leading_whitespace() {
assert!(
parse_address_size(" .address_size 64").is_err()
);
}
#[test]
fn trailing_whitespace() {
assert_eq!(
parse_address_size(".address_size 64 "),
Ok((" ", AddressSize { size: "64" }))
);
}
#[test]
fn immediate_comment() {
assert_eq!(
parse_address_size(".address_size 64// This is a comment"),
Ok(("// This is a comment", AddressSize { size: "64" }))
);
}
}
#[cfg(test)]
mod test_parse_preamble {
use super::{parse_preamble, Preamble, Version, Target, AddressSize};
#[test]
fn no_whitespace() {
assert_eq!(
parse_preamble(".version 1.0\n.target sm_30\n.address_size 64"),
Ok(("", (Preamble {
version: Version { major: "1", minor: "0" },
target: Target { target: "sm_30" },
address_size: AddressSize { size: "64" }
})))
);
}
#[test]
fn leading_whitespace() {
assert_eq!(
parse_preamble(" .version 1.0\n.target sm_30\n.address_size 64"),
Ok(("", (Preamble {
version: Version { major: "1", minor: "0" },
target: Target { target: "sm_30" },
address_size: AddressSize { size: "64" }
})))
);
}
#[test]
fn leading_newline() {
assert_eq!(
parse_preamble(" \n .version 1.0\n.target sm_30\n.address_size 64"),
Ok(("", (Preamble {
version: Version { major: "1", minor: "0" },
target: Target { target: "sm_30" },
address_size: AddressSize { size: "64" }
})))
);
}
#[test]
fn trailing_whitespace() {
assert_eq!(
parse_preamble(".version 1.0\n.target sm_30\n.address_size 64 "),
Ok((" ", (Preamble {
version: Version { major: "1", minor: "0" },
target: Target { target: "sm_30" },
address_size: AddressSize { size: "64" }
})))
);
}
#[test]
fn immediate_comment() {
assert_eq!(
parse_preamble(".version 1.0\n.target sm_30\n.address_size 64// This is a comment"),
Ok(("// This is a comment", (Preamble {
version: Version { major: "1", minor: "0" },
target: Target { target: "sm_30" },
address_size: AddressSize { size: "64" }
})))
);
}
#[test]
fn trailing_comment() {
assert_eq!(
parse_preamble(".version 1.0\n.target sm_30\n.address_size 64\n// This is a comment"),
Ok(("\n// This is a comment", (Preamble {
version: Version { major: "1", minor: "0" },
target: Target { target: "sm_30" },
address_size: AddressSize { size: "64" }
})))
);
}
}