Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions crates/ast/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! The abstract syntax tree for the Cogs templating language.

#[derive(Debug)]
pub struct Component {
pub elements: Vec<Element>,
Expand All @@ -19,12 +21,24 @@ pub struct HtmlTag {

#[derive(Debug, Clone)]
pub struct Attribute {
pub name: Element,
pub value: Option<Element>,
pub name: String,
pub value: Option<Expression>,
}

#[derive(Debug, Clone)]
pub struct CodeBlock {
// pub is_async: bool,
pub content: Vec<Element>,
pub content: Vec<CodeElement>,
}

#[derive(Debug, Clone)]
pub enum Expression {
Code(String),
Text(String),
}

#[derive(Debug, Clone)]
pub enum CodeElement {
Html(HtmlTag),
Code(String)
}
2 changes: 1 addition & 1 deletion crates/codegen/src/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
}
}
Expand Down
32 changes: 10 additions & 22 deletions crates/codegen/src/ir.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::*;

pub enum Expression {
Literal(InternedStr),
Text(InternedStr),
Code(InternedStr),
}

Expand Down Expand Up @@ -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 }
}
}
Expand All @@ -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)
Expand All @@ -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)),
}
}
}
87 changes: 42 additions & 45 deletions crates/parser/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<I, O> = nom::IResult<I, O, error::Error<I>>;
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;
Expand All @@ -34,12 +34,12 @@ pub fn parse_consecutive_proper_elements(input: &str) -> IResult<&str, Vec<Eleme

fn parse_element(input: &str) -> 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
}
Expand All @@ -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,
},
))
}
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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<Element>> {
Expand All @@ -172,7 +172,7 @@ fn parse_html_contents(input: &str) -> IResult<&str, Vec<Element>> {
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)?;
Expand All @@ -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<Element>> {
fn parse_code_elements(input: &str) -> IResult<&str, Vec<CodeElement>> {
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,
}),
},
))
}
1 change: 1 addition & 0 deletions examples/small_axum/build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
fn main() {
let _ = cogs::init_tracing();
cogs::build(std::env::current_dir().unwrap().join("cogs")).unwrap();
}
5 changes: 4 additions & 1 deletion examples/small_axum/cogs/index.cog
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{ let random: u32 = rand::random(); }
{
let random: u32 = rand::random();
let random_p: u8 = rand::random();
}
<html>
<head>
<title>cogs - small_to_console</title>
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub fn parse_cog(input: String, file: &str) -> eyre::Result<cogs_ast::Component>
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) => {
Expand Down