diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index d4af7017..a3813a82 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -1,4 +1,5 @@ mod annotation; +pub mod chain; pub mod definition; pub mod error; pub mod expr; diff --git a/crates/aiken-lang/src/parser/chain/call.rs b/crates/aiken-lang/src/parser/chain/call.rs new file mode 100644 index 00000000..e2e0c0dd --- /dev/null +++ b/crates/aiken-lang/src/parser/chain/call.rs @@ -0,0 +1,38 @@ +use chumsky::prelude::*; + +use super::{Chain, ParserArg}; +use crate::{ + ast, + expr::UntypedExpr, + parser::{token::Token, ParseError}, +}; + +pub(crate) fn parser<'a>( + expression: Recursive<'a, Token, UntypedExpr, ParseError>, +) -> impl Parser + 'a { + choice(( + select! { Token::Name { name } => name } + .then_ignore(just(Token::Colon)) + .or_not() + .then(expression) + .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)) + .allow_trailing() + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .map_with_span(Chain::Call) +} diff --git a/crates/aiken-lang/src/parser/chain/field_access.rs b/crates/aiken-lang/src/parser/chain/field_access.rs new file mode 100644 index 00000000..14f84bce --- /dev/null +++ b/crates/aiken-lang/src/parser/chain/field_access.rs @@ -0,0 +1,30 @@ +use chumsky::prelude::*; + +use super::Chain; +use crate::{ + expr::UntypedExpr, + parser::{token::Token, ParseError}, +}; + +pub(crate) fn parser() -> impl Parser { + just(Token::Dot) + .ignore_then(select! { + Token::Name { name } => name, + }) + .map_with_span(Chain::FieldAccess) +} + +pub(crate) fn constructor() -> impl Parser { + select! {Token::Name { name } => name} + .map_with_span(|module, span| (module, span)) + .then_ignore(just(Token::Dot)) + .then(select! {Token::UpName { name } => name}) + .map_with_span(|((module, m_span), name), span| UntypedExpr::FieldAccess { + location: span, + label: name, + container: Box::new(UntypedExpr::Var { + location: m_span, + name: module, + }), + }) +} diff --git a/crates/aiken-lang/src/parser/chain/mod.rs b/crates/aiken-lang/src/parser/chain/mod.rs new file mode 100644 index 00000000..baad4516 --- /dev/null +++ b/crates/aiken-lang/src/parser/chain/mod.rs @@ -0,0 +1,22 @@ +use crate::ast::{self, Span}; +use crate::expr::UntypedExpr; + +pub(crate) mod call; +pub(crate) mod field_access; +pub(crate) mod tuple_index; + +pub(crate) enum Chain { + Call(Vec, Span), + FieldAccess(String, Span), + TupleIndex(usize, Span), +} + +// Parsing a function call into the appropriate structure +#[derive(Debug)] +pub(crate) enum ParserArg { + Arg(Box>), + Hole { + location: Span, + label: Option, + }, +} diff --git a/crates/aiken-lang/src/parser/chain/tuple_index.rs b/crates/aiken-lang/src/parser/chain/tuple_index.rs new file mode 100644 index 00000000..e3ce7f24 --- /dev/null +++ b/crates/aiken-lang/src/parser/chain/tuple_index.rs @@ -0,0 +1,23 @@ +use chumsky::prelude::*; + +use super::Chain; +use crate::parser::{token::Token, ParseError}; + +pub(crate) fn parser() -> impl Parser { + just(Token::Dot) + .ignore_then(select! { + Token::Ordinal { index } => index, + }) + .validate(|index, span, emit| { + if index < 1 { + emit(ParseError::invalid_tuple_index( + span, + index.to_string(), + None, + )); + Chain::TupleIndex(0, span) + } else { + Chain::TupleIndex(index as usize - 1, span) + } + }) +} diff --git a/crates/aiken-lang/src/parser/expr/chained.rs b/crates/aiken-lang/src/parser/expr/chained.rs new file mode 100644 index 00000000..2a12e96d --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/chained.rs @@ -0,0 +1,133 @@ +use chumsky::prelude::*; + +use super::anonymous_binop::parser as anonymous_binop; +use super::anonymous_function::parser as anonymous_function; +use super::assignment; +use super::block::parser as block; +use super::bytearray::parser as bytearray; +use super::if_else::parser as if_else; +use super::int::parser as int; +use super::list::parser as list; +use super::record::parser as record; +use super::record_update::parser as record_update; +use super::string::parser as string; +use super::tuple::parser as tuple; +use super::var::parser as var; +use super::when::parser as when; + +use crate::{ + ast::{self, Span}, + expr::{FnStyle, UntypedExpr}, + parser::{ + chain::{ + call::parser as call, field_access, tuple_index::parser as tuple_index, Chain, + ParserArg, + }, + error::ParseError, + token::Token, + }, +}; + +pub fn parser<'a>( + sequence: Recursive<'a, Token, UntypedExpr, ParseError>, + expression: Recursive<'a, Token, UntypedExpr, ParseError>, +) -> impl Parser + 'a { + let chain = choice(( + tuple_index(), + field_access::parser(), + call(expression.clone()), + )); + chain_start(sequence, expression) + .then(chain.repeated()) + .foldl(|expr, chain| match chain { + Chain::Call(args, span) => { + let mut holes = Vec::new(); + + let args = args + .into_iter() + .enumerate() + .map(|(index, a)| match a { + ParserArg::Arg(arg) => *arg, + ParserArg::Hole { location, label } => { + let name = format!("{}__{index}", ast::CAPTURE_VARIABLE); + + holes.push(ast::Arg { + location: Span::empty(), + annotation: None, + arg_name: ast::ArgName::Named { + label: name.clone(), + name, + location: Span::empty(), + is_validator_param: false, + }, + tipo: (), + }); + + ast::CallArg { + label, + location, + value: UntypedExpr::Var { + location, + name: format!("{}__{index}", ast::CAPTURE_VARIABLE), + }, + } + } + }) + .collect(); + + let call = UntypedExpr::Call { + location: expr.location().union(span), + fun: Box::new(expr), + arguments: args, + }; + + if holes.is_empty() { + call + } else { + UntypedExpr::Fn { + location: call.location(), + fn_style: FnStyle::Capture, + arguments: holes, + body: Box::new(call), + return_annotation: None, + } + } + } + + Chain::FieldAccess(label, span) => UntypedExpr::FieldAccess { + location: expr.location().union(span), + label, + container: Box::new(expr), + }, + + Chain::TupleIndex(index, span) => UntypedExpr::TupleIndex { + location: expr.location().union(span), + index, + tuple: Box::new(expr), + }, + }) +} + +pub fn chain_start<'a>( + sequence: Recursive<'a, Token, UntypedExpr, ParseError>, + expression: Recursive<'a, Token, UntypedExpr, ParseError>, +) -> impl Parser + 'a { + choice(( + string(), + int(), + record_update(expression.clone()), + record(expression.clone()), + field_access::constructor(), + var(), + tuple(expression.clone()), + bytearray(), + list(expression.clone()), + anonymous_function(sequence.clone()), + anonymous_binop(), + block(sequence.clone()), + when(expression.clone()), + assignment::let_(expression.clone()), + assignment::expect(expression.clone()), + if_else(sequence, expression.clone()), + )) +} diff --git a/crates/aiken-lang/src/parser/expr/mod.rs b/crates/aiken-lang/src/parser/expr/mod.rs index 37d676bf..96d361f2 100644 --- a/crates/aiken-lang/src/parser/expr/mod.rs +++ b/crates/aiken-lang/src/parser/expr/mod.rs @@ -6,6 +6,7 @@ pub mod anonymous_function; pub mod assignment; mod block; pub(crate) mod bytearray; +mod chained; mod if_else; mod int; mod list; @@ -17,10 +18,10 @@ mod tuple; mod var; pub mod when; -use anonymous_binop::parser as anonymous_binop; pub use anonymous_function::parser as anonymous_function; pub use block::parser as block; pub use bytearray::parser as bytearray; +pub use chained::parser as chained; pub use if_else::parser as if_else; pub use int::parser as int; pub use list::parser as list; @@ -32,195 +33,22 @@ pub use tuple::parser as tuple; pub use var::parser as var; pub use when::parser as when; -use crate::{ - ast::{self, Span}, - expr::{FnStyle, UntypedExpr}, -}; - use super::{error::ParseError, token::Token}; +use crate::{ast, expr::UntypedExpr}; pub fn parser( sequence: Recursive<'_, Token, UntypedExpr, ParseError>, ) -> impl Parser + '_ { recursive(|expression| { - let field_access_constructor = select! {Token::Name { name } => name} - .map_with_span(|module, span| (module, span)) - .then_ignore(just(Token::Dot)) - .then(select! {Token::UpName { name } => name}) - .map_with_span(|((module, m_span), name), span| UntypedExpr::FieldAccess { - location: span, - label: name, - container: Box::new(UntypedExpr::Var { - location: m_span, - name: module, - }), - }); - - let expr_unit_parser = choice(( - string(), - int(), - record_update(expression.clone()), - record(expression.clone()), - field_access_constructor, - var(), - tuple(expression.clone()), - bytearray(), - list(expression.clone()), - anonymous_function(sequence.clone()), - anonymous_binop(), - block(sequence.clone()), - when(expression.clone()), - assignment::let_(expression.clone()), - assignment::expect(expression.clone()), - if_else(sequence, expression.clone()), - )); - - // Parsing a function call into the appropriate structure - #[derive(Debug)] - enum ParserArg { - Arg(Box>), - Hole { - location: Span, - label: Option, - }, - } - - enum Chain { - Call(Vec, Span), - FieldAccess(String, Span), - TupleIndex(usize, Span), - } - - let field_access_parser = just(Token::Dot) - .ignore_then(select! { - Token::Name { name } => name, - }) - .map_with_span(Chain::FieldAccess); - - let tuple_index_parser = just(Token::Dot) - .ignore_then(select! { - Token::Ordinal { index } => index, - }) - .validate(|index, span, emit| { - if index < 1 { - emit(ParseError::invalid_tuple_index( - span, - index.to_string(), - None, - )); - Chain::TupleIndex(0, span) - } else { - Chain::TupleIndex(index as usize - 1, span) - } - }); - - let call_parser = choice(( - select! { Token::Name { name } => name } - .then_ignore(just(Token::Colon)) - .or_not() - .then(expression) - .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)) - .allow_trailing() - .delimited_by(just(Token::LeftParen), just(Token::RightParen)) - .map_with_span(Chain::Call); - - let chain = choice((tuple_index_parser, field_access_parser, call_parser)); - - let chained = expr_unit_parser - .then(chain.repeated()) - .foldl(|expr, chain| match chain { - Chain::Call(args, span) => { - let mut holes = Vec::new(); - - let args = args - .into_iter() - .enumerate() - .map(|(index, a)| match a { - ParserArg::Arg(arg) => *arg, - ParserArg::Hole { location, label } => { - let name = format!("{}__{index}", ast::CAPTURE_VARIABLE); - - holes.push(ast::Arg { - location: Span::empty(), - annotation: None, - arg_name: ast::ArgName::Named { - label: name.clone(), - name, - location: Span::empty(), - is_validator_param: false, - }, - tipo: (), - }); - - ast::CallArg { - label, - location, - value: UntypedExpr::Var { - location, - name: format!("{}__{index}", ast::CAPTURE_VARIABLE), - }, - } - } - }) - .collect(); - - let call = UntypedExpr::Call { - location: expr.location().union(span), - fun: Box::new(expr), - arguments: args, - }; - - if holes.is_empty() { - call - } else { - UntypedExpr::Fn { - location: call.location(), - fn_style: FnStyle::Capture, - arguments: holes, - body: Box::new(call), - return_annotation: None, - } - } - } - - Chain::FieldAccess(label, span) => UntypedExpr::FieldAccess { - location: expr.location().union(span), - label, - container: Box::new(expr), - }, - - Chain::TupleIndex(index, span) => UntypedExpr::TupleIndex { - location: expr.location().union(span), - index, - tuple: Box::new(expr), - }, - }); - - let debug = chained.then(just(Token::Question).or_not()).map_with_span( - |(value, token), location| match token { + let chained_debugged = chained(sequence, expression) + .then(just(Token::Question).or_not()) + .map_with_span(|(value, token), location| match token { Some(_) => UntypedExpr::TraceIfFalse { value: Box::new(value), location, }, None => value, - }, - ); + }); // Negate let op = choice(( @@ -244,7 +72,7 @@ pub fn parser( let unary = op .map_with_span(|op, span| (op, span)) .repeated() - .then(debug) + .then(chained_debugged) .foldr(|(un_op, span), value| UntypedExpr::UnOp { op: un_op, location: span.union(value.location()),