From d2fc37fed35f8da8bdf9b17900f3382c462c9079 Mon Sep 17 00:00:00 2001 From: Artemy Egorov Date: Mon, 12 Aug 2024 20:21:19 +0300 Subject: [PATCH] feat: table syntax and formatting --- examples/daleth.dlth | 24 +++--- src/daleth/custom_parsers/mod.rs | 123 +++++++++++++++++++++++++++++++ src/daleth/format.rs | 6 ++ src/daleth/lexer/mod.rs | 25 +++++-- src/daleth/lexer/types.rs | 4 + src/daleth/mod.rs | 3 +- src/daleth/parser/mod.rs | 7 +- 7 files changed, 172 insertions(+), 20 deletions(-) create mode 100644 src/daleth/custom_parsers/mod.rs diff --git a/examples/daleth.dlth b/examples/daleth.dlth index 4d47efa..7ba5c39 100644 --- a/examples/daleth.dlth +++ b/examples/daleth.dlth @@ -117,7 +117,7 @@ row [ ]] ] -# Table has custom format if text used +# Table has custom format # +| cells | - primary column # | cells | - secondary column # | Element | Description | - converts to @@ -125,14 +125,14 @@ row [ # Element # Description # ] -# table { -# +| Tag | Description | -# | h | Heading | -# | p | Paragraph | -# | img | Image | -# | link | Link | -# | btn | Button | -# | ul | Unordered list | -# | br | Line break | -# +| quantity | 7 | -# } +{> table + [[ Tag | Description ]] + [ h | Heading ] + [ p | Paragraph ] + [ img | Image ] + [ link | Link ] + [ btn | Button ] + [ ul | Unordered list ] + [ br | Line break ] + [[ quantity | 7 ]] +} \ No newline at end of file diff --git a/src/daleth/custom_parsers/mod.rs b/src/daleth/custom_parsers/mod.rs new file mode 100644 index 0000000..b7cdff5 --- /dev/null +++ b/src/daleth/custom_parsers/mod.rs @@ -0,0 +1,123 @@ +#[derive(Clone, Debug, PartialEq)] +pub enum TableCol<'src> { + Primary(Vec<&'src str>), + Secondary(Vec<&'src str>), +} + +use std::collections::HashMap; + +use chumsky::prelude::*; + +use crate::typed::{NNBody, Tag}; + +use super::types::Span; + +pub fn table_to_tag(rows: &Vec) -> Tag { + Tag::Table( + rows.into_iter() + .map(|row| match row { + TableCol::Primary(row) => Tag::Tpcol( + row.into_iter() + .map(|t| Tag::El(NNBody::Text(format!("{t}")))) + .collect(), + ), + TableCol::Secondary(row) => Tag::Tcol( + row.into_iter() + .map(|t| Tag::El(NNBody::Text(format!("{t}")))) + .collect(), + ), + }) + .collect(), + ) +} + +pub fn table_to_string(rows: &Vec) -> String { + let mut max_len: HashMap = HashMap::new(); + let mut been_primary = false; + + let mut result = String::new(); + + for row in rows { + let row = match row { + TableCol::Primary(row) => { + been_primary = true; + row + } + TableCol::Secondary(row) => row, + }; + + for i in 0..row.len() { + let current = max_len.get(&i).unwrap_or(&0); + + if *current <= row[i].len() { + max_len.insert(i, row[i].len()); + } + } + } + + for row in rows { + let mut primary = false; + let row = match row { + TableCol::Primary(row) => { + primary = true; + result.push_str("[[ "); + + row + } + TableCol::Secondary(row) => { + if been_primary { + result.push_str(" [ "); + } else { + result.push_str("[ "); + } + + row + } + }; + + let mut cells: Vec = vec![]; + + for (i, col) in row.iter().enumerate() { + let max = max_len.get(&i).unwrap_or(&0); + + cells.push(format!("{}{}", col, " ".repeat(max - col.len()))) + } + + result.push_str(&cells.join(" | ")); + + if primary { + result.push_str(" ]]\n"); + } else { + result.push_str(" ]\n"); + } + } + + result +} + +pub fn table_parser<'src>( +) -> impl Parser<'src, &'src str, Vec>, extra::Err>> { + let escape = just('\\') + .ignore_then(choice((just('|'), just(']'), just('\\')))) + .labelled("Table escape sequence"); + + let cell = none_of("\\|]") + .or(escape) + .repeated() + .to_slice() + .map(|s: &str| s.trim()); + + let col_body = cell.separated_by(just("|")).collect(); + + let primary_col = just("[[") + .ignore_then(col_body) + .then_ignore(just("]]")) + .map(TableCol::Primary); + + let secondary_col = just("[") + .ignore_then(col_body) + .then_ignore(just("]")) + .map(TableCol::Secondary); + + primary_col.or(secondary_col).padded().repeated().collect() +} diff --git a/src/daleth/format.rs b/src/daleth/format.rs index 975773e..79a4e83 100644 --- a/src/daleth/format.rs +++ b/src/daleth/format.rs @@ -1,4 +1,5 @@ use super::{ + custom_parsers::table_to_string, lexer::types::Token, types::Spanned, utils::{prepend_indent, set_indent}, @@ -151,6 +152,11 @@ pub fn format<'src>(spanned_tokens: &Vec>>) -> String { set_indent(t, current_indent + 1), prepend_indent("}", current_indent) ), + Token::TableSyntax(rows) => format!( + "{{> table\n{}\n{}\n", + set_indent(&table_to_string(rows), current_indent + 1), + prepend_indent("}", current_indent) + ), Token::EmptyLine => "\n".to_owned(), }; diff --git a/src/daleth/lexer/mod.rs b/src/daleth/lexer/mod.rs index 1006f90..7b072f9 100644 --- a/src/daleth/lexer/mod.rs +++ b/src/daleth/lexer/mod.rs @@ -3,7 +3,10 @@ pub mod types; use chumsky::prelude::*; use types::*; -use super::types::{Span, Spanned}; +use super::{ + custom_parsers::table_parser, + types::{Span, Spanned}, +}; pub fn lexer<'src>( ) -> impl Parser<'src, &'src str, Vec>>, extra::Err>> { @@ -112,10 +115,7 @@ fn textual<'src>() -> impl Parser<'src, &'src str, Token<'src>, extra::Err() -> impl Parser<'src, &'src str, Token<'src>, extra::Err table"), just("}")) + .map(Token::TableSyntax) + .labelled("Table syntax"); + let mltext = multiline_text_body .clone() .delimited_by(just('{'), just('}')) @@ -162,7 +167,15 @@ fn textual<'src>() -> impl Parser<'src, &'src str, Token<'src>, extra::Err() -> impl Parser<'src, &'src str, Token<'src>, extra::Err>> diff --git a/src/daleth/lexer/types.rs b/src/daleth/lexer/types.rs index 039092b..3b9f4dc 100644 --- a/src/daleth/lexer/types.rs +++ b/src/daleth/lexer/types.rs @@ -1,5 +1,7 @@ use core::fmt; +use crate::daleth::custom_parsers::TableCol; + #[derive(Clone, Debug, PartialEq)] pub enum Token<'src> { // Symbols @@ -28,6 +30,7 @@ pub enum Token<'src> { // Special TextTag(&'src str), Paragraph(&'src str), + TableSyntax(Vec>), // Special for formatting, ignored for parse Comment(&'src str), @@ -115,6 +118,7 @@ impl<'src> fmt::Display for Token<'src> { Token::Code => write!(f, "code"), Token::Pre => write!(f, "pre"), Token::Meta => write!(f, "meta"), + Token::TableSyntax(_) => write!(f, "table syntax"), } } } diff --git a/src/daleth/mod.rs b/src/daleth/mod.rs index dbf90d9..7344628 100644 --- a/src/daleth/mod.rs +++ b/src/daleth/mod.rs @@ -1,5 +1,6 @@ +pub mod custom_parsers; pub mod format; pub mod lexer; pub mod parser; pub mod types; -pub mod utils; +mod utils; diff --git a/src/daleth/parser/mod.rs b/src/daleth/parser/mod.rs index 2cac613..2e1381f 100644 --- a/src/daleth/parser/mod.rs +++ b/src/daleth/parser/mod.rs @@ -1,6 +1,7 @@ pub mod types; use super::{ + custom_parsers::table_to_tag, lexer::types::Token, types::Span, utils::{set_spaces, trim_indent}, @@ -178,11 +179,15 @@ pub fn tag<'tokens, 'src: 'tokens>( } .labelled("Paragraph"); + let table_syntax = select! { + Token::TableSyntax(rows) => table_to_tag(&rows) + }; + choice(( el, h, p, br, ul, ol, row, link, navlink, btn, navbtn, img, table, tcol, tpcol, hr, b, i, bq, footlnk, footn, a, s, sup, sub, disc, )) .or(choice((block, carousel, code, pre, meta))) - .or(choice((el_text, el_tags, paragraph))) + .or(choice((el_text, el_tags, paragraph, table_syntax))) }) }