feat: table syntax and formatting

This commit is contained in:
Artemy Egorov 2024-08-12 20:21:19 +03:00
parent 6099dbd236
commit d2fc37fed3
7 changed files with 172 additions and 20 deletions

View file

@ -117,7 +117,7 @@ row [
]] ]]
] ]
# Table has custom format if text used # Table has custom format
# +| cells | - primary column # +| cells | - primary column
# | cells | - secondary column # | cells | - secondary column
# | Element | Description | - converts to # | Element | Description | - converts to
@ -125,14 +125,14 @@ row [
# Element # Element
# Description # Description
# ] # ]
# table { {> table
# +| Tag | Description | [[ Tag | Description ]]
# | h | Heading | [ h | Heading ]
# | p | Paragraph | [ p | Paragraph ]
# | img | Image | [ img | Image ]
# | link | Link | [ link | Link ]
# | btn | Button | [ btn | Button ]
# | ul | Unordered list | [ ul | Unordered list ]
# | br | Line break | [ br | Line break ]
# +| quantity | 7 | [[ quantity | 7 ]]
# } }

View file

@ -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<TableCol>) -> 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<TableCol>) -> String {
let mut max_len: HashMap<usize, usize> = 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<String> = 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<TableCol<'src>>, extra::Err<Rich<'src, char, Span>>> {
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()
}

View file

@ -1,4 +1,5 @@
use super::{ use super::{
custom_parsers::table_to_string,
lexer::types::Token, lexer::types::Token,
types::Spanned, types::Spanned,
utils::{prepend_indent, set_indent}, utils::{prepend_indent, set_indent},
@ -151,6 +152,11 @@ pub fn format<'src>(spanned_tokens: &Vec<Spanned<Token<'src>>>) -> String {
set_indent(t, current_indent + 1), set_indent(t, current_indent + 1),
prepend_indent("}", current_indent) 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(), Token::EmptyLine => "\n".to_owned(),
}; };

View file

@ -3,7 +3,10 @@ pub mod types;
use chumsky::prelude::*; use chumsky::prelude::*;
use types::*; use types::*;
use super::types::{Span, Spanned}; use super::{
custom_parsers::table_parser,
types::{Span, Spanned},
};
pub fn lexer<'src>( pub fn lexer<'src>(
) -> impl Parser<'src, &'src str, Vec<Spanned<Token<'src>>>, extra::Err<Rich<'src, char, Span>>> { ) -> impl Parser<'src, &'src str, Vec<Spanned<Token<'src>>>, extra::Err<Rich<'src, char, Span>>> {
@ -112,10 +115,7 @@ fn textual<'src>() -> impl Parser<'src, &'src str, Token<'src>, extra::Err<Rich<
.ignore_then(choice((just('}'), just('\\')))) .ignore_then(choice((just('}'), just('\\'))))
.labelled("Multi-line escape sequence"); .labelled("Multi-line escape sequence");
let text = none_of("\n") let text = none_of("\n").repeated().to_slice().map(|s: &str| s.trim());
.repeated()
.to_slice()
.padded_by(text::inline_whitespace());
let text_body = just(':') let text_body = just(':')
.ignore_then(text) .ignore_then(text)
@ -139,6 +139,11 @@ fn textual<'src>() -> impl Parser<'src, &'src str, Token<'src>, extra::Err<Rich<
.map(Token::Paragraph) .map(Token::Paragraph)
.labelled("Paragraph syntax"); .labelled("Paragraph syntax");
let table_syntax = table_parser()
.delimited_by(just("{> table"), just("}"))
.map(Token::TableSyntax)
.labelled("Table syntax");
let mltext = multiline_text_body let mltext = multiline_text_body
.clone() .clone()
.delimited_by(just('{'), just('}')) .delimited_by(just('{'), just('}'))
@ -162,7 +167,15 @@ fn textual<'src>() -> impl Parser<'src, &'src str, Token<'src>, extra::Err<Rich<
.map(Token::MLRText) .map(Token::MLRText)
.labelled("Raw multiline text"); .labelled("Raw multiline text");
choice((paragraph, mlmstext, rmltext, mltext, text_body, text_tag)) choice((
table_syntax,
paragraph,
mlmstext,
rmltext,
mltext,
text_body,
text_tag,
))
} }
fn comment<'src>() -> impl Parser<'src, &'src str, Token<'src>, extra::Err<Rich<'src, char, Span>>> fn comment<'src>() -> impl Parser<'src, &'src str, Token<'src>, extra::Err<Rich<'src, char, Span>>>

View file

@ -1,5 +1,7 @@
use core::fmt; use core::fmt;
use crate::daleth::custom_parsers::TableCol;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Token<'src> { pub enum Token<'src> {
// Symbols // Symbols
@ -28,6 +30,7 @@ pub enum Token<'src> {
// Special // Special
TextTag(&'src str), TextTag(&'src str),
Paragraph(&'src str), Paragraph(&'src str),
TableSyntax(Vec<TableCol<'src>>),
// Special for formatting, ignored for parse // Special for formatting, ignored for parse
Comment(&'src str), Comment(&'src str),
@ -115,6 +118,7 @@ impl<'src> fmt::Display for Token<'src> {
Token::Code => write!(f, "code"), Token::Code => write!(f, "code"),
Token::Pre => write!(f, "pre"), Token::Pre => write!(f, "pre"),
Token::Meta => write!(f, "meta"), Token::Meta => write!(f, "meta"),
Token::TableSyntax(_) => write!(f, "table syntax"),
} }
} }
} }

View file

@ -1,5 +1,6 @@
pub mod custom_parsers;
pub mod format; pub mod format;
pub mod lexer; pub mod lexer;
pub mod parser; pub mod parser;
pub mod types; pub mod types;
pub mod utils; mod utils;

View file

@ -1,6 +1,7 @@
pub mod types; pub mod types;
use super::{ use super::{
custom_parsers::table_to_tag,
lexer::types::Token, lexer::types::Token,
types::Span, types::Span,
utils::{set_spaces, trim_indent}, utils::{set_spaces, trim_indent},
@ -178,11 +179,15 @@ pub fn tag<'tokens, 'src: 'tokens>(
} }
.labelled("Paragraph"); .labelled("Paragraph");
let table_syntax = select! {
Token::TableSyntax(rows) => table_to_tag(&rows)
};
choice(( choice((
el, h, p, br, ul, ol, row, link, navlink, btn, navbtn, img, table, tcol, tpcol, hr, b, 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, i, bq, footlnk, footn, a, s, sup, sub, disc,
)) ))
.or(choice((block, carousel, code, pre, meta))) .or(choice((block, carousel, code, pre, meta)))
.or(choice((el_text, el_tags, paragraph))) .or(choice((el_text, el_tags, paragraph, table_syntax)))
}) })
} }