Macro utils::parser_parse_tree

source ·
macro_rules! parser_parse_tree {
    (@rule $input:ident $furthest_err:ident $furthest_remaining:ident [$(,)?] @expr $rhs:expr) => { ... };
    (@rule $input:ident $furthest_err:ident $furthest_remaining:ident [$(,)?] @expr_res $rhs:expr) => { ... };
    (@rule $input:ident $furthest_err:ident $furthest_remaining:ident [$(,)?] @subtree $($rhs:tt)+) => { ... };
    (@rule $input:ident $furthest_err:ident $furthest_remaining:ident
        [$n:ident @ $lhs:expr $(,$($tail:tt)*)?] $($rhs:tt)+
    ) => { ... };
    (@rule $input:ident $furthest_err:ident $furthest_remaining:ident
        [$lhs:expr $(,$($tail:tt)*)?] $($rhs:tt)+
    ) => { ... };
    (@toplevel $input:ident $furthest_err:ident $furthest_remaining:ident
        ($($lhs:tt)+) => $rhs:expr $(, $($tail:tt)*)?
    ) => { ... };
    (@toplevel $input:ident $furthest_err:ident $furthest_remaining:ident
        ($($lhs:tt)+) =?> $rhs:expr $(, $($tail:tt)*)?
    ) => { ... };
    (@toplevel $input:ident $furthest_err:ident $furthest_remaining:ident
        ($($lhs:tt)+) =>> {$($rhs:tt)+} $(, $($tail:tt)*)?
    ) => { ... };
    (@toplevel $input:ident $furthest_err:ident $furthest_remaining:ident $(,)?) => { ... };
    (($($first:tt)+) $($tail:tt)+) => { ... };
}
Expand description

Macro to define a custom parser using a match inspired parse tree syntax.

Each rule is made up of a list of chained parsers enclosed in brackets on the left-hand side. Parsers can be prefixed with an identifier followed by @ to store the result of that parser in the supplied variable, similar to normal match patterns.

After the list of parsers, there is an arrow determining the functionality of the rule when the parsers match:

  • Expression (=>): The expression on the right-hand is evaluated and returned.
  • Fallible (=?>): Similar to Expression, but the right-hand side evaluates a result. If the expression evaluates to Ok, the value contained inside is returned. Otherwise, the string contained inside the Err is handled as a custom ParseError, and parsing will continue with the following rule.
  • Subtree (=>>): The right-hand side is a nested set of rules enclosed in braces.

If none of the rules match successfully, the error from the rule which parsed furthest into the input is returned.

§Examples

#[derive(Debug, PartialEq)]
enum Register {
    A, B, C
}

#[derive(Debug, PartialEq)]
enum Instruction {
    Add(Register, Register),
    AddConstant(Register, i32),
    Copy(Register, Register),
    Noop,
}

let register = parser::literal_map!(
    "A" => Register::A, "B" => Register::B, "C" => Register::C,
);

let instruction = parser::parse_tree!(
    ("add ", r @ register, ", ") =>> {
        (r2 @ register) => Instruction::Add(r, r2),
        (v @ parser::i32()) => Instruction::AddConstant(r, v),
    },
    ("copy ", r @ register, ", ", r2 @ register) =?> {
        if r == r2 {
            Err("cannot copy register to itself")
        } else {
            Ok(Instruction::Copy(r, r2))
        }
    },
    ("noop") => Instruction::Noop,
);

assert_eq!(
    instruction.parse_complete("add A, B").unwrap(),
    Instruction::Add(Register::A, Register::B)
);
assert_eq!(
    instruction.parse_complete("add C, 100").unwrap(),
    Instruction::AddConstant(Register::C, 100)
);
assert_eq!(
    instruction.parse_complete("copy A, B").unwrap(),
    Instruction::Copy(Register::A, Register::B)
);
assert!(instruction
    .parse_complete("copy A, A")
    .is_err_and(|err| err.to_string().contains("cannot copy register to itself")));