diff options
Diffstat (limited to 'src/parse/mod.rs')
| -rw-r--r-- | src/parse/mod.rs | 254 |
1 files changed, 110 insertions, 144 deletions
diff --git a/src/parse/mod.rs b/src/parse/mod.rs index bf522fc8..99cc9d10 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -33,10 +33,10 @@ fn tree(p: &mut Parser) -> SynTree { let mut tree = vec![]; while !p.eof() { if let Some(node) = p.span_if(|p| node(p, at_start)) { - if node.v == SynNode::Parbreak { - at_start = true; - } else if node.v != SynNode::Space { - at_start = false; + match node.v { + SynNode::Parbreak => at_start = true, + SynNode::Space => {} + _ => at_start = false, } tree.push(node); } @@ -46,9 +46,7 @@ fn tree(p: &mut Parser) -> SynTree { /// Parse a syntax node. fn node(p: &mut Parser, at_start: bool) -> Option<SynNode> { - let start = p.next_start(); - let node = match p.eat()? { - // Spaces. + let node = match p.peek()? { Token::Space(newlines) => { if newlines < 2 { SynNode::Space @@ -56,61 +54,59 @@ fn node(p: &mut Parser, at_start: bool) -> Option<SynNode> { SynNode::Parbreak } } - - // Text. Token::Text(text) => SynNode::Text(text.into()), - // Comments. - Token::LineComment(_) | Token::BlockComment(_) => return None, + Token::LineComment(_) | Token::BlockComment(_) => { + p.eat(); + return None; + } - // Markup. Token::Star => SynNode::Strong, Token::Underscore => SynNode::Emph, + Token::Tilde => SynNode::Text("\u{00A0}".into()), + Token::Backslash => SynNode::Linebreak, Token::Hashtag => { if at_start { - SynNode::Heading(heading(p, start)) + return Some(SynNode::Heading(heading(p))); } else { - SynNode::Text(p.eaten_from(start).into()) + SynNode::Text(p.get(p.peek_span()).into()) } } - Token::Tilde => SynNode::Text("\u{00A0}".into()), - Token::Backslash => SynNode::Linebreak, - Token::UnicodeEscape(token) => SynNode::Text(unicode_escape(p, token, start)), - Token::Raw(token) => SynNode::Raw(raw(p, token)), + Token::Raw(t) => SynNode::Raw(raw(p, t)), + Token::UnicodeEscape(t) => SynNode::Text(unicode_escape(p, t)), - // Functions. Token::LeftBracket => { - p.jump(start); - SynNode::Expr(Expr::Call(bracket_call(p))) + return Some(SynNode::Expr(Expr::Call(bracket_call(p)))); } - // Blocks. Token::LeftBrace => { - p.jump(start); - SynNode::Expr(block_expr(p)?) + return Some(SynNode::Expr(block_expr(p)?)); } - // Bad tokens. _ => { - p.jump(start); p.diag_unexpected(); return None; } }; + p.eat(); Some(node) } /// Parse a heading. -fn heading(p: &mut Parser, start: Pos) -> NodeHeading { - // Parse the section depth. - let mut level = 0u8; - while p.eat_if(Token::Hashtag) { - level = level.saturating_add(1); - } +fn heading(p: &mut Parser) -> NodeHeading { + // Count hashtags. + let mut level = p.span(|p| { + p.eat_assert(Token::Hashtag); + + let mut level = 0u8; + while p.eat_if(Token::Hashtag) { + level = level.saturating_add(1); + } + level + }); - let mut level = level.span_with(start .. p.last_end()); if level.v > 5 { - p.diag(warning!(level.span, "section depth should be at most 6")); + p.diag(warning!(level.span, "section depth should not exceed 6")); level.v = 5; } @@ -125,25 +121,23 @@ fn heading(p: &mut Parser, start: Pos) -> NodeHeading { NodeHeading { level, contents } } -/// Parse a raw block. +/// Handle a raw block. fn raw(p: &mut Parser, token: TokenRaw) -> NodeRaw { + let span = p.peek_span(); let raw = resolve::resolve_raw(token.text, token.backticks); - if !token.terminated { - p.diag(error!(p.last_end(), "expected backtick(s)")); + p.diag(error!(span.end, "expected backtick(s)")); } - raw } -/// Parse a unicode escape sequence. -fn unicode_escape(p: &mut Parser, token: TokenUnicodeEscape, start: Pos) -> String { - let span = Span::new(start, p.last_end()); +/// Handle a unicode escape sequence. +fn unicode_escape(p: &mut Parser, token: TokenUnicodeEscape) -> String { + let span = p.peek_span(); let text = if let Some(c) = resolve::resolve_hex(token.sequence) { c.to_string() } else { - // Print out the escape sequence verbatim if it is - // invalid. + // Print out the escape sequence verbatim if it is invalid. p.diag(error!(span, "invalid unicode escape sequence")); p.get(span).into() }; @@ -155,6 +149,24 @@ fn unicode_escape(p: &mut Parser, token: TokenUnicodeEscape, start: Pos) -> Stri text } +/// Parse a block expression. +fn block_expr(p: &mut Parser) -> Option<Expr> { + p.push_mode(TokenMode::Header); + p.start_group(Group::Brace); + let expr = expr(p); + p.pop_mode(); + p.end_group(); + expr +} + +/// Parse a parenthesized function call. +fn paren_call(p: &mut Parser, name: Spanned<Ident>) -> ExprCall { + p.start_group(Group::Paren); + let args = p.span(|p| dict_contents(p).0); + p.end_group(); + ExprCall { name, args } +} + /// Parse a bracketed function call. fn bracket_call(p: &mut Parser) -> ExprCall { p.push_mode(TokenMode::Header); @@ -180,7 +192,7 @@ fn bracket_call(p: &mut Parser) -> ExprCall { while let Some(mut top) = outer.pop() { let span = inner.span; - let node = inner.map(Expr::Call).map(SynNode::Expr); + let node = inner.map(|c| SynNode::Expr(Expr::Call(c))); let expr = Expr::Lit(Lit::Content(vec![node])).span_with(span); top.v.args.v.0.push(LitDictEntry { key: None, expr }); inner = top; @@ -220,64 +232,35 @@ fn bracket_body(p: &mut Parser) -> SynTree { tree } -/// Parse a parenthesized function call. -fn paren_call(p: &mut Parser, name: Spanned<Ident>) -> ExprCall { - p.start_group(Group::Paren); - let args = p.span(|p| dict_contents(p).0); - p.end_group(); - ExprCall { name, args } -} - -/// Parse a block expression. -fn block_expr(p: &mut Parser) -> Option<Expr> { - p.push_mode(TokenMode::Header); - p.start_group(Group::Brace); - let expr = expr(p); - p.pop_mode(); - p.end_group(); - expr -} - /// Parse the contents of a dictionary. fn dict_contents(p: &mut Parser) -> (LitDict, bool) { let mut dict = LitDict::new(); + let mut missing_coma = None; let mut comma_and_keyless = true; - let mut expected_comma = None; - - loop { - if p.eof() { - break; - } - - let entry = if let Some(entry) = dict_entry(p) { - entry - } else { - expected_comma = None; - p.diag_unexpected(); - continue; - }; - - if let Some(pos) = expected_comma.take() { - p.diag_expected_at("comma", pos); - } - if let Some(key) = &entry.key { - comma_and_keyless = false; - p.deco(Deco::DictKey.span_with(key.span)); - } + while !p.eof() { + if let Some(entry) = dict_entry(p) { + let behind = entry.expr.span.end; + if let Some(pos) = missing_coma.take() { + p.diag_expected_at("comma", pos); + } - let behind = entry.expr.span.end; - dict.0.push(entry); + if let Some(key) = &entry.key { + comma_and_keyless = false; + p.deco(Deco::DictKey.span_with(key.span)); + } - if p.eof() { - break; - } + dict.0.push(entry); + if p.eof() { + break; + } - if !p.eat_if(Token::Comma) { - expected_comma = Some(behind); + if p.eat_if(Token::Comma) { + comma_and_keyless = false; + } else { + missing_coma = Some(behind); + } } - - comma_and_keyless = false; } let coercible = comma_and_keyless && !dict.0.is_empty(); @@ -291,15 +274,10 @@ fn dict_entry(p: &mut Parser) -> Option<LitDictEntry> { // Key-value pair. Some(Token::Colon) => { p.eat_assert(Token::Colon); - if let Some(expr) = p.span_if(expr) { - Some(LitDictEntry { - key: Some(ident.map(|id| DictKey::Str(id.0))), - expr, - }) - } else { - p.diag_expected("value"); - None - } + p.span_if(expr).map(|expr| LitDictEntry { + key: Some(ident.map(|id| DictKey::Str(id.0))), + expr, + }) } // Function call. @@ -318,16 +296,14 @@ fn dict_entry(p: &mut Parser) -> Option<LitDictEntry> { expr: ident.map(|id| Expr::Lit(Lit::Ident(id))), }), } - } else if let Some(expr) = p.span_if(expr) { - Some(LitDictEntry { key: None, expr }) } else { - None + p.span_if(expr).map(|expr| LitDictEntry { key: None, expr }) } } /// Parse an expression: `term (+ term)*`. fn expr(p: &mut Parser) -> Option<Expr> { - binops(p, "summand", term, |token| match token { + binops(p, term, |token| match token { Token::Plus => Some(BinOp::Add), Token::Hyphen => Some(BinOp::Sub), _ => None, @@ -336,7 +312,7 @@ fn expr(p: &mut Parser) -> Option<Expr> { /// Parse a term: `factor (* factor)*`. fn term(p: &mut Parser) -> Option<Expr> { - binops(p, "factor", factor, |token| match token { + binops(p, factor, |token| match token { Token::Star => Some(BinOp::Mul), Token::Slash => Some(BinOp::Div), _ => None, @@ -346,27 +322,20 @@ fn term(p: &mut Parser) -> Option<Expr> { /// Parse binary operations of the from `a (<op> b)*`. fn binops( p: &mut Parser, - operand_name: &str, operand: fn(&mut Parser) -> Option<Expr>, op: fn(Token) -> Option<BinOp>, ) -> Option<Expr> { let mut lhs = p.span_if(operand)?; - loop { - if let Some(op) = p.span_if(|p| p.eat_map(op)) { - if let Some(rhs) = p.span_if(operand) { - let span = lhs.span.join(rhs.span); - let expr = Expr::Binary(ExprBinary { - lhs: Box::new(lhs), - op, - rhs: Box::new(rhs), - }); - lhs = expr.span_with(span); - } else { - let span = lhs.span.join(op.span); - p.diag(error!(span, "missing right {}", operand_name)); - break; - } + while let Some(op) = p.span_if(|p| p.eat_map(op)) { + if let Some(rhs) = p.span_if(operand) { + let span = lhs.span.join(rhs.span); + let expr = Expr::Binary(ExprBinary { + lhs: Box::new(lhs), + op, + rhs: Box::new(rhs), + }); + lhs = expr.span_with(span); } else { break; } @@ -383,12 +352,8 @@ fn factor(p: &mut Parser) -> Option<Expr> { }; if let Some(op) = p.span_if(|p| p.eat_map(op)) { - if let Some(expr) = p.span_if(factor) { - Some(Expr::Unary(ExprUnary { op, expr: Box::new(expr) })) - } else { - p.diag(error!(op.span, "missing factor")); - None - } + p.span_if(factor) + .map(|expr| Expr::Unary(ExprUnary { op, expr: Box::new(expr) })) } else { value(p) } @@ -397,28 +362,28 @@ fn factor(p: &mut Parser) -> Option<Expr> { /// Parse a value. fn value(p: &mut Parser) -> Option<Expr> { let start = p.next_start(); - Some(match p.eat()? { + Some(match p.eat() { // Bracketed function call. - Token::LeftBracket => { + Some(Token::LeftBracket) => { p.jump(start); let node = p.span(|p| SynNode::Expr(Expr::Call(bracket_call(p)))); Expr::Lit(Lit::Content(vec![node])) } // Content expression. - Token::LeftBrace => { + Some(Token::LeftBrace) => { p.jump(start); Expr::Lit(Lit::Content(content(p))) } // Dictionary or just a parenthesized expression. - Token::LeftParen => { + Some(Token::LeftParen) => { p.jump(start); parenthesized(p) } // Function or just ident. - Token::Ident(id) => { + Some(Token::Ident(id)) => { let ident = Ident(id.into()); let after = p.last_end(); if p.peek() == Some(Token::LeftParen) { @@ -429,24 +394,25 @@ fn value(p: &mut Parser) -> Option<Expr> { } } - // Atomic values. - Token::Bool(b) => Expr::Lit(Lit::Bool(b)), - Token::Int(i) => Expr::Lit(Lit::Int(i)), - Token::Float(f) => Expr::Lit(Lit::Float(f)), - Token::Length(val, unit) => Expr::Lit(Lit::Length(val, unit)), - Token::Percent(p) => Expr::Lit(Lit::Percent(p)), - Token::Hex(hex) => Expr::Lit(Lit::Color(color(p, hex, start))), - Token::Str(token) => Expr::Lit(Lit::Str(string(p, token))), + // Basic values. + Some(Token::Bool(b)) => Expr::Lit(Lit::Bool(b)), + Some(Token::Int(i)) => Expr::Lit(Lit::Int(i)), + Some(Token::Float(f)) => Expr::Lit(Lit::Float(f)), + Some(Token::Length(val, unit)) => Expr::Lit(Lit::Length(val, unit)), + Some(Token::Percent(p)) => Expr::Lit(Lit::Percent(p)), + Some(Token::Hex(hex)) => Expr::Lit(Lit::Color(color(p, hex, start))), + Some(Token::Str(token)) => Expr::Lit(Lit::Str(str(p, token))), // No value. _ => { p.jump(start); + p.diag_expected("expression"); return None; } }) } -// Parse a content expression: `{...}`. +// Parse a content value: `{...}`. fn content(p: &mut Parser) -> SynTree { p.push_mode(TokenMode::Body); p.start_group(Group::Brace); @@ -487,7 +453,7 @@ fn color(p: &mut Parser, hex: &str, start: Pos) -> RgbaColor { } /// Parse a string. -fn string(p: &mut Parser, token: TokenStr) -> String { +fn str(p: &mut Parser, token: TokenStr) -> String { if !token.terminated { p.diag_expected_at("quote", p.last_end()); } |
