diff options
Diffstat (limited to 'src/parse')
| -rw-r--r-- | src/parse/collection.rs | 4 | ||||
| -rw-r--r-- | src/parse/mod.rs | 96 | ||||
| -rw-r--r-- | src/parse/parser.rs | 102 | ||||
| -rw-r--r-- | src/parse/tokens.rs | 5 |
4 files changed, 131 insertions, 76 deletions
diff --git a/src/parse/collection.rs b/src/parse/collection.rs index 9addcef0..58fd91ae 100644 --- a/src/parse/collection.rs +++ b/src/parse/collection.rs @@ -11,7 +11,7 @@ pub fn arguments(p: &mut Parser) -> ExprArgs { /// - Dictionary literal /// - Parenthesized expression pub fn parenthesized(p: &mut Parser) -> Expr { - p.start_group(Group::Paren); + p.start_group(Group::Paren, TokenMode::Code); let state = if p.eat_if(Token::Colon) { collection(p, State::Dict(vec![])) } else { @@ -30,7 +30,7 @@ fn collection<T: Collection>(p: &mut Parser, mut collection: T) -> T { collection.push_arg(p, arg); if let Some(pos) = missing_coma.take() { - p.diag_expected_at("comma", pos); + p.expected_at("comma", pos); } if p.eof() { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 0a656366..622223fa 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -78,9 +78,8 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> { Token::UnicodeEscape(t) => Node::Text(unicode_escape(p, t)), // Keywords. - Token::Let => { - return Some(Node::Expr(expr_let(p)?)); - } + Token::Let => return Some(Node::Expr(stmt_let(p)?)), + Token::If => return Some(Node::Expr(expr_if(p)?)), // Comments. Token::LineComment(_) | Token::BlockComment(_) => { @@ -89,7 +88,7 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> { } _ => { - p.diag_unexpected(); + p.unexpected(); return None; } }; @@ -111,7 +110,7 @@ fn heading(p: &mut Parser) -> NodeHeading { }); if level.v > 5 { - p.diag(warning!(level.span, "section depth should not exceed 6")); + p.diag(warning!(level.span, "should not exceed depth 6")); level.v = 5; } @@ -155,8 +154,7 @@ fn unicode_escape(p: &mut Parser, token: TokenUnicodeEscape) -> String { /// Parse a bracketed function call. fn bracket_call(p: &mut Parser) -> Expr { - p.push_mode(TokenMode::Code); - p.start_group(Group::Bracket); + p.start_group(Group::Bracket, TokenMode::Code); // One header is guaranteed, but there may be more (through chaining). let mut outer = vec![]; @@ -167,7 +165,6 @@ fn bracket_call(p: &mut Parser) -> Expr { inner = p.span(bracket_subheader); } - p.pop_mode(); p.end_group(); if p.peek() == Some(Token::LeftBracket) { @@ -189,15 +186,15 @@ fn bracket_call(p: &mut Parser) -> Expr { /// Parse one subheader of a bracketed function call. fn bracket_subheader(p: &mut Parser) -> ExprCall { - p.start_group(Group::Subheader); + p.start_group(Group::Subheader, TokenMode::Code); let start = p.next_start(); let name = p.span_if(ident).unwrap_or_else(|| { let what = "function name"; if p.eof() { - p.diag_expected_at(what, start); + p.expected_at(what, start); } else { - p.diag_expected(what); + p.expected(what); } Ident(String::new()).with_span(start) }); @@ -210,23 +207,19 @@ fn bracket_subheader(p: &mut Parser) -> ExprCall { /// Parse the body of a bracketed function call. fn bracket_body(p: &mut Parser) -> Tree { - p.push_mode(TokenMode::Markup); - p.start_group(Group::Bracket); + p.start_group(Group::Bracket, TokenMode::Markup); let tree = tree(p); - p.pop_mode(); p.end_group(); tree } /// Parse a block expression: `{...}`. fn block(p: &mut Parser) -> Option<Expr> { - p.push_mode(TokenMode::Code); - p.start_group(Group::Brace); + p.start_group(Group::Brace, TokenMode::Code); let expr = p.span_if(expr); while !p.eof() { - p.diag_unexpected(); + p.unexpected(); } - p.pop_mode(); p.end_group(); Some(Expr::Block(Box::new(expr?))) } @@ -333,7 +326,7 @@ fn value(p: &mut Parser) -> Option<Expr> { // No value. _ => { - p.diag_expected("expression"); + p.expected("expression"); return None; } }; @@ -343,17 +336,15 @@ fn value(p: &mut Parser) -> Option<Expr> { // Parse a template value: `[...]`. fn template(p: &mut Parser) -> Expr { - p.push_mode(TokenMode::Markup); - p.start_group(Group::Bracket); + p.start_group(Group::Bracket, TokenMode::Markup); let tree = tree(p); - p.pop_mode(); p.end_group(); Expr::Template(tree) } /// Parse a parenthesized function call. fn paren_call(p: &mut Parser, name: Spanned<Ident>) -> Expr { - p.start_group(Group::Paren); + p.start_group(Group::Paren, TokenMode::Code); let args = p.span(arguments); p.end_group(); Expr::Call(ExprCall { name, args }) @@ -379,36 +370,71 @@ fn color(p: &mut Parser, hex: &str) -> RgbaColor { /// Parse a string. fn string(p: &mut Parser, token: TokenStr) -> String { if !token.terminated { - p.diag_expected_at("quote", p.peek_span().end); + p.expected_at("quote", p.peek_span().end); } resolve::resolve_string(token.string) } -/// Parse a let expresion. -fn expr_let(p: &mut Parser) -> Option<Expr> { - p.push_mode(TokenMode::Code); +/// Parse a let statement. +fn stmt_let(p: &mut Parser) -> Option<Expr> { + p.start_group(Group::Stmt, TokenMode::Code); p.eat_assert(Token::Let); - p.start_group(Group::Expr); let pat = p.span_if(ident); let mut rhs = None; if pat.is_some() { if p.eat_if(Token::Eq) { - if let Some(expr) = p.span_if(expr) { - rhs = Some(Box::new(expr)); - } + rhs = p.span_if(expr); } } else { - p.diag_expected("identifier"); + p.expected("identifier"); } - p.pop_mode(); if !p.eof() { - p.diag_expected("semicolon or line break"); + p.expected_at("semicolon or line break", p.last_end()); } p.end_group(); - pat.map(|pat| Expr::Let(ExprLet { pat, expr: rhs })) + + Some(Expr::Let(ExprLet { pat: pat?, expr: rhs.map(Box::new) })) +} + +/// Parse an if expresion. +fn expr_if(p: &mut Parser) -> Option<Expr> { + p.start_group(Group::Expr, TokenMode::Code); + p.eat_assert(Token::If); + let condition = p.span_if(expr); + p.end_group(); + + let condition = Box::new(condition?); + let if_body = Box::new(control_body(p)?); + let end = p.last_end(); + p.skip_white(); + + let else_body = if p.eat_if(Token::Else) { + control_body(p).map(Box::new) + } else { + p.jump(end); + None + }; + + Some(Expr::If(ExprIf { condition, if_body, else_body })) +} + +/// Parse a control flow body. +fn control_body(p: &mut Parser) -> Option<Spanned<Expr>> { + let start = p.last_end(); + p.skip_white(); + + match p.peek() { + Some(Token::LeftBracket) => Some(p.span(template)), + Some(Token::LeftBrace) => p.span_if(block), + _ => { + p.expected_at("body", start); + p.jump(start); + None + } + } } diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 8c27c8f7..f9ced34f 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -55,7 +55,7 @@ impl<'s> Parser<'s> { /// Eat the next token and add a diagnostic that it is not the expected /// `thing`. - pub fn diag_expected(&mut self, what: &str) { + pub fn expected(&mut self, what: &str) { let before = self.next_start; if let Some(found) = self.eat() { let after = self.last_end; @@ -66,17 +66,17 @@ impl<'s> Parser<'s> { found.name(), )); } else { - self.diag_expected_at(what, self.next_start); + self.expected_at(what, self.next_start); } } - /// Add a diagnostic that the `thing` was expected at the given position. - pub fn diag_expected_at(&mut self, what: &str, pos: Pos) { + /// Add a diagnostic that `what` was expected at the given position. + pub fn expected_at(&mut self, what: &str, pos: Pos) { self.diag(error!(pos, "expected {}", what)); } /// Eat the next token and add a diagnostic that it is unexpected. - pub fn diag_unexpected(&mut self) { + pub fn unexpected(&mut self) { let before = self.next_start; if let Some(found) = self.eat() { let after = self.last_end; @@ -89,21 +89,7 @@ impl<'s> Parser<'s> { self.feedback.decos.push(deco); } - /// Update the token mode and push the previous mode onto a stack. - pub fn push_mode(&mut self, mode: TokenMode) { - self.modes.push(self.tokens.mode()); - self.tokens.set_mode(mode); - } - - /// Pop the topmost token mode from the stack. - /// - /// # Panics - /// This panics if there is no mode on the stack. - pub fn pop_mode(&mut self) { - self.tokens.set_mode(self.modes.pop().expect("no pushed mode")); - } - - /// Continues parsing in a group. + /// Continue parsing in a group. /// /// When the end delimiter of the group is reached, all subsequent calls to /// `eat()` and `peek()` return `None`. Parsing can only continue with @@ -111,37 +97,55 @@ impl<'s> Parser<'s> { /// /// # Panics /// This panics if the next token does not start the given group. - pub fn start_group(&mut self, group: Group) { + pub fn start_group(&mut self, group: Group, mode: TokenMode) { + self.modes.push(self.tokens.mode()); + self.tokens.set_mode(mode); + self.groups.push(group); + self.repeek(); match group { Group::Paren => self.eat_assert(Token::LeftParen), Group::Bracket => self.eat_assert(Token::LeftBracket), Group::Brace => self.eat_assert(Token::LeftBrace), - Group::Expr => self.repeek(), - Group::Subheader => self.repeek(), + Group::Subheader => {} + Group::Stmt => {} + Group::Expr => {} } } - /// Ends the parsing of a group and returns the span of the whole group. + /// End the parsing of a group. /// /// # Panics /// This panics if no group was started. pub fn end_group(&mut self) { + let prev_mode = self.tokens.mode(); + self.tokens.set_mode(self.modes.pop().expect("no pushed mode")); + let group = self.groups.pop().expect("no started group"); self.repeek(); - let (end, required) = match group { - Group::Paren => (Token::RightParen, true), - Group::Bracket => (Token::RightBracket, true), - Group::Brace => (Token::RightBrace, true), - Group::Expr => (Token::Semicolon, false), - Group::Subheader => return, - }; + // Eat the end delimiter if there is one. + if let Some((end, required)) = match group { + Group::Paren => Some((Token::RightParen, true)), + Group::Bracket => Some((Token::RightBracket, true)), + Group::Brace => Some((Token::RightBrace, true)), + Group::Subheader => None, + Group::Stmt => Some((Token::Semicolon, false)), + Group::Expr => None, + } { + if self.next == Some(end) { + // Bump the delimeter and return. No need to rescan in this case. + self.bump(); + return; + } else if required { + self.diag(error!(self.next_start, "expected {}", end.name())); + } + } - if self.next == Some(end) { + // Rescan the peeked token if the mode changed. + if self.tokens.mode() != prev_mode { + self.tokens.jump(self.last_end); self.bump(); - } else if required { - self.diag(error!(self.next_start, "expected {}", end.name())); } } @@ -201,6 +205,18 @@ impl<'s> Parser<'s> { debug_assert_eq!(next, Some(t)); } + /// Skip whitespace and comment tokens. + pub fn skip_white(&mut self) { + while matches!( + self.peek(), + Some(Token::Space(_)) | + Some(Token::LineComment(_)) | + Some(Token::BlockComment(_)) + ) { + self.eat(); + } + } + /// Peek at the next token without consuming it. pub fn peek(&self) -> Option<Token<'s>> { self.peeked @@ -243,6 +259,12 @@ impl<'s> Parser<'s> { self.last_end } + /// Jump to a position in the source string. + pub fn jump(&mut self, pos: Pos) { + self.tokens.jump(pos); + self.bump(); + } + /// Slice a part out of the source string. pub fn get(&self, span: impl Into<Span>) -> &'s str { self.tokens.scanner().get(span.into().to_range()) @@ -265,7 +287,7 @@ impl<'s> Parser<'s> { TokenMode::Code => loop { match self.next { Some(Token::Space(n)) => { - if n >= 1 && self.groups.last() == Some(&Group::Expr) { + if n >= 1 && self.groups.last() == Some(&Group::Stmt) { break; } } @@ -293,8 +315,8 @@ impl<'s> Parser<'s> { Token::RightParen if self.groups.contains(&Group::Paren) => {} Token::RightBracket if self.groups.contains(&Group::Bracket) => {} Token::RightBrace if self.groups.contains(&Group::Brace) => {} - Token::Semicolon if self.groups.contains(&Group::Expr) => {} - Token::Space(n) if n >= 1 && self.groups.last() == Some(&Group::Expr) => {} + Token::Semicolon if self.groups.contains(&Group::Stmt) => {} + Token::Space(n) if n >= 1 && self.groups.last() == Some(&Group::Stmt) => {} Token::Pipe if self.groups.contains(&Group::Subheader) => {} _ => return, } @@ -319,9 +341,11 @@ pub enum Group { Bracket, /// A curly-braced group: `{...}`. Brace, - /// A group ended by a semicolon or a line break: `;`, `\n`. - Expr, /// A group ended by a chained subheader or a closing bracket: /// `... >>`, `...]`. Subheader, + /// A group ended by a semicolon or a line break: `;`, `\n`. + Stmt, + /// A group for a single expression. Not ended by something specific. + Expr, } diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index 68b31a87..312e941b 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -42,6 +42,11 @@ impl<'s> Tokens<'s> { self.s.index().into() } + /// Jump to the given position. + pub fn jump(&mut self, pos: Pos) { + self.s.jump(pos.to_usize()); + } + /// The underlying scanner. pub fn scanner(&self) -> &Scanner<'s> { &self.s |
