summaryrefslogtreecommitdiff
path: root/src/parse
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-07-30 18:04:08 +0200
committerLaurenz <laurmaedje@gmail.com>2021-07-30 18:49:19 +0200
commit1ee1d078e2480ddd08d40915bc7a74a8352acff0 (patch)
tree1e7ff367278a19fead3e404cf06d65bfb80a6cd9 /src/parse
parent42a27b48df427edf8dbb624c51551a90ecf2e7ea (diff)
Fatal errors
- Makes errors fatal, so that a phase is only reached when all previous phases were error-free - Parsing still recovers and can produce multiple errors - Evaluation fails fast and can thus produce only a single error (except for parse errors due to an import) - The single error that could occur during execution is removed for now - Removes Value::Error variant
Diffstat (limited to 'src/parse')
-rw-r--r--src/parse/lines.rs4
-rw-r--r--src/parse/mod.rs56
-rw-r--r--src/parse/parser.rs64
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).