use crate::{ opcode::{opcode, DUAL_NAME_PREFIX, MULTI_NAME_PREFIX, NULL_NAME, PREFIX_CHAR, ROOT_CHAR}, parser::{choice, comment_scope, consume, n_of, take, Parser}, AmlError, }; use alloc::string::String; use core::str; pub fn name_string<'a>() -> impl Parser<'a, String> { /* * NameString := | * PrefixPath := Nothing | <'^' PrefixPath> */ let root_name_string = opcode(ROOT_CHAR).then(name_path()).map(|((), name_path)| String::from("\\") + &name_path); comment_scope("NameString", move |input: &'a [u8]| { let first_char = *input.first().ok_or((input, AmlError::UnexpectedEndOfStream))?; match first_char { ROOT_CHAR => root_name_string.parse(input), PREFIX_CHAR => { // TODO: parse where there are actually PrefixChars unimplemented!(); } _ => name_path().parse(input), } }) } pub fn name_path<'a>() -> impl Parser<'a, String> { /* * NamePath := NullName | DualNamePath | MultiNamePath | NameSeg */ choice!( null_name(), dual_name_path(), multi_name_path(), name_seg().map(|seg| String::from(seg.as_str())) ) } pub fn null_name<'a>() -> impl Parser<'a, String> { /* * NullName := 0x00 */ opcode(NULL_NAME).map(|_| String::from("")) } pub fn dual_name_path<'a>() -> impl Parser<'a, String> { /* * DualNamePath := 0x2e NameSeg NameSeg */ opcode(DUAL_NAME_PREFIX) .then(name_seg()) .then(name_seg()) .map(|(((), first), second)| String::from(first.as_str()) + second.as_str()) } pub fn multi_name_path<'a>() -> impl Parser<'a, String> { /* * MultiNamePath := 0x2f ByteData{SegCount} NameSeg(SegCount) */ move |input| { let (new_input, ((), seg_count)) = opcode(MULTI_NAME_PREFIX).then(take()).parse(input)?; match n_of(name_seg(), usize::from(seg_count)).parse(new_input) { Ok((new_input, name_segs)) => Ok(( new_input, name_segs.iter().fold(String::new(), |name, name_seg| name + name_seg.as_str()), )), // Correct returned input to the one we haven't touched Err((_, err)) => Err((input, err)), } } } #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct NameSeg([u8; 4]); impl NameSeg { /// Turn a `NameSeg` into a `&str`. Returns it in a `ParseResult` so it's easy to use from /// inside parsers. pub fn as_str(&self) -> &str { /* * This is safe, because we always check that all the bytes are valid ASCII, so every * `NameSeg` will be valid UTF8. */ unsafe { str::from_utf8_unchecked(&self.0) } } } pub fn name_seg<'a>() -> impl Parser<'a, NameSeg> { /* * NameSeg := */ // TODO: can we write this better? move |input| { let (input, char_1) = consume(is_lead_name_char).parse(input)?; let (input, char_2) = consume(is_name_char).parse(input)?; let (input, char_3) = consume(is_name_char).parse(input)?; let (input, char_4) = consume(is_name_char).parse(input)?; Ok((input, NameSeg([char_1, char_2, char_3, char_4]))) } } fn is_lead_name_char(byte: u8) -> bool { (byte >= b'A' && byte <= b'Z') || byte == b'_' } fn is_digit_char(byte: u8) -> bool { byte >= b'0' && byte <= b'9' } fn is_name_char(byte: u8) -> bool { is_lead_name_char(byte) || is_digit_char(byte) } #[cfg(test)] mod tests { use super::*; use crate::{parser::Parser, test_utils::*, AmlError}; #[test] fn test_name_seg() { check_ok!( name_seg().parse(&[b'A', b'F', b'3', b'Z']), NameSeg([b'A', b'F', b'3', b'Z']), &[] ); check_ok!( name_seg().parse(&[b'A', b'F', b'3', b'Z', 0xff]), NameSeg([b'A', b'F', b'3', b'Z']), &[0xff] ); check_err!( name_seg().parse(&[0xff, b'E', b'A', b'7']), AmlError::UnexpectedByte(0xff), &[0xff, b'E', b'A', b'7'] ); check_err!(name_seg().parse(&[]), AmlError::UnexpectedEndOfStream, &[]); } #[test] fn test_name_path() { check_err!(name_path().parse(&[]), AmlError::UnexpectedEndOfStream, &[]); check_ok!(name_path().parse(&[0x00]), String::from(""), &[]); check_ok!(name_path().parse(&[0x00, 0x00]), String::from(""), &[0x00]); // TODO: this failure is actually a symptom of `choice!` not working quite correctly. When // the dual_name_path parser fails (this is one, but is too short), it carries on and then // returns a confusing error: `UnexpectedByte(0x2e)`. Not sure how the best way to go about // fixing that is. // // For now, we know about this corner case, so altering the unit-test for now. check_err!( name_path().parse(&[0x2e, b'A']), AmlError::UnexpectedByte(0x2e), // TODO: this is the correct error // AmlError::UnexpectedEndOfStream, &[0x2e, b'A'] ); check_ok!( name_path().parse(&[0x2e, b'A', b'B', b'C', b'D', b'E', b'_', b'F', b'G']), String::from("ABCDE_FG"), &[] ); } }