ozdrgnaDiies
ozdrgnaDiies

Reputation: 1929

Match one of several bytes in nom

I am using nom to parse some binary 65c816 assembly data. Part of my parser structure splits opcodes into their separate forms and then grabs their parameters if they have any. An example parser currently looks like this:

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))));

Where opcode! is a macro I made that does the following:

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

I would instead like to have code like the following but am unable to figure out how I'd make a macro or function to do it:

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)));

The closest I got was making this macro, but it almost immediately runs into recursion limits and it seems really sub-optimal in general:

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

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

Is there any way I can implement something like this or am I better off just using tag_bits? I feel like I should be able to use one_of, but I am unable to get it to work with bytes.

Upvotes: 2

Views: 765

Answers (1)

Centril
Centril

Reputation: 2648

We can use one_of!(..) like so:

#[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! must be given a slice to work with. We can get one by casting an array to it with .as_ref().

Upvotes: 3

Related Questions