diff --git a/src/compare/error.rs b/src/compare/error.rs new file mode 100644 index 0000000..eb23965 --- /dev/null +++ b/src/compare/error.rs @@ -0,0 +1,25 @@ +use nom::error::ErrorKind; +use nom::error::ParseError; +use nom::IResult; + +pub type Res = IResult>; + +#[derive(Debug, PartialEq)] +pub enum CustomError { + MyError(MyError), + Nom(I, ErrorKind), +} + +#[derive(Debug, PartialEq)] +pub struct MyError(pub I); + +impl ParseError for CustomError { + fn from_error_kind(input: I, kind: ErrorKind) -> Self { + CustomError::Nom(input, kind) + } + + fn append(_input: I, _kind: ErrorKind, mut other: Self) -> Self { + // Doesn't do append like VerboseError + other + } +} diff --git a/src/compare/mod.rs b/src/compare/mod.rs index e694bcb..d2ae927 100644 --- a/src/compare/mod.rs +++ b/src/compare/mod.rs @@ -1,2 +1,4 @@ +mod error; mod parse; +mod sexp; pub use parse::emacs_parse_org_document; diff --git a/src/compare/parse.rs b/src/compare/parse.rs index 79633a9..89367c7 100644 --- a/src/compare/parse.rs +++ b/src/compare/parse.rs @@ -1,7 +1,20 @@ use std::path::Path; use std::process::Command; +use crate::compare::sexp::sexp; + pub fn emacs_parse_org_document<'a, C>(file_path: C) -> Result> +where + C: AsRef, +{ + let org_sexp = emacs_parse_org_document_to_sexp(file_path)?; + let parsed_sexp = sexp(org_sexp.as_str()).expect("Parse failure"); + todo!() +} + +fn emacs_parse_org_document_to_sexp<'a, C>( + file_path: C, +) -> Result> where C: AsRef, { diff --git a/src/compare/sexp.rs b/src/compare/sexp.rs new file mode 100644 index 0000000..d50dbdb --- /dev/null +++ b/src/compare/sexp.rs @@ -0,0 +1,65 @@ +use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::bytes::complete::take_till1; +use nom::character::complete::multispace0; +use nom::character::complete::multispace1; +use nom::multi::separated_list1; + +use super::error::Res; + +#[derive(Debug)] +pub enum Token<'s> { + Atom(&'s str), + List(Vec>), +} + +#[tracing::instrument(ret, level = "debug")] +pub fn sexp<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { + let (remaining, _) = multispace0(input)?; + let (remaining, tkn) = token(remaining)?; + let (remaining, _) = multispace0(remaining)?; + Ok((remaining, tkn)) +} + +#[tracing::instrument(ret, level = "debug")] +fn token<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { + alt((list, atom))(input) +} + +#[tracing::instrument(ret, level = "debug")] +fn list<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { + let (remaining, opening_paren) = tag("(")(input)?; + let (remaining, children) = separated_list1(multispace1, token)(remaining)?; + let (remaining, closing_paren) = tag(")")(remaining)?; + Ok((remaining, Token::List(children))) +} + +#[tracing::instrument(ret, level = "debug")] +fn atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { + unquoted_atom(input) +} + +#[tracing::instrument(ret, level = "debug")] +fn unquoted_atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { + let (remaining, body) = take_till1(|c| match c { + ' ' | '\t' | '\r' | '\n' => true, + _ => false, + })(input)?; + Ok((remaining, Token::Atom(body))) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn simple() { + let input = " (foo bar baz ) "; + let (remaining, parsed) = sexp(input).expect("Parse the input"); + assert_eq!(remaining, ""); + assert!(match parsed { + Token::Atom(_) => false, + Token::List(_) => true, + }); + } +}