Ver Fonte

Parse opcodes using combinators

Isaac Woods há 6 anos atrás
pai
commit
685320f8f3
4 ficheiros alterados com 123 adições e 0 exclusões
  1. 6 0
      aml_parser/src/lib.rs
  2. 66 0
      aml_parser/src/opcode.rs
  3. 36 0
      aml_parser/src/parser.rs
  4. 15 0
      aml_parser/src/test_utils.rs

+ 6 - 0
aml_parser/src/lib.rs

@@ -6,6 +6,12 @@ extern crate alloc;
 #[cfg(test)]
 extern crate std;
 
+#[cfg(test)]
+mod test_utils;
+
+pub(crate) mod opcode;
+pub(crate) mod parser;
+
 use alloc::{collections::BTreeMap, string::String};
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]

+ 66 - 0
aml_parser/src/opcode.rs

@@ -0,0 +1,66 @@
+use crate::{parser::*, AmlError};
+
+pub const NULL_NAME: u8 = 0x00;
+pub const DUAL_NAME_PREFIX: u8 = 0x2E;
+pub const MULTI_NAME_PREFIX: u8 = 0x2F;
+
+pub const ZERO_OP: u8 = 0x00;
+pub const ONE_OP: u8 = 0x01;
+pub const ONES_OP: u8 = 0xff;
+pub const BYTE_CONST: u8 = 0x0a;
+pub const WORD_CONST: u8 = 0x0b;
+pub const DWORD_CONST: u8 = 0x0c;
+pub const STRING_PREFIX: u8 = 0x0d;
+pub const QWORD_CONST: u8 = 0x0e;
+
+pub const NAME_OP: u8 = 0x08;
+pub const SCOPE_OP: u8 = 0x10;
+pub const BUFFER_OP: u8 = 0x11;
+pub const PACKAGE_OP: u8 = 0x12;
+pub const METHOD_OP: u8 = 0x14;
+pub const EXT_REVISION_OP: u8 = 0x30;
+pub const EXT_OP_REGION_OP: u8 = 0x80;
+pub const EXT_FIELD_OP: u8 = 0x81;
+pub const EXT_DEVICE_OP: u8 = 0x82;
+
+pub const EXT_OPCODE_PREFIX: u8 = 0x5b;
+
+pub(crate) fn opcode<'a>(opcode: u8) -> impl Parser<'a, ()> {
+    move |stream: &'a [u8]| match stream.first() {
+        None => Err((stream, AmlError::UnexpectedEndOfStream)),
+        Some(&byte) if byte == opcode => Ok((&stream[1..], ())),
+        Some(&byte) => Err((stream, AmlError::UnexpectedByte(byte))),
+    }
+}
+
+pub(crate) fn ext_opcode<'a>(ext_opcode: u8) -> impl Parser<'a, ()> {
+    map(pair(opcode(EXT_OPCODE_PREFIX), opcode(ext_opcode)), |_| ())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::{test_utils::*, AmlError};
+
+    #[test]
+    fn empty() {
+        check_err!(opcode(NULL_NAME).parse(&[]), AmlError::UnexpectedEndOfStream, &[]);
+        check_err!(ext_opcode(EXT_FIELD_OP).parse(&[]), AmlError::UnexpectedEndOfStream, &[]);
+    }
+
+    #[test]
+    fn simple_opcodes() {
+        check_ok!(opcode(SCOPE_OP).parse(&[SCOPE_OP]), (), &[]);
+        check_ok!(opcode(NAME_OP).parse(&[NAME_OP, 0x31, 0x55, 0xf3]), (), &[0x31, 0x55, 0xf3]);
+    }
+
+    #[test]
+    fn extended_opcodes() {
+        check_err!(
+            ext_opcode(EXT_FIELD_OP).parse(&[EXT_FIELD_OP, EXT_FIELD_OP]),
+            AmlError::UnexpectedByte(EXT_FIELD_OP),
+            &[EXT_FIELD_OP, EXT_FIELD_OP]
+        );
+        check_ok!(ext_opcode(EXT_FIELD_OP).parse(&[EXT_OPCODE_PREFIX, EXT_FIELD_OP]), (), &[]);
+    }
+}

+ 36 - 0
aml_parser/src/parser.rs

@@ -0,0 +1,36 @@
+use crate::AmlError;
+
+pub(crate) type ParseResult<'a, R> = Result<(&'a [u8], R), (&'a [u8], AmlError)>;
+
+pub(crate) trait Parser<'a, R> {
+    fn parse(&self, input: &'a [u8]) -> ParseResult<'a, R>;
+}
+
+impl<'a, F, R> Parser<'a, R> for F
+where
+    F: Fn(&'a [u8]) -> ParseResult<'a, R>,
+{
+    fn parse(&self, input: &'a [u8]) -> ParseResult<'a, R> {
+        self(input)
+    }
+}
+
+pub(crate) fn pair<'a, P1, P2, R1, R2>(a: P1, b: P2) -> impl Parser<'a, (R1, R2)>
+where
+    P1: Parser<'a, R1>,
+    P2: Parser<'a, R2>,
+{
+    move |input| {
+        a.parse(input).and_then(|(next_input, result_a)| {
+            b.parse(next_input).map(|(final_input, result_b)| (final_input, (result_a, result_b)))
+        })
+    }
+}
+
+pub(crate) fn map<'a, P, F, A, B>(parser: P, map_fn: F) -> impl Parser<'a, B>
+where
+    P: Parser<'a, A>,
+    F: Fn(A) -> B,
+{
+    move |input| parser.parse(input).map(|(next_input, result)| (next_input, map_fn(result)))
+}

+ 15 - 0
aml_parser/src/test_utils.rs

@@ -0,0 +1,15 @@
+pub(crate) macro check_err($parse: expr, $error: pat, $remains: expr) {
+    match $parse {
+        Ok(result) => panic!("Expected Err, got {:#?}", result),
+        Err((remains, $error)) if *remains == *$remains => (),
+        Err((_, err)) => panic!("Got wrong error: {:?}", err),
+    }
+}
+
+pub(crate) macro check_ok($parse: expr, $expected: expr, $remains: expr) {
+    match $parse {
+        Ok((remains, result)) if remains == *$remains && result == $expected => (),
+        Ok(result) => panic!("Successfully parsed Ok, but it was wrong: {:#?}", result),
+        Err((_, err)) => panic!("Expected Ok, got {:#?}", err),
+    }
+}