From eea94fc9a47be7d3492b131d03a7a67a8369f709 Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 30 Jun 2023 00:20:12 -0400 Subject: [PATCH] feat: move anon fn, let, and expect --- crates/aiken-lang/src/parser.rs | 214 +----------------- .../src/parser/expr/anonymous_function.rs | 56 +++++ .../aiken-lang/src/parser/expr/assignment.rs | 45 ++++ crates/aiken-lang/src/parser/expr/mod.rs | 58 +---- crates/aiken-lang/src/parser/pattern/mod.rs | 195 ++++++++++++++++ 5 files changed, 303 insertions(+), 265 deletions(-) create mode 100644 crates/aiken-lang/src/parser/expr/anonymous_function.rs create mode 100644 crates/aiken-lang/src/parser/expr/assignment.rs diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 17e78680..84ef1809 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -11,6 +11,7 @@ mod utils; pub use annotation::parser as annotation; pub use definitions::parser as definitions; pub use expr::parser as expression; +pub use pattern::parser as pattern; use crate::ast::{self, BinOp, Span}; use chumsky::{chain::Chain, prelude::*}; @@ -96,32 +97,6 @@ pub fn module( Ok((module, extra)) } -pub fn anon_fn_param_parser() -> impl Parser { - // TODO: return a better error when a label is provided `UnexpectedLabel` - choice(( - select! {Token::DiscardName {name} => name}.map_with_span(|name, span| { - ast::ArgName::Discarded { - label: name.clone(), - name, - location: span, - } - }), - select! {Token::Name {name} => name}.map_with_span(|name, span| ast::ArgName::Named { - label: name.clone(), - name, - location: span, - is_validator_param: false, - }), - )) - .then(just(Token::Colon).ignore_then(annotation()).or_not()) - .map_with_span(|(arg_name, annotation), span| ast::Arg { - location: span, - annotation, - tipo: (), - arg_name, - }) -} - pub fn when_clause_guard_parser() -> impl Parser, Error = ParseError> { recursive(|r| { let var_parser = select! { @@ -237,190 +212,3 @@ pub fn when_clause_guard_parser() -> impl Parser, Er }) }) } - -pub fn pattern_parser() -> impl Parser { - recursive(|r| { - let record_constructor_pattern_arg_parser = choice(( - select! {Token::Name {name} => name} - .then_ignore(just(Token::Colon)) - .then(r.clone()) - .map_with_span(|(name, pattern), span| ast::CallArg { - location: span, - label: Some(name), - value: pattern, - }), - select! {Token::Name{name} => name}.map_with_span(|name, span| ast::CallArg { - location: span, - value: ast::UntypedPattern::Var { - name: name.clone(), - location: span, - }, - label: Some(name), - }), - )) - .separated_by(just(Token::Comma)) - .allow_trailing() - .then( - just(Token::DotDot) - .then_ignore(just(Token::Comma).or_not()) - .ignored() - .or_not(), - ) - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)); - - let tuple_constructor_pattern_arg_parser = r - .clone() - .map(|pattern| ast::CallArg { - location: pattern.location(), - value: pattern, - label: None, - }) - .separated_by(just(Token::Comma)) - .allow_trailing() - .then( - just(Token::DotDot) - .then_ignore(just(Token::Comma).or_not()) - .ignored() - .or_not(), - ) - .delimited_by(just(Token::LeftParen), just(Token::RightParen)); - - let constructor_pattern_args_parser = choice(( - record_constructor_pattern_arg_parser.map(|a| (a, true)), - tuple_constructor_pattern_arg_parser.map(|a| (a, false)), - )) - .or_not() - .map(|opt_args| { - opt_args - .map(|((a, b), c)| (a, b.is_some(), c)) - .unwrap_or_else(|| (vec![], false, false)) - }); - - let constructor_pattern_parser = - select! {Token::UpName { name } => name}.then(constructor_pattern_args_parser); - - choice(( - select! { Token::Name {name} => name } - .then( - just(Token::Dot) - .ignore_then(constructor_pattern_parser.clone()) - .or_not(), - ) - .map_with_span(|(name, opt_pattern), span| { - if let Some((c_name, (arguments, with_spread, is_record))) = opt_pattern { - ast::UntypedPattern::Constructor { - is_record, - location: span, - name: c_name, - arguments, - module: Some(name), - constructor: (), - with_spread, - tipo: (), - } - } else { - ast::UntypedPattern::Var { - location: span, - name, - } - } - }), - constructor_pattern_parser.map_with_span( - |(name, (arguments, with_spread, is_record)), span| { - ast::UntypedPattern::Constructor { - is_record, - location: span, - name, - arguments, - module: None, - constructor: (), - with_spread, - tipo: (), - } - }, - ), - select! {Token::DiscardName {name} => name}.map_with_span(|name, span| { - ast::UntypedPattern::Discard { - name, - location: span, - } - }), - select! {Token::Int {value, base} => (value, base)}.map_with_span( - |(value, base), span| ast::UntypedPattern::Int { - location: span, - value, - base, - }, - ), - r.clone() - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by( - choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), - just(Token::RightParen), - ) - .map_with_span(|elems, span| ast::UntypedPattern::Tuple { - location: span, - elems, - }), - just(Token::LeftSquare) - .ignore_then(r.clone().separated_by(just(Token::Comma))) - .then(choice(( - just(Token::Comma) - .ignore_then(just(Token::DotDot).ignore_then(r.clone().or_not()).or_not()), - just(Token::Comma).ignored().or_not().map(|_| None), - ))) - .then_ignore(just(Token::RightSquare)) - .validate(|(elements, tail), span: Span, emit| { - let tail = match tail { - // There is a tail and it has a Pattern::Var or Pattern::Discard - Some(Some( - pat @ (ast::UntypedPattern::Var { .. } - | ast::UntypedPattern::Discard { .. }), - )) => Some(pat), - Some(Some(pat)) => { - emit(ParseError::expected_input_found( - pat.location(), - None, - Some(error::Pattern::Match), - )); - - Some(pat) - } - // There is a tail but it has no content, implicit discard - Some(None) => Some(ast::UntypedPattern::Discard { - location: Span { - start: span.end - 1, - end: span.end, - }, - name: "_".to_string(), - }), - // No tail specified - None => None, - }; - - ast::UntypedPattern::List { - location: span, - elements, - tail: tail.map(Box::new), - } - }), - )) - .then( - just(Token::As) - .ignore_then(select! { Token::Name {name} => name}) - .or_not(), - ) - .map_with_span(|(pattern, opt_as), span| { - if let Some(name) = opt_as { - ast::UntypedPattern::Assign { - name, - location: span, - pattern: Box::new(pattern), - } - } else { - pattern - } - }) - }) -} diff --git a/crates/aiken-lang/src/parser/expr/anonymous_function.rs b/crates/aiken-lang/src/parser/expr/anonymous_function.rs new file mode 100644 index 00000000..9e8ae29d --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/anonymous_function.rs @@ -0,0 +1,56 @@ +use chumsky::prelude::*; + +use crate::{ + ast, + expr::{FnStyle, UntypedExpr}, + parser::{annotation, error::ParseError, token::Token}, +}; + +pub fn parser<'a>( + seq_r: Recursive<'a, Token, UntypedExpr, ParseError>, +) -> impl Parser + 'a { + just(Token::Fn) + .ignore_then( + params() + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftParen), just(Token::RightParen)), + ) + .then(just(Token::RArrow).ignore_then(annotation()).or_not()) + .then(seq_r.delimited_by(just(Token::LeftBrace), just(Token::RightBrace))) + .map_with_span( + |((arguments, return_annotation), body), span| UntypedExpr::Fn { + arguments, + body: Box::new(body), + location: span, + fn_style: FnStyle::Plain, + return_annotation, + }, + ) +} + +pub fn params() -> impl Parser { + // TODO: return a better error when a label is provided `UnexpectedLabel` + choice(( + select! {Token::DiscardName {name} => name}.map_with_span(|name, span| { + ast::ArgName::Discarded { + label: name.clone(), + name, + location: span, + } + }), + select! {Token::Name {name} => name}.map_with_span(|name, span| ast::ArgName::Named { + label: name.clone(), + name, + location: span, + is_validator_param: false, + }), + )) + .then(just(Token::Colon).ignore_then(annotation()).or_not()) + .map_with_span(|(arg_name, annotation), span| ast::Arg { + location: span, + annotation, + tipo: (), + arg_name, + }) +} diff --git a/crates/aiken-lang/src/parser/expr/assignment.rs b/crates/aiken-lang/src/parser/expr/assignment.rs new file mode 100644 index 00000000..4b25431a --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/assignment.rs @@ -0,0 +1,45 @@ +use chumsky::prelude::*; + +use crate::{ + ast, + expr::UntypedExpr, + parser::{annotation, error::ParseError, pattern, token::Token}, +}; + +pub fn let_( + r: Recursive<'_, Token, UntypedExpr, ParseError>, +) -> impl Parser + '_ { + assignment(r, Token::Let) +} + +pub fn expect( + r: Recursive<'_, Token, UntypedExpr, ParseError>, +) -> impl Parser + '_ { + assignment(r, Token::Expect) +} + +fn assignment( + r: Recursive<'_, Token, UntypedExpr, ParseError>, + keyword: Token, +) -> impl Parser + '_ { + let kind = if keyword == Token::Let { + ast::AssignmentKind::Let + } else { + ast::AssignmentKind::Expect + }; + + just(keyword) + .ignore_then(pattern()) + .then(just(Token::Colon).ignore_then(annotation()).or_not()) + .then_ignore(just(Token::Equal)) + .then(r.clone()) + .map_with_span( + move |((pattern, annotation), value), span| UntypedExpr::Assignment { + location: span, + value: Box::new(value), + pattern, + kind, + annotation, + }, + ) +} diff --git a/crates/aiken-lang/src/parser/expr/mod.rs b/crates/aiken-lang/src/parser/expr/mod.rs index 41d1fb50..df8ac0fe 100644 --- a/crates/aiken-lang/src/parser/expr/mod.rs +++ b/crates/aiken-lang/src/parser/expr/mod.rs @@ -1,6 +1,8 @@ use chumsky::prelude::*; use vec1::{vec1, Vec1}; +pub mod anonymous_function; +pub mod assignment; mod block; mod bytearray; mod if_else; @@ -13,6 +15,7 @@ pub mod string; mod tuple; mod var; +pub use anonymous_function::parser as anonymous_function; pub use block::parser as block; pub use bytearray::parser as bytearray; pub use if_else::parser as if_else; @@ -49,25 +52,6 @@ pub fn parser( }), }); - let anon_fn_parser = just(Token::Fn) - .ignore_then( - anon_fn_param_parser() - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftParen), just(Token::RightParen)), - ) - .then(just(Token::RArrow).ignore_then(annotation()).or_not()) - .then(seq_r.delimited_by(just(Token::LeftBrace), just(Token::RightBrace))) - .map_with_span( - |((arguments, return_annotation), body), span| UntypedExpr::Fn { - arguments, - body: Box::new(body), - location: span, - fn_style: FnStyle::Plain, - return_annotation, - }, - ); - let anon_binop_parser = select! { Token::EqualEqual => BinOp::Eq, Token::NotEqual => BinOp::NotEq, @@ -216,36 +200,6 @@ pub fn parser( clauses, }); - let let_parser = just(Token::Let) - .ignore_then(pattern_parser()) - .then(just(Token::Colon).ignore_then(annotation()).or_not()) - .then_ignore(just(Token::Equal)) - .then(r.clone()) - .map_with_span( - |((pattern, annotation), value), span| UntypedExpr::Assignment { - location: span, - value: Box::new(value), - pattern, - kind: ast::AssignmentKind::Let, - annotation, - }, - ); - - let expect_parser = just(Token::Expect) - .ignore_then(pattern_parser()) - .then(just(Token::Colon).ignore_then(annotation()).or_not()) - .then_ignore(just(Token::Equal)) - .then(r.clone()) - .map_with_span( - |((pattern, annotation), value), span| UntypedExpr::Assignment { - location: span, - value: Box::new(value), - pattern, - kind: ast::AssignmentKind::Expect, - annotation, - }, - ); - let expr_unit_parser = choice(( string(), int(), @@ -256,12 +210,12 @@ pub fn parser( tuple(r), bytearray(), list(r), - anon_fn_parser, + anonymous_function(seq_r), anon_binop_parser, block(seq_r), when_parser, - let_parser, - expect_parser, + assignment::let_(r), + assignment::expect(r), if_else(seq_r, r), )); diff --git a/crates/aiken-lang/src/parser/pattern/mod.rs b/crates/aiken-lang/src/parser/pattern/mod.rs index e69de29b..6a3f1b68 100644 --- a/crates/aiken-lang/src/parser/pattern/mod.rs +++ b/crates/aiken-lang/src/parser/pattern/mod.rs @@ -0,0 +1,195 @@ +use chumsky::prelude::*; + +use crate::ast; + +use super::{ + error::{self, ParseError}, + token::Token, +}; + +pub fn parser() -> impl Parser { + recursive(|r| { + let record_constructor_pattern_arg_parser = choice(( + select! {Token::Name {name} => name} + .then_ignore(just(Token::Colon)) + .then(r.clone()) + .map_with_span(|(name, pattern), span| ast::CallArg { + location: span, + label: Some(name), + value: pattern, + }), + select! {Token::Name{name} => name}.map_with_span(|name, span| ast::CallArg { + location: span, + value: ast::UntypedPattern::Var { + name: name.clone(), + location: span, + }, + label: Some(name), + }), + )) + .separated_by(just(Token::Comma)) + .allow_trailing() + .then( + just(Token::DotDot) + .then_ignore(just(Token::Comma).or_not()) + .ignored() + .or_not(), + ) + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)); + + let tuple_constructor_pattern_arg_parser = r + .clone() + .map(|pattern| ast::CallArg { + location: pattern.location(), + value: pattern, + label: None, + }) + .separated_by(just(Token::Comma)) + .allow_trailing() + .then( + just(Token::DotDot) + .then_ignore(just(Token::Comma).or_not()) + .ignored() + .or_not(), + ) + .delimited_by(just(Token::LeftParen), just(Token::RightParen)); + + let constructor_pattern_args_parser = choice(( + record_constructor_pattern_arg_parser.map(|a| (a, true)), + tuple_constructor_pattern_arg_parser.map(|a| (a, false)), + )) + .or_not() + .map(|opt_args| { + opt_args + .map(|((a, b), c)| (a, b.is_some(), c)) + .unwrap_or_else(|| (vec![], false, false)) + }); + + let constructor_pattern_parser = + select! {Token::UpName { name } => name}.then(constructor_pattern_args_parser); + + choice(( + select! { Token::Name {name} => name } + .then( + just(Token::Dot) + .ignore_then(constructor_pattern_parser.clone()) + .or_not(), + ) + .map_with_span(|(name, opt_pattern), span| { + if let Some((c_name, (arguments, with_spread, is_record))) = opt_pattern { + ast::UntypedPattern::Constructor { + is_record, + location: span, + name: c_name, + arguments, + module: Some(name), + constructor: (), + with_spread, + tipo: (), + } + } else { + ast::UntypedPattern::Var { + location: span, + name, + } + } + }), + constructor_pattern_parser.map_with_span( + |(name, (arguments, with_spread, is_record)), span| { + ast::UntypedPattern::Constructor { + is_record, + location: span, + name, + arguments, + module: None, + constructor: (), + with_spread, + tipo: (), + } + }, + ), + select! {Token::DiscardName {name} => name}.map_with_span(|name, span| { + ast::UntypedPattern::Discard { + name, + location: span, + } + }), + select! {Token::Int {value, base} => (value, base)}.map_with_span( + |(value, base), span| ast::UntypedPattern::Int { + location: span, + value, + base, + }, + ), + r.clone() + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by( + choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), + just(Token::RightParen), + ) + .map_with_span(|elems, span| ast::UntypedPattern::Tuple { + location: span, + elems, + }), + just(Token::LeftSquare) + .ignore_then(r.clone().separated_by(just(Token::Comma))) + .then(choice(( + just(Token::Comma) + .ignore_then(just(Token::DotDot).ignore_then(r.clone().or_not()).or_not()), + just(Token::Comma).ignored().or_not().map(|_| None), + ))) + .then_ignore(just(Token::RightSquare)) + .validate(|(elements, tail), span: ast::Span, emit| { + let tail = match tail { + // There is a tail and it has a Pattern::Var or Pattern::Discard + Some(Some( + pat @ (ast::UntypedPattern::Var { .. } + | ast::UntypedPattern::Discard { .. }), + )) => Some(pat), + Some(Some(pat)) => { + emit(ParseError::expected_input_found( + pat.location(), + None, + Some(error::Pattern::Match), + )); + + Some(pat) + } + // There is a tail but it has no content, implicit discard + Some(None) => Some(ast::UntypedPattern::Discard { + location: ast::Span { + start: span.end - 1, + end: span.end, + }, + name: "_".to_string(), + }), + // No tail specified + None => None, + }; + + ast::UntypedPattern::List { + location: span, + elements, + tail: tail.map(Box::new), + } + }), + )) + .then( + just(Token::As) + .ignore_then(select! { Token::Name {name} => name}) + .or_not(), + ) + .map_with_span(|(pattern, opt_as), span| { + if let Some(name) = opt_as { + ast::UntypedPattern::Assign { + name, + location: span, + pattern: Box::new(pattern), + } + } else { + pattern + } + }) + }) +}