diff --git a/src/bin/main.rs b/src/bin/main.rs index edf639f01..4ed643d9c 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -59,11 +59,11 @@ fn run() -> Result<(), Box> { ("CMU-Serif-Italic.ttf", font!["Computer Modern", Italic, Serif]), ("CMU-Serif-Bold.ttf", font!["Computer Modern", Bold, Serif]), ("CMU-Serif-Bold-Italic.ttf", font!["Computer Modern", Bold, Italic, Serif]), - ("CMU-Typewriter-Regular.ttf", font!["Computer Modern", Regular, Monospace]), - ("CMU-Typewriter-Italic.ttf", font!["Computer Modern", Italic, Monospace]), - ("CMU-Typewriter-Bold.ttf", font!["Computer Modern", Bold, Monospace]), - ("CMU-Typewriter-Bold-Italic.ttf", font!["Computer Modern", Bold, Italic, Monospace]), - ("NotoEmoji-Regular.ttf", font!["Noto", Regular, SansSerif, Serif, Monospace]), + ("CMU-Typewriter-Regular.ttf", font!["Computer Modern", Regular, Serif, SansSerif, Monospace]), + ("CMU-Typewriter-Italic.ttf", font!["Computer Modern", Italic, Serif, SansSerif, Monospace]), + ("CMU-Typewriter-Bold.ttf", font!["Computer Modern", Bold, Serif, SansSerif, Monospace]), + ("CMU-Typewriter-Bold-Italic.ttf", font!["Computer Modern", Bold, Italic, Serif, SansSerif, Monospace]), + ("NotoEmoji-Regular.ttf", font!["Noto", Regular, Bold, Italic, SansSerif, Serif, Monospace]), ])); // Typeset the source code. diff --git a/src/func.rs b/src/func.rs index 91f3cf74b..e7f3dbfac 100644 --- a/src/func.rs +++ b/src/func.rs @@ -77,13 +77,12 @@ impl Scope { Scope { parsers: HashMap::new() } } - /// Create a new scope with the standard functions contained: - /// - `italic` - /// - `bold` + /// Create a new scope with the standard functions contained. pub fn with_std() -> Scope { let mut std = Scope::new(); std.add::("bold"); std.add::("italic"); + std.add::("mono"); std } @@ -161,3 +160,9 @@ style_func! { pub struct ItalicFunc { "italic" }, style => { style.toggle_class(FontClass::Italic) } } + +style_func! { + /// Typesets text in monospace. + pub struct MonospaceFunc { "mono" }, + style => { style.toggle_class(FontClass::Monospace) } +} diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 3ac23005f..676024e8b 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -116,6 +116,7 @@ impl<'a, 'p> Layouter<'a, 'p> { // Toggle the text styles. Node::ToggleItalics => self.style.to_mut().toggle_class(FontClass::Italic), Node::ToggleBold => self.style.to_mut().toggle_class(FontClass::Bold), + Node::ToggleMonospace => self.style.to_mut().toggle_class(FontClass::Monospace), // Execute a function. Node::Func(func) => self.layout_func(func)?, diff --git a/src/lib.rs b/src/lib.rs index 1c5cf3256..d12e167a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ //! use typeset::export::pdf::PdfExporter; //! //! // Simple example source code. -//! let src = "Hello World from __Typeset__! 🌍"; +//! let src = "Hello World from _Typeset_! 🌍"; //! //! // Create a typesetter with a font provider that provides three fonts //! // (two sans-serif fonts and a fallback for the emoji). @@ -193,11 +193,11 @@ mod test { ("CMU-Serif-Italic.ttf", font!["Computer Modern", Italic, Serif]), ("CMU-Serif-Bold.ttf", font!["Computer Modern", Bold, Serif]), ("CMU-Serif-Bold-Italic.ttf", font!["Computer Modern", Bold, Italic, Serif]), - ("CMU-Typewriter-Regular.ttf", font!["Computer Modern", Regular, Monospace]), - ("CMU-Typewriter-Italic.ttf", font!["Computer Modern", Italic, Monospace]), - ("CMU-Typewriter-Bold.ttf", font!["Computer Modern", Bold, Monospace]), - ("CMU-Typewriter-Bold-Italic.ttf", font!["Computer Modern", Bold, Italic, Monospace]), - ("NotoEmoji-Regular.ttf", font!["Noto", Regular, SansSerif, Serif, Monospace]), + ("CMU-Typewriter-Regular.ttf", font!["Computer Modern", Regular, Serif, SansSerif, Monospace]), + ("CMU-Typewriter-Italic.ttf", font!["Computer Modern", Italic, Serif, SansSerif, Monospace]), + ("CMU-Typewriter-Bold.ttf", font!["Computer Modern", Bold, Serif, SansSerif, Monospace]), + ("CMU-Typewriter-Bold-Italic.ttf", font!["Computer Modern", Bold, Italic, Serif, SansSerif, Monospace]), + ("NotoEmoji-Regular.ttf", font!["Noto", Regular, Bold, Italic, SansSerif, Serif, Monospace]), ])); // Typeset into document. @@ -213,20 +213,21 @@ mod test { #[test] fn features() { test("features", r" - **Features Test Page** + *Features Test Page* - __Multiline:__ + _Multiline:_ Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. - __Emoji:__ Hello World! 🌍 + _Emoji:_ Hello World! 🌍 - __Styles:__ This is made **bold** and that __italic__ using the built-in syntax! + _Styles:_ This is made *bold*, that _italic_ and this one `monospace` using the + built-in syntax! - __Styles with functions:__ This is in [bold][boldface] and that is in [italic][italics] - using library functions! + _Styles with functions:_ This [bold][word] is made bold and [italic][that] is italic + using the standard library functions [mono][bold] and `italic`! "); } diff --git a/src/parsing.rs b/src/parsing.rs index 79066e47f..902203eee 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -81,12 +81,12 @@ impl<'s> Iterator for Tokens<'s> { /// Advance the iterator, return the next token or nothing. fn next(&mut self) -> Option> { - use TokensState as TS; + use TokensState as TU; // Go to the body state if the function has a body or return to the top-of-stack state. - if self.state == TS::MaybeBody { + if self.state == TU::MaybeBody { if self.chars.peek()?.1 == '[' { - self.state = TS::Body; + self.state = TU::Body; return Some(self.consumed(Token::LeftBracket)); } else { self.unswitch(); @@ -100,12 +100,12 @@ impl<'s> Iterator for Tokens<'s> { Some(match next { // Functions '[' => { - self.switch(TS::Function); + self.switch(TU::Function); Token::LeftBracket }, ']' => { - if self.state == TS::Function { - self.state = TS::MaybeBody; + if self.state == TU::Function { + self.state = TU::MaybeBody; } else { self.unswitch(); } @@ -168,21 +168,20 @@ impl<'s> Iterator for Tokens<'s> { '\r' if afterwards == Some('\n') => self.consumed(Token::Newline), c if is_newline_char(c) => Token::Newline, - // Context sensitive operators in headers - ':' if self.state == TS::Function => Token::Colon, - '=' if self.state == TS::Function => Token::Equals, + // Star/Underscore/Backtick in bodies + '*' if self.state == TU::Body => Token::Star, + '_' if self.state == TU::Body => Token::Underscore, + '`' if self.state == TU::Body => Token::Backtick, - // Double star/underscore in bodies - '*' if self.state == TS::Body && afterwards == Some('*') - => self.consumed(Token::DoubleStar), - '_' if self.state == TS::Body && afterwards == Some('_') - => self.consumed(Token::DoubleUnderscore), + // Context sensitive operators in headers + ':' if self.state == TU::Function => Token::Colon, + '=' if self.state == TU::Function => Token::Equals, // Escaping '\\' => { if let Some((index, c)) = self.chars.peek() { let escapable = match c { - '[' | ']' | '$' | '#' | '\\' | '*' | '_' | '/' => true, + '[' | ']' | '\\' | '*' | '_' | '`' | ':' | '=' | '/' => true, _ => false, }; @@ -202,13 +201,11 @@ impl<'s> Iterator for Tokens<'s> { while let Some((index, c)) = self.chars.peek() { let second = self.chars.peek_second().map(|p| p.1); - // Whether the next token is still from the next or not. + // Whether the next token is still from the text or not. let continues = match c { - '[' | ']' | '$' | '#' | '\\' => false, - ':' | '=' if self.state == TS::Function => false, - - '*' if self.state == TS::Body => second != Some('*'), - '_' if self.state == TS::Body => second != Some('_'), + '[' | ']' | '\\' => false, + '*' | '_' | '`' if self.state == TU::Body => false, + ':' | '=' if self.state == TU::Function => false, '/' => second != Some('/') && second != Some('*'), '*' => second != Some('/'), @@ -390,8 +387,9 @@ impl<'s> Parser<'s> { Token::RightBracket => return Err(ParseError::new("unexpected closing bracket")), // Modifiers - Token::DoubleUnderscore => self.append_consumed(Node::ToggleItalics), - Token::DoubleStar => self.append_consumed(Node::ToggleBold), + Token::Underscore => self.append_consumed(Node::ToggleItalics), + Token::Star => self.append_consumed(Node::ToggleBold), + Token::Backtick => self.append_consumed(Node::ToggleMonospace), // Normal text Token::Text(word) => self.append_consumed(Node::Text(word.to_owned())), @@ -675,7 +673,7 @@ error_type! { mod token_tests { use super::*; use Token::{Space as S, Newline as N, LeftBracket as L, RightBracket as R, - Colon as C, Equals as E, DoubleUnderscore as DU, DoubleStar as DS, + Colon as C, Equals as E, Underscore as TU, Star as TS, Backtick as TB, Text as T, LineComment as LC, BlockComment as BC, StarSlash as SS}; /// Test if the source code tokenizes to the tokens. @@ -690,8 +688,9 @@ mod token_tests { test("Hallo", vec![T("Hallo")]); test("[", vec![L]); test("]", vec![R]); - test("**", vec![DS]); - test("__", vec![DU]); + test("*", vec![TS]); + test("_", vec![TU]); + test("`", vec![TB]); test("\n", vec![N]); } @@ -711,11 +710,9 @@ mod token_tests { fn tokenize_escape() { test(r"\[", vec![T("[")]); test(r"\]", vec![T("]")]); - test(r"\#", vec![T("#")]); - test(r"\$", vec![T("$")]); - test(r"\**", vec![T("*"), T("*")]); + test(r"\**", vec![T("*"), TS]); test(r"\*", vec![T("*")]); - test(r"\__", vec![T("_"), T("_")]); + test(r"\__", vec![T("_"), TU]); test(r"\_", vec![T("_")]); test(r"\hello", vec![T("\\"), T("hello")]); } @@ -736,12 +733,12 @@ mod token_tests { [page: size=A4] [font: size=12pt] - Das ist ein Beispielsatz mit **fetter** Schrift. + Das ist ein Beispielsatz mit *fetter* Schrift. ", vec![ N, S, L, T("page"), C, S, T("size"), E, T("A4"), R, N, S, L, T("font"), C, S, T("size"), E, T("12pt"), R, N, N, S, T("Das"), S, T("ist"), S, T("ein"), S, T("Beispielsatz"), S, T("mit"), S, - DS, T("fetter"), DS, S, T("Schrift."), N, S + TS, T("fetter"), TS, S, T("Schrift."), N, S ]); } @@ -777,12 +774,12 @@ mod token_tests { test("/* My /* line // */ comment */", vec![BC(" My /* line // */ comment ")]) } - /// This test has a special look at the double underscore syntax. + /// This test has a special look at the underscore syntax. #[test] - fn tokenize_double_underscore() { - test("he__llo__world_ _ __ Now this_ is__ special!", - vec![T("he"), DU, T("llo"), DU, T("world_"), S, T("_"), S, DU, S, T("Now"), S, - T("this_"), S, T("is"), DU, S, T("special!")]); + fn tokenize_underscores() { + test("he_llo_world_ __ Now this_ is_ special!", + vec![T("he"), TU, T("llo"), TU, T("world"), TU, S, TU, TU, S, T("Now"), S, + T("this"), TU, S, T("is"), TU, S, T("special!")]); } /// This test is for checking if non-ASCII characters get parsed correctly. diff --git a/src/syntax.rs b/src/syntax.rs index c469686b3..7d30d8949 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -24,10 +24,12 @@ pub enum Token<'s> { /// /// Outside of functions headers, same as with [Colon](Token::Colon). Equals, - /// Two underscores, indicating text in italics. - DoubleUnderscore, - /// Two stars, indicating bold text. - DoubleStar, + /// An underscore, indicating text in italics. + Underscore, + /// A star, indicating bold text. + Star, + /// A backtick, indicating monospace text. + Backtick, /// A line comment. LineComment(&'s str), /// A block comment. @@ -65,6 +67,8 @@ pub enum Node { ToggleItalics, /// Indicates that boldface was enabled / disabled. ToggleBold, + /// Indicates that monospace was enabled / disabled. + ToggleMonospace, /// Literal text. Text(String), /// A function invocation.