diff options
Diffstat (limited to 'src/parse')
| -rw-r--r-- | src/parse/lines.rs | 4 | ||||
| -rw-r--r-- | src/parse/mod.rs | 56 | ||||
| -rw-r--r-- | src/parse/parser.rs | 64 |
3 files changed, 67 insertions, 57 deletions
diff --git a/src/parse/lines.rs b/src/parse/lines.rs index e42d110b..2d97a25c 100644 --- a/src/parse/lines.rs +++ b/src/parse/lines.rs @@ -77,8 +77,8 @@ impl<'s> LineMap<'s> { } } -/// Determine the column at the end of the string. -pub fn search_column(src: &str) -> usize { +/// Count how many column the string would fill. +pub fn count_columns(src: &str) -> usize { let mut column = 0; for c in src.chars().rev() { if is_newline(c) { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index a94345a8..70c23442 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -14,14 +14,21 @@ pub use tokens::*; use std::rc::Rc; -use crate::diag::Pass; +use crate::diag::TypResult; +use crate::loading::FileId; use crate::syntax::*; use crate::util::EcoString; /// Parse a string of source code. -pub fn parse(src: &str) -> Pass<SyntaxTree> { - let mut p = Parser::new(src); - Pass::new(tree(&mut p), p.diags) +pub fn parse(file: FileId, src: &str) -> TypResult<SyntaxTree> { + let mut p = Parser::new(file, src); + let tree = tree(&mut p); + let errors = p.finish(); + if errors.is_empty() { + Ok(tree) + } else { + Err(Box::new(errors)) + } } /// Parse a syntax tree. @@ -126,7 +133,7 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<SyntaxNode> { p.start_group(group, TokenMode::Code); let expr = expr_with(p, true, 0); if stmt && expr.is_some() && !p.eof() { - p.expected_at("semicolon or line break", p.prev_end()); + p.expected_at(p.prev_end(), "semicolon or line break"); } p.end_group(); @@ -160,12 +167,12 @@ fn unicode_escape(p: &mut Parser, token: UnicodeEscapeToken) -> EcoString { c.into() } else { // Print out the escape sequence verbatim if it is invalid. - p.diag(error!(span, "invalid unicode escape sequence")); + p.error(span, "invalid unicode escape sequence"); p.peek_src().into() }; if !token.terminated { - p.diag(error!(span.end, "expected closing brace")); + p.error(span.end, "expected closing brace"); } text @@ -176,7 +183,7 @@ fn raw(p: &mut Parser, token: RawToken) -> SyntaxNode { let span = p.peek_span(); let raw = resolve::resolve_raw(span, token.text, token.backticks); if !token.terminated { - p.diag(error!(p.peek_span().end, "expected backtick(s)")); + p.error(span.end, "expected backtick(s)"); } SyntaxNode::Raw(raw) } @@ -198,11 +205,7 @@ fn heading(p: &mut Parser) -> SyntaxNode { let body = tree_indented(p); - SyntaxNode::Heading(HeadingNode { - span: p.span(start), - level, - body: Rc::new(body), - }) + SyntaxNode::Heading(HeadingNode { span: p.span(start), level, body }) } /// Parse a single list item. @@ -356,7 +359,7 @@ fn literal(p: &mut Parser) -> Option<Expr> { Token::Fraction(p) => Expr::Fractional(span, p), Token::Str(token) => Expr::Str(span, { if !token.terminated { - p.expected_at("quote", p.peek_span().end); + p.expected_at(span.end, "quote"); } resolve::resolve_string(token.string) }), @@ -421,7 +424,7 @@ fn collection(p: &mut Parser) -> (Vec<CallArg>, bool) { items.push(arg); if let Some(pos) = missing_coma.take() { - p.expected_at("comma", pos); + p.expected_at(pos, "comma"); } if p.eof() { @@ -447,7 +450,7 @@ fn item(p: &mut Parser) -> Option<CallArg> { if let Expr::Ident(name) = first { Some(CallArg::Named(Named { name, expr: expr(p)? })) } else { - p.diag(error!(first.span(), "expected identifier")); + p.error(first.span(), "expected identifier"); expr(p); None } @@ -461,7 +464,7 @@ fn array(p: &mut Parser, items: Vec<CallArg>, span: Span) -> Expr { let items = items.into_iter().filter_map(|item| match item { CallArg::Pos(expr) => Some(expr), CallArg::Named(_) => { - p.diag(error!(item.span(), "expected expression, found named pair")); + p.error(item.span(), "expected expression, found named pair"); None } }); @@ -474,7 +477,7 @@ fn dict(p: &mut Parser, items: Vec<CallArg>, span: Span) -> Expr { let items = items.into_iter().filter_map(|item| match item { CallArg::Named(named) => Some(named), CallArg::Pos(_) => { - p.diag(error!(item.span(), "expected named pair, found expression")); + p.error(item.span(), "expected named pair, found expression"); None } }); @@ -488,7 +491,7 @@ fn idents(p: &mut Parser, items: Vec<CallArg>) -> Vec<Ident> { let items = items.into_iter().filter_map(|item| match item { CallArg::Pos(Expr::Ident(id)) => Some(id), _ => { - p.diag(error!(item.span(), "expected identifier")); + p.error(item.span(), "expected identifier"); None } }); @@ -512,7 +515,7 @@ fn block(p: &mut Parser, scoping: bool) -> Expr { if let Some(expr) = expr(p) { exprs.push(expr); if !p.eof() { - p.expected_at("semicolon or line break", p.prev_end()); + p.expected_at(p.prev_end(), "semicolon or line break"); } } p.end_group(); @@ -529,10 +532,7 @@ fn call(p: &mut Parser, callee: Expr) -> Option<Expr> { let mut wide = p.eat_if(Token::Excl); if wide && p.outer_mode() == TokenMode::Code { let span = p.span(callee.span().start); - p.diag(error!( - span, - "wide calls are only allowed directly in templates", - )); + p.error(span, "wide calls are only allowed directly in templates"); wide = false; } @@ -548,7 +548,7 @@ fn call(p: &mut Parser, callee: Expr) -> Option<Expr> { items: vec![], }, _ => { - p.expected_at("argument list", p.prev_end()); + p.expected_at(p.prev_end(), "argument list"); return None; } }; @@ -616,7 +616,7 @@ fn let_expr(p: &mut Parser) -> Option<Expr> { init = expr(p); } else if params.is_some() { // Function definitions must have a body. - p.expected_at("body", p.prev_end()); + p.expected_at(p.prev_end(), "body"); } // Rewrite into a closure expression if it's a function definition. @@ -738,7 +738,7 @@ fn import_expr(p: &mut Parser) -> Option<Expr> { p.start_group(Group::Imports, TokenMode::Code); let items = collection(p).0; if items.is_empty() { - p.expected_at("import items", p.prev_end()); + p.expected_at(p.prev_end(), "import items"); } p.end_group(); Imports::Idents(idents(p, items)) @@ -790,7 +790,7 @@ fn body(p: &mut Parser) -> Option<Expr> { Some(Token::LeftBracket) => Some(template(p)), Some(Token::LeftBrace) => Some(block(p, true)), _ => { - p.expected_at("body", p.prev_end()); + p.expected_at(p.prev_end(), "body"); None } } diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 5fba961a..0238c8be 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -1,14 +1,17 @@ use std::fmt::{self, Debug, Formatter}; use std::ops::Range; -use super::{search_column, TokenMode, Tokens}; -use crate::diag::{Diag, DiagSet}; +use super::{count_columns, TokenMode, Tokens}; +use crate::diag::Error; +use crate::loading::FileId; use crate::syntax::{Pos, Span, Token}; /// A convenient token-based parser. pub struct Parser<'s> { - /// Parsing diagnostics. - pub diags: DiagSet, + /// The id of the parsed file. + file: FileId, + /// Parsing errors. + errors: Vec<Error>, /// An iterator over the source tokens. tokens: Tokens<'s>, /// The stack of open groups. @@ -57,11 +60,12 @@ pub enum Group { impl<'s> Parser<'s> { /// Create a new parser for the source string. - pub fn new(src: &'s str) -> Self { + pub fn new(file: FileId, src: &'s str) -> Self { let mut tokens = Tokens::new(src, TokenMode::Markup); let next = tokens.next(); Self { - diags: DiagSet::new(), + file, + errors: vec![], tokens, groups: vec![], next, @@ -71,39 +75,45 @@ impl<'s> Parser<'s> { } } - /// Add a diagnostic. - pub fn diag(&mut self, diag: Diag) { - self.diags.insert(diag); + /// Finish parsing and return all errors. + pub fn finish(self) -> Vec<Error> { + self.errors } - /// Eat the next token and add a diagnostic that it is not the expected - /// `thing`. + /// Add an error with location and message. + pub fn error(&mut self, span: impl Into<Span>, message: impl Into<String>) { + self.errors.push(Error { + file: self.file, + span: span.into(), + message: message.into(), + }); + } + + /// Eat the next token and add an error that it is not the expected `thing`. pub fn expected(&mut self, what: &str) { let before = self.next_start(); if let Some(found) = self.eat() { let after = self.prev_end(); - self.diag(error!( + self.error( before .. after, - "expected {}, found {}", - what, - found.name(), - )); + format!("expected {}, found {}", what, found.name()), + ); } else { - self.expected_at(what, self.next_start()); + self.expected_at(self.next_start(), what); } } - /// Add a diagnostic that `what` was expected at the given position. - pub fn expected_at(&mut self, what: &str, pos: impl Into<Pos>) { - self.diag(error!(pos.into(), "expected {}", what)); + /// Add an error that `what` was expected at the given position. + pub fn expected_at(&mut self, pos: impl Into<Pos>, what: &str) { + self.error(pos.into(), format!("expected {}", what)); } - /// Eat the next token and add a diagnostic that it is unexpected. + /// Eat the next token and add an error that it is unexpected. pub fn unexpected(&mut self) { let before = self.next_start(); if let Some(found) = self.eat() { let after = self.prev_end(); - self.diag(error!(before .. after, "unexpected {}", found.name())); + self.error(before .. after, format!("unexpected {}", found.name())); } } @@ -159,7 +169,7 @@ impl<'s> Parser<'s> { self.bump(); rescan = false; } else if required { - self.diag(error!(self.next_start(), "expected {}", end.name())); + self.error(self.next_start(), format!("expected {}", end.name())); } } @@ -276,12 +286,12 @@ impl<'s> Parser<'s> { Span::new(start, self.prev_end()) } - /// Consume the next token if it is the given one and produce a diagnostic - /// if not. + /// Consume the next token if it is the given one and produce an error if + /// not. pub fn expect(&mut self, t: Token) -> bool { let eaten = self.eat_if(t); if !eaten { - self.expected_at(t.name(), self.prev_end()); + self.expected_at(self.prev_end(), t.name()); } eaten } @@ -314,7 +324,7 @@ impl<'s> Parser<'s> { /// Determine the column for the given index in the source. pub fn column(&self, index: usize) -> usize { - search_column(self.tokens.scanner().get(.. index)) + count_columns(self.tokens.scanner().get(.. index)) } /// The span from `start` to [`self.prev_end()`](Self::prev_end). |
