This commit is contained in:
nora 2021-10-31 20:35:37 +01:00
parent c6200a901c
commit 7b1c7335c2
3 changed files with 245 additions and 17 deletions

View file

@ -7,7 +7,7 @@ pub use span::Span;
mod span {
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
#[derive(Debug, Default, Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
pub struct Span {
pub start: usize,
pub end: usize,

View file

@ -47,7 +47,6 @@ impl<'code> Parser<'code> {
let mut stmts = Vec::new();
loop {
if let Some(TokenType::BraceC) | None = self.peek_kind() {
let _ = self.next();
return Ok(stmts);
}
let stmt = self.statement()?;
@ -176,7 +175,11 @@ impl<'code> Parser<'code> {
fn while_stmt(&mut self) -> ParseResult<'code, Stmt> {
let keyword_span = self.expect(TokenType::While)?.span;
let cond = self.expression()?;
self.inside_loop_depth += 1;
let body = self.block()?;
self.inside_loop_depth -= 1;
Ok(Stmt::While(WhileStmt {
span: keyword_span.extend(body.span),
cond,

View file

@ -3,7 +3,9 @@ use crate::parse::Parser;
use prelude::*;
mod prelude {
pub(super) use super::{parser, test_literal_bin_op, test_number_literal, token};
pub(super) use super::{
empty_block, num_lit, parser, test_literal_bin_op, test_number_literal, token,
};
pub(super) use crate::ast::*;
pub(super) use crate::errors::Span;
pub(super) use crate::lex::{
@ -19,6 +21,17 @@ fn token(kind: TokenType) -> Token {
}
}
fn num_lit(number: f64) -> Expr {
Expr::Literal(Literal::Number(number, Span::dummy()))
}
fn empty_block() -> Block {
Block {
stmts: vec![],
span: Span::dummy(),
}
}
fn parser(tokens: Vec<Token>) -> Parser {
Parser {
tokens: tokens.into_iter().peekable(),
@ -39,8 +52,8 @@ fn test_literal_bin_op<F: FnOnce(Vec<Token<'_>>) -> Expr>(
assert_eq!(
Expr::BinaryOp(Box::new(BinaryOp {
span: Span::dummy(),
lhs: Expr::Literal(Literal::Number(10.0, Span::dummy())),
rhs: Expr::Literal(Literal::Number(4.0, Span::dummy())),
lhs: num_lit(10.0),
rhs: num_lit(4.0),
kind: expected_op_kind
})),
factor
@ -50,7 +63,147 @@ fn test_literal_bin_op<F: FnOnce(Vec<Token<'_>>) -> Expr>(
fn test_number_literal<F: FnOnce(Vec<Token<'_>>) -> Expr>(parser: F) {
let tokens = [TokenType::Number(10.0)].map(token).into();
let unary = parser(tokens);
assert_eq!(Expr::Literal(Literal::Number(10.0, Span::dummy())), unary);
assert_eq!(num_lit(10.0), unary);
}
mod r#if {
use super::prelude::*;
fn parse_if(tokens: Vec<Token>) -> IfStmt {
let mut parser = parser(tokens);
parser.if_stmt().unwrap()
}
#[test]
fn empty() {
let tokens = [If, True, BraceO, BraceC].map(token).into();
let ast = parse_if(tokens);
assert_eq!(
IfStmt {
span: Span::dummy(),
cond: Expr::Literal(Literal::Boolean(true, Span::dummy())),
body: empty_block(),
else_part: None
},
ast
);
}
#[test]
fn if_else() {
let tokens = [If, True, BraceO, BraceC, Else, BraceO, BraceC]
.map(token)
.into();
let ast = parse_if(tokens);
assert_eq!(
IfStmt {
span: Span::dummy(),
cond: Expr::Literal(Literal::Boolean(true, Span::dummy())),
body: empty_block(),
else_part: Some(Box::new(ElsePart::Else(empty_block(), Span::dummy())))
},
ast
);
}
#[test]
fn if_else_if() {
let tokens = [If, True, BraceO, BraceC, Else, If, True, BraceO, BraceC]
.map(token)
.into();
let ast = parse_if(tokens);
assert_eq!(
IfStmt {
span: Span::dummy(),
cond: Expr::Literal(Literal::Boolean(true, Span::dummy())),
body: empty_block(),
else_part: Some(Box::new(ElsePart::ElseIf(
IfStmt {
span: Span::dummy(),
cond: Expr::Literal(Literal::Boolean(true, Span::dummy())),
body: empty_block(),
else_part: None
},
Span::dummy()
)))
},
ast
);
}
#[test]
fn if_else_if_else() {
let tokens = [
If, True, BraceO, BraceC, Else, If, True, BraceO, BraceC, Else, BraceO, BraceC,
]
.map(token)
.into();
let ast = parse_if(tokens);
assert_eq!(
IfStmt {
span: Span::dummy(),
cond: Expr::Literal(Literal::Boolean(true, Span::dummy())),
body: empty_block(),
else_part: Some(Box::new(ElsePart::ElseIf(
IfStmt {
span: Span::dummy(),
cond: Expr::Literal(Literal::Boolean(true, Span::dummy())),
body: empty_block(),
else_part: Some(Box::new(ElsePart::Else(empty_block(), Span::dummy())))
},
Span::dummy()
)))
},
ast
);
}
}
mod r#while {
use super::prelude::*;
fn parse_while(tokens: Vec<Token>) -> Stmt {
let mut parser = parser(tokens);
parser.while_stmt().unwrap()
}
#[test]
fn empty() {
let tokens = [While, True, BraceO, BraceC].map(token).into();
let ast = parse_while(tokens);
assert_eq!(
Stmt::While(WhileStmt {
span: Span::dummy(),
cond: Expr::Literal(Literal::Boolean(true, Span::dummy())),
body: empty_block()
}),
ast
);
}
#[test]
fn or_condition_break() {
let tokens = [While, False, Or, True, BraceO, Break, Semi, BraceC]
.map(token)
.into();
let ast = parse_while(tokens);
assert_eq!(
Stmt::While(WhileStmt {
span: Span::dummy(),
cond: Expr::BinaryOp(Box::new(BinaryOp {
span: Span::dummy(),
lhs: Expr::Literal(Literal::Boolean(false, Span::dummy())),
rhs: Expr::Literal(Literal::Boolean(true, Span::dummy())),
kind: BinaryOpKind::Or
})),
body: Block {
stmts: vec![Stmt::Break(Span::dummy())],
span: Span::dummy()
}
}),
ast
);
}
}
mod r#loop {
@ -65,10 +218,38 @@ mod r#loop {
fn empty() {
let tokens = [Loop, BraceO, BraceC].map(token).into();
let ast = parse_loop(tokens);
assert_eq!(Stmt::Loop(empty_block(), Span::dummy()), ast);
}
#[test]
fn with_break() {
let tokens = [Loop, BraceO, Break, Semi, BraceC].map(token).into();
let ast = parse_loop(tokens);
assert_eq!(
Stmt::Loop(
Block {
stmts: vec![],
stmts: vec![Stmt::Break(Span::dummy())],
span: Default::default()
},
Span::dummy()
),
ast
);
}
#[test]
fn break_after_inner() {
let tokens = [Loop, BraceO, Loop, BraceO, BraceC, Break, Semi, BraceC]
.map(token)
.into();
let ast = parse_loop(tokens);
assert_eq!(
Stmt::Loop(
Block {
stmts: vec![
Stmt::Loop(empty_block(), Span::dummy()),
Stmt::Break(Span::dummy())
],
span: Span::dummy()
},
Span::dummy()
@ -78,6 +259,50 @@ mod r#loop {
}
}
mod block {
use super::prelude::*;
fn parse_block(tokens: Vec<Token>) -> Block {
let mut parser = parser(tokens);
parser.block().unwrap()
}
#[test]
fn empty() {
let tokens = [BraceO, BraceC].map(token).into();
let ast = parse_block(tokens);
assert_eq!(empty_block(), ast);
}
#[test]
fn two_expressions() {
let tokens = [BraceO, Number(10.0), Semi, Number(20.0), Semi, BraceC]
.map(token)
.into();
let ast = parse_block(tokens);
assert_eq!(
Block {
stmts: vec![Stmt::Expr(num_lit(10.0)), Stmt::Expr(num_lit(20.0)),],
span: Span::dummy()
},
ast
);
}
#[test]
fn nested() {
let tokens = [BraceO, BraceO, BraceC, BraceC].map(token).into();
let ast = parse_block(tokens);
assert_eq!(
Block {
stmts: vec![Stmt::Block(empty_block())],
span: Span::dummy()
},
ast
);
}
}
mod expr {
use super::prelude::*;
use crate::ast::{UnaryOp, UnaryOpKind};
@ -101,11 +326,11 @@ mod expr {
assert_eq!(
Expr::BinaryOp(Box::new(BinaryOp {
span: Span::dummy(),
lhs: Expr::Literal(Literal::Number(10.0, Span::dummy())),
lhs: num_lit(10.0),
rhs: Expr::BinaryOp(Box::new(BinaryOp {
span: Span::dummy(),
lhs: Expr::Literal(Literal::Number(20.0, Span::dummy())),
rhs: Expr::Literal(Literal::Number(100.0, Span::dummy())),
lhs: num_lit(20.0),
rhs: num_lit(100.0),
kind: BinaryOpKind::Mul
})),
@ -124,10 +349,10 @@ mod expr {
assert_eq!(
Expr::BinaryOp(Box::new(BinaryOp {
span: Span::dummy(),
lhs: Expr::Literal(Literal::Number(10.0, Span::dummy())),
lhs: num_lit(10.0),
rhs: Expr::UnaryOp(Box::new(UnaryOp {
span: Span::dummy(),
expr: Expr::Literal(Literal::Number(10.0, Span::dummy())),
expr: num_lit(10.0),
kind: UnaryOpKind::Neg
})),
kind: BinaryOpKind::Equal
@ -153,11 +378,11 @@ mod expr {
assert_eq!(
Expr::BinaryOp(Box::new(BinaryOp {
span: Span::dummy(),
lhs: Expr::Literal(Literal::Number(10.0, Span::dummy())),
lhs: num_lit(10.0),
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())),
lhs: num_lit(20.0),
rhs: num_lit(30.0),
kind: BinaryOpKind::Add
})),
@ -364,7 +589,7 @@ mod unary {
assert_eq!(
Expr::UnaryOp(Box::new(UnaryOp {
span: Span::dummy(),
expr: Expr::Literal(Literal::Number(10.0, Span::dummy())),
expr: num_lit(10.0),
kind: UnaryOpKind::Neg
})),
unary
@ -397,7 +622,7 @@ mod primary {
fn string() {
let tokens = [TokenType::Number(10.0)].map(token).into();
let literal = parse_primary(tokens);
assert_eq!(Expr::Literal(Literal::Number(10.0, Span::dummy())), literal);
assert_eq!(num_lit(10.0), literal);
}
#[test]