Compare commits

...

2 Commits

Author SHA1 Message Date
Greg Shuflin
1c6545fb74 Fix index/call parsing 2021-10-30 20:49:29 -07:00
Greg Shuflin
46fe03b43f Move over some lambda tests 2021-10-30 20:27:16 -07:00
4 changed files with 198 additions and 126 deletions

View File

@ -69,15 +69,21 @@
//! ```text //! ```text
//! expression := precedence_expr type_anno? //! expression := precedence_expr type_anno?
//! precedence_expr := prefix_expr //! precedence_expr := prefix_expr
//! prefix_expr := prefix_op call_expr //! prefix_expr := prefix_op extended_expr
//! prefix_op := "+" | "-" | "!" | "~" //!
//! call_expr := index_expr ( "(" invocation_list ")" )* | ε //! prefix_op := "+" | "-" | "!" | "~" | ε
//! invocation_list := invocation_argument ("," invocation_argument)* | ε //!
//! invocation_argument := expression | IDENTIFIER "=" expression | "_" //! extended_expr := primary (ε | index | call | access)*
//! index_expr := primary ( "[" (expression ("," (expression)* | ε) "]" )* //! index := "[" (expression ("," expression)*) "]"
//! call := "(" invocation_list ")"
//! access := "." identifier
//! primary := literal | paren_expr | if_expr | for_expr | while_expr | identifier_expr | lambda_expr | anonymous_struct | list_expr //! primary := literal | paren_expr | if_expr | for_expr | while_expr | identifier_expr | lambda_expr | anonymous_struct | list_expr
//! expr_or_block := "{" (statement delimiter)* "}" | expr //! expr_or_block := "{" (statement delimiter)* "}" | expr
//!
//! invocation_list := invocation_argument ("," invocation_argument)* | ε
//! invocation_argument := expression | IDENTIFIER "=" expression | "_"
//! ``` //! ```
//! //TODO fix expressions, add accessor_expr as a new thing
//! //!
//! ### Primary expressions //! ### Primary expressions
//! //!
@ -688,20 +694,39 @@ impl Parser {
ExpressionKind::PrefixExp(prefix_op, Box::new(expr)) ExpressionKind::PrefixExp(prefix_op, Box::new(expr))
)) ))
}, },
_ => self.call_expr() _ => self.extended_expr()
} }
} }
#[recursive_descent_method] #[recursive_descent_method]
fn call_expr(&mut self) -> ParseResult<Expression> { fn extended_expr(&mut self) -> ParseResult<Expression> {
let mut expr = self.index_expr()?; let mut expression = self.primary()?;
//TODO look at this loop {
while let LParen = self.token_handler.peek_kind() { //TODO need a next non whitespace
let arguments = delimited!(self, LParen, invocation_argument, Comma, RParen); let next = self.token_handler.peek_kind();
expr = Expression::new(self.id_store.fresh(), ExpressionKind::Call { f: Box::new(expr), arguments }); //TODO no type anno is incorrect match next {
} Period => unimplemented!(),
LSquareBracket => {
Ok(expr) let indexers = delimited!(self, LSquareBracket, expression, Comma, RSquareBracket);
if indexers.is_empty() {
return ParseError::new_with_token("Empty index expressions are not allowed", self.token_handler.peek());
}
expression = Expression::new(self.id_store.fresh(), ExpressionKind::Index {
indexee: Box::new(expression),
indexers,
});
},
LParen => {
let arguments = delimited!(self, LParen, invocation_argument, Comma, RParen);
expression = Expression::new(self.id_store.fresh(), ExpressionKind::Call {
f: Box::new(expression),
arguments,
});
},
_ => break,
}
}
Ok(expression)
} }
#[recursive_descent_method] #[recursive_descent_method]

View File

@ -85,6 +85,10 @@ macro_rules! qn {
}; };
} }
fn int_anno() -> TypeIdentifier {
TypeIdentifier::Singleton(TypeSingletonName { name: rc("Int"), params: vec![] })
}
macro_rules! assert_ast { macro_rules! assert_ast {
($input:expr, $statements:expr) => { ($input:expr, $statements:expr) => {
let ast = parse($input).unwrap(); let ast = parse($input).unwrap();
@ -116,7 +120,6 @@ macro_rules! assert_fail_expr {
assert_eq!(err.msg, $failure); assert_eq!(err.msg, $failure);
}; };
} }
#[test] #[test]
fn basic_literals() { fn basic_literals() {
use ExpressionKind::*; use ExpressionKind::*;
@ -198,14 +201,14 @@ fn operators() {
assert_expr!("a <> 1", binop("<>", expr(Value(qn!(a))), expr(NatLiteral(1)))); assert_expr!("a <> 1", binop("<>", expr(Value(qn!(a))), expr(NatLiteral(1))));
} }
//TODO have a test for dot accessors #[test]
/* fn accessors() {
assert_expr!("a.b.c.d", exst!(binexp!(".", /*
binexp!(".", assert_expr!("a.b");
binexp!(".", val!("a"), val!("b")), assert_expr!("a.b.c.d()");
val!("c")), assert_expr!("a.b().c.d()");
val!("d")))); */
*/ }
#[test] #[test]
fn tuples() { fn tuples() {
@ -269,17 +272,19 @@ fn index() {
indexers: vec![expr(Value(qn!(b))), expr(Value(qn!(c)))] indexers: vec![expr(Value(qn!(b))), expr(Value(qn!(c)))]
}) })
); );
//TODO this is a parser bug assert_expr!(
/* "perspicacity()[a]",
assert_expr!("perspicacity()[a]", expr(Index{ expr(Index {
indexee: bx(expr(Call { indexee: bx(expr(Call { f: bx(expr(Value(qn!(perspicacity)))), arguments: vec![] })),
f: bx(expr(Value(qn!(perspicacity)))), indexers: vec![expr(Value(qn!(a)))]
arguments: vec![] })
})), );
indexers: vec![expr(Value(qn!(a)))]
})); let a = expr(Call { f: bx(expr(Value(qn!(a)))), arguments: vec![] });
*/ let b = expr(Index { indexee: bx(a), indexers: vec![expr(Value(qn!(b)))] });
//TODO parse_test("a()[b]()[d]") let c = expr(Call { f: bx(b), arguments: vec![] });
let d = expr(Index { indexee: bx(c), indexers: vec![expr(Value(qn!(d)))] });
assert_expr!("a()[b]()[d]", d);
assert_fail_expr!("a[]", "Empty index expressions are not allowed"); assert_fail_expr!("a[]", "Empty index expressions are not allowed");
} }
@ -325,6 +330,134 @@ fn for_expression() {
); );
} }
#[test]
fn lambda_expressions() {
use ExpressionKind::*;
assert_expr!(
r#"\(x) { x + 1}"#,
expr(Lambda {
params: vec![FormalParam { name: rc!(x), anno: None, default: None }],
type_anno: None,
body:
vec![stmt(StatementKind::Expression(binop("+", expr(Value(qn!(x))), expr(NatLiteral(1))))),]
.into()
})
);
assert_expr!(
r#"\ (x: Int, y) { a;b;c;}"#,
expr(Lambda {
params: vec![
FormalParam { name: rc!(x), anno: Some(int_anno()), default: None },
FormalParam { name: rc!(y), anno: None, default: None },
],
type_anno: None,
body: vec![
stmt(StatementKind::Expression(expr(Value(qn!(a))))),
stmt(StatementKind::Expression(expr(Value(qn!(b))))),
stmt(StatementKind::Expression(expr(Value(qn!(c))))),
]
.into()
})
);
assert_expr!(
r#"\(x){y}(1)"#,
expr(Call {
f: bx(expr(Lambda {
params: vec![FormalParam { name: rc!(x), anno: None, default: None },],
type_anno: None,
body: vec![stmt(StatementKind::Expression(expr(Value(qn!(y))))),].into()
})),
arguments: vec![InvocationArgument::Positional(expr(NatLiteral(1)))],
})
);
assert_expr!(
r#"\(x: Int): String { "q" }"#,
expr(Lambda {
params: vec![FormalParam { name: rc!(x), anno: Some(int_anno()), default: None },],
type_anno: Some(TypeIdentifier::Singleton(TypeSingletonName {
name: rc("String"),
params: vec![]
})),
body: vec![stmt(StatementKind::Expression(expr(StringLiteral(rc("q"))))),].into()
})
);
}
#[test]
fn single_param_lambda() {
use ExpressionKind::*;
assert_expr!(
r#"\x { x + 10 }"#,
expr(Lambda {
params: vec![FormalParam { name: rc!(x), anno: None, default: None },],
type_anno: None,
body: vec![stmt(StatementKind::Expression(binop(
"+",
expr(Value(qn!(x))),
expr(NatLiteral(10))
)))]
.into()
})
);
assert_expr!(
r#"\x: Int { x + 10 }"#,
expr(Lambda {
params: vec![FormalParam { name: rc!(x), anno: Some(int_anno()), default: None },],
type_anno: None,
body: vec![stmt(StatementKind::Expression(binop(
"+",
expr(Value(qn!(x))),
expr(NatLiteral(10))
)))]
.into()
})
);
}
#[test]
fn complex_lambdas() {
use ExpressionKind::*;
assert_ast! {
r#"fn wahoo() { let a = 10; \(x) { x + a } };
wahoo()(3) "#,
vec![
fn_decl(Signature { name: rc("wahoo"), operator: false, type_anno: None, params: vec![] },
vec![
decl(Declaration::Binding {
name: rc("a"),
constant: true,
type_anno: None,
expr: expr(NatLiteral(10))
}),
stmt(StatementKind::Expression(expr(Lambda {
params: vec![
FormalParam { name: rc("x"), default: None, anno: None }
],
type_anno: None,
body: vec![
stmt(StatementKind::Expression(binop("+", expr(Value(qn!(x))), expr(Value(qn!(a)))))),
].into()
}))),
].into()),
stmt(StatementKind::Expression(expr(Call {
f: bx(expr(Call {
f: bx(expr(Value(qn!(wahoo)))),
arguments: vec![] })),
arguments: vec![
InvocationArgument::Positional(expr(NatLiteral(3)))
]
})))
]
};
}
#[test] #[test]
fn reserved_words() { fn reserved_words() {
assert_fail!("module::item::call()", "Expected an identifier, got Colon"); assert_fail!("module::item::call()", "Expected an identifier, got Colon");
@ -532,7 +665,6 @@ fn functions_with_different_whitespace() {
#[test] #[test]
fn functions_with_default_args() { fn functions_with_default_args() {
use ExpressionKind::*; use ExpressionKind::*;
let int_anno = TypeIdentifier::Singleton(TypeSingletonName { name: rc("Int"), params: vec![] });
assert_ast!( assert_ast!(
"fn func(x: Int, y: Int = 4) { }", "fn func(x: Int, y: Int = 4) { }",
@ -542,8 +674,8 @@ fn functions_with_default_args() {
operator: false, operator: false,
type_anno: None, type_anno: None,
params: vec![ params: vec![
FormalParam { name: rc("x"), anno: Some(int_anno.clone()), default: None }, FormalParam { name: rc("x"), anno: Some(int_anno()), default: None },
FormalParam { name: rc("y"), anno: Some(int_anno), default: Some(expr(NatLiteral(4))) }, FormalParam { name: rc("y"), anno: Some(int_anno()), default: Some(expr(NatLiteral(4))) },
], ],
}, },
vec![].into() vec![].into()

View File

@ -2,6 +2,7 @@
#![allow(clippy::upper_case_acronyms)] #![allow(clippy::upper_case_acronyms)]
#![allow(clippy::vec_init_then_push)] #![allow(clippy::vec_init_then_push)]
use pretty_assertions::assert_eq;
use std::rc::Rc; use std::rc::Rc;
use crate::tokenizing::Location; use crate::tokenizing::Location;
@ -87,10 +88,6 @@ macro_rules! ex {
}; };
} }
macro_rules! inv {
($expr_type:expr) => { InvocationArgument::Positional($expr_type) }
}
macro_rules! exst { macro_rules! exst {
($expr_type:expr) => { make_statement(StatementKind::Expression(Expression::new(Default::default(), $expr_type).into())) }; ($expr_type:expr) => { make_statement(StatementKind::Expression(Expression::new(Default::default(), $expr_type).into())) };
($expr_type:expr, $type_anno:expr) => { make_statement(StatementKind::Expression(Expression::with_anno(Default::default(), $expr_type, $type_anno).into())) }; ($expr_type:expr, $type_anno:expr) => { make_statement(StatementKind::Expression(Expression::with_anno(Default::default(), $expr_type, $type_anno).into())) };
@ -251,89 +248,6 @@ fn parsing_impls() {
})); }));
} }
#[test]
fn parsing_lambdas() {
parse_test_wrap_ast! { r#"\(x) { x + 1}"#, exst!(
Lambda { params: vec![FormalParam { name: rc!(x), anno: None, default: None } ], type_anno: None, body: exst!(s "x + 1").into() }
)
}
parse_test_wrap_ast!(r#"\ (x: Int, y) { a;b;c;}"#,
exst!(Lambda {
params: vec![
FormalParam { name: rc!(x), anno: Some(ty!("Int")), default: None },
FormalParam { name: rc!(y), anno: None, default: None }
],
type_anno: None,
body: vec![exst!(s "a"), exst!(s "b"), exst!(s "c")].into()
})
);
parse_test_wrap_ast! { r#"\(x){y}(1)"#,
exst!(Call { f: bx!(ex!(
Lambda {
params: vec![
FormalParam { name: rc!(x), anno: None, default: None }
],
type_anno: None,
body: exst!(s "y").into() }
)),
arguments: vec![inv!(ex!(NatLiteral(1)))] })
};
parse_test_wrap_ast! {
r#"\(x: Int): String { "q" }"#,
exst!(Lambda {
params: vec![
FormalParam { name: rc!(x), anno: Some(ty!("Int")), default: None },
],
type_anno: Some(ty!("String")),
body: exst!(s r#""q""#).into()
})
}
}
#[test]
fn single_param_lambda() {
parse_test_wrap_ast! {
r"\x { x + 10 }",
exst!(Lambda {
params: vec![FormalParam { name: rc!(x), anno: None, default: None }],
type_anno: None,
body: exst!(s r"x + 10").into()
})
}
parse_test_wrap_ast! {
r"\x: Nat { x + 10 }",
exst!(Lambda {
params: vec![FormalParam { name: rc!(x), anno: Some(ty!("Nat")), default: None }],
type_anno: None,
body: exst!(s r"x + 10").into()
})
}
}
#[test]
fn more_advanced_lambdas() {
parse_test! {
r#"fn wahoo() { let a = 10; \(x) { x + a } };
wahoo()(3) "#,
AST {
id: Default::default(),
statements: vec![
exst!(s r"fn wahoo() { let a = 10; \(x) { x + a } }"),
exst! {
Call {
f: bx!(ex!(Call { f: bx!(ex!(val!("wahoo"))), arguments: vec![] })),
arguments: vec![inv!(ex!(NatLiteral(3)))],
}
}
].into()
}
}
}
#[test] #[test]
fn list_literals() { fn list_literals() {
parse_test_wrap_ast! { parse_test_wrap_ast! {

View File

@ -1,5 +1,6 @@
#![cfg(test)] #![cfg(test)]
use test_case::test_case; use test_case::test_case;
use pretty_assertions::assert_eq;
use crate::{ use crate::{
symbol_table::SymbolTable, symbol_table::SymbolTable,