|
@@ -0,0 +1,149 @@
|
|
|
+use crate::{
|
|
|
+ opcode::{opcode, DUAL_NAME_PREFIX, MULTI_NAME_PREFIX, NULL_NAME, ROOT_CHAR},
|
|
|
+ parser::{choice, consume, n_of, take, Parser},
|
|
|
+ AmlError,
|
|
|
+};
|
|
|
+use alloc::string::String;
|
|
|
+use core::str;
|
|
|
+
|
|
|
+pub fn name_string<'a>() -> impl Parser<'a, String> {
|
|
|
+ /*
|
|
|
+ * NameString := <RootChar('\') NamePath> | <PrefixPath NamePath>
|
|
|
+ * PrefixPath := Nothing | <'^' PrefixPath>
|
|
|
+ */
|
|
|
+ // TODO: this does not yet parse <PrefixPath NamePath>
|
|
|
+ let root_name_string =
|
|
|
+ opcode(ROOT_CHAR).then(name_path()).map(|((), name_path)| String::from("\\") + &name_path);
|
|
|
+
|
|
|
+ choice!(root_name_string)
|
|
|
+}
|
|
|
+
|
|
|
+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 := <LeadNameChar NameChar NameChar NameChar>
|
|
|
+ */
|
|
|
+ // 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]);
|
|
|
+ check_err!(
|
|
|
+ name_path().parse(&[0x2e, b'A']),
|
|
|
+ AmlError::UnexpectedEndOfStream,
|
|
|
+ &[0x2e, 0x00]
|
|
|
+ );
|
|
|
+ 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"),
|
|
|
+ &[]
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|