Initial commit

This commit is contained in:
mars 2022-10-08 22:52:24 -06:00
commit 5496a7064c
6 changed files with 540 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

280
Cargo.lock generated Normal file
View File

@ -0,0 +1,280 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "aho-corasick"
version = "0.7.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "getrandom"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash",
]
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "lexpr"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceee0b80e0043f17bf81130471e1b0975179af75fe657af45577d80e2698fe3b"
dependencies = [
"itoa",
"lexpr-macros",
"proc-macro-hack",
"ryu",
]
[[package]]
name = "lexpr-macros"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd627fb38e19c00d8d068618259205f7a91c91aeade5c15bc35dbca037bb1c35"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote",
]
[[package]]
name = "libc"
version = "0.2.134"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "once_cell"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
version = "1.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
[[package]]
name = "rustversion"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "smawk"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
[[package]]
name = "strum"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]]
name = "syn"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "textwrap"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
dependencies = [
"smawk",
"unicode-linebreak",
"unicode-width",
]
[[package]]
name = "tml"
version = "0.1.0"
dependencies = [
"ansi_term",
"lexpr",
"strum",
"textwrap",
]
[[package]]
name = "unicode-ident"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]]
name = "unicode-linebreak"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137"
dependencies = [
"hashbrown",
"regex",
]
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "tml"
version = "0.1.0"
edition = "2021"
[dependencies]
lexpr = "0.2"
strum = { version = "0.24", features = ["derive"] }
ansi_term = "0.12"
textwrap = "0.15"

1
assets/simple.sex Normal file
View File

@ -0,0 +1 @@
((h1 "Hello world!"))

40
assets/test.sex Normal file
View File

@ -0,0 +1,40 @@
(
(h1 "Hello world!")
(p "Text goes here.")
(h2 "Heading 2")
(p "Text
spread
across
multiple
lines.")
(h3 "Heading 3")
(p
"Some text can be "
(italic "italicized")
" and some text can be "
(bold "emboldened") ". "
"Other text can be "
(underline "underlined") ". "
"If you want to write text "
"but pretend that you didn't, "
"you can "
(strikethrough "strike it out") ".")
(p
"You can even do "
(strikethrough (bold (italic (underline "EVERYTHING AT ONCE"))))
".")
(ul "Unordered list entry")
(ul "Unordered list entry")
(ul "Unordered list entry")
(ol "List entry 1")
(ol "List entry 2")
(ol "List entry 3")
)

208
src/main.rs Normal file
View File

@ -0,0 +1,208 @@
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,
}
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,
}
}
}
#[derive(Clone, Copy, Debug, EnumString)]
#[strum(serialize_all = "kebab-case", ascii_case_insensitive)]
pub enum TagKind {
H1,
H2,
H3,
P,
Underline,
Italic,
Bold,
Strikethrough,
Ul,
Ol,
}
#[derive(Clone, Debug)]
pub struct Tag {
pub kind: TagKind,
pub children: Vec<Node>,
}
impl Tag {
pub fn layout(&self, style: &Stylesheet, available_width: usize) -> Vec<String> {
use TagKind::*;
match self.kind {
H1 | H2 | H3 | P | Underline | Italic | Bold => {
let styled = self.style(style);
textwrap::wrap(&styled, available_width)
.into_iter()
.map(|s| s.to_string())
.collect()
}
Ul => {
if available_width <= style.list_indent {
panic!("Ran out of available width!");
}
let available_width = available_width - style.list_indent;
let styled = self.style_children(style);
let subsequent_indent: String =
std::iter::repeat(" ").take(style.list_indent).collect();
let options = textwrap::Options::new(available_width)
.initial_indent(&style.ul_prefix)
.subsequent_indent(&subsequent_indent);
textwrap::wrap(&styled, options)
.into_iter()
.map(|s| s.to_string())
.collect()
}
_ => unimplemented!(),
}
}
pub fn style_children(&self, style: &Stylesheet) -> String {
let mut string = String::new();
for child in self.children.iter() {
let child_string = match child {
Node::Tag(tag) => tag.style(style),
Node::String(string) => string.to_owned(),
};
string.push_str(&child_string);
}
string
}
pub fn style(&self, style: &Stylesheet) -> String {
use TagKind::*;
let prefix = match self.kind {
H1 => Some(&style.h1_prefix),
H2 => Some(&style.h2_prefix),
H3 => Some(&style.h3_prefix),
_ => None,
};
let string = self.style_children(style);
let style = match self.kind {
H1 => style.h1,
H2 => style.h2,
H3 => style.h3,
P => style.p,
Underline => style.underline,
Bold => style.bold,
Italic => style.italic,
Strikethrough => style.strikethrough,
_ => panic!("Cannot style {:?} tags", self.kind),
};
if let Some(prefix) = prefix {
let string = format!("{}{}", prefix, string);
style.paint(string).to_string()
} else {
style.paint(string).to_string()
}
}
}
#[derive(Clone, Debug)]
pub enum Node {
Tag(Tag),
String(String),
}
pub fn parse_cons(inner: lexpr::Cons) -> Tag {
let mut iter = inner.to_vec().0.into_iter();
let symbol = match iter.next() {
Some(lexpr::Value::Symbol(symbol)) => symbol,
_ => panic!("First element of cons must be a symbol"),
};
let kind = match TagKind::from_str(&symbol) {
Ok(kind) => kind,
_ => panic!("Unrecognized tag kind {}", symbol),
};
let mut children = Vec::new();
for node in iter {
let child = match node {
lexpr::Value::String(string) => Node::String(string.to_string()),
lexpr::Value::Cons(cons) => Node::Tag(parse_cons(cons)),
_ => unimplemented!("Invalid tag argument: {:#?}", node),
};
children.push(child);
}
Tag { kind, children }
}
fn main() {
let src_path = std::env::args().nth(1).unwrap();
let src = std::fs::read_to_string(src_path).unwrap();
let options =
lexpr::parse::Options::new().with_string_syntax(lexpr::parse::StringSyntax::Elisp);
let expr = lexpr::from_str_custom(&src, options).unwrap();
println!("{:#?}", expr);
let cons = expr.as_cons().unwrap().to_vec().0;
let style = Stylesheet::default();
let width = 40;
for value in cons {
let tag_cons = value.as_cons().unwrap().to_owned();
let tag = parse_cons(tag_cons);
for line in tag.layout(&style, width) {
println!("{}", line);
}
println!("");
}
}