diff --git a/crates/ast/src/lib.rs b/crates/ast/src/lib.rs index b43b0d1..a264697 100644 --- a/crates/ast/src/lib.rs +++ b/crates/ast/src/lib.rs @@ -1,3 +1,5 @@ +//! The abstract syntax tree for the Cogs templating language. + #[derive(Debug)] pub struct Component { pub elements: Vec, @@ -19,12 +21,24 @@ pub struct HtmlTag { #[derive(Debug, Clone)] pub struct Attribute { - pub name: Element, - pub value: Option, + pub name: String, + pub value: Option, } #[derive(Debug, Clone)] pub struct CodeBlock { // pub is_async: bool, - pub content: Vec, + pub content: Vec, +} + +#[derive(Debug, Clone)] +pub enum Expression { + Code(String), + Text(String), +} + +#[derive(Debug, Clone)] +pub enum CodeElement { + Html(HtmlTag), + Code(String) } diff --git a/crates/codegen/src/generate.rs b/crates/codegen/src/generate.rs index 02f3255..186f20e 100644 --- a/crates/codegen/src/generate.rs +++ b/crates/codegen/src/generate.rs @@ -42,7 +42,7 @@ fn quoted(s: &str) -> String { impl Expression { fn append(&self, cx: &mut AppendContext) { match self { - Expression::Literal(literal) => push!(cx, "\"{}\"", literal), + Expression::Text(literal) => push!(cx, "\"{}\"", literal), Expression::Code(code) => cx.push_arg(code.trim().to_string()), } } diff --git a/crates/codegen/src/ir.rs b/crates/codegen/src/ir.rs index 294498a..0383a37 100644 --- a/crates/codegen/src/ir.rs +++ b/crates/codegen/src/ir.rs @@ -1,7 +1,7 @@ use super::*; pub enum Expression { - Literal(InternedStr), + Text(InternedStr), Code(InternedStr), } @@ -65,14 +65,11 @@ impl HtmlTag { impl HtmlAttribute { pub fn from_ast(value: &ast::Attribute, intern: &StrInterner) -> Self { - let ast::Element::Text(name) = &value.name else { - panic!("attribute name should be a string") - }; - let name = intern.intern_ref(name); + let name = intern.intern_ref(&value.name); let value = value .value .as_ref() - .map(|v| Expression::from_ast(v, intern)); + .map(|value| Expression::from_ast(value, intern)); HtmlAttribute { name, value } } } @@ -84,7 +81,7 @@ impl CodeBlock { .content .iter() .map(|elem| { - if matches!(elem, ast::Element::Html(_)) { + if matches!(elem, ast::CodeElement::Html(_)) { has_html = true }; CodeTree::from_ast(elem, intern) @@ -95,28 +92,19 @@ impl CodeBlock { } impl Expression { - pub fn from_ast(value: &ast::Element, intern: &StrInterner) -> Self { - // TODO expression in parser + pub fn from_ast(value: &ast::Expression, intern: &StrInterner) -> Self { match value { - ast::Element::Text(text) => Expression::Literal(intern.intern_ref(text)), - ast::Element::Html(_html) => { - panic!("ast::Element::Html should not be used as attribute value") - } // this is the only case where expression is used so we can mention that in the panic message - ast::Element::Block(block) => { - todo!("code blocks are not supported as attribute values until Expression is implemented in the parser") - } + ast::Expression::Code(code) => Expression::Code(intern.intern_ref(code)), + ast::Expression::Text(text) => Expression::Text(intern.intern_ref(text)), } } } impl CodeTree { - pub fn from_ast(value: &ast::Element, intern: &StrInterner) -> Self { + pub fn from_ast(value: &ast::CodeElement, intern: &StrInterner) -> Self { match value { - ast::Element::Text(text) => CodeTree::Code(intern.intern_ref(text)), - ast::Element::Html(html) => CodeTree::HtmlTag(HtmlTag::from_ast(html, intern)), - ast::Element::Block(_) => { - panic!("nested code block detected (how the hell did the parser do this?)") - } + ast::CodeElement::Html(html) => CodeTree::HtmlTag(HtmlTag::from_ast(html, intern)), + ast::CodeElement::Code(code) => CodeTree::Code(intern.intern_ref(code)), } } } diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 6053a1f..4acaed2 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -1,19 +1,19 @@ use nom::{ branch::alt, bytes::complete::{is_not, tag, take_while1}, - character::complete::{char, multispace0, one_of, space0, space1}, + character::complete::{char, multispace0, space0, space1}, combinator::{opt, peek}, error::context, - multi::{many0, many1, separated_list0}, - sequence::{delimited, pair, preceded, terminated, tuple}, - InputLength, + multi::{many0, separated_list0}, + sequence::{delimited, pair, preceded, tuple}, + Parser, }; use tracing::debug; type IResult = nom::IResult>; use error::Error; -use cogs_ast::{Attribute, CodeBlock, Component, Element, HtmlTag}; +use cogs_ast::{Attribute, CodeBlock, CodeElement, Component, Element, Expression, HtmlTag}; // reexport for cogs crate #[doc(hidden)] pub use nom; @@ -34,12 +34,12 @@ pub fn parse_consecutive_proper_elements(input: &str) -> IResult<&str, Vec IResult<&str, Element> { let (input, _) = multispace0(input)?; - alt((parse_html, context("code block", parse_code_block)))(input) + alt((parse_html_tag.map(Element::Html), context("code block", parse_code_block.map(Element::Block))))(input) } fn parse_proper_element(input: &str) -> IResult<&str, Element> { // dbg!(&input); - let res = alt((parse_element, parse_text))(input); + let res = alt((parse_element, parse_text.map(Element::Text)))(input); // dbg!(&res); res } @@ -56,32 +56,32 @@ fn parse_tag_name(input: &str) -> IResult<&str, &str> { take_while1(is_valid_tag_name_char)(input) } +fn parse_expression(input: &str) -> IResult<&str, Expression> { + alt(( + delimited(char('{'), is_not("{}"), char('}')) + .map(|code: &str| Expression::Code(code.to_string())), + delimited(char('"'), is_not("\""), char('"')) + .map(|code: &str| Expression::Text(code.to_string())), + ))(input) +} + fn parse_attribute(input: &str) -> IResult<&str, Attribute> { // Shouldn't be needed with take_while1, re-add if fail /* if input.starts_with('>') || input.is_empty() { // Return a recoverable error so `separated_list0` stops parsing cleanly - return Err(Err::Error(Error::new(input, nom::error::ErrorKind::Eof))); + return Err(Error::eof(input)); } */ - let (input, key) = take_while1(is_valid_attr_char)(input)?; - let (input, value) = opt(preceded( - tuple((tag("="), space0)), - delimited(tag("\""), is_not("\""), tag("\"")), - ))(input)?; - - let mut resulting_value = None; - - if value.is_some_and(|x| !x.is_empty()) { - resulting_value = Some(Element::Text(value.unwrap().to_string())); - } + let (input, name) = take_while1(is_valid_attr_char)(input)?; + let (input, value) = opt(preceded(tuple((tag("="), space0)), parse_expression))(input)?; Ok(( input, Attribute { - name: Element::Text(key.to_string()), - value: resulting_value, + name: name.to_string(), + value, }, )) } @@ -132,7 +132,7 @@ fn parse_html_closing_tag(input: &str) -> IResult<&str, &str> { Ok((input, tag)) } -fn parse_text(input: &str) -> IResult<&str, Element> { +fn parse_text(input: &str) -> IResult<&str, String> { // dbg!(&input); let mut index = 0; while index < input.len() { @@ -161,7 +161,7 @@ fn parse_text(input: &str) -> IResult<&str, Element> { // dbg!(&input[0..index]); - Ok((&input[index..], Element::Text(input[0..index].to_string()))) + Ok((&input[index..], input[0..index].to_string())) } fn parse_html_contents(input: &str) -> IResult<&str, Vec> { @@ -172,7 +172,7 @@ fn parse_html_contents(input: &str) -> IResult<&str, Vec> { Ok((input, out)) } -fn parse_html(input: &str) -> IResult<&str, Element> { +fn parse_html_tag(input: &str) -> IResult<&str, HtmlTag> { let (input, _) = multispace0(input)?; // remove spaces when debugging is complete let (input, mut htag) = parse_html_opening_tag(input)?; let (input, content) = parse_html_contents(input)?; // parse_consecutive_elements(input)?; @@ -182,47 +182,44 @@ fn parse_html(input: &str) -> IResult<&str, Element> { if htag.tag != close_name { return Err(Error::custom_failure( input, - format!("expected closing tag ``, got ``", htag.tag, close_name), + format!( + "expected closing tag ``, got ``", + htag.tag, close_name + ), )); } - Ok((input, Element::Html(htag))) + Ok((input, htag)) } -/* -fn parse_code_until_interrupted(input: &str) -> IResult<&str, Element> { - let (input, code) = pair(is_not("{};"), one_of("{};"))(input)?; - - Ok(( - input, - Element::Text(format!("{}{}", code.0, code.1)) - )) +fn parse_code_element(input: &str) -> IResult<&str, CodeElement> { + alt(( + parse_html_tag.map(CodeElement::Html), + parse_text.map(CodeElement::Code) + ))(input) } -*/ -fn parse_inside_code_block(input: &str) -> IResult<&str, Vec> { +fn parse_code_elements(input: &str) -> IResult<&str, Vec> { debug!("Attempting inside code block {input}"); - let (input, elems) = parse_consecutive_proper_elements(input)?; + + let (input, _) = multispace0(input)?; + let (input, elems) = many0(parse_code_element)(input)?; debug!(?elems, "parsed inside code block"); Ok((input, elems)) } -fn parse_code_block(input: &str) -> IResult<&str, Element> { +fn parse_code_block(input: &str) -> IResult<&str, CodeBlock> { if input.chars().nth(0) == Some('{') { debug!("Attempting code block on {input}"); } - let (input, content) = delimited( - char('{'), - parse_inside_code_block, // get here eventually, currently code blocks do not work at all. - char('}'), - )(input)?; + let (input, content) = delimited(char('{'), parse_code_elements, char('}'))(input)?; Ok(( input, - Element::Block(CodeBlock { + CodeBlock { // is_async: is_async.unwrap_or(false), content, - }), + }, )) } diff --git a/examples/small_axum/build.rs b/examples/small_axum/build.rs index 84f06a9..6b50e3e 100644 --- a/examples/small_axum/build.rs +++ b/examples/small_axum/build.rs @@ -1,3 +1,4 @@ fn main() { + let _ = cogs::init_tracing(); cogs::build(std::env::current_dir().unwrap().join("cogs")).unwrap(); } diff --git a/examples/small_axum/cogs/index.cog b/examples/small_axum/cogs/index.cog index 16bdd79..8556f8f 100644 --- a/examples/small_axum/cogs/index.cog +++ b/examples/small_axum/cogs/index.cog @@ -1,4 +1,7 @@ -{ let random: u32 = rand::random(); } +{ + let random: u32 = rand::random(); + let random_p: u8 = rand::random(); +} cogs - small_to_console diff --git a/src/lib.rs b/src/lib.rs index 9089560..ce25c3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ pub fn parse_cog(input: String, file: &str) -> eyre::Result if leftover.is_empty() { Ok(ast) } else { - Err(eyre::eyre!("Not all input parsed, leftover: {leftover}")) + Err(eyre::eyre!("Not all input parsed, leftover: {leftover}; parsed ast: {ast:#?}")) } } Err(error) => {