pub trait Parser<Ctx>: Default {
type Value;
// Required method
fn parse<'a>(
&self,
input: TokenStream<'a>,
ctx: Ctx,
) -> ParseResult<'a, Self::Value>;
}Expand description
Definition of the parsing and completion algorithm for some type
This trait is fundamental for the functionality of kmdparse. The implementers must define
two operations: parsing (converting the input TokenStream into a value of a target type)
and completion (generating the set of possible completions for the last meaningful token in the
input stream).
Most often, the types being parsed are compound, meaning they contain multiple fields with
different parsers. It is best to keep parsers as simple as possible and delegate most of the
work to the child parsers. To ensure correct interaction between parsers, custom
implementations must follow the parsing protocol. The rules are described in the documentation
for each of the Parser’s methods.
Please note, that in most cases writing the parser by hand isn’t necessary. Parser is
automatically generated for any type that derives Parsable. The name of the generated
parser is constructed by appending the word Parser to the end of the type.
§Context
The Parser trait is generic over an arbitrary context. Context is passed as an argument to both
of the Parser’s methods and is intended to make parsers configurable, meaning their behavior
can depend on some information available at runtime.
The following example demonstrates how to implement the parser for a variant-like data, dependent on data available at runtime.
use kmdparse::{Parser, CompletionResult, ParseResult, parse_parser, complete_parser};
use kmdparse::tokens::{TokenStream, Token};
use kmdparse::error::{ParseError, UnrecognizedToken};
use std::borrow::Cow;
use std::collections::{BTreeSet, HashMap};
struct RuntimeContext { variables: HashMap<String, u32> }
#[derive(Default)]
struct VariableParser;
impl<'c> Parser<&'c RuntimeContext> for VariableParser {
type Value = u32;
fn parse<'a>(&self, input: TokenStream<'a>, ctx: &'c RuntimeContext) -> ParseResult<'a, Self::Value> {
match input.take().transpose()? {
None => Err(ParseError::token_required().expected("variable").into()),
Some((attr @ Token::Attribute(_), remaining)) => {
Err(UnrecognizedToken::new(attr, remaining).into())
}
Some((token @ Token::Text(text), remaining)) => {
let text = text.parse_string();
match ctx.variables.get(&text as &str) {
Some(value) => Ok((*value, remaining)),
None => Err(UnrecognizedToken::new(token, remaining).into()),
}
}
}
}
fn complete<'a>(&self, input: TokenStream<'a>, ctx: &'c RuntimeContext) -> CompletionResult<'a> {
match input.take() {
Some(Err(_)) | None => CompletionResult::new_final(false),
Some(Ok((Token::Attribute(_), _))) => CompletionResult::new(input, false),
Some(Ok((Token::Text(text), remaining))) if remaining.is_all_consumed() => {
let text = text.parse_string();
CompletionResult::new_final(true).add_suggestions(
ctx.variables.keys()
.filter_map(|key| key.strip_prefix(&text as &str))
.map(|suggestion| Cow::Owned(suggestion.to_string()))
)
}
Some(Ok((Token::Text(_), remaining))) => CompletionResult::new(remaining, true),
}
}
}
let context = RuntimeContext {
variables: HashMap::from([("var-1".to_string(), 10), ("var-2".to_string(), 20)]),
};
assert_eq!(parse_parser::<_, VariableParser>("var-1", &context)?, 10);
assert_eq!(parse_parser::<_, VariableParser>("var-2", &context)?, 20);
assert_eq!(
complete_parser::<_, VariableParser>("va", &context),
BTreeSet::from(["r-1".into(), "r-2".into()]),
);Parser implementation should be as generic as possible to avoid type errors when integrating with other parsers.
Required Associated Types§
Required Methods§
Sourcefn parse<'a>(
&self,
input: TokenStream<'a>,
ctx: Ctx,
) -> ParseResult<'a, Self::Value>
fn parse<'a>( &self, input: TokenStream<'a>, ctx: Ctx, ) -> ParseResult<'a, Self::Value>
Parsers the beginning of the token stream into a Value.
This function performs the parsing of the input stream: it repeatedly consumes tokens from the token stream and then produces one of the following return values:
Ok((value, remaining))in case of the correctly parsed sequence of tokens. Herevalueis the result of the parsing, (it has typeSelf::Value), and remaining is the token stream representing the set of tokens that wasn’t consumed;Err(ParseFailure::Error(error))in case the parser failed with an error indicating the malformed input. SeeParseError;Err(ParseFailure::Unexpected(unexpected_token))if the first token in the input stream is an attribute or an enum variant discriminator that the parser does not recognize.
To be interoperable with other parsers, the parse implementation must follow the parsing protocol:
- if the first token in the input stream is an attribute and the parser does not recognize
this attribute, it should return
Err(UnexpectedToken::new(token, remaining).into())wheretokenis the attribute that was not recognized, andremainingis the token stream consisting of tokens directly following token; - if the parser expects the enum variant discriminator and the first token of the input is
not recognized as such, it should return
Err(UnexpectedToken::new(token, remaining).into())with the same values as described above; - the parser must not return [
UnexpectedToken] result with any token other than the first token of the input stream; if it receives this value from an inner parser, it must convert it into the equivalent error if the parser was not called on the original input; - when all required tokens are successfully consumed parser should continue to take tokens until a text token or an attribute that is not recognized is encountered (this is not necessary if parser does not expect attributes)
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.
Implementors§
Source§impl<Ctx> Parser<Ctx> for PathParser
Available on crate feature std only.
impl<Ctx> Parser<Ctx> for PathParser
std only.Source§impl<Ctx> Parser<Ctx> for StringParser
Available on crate feature std only.
impl<Ctx> Parser<Ctx> for StringParser
std only.