-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
made errors generic + file structure + johanverse tests
- Loading branch information
1 parent
d9b210e
commit 9f379d2
Showing
6 changed files
with
280 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
mod ast; | ||
mod combinators; | ||
mod cst; | ||
mod error; | ||
mod primitive; | ||
|
||
use combinators::*; | ||
use error::*; | ||
|
||
pub type ParseResult<'a, Out, Err = Vec<error::ParseError>> = Result<(Out, &'a str), Err>; | ||
|
||
pub trait Parser<'a, Out, Err = Vec<error::ParseError>> { | ||
fn parse(&self, input: &'a str) -> ParseResult<'a, Out, Err>; | ||
|
||
// NOTE(cdecompilador): Impossible with static dispatch, you can't self reference yourself | ||
// inside of a struct so you need to do dynamic dispatch, this Box works like a Rc in reality | ||
// since the only important data it contains is the vtable to the parse method (static) | ||
fn map<F, NewOut>(self, f: F) -> SharedParser<'a, NewOut, Err> | ||
where | ||
Self: Sized + 'a, | ||
Out: 'a, | ||
NewOut: 'a, | ||
Err: 'a, | ||
F: Fn(Out) -> NewOut + 'a, | ||
{ | ||
SharedParser { | ||
parser: Rc::new(map(self, f)), | ||
} | ||
} | ||
|
||
fn map_err<F, NewErr>(self, f: F) -> SharedParser<'a, Out, NewErr> | ||
where | ||
Self: Sized + 'a, | ||
Out: 'a, | ||
Err: 'a, | ||
NewErr: 'a, | ||
F: Fn(Err) -> NewErr + 'a, | ||
{ | ||
SharedParser { | ||
parser: Rc::new(map_err(self, f)), | ||
} | ||
} | ||
} | ||
|
||
use std::rc::Rc; | ||
|
||
#[derive(Clone)] | ||
pub struct SharedParser<'a, Out, Err> { | ||
parser: Rc<dyn Parser<'a, Out, Err> + 'a>, | ||
} | ||
|
||
impl<'a, Out, Err> Parser<'a, Out, Err> for SharedParser<'a, Out, Err> { | ||
fn parse(&self, input: &'a str) -> ParseResult<'a, Out, Err> { | ||
self.parser.parse(input) | ||
} | ||
} | ||
|
||
impl<'a, F, Out, Err> Parser<'a, Out, Err> for F | ||
where | ||
F: Fn(&'a str) -> ParseResult<'a, Out, Err>, | ||
{ | ||
fn parse(&self, input: &'a str) -> ParseResult<'a, Out, Err> { | ||
self(input) | ||
} | ||
} | ||
|
||
// NOTE(cdecompilador): This function can't fail since it will have to handle the | ||
// priting of all warning or errors (with crash), the caller of this will have the responsability | ||
// of passing the &str from a full in_memory file read or a mmap | ||
pub fn parse(_: &str) -> ast::Ast { | ||
todo!() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub enum Ast {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
//! This module contains the fundamental combinators used through all the parsing pipeline | ||
use super::*; | ||
|
||
pub fn pair<'a, Out1, Out2, Err>( | ||
first: impl Parser<'a, Out1, Err>, | ||
second: impl Parser<'a, Out2, Err>, | ||
) -> impl Parser<'a, (Out1, Out2), Err> { | ||
move |input| match first.parse(input) { | ||
Ok((first_result, rest)) => match second.parse(rest) { | ||
Ok((second_result, rest)) => Ok(((first_result, second_result), rest)), | ||
Err(err) => Err(err), | ||
}, | ||
Err(err) => Err(err), | ||
} | ||
} | ||
|
||
pub fn left<'a, Out1, Out2, Err>( | ||
first: impl Parser<'a, Out1, Err>, | ||
second: impl Parser<'a, Out2, Err>, | ||
) -> impl Parser<'a, Out1, Err> { | ||
map(pair(first, second), |(left, _)| left) | ||
} | ||
|
||
pub fn right<'a, Out1, Out2, Err>( | ||
first: impl Parser<'a, Out1, Err>, | ||
second: impl Parser<'a, Out2, Err>, | ||
) -> impl Parser<'a, Out2, Err> { | ||
map(pair(first, second), |(_, right)| right) | ||
} | ||
|
||
pub fn map<'a, In, Out, Err>( | ||
parser: impl Parser<'a, In, Err>, | ||
f: impl Fn(In) -> Out, | ||
) -> impl Parser<'a, Out, Err> { | ||
move |input| match parser.parse(input) { | ||
Ok((result, rest)) => Ok((f(result), rest)), | ||
Err(errors) => Err(errors), | ||
} | ||
} | ||
|
||
pub fn map_err<'a, Out, InErr, OutErr>( | ||
parser: impl Parser<'a, Out, InErr>, | ||
f: impl Fn(InErr) -> OutErr, | ||
) -> impl Parser<'a, Out, OutErr> { | ||
move |input| match parser.parse(input) { | ||
Ok((result, rest)) => Ok((result, rest)), | ||
Err(error) => Err(f(error)), | ||
} | ||
} | ||
|
||
pub fn either<'a, Out, Err1, Err2>( | ||
first: impl Parser<'a, Out, Err1>, | ||
second: impl Parser<'a, Out, Err2>, | ||
) -> impl Parser<'a, Out, (Err1, Err2)> { | ||
move |input| match first.parse(input) { | ||
Ok(result1) => Ok(result1), | ||
Err(fst_err) => match second.parse(input) { | ||
Ok(result2) => Ok(result2), | ||
Err(snd_err) => Err((fst_err, snd_err)), | ||
}, | ||
} | ||
} | ||
|
||
pub fn zero_or_more<'a, Out, Err>( | ||
parser: impl Parser<'a, Out, Err>, | ||
) -> impl Parser<'a, Vec<Out>, Err> { | ||
move |mut input| { | ||
let mut result = Vec::new(); | ||
|
||
while let Ok((out, next_input)) = parser.parse(input) { | ||
input = next_input; | ||
result.push(out); | ||
} | ||
|
||
Ok((result, input)) | ||
} | ||
} | ||
|
||
pub fn one_or_more<'a, Out, Err>( | ||
parser: impl Parser<'a, Out, Err> + Clone, | ||
) -> impl Parser<'a, Vec<Out>, Err> { | ||
map( | ||
pair(parser.clone(), zero_or_more(parser)), | ||
|(head, mut tail)| { | ||
tail.insert(0, head); | ||
tail | ||
}, | ||
) | ||
} | ||
|
||
pub fn surrounded_by<'a, OutS, Out, ErrS, Err>( | ||
parser: impl Parser<'a, Out, Err>, | ||
surr: impl Parser<'a, OutS, ErrS> + Clone, | ||
) -> impl Parser<'a, Out, Err> { | ||
move |input| todo!() | ||
} | ||
|
||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
pub struct LiteralError<'a> { | ||
pub expected: &'static str, | ||
pub found: &'a str, | ||
} | ||
|
||
pub fn literal<'a>(literal: &'static str) -> impl Parser<'a, (), LiteralError<'a>> { | ||
move |input: &'a str| { | ||
if input.starts_with(literal) { | ||
Ok(((), &input[literal.len()..])) | ||
} else { | ||
Err(LiteralError { | ||
expected: literal, | ||
found: input, | ||
}) | ||
} | ||
} | ||
} | ||
|
||
/* | ||
fn regex<'a>(re_str: &'static str) -> impl Parser<'a, Captures<'a>> { | ||
let re = Regex::new(re_str).unwrap(); | ||
move |input: &'a str| match re.captures(input) { | ||
Some(captures) => { | ||
let match_length = captures.get(0).unwrap().end(); | ||
Ok((captures, &input[match_length..])) | ||
} | ||
None => { | ||
let next_line = input.find(&['\n', '\r']).unwrap_or(min(10, input.len())); | ||
Err(vec![ParserError::RegexError(re_str, &input[0..next_line])]) | ||
} | ||
} | ||
} | ||
*/ | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn it_works() { | ||
let johan = literal("johan"); | ||
|
||
assert_eq!(johan.parse("johan"), Ok(((), ""))); | ||
assert_eq!(johan.parse("johan jr"), Ok(((), " jr"))); | ||
assert_eq!( | ||
Err(LiteralError { | ||
expected: "johan", | ||
found: "pepe", | ||
}), | ||
johan.parse("pepe"), | ||
); | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
struct Johan; | ||
#[derive(Debug, Clone, PartialEq, Eq)] | ||
struct JohanErr; | ||
|
||
let johan = johan.map(|_| Johan).map_err(|_| JohanErr); | ||
|
||
assert_eq!(johan.parse("johan"), Ok((Johan, ""))); | ||
assert_eq!(johan.parse("pepe"), Err(JohanErr)); | ||
|
||
let johan_verse_dyn = left(johan.clone(), johan.clone()); | ||
assert_eq!(johan_verse_dyn.parse("johanjohan"), Ok((Johan, ""))); | ||
|
||
assert_eq!( | ||
zero_or_more(johan.clone()).parse("johanjohanjohan"), | ||
Ok((vec![Johan, Johan, Johan], "")) | ||
); | ||
assert_eq!( | ||
zero_or_more(johan.clone()).parse("uwu"), | ||
Ok((vec![], "uwu")) | ||
); | ||
assert_eq!(one_or_more(johan.clone()).parse("uwu"), Err(JohanErr)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
pub enum ParseError { | ||
LexError(LexError), | ||
SintacticError(SintacticError), | ||
} | ||
|
||
pub enum LexError { | ||
ExpectedCharacter(char), | ||
ExpectedPattern(String), | ||
ExpectedOneOf(Vec<String>), | ||
InvalidIntLiteral, | ||
UnclosedStringLiteral, | ||
} | ||
|
||
pub enum SintacticError {} | ||
|
||
// TODO: Semantic error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
use super::*; | ||
|
||
pub fn integer_literal(input: &str) -> ParseResult<'_, i64, ParseError> { | ||
todo!() | ||
} | ||
|
||
// DESIGN(cdecompilador): Should this really a &str?? or a Span?? or an allocated String?? | ||
pub fn identifier(input: &str) -> ParseResult<'_, String, ParseError> { | ||
todo!() | ||
} | ||
|
||
// DESIGN(cdecompilador): Should this really a &str?? or a Span?? or an allocated String?? | ||
pub fn string_literal(input: &str) -> ParseResult<'_, String, ParseError> { | ||
todo!() | ||
} |