better errors

This commit is contained in:
nora 2021-10-31 12:55:39 +01:00
parent c6e5a5d686
commit 67e6dfccc2
4 changed files with 152 additions and 43 deletions

View file

@ -66,7 +66,8 @@
<unary> ::= { ( "not" | "-" ) } <primary> <unary> ::= { ( "not" | "-" ) } <primary>
<primary> ::= <NUMBER> <primary> ::= <IDENT>
| <NUMBER>
| <STRING> | <STRING>
| <object-literal> | <object-literal>
| <array-literal> | <array-literal>

View file

@ -76,6 +76,7 @@ pub struct Break {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Expr { pub enum Expr {
Ident(Symbol, Span),
Literal(Literal), Literal(Literal),
UnaryOp(Box<UnaryOp>), UnaryOp(Box<UnaryOp>),
BinaryOp(Box<BinaryOp>), BinaryOp(Box<BinaryOp>),
@ -87,6 +88,7 @@ impl Expr {
Expr::Literal(lit) => lit.span(), Expr::Literal(lit) => lit.span(),
Expr::UnaryOp(unary) => unary.span, Expr::UnaryOp(unary) => unary.span,
Expr::BinaryOp(binary) => binary.span, Expr::BinaryOp(binary) => binary.span,
Expr::Ident(_, span) => *span,
} }
} }
} }

View file

@ -181,8 +181,16 @@ impl<'code> Parser<'code> {
TokenType::Null => Ok(Expr::Literal(Literal::Null(next.span))), TokenType::Null => Ok(Expr::Literal(Literal::Null(next.span))),
TokenType::BraceO => self.object_literal(next.span), TokenType::BraceO => self.object_literal(next.span),
TokenType::BracketO => self.array_literal(next.span), TokenType::BracketO => self.array_literal(next.span),
TokenType::ParenO => todo!(), TokenType::ParenO => {
_ => todo!(), let expr = self.expression()?;
let _ = self.expect(TokenType::ParenC)?;
Ok(expr)
}
TokenType::Ident(name) => {
let name_owned = name.to_owned();
Ok(Expr::Ident(name_owned, next.span))
}
_ => Err(ParseErr::InvalidTokenPrimary(next)),
} }
} }
@ -193,7 +201,12 @@ impl<'code> Parser<'code> {
fn array_literal(&mut self, open_span: Span) -> ParseResult<'code, Expr> { fn array_literal(&mut self, open_span: Span) -> ParseResult<'code, Expr> {
let mut elements = Vec::new(); let mut elements = Vec::new();
while self.peek().ok_or(ParseErr::EOF("array literal"))?.kind != TokenType::BracketC { while self
.peek()
.ok_or(ParseErr::EOFExpecting(TokenType::BracketC))?
.kind
!= TokenType::BracketC
{
let expr = self.expression()?; let expr = self.expression()?;
elements.push(expr); elements.push(expr);
self.expect(TokenType::Comma)?; self.expect(TokenType::Comma)?;
@ -222,32 +235,60 @@ impl<'code> Parser<'code> {
if token.kind == kind { if token.kind == kind {
Ok(token) Ok(token)
} else { } else {
Err(ParseErr::MismatchedKind { expected: kind }) Err(ParseErr::MismatchedKind {
expected: kind,
actual: token,
})
} }
} else { } else {
Err(ParseErr::ExpectedToken(kind)) Err(ParseErr::EOFExpecting(kind))
} }
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub enum ParseErr<'code> { pub enum ParseErr<'code> {
MismatchedKind { expected: TokenType<'code> }, MismatchedKind {
ExpectedToken(TokenType<'code>), expected: TokenType<'code>,
actual: Token<'code>,
},
InvalidTokenPrimary(Token<'code>),
EOFExpecting(TokenType<'code>),
EOF(&'static str), EOF(&'static str),
} }
impl CompilerError for ParseErr<'_> { impl CompilerError for ParseErr<'_> {
fn span(&self) -> Span { fn span(&self) -> Span {
todo!() match self {
ParseErr::MismatchedKind {
actual: Token { span, .. },
..
} => *span,
ParseErr::InvalidTokenPrimary(Token { span, .. }) => *span,
ParseErr::EOFExpecting(_) => Span::dummy(),
ParseErr::EOF(_) => Span::dummy(),
}
} }
fn message(&self) -> String { fn message(&self) -> String {
todo!() match self {
ParseErr::MismatchedKind { expected, actual } => {
format!("expected: {:?}, received: {:?}", expected, actual.kind)
}
ParseErr::InvalidTokenPrimary(token) => {
format!("invalid token in expression: {:?}", token.kind)
}
ParseErr::EOFExpecting(token) => {
format!("reached EOF searching for: {:?}", token)
}
ParseErr::EOF(message) => {
format!("reached EOF while parsing: {}", message)
}
}
} }
fn note(&self) -> Option<String> { fn note(&self) -> Option<String> {
todo!() None
} }
} }
@ -266,7 +307,7 @@ mod test {
fn token(kind: TokenType) -> Token { fn token(kind: TokenType) -> Token {
Token { Token {
span: crate::errors::Span::dummy(), span: Span::dummy(),
kind, kind,
} }
} }
@ -305,6 +346,7 @@ mod test {
mod expr { mod expr {
use super::prelude::*; use super::prelude::*;
use crate::ast::{UnaryOp, UnaryOpKind};
use TokenType::*; use TokenType::*;
fn parse_expr(tokens: Vec<Token>) -> Expr { fn parse_expr(tokens: Vec<Token>) -> Expr {
@ -312,6 +354,11 @@ mod test {
parser.expression().unwrap() parser.expression().unwrap()
} }
#[test]
fn number_literal() {
test_number_literal(parse_expr);
}
#[test] #[test]
fn add_multiply() { fn add_multiply() {
let tokens = [Number(10.0), Plus, Number(20.0), Asterisk, Number(100.0)] let tokens = [Number(10.0), Plus, Number(20.0), Asterisk, Number(100.0)]
@ -334,6 +381,58 @@ mod test {
expr expr
); );
} }
#[test]
fn equal_unary() {
let tokens = [Number(10.0), EqualEqual, Minus, Number(10.0)]
.map(token)
.into();
let expr = parse_expr(tokens);
assert_eq!(
Expr::BinaryOp(Box::new(BinaryOp {
span: Span::dummy(),
lhs: Expr::Literal(Literal::Number(10.0, Span::dummy())),
rhs: Expr::UnaryOp(Box::new(UnaryOp {
span: Span::dummy(),
expr: Expr::Literal(Literal::Number(10.0, Span::dummy())),
kind: UnaryOpKind::Neg
})),
kind: BinaryOpKind::Equal
})),
expr
);
}
#[test]
fn parentheses_mul_add() {
let tokens = [
Number(10.0),
Asterisk,
ParenO,
Number(20.0),
Plus,
Number(30.0),
ParenC,
]
.map(token)
.into();
let expr = parse_expr(tokens);
assert_eq!(
Expr::BinaryOp(Box::new(BinaryOp {
span: Span::dummy(),
lhs: Expr::Literal(Literal::Number(10.0, Span::dummy())),
rhs: Expr::BinaryOp(Box::new(BinaryOp {
span: Span::dummy(),
lhs: Expr::Literal(Literal::Number(20.0, Span::dummy())),
rhs: Expr::Literal(Literal::Number(30.0, Span::dummy())),
kind: BinaryOpKind::Add
})),
kind: BinaryOpKind::Mul
})),
expr
);
}
} }
mod logical_or { mod logical_or {
@ -495,6 +594,7 @@ mod test {
mod unary { mod unary {
use super::prelude::*; use super::prelude::*;
use crate::ast::{UnaryOp, UnaryOpKind};
fn parse_unary(tokens: Vec<Token>) -> Expr { fn parse_unary(tokens: Vec<Token>) -> Expr {
let mut parser = parser(tokens); let mut parser = parser(tokens);
@ -508,36 +608,35 @@ mod test {
// needs expr support // needs expr support
// #[test]
// #[test] fn not() {
// fn not() { let tokens = [TokenType::Not, TokenType::True].map(token).into();
// let tokens = [TokenType::Not, TokenType::True].map(token).into(); let unary = parse_unary(tokens);
// let unary = parse_unary(tokens); assert_eq!(
// assert_eq!( Expr::UnaryOp(Box::new(UnaryOp {
// Expr::UnaryOp(Box::new(UnaryOp { span: Span::dummy(),
// span: Span::dummy(), expr: Expr::Literal(Literal::Boolean(true, Span::dummy())),
// expr: Expr::Literal(Literal::Boolean(true, Span::dummy())), kind: UnaryOpKind::Not
// kind: UnaryOpKind::Not })),
// })), unary
// unary );
// ); }
// }
// #[test]
// #[test] fn neg() {
// fn neg() { let tokens = [TokenType::Minus, TokenType::Number(10.0)]
// let tokens = [TokenType::Minus, TokenType::Number(10.0)] .map(token)
// .map(token) .into();
// .into(); let unary = parse_unary(tokens);
// let unary = parse_unary(tokens); assert_eq!(
// assert_eq!( Expr::UnaryOp(Box::new(UnaryOp {
// Expr::UnaryOp(Box::new(UnaryOp { span: Span::dummy(),
// span: Span::dummy(), expr: Expr::Literal(Literal::Number(10.0, Span::dummy())),
// expr: Expr::Literal(Literal::Number(10.0, Span::dummy())), kind: UnaryOpKind::Neg
// kind: UnaryOpKind::Neg })),
// })), unary
// unary );
// ); }
// }
} }
mod primary { mod primary {
@ -548,6 +647,13 @@ mod test {
parser.primary().unwrap() parser.primary().unwrap()
} }
#[test]
fn ident() {
let tokens = [TokenType::Ident("tokens")].map(token).into();
let literal = parse_primary(tokens);
assert_eq!(Expr::Ident("tokens".to_string(), Span::dummy()), literal);
}
#[test] #[test]
fn string() { fn string() {
let tokens = [TokenType::Number(10.0)].map(token).into(); let tokens = [TokenType::Number(10.0)].map(token).into();

View file

@ -1 +1 @@
1 + 2 * 10; (1 + 5 and 5) / 2;