Add function expression parsing

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2020-05-07 17:43:39 -04:00
parent d90ecfd4d7
commit 1232eca64c
3 changed files with 164 additions and 19 deletions

View File

@@ -26,17 +26,19 @@ pub enum Expr {
Un(Box<UnExpr>),
FunCall(Box<FunCallExpr>),
Index(Box<IndexExpr>),
Fun(Box<FunExpr>),
Base(BaseExpr),
}
impl Spanned for Expr {
fn span(&self) -> Span {
match self {
Expr::Base(b) => b.span(),
Expr::Bin(b) => b.span(),
Expr::Un(u) => u.span(),
Expr::FunCall(f) => f.span(),
Expr::Index(i) => i.span(),
Expr::Fun(f) => f.span(),
Expr::Base(b) => b.span(),
}
}
}
@@ -120,6 +122,21 @@ impl Spanned for IndexExpr {
}
}
#[derive(Derivative, Clone, PartialEq, Eq)]
#[derivative(Debug)]
pub struct FunExpr {
pub params: Vec<String>,
pub expr: Expr,
#[derivative(Debug = "ignore")]
pub span: Span,
}
impl Spanned for FunExpr {
fn span(&self) -> Span {
self.span
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BaseExprKind {
Ident,

View File

@@ -26,6 +26,10 @@ impl<'t> Lexer<'t> {
self.chars().next().is_none()
}
pub fn text(&self) -> &'t str {
&self.text
}
////////////////////////////////////////////////////////////////////////////////
// Character advancement
////////////////////////////////////////////////////////////////////////////////

View File

@@ -253,26 +253,34 @@ impl<'t> Parser<'t> {
}
TokenKind::LParen => {
let prev_skip = self.set_skip_newlines(true)?;
let first = self.next_expr()?;
if let Some(_) = self.match_token_kind(TokenKind::Comma)? {
let mut list = self.next_expr_list(TokenKind::RParen)?;
self.set_skip_newlines(prev_skip)?;
let end_token = self.expect_token_kind(TokenKind::RParen, "end of tuple")?;
if let Some(end_token) = self.match_token_kind(TokenKind::RParen)? {
// empty tuple
let span = token.span().union(end_token.span());
list.insert(0, first);
Expr::Base(
BaseExpr {
kind: BaseExprKind::Tuple(list),
span,
}
.into(),
)
Expr::Base(BaseExpr {
kind: BaseExprKind::Tuple(Default::default()),
span,
})
} else {
self.set_skip_newlines(prev_skip)?;
self.expect_token_kind(TokenKind::RParen, "end of expression")?;
first
let first = self.next_expr()?;
if let Some(_) = self.match_token_kind(TokenKind::Comma)? {
let mut list = self.next_expr_list(TokenKind::RParen)?;
self.set_skip_newlines(prev_skip)?;
let end_token = self.expect_token_kind(TokenKind::RParen, "end of tuple")?;
let span = token.span().union(end_token.span());
list.insert(0, first);
Expr::Base(
BaseExpr {
kind: BaseExprKind::Tuple(list),
span,
}
.into(),
)
} else {
self.set_skip_newlines(prev_skip)?;
self.expect_token_kind(TokenKind::RParen, "end of expression")?;
first
}
}
}
_ => unreachable!(),
@@ -306,6 +314,51 @@ impl<'t> Parser<'t> {
let span = expr.span().union(end_token.span());
let index_expr = Expr::Index(IndexExpr { expr, index, span }.into());
self.next_suffix_expr(index_expr)
} else if self.match_token_kind(TokenKind::Arrow)?.is_some() {
// check that the LHS is a tuple of idents, and get the parameters as well.
let mut params: Vec<String>;
match expr {
Expr::Base(BaseExpr { kind: BaseExprKind::Tuple(ref tuple), .. }) => {
// Multi-parameter case
params = Vec::with_capacity(tuple.len());
for param_expr in tuple.iter() {
if let Expr::Base(BaseExpr { kind: BaseExprKind::Ident, span, }) = &param_expr {
let name = span.text_at(self.lexer.text());
params.push(name.to_string());
} else {
return Err(Error::ExpectedGot {
expected: "identifier".to_string(),
// TODO : something more specific
got: "some other expression".to_string(),
pos: param_expr.span().start,
});
}
}
}
Expr::Base(BaseExpr { kind: BaseExprKind::Ident, span }) => {
// Single parameter case
params = vec![span.text_at(self.lexer.text()).to_string()]
}
_ => {
return Err(Error::ExpectedGot {
expected: "tuple of function parameters".to_string(),
// TODO : something more specific
got: "something else".to_string(),
pos: expr.span().start,
});
}
}
// function definition
let rhs = self.next_expr()?;
let span = expr.span().union(rhs.span());
// TODO : get params from tuple expr - it's a little funky to do it at this stage but I
// think it will be fine
Ok(Expr::Fun(FunExpr {
params,
expr: rhs,
span,
}.into()))
} else {
Ok(expr)
}
@@ -624,6 +677,17 @@ mod test {
)
}
fn fun_expr(params: Vec<String>, expr: Expr) -> Expr {
Expr::Fun(
FunExpr {
params,
expr,
span: Default::default(),
}
.into(),
)
}
#[test]
fn test_base_expr() {
test_parser!(
@@ -732,6 +796,66 @@ mod test {
);
}
#[test]
fn test_fun_expr() {
test_parser!(
Parser::try_from(
r#"() -> ()"#
)
.unwrap(),
next_expr,
fun_expr(
vec![],
base_expr(BaseExprKind::Tuple(vec![]))
)
);
test_parser!(
Parser::try_from(
r#"() -> 1"#
)
.unwrap(),
next_expr,
fun_expr(
vec![],
base_expr(BaseExprKind::Num),
)
);
test_parser!(
Parser::try_from(
r#"(a) -> 1"#
)
.unwrap(),
next_expr,
fun_expr(
vec![
String::from("a"),
],
base_expr(BaseExprKind::Num),
)
);
test_parser!(
Parser::try_from(
r#"(a, b) -> a + b"#
)
.unwrap(),
next_expr,
fun_expr(
vec![
String::from("a"),
String::from("b"),
],
bin_expr(
base_expr(BaseExprKind::Ident),
BinOp::Plus,
base_expr(BaseExprKind::Ident)
)
)
);
}
#[test]
fn test_bin_expr() {
test_parser!(