No hashtag before else anymore

This commit is contained in:
Laurenz 2021-09-30 18:28:25 +02:00
parent 989d344d3d
commit 9e95502622
6 changed files with 28 additions and 38 deletions

View File

@ -654,12 +654,7 @@ fn if_expr(p: &mut Parser) -> Option<Expr> {
if let Some(condition) = expr(p) { if let Some(condition) = expr(p) {
if let Some(if_body) = body(p) { if let Some(if_body) = body(p) {
let mut else_body = None; let mut else_body = None;
if p.eat_if(Token::Else) {
// We are in code mode but still want to react to `#else` if the
// outer mode is markup.
if (p.outer_mode() == TokenMode::Code || p.eat_if(Token::Invalid("#")))
&& p.eat_if(Token::Else)
{
else_body = body(p); else_body = body(p);
} }

View File

@ -28,15 +28,16 @@ pub struct Parser<'s> {
/// A logical group of tokens, e.g. `[...]`. /// A logical group of tokens, e.g. `[...]`.
struct GroupEntry { struct GroupEntry {
/// The start index of the group. Used by `Parser::end_group` to return the
/// group's full span.
pub start: usize,
/// The kind of group this is. This decides which tokens will end the group. /// The kind of group this is. This decides which tokens will end the group.
/// For example, a [`Group::Paren`] will be ended by /// For example, a [`Group::Paren`] will be ended by
/// [`Token::RightParen`]. /// [`Token::RightParen`].
pub kind: Group, pub kind: Group,
/// The mode the parser was in _before_ the group started. /// The start index of the group. Used by `Parser::end_group` to return the
pub outer_mode: TokenMode, /// group's full span.
pub start: usize,
/// The mode the parser was in _before_ the group started (to which we go
/// back once the group ends).
pub prev_mode: TokenMode,
} }
/// A group, confined by optional start and end delimiters. /// A group, confined by optional start and end delimiters.
@ -232,9 +233,9 @@ impl<'s> Parser<'s> {
/// This panics if the next token does not start the given group. /// This panics if the next token does not start the given group.
pub fn start_group(&mut self, kind: Group, mode: TokenMode) { pub fn start_group(&mut self, kind: Group, mode: TokenMode) {
self.groups.push(GroupEntry { self.groups.push(GroupEntry {
start: self.next_start(),
kind, kind,
outer_mode: self.tokens.mode(), start: self.next_start(),
prev_mode: self.tokens.mode(),
}); });
self.tokens.set_mode(mode); self.tokens.set_mode(mode);
@ -256,7 +257,7 @@ impl<'s> Parser<'s> {
pub fn end_group(&mut self) -> Span { pub fn end_group(&mut self) -> Span {
let prev_mode = self.tokens.mode(); let prev_mode = self.tokens.mode();
let group = self.groups.pop().expect("no started group"); let group = self.groups.pop().expect("no started group");
self.tokens.set_mode(group.outer_mode); self.tokens.set_mode(group.prev_mode);
self.repeek(); self.repeek();
let mut rescan = self.tokens.mode() != prev_mode; let mut rescan = self.tokens.mode() != prev_mode;
@ -291,17 +292,6 @@ impl<'s> Parser<'s> {
Span::new(self.id(), group.start, self.prev_end()) Span::new(self.id(), group.start, self.prev_end())
} }
/// The tokenization mode outside of the current group.
///
/// For example, this would be [`Markup`] if we are in a [`Code`] group that
/// is embedded in a [`Markup`] group.
///
/// [`Markup`]: TokenMode::Markup
/// [`Code`]: TokenMode::Code
pub fn outer_mode(&mut self) -> TokenMode {
self.groups.last().map_or(TokenMode::Markup, |group| group.outer_mode)
}
/// Add an error with location and message. /// Add an error with location and message.
pub fn error(&mut self, span: impl IntoSpan, message: impl Into<String>) { pub fn error(&mut self, span: impl IntoSpan, message: impl Into<String>) {
self.errors.push(Error::new(span.into_span(self.id()), message)); self.errors.push(Error::new(span.into_span(self.id()), message));
@ -380,8 +370,10 @@ impl<'s> Parser<'s> {
/// Whether the active group ends at a newline. /// Whether the active group ends at a newline.
fn stop_at_newline(&self) -> bool { fn stop_at_newline(&self) -> bool {
let active = self.groups.last().map(|group| group.kind); matches!(
matches!(active, Some(Group::Stmt | Group::Expr | Group::Imports)) self.groups.last().map(|group| group.kind),
Some(Group::Stmt | Group::Expr | Group::Imports)
)
} }
/// Whether we are inside the given group. /// Whether we are inside the given group.

View File

@ -419,7 +419,6 @@ impl Pretty for IfExpr {
p.push(' '); p.push(' ');
self.if_body.pretty(p); self.if_body.pretty(p);
if let Some(expr) = &self.else_body { if let Some(expr) = &self.else_body {
// FIXME: Hashtag in markup.
p.push_str(" else "); p.push_str(" else ");
expr.pretty(p); expr.pretty(p);
} }
@ -603,7 +602,7 @@ mod tests {
// Control flow. // Control flow.
roundtrip("#let x = 1 + 2"); roundtrip("#let x = 1 + 2");
test_parse("#let f(x) = y", "#let f = (x) => y"); test_parse("#let f(x) = y", "#let f = (x) => y");
test_parse("#if x [y] #else [z]", "#if x [y] else [z]"); roundtrip("#if x [y] else [z]");
roundtrip("#while x {y}"); roundtrip("#while x {y}");
roundtrip("#for x in y {z}"); roundtrip("#for x in y {z}");
roundtrip("#for k, x in y {z}"); roundtrip("#for k, x in y {z}");

View File

@ -27,14 +27,14 @@
== 1 == 1
) [ ) [
Nope. Nope.
] #else { ] else {
"Three." "Three."
} }
// Multiline. // Multiline.
#if false [ #if false [
Bad. Bad.
] #else { ] else {
let point = "." let point = "."
"Four" + point "Four" + point
} }
@ -71,7 +71,7 @@
// Condition must be boolean. // Condition must be boolean.
// If it isn't, neither branch is evaluated. // If it isn't, neither branch is evaluated.
// Error: 5-14 expected boolean, found string // Error: 5-14 expected boolean, found string
#if "a" + "b" { nope } #else { nope } #if "a" + "b" { nope } else { nope }
--- ---
// Make sure that we don't complain twice. // Make sure that we don't complain twice.
@ -101,5 +101,9 @@ x {}
#if x something #if x something
// Should output `A thing.` // Should output `A thing.`
// Error: 20 expected body // Error: 19 expected body
A#if false {} #else thing A#if false {} else thing
#if a []else [b]
#if a [] else [b]
#if a {} else [b]

View File

@ -12,8 +12,8 @@ A#if true [B]C \
A#if true [B] C \ A#if true [B] C \
A #if true{"B"}C \ A #if true{"B"}C \
A #if true{"B"} C \ A #if true{"B"} C \
A#if false [] #else [B]C \ A#if false [] else [B]C \
A#if true [B] #else [] C A#if true [B] else [] C
--- ---
// Spacing around while loop. // Spacing around while loop.

View File

@ -133,11 +133,11 @@
"patterns": [{ "include": "#code" }] "patterns": [{ "include": "#code" }]
}, },
{ {
"begin": "(#)(if|else)\\b", "begin": "((#)if|(?<=(}|])\\s*)else)\\b",
"end": "\n|(?=])|(?<=}|])", "end": "\n|(?=])|(?<=}|])",
"beginCaptures": { "beginCaptures": {
"0": { "name": "keyword.control.conditional.typst" }, "0": { "name": "keyword.control.conditional.typst" },
"1": { "name": "punctuation.definition.keyword.typst" } "2": { "name": "punctuation.definition.keyword.typst" }
}, },
"patterns": [{ "include": "#code" }] "patterns": [{ "include": "#code" }]
}, },