pub trait Rule<'input, SliceType: ?Sized + 'input>: NamedRule {
type Output;
// Required method
fn parse_at<'cursor, 'this, 'index>(
&'this self,
input: &'cursor mut &'input SliceType,
index: &'index mut usize,
) -> Result<Self::Output, ParseError>
where 'input: 'this;
// Provided methods
fn parse<'this>(
&'this self,
input: &'input SliceType,
) -> Result<(&'input SliceType, Self::Output), ParseError>
where 'input: 'this { ... }
fn map_parsed<Output, F: Fn(Self::Output) -> Output>(
self,
function: F,
) -> Map<'input, SliceType, Self, Output, F>
where Self: Sized { ... }
fn try_map_parsed<Output, E: Error + 'static, F: Fn(Self::Output) -> Result<Output, E>>(
self,
function: F,
) -> TryMap<'input, SliceType, Self, Output, E, F>
where Self: Sized { ... }
fn prevent(self) -> Not<Self>
where Self: Sized { ... }
fn repeat_for<const REPS: usize>(
self,
) -> RepeatFor<'input, SliceType, Self, REPS>
where Self: Sized { ... }
fn repeat(self, reps: usize) -> Repeat<'input, SliceType, Self>
where Self: Sized { ... }
fn take(self, at_most: usize) -> Many<'input, SliceType, Self>
where Self: Sized { ... }
fn hoard(self) -> Many<'input, SliceType, Self>
where Self: Sized { ... }
fn consume_all(self) -> Consume<'input, SliceType, Self>
where Self: Sized,
Consume<'input, SliceType, Self>: Rule<'input, SliceType, Output = Vec<Self::Output>> { ... }
fn spanned(self) -> Spanned<'input, SliceType, Self>
where Self: Sized,
SliceType: Index<RangeTo<usize>, Output = SliceType> { ... }
fn attempt(self) -> Attempt<'input, SliceType, Self>
where Self: Sized { ... }
}
Expand description
Trait dictating that something can be used as a rule within a parsing grammar.
§Implementation
Imlpementing this trait means that anyone can use your struct as a rule in any of their their grammars with a supported slice type.
If you’re defining simple rules that don’t depend on the input, you can make rules generic over all slice types!
This is done for most of the “helper rules”, like Any
, in this crate.
Required Associated Types§
Required Methods§
sourcefn parse_at<'cursor, 'this, 'index>(
&'this self,
input: &'cursor mut &'input SliceType,
index: &'index mut usize,
) -> Result<Self::Output, ParseError>where
'input: 'this,
fn parse_at<'cursor, 'this, 'index>(
&'this self,
input: &'cursor mut &'input SliceType,
index: &'index mut usize,
) -> Result<Self::Output, ParseError>where
'input: 'this,
Parses a rule at a given index with a given input.
§Errors
Errors if the rule fails to parse.
§Correctness
When a parse succeeds, you must replace the borrowed input and index with a slice of it past the index you stopped parsing at - for example,
*input = &input[2..];
*index += 2;
You also must reset the values of input
and index
if an error occurs.
For example, this can be done as follows:
let before = (*input, *index);
// later...
let res = match inner_rule.parse_at(input, index) {
Ok(v) => v,
Err(err) => {
(*input, *index) = before;
return Err(err);
}
}
Fsilure to do uphold of these could cause other code using your rule to misbehave, potentially inducing panics and/or non-termination.
As this is not a safety contract, implementors cannot rely on this for soundness in unsafe
code.
Provided Methods§
sourcefn parse<'this>(
&'this self,
input: &'input SliceType,
) -> Result<(&'input SliceType, Self::Output), ParseError>where
'input: 'this,
fn parse<'this>(
&'this self,
input: &'input SliceType,
) -> Result<(&'input SliceType, Self::Output), ParseError>where
'input: 'this,
Examples found in repository?
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
fn main() -> Result<(), std::io::Error> {
let mut lines = std::io::stdin().lines();
println!("Input a math expression below, `clear` to clear the console, or `exit` / `quit` to exit.");
println!("You can access the result of the last expression with `ans`.");
let mut last_ans = None;
'outer: loop {
print!("[?] ");
std::io::stdout().flush()?;
let Some(input) = lines.next().transpose()? else { break Ok(()) };
let input = input.trim_ascii();
if input.is_empty() { print!("\x1b[1A"); continue; }
match input {
"clear" => print!("\x1bc"),
"exit" | "quit" => break Ok(()),
_ => {
let (_, mut tokens) = match MathTokens::LangTokens.parse(&input) {
Ok(v) => v,
Err(err) => {
println!("[!] Failed to parse: {err}");
continue;
}
};
for ans in tokens.iter_mut().filter(|t| matches!(t, Token::Ans)) {
let Some(answer) = last_ans else {
println!("[!] No previous answer exists");
continue 'outer;
};
*ans = Token::Number(answer);
}
let (_, result) = match TokenMath::Expr.parse(tokens.as_ref()) {
Ok(v) => v,
Err(err) => {
println!("[!] Failed to parse: {err}");
continue;
}
};
last_ans = Some(result);
println!("[=] {result:.}")
}
}
}
}
sourcefn map_parsed<Output, F: Fn(Self::Output) -> Output>(
self,
function: F,
) -> Map<'input, SliceType, Self, Output, F>where
Self: Sized,
fn map_parsed<Output, F: Fn(Self::Output) -> Output>(
self,
function: F,
) -> Map<'input, SliceType, Self, Output, F>where
Self: Sized,
Maps an infallible function onto the output of a rule.
sourcefn try_map_parsed<Output, E: Error + 'static, F: Fn(Self::Output) -> Result<Output, E>>(
self,
function: F,
) -> TryMap<'input, SliceType, Self, Output, E, F>where
Self: Sized,
fn try_map_parsed<Output, E: Error + 'static, F: Fn(Self::Output) -> Result<Output, E>>(
self,
function: F,
) -> TryMap<'input, SliceType, Self, Output, E, F>where
Self: Sized,
Maps a function onto the output of a rule, passing the error back if it fails.
sourcefn repeat_for<const REPS: usize>(
self,
) -> RepeatFor<'input, SliceType, Self, REPS>where
Self: Sized,
fn repeat_for<const REPS: usize>(
self,
) -> RepeatFor<'input, SliceType, Self, REPS>where
Self: Sized,
Repeats this rule a known amount of times.
sourcefn repeat(self, reps: usize) -> Repeat<'input, SliceType, Self>where
Self: Sized,
fn repeat(self, reps: usize) -> Repeat<'input, SliceType, Self>where
Self: Sized,
Repeats this rule a set amount of times.
sourcefn take(self, at_most: usize) -> Many<'input, SliceType, Self>where
Self: Sized,
fn take(self, at_most: usize) -> Many<'input, SliceType, Self>where
Self: Sized,
Repeats this rule at most a set amount of times.
sourcefn hoard(self) -> Many<'input, SliceType, Self>where
Self: Sized,
fn hoard(self) -> Many<'input, SliceType, Self>where
Self: Sized,
Repeats this rule forever until it fails.
sourcefn consume_all(self) -> Consume<'input, SliceType, Self>
fn consume_all(self) -> Consume<'input, SliceType, Self>
Repeats this rule until the end of input, failing if it ever does.
Examples found in repository?
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
pub LangTokens -> Vec<Token> = LangToken.consume_all()
.map_parsed(|v| v.into_iter().filter_map(|v| v).collect() );
LangToken -> Option<Token> =
Num : Plus : Minus : Asterisk : Slash : Percent : Carat
: LParen : RParen : Ans : _ Whitespace
: InvalidChar;
// Since Fail returns !, we can coerce from that to a token
InvalidChar -> Token from(|_, n| n) = Any, Fail::new(Unexpected::new(arg_0));
Plus -> Token = '+'.map_parsed(|_| Token::Plus);
Minus -> Token = '-'.map_parsed(|_| Token::Minus);
Asterisk -> Token = '*'.map_parsed(|_| Token::Asterisk);
Slash -> Token = '/'.map_parsed(|_| Token::Slash);
Percent -> Token = '%'.map_parsed(|_| Token::Percent);
Carat -> Token = '^'.map_parsed(|_| Token::Carat);
LParen -> Token = '('.map_parsed(|_| Token::LeftParen);
RParen -> Token = ')'.map_parsed(|_| Token::RightParen);
Ans -> Token = "ans".map_parsed(|_| Token::Ans);
Num -> Token from(|n| Token::Number(n)) =
("nan", "NaN").map_parsed(|_| f64::NAN) :
("inf", "Infinity").map_parsed(|_| f64::INFINITY) :
Float;
Float -> f64 try_from(f64::from_str) = FloatTokens.spanned().map_parsed(|span| span.source);
FloatTokens -> () = _ UInt, _ FloatFract.attempt(), _ FloatExp.attempt();
FloatFract -> () = _ '.', _ UInt;
FloatExp -> () = _ ('e', 'E'), _ ('-', '+').attempt(), _ UInt;
UInt -> &'input str = While::from(char::is_ascii_digit);
}
}
define! {
grammar TokenMath<[Token]> {
pub Expr -> f64 from(parse_expr) = Prod, SumSuf.consume_all();
Implementations on Foreign Types§
source§impl<'input, SliceType: ?Sized + 'input, Output, T> Rule<SliceType> for (T₁, T₂, …, Tₙ)where
T: Rule<'input, SliceType, Output = Output>,
impl<'input, SliceType: ?Sized + 'input, Output, T> Rule<SliceType> for (T₁, T₂, …, Tₙ)where
T: Rule<'input, SliceType, Output = Output>,
For tuples up to arity 16, Rule
is implemented as a sort of “anonymous group”,
where each rule will be tried in sequence until one matches, or an error will be raised if none do.
The more_tuple_impls
feature raises the implemented arity to 256, but massively increases compilation time.
Use only when strictly necessary.