macro_rules! construct { ($(::)? $ns:ident $(:: $con:ident)* { $($tokens:tt)* }) => { ... }; ($(::)? $ns:ident $(:: $con:ident)* ( $($tokens:tt)* )) => { ... }; ($first:ident $($tokens:tt)*) => { ... }; ([$first:ident $($tokens:tt)*]) => { ... }; (@prepare $ty:tt [$($fields:tt)*] $field:ident () $(, $($rest:tt)*)? ) => { ... }; (@prepare $ty:tt [$($fields:tt)*] $field:ident ($expr:expr) $(, $($rest:tt)*)?) => { ... }; (@prepare $ty:tt [$($fields:tt)*] $field:ident $(, $($rest:tt)*)? ) => { ... }; (@prepare [alt] [$first:ident $($fields:ident)*]) => { ... }; (@prepare $ty:tt [$($fields:tt)*]) => { ... }; (@make [named [$($con:tt)+]] [$($fields:ident)*]) => { ... }; (@make [pos [$($con:tt)+]] [$($fields:ident)*]) => { ... }; (@make [pos] [$($fields:ident)*]) => { ... }; (@fin [named [$($con:tt)+]] []) => { ... }; (@fin [pos [$($con:tt)+]] []) => { ... }; (@fin [pos] [$field:ident]) => { ... }; (@fin $ty:tt [$front:ident $($fields:ident)*]) => { ... }; }
Expand description
Compose several parsers to produce a single result
§Usage reference
// structs with unnamed fields:
construct!(Res(a, b, c));
// structs with named fields:
construct!(Res {a, b, c});
// enums with unnamed fields:
construct!(Ty::Res(a, b, c));
// enums with named fields:
construct!(Ty::Res {a, b, c});
// tuples:
construct!(a, b, c);
// parallel composition, tries all parsers, picks one that consumes the left most value,
// or if they consume the same (or not at all) - the left most in a list
construct!([a, b, c]);
// defining primitive parsers inside construct macro :)
construct!(a(short('a').switch()), b(long("arg").argument::<usize>("ARG")));
// defining a boxed parser
construct!(a);
§Combinatoric usage
construct!
can compose parsers sequentially or in parallel.
Sequential composition runs each parser and if all of them succeed you get a parser object of a
new type back. Placeholder names for values inside construct!
macro must correspond to both
struct/enum names and parser names present in scope. In examples below a
corresponds to a
function and b
corresponds to a variable name. Note parens in a()
, you must to use them to
indicate function parsers.
Inside the parens you can put a whole expression to use instead of
having to define them in advance: a(positional::<String>("POS"))
. Probably a good idea to use this
approach only for simple parsers.
struct Res (u32, u32);
enum Ul { T { a: u32, b: u32 } }
// You can share parameters across multiple construct invocations
// if defined as functions
fn a() -> impl Parser<u32> {
short('a').argument::<u32>("N")
}
// You can construct structs or enums with unnamed fields
fn res() -> impl Parser<Res> {
let b = short('b').argument::<u32>("n");
construct!(Res ( a(), b ))
}
// You can construct structs or enums with named fields
fn ult() -> impl Parser<Ul> {
let b = short('b').argument::<u32>("n");
construct!(Ul::T { a(), b })
}
// You can also construct simple tuples
fn tuple() -> impl Parser<(u32, u32)> {
let b = short('b').argument::<u32>("n");
construct!(a(), b)
}
// You can create boxed version of parsers so the type matches as long
// as return type is the same - can be useful for all sort of dynamic parsers
fn boxed() -> Box<dyn Parser<u32>> {
let a = short('a').argument::<u32>("n");
construct!(a)
}
// In addition to having primitives defined before using them - you can also define
// them directly inside construct macro. Probably only a good idea if you have only simple
// components
struct Options {
arg: u32,
switch: bool,
}
fn coyoda() -> impl Parser<Options> {
construct!(Options {
arg(short('a').argument::<u32>("ARG")),
switch(short('s').switch())
})
}
Parallel composition picks one of several available parsers (result types must match) and returns a parser object of the same type. Similar to sequential composition you can use parsers from variables or functions:
fn b() -> impl Parser<u32> {
short('b').argument::<u32>("NUM")
}
fn a_or_b() -> impl Parser<u32> {
let a = short('a').argument::<u32>("NUM");
// equivalent way of writing this would be `a.or_else(b())`
construct!([a, b()])
}
§Derive usage
bpaf
would combine fields of struct or enum constructors sequentially and enum
variants in parallel.
// to satisfy this parser user needs to pass both -a and -b
#[derive(Debug, Clone, Bpaf)]
struct Res {
a: u32,
b: u32,
}
// to satisfy this parser user needs to pass one (and only one) of -a, -b, -c or -d
#[derive(Debug, Clone, Bpaf)]
enum Enumeraton {
A { a: u32 },
B { b: u32 },
C { c: u32 },
D { d: u32 },
}
// here user needs to pass either both -a AND -b or both -c AND -d
#[derive(Debug, Clone, Bpaf)]
enum Ult {
AB { a: u32, b: u32 },
CD { c: u32, d: u32 }
}