diff --git a/.env b/.env new file mode 100644 index 0000000..8283478 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +GEMINI_API_KEY=AIzaSyDvmFWcX5GREry1rU9-Rq_MSOXqMoMShLc diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..17783ac --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,385 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstream" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bin_inspect" +version = "0.1.0" +dependencies = [ + "clap", + "nanoid", + "nom", + "parse_int", + "thiserror", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "clap" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "nanoid" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" +dependencies = [ + "rand", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "parse_int" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c464266693329dd5a8715098c7f86e6c5fd5d985018b8318f53d9c6c2b21a31" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index d531286..3ba9ade 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,5 @@ edition = "2024" clap = "4.5.40" nanoid = "0.4.0" nom = "8.0.0" +parse_int = "0.9.0" thiserror = "2.0.12" diff --git a/README.md b/README.md index e69de29..d52494e 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,14 @@ + +# BRIN, the Binary (Rust btw) INspector + +*BRIN* is a small tool to parse binary formats. +You can define a .brin file with a description of your binary format. +Then you can `brin inspect -f description.brin -b binary_file` and BRIN +will attempt to parse and print your binary file into a structured, JSON-like format. + +BRIN is designed to quickly troubleshoot problems in binary files, so it will always +attempt to parse as much as possible even if parts of the parsing fails. + +## great how do I start? + +You don't! Work in progress! bye diff --git a/formats/c37118.brin b/formats/c37118.brin index 3086ede..c37d9a8 100644 --- a/formats/c37118.brin +++ b/formats/c37118.brin @@ -1,4 +1,4 @@ -#= BigEndian +#= Bigendian CFGFRAME { 0-1 bytes: field "SYNC" { @@ -9,4 +9,3 @@ CFGFRAME { 5-6 bytes: field "NUM_PMU" u16 } - diff --git a/src/grammar/mod.rs b/src/grammar/mod.rs index 540daec..79a292e 100644 --- a/src/grammar/mod.rs +++ b/src/grammar/mod.rs @@ -2,14 +2,20 @@ use crate::ir::{Atom, OwnedAtom}; pub(crate) mod parser; #[derive(PartialEq, Debug)] -struct Module { - objs: Vec<(String, Region)>, +pub(crate) struct Module { + pub(crate) objs: Vec<(String, Region)>, + pub(crate) opts: Options, } +/// Module options (endianess etc.) +/// To be defined :( +#[derive(PartialEq, Debug)] +pub(crate) struct Options(pub(crate) Vec); + /// A region of memory. /// Either a structured field list or an atomic type. #[derive(PartialEq, Debug)] -enum Region { +pub(crate) enum Region { Fields(Vec<(Range, FieldRegion)>), Atom { ty: Type }, } @@ -17,7 +23,7 @@ enum Region { /// Content of a field. /// Can be a region or a constant. #[derive(PartialEq, Debug)] -enum FieldRegion { +pub(crate) enum FieldRegion { Subfield { name: String, content: Region }, Const { name: String, value: OwnedAtom }, } diff --git a/src/grammar/parser.rs b/src/grammar/parser.rs index 4c7c7ee..a8a867a 100644 --- a/src/grammar/parser.rs +++ b/src/grammar/parser.rs @@ -3,6 +3,7 @@ use std::error::Error; use nom::IResult; use nom::Parser; use nom::branch::alt; +use nom::bytes::complete::is_not; use nom::bytes::complete::tag; use nom::bytes::complete::tag_no_case; use nom::character::complete::alpha1; @@ -12,28 +13,46 @@ use nom::character::complete::char; use nom::character::complete::digit1; use nom::character::complete::hex_digit1; use nom::character::complete::line_ending; +use nom::character::complete::multispace0; use nom::character::complete::newline; use nom::character::complete::space0; +use nom::character::streaming::multispace1; use nom::combinator::map; use nom::combinator::map_res; use nom::combinator::opt; use nom::combinator::recognize; use nom::error::ParseError; +use nom::multi::many0; use nom::multi::many0_count; -use nom::multi::separated_list1; +use nom::multi::many1; +use nom::multi::separated_list0; use nom::sequence::delimited; use nom::sequence::pair; use nom::sequence::preceded; +use nom::sequence::terminated; + +use crate::ir::Atom; use super::FieldRegion; +use super::Module; +use super::Options; use super::OwnedAtom; use super::Range; use super::Region; use super::Type; /// A combinator that takes a parser `inner` and produces a parser that also consumes both leading and -/// trailing whitespace, returning the output of `inner`. +/// trailing whitespace, returning the output of `inner`. This includes newlines pub fn ws<'a, O, E: ParseError<&'a str>, F>(inner: F) -> impl Parser<&'a str, Output = O, Error = E> +where + F: Parser<&'a str, Output = O, Error = E>, +{ + delimited(multispace0, inner, multispace0) +} + +pub fn ws_no_newline<'a, O, E: ParseError<&'a str>, F>( + inner: F, +) -> impl Parser<&'a str, Output = O, Error = E> where F: Parser<&'a str, Output = O, Error = E>, { @@ -64,7 +83,7 @@ pub fn parse_field_name(input: &str) -> IResult<&str, &str> { char('"'), recognize(pair( alt((alpha1, tag("_"))), - many0_count(alt((alphanumeric1, tag("_")))), + many0_count(alt((alphanumeric1, tag("_"), tag(" ")))), )), char('"'), ) @@ -79,8 +98,26 @@ fn field_name_test() { ) } +/// A combinator that parses identifiers. +pub fn parse_identifier(input: &str) -> IResult<&str, &str> { + recognize(pair( + alt((alpha1, tag("_"))), + many0_count(alt((alphanumeric1, tag("_")))), + )) + .parse(input) +} + +#[test] +fn identifier_test() { + assert_eq!( + "CFGFRAME".to_owned(), + parse_identifier("CFGFRAME").unwrap().1 + ) +} + /// A combinator that parses primitive types. pub fn parse_type(input: &str) -> IResult<&str, Type> { + println!("type: {:?}", input); alt(( (map(tag("bool"), |_| Type::Bool)), (map(tag("u8"), |_| Type::U8)), @@ -97,13 +134,13 @@ pub fn parse_type(input: &str) -> IResult<&str, Type> { (map(tag("f64"), |_| Type::F64)), (map(tag("utf8"), |_| Type::Utf8)), (preceded( - tag("u"), + char('u'), map_res(digit1, |s: &str| { Ok::>(Type::UX(s.parse()?)) }), )), (preceded( - tag("i"), + char('i'), map_res(digit1, |s: &str| { Ok::>(Type::IX(s.parse()?)) }), @@ -139,20 +176,27 @@ fn parse_range_vals(input: &str) -> IResult<&str, Range> { fn parse_range(input: &str) -> IResult<&str, Range> { map( (parse_range_vals, char(' '), parse_unit), - |((l, h), _, s)| (l * s, h * s), + |((l, h), _, s)| { + if s == 1 { + (l, h) + } else { + ((l * s), ((1 + h) * s).saturating_sub(1)) + } + }, ) .parse(input) } #[test] fn range_test() { - assert_eq!((0, 16), parse_range("0-2 bytes").unwrap().1); - assert_eq!((24, 24), parse_range("3 byte").unwrap().1); + assert_eq!((0, 15), parse_range("0-1 bytes").unwrap().1); + assert_eq!((24, 31), parse_range("3 byte").unwrap().1); assert_eq!((0, 5), parse_range("0-5 bits").unwrap().1); assert_eq!((0, 2), parse_range("0-2 bit").unwrap().1); } pub fn parse_field_entry(input: &str) -> IResult<&str, (Range, FieldRegion)> { + println!("field_entry{:?}", input); map( ( parse_range, @@ -169,7 +213,7 @@ pub fn parse_field_entry(input: &str) -> IResult<&str, (Range, FieldRegion)> { ( tag("const "), parse_field_name, - char(' '), + many1(char(' ')), parse_const_region, ), |(_, name, _, r)| FieldRegion::Const { @@ -184,33 +228,19 @@ pub fn parse_field_entry(input: &str) -> IResult<&str, (Range, FieldRegion)> { .parse(input) } -#[test] -fn parse_field_line_test() { - let line = include_str!("test/line.brin"); - - assert_eq!( - ( - (0, 2), - FieldRegion::Subfield { - name: "test".to_owned(), - content: Region::Atom { ty: Type::U16 } - } - ), - parse_field_entry(line).unwrap().1 - ); -} - pub fn parse_region(input: &str) -> IResult<&str, Region> { + println!("Region: {:?}", input); alt(( map( ( - ws(char('{')), - opt(ws(newline)), - separated_list1(ws(line_ending), parse_field_entry), - opt(ws(newline)), - ws(char('}')), + multispace0, + char('{'), + multispace1, + separated_list0(multispace1, parse_field_entry), + multispace1, + char('}'), ), - |(_, _, field_list, _, _)| Region::Fields(field_list), + |(_, _, _, field_list, _, _)| Region::Fields(field_list), ), map(parse_type, |ty| Region::Atom { ty }), )) @@ -234,64 +264,64 @@ fn parse_concrete_type(ty: Type) -> fn(&str) -> IResult<&str, OwnedAtom> { }, Type::U8 => |s: &str| { map_res(parse_number, |x| { - Ok::>(OwnedAtom::U8(x.parse()?)) + Ok::>(OwnedAtom::U8(parse_int::parse(x)?)) }) .parse(s) }, Type::U16 => |s: &str| { map_res(parse_number, |x| { - Ok::>(OwnedAtom::U16(x.parse()?)) + Ok::>(OwnedAtom::U16(parse_int::parse(x)?)) }) .parse(s) }, Type::U32 => |s: &str| { map_res(parse_number, |x| { - Ok::>(OwnedAtom::U32(x.parse()?)) + Ok::>(OwnedAtom::U32(parse_int::parse(x)?)) }) .parse(s) }, Type::U64 => |s: &str| { map_res(parse_number, |x| { - Ok::>(OwnedAtom::U64(x.parse()?)) + Ok::>(OwnedAtom::U64(parse_int::parse(x)?)) }) .parse(s) }, Type::U128 => |s: &str| { map_res(parse_number, |x| { - Ok::>(OwnedAtom::U128(x.parse()?)) + Ok::>(OwnedAtom::U128(parse_int::parse(x)?)) }) .parse(s) }, Type::I8 => |s: &str| { map_res(parse_number, |x| { - Ok::>(OwnedAtom::I8(x.parse()?)) + Ok::>(OwnedAtom::I8(parse_int::parse(x)?)) }) .parse(s) }, Type::I16 => |s: &str| { map_res(parse_number, |x| { - Ok::>(OwnedAtom::I16(x.parse()?)) + Ok::>(OwnedAtom::I16(parse_int::parse(x)?)) }) .parse(s) }, Type::I32 => |s: &str| { map_res(parse_number, |x| { - Ok::>(OwnedAtom::I32(x.parse()?)) + Ok::>(OwnedAtom::I32(parse_int::parse(x)?)) }) .parse(s) }, Type::I64 => |s: &str| { map_res(parse_number, |x| { - Ok::>(OwnedAtom::I64(x.parse()?)) + Ok::>(OwnedAtom::I64(parse_int::parse(x)?)) }) .parse(s) }, Type::I128 => |s: &str| { map_res(parse_number, |x| { - Ok::>(OwnedAtom::I128(x.parse()?)) + Ok::>(OwnedAtom::I128(parse_int::parse(x)?)) }) .parse(s) }, @@ -316,26 +346,201 @@ fn parse_concrete_type(ty: Type) -> fn(&str) -> IResult<&str, OwnedAtom> { } #[test] -fn parse_region_test() { - let region = include_str!("test/region.brin"); +fn test_const_region() { + let region = "u16 0x10"; + assert_eq!(parse_const_region(region).unwrap().1, OwnedAtom::U16(0x10)); +} +fn parse_options(input: &str) -> IResult<&str, Options> { + map( + many0(map( + terminated(preceded(tag("#= "), is_not("\r\n")), line_ending), + |s: &str| s.trim().to_owned(), + )), + Options, + ) + .parse(input) +} + +#[test] +fn options_test() { + let options = "#= BigEndian\n#= Another Option\nCFGFRAME"; + let (rem, parsed) = parse_options(options).unwrap(); + assert_eq!(rem, "CFGFRAME"); assert_eq!( + parsed, + Options(vec!["BigEndian".to_owned(), "Another Option".to_owned()]) + ); + + let options = "CFGFRAME"; + let (rem, parsed) = parse_options(options).unwrap(); + assert_eq!(rem, "CFGFRAME"); + assert_eq!(parsed, Options(Vec::::new())); +} + +fn parse_named_region(input: &str) -> IResult<&str, (String, Region)> { + map((parse_identifier, parse_region), |(name, region)| { + (name.to_owned(), region) + }) + .parse(input) +} + +pub fn parse_module(input: &str) -> IResult<&str, Module> { + println!("{}", input); + map( + ( + parse_options, + multispace1, + separated_list0(multispace0, parse_named_region), + ), + |(opts, _, objs)| Module { opts, objs }, + ) + .parse(input) +} + +#[test] +fn module_test() { + let module = "#= BigEndian\n\n\nCFGFRAME { 0-1 bytes: field \"SYNC\" u16 }"; + let (_, module) = parse_module(module).unwrap(); + assert_eq!(module.opts, Options(vec!["BigEndian".to_owned()])); + assert_eq!(module.objs[0].0, "CFGFRAME"); +} + +#[test] +fn c37118_test() { + let brin = include_str!("../../formats/c37118.brin"); + let (_, module) = parse_module(brin).unwrap(); + assert_eq!(module.opts, Options(vec!["Bigendian".to_owned()])); + assert_eq!(module.objs[0].0, "CFGFRAME"); + + if let Region::Fields(fields) = &module.objs[0].1 { + assert_eq!(fields[0].0, (0, 15)); + if let FieldRegion::Subfield { name, content } = &fields[0].1 { + assert_eq!(name, "SYNC"); + if let Region::Fields(subfields) = content { + assert_eq!(subfields[0].0, (0, 7)); + if let FieldRegion::Const { name, value } = &subfields[0].1 { + assert_eq!(name, "magic_number"); + assert_eq!(*value, OwnedAtom::U8(0xAA)); + } + + assert_eq!(subfields[1].0, (8, 15)); + if let FieldRegion::Const { name, value } = &subfields[1].1 { + assert_eq!(name, "ver_number"); + assert_eq!(*value, OwnedAtom::U8(0x31)); + } + } + } + + assert_eq!(fields[1].0, (24, 39)); + if let FieldRegion::Subfield { name, content } = &fields[1].1 { + assert_eq!(*name, "TIME BASE"); + if let Region::Atom { ty } = content { + assert_eq!(*ty, Type::U16); + } else { + panic!("Incorrect content for TIME BASE"); + } + } else { + panic!("Incorrect field region for TIME BASE"); + } + + assert_eq!(fields[2].0, (40, 55)); + if let FieldRegion::Subfield { name, content } = &fields[2].1 { + assert_eq!(*name, "NUM_PMU"); + if let Region::Atom { ty } = content { + assert_eq!(*ty, Type::U16); + } else { + panic!("Incorrect content for NUM_PMU"); + } + } else { + panic!("Incorrect field region for NUM_PMU"); + } + } else { + panic!("Wrong region type"); + } +} + +#[test] +fn test_field_entry() { + let region = "3-4 bytes: field \"TIME BASE\" u16"; + assert_eq!( + parse_field_entry(region).unwrap().1, + ( + (24, 39), + FieldRegion::Subfield { + name: "TIME BASE".to_owned(), + content: Region::Atom { ty: Type::U16 } + } + ) + ); +} + +#[test] +fn test_region_good() { + let region = "{ + 3-4 byte: field \"time base\" u16 + 5-6 byte: field \"magic number\" { + 0-4 bits: const \"aaa\" u8 0x10 + } +}"; + assert_eq!( + parse_region(region).unwrap().1, Region::Fields(vec![ ( - (0, 1), + (24, 39), FieldRegion::Subfield { - name: "test".to_owned(), + name: "time base".to_owned(), content: Region::Atom { ty: Type::U16 } } ), ( - (2, 3), + (40, 55), FieldRegion::Subfield { - name: "second_field".to_owned(), - content: Region::Atom { ty: Type::I32 } + name: "magic number".to_owned(), + content: Region::Fields(vec![( + (0, 4), + FieldRegion::Const { + name: "aaa".to_owned(), + value: Atom::U8(16) + } + )]) } ) - ]), - parse_region(region).unwrap().1 + ]) + ); +} + +#[test] +fn test_region_bad() { + let region = "{ + 3-4 byte: field \"magic number\" { + 0 bit: const \"active\" u8 0x10 + } + 1-2 byte: field \"timestamp\" u8 +}"; + assert_eq!( + parse_region(region).unwrap().1, + Region::Fields(vec![ + ( + (24, 39), + FieldRegion::Subfield { + name: "magic number".to_owned(), + content: Region::Fields(vec![( + (0, 0), + FieldRegion::Const { + name: "active".to_owned(), + value: Atom::U8(16) + } + )]) + } + ), + ( + (8, 23), + FieldRegion::Subfield { + name: "timestamp".to_owned(), + content: Region::Atom { ty: Type::U8 } + } + ), + ]) ); } diff --git a/src/grammar/test/line.brin b/src/grammar/test/line.brin deleted file mode 100644 index 1be232d..0000000 --- a/src/grammar/test/line.brin +++ /dev/null @@ -1 +0,0 @@ -0-2 bits: field "test" u16 diff --git a/src/grammar/test/region.brin b/src/grammar/test/region.brin deleted file mode 100644 index 2518f03..0000000 --- a/src/grammar/test/region.brin +++ /dev/null @@ -1,8 +0,0 @@ -{ - 0-1 bits: field "test" u16 - 2-3 bits: field "second_field" i16 - 4-8 bits: field "big_struct" { - 0-1 bits: field "test" u8 - 2-3 bits: const "magic" u16 0 - } -}