Add AsciiMath inline and block parsing

feature/epub-rendering
trivernis 4 years ago
parent 06ee81cc15
commit d292dd6f32

25
Cargo.lock generated

@ -21,6 +21,17 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "asciimath-rs"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"charred 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.14"
@ -75,6 +86,11 @@ name = "charred"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "charred"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "chrono"
version = "0.4.11"
@ -299,6 +315,11 @@ dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "2.3.3"
@ -573,6 +594,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "snekdown"
version = "0.18.3"
dependencies = [
"asciimath-rs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"charred 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
"colored 1.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -789,6 +811,7 @@ dependencies = [
"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
"checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum asciimath-rs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60e268057257d5bb6a296d0412445b54f7e36bb90d7bc0071f31310a4231c8ec"
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
"checksum base64 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42"
@ -798,6 +821,7 @@ dependencies = [
"checksum cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)" = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum charred 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8cf73c7fbbaf59d5643f99c6a4413eba1b914a7489c39b730ec7d8d72e7bb061"
"checksum charred 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81443b6f18b18560f94a0e14d529a1db379581473217b1b2a40ecae0c96a5d0e"
"checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
"checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
"checksum colored 1.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59"
@ -826,6 +850,7 @@ dependencies = [
"checksum line-wrap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9"
"checksum linked-hash-map 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
"checksum minify 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ef7c582bd7587da887914eaf294897e82f2f5c98b741f137f2a918cd26a885b"
"checksum miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5"

@ -31,4 +31,5 @@ gh-emoji = "1.0.3"
notify = "4.0.12"
toml = "0.5.6"
serde ="1.0.111"
serde_derive = "1.0.111"
serde_derive = "1.0.111"
asciimath-rs = "0.4.0"

@ -184,6 +184,22 @@ There is a book about snekdown[^book] and a github repo[^github].
Bibliography entries are only shown when used in the document.
## Math
Snekdown allows the embedding of [AsciiMath](http://asciimath.org/):
```
inline math $$ a^2 + b^2 = c^2 $$
Block Math
$$$
A = [[1, 2],[3,4]]
$$$
```
Currently math only get's rendered into MathML which is only supported by Safari and Firefox.
## Roadmap
The end goal is to have a markup language with features similar to LaTeX.
@ -194,7 +210,7 @@ The end goal is to have a markup language with features similar to LaTeX.
- [x] Watching and rendering on change
- [ ] Metadata files
- [x] Bibliography
- [ ] Math
- [x] Math
- [ ] Text sizes
- [ ] Title pages
- [ ] Glossary

@ -4,6 +4,7 @@ use crate::references::bibliography::{BibEntry, BibReference, Bibliography};
use crate::references::configuration::Configuration;
use crate::references::placeholders::ProcessPlaceholders;
use crate::references::templates::{Template, TemplateVariable};
use asciimath_rs::elements::special::Expression;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, RwLock};
@ -40,6 +41,7 @@ pub enum Block {
List(List),
Table(Table),
CodeBlock(CodeBlock),
MathBlock(MathBlock),
Quote(Quote),
Import(Import),
Placeholder(Arc<RwLock<Placeholder>>),
@ -164,6 +166,7 @@ pub enum Inline {
Checkbox(Checkbox),
Emoji(Emoji),
Colored(Colored),
Math(Math),
BibReference(Arc<RwLock<BibReference>>),
TemplateVar(Arc<RwLock<TemplateVariable>>),
}
@ -250,6 +253,16 @@ pub struct Colored {
pub(crate) color: String,
}
#[derive(Clone, Debug)]
pub struct Math {
pub(crate) expression: Expression,
}
#[derive(Clone, Debug)]
pub struct MathBlock {
pub(crate) expression: Expression,
}
// implementations
impl Document {

@ -32,6 +32,7 @@ pub(crate) const R_BRACE: char = '{';
pub(crate) const L_BRACE: char = '}';
pub(crate) const PERCENT: char = '%';
pub(crate) const COMMA: char = ',';
pub(crate) const MATH: char = '$';
// aliases
@ -71,13 +72,14 @@ pub(crate) const STRIKED: char = TILDE;
pub(crate) const UNDERLINED: char = UNDERSCR;
pub(crate) const SUPER: char = UP;
pub(crate) const EMOJI: char = COLON;
pub(crate) const MATH_INLINE: &'static [char] = &[MATH, MATH];
pub(crate) const BOLD: [char; 2] = [ASTERISK, ASTERISK];
// groups
pub(crate) const QUOTES: [char; 2] = [SINGLE_QUOTE, DOUBLE_QUOTE];
pub(crate) const BLOCK_SPECIAL_CHARS: [&[char]; 9] = [
pub(crate) const BLOCK_SPECIAL_CHARS: &'static [&[char]] = &[
&[HASH],
&[HASH, META_OPEN],
&[MINUS, SPACE],
@ -87,9 +89,10 @@ pub(crate) const BLOCK_SPECIAL_CHARS: [&[char]; 9] = [
&[META_OPEN],
&[IMPORT_START, IMPORT_OPEN],
&SQ_CENTERED_START,
&SQ_MATH,
];
pub(crate) const INLINE_SPECIAL_CHARS: [char; 11] = [
pub(crate) const INLINE_SPECIAL_CHARS: &'static [char] = &[
BACKTICK,
TILDE,
UNDERSCR,
@ -101,6 +104,7 @@ pub(crate) const INLINE_SPECIAL_CHARS: [char; 11] = [
SUPER,
EMOJI,
COLOR_START,
MATH,
];
pub(crate) const LIST_SPECIAL_CHARS: [char; 14] = [
@ -119,3 +123,4 @@ pub(crate) const SQ_PHOLDER_STOP: [char; 2] = [PHOLDER_CLOSE, PHOLDER_CLOSE];
pub(crate) const SQ_CENTERED_START: [char; 2] = [PIPE, PIPE];
pub(crate) const SQ_COLOR_START: [char; 2] = [COLOR_START, COLOR_OPEN];
pub(crate) const SQ_BIBREF_START: [char; 2] = [BIBREF_OPEN, BIBREF_REF];
pub(crate) const SQ_MATH: &'static [char] = &[MATH, MATH, MATH];

@ -3,6 +3,7 @@ use crate::format::PlaceholderTemplate;
use crate::references::bibliography::{BibEntry, BibReference};
use crate::references::configuration::Value;
use crate::references::templates::{Template, TemplateVariable};
use asciimath_rs::format::mathml::ToMathML;
use htmlescape::{encode_attribute, encode_minimal};
use minify::html::minify;
use std::cell::RefCell;
@ -64,6 +65,7 @@ impl ToHtml for Inline {
Inline::Colored(colored) => colored.to_html(),
Inline::BibReference(bibref) => bibref.read().unwrap().to_html(),
Inline::TemplateVar(var) => var.read().unwrap().to_html(),
Inline::Math(m) => m.to_html(),
}
}
}
@ -79,6 +81,7 @@ impl ToHtml for Block {
Block::Section(section) => section.to_html(),
Block::Import(import) => import.to_html(),
Block::Placeholder(placeholder) => placeholder.read().unwrap().to_html(),
Block::MathBlock(m) => m.to_html(),
}
}
}
@ -122,6 +125,24 @@ impl ToHtml for Document {
}
}
impl ToHtml for Math {
fn to_html(&self) -> String {
format!(
"<math xmlns='http://www.w3.org/1998/Math/MathML'>{}</math>",
self.expression.to_mathml()
)
}
}
impl ToHtml for MathBlock {
fn to_html(&self) -> String {
format!(
"<math xmlns='http://www.w3.org/1998/Math/MathML' display='block'>{}</math>",
self.expression.to_mathml()
)
}
}
impl ToHtml for Import {
fn to_html(&self) -> String {
let anchor = self.anchor.read().unwrap();

@ -1,6 +1,8 @@
use super::ParseResult;
use crate::elements::tokens::*;
use crate::elements::{Block, CodeBlock, Import, List, ListItem, Paragraph, Quote, Section, Table};
use crate::elements::{
Block, CodeBlock, Import, List, ListItem, MathBlock, Paragraph, Quote, Section, Table,
};
use crate::parser::inline::ParseInline;
use crate::parser::line::ParseLine;
use crate::Parser;
@ -9,6 +11,7 @@ pub(crate) trait ParseBlock {
fn parse_block(&mut self) -> ParseResult<Block>;
fn parse_section(&mut self) -> ParseResult<Section>;
fn parse_code_block(&mut self) -> ParseResult<CodeBlock>;
fn parse_math_block(&mut self) -> ParseResult<MathBlock>;
fn parse_quote(&mut self) -> ParseResult<Quote>;
fn parse_paragraph(&mut self) -> ParseResult<Paragraph>;
fn parse_list(&mut self) -> ParseResult<List>;
@ -36,6 +39,8 @@ impl ParseBlock for Parser {
Block::Table(table)
} else if let Ok(code_block) = self.parse_code_block() {
Block::CodeBlock(code_block)
} else if let Ok(math_block) = self.parse_math_block() {
Block::MathBlock(math_block)
} else if let Ok(quote) = self.parse_quote() {
Block::Quote(quote)
} else if let Ok(import) = self.parse_import() {
@ -120,6 +125,21 @@ impl ParseBlock for Parser {
})
}
/// parses a math block
fn parse_math_block(&mut self) -> ParseResult<MathBlock> {
let start_index = self.ctm.get_index();
self.ctm.seek_whitespace();
self.ctm.assert_sequence(SQ_MATH, Some(start_index))?;
self.ctm.seek_one()?;
let text = self.ctm.get_string_until_sequence(&[SQ_MATH], &[])?;
for _ in 0..1 {
self.ctm.seek_one()?;
}
Ok(MathBlock {
expression: asciimath_rs::parse(text),
})
}
/// parses a quote
fn parse_quote(&mut self) -> ParseResult<Quote> {
let start_index = self.ctm.get_index();

@ -18,6 +18,7 @@ pub(crate) trait ParseInline {
fn parse_bold(&mut self) -> ParseResult<BoldText>;
fn parse_italic(&mut self) -> ParseResult<ItalicText>;
fn parse_striked(&mut self) -> ParseResult<StrikedText>;
fn parse_math(&mut self) -> ParseResult<Math>;
fn parse_monospace(&mut self) -> ParseResult<MonospaceText>;
fn parse_underlined(&mut self) -> ParseResult<UnderlinedText>;
fn parse_superscript(&mut self) -> ParseResult<SuperscriptText>;
@ -84,6 +85,8 @@ impl ParseInline for Parser {
Ok(Inline::Colored(colored))
} else if let Ok(bibref) = self.parse_bibref() {
Ok(Inline::BibReference(bibref))
} else if let Ok(math) = self.parse_math() {
Ok(Inline::Math(math))
} else {
Ok(Inline::Plain(self.parse_plain()?))
}
@ -195,6 +198,20 @@ impl ParseInline for Parser {
})
}
fn parse_math(&mut self) -> ParseResult<Math> {
let start_index = self.ctm.get_index();
self.ctm.assert_sequence(&MATH_INLINE, Some(start_index))?;
self.ctm.seek_one()?;
let content = self
.ctm
.get_string_until_sequence(&[MATH_INLINE, &[LB]], &[])?;
self.ctm.seek_one()?;
Ok(Math {
expression: asciimath_rs::parse(content),
})
}
/// parses monospace text (inline-code) that isn't allowed to contain special characters
fn parse_monospace(&mut self) -> ParseResult<MonospaceText> {
let start_index = self.ctm.get_index();

Loading…
Cancel
Save