Совпадение с одним из нескольких байтов в nom

Я использую nom для анализа некоторых двоичных данных сборки 65c816. Часть моей структуры парсера разбивает коды операций на отдельные формы, а затем получает их параметры, если они есть. Пример парсера в настоящее время выглядит так:

named!(absolute_long<Instruction>, 
    do_parse!(op: bits!(alt!(
        opcode!(0x0F) | opcode!(0x22) | opcode!(0x2F) | opcode!(0x4F) | 
        opcode!(0x5C) | opcode!(0x6F) | opcode!(0x8F) | opcode!(0xAF) | 
        opcode!(0xCF) | opcode!(0xEF)))
>> param: le_u24
>> (Instruction::AbsoluteLong(op, param))));

Где opcode! — это макрос, который я сделал следующим образом:

macro_rules! opcode(
    ($i:expr, $op:expr) => (
        tag_bits!($i, u8, 8, $op);
    );
    ($i:expr, $f:expr) => (
        opcode!($i, call!($f));
    );
);

Вместо этого я хотел бы иметь код, подобный следующему, но не могу понять, как сделать макрос или функцию для этого:

named!(absolute_long<Instruction>, 
    do_parse!(op: opcodes!(
        0x0F, 0x22, 0x2F, 0x4F, 0x5C, 0x6F, 0x8F, 0xAF, 
        0xCF, 0xEF)
    >> param: le_u24
    >> (Instruction::AbsoluteLong(op, parm)));

Самое близкое, что я получил, это сделать этот макрос, но он почти сразу упирается в ограничения рекурсии и в целом кажется действительно неоптимальным:

macro_rules! opcodes(
    ($i:expr, $op:expr) => {
        opcode!($i, $op)
    };

    ($i:expr, $op:expr, $($more:expr), *) => (
        bits!($i, alt!(opcode!($op) | opcodes!($($more),*)))
    );
);

Есть ли способ реализовать что-то подобное или мне лучше просто использовать tag_bits? Я чувствую, что должен иметь возможность использовать one_of, но я не могу заставить его работать с байтами.


person ozdrgnaDiies    schedule 09.06.2017    source источник


Ответы (1)


Мы можем использовать one_of!(..), например так:

#[macro_use]
extern crate nom;
use nom::*;

// Your type might look a different, I infered what I could:
#[derive(Debug)]
enum Instruction {
    AbsoluteLong(u8, u32)
}

named!(absolute_long<Instruction>, do_parse!(
       op: one_of!([0x0F, 0x22, 0x2F, 0x4F, 0x5C, 0x6F, 0x8F, 0xAF, 0xCF, 0xEF].as_ref())
    >> param: le_u24
    >> (Instruction::AbsoluteLong(op as u8, param))
));

fn main() {
    println!("{:?}", absolute_long(&[0x0F, 0x01, 0x01, 0x01, 0x01]));
}

one_of! должен быть предоставлен срез для работы. Мы можем получить его, приведя к нему массив с помощью .as_ref().

person Centril    schedule 09.06.2017
comment
Благодарю вас! Я пытался работать с one_of! в течение некоторого времени, но, поскольку я новичок в Rust, я не знал о .as_ref() или о том, как интерпретировать ошибки, связанные со срезами. Делая это так, я избавляюсь от моих макросов и заставляю все эти парсеры выглядеть намного чище. - person ozdrgnaDiies; 09.06.2017
comment
Синтаксис массивов и фрагментов может быть немного сложным. Добро пожаловать =) - person Centril; 09.06.2017