Extract combinator from Hurl parser and use generic for Error.

This commit is contained in:
Jean-Christophe Amiel 2024-06-28 15:22:02 +02:00
parent 8507d11a7a
commit 8554f74bdd
No known key found for this signature in database
GPG Key ID: 07FF11CFD55356CC
17 changed files with 123 additions and 84 deletions

View File

@ -15,16 +15,34 @@
* limitations under the License.
*
*/
use crate::parser::error::*;
use crate::parser::{ParseFunc, ParseResult};
use crate::reader::Reader;
pub fn optional<T>(f: ParseFunc<T>, reader: &mut Reader) -> ParseResult<Option<T>> {
/// Represent a parser error. This type of error ca be recoverable or not and
/// implements conversion to recoverable / non-recoverable instance.
pub trait ParseError {
/// Is this error recoverable or not?
fn is_recoverable(&self) -> bool;
/// Transforms this error to a recoverable one.
fn to_recoverable(self) -> Self;
/// Transforms this error to a non-recoverable one.
fn to_non_recoverable(self) -> Self;
}
/// A parser func.
pub type ParseFunc<T, E> = fn(&mut Reader) -> Result<T, E>;
/// Try to consume one instances of the provided parser.
pub fn optional<T, E>(f: ParseFunc<T, E>, reader: &mut Reader) -> Result<Option<T>, E>
where
E: ParseError,
{
let start = reader.state;
match f(reader) {
Ok(r) => Ok(Some(r)),
Err(e) => {
if e.recoverable {
if e.is_recoverable() {
reader.state = start;
Ok(None)
} else {
@ -34,26 +52,34 @@ pub fn optional<T>(f: ParseFunc<T>, reader: &mut Reader) -> ParseResult<Option<T
}
}
pub fn recover<T>(f: ParseFunc<T>, reader: &mut Reader) -> ParseResult<T> {
// make an error recoverable
// but does not reset cursor
/// Makes an error recoverable but does not reset cursor.
pub fn recover<T, E>(f: ParseFunc<T, E>, reader: &mut Reader) -> Result<T, E>
where
E: ParseError,
{
match f(reader) {
Ok(r) => Ok(r),
Err(e) => Err(ParseError::new(e.pos, true, e.kind)),
Err(e) => Err(e.to_recoverable()),
}
}
pub fn nonrecover<T>(f: ParseFunc<T>, reader: &mut Reader) -> ParseResult<T> {
/// Makes an error non recoverable.
pub fn non_recover<T, E>(f: ParseFunc<T, E>, reader: &mut Reader) -> Result<T, E>
where
E: ParseError,
{
match f(reader) {
Ok(r) => Ok(r),
Err(e) => Err(ParseError::new(e.pos, false, e.kind)),
Err(e) => Err(e.to_non_recoverable()),
}
}
pub fn zero_or_more<T>(f: ParseFunc<T>, reader: &mut Reader) -> ParseResult<Vec<T>> {
let _start = reader.state;
let mut v: Vec<T> = Vec::new();
/// Consumes zero or more instances of the provided parser.
pub fn zero_or_more<T, E>(f: ParseFunc<T, E>, reader: &mut Reader) -> Result<Vec<T>, E>
where
E: ParseError,
{
let mut v = Vec::new();
loop {
let initial_state = reader.state;
if reader.is_eof() {
@ -65,7 +91,7 @@ pub fn zero_or_more<T>(f: ParseFunc<T>, reader: &mut Reader) -> ParseResult<Vec<
v.push(r);
}
Err(e) => {
return if e.recoverable {
return if e.is_recoverable() {
reader.state.pos = initial_state.pos;
reader.state.cursor = initial_state.cursor;
Ok(v)
@ -77,8 +103,11 @@ pub fn zero_or_more<T>(f: ParseFunc<T>, reader: &mut Reader) -> ParseResult<Vec<
}
}
pub fn one_or_more<T>(f: ParseFunc<T>, reader: &mut Reader) -> ParseResult<Vec<T>> {
let _initial_state = reader.state;
/// Consumes one or more instances of the provided parser.
pub fn one_or_more<T, E>(f: ParseFunc<T, E>, reader: &mut Reader) -> Result<Vec<T>, E>
where
E: ParseError,
{
match f(reader) {
Ok(first) => {
let mut v = vec![first];
@ -89,7 +118,7 @@ pub fn one_or_more<T>(f: ParseFunc<T>, reader: &mut Reader) -> ParseResult<Vec<T
v.push(r);
}
Err(e) => {
return if e.recoverable {
return if e.is_recoverable() {
reader.state.pos = initial_state.pos;
reader.state.cursor = initial_state.cursor;
Ok(v)
@ -101,27 +130,28 @@ pub fn one_or_more<T>(f: ParseFunc<T>, reader: &mut Reader) -> ParseResult<Vec<T
}
}
// if zero occurrence => should fail?
Err(ParseError { pos, kind, .. }) => Err(ParseError::new(pos, false, kind)),
Err(e) => Err(e.to_non_recoverable()),
}
}
/// Tries to apply the list of parser functions `fs` until one of them succeeds.
/// Typically this should be recoverable
pub fn choice<T>(fs: &[ParseFunc<T>], reader: &mut Reader) -> ParseResult<T> {
/// Tries to apply the list of parser until one of them succeeds.
/// Typically, this should be recoverable
pub fn choice<T, E>(fs: &[ParseFunc<T, E>], reader: &mut Reader) -> Result<T, E>
where
E: ParseError,
{
for (pos, f) in fs.iter().enumerate() {
let start = reader.state;
if pos == fs.len() - 1 {
return f(reader);
}
match f(reader) {
Err(ParseError {
recoverable: true, ..
}) => {
Err(err) if err.is_recoverable() => {
reader.state = start;
continue;
}
x => return x,
}
}
panic!("You can't call choice with an empty vector of choice")
unreachable!("You can't call choice with an empty vector of choice")
}

View File

@ -16,6 +16,7 @@
*
*/
pub mod ast;
pub mod combinator;
pub mod error;
pub mod format;
pub mod parser;

View File

@ -16,7 +16,7 @@
*
*/
use crate::ast::*;
use crate::parser::combinators::*;
use crate::combinator::choice;
use crate::parser::json::parse as parse_json;
use crate::parser::multiline::multiline_string;
use crate::parser::primitives::*;

View File

@ -16,7 +16,7 @@
*
*/
use crate::ast::*;
use crate::parser::combinators::*;
use crate::combinator::optional;
use crate::parser::error::*;
use crate::parser::primitives::*;
use crate::parser::string::*;

View File

@ -80,16 +80,6 @@ impl ParseError {
kind,
}
}
/// Makes a recoverable error.
pub fn recoverable(&self) -> ParseError {
ParseError::new(self.pos, true, self.kind.clone())
}
/// Makes a non recoverable error.
pub fn non_recoverable(&self) -> ParseError {
ParseError::new(self.pos, false, self.kind.clone())
}
}
impl DisplaySourceError for ParseError {
@ -254,6 +244,26 @@ impl DisplaySourceError for ParseError {
}
}
impl crate::combinator::ParseError for ParseError {
fn is_recoverable(&self) -> bool {
self.recoverable
}
fn to_recoverable(self) -> Self {
ParseError {
recoverable: true,
..self
}
}
fn to_non_recoverable(self) -> Self {
ParseError {
recoverable: false,
..self
}
}
}
fn did_you_mean(valid_values: &[&str], actual: &str, default: &str) -> String {
if let Some(suggest) = suggestion(valid_values, actual) {
format!("Did you mean {suggest}?")

View File

@ -16,7 +16,7 @@
*
*/
use crate::ast::{Filter, FilterValue, SourceInfo, Whitespace};
use crate::parser::combinators::choice;
use crate::combinator::{choice, ParseError as ParseErrorTrait};
use crate::parser::number::natural;
use crate::parser::primitives::{one_or_more_spaces, try_literal, zero_or_more_spaces};
use crate::parser::query::regex_value;
@ -131,7 +131,7 @@ fn html_decode_filter(reader: &mut Reader) -> ParseResult<FilterValue> {
fn jsonpath_filter(reader: &mut Reader) -> ParseResult<FilterValue> {
try_literal("jsonpath", reader)?;
let space0 = one_or_more_spaces(reader)?;
let expr = quoted_template(reader).map_err(|e| e.non_recoverable())?;
let expr = quoted_template(reader).map_err(|e| e.to_non_recoverable())?;
Ok(FilterValue::JsonPath { space0, expr })
}
@ -154,7 +154,7 @@ fn replace_filter(reader: &mut Reader) -> ParseResult<FilterValue> {
let space0 = one_or_more_spaces(reader)?;
let old_value = regex_value(reader)?;
let space1 = one_or_more_spaces(reader)?;
let new_value = quoted_template(reader).map_err(|e| e.non_recoverable())?;
let new_value = quoted_template(reader).map_err(|e| e.to_non_recoverable())?;
Ok(FilterValue::Replace {
space0,
old_value,
@ -166,7 +166,7 @@ fn replace_filter(reader: &mut Reader) -> ParseResult<FilterValue> {
fn split_filter(reader: &mut Reader) -> ParseResult<FilterValue> {
try_literal("split", reader)?;
let space0 = one_or_more_spaces(reader)?;
let sep = quoted_template(reader).map_err(|e| e.non_recoverable())?;
let sep = quoted_template(reader).map_err(|e| e.to_non_recoverable())?;
Ok(FilterValue::Split { space0, sep })
}
@ -200,7 +200,7 @@ fn url_decode_filter(reader: &mut Reader) -> ParseResult<FilterValue> {
fn xpath_filter(reader: &mut Reader) -> ParseResult<FilterValue> {
try_literal("xpath", reader)?;
let space0 = one_or_more_spaces(reader)?;
let expr = quoted_template(reader).map_err(|e| e.non_recoverable())?;
let expr = quoted_template(reader).map_err(|e| e.to_non_recoverable())?;
Ok(FilterValue::XPath { space0, expr })
}

View File

@ -16,7 +16,7 @@
*
*/
use crate::ast::{JsonListElement, JsonObjectElement, JsonValue, SourceInfo, Template};
use crate::parser::combinators::*;
use crate::combinator::{choice, non_recover, ParseError as ParseErrorTrait};
use crate::parser::error::*;
use crate::parser::primitives::*;
use crate::parser::template::*;
@ -203,10 +203,10 @@ fn cp_surrogate_pair(cp1: u32, cp2: u32) -> Option<u32> {
}
fn hex_value(reader: &mut Reader) -> ParseResult<u32> {
let digit1 = nonrecover(hex_digit, reader)?;
let digit2 = nonrecover(hex_digit, reader)?;
let digit3 = nonrecover(hex_digit, reader)?;
let digit4 = nonrecover(hex_digit, reader)?;
let digit1 = non_recover(hex_digit, reader)?;
let digit2 = non_recover(hex_digit, reader)?;
let digit3 = non_recover(hex_digit, reader)?;
let digit4 = non_recover(hex_digit, reader)?;
let value = digit1 * (16 * 16 * 16) + digit2 * (16 * 16) + digit3 * 16 + digit4;
Ok(value)
}
@ -363,7 +363,7 @@ pub fn object_value(reader: &mut Reader) -> ParseResult<JsonValue> {
}
fn key(reader: &mut Reader) -> ParseResult<Template> {
let name = string_template(reader).map_err(|e| e.non_recoverable())?;
let name = string_template(reader).map_err(|e| e.to_non_recoverable())?;
Ok(name)
}

View File

@ -16,7 +16,6 @@
*
*/
pub type ParseResult<T> = Result<T, ParseError>;
pub type ParseFunc<T> = fn(&mut Reader) -> ParseResult<T>;
pub fn parse_hurl_file(s: &str) -> ParseResult<HurlFile> {
let mut reader = Reader::new(s);
@ -34,7 +33,6 @@ use crate::reader::Reader;
mod base64;
mod bytes;
mod combinators;
mod cookiepath;
mod error;
mod expr;

View File

@ -16,7 +16,7 @@
*
*/
use crate::ast::*;
use crate::parser::combinators::*;
use crate::combinator::{choice, optional, zero_or_more};
use crate::parser::json::object_value;
use crate::parser::primitives::*;
use crate::parser::{template, ParseError, ParseErrorKind, ParseResult};

View File

@ -16,7 +16,7 @@
*
*/
use crate::ast::*;
use crate::parser::combinators::*;
use crate::combinator::{choice, non_recover};
use crate::parser::error::*;
use crate::parser::number::{integer, natural, number};
use crate::parser::primitives::*;
@ -108,7 +108,7 @@ fn option_cert(reader: &mut Reader) -> ParseResult<OptionKind> {
}
fn option_compressed(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::Compressed(value))
}
@ -123,47 +123,47 @@ fn option_delay(reader: &mut Reader) -> ParseResult<OptionKind> {
}
fn option_follow_location(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::FollowLocation(value))
}
fn option_follow_location_trusted(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::FollowLocationTrusted(value))
}
fn option_http_10(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::Http10(value))
}
fn option_http_11(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::Http11(value))
}
fn option_http_2(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::Http2(value))
}
fn option_http_3(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::Http3(value))
}
fn option_insecure(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::Insecure(value))
}
fn option_ipv4(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::IpV4(value))
}
fn option_ipv6(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::IpV6(value))
}
@ -173,12 +173,12 @@ fn option_key(reader: &mut Reader) -> ParseResult<OptionKind> {
}
fn option_max_redirect(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(natural_option, reader)?;
let value = non_recover(natural_option, reader)?;
Ok(OptionKind::MaxRedirect(value))
}
fn option_netrc(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::NetRc(value))
}
@ -188,7 +188,7 @@ fn option_netrc_file(reader: &mut Reader) -> ParseResult<OptionKind> {
}
fn option_netrc_optional(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::NetRcOptional(value))
}
@ -198,7 +198,7 @@ fn option_output(reader: &mut Reader) -> ParseResult<OptionKind> {
}
fn option_path_as_is(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::PathAsIs(value))
}
@ -223,12 +223,12 @@ fn option_retry(reader: &mut Reader) -> ParseResult<OptionKind> {
}
fn option_retry_interval(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(natural_option, reader)?;
let value = non_recover(natural_option, reader)?;
Ok(OptionKind::RetryInterval(value))
}
fn option_skip(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::Skip(value))
}
@ -248,18 +248,18 @@ fn option_variable(reader: &mut Reader) -> ParseResult<OptionKind> {
}
fn option_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::Verbose(value))
}
fn option_very_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = nonrecover(boolean_option, reader)?;
let value = non_recover(boolean_option, reader)?;
Ok(OptionKind::VeryVerbose(value))
}
fn repeat(reader: &mut Reader) -> ParseResult<Repeat> {
let pos = reader.state.pos;
let value = nonrecover(integer, reader)?;
let value = non_recover(integer, reader)?;
if value == -1 {
Ok(Repeat::Forever)
} else if value >= 0 {
@ -274,7 +274,7 @@ fn repeat(reader: &mut Reader) -> ParseResult<Repeat> {
fn retry(reader: &mut Reader) -> ParseResult<Retry> {
let pos = reader.state.pos;
let value = nonrecover(integer, reader)?;
let value = non_recover(integer, reader)?;
if value == -1 {
Ok(Retry::Infinite)
} else if value >= 0 {

View File

@ -17,8 +17,8 @@
*/
use crate::ast::VersionValue::VersionAny;
use crate::ast::*;
use crate::combinator::{optional, zero_or_more};
use crate::parser::bytes::*;
use crate::parser::combinators::*;
use crate::parser::error::*;
use crate::parser::number::natural;
use crate::parser::primitives::*;

View File

@ -16,7 +16,7 @@
*
*/
use crate::ast::*;
use crate::parser::combinators::*;
use crate::combinator::choice;
use crate::parser::error::*;
use crate::parser::predicate_value::predicate_value;
use crate::parser::primitives::*;

View File

@ -16,7 +16,7 @@
*
*/
use crate::ast::*;
use crate::parser::combinators::*;
use crate::combinator::choice;
use crate::parser::multiline::multiline_string;
use crate::parser::number::number;
use crate::parser::primitives::*;

View File

@ -16,7 +16,7 @@
*
*/
use crate::ast::*;
use crate::parser::combinators::*;
use crate::combinator::{one_or_more, optional, recover, zero_or_more};
use crate::parser::error::*;
use crate::parser::string::*;
use crate::parser::{base64, filename, key_string, ParseResult};

View File

@ -16,7 +16,7 @@
*
*/
use crate::ast::*;
use crate::parser::combinators::*;
use crate::combinator::{choice, ParseError as ParseErrorTrait};
use crate::parser::cookiepath::cookiepath;
use crate::parser::primitives::*;
use crate::parser::string::*;
@ -68,7 +68,7 @@ fn url_query(reader: &mut Reader) -> ParseResult<QueryValue> {
fn header_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("header", reader)?;
let space0 = one_or_more_spaces(reader)?;
let name = quoted_template(reader).map_err(|e| e.non_recoverable())?;
let name = quoted_template(reader).map_err(|e| e.to_non_recoverable())?;
Ok(QueryValue::Header { space0, name })
}
@ -98,7 +98,7 @@ fn body_query(reader: &mut Reader) -> ParseResult<QueryValue> {
fn xpath_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("xpath", reader)?;
let space0 = one_or_more_spaces(reader)?;
let expr = quoted_template(reader).map_err(|e| e.non_recoverable())?;
let expr = quoted_template(reader).map_err(|e| e.to_non_recoverable())?;
Ok(QueryValue::Xpath { space0, expr })
}
@ -107,7 +107,7 @@ fn jsonpath_query(reader: &mut Reader) -> ParseResult<QueryValue> {
let space0 = one_or_more_spaces(reader)?;
//let expr = jsonpath_expr(reader)?;
// let start = reader.state.pos.clone();
let expr = quoted_template(reader).map_err(|e| e.non_recoverable())?;
let expr = quoted_template(reader).map_err(|e| e.to_non_recoverable())?;
// let end = reader.state.pos.clone();
// let expr = Template {
// elements: template.elements.iter().map(|e| match e {
@ -155,7 +155,7 @@ pub fn regex_value(reader: &mut Reader) -> ParseResult<RegexValue> {
fn variable_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("variable", reader)?;
let space0 = one_or_more_spaces(reader)?;
let name = quoted_template(reader).map_err(|e| e.non_recoverable())?;
let name = quoted_template(reader).map_err(|e| e.to_non_recoverable())?;
Ok(QueryValue::Variable { space0, name })
}

View File

@ -16,7 +16,7 @@
*
*/
use crate::ast::*;
use crate::parser::combinators::*;
use crate::combinator::{optional, recover, zero_or_more};
use crate::parser::error::*;
use crate::parser::filter::filters;
use crate::parser::predicate::predicate;

View File

@ -16,7 +16,7 @@
*
*/
use crate::ast::*;
use crate::parser::combinators::*;
use crate::combinator::one_or_more;
use crate::parser::error::*;
use crate::parser::primitives::*;
use crate::parser::{template, ParseResult};