diff --git a/docs/grammar.md b/docs/grammar.md index c99257df85..190782bba4 100644 --- a/docs/grammar.md +++ b/docs/grammar.md @@ -27,7 +27,9 @@ BACKTICK -> U+0060 LF -> U+000A -Production -> `@root`? Name ` ->` Expression +Production -> + ( Comment LF )* + `@root`? Name ` ->` Expression Name -> + @@ -55,6 +57,7 @@ Expr1 -> Unicode | NonTerminal | Break + | Comment | Terminal | Charset | Prose @@ -67,6 +70,8 @@ NonTerminal -> Name Break -> LF ` `+ +Comment -> `//` ~[LF]+ + Terminal -> BACKTICK ~[LF]+ BACKTICK Charset -> `[` (` `* Characters)+ ` `* `]` @@ -96,6 +101,7 @@ The general format is a series of productions separated by blank lines. The expr | Unicode | U+0060 | A single unicode character. | | NonTerminal | FunctionParameters | A reference to another production by name. | | Break | | This is used internally by the renderer to detect line breaks and indentation. | +| Comment | // Single line comment. | A comment extending to the end of the line. | | Terminal | \`example\` | This is a sequence of exact characters, surrounded by backticks | | Charset | [ \`A\`-\`Z\` \`0\`-\`9\` \`_\` ] | A choice from a set of characters, space separated. There are three different forms. | | CharacterRange | [ \`A\`-\`Z\` ] | A range of characters, each character should be in backticks. diff --git a/mdbook-spec/src/grammar.rs b/mdbook-spec/src/grammar.rs index 98d3cd5d9d..8a15c71936 100644 --- a/mdbook-spec/src/grammar.rs +++ b/mdbook-spec/src/grammar.rs @@ -22,6 +22,8 @@ pub struct Grammar { #[derive(Debug)] pub struct Production { name: String, + /// Comments and breaks that precede the production name. + comments: Vec, /// Category is from the markdown lang string, and defines how it is /// grouped and organized on the summary page. category: String, @@ -70,6 +72,8 @@ enum ExpressionKind { /// /// Used by the renderer to help format and structure the grammar. Break(usize), + /// `// Single line comment.` + Comment(String), /// ``[`A`-`Z` `_` LF]`` Charset(Vec), /// ``~[` ` LF]`` @@ -135,6 +139,7 @@ impl Expression { ExpressionKind::Terminal(_) | ExpressionKind::Prose(_) | ExpressionKind::Break(_) + | ExpressionKind::Comment(_) | ExpressionKind::Unicode(_) => {} ExpressionKind::Charset(set) => { for ch in set { diff --git a/mdbook-spec/src/grammar/parser.rs b/mdbook-spec/src/grammar/parser.rs index 631fb7ae8f..c2d6fd546b 100644 --- a/mdbook-spec/src/grammar/parser.rs +++ b/mdbook-spec/src/grammar/parser.rs @@ -122,6 +122,12 @@ impl Parser<'_> { } fn parse_production(&mut self, category: &str, path: &Path) -> Result { + let mut comments = Vec::new(); + while let Ok(comment) = self.parse_comment() { + self.expect("\n", "expected newline")?; + comments.push(Expression::new_kind(comment)); + comments.push(Expression::new_kind(ExpressionKind::Break(0))); + } let is_root = self.parse_is_root(); self.space0(); let name = self @@ -133,6 +139,7 @@ impl Parser<'_> { }; Ok(Production { name, + comments, category: category.to_string(), expression, path: path.to_owned(), @@ -218,6 +225,8 @@ impl Parser<'_> { bail!(self, "expected indentation on next line"); } ExpressionKind::Break(space.len()) + } else if next == b'/' { + self.parse_comment()? } else if next == b'`' { self.parse_terminal()? } else if next == b'[' { @@ -269,6 +278,13 @@ impl Parser<'_> { Ok(term) } + /// Parse e.g. `// Single line comment.`. + fn parse_comment(&mut self) -> Result { + self.expect("//", "expected `//`")?; + let text = self.take_while(&|x| x != '\n').to_string(); + Ok(ExpressionKind::Comment(text)) + } + fn parse_charset(&mut self) -> Result { self.expect("[", "expected opening [")?; let mut characters = Vec::new(); diff --git a/mdbook-spec/src/grammar/render_markdown.rs b/mdbook-spec/src/grammar/render_markdown.rs index 1aacc9e86a..e119044601 100644 --- a/mdbook-spec/src/grammar/render_markdown.rs +++ b/mdbook-spec/src/grammar/render_markdown.rs @@ -45,6 +45,9 @@ impl Production { .get(&self.name) .map(|path| path.to_string()) .unwrap_or_else(|| format!("missing")); + for expr in &self.comments { + expr.render_markdown(cx, output); + } write!( output, " &self.kind, @@ -163,6 +167,9 @@ impl Expression { output.push_str("\\\n"); output.push_str(&" ".repeat(*indent)); } + ExpressionKind::Comment(s) => { + write!(output, "// {s}").unwrap(); + } ExpressionKind::Charset(set) => charset_render_markdown(cx, set, output), ExpressionKind::NegExpression(e) => { output.push('~'); diff --git a/mdbook-spec/src/grammar/render_railroad.rs b/mdbook-spec/src/grammar/render_railroad.rs index 93d68a589c..b31684f7df 100644 --- a/mdbook-spec/src/grammar/render_railroad.rs +++ b/mdbook-spec/src/grammar/render_railroad.rs @@ -102,8 +102,11 @@ impl Expression { .map(|e| e.render_railroad(cx, stack)) .filter_map(|n| n) .collect(); + if seq.is_empty() { + return None; + } let seq: Sequence> = Sequence::new(seq); - Box::new(seq) + Some(Box::new(seq)) }; // If `stack` is true, split the sequence on Breaks and @@ -127,16 +130,18 @@ impl Expression { &es[..] }; - let mut breaks: Vec<_> = - es.split(|e| e.is_break()).map(|es| make_seq(es)).collect(); + let mut breaks: Vec<_> = es + .split(|e| e.is_break()) + .flat_map(|es| make_seq(es)) + .collect(); // If there aren't any breaks, don't bother stacking. - if breaks.len() == 1 { - breaks.pop().unwrap() - } else { - Box::new(Stack::new(breaks)) + match breaks.len() { + 0 => return None, + 1 => breaks.pop().unwrap(), + _ => Box::new(Stack::new(breaks)), } } else { - make_seq(&es) + make_seq(&es)? } } // Treat `e?` and `e{..1}` / `e{0..1}` equally. @@ -205,6 +210,7 @@ impl Expression { ExpressionKind::Terminal(t) => Box::new(Terminal::new(t.clone())), ExpressionKind::Prose(s) => Box::new(Terminal::new(s.clone())), ExpressionKind::Break(_) => return None, + ExpressionKind::Comment(_) => return None, ExpressionKind::Charset(set) => { let ns: Vec<_> = set.iter().map(|c| c.render_railroad(cx)).collect(); Box::new(Choice::>::new(ns)) diff --git a/src/notation.md b/src/notation.md index b4954d42c9..e88679220c 100644 --- a/src/notation.md +++ b/src/notation.md @@ -27,6 +27,7 @@ The following notations are used by the *Lexer* and *Syntax* grammar snippets: | U+xxxx | U+0060 | A single unicode character | | \ | \ | An English description of what should be matched | | Rule suffix | IDENTIFIER_OR_KEYWORD _except `crate`_ | A modification to the previous rule | +| // Comment. | // Single line comment. | A comment extending to the end of the line. | Sequences have a higher precedence than `|` alternation. diff --git a/theme/reference.css b/theme/reference.css index 727c800044..3e16b4cd9b 100644 --- a/theme/reference.css +++ b/theme/reference.css @@ -1,5 +1,60 @@ /* Custom CSS for the Rust Specification. */ +/* Per-theme variables. */ +:root { + --railroad-background-color: hsl(30, 20%, 95%); + --railroad-background-image: + linear-gradient(to right, rgba(30, 30, 30, .05) 1px, transparent 1px), + linear-gradient(to bottom, rgba(30, 30, 30, .05) 1px, transparent 1px); + --railroad-path-stroke: black; + --railroad-rect-stroke: black; + --railroad-rect-fill: hsl(-290, 70%, 90%); +} +.light { + --alert-note-color: #0969da; + --alert-warning-color: #9a6700; + --alert-edition-color: #1a7f37; + --alert-example-color: #8250df; + --gramar-literal-bg: #fafafa; +} +.rust { + --alert-note-color: #023b95; + --alert-warning-color: #603700; + --alert-edition-color: #008200; + --alert-example-color: #8250df; + --grammar-literal-bg: #dedede; +} +.light, .rust { + --grammar-comment-color: lch(from var(--quote-bg) calc(l - 50) 0 0); + --inline-code-color: var(--grammar-comment-color); +} +.coal, .navy { + --alert-note-color: #4493f8; + --alert-warning-color: #d29922; + --alert-edition-color: #3fb950; + --alert-example-color: #ab7df8; + --grammar-literal-bg: #1d1f21; +} +.ayu { + --alert-note-color: #74b9ff; + --alert-warning-color: #f0b72f; + --alert-edition-color: #2bd853; + --alert-example-color: #d3abff; + --gramar-literal-bg: #191f26; +} +.coal, .navy, .ayu { + --grammar-comment-color: lch(from var(--quote-bg) calc(l + 50) 0 0); + --inline-code-color: var(--grammar-comment-color); + --railroad-background-color: hsl(230, 10%, 20%); + --railroad-background-image: + linear-gradient(to right, rgba(150, 150, 150, .05) 1px, transparent 1px), + linear-gradient(to bottom, rgba(150, 150, 150, .05) 1px, transparent 1px); + --railroad-path-stroke: hsl(200, 10%, 60%); + --railroad-text-fill: hsl(230, 30%, 80%); + --railroad-rect-stroke: hsl(200, 10%, 50%); + --railroad-rect-fill: hsl(230, 20%, 20%); +} + /* .parenthetical class used to keep e.g. "less-than symbol (<)" from wrapping the end parenthesis onto its own line. Use in a span between the last word and @@ -58,31 +113,6 @@ See mdbook-spec/src/admonitions.rs. margin-right: 8px; } -.light .alert { - --alert-note-color: #0969da; - --alert-warning-color: #9a6700; - --alert-edition-color: #1a7f37; - --alert-example-color: #8250df; -} -.ayu .alert { - --alert-note-color: #74b9ff; - --alert-warning-color: #f0b72f; - --alert-edition-color: #2bd853; - --alert-example-color: #d3abff; -} -.rust .alert { - --alert-note-color: #023b95; - --alert-warning-color: #603700; - --alert-edition-color: #008200; - --alert-example-color: #8250df; -} -.coal .alert, -.navy .alert { - --alert-note-color: #4493f8; - --alert-warning-color: #d29922; - --alert-edition-color: #3fb950; - --alert-example-color: #ab7df8; -} .alert-note blockquote { border-inline-start-color: var(--alert-note-color); } @@ -441,11 +471,11 @@ main > .rule { main > .rule > a.rule-link::before { content: "[*]"; - } - + } + .test-link > a::before { content: "[T]"; - } + } } /* Align rules to various siblings */ @@ -470,7 +500,7 @@ main > .rule { 2.5em * var(--h2-em-mult) - 16px /* half of the font size difference */ + (1em * var(--h2-em-mult) - 1em) / 2 - ) + ); } .rule:has(+ h3, + .tests-popup + h3) { /* multiplying by this turns h3's em into .rule's em*/ @@ -484,7 +514,7 @@ main > .rule { 2.5em * var(--h3-em-mult) - 16px /* half of the font size difference */ + (1em * var(--h3-em-mult) - 1em) / 2 - ) + ); } .rule:has(+ h4, + .tests-popup + h4) { @@ -499,7 +529,7 @@ main > .rule { 2em * var(--h4-em-mult) - 16px /* half of the font size difference */ + (1em * var(--h4-em-mult) - 1em) / 2 - ) + ); } .rule:has(+ h5, + .tests-popup + h5) { @@ -514,7 +544,7 @@ main > .rule { 2em * var(--h5-em-mult) - 16px /* half of the font size difference */ + (1em * var(--h5-em-mult) - 1em) / 2 - ) + ); } .rule:has(+ h6, + .tests-popup + h6) { @@ -529,7 +559,7 @@ main > .rule { 2em * var(--h6-em-mult) - 16px /* half of the font size difference */ + (1em * var(--h6-em-mult) - 1em) / 2 - ) + ); } /* Sets the color for [!HISTORY] blockquote admonitions. */ @@ -585,6 +615,15 @@ main > .rule { font-family: "Open Sans", sans-serif; } +/* Comments inside the grammar. */ +.grammar-comment { + font-family: "Open Sans", sans-serif; + color: var(--grammar-comment-color); +} +.grammar-comment code.hljs { + background: var(--grammar-literal-bg); +} + /* Places a box around literals to differentiate from other grammar punctuation like | and ( . */ .grammar-literal { font-family: var(--mono-font); @@ -596,20 +635,8 @@ main > .rule { color: var(--inline-code-color); } -.light .grammar-literal { - background-color: #fafafa -} -.rust .grammar-literal { - background-color: #dedede -} -.coal .grammar-literal { - background-color: #1d1f21; -} -.navy .grammar-literal { - background-color: #1d1f21; -} -.ayu .grammar-literal { - background-color: #191f26 +.grammar-literal { + background-color: var(--grammar-literal-bg); } .grammar-production:target, .railroad-production:target { @@ -653,25 +680,6 @@ main > .rule { display: none; } -:root { - --railroad-background-color: hsl(30, 20%, 95%); - --railroad-background-image: linear-gradient(to right, rgba(30, 30, 30, .05) 1px, transparent 1px), - linear-gradient(to bottom, rgba(30, 30, 30, .05) 1px, transparent 1px); - --railroad-path-stroke: black; - --railroad-rect-stroke: black; - --railroad-rect-fill: hsl(-290, 70%, 90%); -} - -.coal, .navy, .ayu { - --railroad-background-color: hsl(230, 10%, 20%); - --railroad-background-image: linear-gradient(to right, rgba(150, 150, 150, .05) 1px, transparent 1px), - linear-gradient(to bottom, rgba(150, 150, 150, .05) 1px, transparent 1px); - --railroad-path-stroke: hsl(200, 10%, 60%); - --railroad-text-fill: hsl(230, 30%, 80%); - --railroad-rect-stroke: hsl(200, 10%, 50%); - --railroad-rect-fill: hsl(230, 20%, 20%); -} - svg.railroad { background-color: var(--railroad-background-color); background-size: 15px 15px;