continue working ish
This commit is contained in:
parent
c3642676b2
commit
b22570c2e2
6 changed files with 175 additions and 0 deletions
12
data/demo-result.html
Normal file
12
data/demo-result.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 class="class">some content</h1>
|
||||
<p>this is some text and contains <a href="https://example.com"></a> a link</p>
|
||||
</body>
|
||||
</html>
|
36
data/demo.ihl
Normal file
36
data/demo.ihl
Normal file
|
@ -0,0 +1,36 @@
|
|||
// defined base element
|
||||
document [
|
||||
title: "example",
|
||||
lang: "de",
|
||||
] {
|
||||
h1.class "some content";
|
||||
p {
|
||||
"this is some text and contains ";
|
||||
a [ href: "https://example.com" ] "a link";
|
||||
".";
|
||||
}
|
||||
}
|
||||
|
||||
// attributes are by default parsed as:
|
||||
// key: value
|
||||
// and are comma seperated.
|
||||
//
|
||||
// items can define custom parsers for attributes
|
||||
|
||||
// content is by default parsed as semicolon seperated further item structures
|
||||
|
||||
// plain text is just plain text
|
||||
|
||||
// definitions have a : after the name
|
||||
// definitions only allow default parsing
|
||||
// @slot is the attribute that defines the element body, but can be overwritten
|
||||
navItem: [
|
||||
dest: string,
|
||||
] {
|
||||
li.nav-item {
|
||||
// use attributes using @
|
||||
// interpolate in @{} blocks
|
||||
a[ href: "https://example.com@{dest}" ] @dest;
|
||||
}
|
||||
}
|
||||
|
1
example.html
Normal file
1
example.html
Normal file
|
@ -0,0 +1 @@
|
|||
<html><head><title>test file!</title></head><body><h1>hello world</h1><p>this is a test file</p><p>some text followed by <a href="https://schrottkatze.de">a hyperlink</a></p></body></html>
|
63
src/element.rs
Normal file
63
src/element.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use std::{collections::HashMap, fmt::{Display, Write}};
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Element<'a> {
|
||||
pub name: &'a str,
|
||||
pub attributes: Option<HashMap<&'a str, &'a str>>,
|
||||
pub children: ElBody<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Display for Element<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"<{0}{1}>{2}</{0}>",
|
||||
self.name,
|
||||
if let Some(attrs) = &self.attributes {
|
||||
let mut attributes = String::from(" ");
|
||||
|
||||
attrs.iter().for_each(|(key, value)| {
|
||||
attributes
|
||||
.write_fmt(format_args!("{key}=\"{value}\""))
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
attributes
|
||||
} else {
|
||||
"".to_owned()
|
||||
},
|
||||
self.children
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// the direct content of `children`, so it can be a plaintext element
|
||||
#[derive(Debug)]
|
||||
pub enum ElBody<'a> {
|
||||
Elements(Vec<ElContent<'a>>),
|
||||
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)]
|
||||
pub 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()),
|
||||
}
|
||||
}
|
||||
}
|
53
src/parser.rs
Normal file
53
src/parser.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use winnow::{IResult, combinator, character::alphanumeric1, branch, sequence::delimited, multi::many0, bytes::take_until0, Parser};
|
||||
|
||||
use crate::{element::{Element, ElBody, ElContent}, util::ws};
|
||||
|
||||
pub fn el_parser(input: &str) -> IResult<&str, Element> {
|
||||
(
|
||||
name_parser,
|
||||
combinator::opt(el_attrlist_parser),
|
||||
el_body_parser,
|
||||
)
|
||||
.map(|(name, attributes, children)| Element {
|
||||
name,
|
||||
attributes,
|
||||
children,
|
||||
})
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
pub fn name_parser(input: &str) -> IResult<&str, &str> {
|
||||
ws(alphanumeric1).parse_next(input)
|
||||
}
|
||||
|
||||
pub 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)
|
||||
}
|
||||
|
||||
pub fn el_attrlist_parser(input: &str) -> IResult<&str, HashMap<&str, &str>> {
|
||||
ws(delimited('[', many0(attribute_parser), ']')).parse_next(input)
|
||||
}
|
||||
|
||||
pub fn attribute_parser(input: &str) -> IResult<&str, (&str, &str)> {
|
||||
(name_parser, '=', ws(string_parser))
|
||||
.map(|(key, _, value)| (key, value))
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
pub 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)
|
||||
}
|
||||
|
||||
pub fn string_parser(input: &str) -> IResult<&str, &str> {
|
||||
delimited('"', take_until0("\""), '"').parse_next(input)
|
||||
}
|
10
src/util.rs
Normal file
10
src/util.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use winnow::{error::ParseError, Parser, sequence::delimited, character::multispace0};
|
||||
|
||||
|
||||
// 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)
|
||||
pub 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)
|
||||
}
|
Loading…
Reference in a new issue