Add framework for new tag system

This commit is contained in:
mars 2022-10-09 00:31:53 -06:00
parent 618f5dffa6
commit 5ac8032f92
5 changed files with 284 additions and 42 deletions

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

118
src/parse.rs Normal file
View File

@ -0,0 +1,118 @@
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() {
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 children = std::mem::replace(&mut self.vals, Vec::new());
(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 mut parser = Self::from_value(&child);
let node = match parser.parse_any_tag_kind() {
AnyTagKind::Text(kind) => BlockNode::Text(self.parse_text_tag(kind)),
AnyTagKind::Leaf(kind) => BlockNode::Leaf(self.parse_leaf_tag(kind)),
AnyTagKind::Block(kind) => BlockNode::Block(self.parse_block_tag(kind)),
};
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,
}
}
}

111
src/tag.rs Normal file
View File

@ -0,0 +1,111 @@
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 {
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 {
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)
}
}
}