From 1b61f4b25bcab91eaa248368e9e4af8be61b5f68 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 2 Oct 2022 18:16:19 -0400 Subject: [PATCH] feat: field access --- crates/lang/src/parser.rs | 66 ++++++++++++++++++++++++++++++++- crates/lang/src/tests/parser.rs | 34 +++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index a5b9663f..2e6bca86 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -2,12 +2,22 @@ use chumsky::prelude::*; use vec1::Vec1; use crate::{ - ast::{self, BinOp, TodoKind}, + ast::{self, BinOp, Span, TodoKind}, error::ParseError, expr, token::Token, }; +// Parsing a function call into the appropriate structure +#[derive(Debug)] +pub enum ParserArg { + Arg(Box>), + Hole { + location: Span, + label: Option, + }, +} + pub fn module_parser( kind: ast::ModuleKind, ) -> impl Parser { @@ -453,13 +463,65 @@ pub fn expr_parser( assert_parser, )); + enum Chain { + FieldAccess(String, Span), + RecordUpdate, + Call(Vec, Span), + } + + let field_access_parser = just(Token::Dot) + .ignore_then(select! { + Token::Name { name } => name, + Token::UpName { name } => name + }) + .map_with_span(Chain::FieldAccess); + + let call_parser = choice(( + select! { Token::Name { name } => name } + .then_ignore(just(Token::Colon)) + .or_not() + .then(r.clone()) + .map_with_span(|(label, value), span| { + ParserArg::Arg(Box::new(ast::CallArg { + label, + location: span, + value, + })) + }), + select! { Token::Name { name } => name } + .then_ignore(just(Token::Colon)) + .or_not() + .then_ignore(select! {Token::DiscardName {name} => name }) + .map_with_span(|label, span| ParserArg::Hole { + location: span, + label, + }), + )) + .separated_by(just(Token::Comma)) + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .map_with_span(Chain::Call); + + let chain = choice((field_access_parser, call_parser)); + + let chained = expr_unit_parser + .then(chain.repeated()) + .foldl(|e, chain| match chain { + Chain::FieldAccess(label, span) => expr::UntypedExpr::FieldAccess { + location: e.location().union(span), + label, + container: Box::new(e), + }, + _ => todo!(), + }); + + // Negate let op = just(Token::Bang); let unary = op .ignored() .map_with_span(|_, span| span) .repeated() - .then(expr_unit_parser) + .then(chained) .foldr(|span, value| expr::UntypedExpr::Negate { location: span.union(value.location()), value: Box::new(value), diff --git a/crates/lang/src/tests/parser.rs b/crates/lang/src/tests/parser.rs index 09db6f0c..5ab777e9 100644 --- a/crates/lang/src/tests/parser.rs +++ b/crates/lang/src/tests/parser.rs @@ -77,6 +77,10 @@ fn module() { } fn run() {} + + fn name(user: User) { + user.name + } "#; let len = code.chars().count(); @@ -725,6 +729,36 @@ fn module() { return_annotation: None, return_type: (), }, + ast::UntypedDefinition::Fn { + arguments: vec![ast::Arg { + arg_name: ast::ArgName::Named { + name: "user".to_string(), + location: Span::new(SrcId::empty(), 1425..1429), + }, + location: Span::new(SrcId::empty(), 1425..1435), + annotation: Some(ast::Annotation::Constructor { + location: Span::new(SrcId::empty(), 1431..1435), + module: None, + name: "User".to_string(), + arguments: vec![], + },), + tipo: (), + },], + body: expr::UntypedExpr::FieldAccess { + location: Span::new(SrcId::empty(), 1455..1464), + label: "name".to_string(), + container: Box::new(expr::UntypedExpr::Var { + location: Span::new(SrcId::empty(), 1455..1459), + name: "user".to_string(), + }), + }, + doc: None, + location: Span::new(SrcId::empty(), 1417..1478), + name: "name".to_string(), + public: false, + return_annotation: None, + return_type: (), + }, ] }, );