Compare commits

...

3 Commits

Author SHA1 Message Date
mars 2aa36d9b02 Use the new DOM parser on the input file 2022-10-09 00:48:05 -06:00
mars 5ac8032f92 Add framework for new tag system 2022-10-09 00:31:53 -06:00
mars 618f5dffa6 Add styled header to test 2022-10-08 23:07:20 -06:00
6 changed files with 311 additions and 48 deletions

View File

@ -30,11 +30,15 @@ lines.")
(strikethrough (bold (italic (underline "EVERYTHING AT ONCE"))))
".")
(ul "Unordered list entry")
(ul "Unordered list entry")
(ul "Unordered list entry")
(h3 "Styling " (italic "even ") "works " (bold "in ") (strikethrough "headers."))
(ol "List entry 1")
(ol "List entry 2")
(ol "List entry 3")
(ul
(li "Unordered list entry")
(li "Unordered list entry")
(li "Unordered list entry"))
(ol
(li "List entry 1")
(li "List entry 2")
(li "List entry 3"))
)

6
src/dom.rs Normal file
View File

@ -0,0 +1,6 @@
use crate::tag::BlockTag;
#[derive(Clone, Debug, Hash)]
pub struct Dom {
pub tags: Vec<BlockTag>,
}

View File

@ -1,49 +1,12 @@
use ansi_term::Style;
use std::str::FromStr;
use strum::EnumString;
#[derive(Clone, Debug)]
pub struct Stylesheet {
pub h1: Style,
pub h2: Style,
pub h3: Style,
pub h1_prefix: String,
pub h2_prefix: String,
pub h3_prefix: String,
pub p: Style,
pub underline: Style,
pub bold: Style,
pub italic: Style,
pub strikethrough: Style,
pub bullet: Style,
pub ul_prefix: String,
pub list_indent: usize,
}
pub mod dom;
pub mod parse;
pub mod style;
pub mod tag;
impl Default for Stylesheet {
fn default() -> Self {
use ansi_term::Color::*;
let header_style = Blue.bold().underline();
Self {
h1: header_style,
h2: header_style,
h3: header_style,
h1_prefix: "# ".to_string(),
h2_prefix: "## ".to_string(),
h3_prefix: "### ".to_string(),
p: Style::new(),
underline: Style::new().underline(),
bold: Style::new().bold(),
italic: Style::new().italic(),
strikethrough: Style::new().strikethrough(),
bullet: Black.into(),
ul_prefix: "* ".to_string(),
list_indent: 4,
}
}
}
use style::Stylesheet;
#[derive(Clone, Copy, Debug, EnumString)]
#[strum(serialize_all = "kebab-case", ascii_case_insensitive)]
@ -192,6 +155,10 @@ fn main() {
println!("{:#?}", expr);
let dom = parse::Parser::parse(&expr);
println!("{:#?}", dom);
let cons = expr.as_cons().unwrap().to_vec().0;
let style = Stylesheet::default();
let width = 40;

127
src/parse.rs Normal file
View File

@ -0,0 +1,127 @@
use crate::dom::Dom;
use crate::tag::*;
use lexpr::{Cons, Value};
use std::str::FromStr;
pub struct Parser {
vals: Vec<Value>,
}
impl Parser {
pub fn parse(document: &Value) -> Dom {
let parser = Self::from_value(document);
let mut tags = Vec::new();
for val in parser.vals.into_iter().rev() {
let mut parser = Self::from_value(&val);
let kind = parser
.parse_any_tag_kind()
.as_block()
.expect("Root tag must be a block tag");
let tag = parser.parse_block_tag(kind);
tags.push(tag);
}
Dom { tags }
}
pub fn from_value(value: &Value) -> Self {
let cons = value.as_cons().expect("Must be a cons");
Self::from_cons(cons)
}
pub fn from_cons(cons: &Cons) -> Self {
let mut vals = cons.to_vec().0;
vals.reverse();
Self { vals }
}
pub fn parse_args(&mut self) -> (Vec<(String, Value)>, Vec<Value>) {
let args = Vec::new();
let mut children = std::mem::replace(&mut self.vals, Vec::new());
children.reverse();
(args, children)
}
pub fn parse_text_tag(&mut self, kind: TextTagKind) -> TextTag {
let (_args, children_vals) = self.parse_args();
let mut children = Vec::new();
for child in children_vals.into_iter() {
let node = match child {
Value::Cons(cons) => {
let mut parser = Self::from_cons(&cons);
let kind = parser
.parse_any_tag_kind()
.as_text()
.expect("Text tag can only have other text tags as children");
TextNode::Tag(parser.parse_text_tag(kind))
}
Value::String(string) => TextNode::Text(string.to_string()),
_ => panic!("Text tag child must be a string or a cons"),
};
children.push(node);
}
TextTag { kind, children }
}
pub fn parse_leaf_tag(&mut self, kind: LeafTagKind) -> LeafTag {
let (_args, children) = self.parse_args();
if !children.is_empty() {
panic!("Leaf tag cannot have children");
}
LeafTag { kind }
}
pub fn parse_block_tag(&mut self, kind: BlockTagKind) -> BlockTag {
let (_args, children_vals) = self.parse_args();
let mut children = Vec::new();
for child in children_vals.into_iter() {
let node = match child {
Value::Cons(cons) => {
let mut parser = Self::from_cons(&cons);
match parser.parse_any_tag_kind() {
AnyTagKind::Text(kind) => BlockNode::Text(parser.parse_text_tag(kind)),
AnyTagKind::Leaf(kind) => BlockNode::Leaf(parser.parse_leaf_tag(kind)),
AnyTagKind::Block(kind) => BlockNode::Block(parser.parse_block_tag(kind)),
}
}
Value::String(string) => BlockNode::Text(TextTag {
kind: TextTagKind::Plain,
children: vec![TextNode::Text(string.to_string())],
}),
_ => panic!("Block tag child must be either a string or a cons"),
};
children.push(node);
}
BlockTag { kind, children }
}
pub fn parse_any_tag_kind(&mut self) -> AnyTagKind {
let symbol = self.parse_symbol();
AnyTagKind::from_str(&symbol).expect("Unrecognized tag")
}
pub fn parse_symbol(&mut self) -> String {
self.vals
.pop()
.as_ref()
.map(|v| v.as_symbol())
.flatten()
.expect("Expected symbol")
.to_string()
}
}

44
src/style.rs Normal file
View File

@ -0,0 +1,44 @@
use ansi_term::Style;
#[derive(Clone, Debug)]
pub struct Stylesheet {
pub h1: Style,
pub h2: Style,
pub h3: Style,
pub h1_prefix: String,
pub h2_prefix: String,
pub h3_prefix: String,
pub p: Style,
pub underline: Style,
pub bold: Style,
pub italic: Style,
pub strikethrough: Style,
pub bullet: Style,
pub ul_prefix: String,
pub list_indent: usize,
}
impl Default for Stylesheet {
fn default() -> Self {
use ansi_term::Color::*;
let header_style = Blue.bold().underline();
Self {
h1: header_style,
h2: header_style,
h3: header_style,
h1_prefix: "# ".to_string(),
h2_prefix: "## ".to_string(),
h3_prefix: "### ".to_string(),
p: Style::new(),
underline: Style::new().underline(),
bold: Style::new().bold(),
italic: Style::new().italic(),
strikethrough: Style::new().strikethrough(),
bullet: Black.into(),
ul_prefix: "* ".to_string(),
list_indent: 4,
}
}
}

115
src/tag.rs Normal file
View File

@ -0,0 +1,115 @@
use std::str::FromStr;
use strum::EnumString;
#[derive(Clone, Debug, Hash, PartialEq, Eq, EnumString)]
#[strum(serialize_all = "kebab-case", ascii_case_insensitive)]
pub enum TextTagKind {
Plain,
Italic,
Bold,
Underline,
Strikethrough,
A,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum TextNode {
Text(String),
Tag(TextTag),
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct TextTag {
pub kind: TextTagKind,
pub children: Vec<TextNode>,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, EnumString)]
#[strum(serialize_all = "kebab-case", ascii_case_insensitive)]
pub enum LeafTagKind {
Figlet,
Code,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct LeafTag {
pub kind: LeafTagKind,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, EnumString)]
#[strum(serialize_all = "kebab-case", ascii_case_insensitive)]
pub enum BlockTagKind {
H1,
H2,
H3,
P,
Ul,
Ol,
Li,
Div,
Table,
Tr,
Td,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum BlockNode {
Text(TextTag),
Leaf(LeafTag),
Block(BlockTag),
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct BlockTag {
pub kind: BlockTagKind,
pub children: Vec<BlockNode>,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum AnyTagKind {
Text(TextTagKind),
Leaf(LeafTagKind),
Block(BlockTagKind),
}
impl AnyTagKind {
pub fn as_text(self) -> Option<TextTagKind> {
if let AnyTagKind::Text(text) = self {
Some(text)
} else {
None
}
}
pub fn as_leaf(self) -> Option<LeafTagKind> {
if let AnyTagKind::Leaf(leaf) = self {
Some(leaf)
} else {
None
}
}
pub fn as_block(self) -> Option<BlockTagKind> {
if let AnyTagKind::Block(block) = self {
Some(block)
} else {
None
}
}
}
impl FromStr for AnyTagKind {
type Err = strum::ParseError;
fn from_str(string: &str) -> Result<Self, strum::ParseError> {
if let Ok(text) = TextTagKind::from_str(string) {
Ok(AnyTagKind::Text(text))
} else if let Ok(leaf) = LeafTagKind::from_str(string) {
Ok(AnyTagKind::Leaf(leaf))
} else if let Ok(block) = BlockTagKind::from_str(string) {
Ok(AnyTagKind::Block(block))
} else {
Err(strum::ParseError::VariantNotFound)
}
}
}