diff --git a/src/main.rs b/src/main.rs index 4a94637..15bef38 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,121 @@ -fn main() {} +use std::fmt::Display; + +use winnow::{IResult, bytes::{take_while0, take_until0}, character::{alphanumeric1, multispace0}, error::ParseError, Parser, sequence::{delimited, preceded}, branch, combinator::todo, multi::{fold_many0, many0}}; + +const TEST_IHL: &str = " +html { + head { + title \"test file!\" + } + body { + h1 \"hello world\" + p \"this is a test file\" + p { + \"some text followed by \" + a \"a hyperlink\" + } + } +} +"; + +fn main() { + let (remainder, e) = el_parser.parse_next(TEST_IHL).unwrap(); + + println!("{e}"); +} + +fn el_parser(input: &str) -> IResult<&str, Element> { + ( + el_name_parser, + el_body_parser + ).map(|(name, children)| Element { name, children }).parse_next(input) +} + + +fn el_name_parser(input: &str) -> IResult<&str, &str> { + ws(alphanumeric1).parse_next(input) +} + +fn el_body_parser(input: &str) -> IResult<&str, ElBody> { + ws(branch::alt(( + string_parser.map(|s| ElBody::Text(s)), + delimited( + '{', + many0(content_parser).map(|v| ElBody::Elements(v)), + '}' + ) + ))).parse_next(input) +} + +fn content_parser(input: &str) -> IResult<&str, ElContent> { + ws(branch::alt(( + string_parser.map(|s| ElContent::Text(s)), + el_parser.map(|e| ElContent::El(e)) + ))).parse_next(input) +} + +fn string_parser(input: &str) -> IResult<&str, &str> { + delimited( + '"', + take_until0("\""), + '"', + ).parse_next(input) +} + +#[derive(Debug)] +struct Element<'a> { + name: &'a str, + children: ElBody<'a>, +} + +impl<'a> Display for Element<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "<{0}>{1}", self.name, self.children) + } +} + +/// the direct content of `children`, so it can be a plaintext element +#[derive(Debug)] +enum ElBody<'a> { + Elements(Vec>), + Text(&'a str), +} +impl<'a> Display for ElBody<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ElBody::Text(s) => f.write_str(s), + ElBody::Elements(els) => els.iter().try_for_each(|e| f.write_str(&e.to_string())) + } + } +} + +#[derive(Debug)] +enum ElContent<'a> { + El(Element<'a>), + Text(&'a str), +} + +impl<'a> Display for ElContent<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ElContent::Text(s) => f.write_str(s), + ElContent::El(el) => f.write_str(&el.to_string()) + } + } +} #[test] fn parse_basic() { - const TESTDATA: &str = ""; +} + +// whitespace combinator from [winnow docs](https://docs.rs/winnow/latest/winnow/_topic/language/index.html#wrapper-combinators-that-eat-whitespace-before-and-after-a-parser) +fn ws<'a, F, O, E: ParseError<&'a str>>(inner: F) -> impl Parser<&'a str, O, E> + where + F: Parser<&'a str, O, E>, +{ + delimited( + multispace0, + inner, + multispace0 + ) }