#[macro_use]
mod tests;
use std::rc::Rc;
use std::fmt::*;
use std::collections::HashSet;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Located<A> {
pub value: A,
pub from: Location,
pub to: Location,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Location {
pub row: usize,
pub col: usize,
}
#[derive(Debug, PartialEq, Eq)]
pub enum ParseResult<'a, Output, State> {
Ok {
input: &'a str,
location: Location,
output: Output,
state: State,
bound: bool,
},
Err {
message: String,
from: Location,
to: Location,
state: State,
bound: bool,
},
}
impl<'a, T, S: Clone> ParseResult<'a, T, S> {
pub fn map<U, F: FnOnce(T) -> U>(self, func: F) -> ParseResult<'a, U, S> {
match self {
ParseResult::Ok { input, location, output, state, bound } =>
ParseResult::Ok {
input,
location,
output: func(output),
state,
bound,
},
ParseResult::Err { message, from, to, state, bound } =>
ParseResult::Err { message, from, to, state, bound },
}
}
pub fn map_with_state<U, F: FnOnce(T, S) -> U>(self, func: F) -> ParseResult<'a, U, S> {
match self {
ParseResult::Ok { input, location, output, state, bound } =>
ParseResult::Ok {
input,
location,
output: func(output, state.clone()),
state,
bound,
},
ParseResult::Err { message, from, to, state, bound } =>
ParseResult::Err { message, from, to, state,bound },
}
}
pub fn map_err<F: FnOnce(String) -> String>(self, func: F) -> ParseResult<'a, T, S> {
match self {
ParseResult::Ok { input, location, output, state, bound } =>
ParseResult::Ok {
input,
location,
output,
state,
bound,
},
ParseResult::Err { message, from, to, state, bound } =>
ParseResult::Err {
message: func(message),
from,
to,
state,
bound,
}
}
}
pub fn and_then<U, F: FnOnce(&'a str, T, Location, S) -> ParseResult<'a, U, S>>(self, func: F) -> ParseResult<'a, U, S> {
match self {
ParseResult::Ok { input, output, location, state, bound } =>
match func(input, output, location, state) {
ParseResult::Ok { input, output, location, state, bound: next_bound } =>
ParseResult::Ok { input, output, location, state, bound: bound || next_bound },
ParseResult::Err { message, from, to, state, bound: next_bound } =>
ParseResult::Err { message, from, to, state, bound: bound || next_bound }
},
ParseResult::Err { message, from, to, state, bound } =>
ParseResult::Err { message, from, to, state, bound },
}
}
fn unwrap(self, source: &'a str) -> T {
match self {
ParseResult::Ok { output, .. } =>
output,
ParseResult::Err { message, from, to, .. } =>
panic!(display_error(source, message, from, to)),
}
}
fn unwrap_err(self) -> String
where
T: Debug + 'a
{
match self {
ParseResult::Ok { output, .. } =>
panic!(format!("{:#?}", output)),
ParseResult::Err { message, .. } =>
message,
}
}
}
pub trait Parser<'a, Output, State: Clone> {
fn parse(&self, input: &'a str, location: Location, state: State) -> ParseResult<'a, Output, State>;
fn map<F, NewOutput>(self, map_fn: F) -> BoxedParser<'a, NewOutput, State>
where
Self: Sized + 'a,
Output: 'a,
NewOutput: 'a,
State: 'a,
F: Fn(Output) -> NewOutput + 'a,
{
BoxedParser::new(map(self, map_fn))
}
fn map_with_state<F, NewOutput>(self, map_fn: F) -> BoxedParser<'a, NewOutput, State>
where
Self: Sized + 'a,
Output: 'a,
NewOutput: 'a,
State: 'a,
F: Fn(Output, State) -> NewOutput + 'a,
{
BoxedParser::new(map_with_state(self, map_fn))
}
fn map_err<F>(self, map_fn: F) -> BoxedParser<'a, Output, State>
where
Self: Sized + 'a,
Output: 'a,
State: 'a,
F: Fn(String) -> String + 'a,
{
BoxedParser::new(map_err(self, map_fn))
}
fn and_then<F, NextParser, NewOutput>(self, f: F) -> BoxedParser<'a, NewOutput, State>
where
Self: Sized + 'a,
Output: 'a,
NewOutput: 'a,
State: 'a,
NextParser: Parser<'a, NewOutput, State> + 'a,
F: Fn(Output) -> NextParser + 'a,
{
BoxedParser::new(and_then(self, f))
}
fn pred<F>(self, predicate: F, expecting: &'a str) -> BoxedParser<'a, Output, State>
where
Self: Sized + 'a,
Output: std::fmt::Display + 'a,
State: 'a,
F: Fn(&Output) -> bool + 'a,
{
BoxedParser::new(pred(self, predicate, expecting))
}
fn ignore(self) -> BoxedParser<'a, (), State>
where
Self: Sized + 'a,
Output: 'a,
State: 'a,
{
BoxedParser::new(map(self, |_| ()))
}
fn update_state<F>(self, f: F) -> BoxedParser<'a, Output, State>
where
Self: Sized + 'a,
State: 'a,
Output: Clone + 'a,
F: Fn(Output, State) -> State + 'a,
{
BoxedParser::new(update_state(self, f))
}
fn update<F, NewOutput>(self, f: F) -> BoxedParser<'a, NewOutput, State>
where
Self: Sized + 'a,
State: 'a,
Output: Clone + 'a,
NewOutput: Clone + 'a,
F: Fn(&'a str, Output, Location, State) -> ParseResult<'a, NewOutput, State> + 'a,
{
BoxedParser::new(update(self, f))
}
fn end(self) -> BoxedParser<'a, Output, State>
where
Self: Sized + 'a,
Output: Clone + 'a,
State: 'a,
{
BoxedParser::new(end(self))
}
}
impl<'a, F, Output, State: 'a> Parser<'a, Output, State> for F
where
F: Fn(&'a str, Location, State) -> ParseResult<'a, Output,State>,
State: Clone,
{
fn parse(&self, input: &'a str, location: Location, state: State) -> ParseResult<'a, Output, State> {
self(input, location, state)
}
}
impl<'a, Output: 'a, State: 'a> Clone for BoxedParser<'a, Output, State> {
fn clone(&self) -> Self {
BoxedParser {
parser: self.parser.clone()
}
}
}
pub struct BoxedParser<'a, Output, State> {
parser: Rc<dyn Parser<'a, Output, State> + 'a>,
}
impl<'a, Output,State> BoxedParser<'a, Output, State> {
pub fn new<P>(parser: P) -> Self
where
P: Parser<'a, Output, State> + 'a,
State: Clone,
{
BoxedParser {
parser: Rc::new(parser),
}
}
}
impl<'a, Output, State> Parser<'a, Output, State> for BoxedParser<'a, Output, State>
where
State: Clone,
{
fn parse(&self, input: &'a str, location: Location, state: State) -> ParseResult<'a, Output, State> {
self.parser.parse(input, location, state)
}
}
fn and_then<'a, P, F, A, B, S: Clone + 'a, NextP>(parser: P, f: F) -> impl Parser<'a, B, S>
where
P: Parser<'a, A, S>,
NextP: Parser<'a, B, S>,
F: Fn(A) -> NextP,
S: Clone,
{
move |input, location, state| parser.parse(input, location, state)
.and_then(| next_input, next_output, next_location, next_state: S |
f(next_output).parse(next_input, next_location, next_state)
)
}
pub fn token<'a, S: Clone + 'a>(expected: &'static str) -> BoxedParser<'a, &str, S> {
BoxedParser::new(
move |input: &'a str, location: Location, state: S| {
let found = input.get(0..expected.len());
match found {
Some(next) if next == expected => ParseResult::Ok {
input: &input[expected.len()..],
output: expected,
location: increment_col(expected.len(), location),
state,
bound: true,
},
_ => ParseResult::Err {
message: format!(
"I'm expecting a `{}` but found {}.",
expected,
display_token(found)
),
from: location,
to: match found {
Some(found_str) => Location {
col: location.col + found_str.len(),
..location
},
None => location,
},
state,
bound: false,
},
}
})
}
fn increment_col(additional_col: usize, location: Location) -> Location {
Location {
col: location.col + additional_col,
..location
}
}
fn increment_row(additional_row: usize, location: Location) -> Location {
Location {
row: location.row + additional_row,
col: 1,
}
}
fn display_token<T: Display>(token: Option<T>) -> String {
match token {
Some(token_content) => format!("`{}`", token_content).replace("\n", "\\n"),
None => "nothing".to_string(),
}
}
pub fn pair<'a, P1, P2, R1, R2, S: Clone + 'a>(parser1: P1, parser2: P2) -> impl Parser<'a, (R1, R2), S>
where
P1: Parser<'a, R1, S>,
P2: Parser<'a, R2, S>,
{
move |input, location, state|
parser1.parse(input, location, state)
.and_then(| next_input, first_output, next_location, next_state: S |
parser2.parse(next_input, next_location, next_state).map(| second_output |
(first_output, second_output)
)
)
}
fn map<'a, P: 'a, F: 'a, A, B, S: Clone + 'a>(parser: P, map_fn: F) -> BoxedParser<'a, B, S>
where
P: Parser<'a, A, S>,
F: Fn(A) -> B,
{
BoxedParser::new(
move |input, location, state| parser.parse(input, location, state).map(
|output| map_fn(output)
))
}
fn map_with_state<'a, P: 'a, F: 'a, A, B, S: Clone + 'a>(parser: P, map_fn: F) -> BoxedParser<'a, B, S>
where
P: Parser<'a, A, S>,
F: Fn(A, S) -> B,
{
BoxedParser::new(
move |input, location, state: S| match parser.parse(input, location, state.clone()) {
ParseResult::Ok { input, output, location, state, bound } => ParseResult::Ok {
input,
output: map_fn(output, state.clone()),
location,
state,
bound,
},
ParseResult::Err { message, from, to, state, bound } =>
ParseResult::Err { message, from, to, state, bound }
}
)
}
fn map_err<'a, P, F, A, S: Clone + 'a>(parser: P, map_fn: F) -> impl Parser<'a, A, S>
where
P: Parser<'a, A, S>,
F: Fn(String) -> String,
{
move |input, location, state| parser.parse(input, location, state).map_err(
|error_message| map_fn(error_message)
)
}
pub fn left<'a, P1: 'a, P2: 'a, R1: 'a, R2: 'a, S: Clone + 'a>(parser1: P1, parser2: P2) -> BoxedParser<'a, R1, S>
where
P1: Parser<'a, R1, S>,
P2: Parser<'a, R2, S>,
{
map(pair(parser1, parser2), |(left, _right)| left)
}
pub fn right<'a, P1: 'a, P2: 'a, R1: 'a, R2: 'a, S: Clone + 'a>(parser1: P1, parser2: P2) -> BoxedParser<'a, R2, S>
where
P1: Parser<'a, R1, S>,
P2: Parser<'a, R2, S>,
{
map(pair(parser1, parser2), |(_left, right)| right)
}
pub fn one_or_more<'a, P, A, S: Clone + 'a>(parser: P) -> impl Parser<'a, Vec<A>, S>
where
P: Parser<'a, A, S>
{
move |mut input, mut location, mut state: S| {
let mut output = Vec::new();
let mut bound = false;
match parser.parse(input, location, state.clone()) {
ParseResult::Ok {
input: next_input,
output: first_item,
location: next_location,
state: next_state,
bound: next_bound,
} => {
input = next_input;
location = next_location;
state = next_state;
bound = next_bound;
output.push(first_item);
}
ParseResult::Err {
message,
from,
to,
state,
bound,
} => {
return ParseResult::Err { message, from, to, state, bound };
}
}
loop {
match parser.parse(input, location, state.clone()) {
ParseResult::Ok {
input: next_input,
output: next_item,
location: next_location,
state: next_state,
..
} => {
input = next_input;
location = next_location;
state = next_state;
output.push(next_item);
},
ParseResult::Err {..} => break
}
}
ParseResult::Ok { input, output, location, state, bound }
}
}
fn end<'a, P: 'a, A: Clone + 'a, S: Clone + 'a>(parser: P) -> impl Parser<'a, A, S>
where
P: Parser<'a, A, S>,
{
parser.update(|input, output, location, state|
if input != "" {
ParseResult::Err {
message: "I'm expecting the end of input.".to_string(),
from: location,
to: location,
state,
bound: false,
}
} else {
ParseResult::Ok { input, output, location, state, bound: false }
}
)
}
pub fn zero_or_more<'a, P: 'a, A, S: Clone + 'a>(parser: P) -> BoxedParser<'a, Vec<A>, S>
where
P: Parser<'a, A, S>,
{
BoxedParser::new(
move |mut input, mut location, mut state: S| {
let mut output = Vec::new();
let mut bound = false;
while let ParseResult::Ok {
input: next_input,
output: next_item,
location: next_location,
state: next_state,
bound: next_bound,
} = parser.parse(input, location, state.clone())
{
input = next_input;
location = next_location;
state = next_state;
bound = next_bound;
output.push(next_item);
}
ParseResult::Ok { input, output, location, state, bound }
})
}
fn any_char<'a, S: Clone + 'a>(expecting: &'a str) -> impl Parser<'a, char, S> {
move |input: &'a str, location: Location, state| match input.chars().next() {
Some(character) => ParseResult::Ok {
input: &input[character.len_utf8()..],
output: character,
location: increment_col(character.len_utf8(), location),
state,
bound: false,
},
_ => ParseResult::Err {
message: format!("I'm expecting {} but reached the end of input.", expecting),
from: location,
to: location,
state,
bound: false,
},
}
}
pub fn chomp_if<'a, F: 'a, S: Clone + 'a>(predicate: F, expecting: &'a str) -> BoxedParser<'a, (), S>
where
F: Fn(&char) -> bool,
{
any_char(expecting).pred(predicate, expecting).ignore()
}
pub fn chomp_while0<'a, F: 'a, S: Clone + 'a>(predicate: F, expecting: &'a str) -> BoxedParser<'a, (), S>
where
F: Fn(&char) -> bool,
{
zero_or_more(chomp_if(predicate, expecting)).ignore()
}
pub fn chomp_while1<'a, F: 'a, S: Clone + 'a>(predicate: F, expecting: &'a str) -> BoxedParser<'a, (), S>
where
F: Fn(&char) -> bool,
{
one_or_more(chomp_if(predicate, expecting)).ignore()
}
pub fn take_chomped<'a, P, A, S: Clone + 'a>(parser: P) -> impl Parser<'a, String, S>
where
P: Parser<'a, A, S>
{
move |input, location, state|
match parser.parse(input, location, state) {
ParseResult::Ok {
input: next_input,
location: next_location,
state: next_state,
..
} => ParseResult::Ok {
input: next_input,
output: get_difference(input, next_input).to_string(),
location: next_location,
state: next_state,
bound: true,
},
ParseResult::Err { message, from, to , state, bound } =>
ParseResult::Err { message, from, to , state, bound }
}
}
fn get_difference<'a>(whole_str: &'a str, substr: &'a str) -> &'a str {
&whole_str[..whole_str.len() - substr.len()]
}
fn pred<'a, P, F, A: std::fmt::Display, S: Clone + 'a>(parser: P, predicate: F, expecting: &'a str) -> impl Parser<'a, A, S>
where
P: Parser<'a, A, S>,
F: Fn(&A) -> bool,
{
move |input, location, state: S| match parser.parse(input, location, state.clone()) {
ParseResult::Ok {
input: next_input,
output: content,
location: next_location,
state: next_state,
bound: next_bound,
} => if predicate(&content) {
ParseResult::Ok {
input: next_input,
output: content,
location: next_location,
state: next_state,
bound: true,
}
} else {
ParseResult::Err {
message: format!(
"I'm expecting {} but found {}.",
expecting,
display_token(Some(content)),
)
.to_string(),
from: location,
to: next_location,
state: next_state,
bound: next_bound,
}
},
err @ ParseResult::Err { .. } => err,
}
}
pub fn space_char<'a, S: Clone + 'a>() -> BoxedParser<'a, (), S> {
chomp_if(
&(|c: &char| *c == ' '),
"a whitespace",
)
}
pub fn newline_char<'a, S: Clone + 'a>() -> BoxedParser<'a, (), S> {
BoxedParser::new(
(move |input, location, state: S| {
let mut next_input: &str = input;
let mut next_location: Location = location;
let mut next_state: S = state.clone();
let result1 = chomp_if(
&(|c: &char| *c == '\r'),
"a carriage return",
).parse(input, location, state);
match result1 {
ParseResult::Ok {
input,
location,
state,
..
} => {
next_input = input;
next_location = location;
next_state = state;
}
_ => {}
}
let result = chomp_if(
&(|c: &char| *c == '\n'),
"a newline",
).parse(next_input, next_location, next_state);
match result {
ParseResult::Ok { input, output, location, state, bound } => ParseResult::Ok {
input, output, state, bound,
location: increment_row(1, location)
},
err @ ParseResult::Err { .. } => err,
}
}).ignore()
)
}
pub fn newline0<'a, S: Clone + 'a>(indent_parser: BoxedParser<'a, (), S>) -> BoxedParser<'a, (), S> {
zero_or_more(pair(
indent_parser,
newline_char(),
)).ignore()
}
pub fn newline1<'a, S: Clone + 'a>(indent_parser: BoxedParser<'a, (), S>) -> BoxedParser<'a, (), S> {
pair(
newline_char(),
newline0(indent_parser),
).ignore()
}
pub fn space0<'a, S: Clone + 'a>() -> BoxedParser<'a, (), S> {
zero_or_more(space_char()).ignore()
}
pub fn space1<'a, S: Clone + 'a>() -> BoxedParser<'a, (), S> {
one_or_more(space_char()).ignore()
}
pub fn indent<'a, S: Clone + 'a>(number_of_spaces: usize) -> BoxedParser<'a, (), S> {
repeat(
number_of_spaces,
space_char(),
).ignore()
.map_err(move |_| format!("I'm expecting an indentation.\nAll indentations should be {} spaces.", number_of_spaces))
}
pub fn indents<'a, S: Clone + 'a>(number_of_spaces: usize, number_of_indents: usize) -> BoxedParser<'a, (), S> {
repeat(
number_of_indents,
indent(number_of_spaces),
).map_err(move |_| format!("I'm expecting {} indentation{}.\nAll indentations should be {} spaces.",
number_of_indents,
plural_suffix(number_of_indents),
number_of_spaces,
))
.ignore()
}
fn plural_suffix(count: usize) -> &'static str {
if count > 1 { "s" } else { "" }
}
fn repeat<'a, A, P, S: Clone + 'a>(times: usize, parser: P)
-> impl Parser<'a, Vec<A>, S>
where
P: Parser<'a, A, S>
{
move |mut input, mut location, mut state: S| {
let mut output = Vec::new();
let mut bound = false;
if times == 0 {
return ParseResult::Ok {
input,
location,
output,
state,
bound,
}
}
let mut counter = 0;
while let ParseResult::Ok {
input: next_input,
output: next_item,
location: next_location,
state: next_state,
bound: next_bound,
} = parser.parse(input, location, state.clone())
{
if counter >= times {
break;
}
input = next_input;
location = next_location;
state = next_state;
output.push(next_item);
counter = counter + 1;
bound = next_bound;
}
ParseResult::Ok { input, output, location, state, bound }
}
}
#[macro_export]
macro_rules! one_of {
($single_parser:expr) => { $single_parser };
($first_parser:expr, $($parsers:expr),+) => {
either($first_parser, one_of!($($parsers),*))
};
}
pub fn either<'a, A, P: 'a, S: Clone + 'a>(parser1: P, parser2: P)
-> BoxedParser<'a, A, S>
where
P: Parser<'a, A, S>
{
BoxedParser::new(
move |input, location, state: S| {
let result = match parser1.parse(input, location, state.clone()) {
ok @ ParseResult::Ok {..} => ok,
ParseResult::Err { message, from, to, state, bound } =>
if bound {
ParseResult::Err { message, from, to, state, bound }
} else {
parser2.parse(input, location, state)
}
};
result
}
)
}
pub fn optional<'a, A: Clone + 'a, P: 'a, S: Clone + 'a>(default: A, parser: P)
-> BoxedParser<'a, A, S>
where
P: Parser<'a, A, S>
{
either(
BoxedParser::new(
parser
),
BoxedParser::new(
move |input, location, state|
ParseResult::Ok {
input,
location,
output: default.clone(),
state,
bound: true,
}
)
)
}
pub fn newline_with_comment<'a, S: Clone + 'a>(comment_symbol: &'static str) -> impl Parser<'a, (), S> {
either(
chain!(
space0(),
line_comment(comment_symbol)
),
chain!(
space0(),
newline_char()
),
).ignore()
}
pub fn line_comment<'a, S: Clone + 'a>(comment_symbol: &'static str) -> BoxedParser<'a, (), S> {
chain!(
token(comment_symbol),
zero_or_more(chomp_if(
&(|c: &char| *c != '\n' && *c != '\r'),
"any character",
)),
newline_char()
).ignore()
}
#[macro_export]
macro_rules! chain {
($single_parser:expr) => { $single_parser };
($first_parser:expr, $($parsers:expr),+) => {
$first_parser.and_then(move | output |
chain!($($parsers),*).map(move | next_output | (output.clone(), next_output) )
)
};
}
pub fn int<'a, S: Clone + 'a>() -> impl Parser<'a, usize, S> {
digits("an integer", false).map(|int_str| int_str.parse().unwrap())
}
pub fn float<'a, S: Clone + 'a>() -> impl Parser<'a, f64, S> {
let expecting = "a floating point number";
chain!(
digits(expecting, false),
optional(
"".to_string(),
right(token("."), digits(expecting, true)).map(|digits| ".".to_owned() + &digits)
),
optional(
"".to_string(),
chain!(
either(token("e"), token("E")),
optional("", either(token("+"), token("-"))),
digits(expecting, false)
).map(|(_, (sign, exponent))|
"e".to_string() + sign + &exponent
)
)
).map(|(whole, (fraction, exponent))|
(whole + &fraction + &exponent).parse().unwrap()
)
}
fn digits<'a, S: Clone + 'a>(name: &'a str, allow_leading_zeroes: bool) -> impl Parser<'a, String, S> {
take_chomped(chomp_while1(
&(| character: &char | character.is_digit(10)),
name
)).update(move |input, digits, location, state|
if !allow_leading_zeroes && digits.chars().next() == Some('0') && digits.len() > 1 {
ParseResult::Err {
message: format!("You can't have leading zeroes in {}.", name),
from: Location {
col: location.col - digits.len(),
..location
},
to: location,
state,
bound: false,
}
} else {
ParseResult::Ok {
input,
output: digits,
location,
state,
bound: true,
}
}
)
}
pub fn variable<'a, S: Clone + 'a, F1: 'a, F2: 'a, F3: 'a>(start: &'a F1, inner: &'a F2, separator: &'a F3, reserved: &'a HashSet<String>, expecting: &'a str) -> impl Parser<'a, String, S>
where
F1: Fn(&char) -> bool,
F2: Fn(&char) -> bool,
F3: Fn(&char) -> bool,
{
take_chomped(pair(
chomp_if(start, expecting),
chomp_while0(move |c: &char| inner(c) || separator(c), expecting)
)).update(move |input, name, location, state| {
if reserved.contains(&name) {
ParseResult::Err {
message: format!("I'm expecting {} but found a reserved word `{}`.", expecting, name),
from: Location {
col: location.col - name.len(),
..location
},
to: location,
state,
bound: false,
}
} else {
if name.chars().zip(name[1..].chars()).any(|(c, next_c)| separator(&c) && separator(&next_c)) {
ParseResult::Err {
message: format!("I'm expecting {} but found `{}` with duplicated separators.", expecting, name),
from: Location {
col: location.col - name.len(),
..location
},
to: location,
state,
bound: false,
}
} else if separator(&name.chars().last().unwrap()) {
ParseResult::Err {
message: format!("I'm expecting {} but found `{}` ended with the separator `{}`.", expecting, name, &name.chars().last().unwrap()),
from: Location {
col: location.col - name.len(),
..location
},
to: location,
state,
bound: false,
}
} else {
ParseResult::Ok {
input, location, output: name.clone(), state, bound: true,
}
}
}
})
}
#[derive(Copy, Clone)]
pub enum Trailing {
Forbidden,
Optional,
Mandatory,
}
pub fn sequence<'a, A: Clone + 'a, S: Clone + 'a>(
start: &'static str,
item: BoxedParser<'a, A, S>,
separator: &'static str,
spaces: BoxedParser<'a, (), S>,
end: &'static str,
trailing: Trailing
) -> BoxedParser<'a, Vec<A>, S> {
wrap(
pair(
token(start),
spaces.clone(),
),
optional(
vec![],
pair(
item.clone(),
left(
zero_or_more(right(wrap(spaces.clone(), token(separator), spaces.clone()), item.clone())),
right(
spaces.clone(),
match trailing {
Trailing::Forbidden =>
token(""),
Trailing::Optional =>
optional("", left(token(separator), spaces.clone())),
Trailing::Mandatory =>
left(token(separator), spaces.clone())
}
)
)
).map(move |(first_item, mut rest_items)| {
rest_items.insert(0, first_item);
rest_items
})
),
token(end),
)
}
pub fn wrap<'a, A: 'a, B: 'a, C: 'a, S: Clone + 'a, P1: 'a, P2: 'a, P3: 'a>(left_delimiter: P1, wrapped: P2, right_delimiter: P3) -> BoxedParser<'a, B, S>
where
P1: Parser<'a, A, S>,
P2: Parser<'a, B, S>,
P3: Parser<'a, C, S>,
{
right(
left_delimiter,
left(
wrapped,
right_delimiter,
),
)
}
pub fn located<'a, P: 'a, A, S: Clone + 'a>(parser: P) -> impl Parser<'a, Located<A>, S>
where
P: Parser<'a, A, S>
{
move |input, location, state|
match parser.parse(input, location, state) {
ParseResult::Ok {
input: next_input,
output,
location: next_location,
state: next_state,
..
} => ParseResult::Ok {
input: next_input,
output: Located {
value: output,
from: Location {
row: location.row,
col: location.col,
},
to: Location {
row: next_location.row,
col: next_location.col,
},
},
location: next_location,
state: next_state,
bound: true,
},
ParseResult::Err { message, from, to , state, bound } =>
ParseResult::Err { message, from, to , state, bound }
}
}
pub fn display_error(source: &str, error_message: String, from: Location, to: Location) -> String {
let row = from.row;
let col = from.col;
let error_length = if to.col == from.col {
1
} else {
to.col - from.col
};
let error_line = row.to_string() + "| " + source.split("\n").collect::<Vec<&str>>()[row - 1];
let error_pointer = " ".repeat(col - 1 + row.to_string().len() + 2) + &"^".repeat(error_length);
let error_report =
error_line + "\n" + &error_pointer + "\n" + "⚠️ " + &error_message;
error_report
}
fn update_state<'a, P, A: Clone, S: Clone + 'a, F>(parser: P, f: F) -> impl Parser<'a, A, S>
where
P: Parser<'a, A, S>,
F: Fn(A, S) -> S
{
move |input, location, state| {
match parser.parse(input, location, state) {
ParseResult::Ok {
input: next_input,
location: next_location,
state: next_state,
output,
bound,
} =>
ParseResult::Ok {
input: next_input,
output: output.clone(),
location: next_location,
state: f(output, next_state),
bound,
},
err @ ParseResult::Err { .. } => err,
}
}
}
fn update<'a, P, A: Clone, B: Clone, S: Clone + 'a, F>(parser: P, f: F) -> impl Parser<'a, B, S>
where
P: Parser<'a, A, S>,
F: Fn(&'a str, A, Location, S) -> ParseResult<'a, B, S>
{
move |input, location, state| {
match parser.parse(input, location, state) {
ParseResult::Ok {
input: next_input,
location: next_location,
state: next_state,
output,
..
} =>
f(next_input, output, next_location, next_state),
ParseResult::Err { message, from, to , state, bound } =>
ParseResult::Err { message, from, to , state, bound }
}
}
}
#[doc(hidden)]
pub fn succeed<'a, P: 'a, A: PartialEq + Debug + 'a>(parser: P, source: &'a str, expected: A)
where
P: Parser<'a, A, ()>
{
assert_eq!(
parser.parse(source, Location { row: 1, col: 1 }, ()).unwrap(source),
expected
)
}
#[doc(hidden)]
pub fn fail<'a, P: 'a, A: Debug + 'a>(parser: P, source: &'a str, message: &'a str)
where
P: Parser<'a, A, ()>
{
assert_eq!(
parser.parse(source, Location { row: 1, col: 1 }, ()).unwrap_err(),
message
)
}