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 | - 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 ]]
}

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::{
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<Spanned<Token<'src>>>) -> 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(),
};

View file

@ -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<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('\\'))))
.labelled("Multi-line escape sequence");
let text = none_of("\n")
.repeated()
.to_slice()
.padded_by(text::inline_whitespace());
let text = none_of("\n").repeated().to_slice().map(|s: &str| s.trim());
let text_body = just(':')
.ignore_then(text)
@ -139,6 +139,11 @@ fn textual<'src>() -> impl Parser<'src, &'src str, Token<'src>, extra::Err<Rich<
.map(Token::Paragraph)
.labelled("Paragraph syntax");
let table_syntax = table_parser()
.delimited_by(just("{> 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<Rich<
.map(Token::MLRText)
.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>>>

View file

@ -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<TableCol<'src>>),
// 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"),
}
}
}

View file

@ -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;

View file

@ -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)))
})
}