From fc580d4fa0e25d1045f7d951e19ddef1c9ef1990 Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 19 Jun 2023 20:34:51 -0400 Subject: [PATCH 01/31] feat(parser): move definitions to their own modules --- crates/aiken-lang/src/parser.rs | 314 +----------------- .../src/parser/definitions/constant.rs | 27 ++ .../src/parser/definitions/data_type.rs | 77 +++++ .../src/parser/definitions/function.rs | 48 +++ .../src/parser/definitions/import.rs | 57 ++++ .../aiken-lang/src/parser/definitions/mod.rs | 15 + .../aiken-lang/src/parser/definitions/test.rs | 36 ++ .../src/parser/definitions/type_alias.rs | 25 ++ .../src/parser/definitions/validator.rs | 58 ++++ crates/aiken-lang/src/parser/module.rs | 19 ++ crates/aiken-lang/src/parser/utils.rs | 7 + 11 files changed, 374 insertions(+), 309 deletions(-) create mode 100644 crates/aiken-lang/src/parser/definitions/constant.rs create mode 100644 crates/aiken-lang/src/parser/definitions/data_type.rs create mode 100644 crates/aiken-lang/src/parser/definitions/function.rs create mode 100644 crates/aiken-lang/src/parser/definitions/import.rs create mode 100644 crates/aiken-lang/src/parser/definitions/mod.rs create mode 100644 crates/aiken-lang/src/parser/definitions/test.rs create mode 100644 crates/aiken-lang/src/parser/definitions/type_alias.rs create mode 100644 crates/aiken-lang/src/parser/definitions/validator.rs create mode 100644 crates/aiken-lang/src/parser/module.rs create mode 100644 crates/aiken-lang/src/parser/utils.rs diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 408511f96..6dba0a955 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -1,13 +1,13 @@ +pub mod definitions; pub mod error; pub mod extra; pub mod lexer; +mod module; pub mod token; +mod utils; use crate::{ - ast::{ - self, BinOp, ByteArrayFormatPreference, Span, TraceKind, UnOp, UntypedDefinition, - CAPTURE_VARIABLE, - }, + ast::{self, BinOp, ByteArrayFormatPreference, Span, TraceKind, UnOp, CAPTURE_VARIABLE}, expr, }; use chumsky::{chain::Chain, prelude::*}; @@ -79,7 +79,7 @@ pub fn module( }); let definitions = - module_parser().parse(chumsky::Stream::from_iter(span(tokens.len(), 1), tokens))?; + module::parser().parse(chumsky::Stream::from_iter(span(tokens.len()), tokens))?; let module = ast::UntypedModule { kind, @@ -92,306 +92,6 @@ pub fn module( Ok((module, extra)) } -fn module_parser() -> impl Parser, Error = ParseError> { - choice(( - import_parser(), - data_parser(), - type_alias_parser(), - validator_parser(), - fn_parser(), - test_parser(), - constant_parser(), - )) - .repeated() - .then_ignore(end()) -} - -pub fn import_parser() -> impl Parser { - let unqualified_import = choice(( - select! {Token::Name { name } => name}.then( - just(Token::As) - .ignore_then(select! {Token::Name { name } => name}) - .or_not(), - ), - select! {Token::UpName { name } => name}.then( - just(Token::As) - .ignore_then(select! {Token::UpName { name } => name}) - .or_not(), - ), - )) - .map_with_span(|(name, as_name), span| ast::UnqualifiedImport { - name, - location: span, - as_name, - layer: Default::default(), - }); - - let unqualified_imports = just(Token::Dot) - .ignore_then( - unqualified_import - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), - ) - .or_not(); - - let as_name = just(Token::As) - .ignore_then(select! {Token::Name { name } => name}) - .or_not(); - - let module_path = select! {Token::Name { name } => name} - .separated_by(just(Token::Slash)) - .then(unqualified_imports) - .then(as_name); - - just(Token::Use).ignore_then(module_path).map_with_span( - |((module, unqualified), as_name), span| { - ast::UntypedDefinition::Use(ast::Use { - module, - as_name, - unqualified: unqualified.unwrap_or_default(), - package: (), - location: span, - }) - }, - ) -} - -pub fn data_parser() -> impl Parser { - let unlabeled_constructor_type_args = type_parser() - .map_with_span(|annotation, span| ast::RecordConstructorArg { - label: None, - annotation, - tipo: (), - doc: None, - location: span, - }) - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftParen), just(Token::RightParen)); - - let constructors = select! {Token::UpName { name } => name} - .then( - choice(( - labeled_constructor_type_args(), - unlabeled_constructor_type_args, - )) - .or_not(), - ) - .map_with_span(|(name, arguments), span| ast::RecordConstructor { - location: span, - arguments: arguments.unwrap_or_default(), - name, - doc: None, - sugar: false, - }) - .repeated() - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)); - - let record_sugar = labeled_constructor_type_args().map_with_span(|arguments, span| { - vec![ast::RecordConstructor { - location: span, - arguments, - doc: None, - name: String::from("_replace"), - sugar: true, - }] - }); - - pub_parser() - .then(just(Token::Opaque).ignored().or_not()) - .or_not() - .then(type_name_with_args()) - .then(choice((constructors, record_sugar))) - .map_with_span(|((pub_opaque, (name, parameters)), constructors), span| { - ast::UntypedDefinition::DataType(ast::DataType { - location: span, - constructors: constructors - .into_iter() - .map(|mut constructor| { - if constructor.sugar { - constructor.name = name.clone(); - } - - constructor - }) - .collect(), - doc: None, - name, - opaque: pub_opaque - .map(|(_, opt_opaque)| opt_opaque.is_some()) - .unwrap_or(false), - parameters: parameters.unwrap_or_default(), - public: pub_opaque.is_some(), - typed_parameters: vec![], - }) - }) -} - -pub fn type_alias_parser() -> impl Parser { - pub_parser() - .or_not() - .then(type_name_with_args()) - .then_ignore(just(Token::Equal)) - .then(type_parser()) - .map_with_span(|((opt_pub, (alias, parameters)), annotation), span| { - ast::UntypedDefinition::TypeAlias(ast::TypeAlias { - alias, - annotation, - doc: None, - location: span, - parameters: parameters.unwrap_or_default(), - public: opt_pub.is_some(), - tipo: (), - }) - }) -} - -pub fn validator_parser() -> impl Parser { - just(Token::Validator) - .ignore_then( - fn_param_parser(true) - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftParen), just(Token::RightParen)) - .map_with_span(|arguments, span| (arguments, span)) - .or_not(), - ) - .then( - fn_parser() - .repeated() - .at_least(1) - .at_most(2) - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)) - .map(|defs| { - defs.into_iter().map(|def| { - let ast::UntypedDefinition::Fn(fun) = def else { - unreachable!("It should be a fn definition"); - }; - - fun - }) - }), - ) - .map_with_span(|(opt_extra_params, mut functions), span| { - let (params, params_span) = opt_extra_params.unwrap_or(( - vec![], - Span { - start: 0, - end: span.start + "validator".len(), - }, - )); - - ast::UntypedDefinition::Validator(ast::Validator { - doc: None, - fun: functions - .next() - .expect("unwrapping safe because there's 'at_least(1)' function"), - other_fun: functions.next(), - location: Span { - start: span.start, - // capture the span from the optional params - end: params_span.end, - }, - params, - end_position: span.end - 1, - }) - }) -} - -pub fn fn_parser() -> impl Parser { - pub_parser() - .or_not() - .then_ignore(just(Token::Fn)) - .then(select! {Token::Name {name} => name}) - .then( - fn_param_parser(false) - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftParen), just(Token::RightParen)) - .map_with_span(|arguments, span| (arguments, span)), - ) - .then(just(Token::RArrow).ignore_then(type_parser()).or_not()) - .then( - expr_seq_parser() - .or_not() - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), - ) - .map_with_span( - |((((opt_pub, name), (arguments, args_span)), return_annotation), body), span| { - ast::UntypedDefinition::Fn(ast::Function { - arguments, - body: body.unwrap_or_else(|| expr::UntypedExpr::todo(span, None)), - doc: None, - location: Span { - start: span.start, - end: return_annotation - .as_ref() - .map(|l| l.location().end) - .unwrap_or_else(|| args_span.end), - }, - end_position: span.end - 1, - name, - public: opt_pub.is_some(), - return_annotation, - return_type: (), - can_error: true, - }) - }, - ) -} - -pub fn test_parser() -> impl Parser { - just(Token::Bang) - .ignored() - .or_not() - .then_ignore(just(Token::Test)) - .then(select! {Token::Name {name} => name}) - .then_ignore(just(Token::LeftParen)) - .then_ignore(just(Token::RightParen)) - .map_with_span(|name, span| (name, span)) - .then( - expr_seq_parser() - .or_not() - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), - ) - .map_with_span(|(((fail, name), span_end), body), span| { - ast::UntypedDefinition::Test(ast::Function { - arguments: vec![], - body: body.unwrap_or_else(|| expr::UntypedExpr::todo(span, None)), - doc: None, - location: span_end, - end_position: span.end - 1, - name, - public: false, - return_annotation: None, - return_type: (), - can_error: fail.is_some(), - }) - }) -} - -fn constant_parser() -> impl Parser { - pub_parser() - .or_not() - .then_ignore(just(Token::Const)) - .then(select! {Token::Name{name} => name}) - .then(just(Token::Colon).ignore_then(type_parser()).or_not()) - .then_ignore(just(Token::Equal)) - .then(constant_value_parser()) - .map_with_span(|(((public, name), annotation), value), span| { - ast::UntypedDefinition::ModuleConstant(ast::ModuleConstant { - doc: None, - location: span, - public: public.is_some(), - name, - annotation, - value: Box::new(value), - tipo: (), - }) - }) -} - fn constant_value_parser() -> impl Parser { let constant_string_parser = select! {Token::String {value} => value}.map_with_span(|value, span| { @@ -1697,10 +1397,6 @@ pub fn type_name_with_args() -> impl Parser>) ) } -pub fn pub_parser() -> impl Parser { - just(Token::Pub).ignored() -} - pub fn pattern_parser() -> impl Parser { recursive(|r| { let record_constructor_pattern_arg_parser = choice(( diff --git a/crates/aiken-lang/src/parser/definitions/constant.rs b/crates/aiken-lang/src/parser/definitions/constant.rs new file mode 100644 index 000000000..b0c52f120 --- /dev/null +++ b/crates/aiken-lang/src/parser/definitions/constant.rs @@ -0,0 +1,27 @@ +use chumsky::prelude::*; + +use crate::{ + ast, + parser::{error::ParseError, token::Token, utils}, +}; + +pub fn parser() -> impl Parser { + utils::public() + .or_not() + .then_ignore(just(Token::Const)) + .then(select! {Token::Name{name} => name}) + .then(just(Token::Colon).ignore_then(type_parser()).or_not()) + .then_ignore(just(Token::Equal)) + .then(constant_value_parser()) + .map_with_span(|(((public, name), annotation), value), span| { + ast::UntypedDefinition::ModuleConstant(ast::ModuleConstant { + doc: None, + location: span, + public: public.is_some(), + name, + annotation, + value: Box::new(value), + tipo: (), + }) + }) +} diff --git a/crates/aiken-lang/src/parser/definitions/data_type.rs b/crates/aiken-lang/src/parser/definitions/data_type.rs new file mode 100644 index 000000000..b20d328c4 --- /dev/null +++ b/crates/aiken-lang/src/parser/definitions/data_type.rs @@ -0,0 +1,77 @@ +use chumsky::prelude::*; + +use crate::{ + ast, + parser::{error::ParseError, token::Token, utils}, +}; + +pub fn parser() -> impl Parser { + let unlabeled_constructor_type_args = type_parser() + .map_with_span(|annotation, span| ast::RecordConstructorArg { + label: None, + annotation, + tipo: (), + doc: None, + location: span, + }) + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftParen), just(Token::RightParen)); + + let constructors = select! {Token::UpName { name } => name} + .then( + choice(( + labeled_constructor_type_args(), + unlabeled_constructor_type_args, + )) + .or_not(), + ) + .map_with_span(|(name, arguments), span| ast::RecordConstructor { + location: span, + arguments: arguments.unwrap_or_default(), + name, + doc: None, + sugar: false, + }) + .repeated() + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)); + + let record_sugar = labeled_constructor_type_args().map_with_span(|arguments, span| { + vec![ast::RecordConstructor { + location: span, + arguments, + doc: None, + name: String::from("_replace"), + sugar: true, + }] + }); + + utils::public() + .then(just(Token::Opaque).ignored().or_not()) + .or_not() + .then(type_name_with_args()) + .then(choice((constructors, record_sugar))) + .map_with_span(|((pub_opaque, (name, parameters)), constructors), span| { + ast::UntypedDefinition::DataType(ast::DataType { + location: span, + constructors: constructors + .into_iter() + .map(|mut constructor| { + if constructor.sugar { + constructor.name = name.clone(); + } + + constructor + }) + .collect(), + doc: None, + name, + opaque: pub_opaque + .map(|(_, opt_opaque)| opt_opaque.is_some()) + .unwrap_or(false), + parameters: parameters.unwrap_or_default(), + public: pub_opaque.is_some(), + typed_parameters: vec![], + }) + }) +} diff --git a/crates/aiken-lang/src/parser/definitions/function.rs b/crates/aiken-lang/src/parser/definitions/function.rs new file mode 100644 index 000000000..4f3f2e29b --- /dev/null +++ b/crates/aiken-lang/src/parser/definitions/function.rs @@ -0,0 +1,48 @@ +use chumsky::prelude::*; + +use crate::{ + ast, expr, + parser::{error::ParseError, token::Token, utils}, +}; + +pub fn parser() -> impl Parser { + utils::public() + .or_not() + .then_ignore(just(Token::Fn)) + .then(select! {Token::Name {name} => name}) + .then( + fn_param_parser(false) + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .map_with_span(|arguments, span| (arguments, span)), + ) + .then(just(Token::RArrow).ignore_then(type_parser()).or_not()) + .then( + expr_seq_parser() + .or_not() + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), + ) + .map_with_span( + |((((opt_pub, name), (arguments, args_span)), return_annotation), body), span| { + ast::UntypedDefinition::Fn(ast::Function { + arguments, + body: body.unwrap_or_else(|| expr::UntypedExpr::todo(span, None)), + doc: None, + location: ast::Span { + start: span.start, + end: return_annotation + .as_ref() + .map(|l| l.location().end) + .unwrap_or_else(|| args_span.end), + }, + end_position: span.end - 1, + name, + public: opt_pub.is_some(), + return_annotation, + return_type: (), + can_error: true, + }) + }, + ) +} diff --git a/crates/aiken-lang/src/parser/definitions/import.rs b/crates/aiken-lang/src/parser/definitions/import.rs new file mode 100644 index 000000000..af401954d --- /dev/null +++ b/crates/aiken-lang/src/parser/definitions/import.rs @@ -0,0 +1,57 @@ +use chumsky::prelude::*; + +use crate::{ + ast, + parser::{error::ParseError, token::Token}, +}; + +pub fn parser() -> impl Parser { + let unqualified_import = choice(( + select! {Token::Name { name } => name}.then( + just(Token::As) + .ignore_then(select! {Token::Name { name } => name}) + .or_not(), + ), + select! {Token::UpName { name } => name}.then( + just(Token::As) + .ignore_then(select! {Token::UpName { name } => name}) + .or_not(), + ), + )) + .map_with_span(|(name, as_name), span| ast::UnqualifiedImport { + name, + location: span, + as_name, + layer: Default::default(), + }); + + let unqualified_imports = just(Token::Dot) + .ignore_then( + unqualified_import + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), + ) + .or_not(); + + let as_name = just(Token::As) + .ignore_then(select! {Token::Name { name } => name}) + .or_not(); + + let module_path = select! {Token::Name { name } => name} + .separated_by(just(Token::Slash)) + .then(unqualified_imports) + .then(as_name); + + just(Token::Use).ignore_then(module_path).map_with_span( + |((module, unqualified), as_name), span| { + ast::UntypedDefinition::Use(ast::Use { + module, + as_name, + unqualified: unqualified.unwrap_or_default(), + package: (), + location: span, + }) + }, + ) +} diff --git a/crates/aiken-lang/src/parser/definitions/mod.rs b/crates/aiken-lang/src/parser/definitions/mod.rs new file mode 100644 index 000000000..287e89f82 --- /dev/null +++ b/crates/aiken-lang/src/parser/definitions/mod.rs @@ -0,0 +1,15 @@ +mod constant; +mod data_type; +mod function; +mod import; +mod test; +mod type_alias; +mod validator; + +pub use constant::parser as constant; +pub use data_type::parser as data_type; +pub use function::parser as function; +pub use import::parser as import; +pub use test::parser as test; +pub use type_alias::parser as type_alias; +pub use validator::parser as validator; diff --git a/crates/aiken-lang/src/parser/definitions/test.rs b/crates/aiken-lang/src/parser/definitions/test.rs new file mode 100644 index 000000000..f565d551b --- /dev/null +++ b/crates/aiken-lang/src/parser/definitions/test.rs @@ -0,0 +1,36 @@ +use chumsky::prelude::*; + +use crate::{ + ast, expr, + parser::{error::ParseError, token::Token}, +}; + +pub fn parser() -> impl Parser { + just(Token::Bang) + .ignored() + .or_not() + .then_ignore(just(Token::Test)) + .then(select! {Token::Name {name} => name}) + .then_ignore(just(Token::LeftParen)) + .then_ignore(just(Token::RightParen)) + .map_with_span(|name, span| (name, span)) + .then( + expr_seq_parser() + .or_not() + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), + ) + .map_with_span(|(((fail, name), span_end), body), span| { + ast::UntypedDefinition::Test(ast::Function { + arguments: vec![], + body: body.unwrap_or_else(|| expr::UntypedExpr::todo(span, None)), + doc: None, + location: span_end, + end_position: span.end - 1, + name, + public: false, + return_annotation: None, + return_type: (), + can_error: fail.is_some(), + }) + }) +} diff --git a/crates/aiken-lang/src/parser/definitions/type_alias.rs b/crates/aiken-lang/src/parser/definitions/type_alias.rs new file mode 100644 index 000000000..d2a88c1ef --- /dev/null +++ b/crates/aiken-lang/src/parser/definitions/type_alias.rs @@ -0,0 +1,25 @@ +use chumsky::prelude::*; + +use crate::{ + ast, + parser::{error::ParseError, token::Token, utils}, +}; + +pub fn parser() -> impl Parser { + utils::public() + .or_not() + .then(type_name_with_args()) + .then_ignore(just(Token::Equal)) + .then(type_parser()) + .map_with_span(|((opt_pub, (alias, parameters)), annotation), span| { + ast::UntypedDefinition::TypeAlias(ast::TypeAlias { + alias, + annotation, + doc: None, + location: span, + parameters: parameters.unwrap_or_default(), + public: opt_pub.is_some(), + tipo: (), + }) + }) +} diff --git a/crates/aiken-lang/src/parser/definitions/validator.rs b/crates/aiken-lang/src/parser/definitions/validator.rs new file mode 100644 index 000000000..baf56e96d --- /dev/null +++ b/crates/aiken-lang/src/parser/definitions/validator.rs @@ -0,0 +1,58 @@ +use chumsky::prelude::*; + +use crate::{ + ast, + parser::{error::ParseError, token::Token}, +}; + +pub fn parser() -> impl Parser { + just(Token::Validator) + .ignore_then( + fn_param_parser(true) + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .map_with_span(|arguments, span| (arguments, span)) + .or_not(), + ) + .then( + super::function() + .repeated() + .at_least(1) + .at_most(2) + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)) + .map(|defs| { + defs.into_iter().map(|def| { + let ast::UntypedDefinition::Fn(fun) = def else { + unreachable!("It should be a fn definition"); + }; + + fun + }) + }), + ) + .map_with_span(|(opt_extra_params, mut functions), span| { + let (params, params_span) = opt_extra_params.unwrap_or(( + vec![], + ast::Span { + start: 0, + end: span.start + "validator".len(), + }, + )); + + ast::UntypedDefinition::Validator(ast::Validator { + doc: None, + fun: functions + .next() + .expect("unwrapping safe because there's 'at_least(1)' function"), + other_fun: functions.next(), + location: ast::Span { + start: span.start, + // capture the span from the optional params + end: params_span.end, + }, + params, + end_position: span.end - 1, + }) + }) +} diff --git a/crates/aiken-lang/src/parser/module.rs b/crates/aiken-lang/src/parser/module.rs new file mode 100644 index 000000000..2e18b90b8 --- /dev/null +++ b/crates/aiken-lang/src/parser/module.rs @@ -0,0 +1,19 @@ +use chumsky::prelude::*; + +use crate::ast::UntypedDefinition; + +use super::{definitions, error::ParseError, token::Token}; + +pub fn parser() -> impl Parser, Error = ParseError> { + choice(( + definitions::import(), + definitions::data_type(), + definitions::type_alias(), + definitions::validator(), + definitions::function(), + definitions::test(), + definitions::constant(), + )) + .repeated() + .then_ignore(end()) +} diff --git a/crates/aiken-lang/src/parser/utils.rs b/crates/aiken-lang/src/parser/utils.rs new file mode 100644 index 000000000..22be13d81 --- /dev/null +++ b/crates/aiken-lang/src/parser/utils.rs @@ -0,0 +1,7 @@ +use chumsky::prelude::*; + +use super::{error::ParseError, token::Token}; + +pub fn public() -> impl Parser { + just(Token::Pub).ignored() +} From 3339d41fdda5d2620b0f3bb18a51c0a2eab360cf Mon Sep 17 00:00:00 2001 From: rvcas Date: Wed, 21 Jun 2023 16:54:13 -0400 Subject: [PATCH 02/31] feat: finish moving definitions and start exprs --- crates/aiken-lang/src/parser.rs | 441 +++--------------- crates/aiken-lang/src/parser/annotation.rs | 91 ++++ .../src/parser/definitions/constant.rs | 44 +- .../src/parser/definitions/data_type.rs | 23 +- .../src/parser/definitions/function.rs | 53 ++- .../aiken-lang/src/parser/definitions/mod.rs | 2 +- .../aiken-lang/src/parser/definitions/test.rs | 9 +- .../src/parser/definitions/type_alias.rs | 6 +- .../src/parser/definitions/validator.rs | 6 +- crates/aiken-lang/src/parser/expr/mod.rs | 3 + crates/aiken-lang/src/parser/expr/sequence.rs | 36 ++ crates/aiken-lang/src/parser/utils.rs | 90 +++- 12 files changed, 417 insertions(+), 387 deletions(-) create mode 100644 crates/aiken-lang/src/parser/annotation.rs create mode 100644 crates/aiken-lang/src/parser/expr/mod.rs create mode 100644 crates/aiken-lang/src/parser/expr/sequence.rs diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 6dba0a955..4e72a85e1 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -1,19 +1,23 @@ +mod annotation; pub mod definitions; pub mod error; +mod expr; pub mod extra; pub mod lexer; mod module; pub mod token; mod utils; +pub use annotation::parser as annotation; + use crate::{ - ast::{self, BinOp, ByteArrayFormatPreference, Span, TraceKind, UnOp, CAPTURE_VARIABLE}, - expr, + ast::{self, BinOp, ByteArrayFormatPreference, Span, UnOp, CAPTURE_VARIABLE}, + expr::{FnStyle, UntypedExpr}, }; use chumsky::{chain::Chain, prelude::*}; use error::ParseError; use extra::ModuleExtra; -use token::{Base, Token}; +use token::Token; use vec1::{vec1, Vec1}; pub fn module( @@ -74,7 +78,9 @@ pub fn module( Token::NewLine => None, _ => Some((token, *span)), }; + previous_is_newline = current_is_newline; + result }); @@ -92,152 +98,6 @@ pub fn module( Ok((module, extra)) } -fn constant_value_parser() -> impl Parser { - let constant_string_parser = - select! {Token::String {value} => value}.map_with_span(|value, span| { - ast::Constant::String { - location: span, - value, - } - }); - - let constant_int_parser = - select! {Token::Int {value, base} => (value, base)}.map_with_span(|(value, base), span| { - ast::Constant::Int { - location: span, - value, - base, - } - }); - - let constant_bytearray_parser = - bytearray_parser().map_with_span(|(preferred_format, bytes), span| { - ast::Constant::ByteArray { - location: span, - bytes, - preferred_format, - } - }); - - choice(( - constant_string_parser, - constant_int_parser, - constant_bytearray_parser, - )) -} - -pub fn bytearray_parser( -) -> impl Parser), Error = ParseError> { - let bytearray_list_parser = just(Token::Hash) - .ignore_then( - select! {Token::Int {value, base, ..} => (value, base)} - .validate(|(value, base), span, emit| { - let byte: u8 = match value.parse() { - Ok(b) => b, - Err(_) => { - emit(ParseError::expected_input_found( - span, - None, - Some(error::Pattern::Byte), - )); - 0 - } - }; - (byte, base) - }) - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftSquare), just(Token::RightSquare)), - ) - .validate(|bytes, span, emit| { - let base = bytes.iter().fold(Ok(None), |acc, (_, base)| match acc { - Ok(None) => Ok(Some(base)), - Ok(Some(previous_base)) if previous_base == base => Ok(Some(base)), - _ => Err(()), - }); - - let base = match base { - Err(()) => { - emit(ParseError::hybrid_notation_in_bytearray(span)); - Base::Decimal { - numeric_underscore: false, - } - } - Ok(None) => Base::Decimal { - numeric_underscore: false, - }, - Ok(Some(base)) => *base, - }; - - (bytes.into_iter().map(|(b, _)| b).collect::>(), base) - }) - .map(|(bytes, base)| (ByteArrayFormatPreference::ArrayOfBytes(base), bytes)); - - let bytearray_hexstring_parser = - just(Token::Hash) - .ignore_then(select! {Token::ByteString {value} => value}.validate( - |value, span, emit| match hex::decode(value) { - Ok(bytes) => bytes, - Err(_) => { - emit(ParseError::malformed_base16_string_literal(span)); - vec![] - } - }, - )) - .map(|token| (ByteArrayFormatPreference::HexadecimalString, token)); - - let bytearray_utf8_parser = select! {Token::ByteString {value} => value.into_bytes() } - .map(|token| (ByteArrayFormatPreference::Utf8String, token)); - - choice(( - bytearray_list_parser, - bytearray_hexstring_parser, - bytearray_utf8_parser, - )) -} - -pub fn fn_param_parser( - is_validator_param: bool, -) -> impl Parser { - choice(( - select! {Token::Name {name} => name} - .then(select! {Token::DiscardName {name} => name}) - .map_with_span(|(label, name), span| ast::ArgName::Discarded { - label, - name, - location: span, - }), - select! {Token::DiscardName {name} => name}.map_with_span(|name, span| { - ast::ArgName::Discarded { - label: name.clone(), - name, - location: span, - } - }), - select! {Token::Name {name} => name} - .then(select! {Token::Name {name} => name}) - .map_with_span(move |(label, name), span| ast::ArgName::Named { - label, - name, - location: span, - is_validator_param, - }), - select! {Token::Name {name} => name}.map_with_span(move |name, span| ast::ArgName::Named { - label: name.clone(), - name, - location: span, - is_validator_param, - }), - )) - .then(just(Token::Colon).ignore_then(type_parser()).or_not()) - .map_with_span(|(arg_name, annotation), span| ast::Arg { - location: span, - annotation, - tipo: (), - arg_name, - }) -} - pub fn anon_fn_param_parser() -> impl Parser { // TODO: return a better error when a label is provided `UnexpectedLabel` choice(( @@ -255,7 +115,7 @@ pub fn anon_fn_param_parser() -> impl Parser impl Parser expr::UntypedExpr { +fn flexible_string_literal(expr: UntypedExpr) -> UntypedExpr { match expr { - expr::UntypedExpr::ByteArray { + UntypedExpr::ByteArray { preferred_format: ByteArrayFormatPreference::Utf8String, bytes, location, - } => expr::UntypedExpr::String { + } => UntypedExpr::String { location, value: String::from_utf8(bytes).unwrap(), }, @@ -283,49 +143,20 @@ fn flexible_string_literal(expr: expr::UntypedExpr) -> expr::UntypedExpr { } } -pub fn expr_seq_parser() -> impl Parser { - recursive(|r| { - choice(( - just(Token::Trace) - .ignore_then(expr_parser(r.clone())) - .then(r.clone()) - .map_with_span(|(text, then_), span| expr::UntypedExpr::Trace { - kind: TraceKind::Trace, - location: span, - then: Box::new(then_), - text: Box::new(flexible_string_literal(text)), - }), - just(Token::ErrorTerm) - .ignore_then(expr_parser(r.clone()).or_not()) - .map_with_span(|reason, span| { - expr::UntypedExpr::error(span, reason.map(flexible_string_literal)) - }), - just(Token::Todo) - .ignore_then(expr_parser(r.clone()).or_not()) - .map_with_span(|reason, span| { - expr::UntypedExpr::todo(span, reason.map(flexible_string_literal)) - }), - expr_parser(r.clone()) - .then(r.repeated()) - .foldl(|current, next| current.append_in_sequence(next)), - )) - }) -} - pub fn expr_parser( - seq_r: Recursive<'_, Token, expr::UntypedExpr, ParseError>, -) -> impl Parser + '_ { + seq_r: Recursive<'_, Token, UntypedExpr, ParseError>, +) -> impl Parser + '_ { recursive(|r| { let string_parser = select! {Token::String {value} => value}.map_with_span(|value, span| { - expr::UntypedExpr::String { + UntypedExpr::String { location: span, value, } }); let int_parser = select! { Token::Int {value, base} => (value, base)}.map_with_span( - |(value, base), span| expr::UntypedExpr::Int { + |(value, base), span| UntypedExpr::Int { location: span, value, base, @@ -357,7 +188,7 @@ pub fn expr_parser( select! {Token::Name {name} => name}.map_with_span( |name, span| ast::UntypedRecordUpdateArg { location: span, - value: expr::UntypedExpr::Var { + value: UntypedExpr::Var { name: name.clone(), location: span, }, @@ -375,16 +206,16 @@ pub fn expr_parser( ) .map(|((module, (name, n_span)), ((spread, opt_args), span))| { let constructor = if let Some((module, m_span)) = module { - expr::UntypedExpr::FieldAccess { + UntypedExpr::FieldAccess { location: m_span.union(n_span), label: name, - container: Box::new(expr::UntypedExpr::Var { + container: Box::new(UntypedExpr::Var { location: m_span, name: module, }), } } else { - expr::UntypedExpr::Var { + UntypedExpr::Var { location: n_span, name, } @@ -399,7 +230,7 @@ pub fn expr_parser( location, }; - expr::UntypedExpr::RecordUpdate { + UntypedExpr::RecordUpdate { location: constructor.location().union(span), constructor: Box::new(constructor), spread, @@ -430,7 +261,7 @@ pub fn expr_parser( Some(error::Pattern::Discard), )); - expr::UntypedExpr::Var { + UntypedExpr::Var { location: span, name: CAPTURE_VARIABLE.to_string(), } @@ -445,7 +276,7 @@ pub fn expr_parser( choice(( select! {Token::Name {name} => name}.map_with_span(|name, span| { ( - expr::UntypedExpr::Var { + UntypedExpr::Var { name: name.clone(), location: span, }, @@ -461,7 +292,7 @@ pub fn expr_parser( )); ( - expr::UntypedExpr::Var { + UntypedExpr::Var { location: span, name: CAPTURE_VARIABLE.to_string(), }, @@ -510,7 +341,7 @@ pub fn expr_parser( Some(error::Pattern::Discard), )); - expr::UntypedExpr::Var { + UntypedExpr::Var { location: span, name: CAPTURE_VARIABLE.to_string(), } @@ -529,22 +360,22 @@ pub fn expr_parser( )) .map_with_span(|((module, (name, n_span)), arguments), span| { let fun = if let Some((module, m_span)) = module { - expr::UntypedExpr::FieldAccess { + UntypedExpr::FieldAccess { location: m_span.union(n_span), label: name, - container: Box::new(expr::UntypedExpr::Var { + container: Box::new(UntypedExpr::Var { location: m_span, name: module, }), } } else { - expr::UntypedExpr::Var { + UntypedExpr::Var { location: n_span, name, } }; - expr::UntypedExpr::Call { + UntypedExpr::Call { arguments, fun: Box::new(fun), location: span, @@ -555,22 +386,20 @@ pub fn expr_parser( .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| expr::UntypedExpr::FieldAccess { - location: span, - label: name, - container: Box::new(expr::UntypedExpr::Var { - location: m_span, - name: module, - }), - }, - ); + .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 var_parser = select! { Token::Name { name } => name, Token::UpName { name } => name, } - .map_with_span(|name, span| expr::UntypedExpr::Var { + .map_with_span(|name, span| UntypedExpr::Var { location: span, name, }); @@ -584,13 +413,13 @@ pub fn expr_parser( choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), just(Token::RightParen), ) - .map_with_span(|elems, span| expr::UntypedExpr::Tuple { + .map_with_span(|elems, span| UntypedExpr::Tuple { location: span, elems, }); - let bytearray = bytearray_parser().map_with_span(|(preferred_format, bytes), span| { - expr::UntypedExpr::ByteArray { + let bytearray = utils::bytearray().map_with_span(|(preferred_format, bytes), span| { + UntypedExpr::ByteArray { location: span, bytes, preferred_format, @@ -610,7 +439,7 @@ pub fn expr_parser( ))) .then_ignore(just(Token::RightSquare)) // TODO: check if tail.is_some and elements.is_empty then return ListSpreadWithoutElements error - .map_with_span(|(elements, tail), span| expr::UntypedExpr::List { + .map_with_span(|(elements, tail), span| UntypedExpr::List { location: span, elements, tail, @@ -633,14 +462,14 @@ pub fn expr_parser( .allow_trailing() .delimited_by(just(Token::LeftParen), just(Token::RightParen)), ) - .then(just(Token::RArrow).ignore_then(type_parser()).or_not()) + .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| expr::UntypedExpr::Fn { + |((arguments, return_annotation), body), span| UntypedExpr::Fn { arguments, body: Box::new(body), location: span, - fn_style: expr::FnStyle::Plain, + fn_style: FnStyle::Plain, return_annotation, }, ); @@ -703,24 +532,24 @@ pub fn expr_parser( }, ]; - let body = expr::UntypedExpr::BinOp { + let body = UntypedExpr::BinOp { location, name, - left: Box::new(expr::UntypedExpr::Var { + left: Box::new(UntypedExpr::Var { location, name: "left".to_string(), }), - right: Box::new(expr::UntypedExpr::Var { + right: Box::new(UntypedExpr::Var { location, name: "right".to_string(), }), }; - expr::UntypedExpr::Fn { + UntypedExpr::Fn { arguments, body: Box::new(body), return_annotation, - fn_style: expr::FnStyle::BinOp(name), + fn_style: FnStyle::BinOp(name), location, } }); @@ -754,7 +583,7 @@ pub fn expr_parser( .or_not(), ) .map_with_span(|reason, span| { - expr::UntypedExpr::todo(span, reason.map(flexible_string_literal)) + UntypedExpr::todo(span, reason.map(flexible_string_literal)) }), just(Token::ErrorTerm) .ignore_then( @@ -763,7 +592,7 @@ pub fn expr_parser( .or_not(), ) .map_with_span(|reason, span| { - expr::UntypedExpr::error(span, reason.map(flexible_string_literal)) + UntypedExpr::error(span, reason.map(flexible_string_literal)) }), ))) .map_with_span( @@ -787,7 +616,7 @@ pub fn expr_parser( // TODO: If clauses are empty we should return ParseErrorType::NoCaseClause .then(when_clause_parser.repeated()) .then_ignore(just(Token::RightBrace)) - .map_with_span(|(subject, clauses), span| expr::UntypedExpr::When { + .map_with_span(|(subject, clauses), span| UntypedExpr::When { location: span, subject, clauses, @@ -795,11 +624,11 @@ pub fn expr_parser( let let_parser = just(Token::Let) .ignore_then(pattern_parser()) - .then(just(Token::Colon).ignore_then(type_parser()).or_not()) + .then(just(Token::Colon).ignore_then(annotation()).or_not()) .then_ignore(just(Token::Equal)) .then(r.clone()) .map_with_span( - |((pattern, annotation), value), span| expr::UntypedExpr::Assignment { + |((pattern, annotation), value), span| UntypedExpr::Assignment { location: span, value: Box::new(value), pattern, @@ -810,11 +639,11 @@ pub fn expr_parser( let expect_parser = just(Token::Expect) .ignore_then(pattern_parser()) - .then(just(Token::Colon).ignore_then(type_parser()).or_not()) + .then(just(Token::Colon).ignore_then(annotation()).or_not()) .then_ignore(just(Token::Equal)) .then(r.clone()) .map_with_span( - |((pattern, annotation), value), span| expr::UntypedExpr::Assignment { + |((pattern, annotation), value), span| UntypedExpr::Assignment { location: span, value: Box::new(value), pattern, @@ -850,7 +679,7 @@ pub fn expr_parser( branches.extend(alternative_branches); - expr::UntypedExpr::If { + UntypedExpr::If { location: span, branches, final_else: Box::new(final_else), @@ -879,7 +708,7 @@ pub fn expr_parser( // Parsing a function call into the appropriate structure #[derive(Debug)] enum ParserArg { - Arg(Box>), + Arg(Box>), Hole { location: Span, label: Option, @@ -971,7 +800,7 @@ pub fn expr_parser( ast::CallArg { label, location, - value: expr::UntypedExpr::Var { + value: UntypedExpr::Var { location, name: format!("{CAPTURE_VARIABLE}__{index}"), }, @@ -980,7 +809,7 @@ pub fn expr_parser( }) .collect(); - let call = expr::UntypedExpr::Call { + let call = UntypedExpr::Call { location: expr.location().union(span), fun: Box::new(expr), arguments: args, @@ -989,9 +818,9 @@ pub fn expr_parser( if holes.is_empty() { call } else { - expr::UntypedExpr::Fn { + UntypedExpr::Fn { location: call.location(), - fn_style: expr::FnStyle::Capture, + fn_style: FnStyle::Capture, arguments: holes, body: Box::new(call), return_annotation: None, @@ -999,13 +828,13 @@ pub fn expr_parser( } } - Chain::FieldAccess(label, span) => expr::UntypedExpr::FieldAccess { + Chain::FieldAccess(label, span) => UntypedExpr::FieldAccess { location: expr.location().union(span), label, container: Box::new(expr), }, - Chain::TupleIndex(index, span) => expr::UntypedExpr::TupleIndex { + Chain::TupleIndex(index, span) => UntypedExpr::TupleIndex { location: expr.location().union(span), index, tuple: Box::new(expr), @@ -1014,7 +843,7 @@ pub fn expr_parser( let debug = chained.then(just(Token::Question).or_not()).map_with_span( |(value, token), location| match token { - Some(_) => expr::UntypedExpr::TraceIfFalse { + Some(_) => UntypedExpr::TraceIfFalse { value: Box::new(value), location, }, @@ -1045,7 +874,7 @@ pub fn expr_parser( .map_with_span(|op, span| (op, span)) .repeated() .then(debug) - .foldr(|(un_op, span), value| expr::UntypedExpr::UnOp { + .foldr(|(un_op, span), value| UntypedExpr::UnOp { op: un_op, location: span.union(value.location()), value: Box::new(value), @@ -1062,7 +891,7 @@ pub fn expr_parser( let product = unary .clone() .then(op.then(unary).repeated()) - .foldl(|a, (op, b)| expr::UntypedExpr::BinOp { + .foldl(|a, (op, b)| UntypedExpr::BinOp { location: a.location().union(b.location()), name: op, left: Box::new(a), @@ -1079,7 +908,7 @@ pub fn expr_parser( let sum = product .clone() .then(op.then(product).repeated()) - .foldl(|a, (op, b)| expr::UntypedExpr::BinOp { + .foldl(|a, (op, b)| UntypedExpr::BinOp { location: a.location().union(b.location()), name: op, left: Box::new(a), @@ -1100,7 +929,7 @@ pub fn expr_parser( let comparison = sum .clone() .then(op.then(sum).repeated()) - .foldl(|a, (op, b)| expr::UntypedExpr::BinOp { + .foldl(|a, (op, b)| UntypedExpr::BinOp { location: a.location().union(b.location()), name: op, left: Box::new(a), @@ -1113,7 +942,7 @@ pub fn expr_parser( let conjunction = comparison .clone() .then(op.then(comparison).repeated()) - .foldl(|a, (op, b)| expr::UntypedExpr::BinOp { + .foldl(|a, (op, b)| UntypedExpr::BinOp { location: a.location().union(b.location()), name: op, left: Box::new(a), @@ -1126,7 +955,7 @@ pub fn expr_parser( let disjunction = conjunction .clone() .then(op.then(conjunction).repeated()) - .foldl(|a, (op, b)| expr::UntypedExpr::BinOp { + .foldl(|a, (op, b)| UntypedExpr::BinOp { location: a.location().union(b.location()), name: op, left: Box::new(a), @@ -1143,13 +972,13 @@ pub fn expr_parser( .repeated(), ) .foldl(|l, (pipe, r)| { - if let expr::UntypedExpr::PipeLine { + if let UntypedExpr::PipeLine { mut expressions, one_liner, } = l { expressions.push(r); - return expr::UntypedExpr::PipeLine { + return UntypedExpr::PipeLine { expressions, one_liner, }; @@ -1157,7 +986,7 @@ pub fn expr_parser( let mut expressions = Vec1::new(l); expressions.push(r); - expr::UntypedExpr::PipeLine { + UntypedExpr::PipeLine { expressions, one_liner: pipe != Token::NewLinePipe, } @@ -1177,7 +1006,7 @@ pub fn when_clause_guard_parser() -> impl Parser, Er location: span, }); - let constant_parser = constant_value_parser().map(ast::ClauseGuard::Constant); + let constant_parser = definitions::constant::value().map(ast::ClauseGuard::Constant); let block_parser = r .clone() @@ -1281,122 +1110,6 @@ pub fn when_clause_guard_parser() -> impl Parser, Er }) } -pub fn type_parser() -> impl Parser { - recursive(|r| { - choice(( - // Type hole - select! {Token::DiscardName { name } => name}.map_with_span(|name, span| { - ast::Annotation::Hole { - location: span, - name, - } - }), - // Tuple - r.clone() - .separated_by(just(Token::Comma)) - .at_least(2) - .allow_trailing() - .delimited_by( - choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), - just(Token::RightParen), - ) - .map_with_span(|elems, span| ast::Annotation::Tuple { - location: span, - elems, - }), - // Function - just(Token::Fn) - .ignore_then( - r.clone() - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftParen), just(Token::RightParen)), - ) - .then_ignore(just(Token::RArrow)) - .then(r.clone()) - .map_with_span(|(arguments, ret), span| ast::Annotation::Fn { - location: span, - arguments, - ret: Box::new(ret), - }), - // Constructor function - select! {Token::UpName { name } => name} - .then( - r.clone() - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::Less), just(Token::Greater)) - .or_not(), - ) - .map_with_span(|(name, arguments), span| ast::Annotation::Constructor { - location: span, - module: None, - name, - arguments: arguments.unwrap_or_default(), - }), - // Constructor Module or type Variable - select! {Token::Name { name } => name} - .then( - just(Token::Dot) - .ignore_then(select! {Token::UpName {name} => name}) - .then( - r.separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::Less), just(Token::Greater)) - .or_not(), - ) - .or_not(), - ) - .map_with_span(|(mod_name, opt_dot), span| { - if let Some((name, arguments)) = opt_dot { - ast::Annotation::Constructor { - location: span, - module: Some(mod_name), - name, - arguments: arguments.unwrap_or_default(), - } - } else { - // TODO: parse_error(ParseErrorType::NotConstType, SrcSpan { start, end }) - ast::Annotation::Var { - location: span, - name: mod_name, - } - } - }), - )) - }) -} - -pub fn labeled_constructor_type_args( -) -> impl Parser>, Error = ParseError> { - select! {Token::Name {name} => name} - .then_ignore(just(Token::Colon)) - .then(type_parser()) - .map_with_span(|(name, annotation), span| ast::RecordConstructorArg { - label: Some(name), - annotation, - tipo: (), - doc: None, - location: span, - }) - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)) -} - -pub fn type_name_with_args() -> impl Parser>), Error = ParseError> -{ - just(Token::Type).ignore_then( - select! {Token::UpName { name } => name}.then( - select! {Token::Name { name } => name} - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::Less), just(Token::Greater)) - .or_not(), - ), - ) -} - pub fn pattern_parser() -> impl Parser { recursive(|r| { let record_constructor_pattern_arg_parser = choice(( diff --git a/crates/aiken-lang/src/parser/annotation.rs b/crates/aiken-lang/src/parser/annotation.rs new file mode 100644 index 000000000..c19aeff4b --- /dev/null +++ b/crates/aiken-lang/src/parser/annotation.rs @@ -0,0 +1,91 @@ +use chumsky::prelude::*; + +use crate::ast; + +use super::{error::ParseError, token::Token}; + +pub fn parser() -> impl Parser { + recursive(|r| { + choice(( + // Type hole + select! {Token::DiscardName { name } => name}.map_with_span(|name, span| { + ast::Annotation::Hole { + location: span, + name, + } + }), + // Tuple + r.clone() + .separated_by(just(Token::Comma)) + .at_least(2) + .allow_trailing() + .delimited_by( + choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), + just(Token::RightParen), + ) + .map_with_span(|elems, span| ast::Annotation::Tuple { + location: span, + elems, + }), + // Function + just(Token::Fn) + .ignore_then( + r.clone() + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftParen), just(Token::RightParen)), + ) + .then_ignore(just(Token::RArrow)) + .then(r.clone()) + .map_with_span(|(arguments, ret), span| ast::Annotation::Fn { + location: span, + arguments, + ret: Box::new(ret), + }), + // Constructor function + select! {Token::UpName { name } => name} + .then( + r.clone() + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::Less), just(Token::Greater)) + .or_not(), + ) + .map_with_span(|(name, arguments), span| ast::Annotation::Constructor { + location: span, + module: None, + name, + arguments: arguments.unwrap_or_default(), + }), + // Constructor Module or type Variable + select! {Token::Name { name } => name} + .then( + just(Token::Dot) + .ignore_then(select! {Token::UpName {name} => name}) + .then( + r.separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::Less), just(Token::Greater)) + .or_not(), + ) + .or_not(), + ) + .map_with_span(|(mod_name, opt_dot), span| { + if let Some((name, arguments)) = opt_dot { + ast::Annotation::Constructor { + location: span, + module: Some(mod_name), + name, + arguments: arguments.unwrap_or_default(), + } + } else { + // TODO: parse_error(ParseErrorType::NotConstType, SrcSpan { start, end }) + ast::Annotation::Var { + location: span, + name: mod_name, + } + } + }), + )) + }) +} diff --git a/crates/aiken-lang/src/parser/definitions/constant.rs b/crates/aiken-lang/src/parser/definitions/constant.rs index b0c52f120..137111ae6 100644 --- a/crates/aiken-lang/src/parser/definitions/constant.rs +++ b/crates/aiken-lang/src/parser/definitions/constant.rs @@ -2,7 +2,7 @@ use chumsky::prelude::*; use crate::{ ast, - parser::{error::ParseError, token::Token, utils}, + parser::{annotation, error::ParseError, token::Token, utils}, }; pub fn parser() -> impl Parser { @@ -10,9 +10,13 @@ pub fn parser() -> impl Parser name}) - .then(just(Token::Colon).ignore_then(type_parser()).or_not()) + .then( + just(Token::Colon) + .ignore_then(annotation::parser()) + .or_not(), + ) .then_ignore(just(Token::Equal)) - .then(constant_value_parser()) + .then(value()) .map_with_span(|(((public, name), annotation), value), span| { ast::UntypedDefinition::ModuleConstant(ast::ModuleConstant { doc: None, @@ -25,3 +29,37 @@ pub fn parser() -> impl Parser impl Parser { + let constant_string_parser = + select! {Token::String {value} => value}.map_with_span(|value, span| { + ast::Constant::String { + location: span, + value, + } + }); + + let constant_int_parser = + select! {Token::Int {value, base} => (value, base)}.map_with_span(|(value, base), span| { + ast::Constant::Int { + location: span, + value, + base, + } + }); + + let constant_bytearray_parser = + utils::bytearray().map_with_span(|(preferred_format, bytes), span| { + ast::Constant::ByteArray { + location: span, + bytes, + preferred_format, + } + }); + + choice(( + constant_string_parser, + constant_int_parser, + constant_bytearray_parser, + )) +} diff --git a/crates/aiken-lang/src/parser/definitions/data_type.rs b/crates/aiken-lang/src/parser/definitions/data_type.rs index b20d328c4..14083cf00 100644 --- a/crates/aiken-lang/src/parser/definitions/data_type.rs +++ b/crates/aiken-lang/src/parser/definitions/data_type.rs @@ -2,11 +2,11 @@ use chumsky::prelude::*; use crate::{ ast, - parser::{error::ParseError, token::Token, utils}, + parser::{annotation, error::ParseError, token::Token, utils}, }; pub fn parser() -> impl Parser { - let unlabeled_constructor_type_args = type_parser() + let unlabeled_constructor_type_args = annotation() .map_with_span(|annotation, span| ast::RecordConstructorArg { label: None, annotation, @@ -49,7 +49,7 @@ pub fn parser() -> impl Parser impl Parser impl Parser>, Error = ParseError> { + select! {Token::Name {name} => name} + .then_ignore(just(Token::Colon)) + .then(annotation()) + .map_with_span(|(name, annotation), span| ast::RecordConstructorArg { + label: Some(name), + annotation, + tipo: (), + doc: None, + location: span, + }) + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)) +} diff --git a/crates/aiken-lang/src/parser/definitions/function.rs b/crates/aiken-lang/src/parser/definitions/function.rs index 4f3f2e29b..4b61939eb 100644 --- a/crates/aiken-lang/src/parser/definitions/function.rs +++ b/crates/aiken-lang/src/parser/definitions/function.rs @@ -1,8 +1,9 @@ use chumsky::prelude::*; use crate::{ - ast, expr, - parser::{error::ParseError, token::Token, utils}, + ast, + expr::UntypedExpr, + parser::{annotation, error::ParseError, expr, token::Token, utils}, }; pub fn parser() -> impl Parser { @@ -11,15 +12,15 @@ pub fn parser() -> impl Parser name}) .then( - fn_param_parser(false) + param(false) .separated_by(just(Token::Comma)) .allow_trailing() .delimited_by(just(Token::LeftParen), just(Token::RightParen)) .map_with_span(|arguments, span| (arguments, span)), ) - .then(just(Token::RArrow).ignore_then(type_parser()).or_not()) + .then(just(Token::RArrow).ignore_then(annotation()).or_not()) .then( - expr_seq_parser() + expr::sequence() .or_not() .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), ) @@ -27,7 +28,7 @@ pub fn parser() -> impl Parser impl Parser impl Parser { + choice(( + select! {Token::Name {name} => name} + .then(select! {Token::DiscardName {name} => name}) + .map_with_span(|(label, name), span| ast::ArgName::Discarded { + label, + name, + location: span, + }), + select! {Token::DiscardName {name} => name}.map_with_span(|name, span| { + ast::ArgName::Discarded { + label: name.clone(), + name, + location: span, + } + }), + select! {Token::Name {name} => name} + .then(select! {Token::Name {name} => name}) + .map_with_span(move |(label, name), span| ast::ArgName::Named { + label, + name, + location: span, + is_validator_param, + }), + select! {Token::Name {name} => name}.map_with_span(move |name, span| ast::ArgName::Named { + label: name.clone(), + name, + location: span, + is_validator_param, + }), + )) + .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/definitions/mod.rs b/crates/aiken-lang/src/parser/definitions/mod.rs index 287e89f82..54d87aa62 100644 --- a/crates/aiken-lang/src/parser/definitions/mod.rs +++ b/crates/aiken-lang/src/parser/definitions/mod.rs @@ -1,4 +1,4 @@ -mod constant; +pub mod constant; mod data_type; mod function; mod import; diff --git a/crates/aiken-lang/src/parser/definitions/test.rs b/crates/aiken-lang/src/parser/definitions/test.rs index f565d551b..6ef73c69c 100644 --- a/crates/aiken-lang/src/parser/definitions/test.rs +++ b/crates/aiken-lang/src/parser/definitions/test.rs @@ -1,8 +1,9 @@ use chumsky::prelude::*; use crate::{ - ast, expr, - parser::{error::ParseError, token::Token}, + ast, + expr::UntypedExpr, + parser::{error::ParseError, expr, token::Token}, }; pub fn parser() -> impl Parser { @@ -15,14 +16,14 @@ pub fn parser() -> impl Parser impl Parser { utils::public() .or_not() - .then(type_name_with_args()) + .then(utils::type_name_with_args()) .then_ignore(just(Token::Equal)) - .then(type_parser()) + .then(annotation()) .map_with_span(|((opt_pub, (alias, parameters)), annotation), span| { ast::UntypedDefinition::TypeAlias(ast::TypeAlias { alias, diff --git a/crates/aiken-lang/src/parser/definitions/validator.rs b/crates/aiken-lang/src/parser/definitions/validator.rs index baf56e96d..59a74d04e 100644 --- a/crates/aiken-lang/src/parser/definitions/validator.rs +++ b/crates/aiken-lang/src/parser/definitions/validator.rs @@ -5,10 +5,12 @@ use crate::{ parser::{error::ParseError, token::Token}, }; +use super::function; + pub fn parser() -> impl Parser { just(Token::Validator) .ignore_then( - fn_param_parser(true) + function::param(true) .separated_by(just(Token::Comma)) .allow_trailing() .delimited_by(just(Token::LeftParen), just(Token::RightParen)) @@ -16,7 +18,7 @@ pub fn parser() -> impl Parser impl Parser { + recursive(|r| { + choice(( + just(Token::Trace) + .ignore_then(expr_parser(r.clone())) + .then(r.clone()) + .map_with_span(|(text, then_), span| UntypedExpr::Trace { + kind: TraceKind::Trace, + location: span, + then: Box::new(then_), + text: Box::new(flexible_string_literal(text)), + }), + just(Token::ErrorTerm) + .ignore_then(expr_parser(r.clone()).or_not()) + .map_with_span(|reason, span| { + UntypedExpr::error(span, reason.map(flexible_string_literal)) + }), + just(Token::Todo) + .ignore_then(expr_parser(r.clone()).or_not()) + .map_with_span(|reason, span| { + UntypedExpr::todo(span, reason.map(flexible_string_literal)) + }), + expr_parser(r.clone()) + .then(r.repeated()) + .foldl(|current, next| current.append_in_sequence(next)), + )) + }) +} diff --git a/crates/aiken-lang/src/parser/utils.rs b/crates/aiken-lang/src/parser/utils.rs index 22be13d81..4c7b5c6bd 100644 --- a/crates/aiken-lang/src/parser/utils.rs +++ b/crates/aiken-lang/src/parser/utils.rs @@ -1,7 +1,95 @@ use chumsky::prelude::*; -use super::{error::ParseError, token::Token}; +use crate::ast; + +use super::{ + error::{self, ParseError}, + token::{Base, Token}, +}; pub fn public() -> impl Parser { just(Token::Pub).ignored() } + +pub fn bytearray( +) -> impl Parser), Error = ParseError> { + let bytearray_list_parser = just(Token::Hash) + .ignore_then( + select! {Token::Int {value, base, ..} => (value, base)} + .validate(|(value, base), span, emit| { + let byte: u8 = match value.parse() { + Ok(b) => b, + Err(_) => { + emit(ParseError::expected_input_found( + span, + None, + Some(error::Pattern::Byte), + )); + 0 + } + }; + (byte, base) + }) + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftSquare), just(Token::RightSquare)), + ) + .validate(|bytes, span, emit| { + let base = bytes.iter().fold(Ok(None), |acc, (_, base)| match acc { + Ok(None) => Ok(Some(base)), + Ok(Some(previous_base)) if previous_base == base => Ok(Some(base)), + _ => Err(()), + }); + + let base = match base { + Err(()) => { + emit(ParseError::hybrid_notation_in_bytearray(span)); + Base::Decimal { + numeric_underscore: false, + } + } + Ok(None) => Base::Decimal { + numeric_underscore: false, + }, + Ok(Some(base)) => *base, + }; + + (bytes.into_iter().map(|(b, _)| b).collect::>(), base) + }) + .map(|(bytes, base)| (ast::ByteArrayFormatPreference::ArrayOfBytes(base), bytes)); + + let bytearray_hexstring_parser = + just(Token::Hash) + .ignore_then(select! {Token::ByteString {value} => value}.validate( + |value, span, emit| match hex::decode(value) { + Ok(bytes) => bytes, + Err(_) => { + emit(ParseError::malformed_base16_string_literal(span)); + vec![] + } + }, + )) + .map(|token| (ast::ByteArrayFormatPreference::HexadecimalString, token)); + + let bytearray_utf8_parser = select! {Token::ByteString {value} => value.into_bytes() } + .map(|token| (ast::ByteArrayFormatPreference::Utf8String, token)); + + choice(( + bytearray_list_parser, + bytearray_hexstring_parser, + bytearray_utf8_parser, + )) +} + +pub fn type_name_with_args() -> impl Parser>), Error = ParseError> +{ + just(Token::Type).ignore_then( + select! {Token::UpName { name } => name}.then( + select! {Token::Name { name } => name} + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::Less), just(Token::Greater)) + .or_not(), + ), + ) +} From e3ed5d3b0003d3fc93ac9dbe671723cc0cc1721a Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 23 Jun 2023 13:04:23 -0400 Subject: [PATCH 03/31] feat: move expr_parser and remove module.rs to definitions --- crates/aiken-lang/src/parser.rs | 884 +----------------- .../aiken-lang/src/parser/definitions/mod.rs | 19 + crates/aiken-lang/src/parser/expr/mod.rs | 863 +++++++++++++++++ crates/aiken-lang/src/parser/expr/sequence.rs | 14 +- crates/aiken-lang/src/parser/expr/string.rs | 20 + crates/aiken-lang/src/parser/module.rs | 19 - crates/aiken-lang/src/parser/pattern/mod.rs | 0 7 files changed, 915 insertions(+), 904 deletions(-) create mode 100644 crates/aiken-lang/src/parser/expr/string.rs delete mode 100644 crates/aiken-lang/src/parser/module.rs create mode 100644 crates/aiken-lang/src/parser/pattern/mod.rs diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 4e72a85e1..17e786803 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -1,24 +1,22 @@ mod annotation; pub mod definitions; pub mod error; -mod expr; +pub mod expr; pub mod extra; pub mod lexer; -mod module; +pub mod pattern; pub mod token; mod utils; pub use annotation::parser as annotation; +pub use definitions::parser as definitions; +pub use expr::parser as expression; -use crate::{ - ast::{self, BinOp, ByteArrayFormatPreference, Span, UnOp, CAPTURE_VARIABLE}, - expr::{FnStyle, UntypedExpr}, -}; +use crate::ast::{self, BinOp, Span}; use chumsky::{chain::Chain, prelude::*}; use error::ParseError; use extra::ModuleExtra; use token::Token; -use vec1::{vec1, Vec1}; pub fn module( src: &str, @@ -85,7 +83,7 @@ pub fn module( }); let definitions = - module::parser().parse(chumsky::Stream::from_iter(span(tokens.len()), tokens))?; + definitions().parse(chumsky::Stream::from_iter(span(tokens.len()), tokens))?; let module = ast::UntypedModule { kind, @@ -124,876 +122,6 @@ pub fn anon_fn_param_parser() -> impl Parser UntypedExpr { - match expr { - UntypedExpr::ByteArray { - preferred_format: ByteArrayFormatPreference::Utf8String, - bytes, - location, - } => UntypedExpr::String { - location, - value: String::from_utf8(bytes).unwrap(), - }, - _ => expr, - } -} - -pub fn expr_parser( - seq_r: Recursive<'_, Token, UntypedExpr, ParseError>, -) -> impl Parser + '_ { - recursive(|r| { - let string_parser = - select! {Token::String {value} => value}.map_with_span(|value, span| { - UntypedExpr::String { - location: span, - value, - } - }); - - let int_parser = select! { Token::Int {value, base} => (value, base)}.map_with_span( - |(value, base), span| UntypedExpr::Int { - location: span, - value, - base, - }, - ); - - let record_update_parser = select! {Token::Name { name } => name} - .map_with_span(|module, span: Span| (module, span)) - .then_ignore(just(Token::Dot)) - .or_not() - .then(select! {Token::UpName { name } => name}.map_with_span(|name, span| (name, span))) - .then( - just(Token::DotDot) - .ignore_then(r.clone()) - .then( - just(Token::Comma) - .ignore_then( - choice(( - select! { Token::Name {name} => name } - .then_ignore(just(Token::Colon)) - .then(r.clone()) - .map_with_span(|(label, value), span| { - ast::UntypedRecordUpdateArg { - label, - value, - location: span, - } - }), - select! {Token::Name {name} => name}.map_with_span( - |name, span| ast::UntypedRecordUpdateArg { - location: span, - value: UntypedExpr::Var { - name: name.clone(), - location: span, - }, - label: name, - }, - ), - )) - .separated_by(just(Token::Comma)) - .allow_trailing(), - ) - .or_not(), - ) - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)) - .map_with_span(|a, span: Span| (a, span)), - ) - .map(|((module, (name, n_span)), ((spread, opt_args), span))| { - let constructor = if let Some((module, m_span)) = module { - UntypedExpr::FieldAccess { - location: m_span.union(n_span), - label: name, - container: Box::new(UntypedExpr::Var { - location: m_span, - name: module, - }), - } - } else { - UntypedExpr::Var { - location: n_span, - name, - } - }; - - let spread_span = spread.location(); - - let location = Span::new((), spread_span.start - 2..spread_span.end); - - let spread = ast::RecordUpdateSpread { - base: Box::new(spread), - location, - }; - - UntypedExpr::RecordUpdate { - location: constructor.location().union(span), - constructor: Box::new(constructor), - spread, - arguments: opt_args.unwrap_or_default(), - } - }); - - let record_parser = choice(( - select! {Token::Name { name } => name} - .map_with_span(|module, span: Span| (module, span)) - .then_ignore(just(Token::Dot)) - .or_not() - .then( - select! {Token::UpName { name } => name} - .map_with_span(|name, span| (name, span)), - ) - .then( - choice(( - select! {Token::Name {name} => name} - .then_ignore(just(Token::Colon)) - .then(choice(( - r.clone(), - select! {Token::DiscardName {name} => name }.validate( - |_name, span, emit| { - emit(ParseError::expected_input_found( - span, - None, - Some(error::Pattern::Discard), - )); - - UntypedExpr::Var { - location: span, - name: CAPTURE_VARIABLE.to_string(), - } - }, - ), - ))) - .map_with_span(|(label, value), span| ast::CallArg { - location: span, - value, - label: Some(label), - }), - choice(( - select! {Token::Name {name} => name}.map_with_span(|name, span| { - ( - UntypedExpr::Var { - name: name.clone(), - location: span, - }, - name, - ) - }), - select! {Token::DiscardName {name} => name }.validate( - |name, span, emit| { - emit(ParseError::expected_input_found( - span, - None, - Some(error::Pattern::Discard), - )); - - ( - UntypedExpr::Var { - location: span, - name: CAPTURE_VARIABLE.to_string(), - }, - name, - ) - }, - ), - )) - .map(|(value, name)| ast::CallArg { - location: value.location(), - value, - label: Some(name), - }), - )) - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), - ), - select! {Token::Name { name } => name} - .map_with_span(|module, span| (module, span)) - .then_ignore(just(Token::Dot)) - .or_not() - .then( - select! {Token::UpName { name } => name} - .map_with_span(|name, span| (name, span)), - ) - .then( - select! {Token::Name {name} => name} - .ignored() - .then_ignore(just(Token::Colon)) - .validate(|_label, span, emit| { - emit(ParseError::expected_input_found( - span, - None, - Some(error::Pattern::Label), - )) - }) - .or_not() - .then(choice(( - r.clone(), - select! {Token::DiscardName {name} => name }.validate( - |_name, span, emit| { - emit(ParseError::expected_input_found( - span, - None, - Some(error::Pattern::Discard), - )); - - UntypedExpr::Var { - location: span, - name: CAPTURE_VARIABLE.to_string(), - } - }, - ), - ))) - .map(|(_label, value)| ast::CallArg { - location: value.location(), - value, - label: None, - }) - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftParen), just(Token::RightParen)), - ), - )) - .map_with_span(|((module, (name, n_span)), arguments), span| { - let fun = if let Some((module, m_span)) = module { - UntypedExpr::FieldAccess { - location: m_span.union(n_span), - label: name, - container: Box::new(UntypedExpr::Var { - location: m_span, - name: module, - }), - } - } else { - UntypedExpr::Var { - location: n_span, - name, - } - }; - - UntypedExpr::Call { - arguments, - fun: Box::new(fun), - location: span, - } - }); - - 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 var_parser = select! { - Token::Name { name } => name, - Token::UpName { name } => name, - } - .map_with_span(|name, span| UntypedExpr::Var { - location: span, - name, - }); - - let tuple = r - .clone() - .separated_by(just(Token::Comma)) - .at_least(2) - .allow_trailing() - .delimited_by( - choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), - just(Token::RightParen), - ) - .map_with_span(|elems, span| UntypedExpr::Tuple { - location: span, - elems, - }); - - let bytearray = utils::bytearray().map_with_span(|(preferred_format, bytes), span| { - UntypedExpr::ByteArray { - location: span, - bytes, - preferred_format, - } - }); - - let list_parser = 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()) - .map(Box::new) - .or_not(), - ), - just(Token::Comma).ignored().or_not().map(|_| None), - ))) - .then_ignore(just(Token::RightSquare)) - // TODO: check if tail.is_some and elements.is_empty then return ListSpreadWithoutElements error - .map_with_span(|(elements, tail), span| UntypedExpr::List { - location: span, - elements, - tail, - }); - - let block_parser = choice(( - seq_r - .clone() - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), - seq_r.clone().delimited_by( - choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), - just(Token::RightParen), - ), - )); - - 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, - Token::Less => BinOp::LtInt, - Token::LessEqual => BinOp::LtEqInt, - Token::Greater => BinOp::GtInt, - Token::GreaterEqual => BinOp::GtEqInt, - Token::VbarVbar => BinOp::Or, - Token::AmperAmper => BinOp::And, - Token::Plus => BinOp::AddInt, - Token::Minus => BinOp::SubInt, - Token::Slash => BinOp::DivInt, - Token::Star => BinOp::MultInt, - Token::Percent => BinOp::ModInt, - } - .map_with_span(|name, location| { - use BinOp::*; - - let arg_annotation = match name { - Or | And => Some(ast::Annotation::boolean(location)), - Eq | NotEq => None, - LtInt | LtEqInt | GtInt | GtEqInt | AddInt | SubInt | MultInt | DivInt | ModInt => { - Some(ast::Annotation::int(location)) - } - }; - - let return_annotation = match name { - Or | And | Eq | NotEq | LtInt | LtEqInt | GtInt | GtEqInt => { - Some(ast::Annotation::boolean(location)) - } - AddInt | SubInt | MultInt | DivInt | ModInt => Some(ast::Annotation::int(location)), - }; - - let arguments = vec![ - ast::Arg { - arg_name: ast::ArgName::Named { - name: "left".to_string(), - label: "left".to_string(), - location, - is_validator_param: false, - }, - annotation: arg_annotation.clone(), - location, - tipo: (), - }, - ast::Arg { - arg_name: ast::ArgName::Named { - name: "right".to_string(), - label: "right".to_string(), - location, - is_validator_param: false, - }, - annotation: arg_annotation, - location, - tipo: (), - }, - ]; - - let body = UntypedExpr::BinOp { - location, - name, - left: Box::new(UntypedExpr::Var { - location, - name: "left".to_string(), - }), - right: Box::new(UntypedExpr::Var { - location, - name: "right".to_string(), - }), - }; - - UntypedExpr::Fn { - arguments, - body: Box::new(body), - return_annotation, - fn_style: FnStyle::BinOp(name), - location, - } - }); - - let when_clause_parser = pattern_parser() - .then( - just(Token::Vbar) - .ignore_then(pattern_parser()) - .repeated() - .or_not(), - ) - .then(choice(( - just(Token::If) - .ignore_then(when_clause_guard_parser()) - .or_not() - .then_ignore(just(Token::RArrow)), - just(Token::If) - .ignore_then(take_until(just(Token::RArrow))) - .validate(|_value, span, emit| { - emit(ParseError::invalid_when_clause_guard(span)); - None - }), - ))) - // TODO: add hint "Did you mean to wrap a multi line clause in curly braces?" - .then(choice(( - r.clone(), - just(Token::Todo) - .ignore_then( - r.clone() - .then_ignore(one_of(Token::RArrow).not().rewind()) - .or_not(), - ) - .map_with_span(|reason, span| { - UntypedExpr::todo(span, reason.map(flexible_string_literal)) - }), - just(Token::ErrorTerm) - .ignore_then( - r.clone() - .then_ignore(just(Token::RArrow).not().rewind()) - .or_not(), - ) - .map_with_span(|reason, span| { - UntypedExpr::error(span, reason.map(flexible_string_literal)) - }), - ))) - .map_with_span( - |(((pattern, alternative_patterns_opt), guard), then), span| { - let mut patterns = vec1![pattern]; - patterns.append(&mut alternative_patterns_opt.unwrap_or_default()); - ast::UntypedClause { - location: span, - patterns, - guard, - then, - } - }, - ); - - let when_parser = just(Token::When) - // TODO: If subject is empty we should return ParseErrorType::ExpectedExpr, - .ignore_then(r.clone().map(Box::new)) - .then_ignore(just(Token::Is)) - .then_ignore(just(Token::LeftBrace)) - // TODO: If clauses are empty we should return ParseErrorType::NoCaseClause - .then(when_clause_parser.repeated()) - .then_ignore(just(Token::RightBrace)) - .map_with_span(|(subject, clauses), span| UntypedExpr::When { - location: span, - subject, - 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 if_parser = just(Token::If) - .ignore_then(r.clone().then(block_parser.clone()).map_with_span( - |(condition, body), span| ast::IfBranch { - condition, - body, - location: span, - }, - )) - .then( - just(Token::Else) - .ignore_then(just(Token::If)) - .ignore_then(r.clone().then(block_parser.clone()).map_with_span( - |(condition, body), span| ast::IfBranch { - condition, - body, - location: span, - }, - )) - .repeated(), - ) - .then_ignore(just(Token::Else)) - .then(block_parser.clone()) - .map_with_span(|((first, alternative_branches), final_else), span| { - let mut branches = vec1::vec1![first]; - - branches.extend(alternative_branches); - - UntypedExpr::If { - location: span, - branches, - final_else: Box::new(final_else), - } - }); - - let expr_unit_parser = choice(( - string_parser, - int_parser, - record_update_parser, - record_parser, - field_access_constructor, - var_parser, - tuple, - bytearray, - list_parser, - anon_fn_parser, - anon_binop_parser, - block_parser, - when_parser, - let_parser, - expect_parser, - if_parser, - )); - - // 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(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)) - .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!("{CAPTURE_VARIABLE}__{index}"); - 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!("{CAPTURE_VARIABLE}__{index}"), - }, - } - } - }) - .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 { - Some(_) => UntypedExpr::TraceIfFalse { - value: Box::new(value), - location, - }, - None => value, - }, - ); - - // Negate - let op = choice(( - just(Token::Bang).to(UnOp::Not), - just(Token::Minus) - // NOTE: Prevent conflict with usage for '-' as a standalone binary op. - // This will make '-' parse when used as standalone binop in a function call. - // For example: - // - // foo(a, -, b) - // - // but it'll fail in a let-binding: - // - // let foo = - - // - // which seems acceptable. - .then_ignore(just(Token::Comma).not().rewind()) - .to(UnOp::Negate), - )); - - let unary = op - .map_with_span(|op, span| (op, span)) - .repeated() - .then(debug) - .foldr(|(un_op, span), value| UntypedExpr::UnOp { - op: un_op, - location: span.union(value.location()), - value: Box::new(value), - }) - .boxed(); - - // Product - let op = choice(( - just(Token::Star).to(BinOp::MultInt), - just(Token::Slash).to(BinOp::DivInt), - just(Token::Percent).to(BinOp::ModInt), - )); - - let product = unary - .clone() - .then(op.then(unary).repeated()) - .foldl(|a, (op, b)| UntypedExpr::BinOp { - location: a.location().union(b.location()), - name: op, - left: Box::new(a), - right: Box::new(b), - }) - .boxed(); - - // Sum - let op = choice(( - just(Token::Plus).to(BinOp::AddInt), - just(Token::Minus).to(BinOp::SubInt), - )); - - let sum = product - .clone() - .then(op.then(product).repeated()) - .foldl(|a, (op, b)| UntypedExpr::BinOp { - location: a.location().union(b.location()), - name: op, - left: Box::new(a), - right: Box::new(b), - }) - .boxed(); - - // Comparison - let op = choice(( - just(Token::EqualEqual).to(BinOp::Eq), - just(Token::NotEqual).to(BinOp::NotEq), - just(Token::Less).to(BinOp::LtInt), - just(Token::Greater).to(BinOp::GtInt), - just(Token::LessEqual).to(BinOp::LtEqInt), - just(Token::GreaterEqual).to(BinOp::GtEqInt), - )); - - let comparison = sum - .clone() - .then(op.then(sum).repeated()) - .foldl(|a, (op, b)| UntypedExpr::BinOp { - location: a.location().union(b.location()), - name: op, - left: Box::new(a), - right: Box::new(b), - }) - .boxed(); - - // Conjunction - let op = just(Token::AmperAmper).to(BinOp::And); - let conjunction = comparison - .clone() - .then(op.then(comparison).repeated()) - .foldl(|a, (op, b)| UntypedExpr::BinOp { - location: a.location().union(b.location()), - name: op, - left: Box::new(a), - right: Box::new(b), - }) - .boxed(); - - // Disjunction - let op = just(Token::VbarVbar).to(BinOp::Or); - let disjunction = conjunction - .clone() - .then(op.then(conjunction).repeated()) - .foldl(|a, (op, b)| UntypedExpr::BinOp { - location: a.location().union(b.location()), - name: op, - left: Box::new(a), - right: Box::new(b), - }) - .boxed(); - - // Pipeline - disjunction - .clone() - .then( - choice((just(Token::Pipe), just(Token::NewLinePipe))) - .then(disjunction) - .repeated(), - ) - .foldl(|l, (pipe, r)| { - if let UntypedExpr::PipeLine { - mut expressions, - one_liner, - } = l - { - expressions.push(r); - return UntypedExpr::PipeLine { - expressions, - one_liner, - }; - } - - let mut expressions = Vec1::new(l); - expressions.push(r); - UntypedExpr::PipeLine { - expressions, - one_liner: pipe != Token::NewLinePipe, - } - }) - }) -} - pub fn when_clause_guard_parser() -> impl Parser, Error = ParseError> { recursive(|r| { let var_parser = select! { diff --git a/crates/aiken-lang/src/parser/definitions/mod.rs b/crates/aiken-lang/src/parser/definitions/mod.rs index 54d87aa62..d8691b86d 100644 --- a/crates/aiken-lang/src/parser/definitions/mod.rs +++ b/crates/aiken-lang/src/parser/definitions/mod.rs @@ -1,3 +1,5 @@ +use chumsky::prelude::*; + pub mod constant; mod data_type; mod function; @@ -13,3 +15,20 @@ pub use import::parser as import; pub use test::parser as test; pub use type_alias::parser as type_alias; pub use validator::parser as validator; + +use super::{error::ParseError, token::Token}; +use crate::ast; + +pub fn parser() -> impl Parser, Error = ParseError> { + choice(( + import(), + data_type(), + type_alias(), + validator(), + function(), + test(), + constant(), + )) + .repeated() + .then_ignore(end()) +} diff --git a/crates/aiken-lang/src/parser/expr/mod.rs b/crates/aiken-lang/src/parser/expr/mod.rs index 5f969d5c4..ca360101c 100644 --- a/crates/aiken-lang/src/parser/expr/mod.rs +++ b/crates/aiken-lang/src/parser/expr/mod.rs @@ -1,3 +1,866 @@ +use chumsky::prelude::*; +use vec1::{vec1, Vec1}; + mod sequence; +pub mod string; pub use sequence::parser as sequence; + +use crate::{ + ast::{self, Span}, + expr::UntypedExpr, + parser::error, +}; + +use super::{error::ParseError, token::Token}; + +pub fn parser( + seq_r: Recursive<'_, Token, UntypedExpr, ParseError>, +) -> impl Parser + '_ { + recursive(|r| { + let string_parser = + select! {Token::String {value} => value}.map_with_span(|value, span| { + UntypedExpr::String { + location: span, + value, + } + }); + + let int_parser = select! { Token::Int {value, base} => (value, base)}.map_with_span( + |(value, base), span| UntypedExpr::Int { + location: span, + value, + base, + }, + ); + + let record_update_parser = select! {Token::Name { name } => name} + .map_with_span(|module, span: Span| (module, span)) + .then_ignore(just(Token::Dot)) + .or_not() + .then(select! {Token::UpName { name } => name}.map_with_span(|name, span| (name, span))) + .then( + just(Token::DotDot) + .ignore_then(r.clone()) + .then( + just(Token::Comma) + .ignore_then( + choice(( + select! { Token::Name {name} => name } + .then_ignore(just(Token::Colon)) + .then(r.clone()) + .map_with_span(|(label, value), span| { + ast::UntypedRecordUpdateArg { + label, + value, + location: span, + } + }), + select! {Token::Name {name} => name}.map_with_span( + |name, span| ast::UntypedRecordUpdateArg { + location: span, + value: UntypedExpr::Var { + name: name.clone(), + location: span, + }, + label: name, + }, + ), + )) + .separated_by(just(Token::Comma)) + .allow_trailing(), + ) + .or_not(), + ) + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)) + .map_with_span(|a, span: Span| (a, span)), + ) + .map(|((module, (name, n_span)), ((spread, opt_args), span))| { + let constructor = if let Some((module, m_span)) = module { + UntypedExpr::FieldAccess { + location: m_span.union(n_span), + label: name, + container: Box::new(UntypedExpr::Var { + location: m_span, + name: module, + }), + } + } else { + UntypedExpr::Var { + location: n_span, + name, + } + }; + + let spread_span = spread.location(); + + let location = Span::new((), spread_span.start - 2..spread_span.end); + + let spread = ast::RecordUpdateSpread { + base: Box::new(spread), + location, + }; + + UntypedExpr::RecordUpdate { + location: constructor.location().union(span), + constructor: Box::new(constructor), + spread, + arguments: opt_args.unwrap_or_default(), + } + }); + + let record_parser = choice(( + select! {Token::Name { name } => name} + .map_with_span(|module, span: Span| (module, span)) + .then_ignore(just(Token::Dot)) + .or_not() + .then( + select! {Token::UpName { name } => name} + .map_with_span(|name, span| (name, span)), + ) + .then( + choice(( + select! {Token::Name {name} => name} + .then_ignore(just(Token::Colon)) + .then(choice(( + r.clone(), + select! {Token::DiscardName {name} => name }.validate( + |_name, span, emit| { + emit(ParseError::expected_input_found( + span, + None, + Some(error::Pattern::Discard), + )); + + UntypedExpr::Var { + location: span, + name: ast::CAPTURE_VARIABLE.to_string(), + } + }, + ), + ))) + .map_with_span(|(label, value), span| ast::CallArg { + location: span, + value, + label: Some(label), + }), + choice(( + select! {Token::Name {name} => name}.map_with_span(|name, span| { + ( + UntypedExpr::Var { + name: name.clone(), + location: span, + }, + name, + ) + }), + select! {Token::DiscardName {name} => name }.validate( + |name, span, emit| { + emit(ParseError::expected_input_found( + span, + None, + Some(error::Pattern::Discard), + )); + + ( + UntypedExpr::Var { + location: span, + name: CAPTURE_VARIABLE.to_string(), + }, + name, + ) + }, + ), + )) + .map(|(value, name)| ast::CallArg { + location: value.location(), + value, + label: Some(name), + }), + )) + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), + ), + select! {Token::Name { name } => name} + .map_with_span(|module, span| (module, span)) + .then_ignore(just(Token::Dot)) + .or_not() + .then( + select! {Token::UpName { name } => name} + .map_with_span(|name, span| (name, span)), + ) + .then( + select! {Token::Name {name} => name} + .ignored() + .then_ignore(just(Token::Colon)) + .validate(|_label, span, emit| { + emit(ParseError::expected_input_found( + span, + None, + Some(error::Pattern::Label), + )) + }) + .or_not() + .then(choice(( + r.clone(), + select! {Token::DiscardName {name} => name }.validate( + |_name, span, emit| { + emit(ParseError::expected_input_found( + span, + None, + Some(error::Pattern::Discard), + )); + + UntypedExpr::Var { + location: span, + name: CAPTURE_VARIABLE.to_string(), + } + }, + ), + ))) + .map(|(_label, value)| ast::CallArg { + location: value.location(), + value, + label: None, + }) + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftParen), just(Token::RightParen)), + ), + )) + .map_with_span(|((module, (name, n_span)), arguments), span| { + let fun = if let Some((module, m_span)) = module { + UntypedExpr::FieldAccess { + location: m_span.union(n_span), + label: name, + container: Box::new(UntypedExpr::Var { + location: m_span, + name: module, + }), + } + } else { + UntypedExpr::Var { + location: n_span, + name, + } + }; + + UntypedExpr::Call { + arguments, + fun: Box::new(fun), + location: span, + } + }); + + 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 var_parser = select! { + Token::Name { name } => name, + Token::UpName { name } => name, + } + .map_with_span(|name, span| UntypedExpr::Var { + location: span, + name, + }); + + let tuple = r + .clone() + .separated_by(just(Token::Comma)) + .at_least(2) + .allow_trailing() + .delimited_by( + choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), + just(Token::RightParen), + ) + .map_with_span(|elems, span| UntypedExpr::Tuple { + location: span, + elems, + }); + + let bytearray = utils::bytearray().map_with_span(|(preferred_format, bytes), span| { + UntypedExpr::ByteArray { + location: span, + bytes, + preferred_format, + } + }); + + let list_parser = 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()) + .map(Box::new) + .or_not(), + ), + just(Token::Comma).ignored().or_not().map(|_| None), + ))) + .then_ignore(just(Token::RightSquare)) + // TODO: check if tail.is_some and elements.is_empty then return ListSpreadWithoutElements error + .map_with_span(|(elements, tail), span| UntypedExpr::List { + location: span, + elements, + tail, + }); + + let block_parser = choice(( + seq_r + .clone() + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), + seq_r.clone().delimited_by( + choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), + just(Token::RightParen), + ), + )); + + 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, + Token::Less => BinOp::LtInt, + Token::LessEqual => BinOp::LtEqInt, + Token::Greater => BinOp::GtInt, + Token::GreaterEqual => BinOp::GtEqInt, + Token::VbarVbar => BinOp::Or, + Token::AmperAmper => BinOp::And, + Token::Plus => BinOp::AddInt, + Token::Minus => BinOp::SubInt, + Token::Slash => BinOp::DivInt, + Token::Star => BinOp::MultInt, + Token::Percent => BinOp::ModInt, + } + .map_with_span(|name, location| { + use BinOp::*; + + let arg_annotation = match name { + Or | And => Some(ast::Annotation::boolean(location)), + Eq | NotEq => None, + LtInt | LtEqInt | GtInt | GtEqInt | AddInt | SubInt | MultInt | DivInt | ModInt => { + Some(ast::Annotation::int(location)) + } + }; + + let return_annotation = match name { + Or | And | Eq | NotEq | LtInt | LtEqInt | GtInt | GtEqInt => { + Some(ast::Annotation::boolean(location)) + } + AddInt | SubInt | MultInt | DivInt | ModInt => Some(ast::Annotation::int(location)), + }; + + let arguments = vec![ + ast::Arg { + arg_name: ast::ArgName::Named { + name: "left".to_string(), + label: "left".to_string(), + location, + is_validator_param: false, + }, + annotation: arg_annotation.clone(), + location, + tipo: (), + }, + ast::Arg { + arg_name: ast::ArgName::Named { + name: "right".to_string(), + label: "right".to_string(), + location, + is_validator_param: false, + }, + annotation: arg_annotation, + location, + tipo: (), + }, + ]; + + let body = UntypedExpr::BinOp { + location, + name, + left: Box::new(UntypedExpr::Var { + location, + name: "left".to_string(), + }), + right: Box::new(UntypedExpr::Var { + location, + name: "right".to_string(), + }), + }; + + UntypedExpr::Fn { + arguments, + body: Box::new(body), + return_annotation, + fn_style: FnStyle::BinOp(name), + location, + } + }); + + let when_clause_parser = pattern_parser() + .then( + just(Token::Vbar) + .ignore_then(pattern_parser()) + .repeated() + .or_not(), + ) + .then(choice(( + just(Token::If) + .ignore_then(when_clause_guard_parser()) + .or_not() + .then_ignore(just(Token::RArrow)), + just(Token::If) + .ignore_then(take_until(just(Token::RArrow))) + .validate(|_value, span, emit| { + emit(ParseError::invalid_when_clause_guard(span)); + None + }), + ))) + // TODO: add hint "Did you mean to wrap a multi line clause in curly braces?" + .then(choice(( + r.clone(), + just(Token::Todo) + .ignore_then( + r.clone() + .then_ignore(one_of(Token::RArrow).not().rewind()) + .or_not(), + ) + .map_with_span(|reason, span| { + UntypedExpr::todo(span, reason.map(flexible_string_literal)) + }), + just(Token::ErrorTerm) + .ignore_then( + r.clone() + .then_ignore(just(Token::RArrow).not().rewind()) + .or_not(), + ) + .map_with_span(|reason, span| { + UntypedExpr::error(span, reason.map(flexible_string_literal)) + }), + ))) + .map_with_span( + |(((pattern, alternative_patterns_opt), guard), then), span| { + let mut patterns = vec1![pattern]; + patterns.append(&mut alternative_patterns_opt.unwrap_or_default()); + ast::UntypedClause { + location: span, + patterns, + guard, + then, + } + }, + ); + + let when_parser = just(Token::When) + // TODO: If subject is empty we should return ParseErrorType::ExpectedExpr, + .ignore_then(r.clone().map(Box::new)) + .then_ignore(just(Token::Is)) + .then_ignore(just(Token::LeftBrace)) + // TODO: If clauses are empty we should return ParseErrorType::NoCaseClause + .then(when_clause_parser.repeated()) + .then_ignore(just(Token::RightBrace)) + .map_with_span(|(subject, clauses), span| UntypedExpr::When { + location: span, + subject, + 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 if_parser = just(Token::If) + .ignore_then(r.clone().then(block_parser.clone()).map_with_span( + |(condition, body), span| ast::IfBranch { + condition, + body, + location: span, + }, + )) + .then( + just(Token::Else) + .ignore_then(just(Token::If)) + .ignore_then(r.clone().then(block_parser.clone()).map_with_span( + |(condition, body), span| ast::IfBranch { + condition, + body, + location: span, + }, + )) + .repeated(), + ) + .then_ignore(just(Token::Else)) + .then(block_parser.clone()) + .map_with_span(|((first, alternative_branches), final_else), span| { + let mut branches = vec1::vec1![first]; + + branches.extend(alternative_branches); + + UntypedExpr::If { + location: span, + branches, + final_else: Box::new(final_else), + } + }); + + let expr_unit_parser = choice(( + string_parser, + int_parser, + record_update_parser, + record_parser, + field_access_constructor, + var_parser, + tuple, + bytearray, + list_parser, + anon_fn_parser, + anon_binop_parser, + block_parser, + when_parser, + let_parser, + expect_parser, + if_parser, + )); + + // 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(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)) + .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!("{CAPTURE_VARIABLE}__{index}"); + 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!("{CAPTURE_VARIABLE}__{index}"), + }, + } + } + }) + .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 { + Some(_) => UntypedExpr::TraceIfFalse { + value: Box::new(value), + location, + }, + None => value, + }, + ); + + // Negate + let op = choice(( + just(Token::Bang).to(UnOp::Not), + just(Token::Minus) + // NOTE: Prevent conflict with usage for '-' as a standalone binary op. + // This will make '-' parse when used as standalone binop in a function call. + // For example: + // + // foo(a, -, b) + // + // but it'll fail in a let-binding: + // + // let foo = - + // + // which seems acceptable. + .then_ignore(just(Token::Comma).not().rewind()) + .to(UnOp::Negate), + )); + + let unary = op + .map_with_span(|op, span| (op, span)) + .repeated() + .then(debug) + .foldr(|(un_op, span), value| UntypedExpr::UnOp { + op: un_op, + location: span.union(value.location()), + value: Box::new(value), + }) + .boxed(); + + // Product + let op = choice(( + just(Token::Star).to(BinOp::MultInt), + just(Token::Slash).to(BinOp::DivInt), + just(Token::Percent).to(BinOp::ModInt), + )); + + let product = unary + .clone() + .then(op.then(unary).repeated()) + .foldl(|a, (op, b)| UntypedExpr::BinOp { + location: a.location().union(b.location()), + name: op, + left: Box::new(a), + right: Box::new(b), + }) + .boxed(); + + // Sum + let op = choice(( + just(Token::Plus).to(BinOp::AddInt), + just(Token::Minus).to(BinOp::SubInt), + )); + + let sum = product + .clone() + .then(op.then(product).repeated()) + .foldl(|a, (op, b)| UntypedExpr::BinOp { + location: a.location().union(b.location()), + name: op, + left: Box::new(a), + right: Box::new(b), + }) + .boxed(); + + // Comparison + let op = choice(( + just(Token::EqualEqual).to(BinOp::Eq), + just(Token::NotEqual).to(BinOp::NotEq), + just(Token::Less).to(BinOp::LtInt), + just(Token::Greater).to(BinOp::GtInt), + just(Token::LessEqual).to(BinOp::LtEqInt), + just(Token::GreaterEqual).to(BinOp::GtEqInt), + )); + + let comparison = sum + .clone() + .then(op.then(sum).repeated()) + .foldl(|a, (op, b)| UntypedExpr::BinOp { + location: a.location().union(b.location()), + name: op, + left: Box::new(a), + right: Box::new(b), + }) + .boxed(); + + // Conjunction + let op = just(Token::AmperAmper).to(BinOp::And); + let conjunction = comparison + .clone() + .then(op.then(comparison).repeated()) + .foldl(|a, (op, b)| UntypedExpr::BinOp { + location: a.location().union(b.location()), + name: op, + left: Box::new(a), + right: Box::new(b), + }) + .boxed(); + + // Disjunction + let op = just(Token::VbarVbar).to(BinOp::Or); + let disjunction = conjunction + .clone() + .then(op.then(conjunction).repeated()) + .foldl(|a, (op, b)| UntypedExpr::BinOp { + location: a.location().union(b.location()), + name: op, + left: Box::new(a), + right: Box::new(b), + }) + .boxed(); + + // Pipeline + disjunction + .clone() + .then( + choice((just(Token::Pipe), just(Token::NewLinePipe))) + .then(disjunction) + .repeated(), + ) + .foldl(|l, (pipe, r)| { + if let UntypedExpr::PipeLine { + mut expressions, + one_liner, + } = l + { + expressions.push(r); + return UntypedExpr::PipeLine { + expressions, + one_liner, + }; + } + + let mut expressions = Vec1::new(l); + expressions.push(r); + UntypedExpr::PipeLine { + expressions, + one_liner: pipe != Token::NewLinePipe, + } + }) + }) +} diff --git a/crates/aiken-lang/src/parser/expr/sequence.rs b/crates/aiken-lang/src/parser/expr/sequence.rs index 0be03bfad..38e5eab24 100644 --- a/crates/aiken-lang/src/parser/expr/sequence.rs +++ b/crates/aiken-lang/src/parser/expr/sequence.rs @@ -10,25 +10,25 @@ pub fn parser() -> impl Parser { recursive(|r| { choice(( just(Token::Trace) - .ignore_then(expr_parser(r.clone())) + .ignore_then(super::parser(r.clone())) .then(r.clone()) .map_with_span(|(text, then_), span| UntypedExpr::Trace { kind: TraceKind::Trace, location: span, then: Box::new(then_), - text: Box::new(flexible_string_literal(text)), + text: Box::new(super::string::flexible(text)), }), just(Token::ErrorTerm) - .ignore_then(expr_parser(r.clone()).or_not()) + .ignore_then(super::parser(r.clone()).or_not()) .map_with_span(|reason, span| { - UntypedExpr::error(span, reason.map(flexible_string_literal)) + UntypedExpr::error(span, reason.map(super::string::flexible)) }), just(Token::Todo) - .ignore_then(expr_parser(r.clone()).or_not()) + .ignore_then(super::parser(r.clone()).or_not()) .map_with_span(|reason, span| { - UntypedExpr::todo(span, reason.map(flexible_string_literal)) + UntypedExpr::todo(span, reason.map(super::string::flexible)) }), - expr_parser(r.clone()) + super::parser(r.clone()) .then(r.repeated()) .foldl(|current, next| current.append_in_sequence(next)), )) diff --git a/crates/aiken-lang/src/parser/expr/string.rs b/crates/aiken-lang/src/parser/expr/string.rs new file mode 100644 index 000000000..1f004fd4c --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/string.rs @@ -0,0 +1,20 @@ +use crate::{ast, expr::UntypedExpr}; + +/// Interpret bytearray string literals written as utf-8 strings, as strings. +/// +/// This is mostly convenient so that todo & error works with either @"..." or plain "...". +/// In this particular context, there's actually no ambiguity about the right-hand-side, so +/// we can provide this syntactic sugar. +pub fn flexible(expr: UntypedExpr) -> UntypedExpr { + match expr { + UntypedExpr::ByteArray { + preferred_format: ast::ByteArrayFormatPreference::Utf8String, + bytes, + location, + } => UntypedExpr::String { + location, + value: String::from_utf8(bytes).unwrap(), + }, + _ => expr, + } +} diff --git a/crates/aiken-lang/src/parser/module.rs b/crates/aiken-lang/src/parser/module.rs deleted file mode 100644 index 2e18b90b8..000000000 --- a/crates/aiken-lang/src/parser/module.rs +++ /dev/null @@ -1,19 +0,0 @@ -use chumsky::prelude::*; - -use crate::ast::UntypedDefinition; - -use super::{definitions, error::ParseError, token::Token}; - -pub fn parser() -> impl Parser, Error = ParseError> { - choice(( - definitions::import(), - definitions::data_type(), - definitions::type_alias(), - definitions::validator(), - definitions::function(), - definitions::test(), - definitions::constant(), - )) - .repeated() - .then_ignore(end()) -} diff --git a/crates/aiken-lang/src/parser/pattern/mod.rs b/crates/aiken-lang/src/parser/pattern/mod.rs new file mode 100644 index 000000000..e69de29bb From 9c98fc8026cfa0109652406842d528934a4c79ad Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 29 Jun 2023 23:09:16 -0400 Subject: [PATCH 04/31] feat: start splitting apart expr_parser --- crates/aiken-lang/src/parser/expr/block.rs | 20 + .../aiken-lang/src/parser/expr/bytearray.rs | 14 + crates/aiken-lang/src/parser/expr/if_else.rs | 48 +++ crates/aiken-lang/src/parser/expr/int.rs | 16 + crates/aiken-lang/src/parser/expr/list.rs | 29 ++ crates/aiken-lang/src/parser/expr/mod.rs | 371 ++---------------- crates/aiken-lang/src/parser/expr/record.rs | 152 +++++++ .../src/parser/expr/record_update.rs | 86 ++++ crates/aiken-lang/src/parser/expr/string.rs | 15 +- crates/aiken-lang/src/parser/expr/tuple.rs | 23 ++ crates/aiken-lang/src/parser/expr/var.rs | 17 + 11 files changed, 449 insertions(+), 342 deletions(-) create mode 100644 crates/aiken-lang/src/parser/expr/block.rs create mode 100644 crates/aiken-lang/src/parser/expr/bytearray.rs create mode 100644 crates/aiken-lang/src/parser/expr/if_else.rs create mode 100644 crates/aiken-lang/src/parser/expr/int.rs create mode 100644 crates/aiken-lang/src/parser/expr/list.rs create mode 100644 crates/aiken-lang/src/parser/expr/record.rs create mode 100644 crates/aiken-lang/src/parser/expr/record_update.rs create mode 100644 crates/aiken-lang/src/parser/expr/tuple.rs create mode 100644 crates/aiken-lang/src/parser/expr/var.rs diff --git a/crates/aiken-lang/src/parser/expr/block.rs b/crates/aiken-lang/src/parser/expr/block.rs new file mode 100644 index 000000000..320723f25 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/block.rs @@ -0,0 +1,20 @@ +use chumsky::prelude::*; + +use crate::{ + expr::UntypedExpr, + parser::{error::ParseError, token::Token}, +}; + +pub fn parser( + seq_r: Recursive<'_, Token, UntypedExpr, ParseError>, +) -> impl Parser + '_ { + choice(( + seq_r + .clone() + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), + seq_r.clone().delimited_by( + choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), + just(Token::RightParen), + ), + )) +} diff --git a/crates/aiken-lang/src/parser/expr/bytearray.rs b/crates/aiken-lang/src/parser/expr/bytearray.rs new file mode 100644 index 000000000..66b356f7b --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/bytearray.rs @@ -0,0 +1,14 @@ +use chumsky::prelude::*; + +use crate::{ + expr::UntypedExpr, + parser::{error::ParseError, token::Token, utils}, +}; + +pub fn parser() -> impl Parser { + utils::bytearray().map_with_span(|(preferred_format, bytes), span| UntypedExpr::ByteArray { + location: span, + bytes, + preferred_format, + }) +} diff --git a/crates/aiken-lang/src/parser/expr/if_else.rs b/crates/aiken-lang/src/parser/expr/if_else.rs new file mode 100644 index 000000000..0d1868187 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/if_else.rs @@ -0,0 +1,48 @@ +use chumsky::prelude::*; + +use crate::{ + ast, + expr::UntypedExpr, + parser::{error::ParseError, token::Token}, +}; + +use super::block; + +pub fn parser<'a>( + seq_r: Recursive<'a, Token, UntypedExpr, ParseError>, + r: Recursive<'a, Token, UntypedExpr, ParseError>, +) -> impl Parser + 'a { + just(Token::If) + .ignore_then(r.clone().then(block(seq_r.clone())).map_with_span( + |(condition, body), span| ast::IfBranch { + condition, + body, + location: span, + }, + )) + .then( + just(Token::Else) + .ignore_then(just(Token::If)) + .ignore_then(r.clone().then(block(seq_r.clone())).map_with_span( + |(condition, body), span| ast::IfBranch { + condition, + body, + location: span, + }, + )) + .repeated(), + ) + .then_ignore(just(Token::Else)) + .then(block(seq_r)) + .map_with_span(|((first, alternative_branches), final_else), span| { + let mut branches = vec1::vec1![first]; + + branches.extend(alternative_branches); + + UntypedExpr::If { + location: span, + branches, + final_else: Box::new(final_else), + } + }) +} diff --git a/crates/aiken-lang/src/parser/expr/int.rs b/crates/aiken-lang/src/parser/expr/int.rs new file mode 100644 index 000000000..aa73a32d2 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/int.rs @@ -0,0 +1,16 @@ +use chumsky::prelude::*; + +use crate::{ + expr::UntypedExpr, + parser::{error::ParseError, token::Token}, +}; + +pub fn parser() -> impl Parser { + select! { Token::Int {value, base} => (value, base)}.map_with_span(|(value, base), span| { + UntypedExpr::Int { + location: span, + value, + base, + } + }) +} diff --git a/crates/aiken-lang/src/parser/expr/list.rs b/crates/aiken-lang/src/parser/expr/list.rs new file mode 100644 index 000000000..87f37bbe4 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/list.rs @@ -0,0 +1,29 @@ +use chumsky::prelude::*; + +use crate::{ + expr::UntypedExpr, + parser::{error::ParseError, token::Token}, +}; + +pub fn parser( + r: Recursive<'_, Token, UntypedExpr, ParseError>, +) -> impl Parser + '_ { + 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()) + .map(Box::new) + .or_not(), + ), + just(Token::Comma).ignored().or_not().map(|_| None), + ))) + .then_ignore(just(Token::RightSquare)) + // TODO: check if tail.is_some and elements.is_empty then return ListSpreadWithoutElements error + .map_with_span(|(elements, tail), span| UntypedExpr::List { + location: span, + elements, + tail, + }) +} diff --git a/crates/aiken-lang/src/parser/expr/mod.rs b/crates/aiken-lang/src/parser/expr/mod.rs index ca360101c..41d1fb50d 100644 --- a/crates/aiken-lang/src/parser/expr/mod.rs +++ b/crates/aiken-lang/src/parser/expr/mod.rs @@ -1,15 +1,33 @@ use chumsky::prelude::*; use vec1::{vec1, Vec1}; +mod block; +mod bytearray; +mod if_else; +mod int; +mod list; +mod record; +mod record_update; mod sequence; pub mod string; - +mod tuple; +mod var; + +pub use block::parser as block; +pub use bytearray::parser as bytearray; +pub use if_else::parser as if_else; +pub use int::parser as int; +pub use list::parser as list; +pub use record::parser as record; +pub use record_update::parser as record_update; pub use sequence::parser as sequence; +pub use string::parser as string; +pub use tuple::parser as tuple; +pub use var::parser as var; use crate::{ ast::{self, Span}, expr::UntypedExpr, - parser::error, }; use super::{error::ParseError, token::Token}; @@ -18,241 +36,6 @@ pub fn parser( seq_r: Recursive<'_, Token, UntypedExpr, ParseError>, ) -> impl Parser + '_ { recursive(|r| { - let string_parser = - select! {Token::String {value} => value}.map_with_span(|value, span| { - UntypedExpr::String { - location: span, - value, - } - }); - - let int_parser = select! { Token::Int {value, base} => (value, base)}.map_with_span( - |(value, base), span| UntypedExpr::Int { - location: span, - value, - base, - }, - ); - - let record_update_parser = select! {Token::Name { name } => name} - .map_with_span(|module, span: Span| (module, span)) - .then_ignore(just(Token::Dot)) - .or_not() - .then(select! {Token::UpName { name } => name}.map_with_span(|name, span| (name, span))) - .then( - just(Token::DotDot) - .ignore_then(r.clone()) - .then( - just(Token::Comma) - .ignore_then( - choice(( - select! { Token::Name {name} => name } - .then_ignore(just(Token::Colon)) - .then(r.clone()) - .map_with_span(|(label, value), span| { - ast::UntypedRecordUpdateArg { - label, - value, - location: span, - } - }), - select! {Token::Name {name} => name}.map_with_span( - |name, span| ast::UntypedRecordUpdateArg { - location: span, - value: UntypedExpr::Var { - name: name.clone(), - location: span, - }, - label: name, - }, - ), - )) - .separated_by(just(Token::Comma)) - .allow_trailing(), - ) - .or_not(), - ) - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)) - .map_with_span(|a, span: Span| (a, span)), - ) - .map(|((module, (name, n_span)), ((spread, opt_args), span))| { - let constructor = if let Some((module, m_span)) = module { - UntypedExpr::FieldAccess { - location: m_span.union(n_span), - label: name, - container: Box::new(UntypedExpr::Var { - location: m_span, - name: module, - }), - } - } else { - UntypedExpr::Var { - location: n_span, - name, - } - }; - - let spread_span = spread.location(); - - let location = Span::new((), spread_span.start - 2..spread_span.end); - - let spread = ast::RecordUpdateSpread { - base: Box::new(spread), - location, - }; - - UntypedExpr::RecordUpdate { - location: constructor.location().union(span), - constructor: Box::new(constructor), - spread, - arguments: opt_args.unwrap_or_default(), - } - }); - - let record_parser = choice(( - select! {Token::Name { name } => name} - .map_with_span(|module, span: Span| (module, span)) - .then_ignore(just(Token::Dot)) - .or_not() - .then( - select! {Token::UpName { name } => name} - .map_with_span(|name, span| (name, span)), - ) - .then( - choice(( - select! {Token::Name {name} => name} - .then_ignore(just(Token::Colon)) - .then(choice(( - r.clone(), - select! {Token::DiscardName {name} => name }.validate( - |_name, span, emit| { - emit(ParseError::expected_input_found( - span, - None, - Some(error::Pattern::Discard), - )); - - UntypedExpr::Var { - location: span, - name: ast::CAPTURE_VARIABLE.to_string(), - } - }, - ), - ))) - .map_with_span(|(label, value), span| ast::CallArg { - location: span, - value, - label: Some(label), - }), - choice(( - select! {Token::Name {name} => name}.map_with_span(|name, span| { - ( - UntypedExpr::Var { - name: name.clone(), - location: span, - }, - name, - ) - }), - select! {Token::DiscardName {name} => name }.validate( - |name, span, emit| { - emit(ParseError::expected_input_found( - span, - None, - Some(error::Pattern::Discard), - )); - - ( - UntypedExpr::Var { - location: span, - name: CAPTURE_VARIABLE.to_string(), - }, - name, - ) - }, - ), - )) - .map(|(value, name)| ast::CallArg { - location: value.location(), - value, - label: Some(name), - }), - )) - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), - ), - select! {Token::Name { name } => name} - .map_with_span(|module, span| (module, span)) - .then_ignore(just(Token::Dot)) - .or_not() - .then( - select! {Token::UpName { name } => name} - .map_with_span(|name, span| (name, span)), - ) - .then( - select! {Token::Name {name} => name} - .ignored() - .then_ignore(just(Token::Colon)) - .validate(|_label, span, emit| { - emit(ParseError::expected_input_found( - span, - None, - Some(error::Pattern::Label), - )) - }) - .or_not() - .then(choice(( - r.clone(), - select! {Token::DiscardName {name} => name }.validate( - |_name, span, emit| { - emit(ParseError::expected_input_found( - span, - None, - Some(error::Pattern::Discard), - )); - - UntypedExpr::Var { - location: span, - name: CAPTURE_VARIABLE.to_string(), - } - }, - ), - ))) - .map(|(_label, value)| ast::CallArg { - location: value.location(), - value, - label: None, - }) - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftParen), just(Token::RightParen)), - ), - )) - .map_with_span(|((module, (name, n_span)), arguments), span| { - let fun = if let Some((module, m_span)) = module { - UntypedExpr::FieldAccess { - location: m_span.union(n_span), - label: name, - container: Box::new(UntypedExpr::Var { - location: m_span, - name: module, - }), - } - } else { - UntypedExpr::Var { - location: n_span, - name, - } - }; - - UntypedExpr::Call { - arguments, - fun: Box::new(fun), - location: span, - } - }); - let field_access_constructor = select! {Token::Name { name } => name} .map_with_span(|module, span| (module, span)) .then_ignore(just(Token::Dot)) @@ -266,66 +49,6 @@ pub fn parser( }), }); - let var_parser = select! { - Token::Name { name } => name, - Token::UpName { name } => name, - } - .map_with_span(|name, span| UntypedExpr::Var { - location: span, - name, - }); - - let tuple = r - .clone() - .separated_by(just(Token::Comma)) - .at_least(2) - .allow_trailing() - .delimited_by( - choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), - just(Token::RightParen), - ) - .map_with_span(|elems, span| UntypedExpr::Tuple { - location: span, - elems, - }); - - let bytearray = utils::bytearray().map_with_span(|(preferred_format, bytes), span| { - UntypedExpr::ByteArray { - location: span, - bytes, - preferred_format, - } - }); - - let list_parser = 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()) - .map(Box::new) - .or_not(), - ), - just(Token::Comma).ignored().or_not().map(|_| None), - ))) - .then_ignore(just(Token::RightSquare)) - // TODO: check if tail.is_some and elements.is_empty then return ListSpreadWithoutElements error - .map_with_span(|(elements, tail), span| UntypedExpr::List { - location: span, - elements, - tail, - }); - - let block_parser = choice(( - seq_r - .clone() - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), - seq_r.clone().delimited_by( - choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), - just(Token::RightParen), - ), - )); - let anon_fn_parser = just(Token::Fn) .ignore_then( anon_fn_param_parser() @@ -523,57 +246,23 @@ pub fn parser( }, ); - let if_parser = just(Token::If) - .ignore_then(r.clone().then(block_parser.clone()).map_with_span( - |(condition, body), span| ast::IfBranch { - condition, - body, - location: span, - }, - )) - .then( - just(Token::Else) - .ignore_then(just(Token::If)) - .ignore_then(r.clone().then(block_parser.clone()).map_with_span( - |(condition, body), span| ast::IfBranch { - condition, - body, - location: span, - }, - )) - .repeated(), - ) - .then_ignore(just(Token::Else)) - .then(block_parser.clone()) - .map_with_span(|((first, alternative_branches), final_else), span| { - let mut branches = vec1::vec1![first]; - - branches.extend(alternative_branches); - - UntypedExpr::If { - location: span, - branches, - final_else: Box::new(final_else), - } - }); - let expr_unit_parser = choice(( - string_parser, - int_parser, - record_update_parser, - record_parser, + string(), + int(), + record_update(r), + record(r), field_access_constructor, - var_parser, - tuple, - bytearray, - list_parser, + var(), + tuple(r), + bytearray(), + list(r), anon_fn_parser, anon_binop_parser, - block_parser, + block(seq_r), when_parser, let_parser, expect_parser, - if_parser, + if_else(seq_r, r), )); // Parsing a function call into the appropriate structure diff --git a/crates/aiken-lang/src/parser/expr/record.rs b/crates/aiken-lang/src/parser/expr/record.rs new file mode 100644 index 000000000..d27184f76 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/record.rs @@ -0,0 +1,152 @@ +use chumsky::prelude::*; + +use crate::{ + ast, + expr::UntypedExpr, + parser::{ + error::{self, ParseError}, + token::Token, + }, +}; + +pub fn parser( + r: Recursive<'_, Token, UntypedExpr, ParseError>, +) -> impl Parser + '_ { + choice(( + select! {Token::Name { name } => name} + .map_with_span(|module, span: ast::Span| (module, span)) + .then_ignore(just(Token::Dot)) + .or_not() + .then(select! {Token::UpName { name } => name}.map_with_span(|name, span| (name, span))) + .then( + choice(( + select! {Token::Name {name} => name} + .then_ignore(just(Token::Colon)) + .then(choice(( + r.clone(), + select! {Token::DiscardName {name} => name }.validate( + |_name, span, emit| { + emit(ParseError::expected_input_found( + span, + None, + Some(error::Pattern::Discard), + )); + + UntypedExpr::Var { + location: span, + name: ast::CAPTURE_VARIABLE.to_string(), + } + }, + ), + ))) + .map_with_span(|(label, value), span| ast::CallArg { + location: span, + value, + label: Some(label), + }), + choice(( + select! {Token::Name {name} => name}.map_with_span(|name, span| { + ( + UntypedExpr::Var { + name: name.clone(), + location: span, + }, + name, + ) + }), + select! {Token::DiscardName {name} => name }.validate( + |name, span, emit| { + emit(ParseError::expected_input_found( + span, + None, + Some(error::Pattern::Discard), + )); + + ( + UntypedExpr::Var { + location: span, + name: ast::CAPTURE_VARIABLE.to_string(), + }, + name, + ) + }, + ), + )) + .map(|(value, name)| ast::CallArg { + location: value.location(), + value, + label: Some(name), + }), + )) + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), + ), + select! {Token::Name { name } => name} + .map_with_span(|module, span| (module, span)) + .then_ignore(just(Token::Dot)) + .or_not() + .then(select! {Token::UpName { name } => name}.map_with_span(|name, span| (name, span))) + .then( + select! {Token::Name {name} => name} + .ignored() + .then_ignore(just(Token::Colon)) + .validate(|_label, span, emit| { + emit(ParseError::expected_input_found( + span, + None, + Some(error::Pattern::Label), + )) + }) + .or_not() + .then(choice(( + r.clone(), + select! {Token::DiscardName {name} => name }.validate( + |_name, span, emit| { + emit(ParseError::expected_input_found( + span, + None, + Some(error::Pattern::Discard), + )); + + UntypedExpr::Var { + location: span, + name: ast::CAPTURE_VARIABLE.to_string(), + } + }, + ), + ))) + .map(|(_label, value)| ast::CallArg { + location: value.location(), + value, + label: None, + }) + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftParen), just(Token::RightParen)), + ), + )) + .map_with_span(|((module, (name, n_span)), arguments), span| { + let fun = if let Some((module, m_span)) = module { + UntypedExpr::FieldAccess { + location: m_span.union(n_span), + label: name, + container: Box::new(UntypedExpr::Var { + location: m_span, + name: module, + }), + } + } else { + UntypedExpr::Var { + location: n_span, + name, + } + }; + + UntypedExpr::Call { + arguments, + fun: Box::new(fun), + location: span, + } + }) +} diff --git a/crates/aiken-lang/src/parser/expr/record_update.rs b/crates/aiken-lang/src/parser/expr/record_update.rs new file mode 100644 index 000000000..11dc9ea6d --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/record_update.rs @@ -0,0 +1,86 @@ +use chumsky::prelude::*; + +use crate::{ + ast, + expr::UntypedExpr, + parser::{error::ParseError, token::Token}, +}; + +pub fn parser( + r: Recursive<'_, Token, UntypedExpr, ParseError>, +) -> impl Parser + '_ { + select! {Token::Name { name } => name} + .map_with_span(|module, span: ast::Span| (module, span)) + .then_ignore(just(Token::Dot)) + .or_not() + .then(select! {Token::UpName { name } => name}.map_with_span(|name, span| (name, span))) + .then( + just(Token::DotDot) + .ignore_then(r.clone()) + .then( + just(Token::Comma) + .ignore_then( + choice(( + select! { Token::Name {name} => name } + .then_ignore(just(Token::Colon)) + .then(r.clone()) + .map_with_span(|(label, value), span| { + ast::UntypedRecordUpdateArg { + label, + value, + location: span, + } + }), + select! {Token::Name {name} => name}.map_with_span(|name, span| { + ast::UntypedRecordUpdateArg { + location: span, + value: UntypedExpr::Var { + name: name.clone(), + location: span, + }, + label: name, + } + }), + )) + .separated_by(just(Token::Comma)) + .allow_trailing(), + ) + .or_not(), + ) + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)) + .map_with_span(|a, span: ast::Span| (a, span)), + ) + .map(|((module, (name, n_span)), ((spread, opt_args), span))| { + let constructor = if let Some((module, m_span)) = module { + UntypedExpr::FieldAccess { + location: m_span.union(n_span), + label: name, + container: Box::new(UntypedExpr::Var { + location: m_span, + name: module, + }), + } + } else { + UntypedExpr::Var { + location: n_span, + name, + } + }; + + let spread_span = spread.location(); + + let location = ast::Span::new((), spread_span.start - 2..spread_span.end); + + let spread = ast::RecordUpdateSpread { + base: Box::new(spread), + location, + }; + + UntypedExpr::RecordUpdate { + location: constructor.location().union(span), + constructor: Box::new(constructor), + spread, + arguments: opt_args.unwrap_or_default(), + } + }) +} diff --git a/crates/aiken-lang/src/parser/expr/string.rs b/crates/aiken-lang/src/parser/expr/string.rs index 1f004fd4c..ca5c94bab 100644 --- a/crates/aiken-lang/src/parser/expr/string.rs +++ b/crates/aiken-lang/src/parser/expr/string.rs @@ -1,4 +1,17 @@ -use crate::{ast, expr::UntypedExpr}; +use chumsky::prelude::*; + +use crate::{ + ast, + expr::UntypedExpr, + parser::{error::ParseError, token::Token}, +}; + +pub fn parser() -> impl Parser { + select! {Token::String {value} => value}.map_with_span(|value, span| UntypedExpr::String { + location: span, + value, + }) +} /// Interpret bytearray string literals written as utf-8 strings, as strings. /// diff --git a/crates/aiken-lang/src/parser/expr/tuple.rs b/crates/aiken-lang/src/parser/expr/tuple.rs new file mode 100644 index 000000000..6f1e1bbf2 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/tuple.rs @@ -0,0 +1,23 @@ +use chumsky::prelude::*; + +use crate::{ + expr::UntypedExpr, + parser::{error::ParseError, token::Token}, +}; + +pub fn parser( + r: Recursive<'_, Token, UntypedExpr, ParseError>, +) -> impl Parser + '_ { + r.clone() + .separated_by(just(Token::Comma)) + .at_least(2) + .allow_trailing() + .delimited_by( + choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), + just(Token::RightParen), + ) + .map_with_span(|elems, span| UntypedExpr::Tuple { + location: span, + elems, + }) +} diff --git a/crates/aiken-lang/src/parser/expr/var.rs b/crates/aiken-lang/src/parser/expr/var.rs new file mode 100644 index 000000000..b80c1da8a --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/var.rs @@ -0,0 +1,17 @@ +use chumsky::prelude::*; + +use crate::{ + expr::UntypedExpr, + parser::{error::ParseError, token::Token}, +}; + +pub fn parser() -> impl Parser { + select! { + Token::Name { name } => name, + Token::UpName { name } => name, + } + .map_with_span(|name, span| UntypedExpr::Var { + location: span, + name, + }) +} From eea94fc9a47be7d3492b131d03a7a67a8369f709 Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 30 Jun 2023 00:20:12 -0400 Subject: [PATCH 05/31] 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 17e786803..84ef18093 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 000000000..9e8ae29d8 --- /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 000000000..4b25431ac --- /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 41d1fb50d..df8ac0feb 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 e69de29bb..6a3f1b68a 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 + } + }) + }) +} From 58c854fd3f952ec865cdbdca6d841d8e7c14278d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cain=C3=A3=20Costa?= Date: Fri, 30 Jun 2023 12:28:48 -0300 Subject: [PATCH 06/31] feat: add insta as dependency We are going to start to add "golden"/snapshot tests, so we are using [insta](https://insta.rs) to do so. --- Cargo.lock | 54 ++++++++++++++++++++++++++++++++++++ Cargo.toml | 6 ++++ crates/aiken-lang/Cargo.toml | 7 +++++ 3 files changed, 67 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 25a29a3c8..d6a09b89d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,6 +83,7 @@ dependencies = [ "hex", "indexmap", "indoc", + "insta", "itertools", "miette", "num-bigint", @@ -525,6 +526,18 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "const-oid" version = "0.9.2" @@ -738,6 +751,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.32" @@ -1208,6 +1227,20 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f2cb48b81b1dc9f39676bf99f5499babfec7cd8fe14307f7b3d747208fb5690" +[[package]] +name = "insta" +version = "1.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28491f7753051e5704d4d0ae7860d45fae3238d7d235bc4289dcd45c48d3cec3" +dependencies = [ + "console", + "lazy_static", + "linked-hash-map", + "serde", + "similar", + "yaml-rust", +] + [[package]] name = "instant" version = "0.1.12" @@ -1341,6 +1374,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.3.1" @@ -2385,6 +2424,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "similar" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" + [[package]] name = "slab" version = "0.4.8" @@ -3257,6 +3302,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 6a22a2d40..8de2641bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,9 @@ strip = true [workspace.metadata.release] shared-version = true tag-name = "v{{version}}" + +[profile.dev.package.insta] +opt-level = 3 + +[profile.dev.package.similar] +opt-level = 3 diff --git a/crates/aiken-lang/Cargo.toml b/crates/aiken-lang/Cargo.toml index 6a079f21b..365e83863 100644 --- a/crates/aiken-lang/Cargo.toml +++ b/crates/aiken-lang/Cargo.toml @@ -34,4 +34,11 @@ chumsky = { version = "0.9.2", features = ["ahash", "std"], default-features = f [dev-dependencies] indoc = "2.0.1" +insta = { version = "1.30.0", features = ["yaml"] } pretty_assertions = "1.3.0" + +[profile.dev.package.insta] +opt-level = 3 + +[profile.dev.package.similar] +opt-level = 3 From eeaa1a05d225ced6b3d7b448e8620ca9ec582dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cain=C3=A3=20Costa?= Date: Fri, 30 Jun 2023 12:37:20 -0300 Subject: [PATCH 07/31] feat: add first code snapshot test with insta --- crates/aiken-lang/src/tests/parser.rs | 11 +++++++++ ...en_lang__tests__parser__snapshot_test.snap | 24 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__snapshot_test.snap diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 902bec422..df0e0c88b 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -23,6 +23,17 @@ fn assert_definitions(code: &str, definitions: Vec) { ) } +fn snapshot_test(code: &str) { + let (module, _) = + parser::module(code, ast::ModuleKind::Validator).expect("Failed to parse code"); + insta::assert_debug_snapshot!(module); +} + +#[test] +fn snapshot_windows_newline() { + snapshot_test("use aiken/list\r\n") +} + #[test] fn windows_newline() { let code = "use aiken/list\r\n"; diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__snapshot_test.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__snapshot_test.snap new file mode 100644 index 000000000..a604e44e8 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__snapshot_test.snap @@ -0,0 +1,24 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +expression: module +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Use( + Use { + as_name: None, + location: 0..14, + module: [ + "aiken", + "list", + ], + package: (), + unqualified: [], + }, + ), + ], + kind: Validator, +} From 63cdb8aa09f2e7a7649c4f29f180fe8ad10702ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cain=C3=A3=20Costa?= Date: Fri, 30 Jun 2023 12:46:15 -0300 Subject: [PATCH 08/31] chore: add more details on snapshot tests We added a macro to add more information about the code that is being tested, so we can add lots and lots of small snapshot tests. --- crates/aiken-lang/src/tests/parser.rs | 25 +++++++++++++------ ...ts__parser__snapshot_windows_newline.snap} | 3 ++- 2 files changed, 19 insertions(+), 9 deletions(-) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__snapshot_test.snap => aiken_lang__tests__parser__snapshot_windows_newline.snap} (87%) diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index df0e0c88b..5a4f174dc 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -23,17 +23,26 @@ fn assert_definitions(code: &str, definitions: Vec) { ) } -fn snapshot_test(code: &str) { - let (module, _) = - parser::module(code, ast::ModuleKind::Validator).expect("Failed to parse code"); - insta::assert_debug_snapshot!(module); -} +macro_rules! snapshot_test { + ($name:ident, $code:expr) => { + #[test] + fn $name() { + let (module, _) = + parser::module($code, ast::ModuleKind::Validator).expect("Failed to parse code"); -#[test] -fn snapshot_windows_newline() { - snapshot_test("use aiken/list\r\n") + insta::with_settings!({ + info => &stringify!($name), + description => $code, + omit_expression => true + }, { + insta::assert_debug_snapshot!(module); + }); + } + }; } +snapshot_test!(snapshot_windows_newline, "use aiken/list\r\n"); + #[test] fn windows_newline() { let code = "use aiken/list\r\n"; diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__snapshot_test.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__snapshot_windows_newline.snap similarity index 87% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__snapshot_test.snap rename to crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__snapshot_windows_newline.snap index a604e44e8..b6e2b5b1c 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__snapshot_test.snap +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__snapshot_windows_newline.snap @@ -1,6 +1,7 @@ --- source: crates/aiken-lang/src/tests/parser.rs -expression: module +description: "use aiken/list\r\n" +info: snapshot_windows_newline --- Module { name: "", From 2226747dc1432b287e2c0916eca58ce0e5ab316d Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 30 Jun 2023 13:40:53 -0400 Subject: [PATCH 09/31] feat: finish splitting up parsers --- Cargo.toml | 3 + crates/aiken-lang/Cargo.toml | 19 +- crates/aiken-lang/src/parser.rs | 118 +--------- .../src/parser/expr/anonymous_binop.rs | 89 ++++++++ .../src/parser/expr/anonymous_function.rs | 6 +- crates/aiken-lang/src/parser/expr/mod.rs | 215 +++--------------- .../aiken-lang/src/parser/expr/when/clause.rs | 59 +++++ .../aiken-lang/src/parser/expr/when/guard.rs | 122 ++++++++++ crates/aiken-lang/src/parser/expr/when/mod.rs | 30 +++ 9 files changed, 351 insertions(+), 310 deletions(-) create mode 100644 crates/aiken-lang/src/parser/expr/anonymous_binop.rs create mode 100644 crates/aiken-lang/src/parser/expr/when/clause.rs create mode 100644 crates/aiken-lang/src/parser/expr/when/guard.rs create mode 100644 crates/aiken-lang/src/parser/expr/when/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 8de2641bc..84a62eeb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,9 @@ strip = true shared-version = true tag-name = "v{{version}}" +[workspace.dependencies] +insta = { version = "1.30.0", features = ["yaml"] } + [profile.dev.package.insta] opt-level = 3 diff --git a/crates/aiken-lang/Cargo.toml b/crates/aiken-lang/Cargo.toml index 365e83863..936dddf3a 100644 --- a/crates/aiken-lang/Cargo.toml +++ b/crates/aiken-lang/Cargo.toml @@ -7,9 +7,9 @@ repository = "https://github.com/aiken-lang/aiken" homepage = "https://github.com/aiken-lang/aiken" license = "Apache-2.0" authors = [ - "Lucas Rosa ", - "Kasey White ", - "KtorZ ", + "Lucas Rosa ", + "Kasey White ", + "KtorZ ", ] rust-version = "1.66.1" @@ -30,15 +30,12 @@ num-bigint = "0.4.3" [target.'cfg(not(target_family="wasm"))'.dependencies] chumsky = "0.9.2" [target.'cfg(target_family="wasm")'.dependencies] -chumsky = { version = "0.9.2", features = ["ahash", "std"], default-features = false } +chumsky = { version = "0.9.2", features = [ + "ahash", + "std", +], default-features = false } [dev-dependencies] indoc = "2.0.1" -insta = { version = "1.30.0", features = ["yaml"] } +insta.workspace = true pretty_assertions = "1.3.0" - -[profile.dev.package.insta] -opt-level = 3 - -[profile.dev.package.similar] -opt-level = 3 diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 84ef18093..2a7b628d0 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -13,7 +13,7 @@ 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 crate::ast::{self, Span}; use chumsky::{chain::Chain, prelude::*}; use error::ParseError; use extra::ModuleExtra; @@ -96,119 +96,3 @@ pub fn module( Ok((module, extra)) } - -pub fn when_clause_guard_parser() -> impl Parser, Error = ParseError> { - recursive(|r| { - let var_parser = select! { - Token::Name { name } => name, - Token::UpName { name } => name, - } - .map_with_span(|name, span| ast::ClauseGuard::Var { - name, - tipo: (), - location: span, - }); - - let constant_parser = definitions::constant::value().map(ast::ClauseGuard::Constant); - - let block_parser = r - .clone() - .delimited_by(just(Token::LeftParen), just(Token::RightParen)); - - let leaf_parser = choice((var_parser, constant_parser, block_parser)).boxed(); - - let unary_op = just(Token::Bang); - - let unary = unary_op - .map_with_span(|op, span| (op, span)) - .repeated() - .then(leaf_parser) - .foldr(|(_, span), value| ast::ClauseGuard::Not { - location: span.union(value.location()), - value: Box::new(value), - }) - .boxed(); - - let comparison_op = choice(( - just(Token::EqualEqual).to(BinOp::Eq), - just(Token::NotEqual).to(BinOp::NotEq), - just(Token::Less).to(BinOp::LtInt), - just(Token::Greater).to(BinOp::GtInt), - just(Token::LessEqual).to(BinOp::LtEqInt), - just(Token::GreaterEqual).to(BinOp::GtEqInt), - )); - - let comparison = unary - .clone() - .then(comparison_op.then(unary).repeated()) - .foldl(|left, (op, right)| { - let location = left.location().union(right.location()); - let left = Box::new(left); - let right = Box::new(right); - match op { - BinOp::Eq => ast::ClauseGuard::Equals { - location, - left, - right, - }, - BinOp::NotEq => ast::ClauseGuard::NotEquals { - location, - left, - right, - }, - BinOp::LtInt => ast::ClauseGuard::LtInt { - location, - left, - right, - }, - BinOp::GtInt => ast::ClauseGuard::GtInt { - location, - left, - right, - }, - BinOp::LtEqInt => ast::ClauseGuard::LtEqInt { - location, - left, - right, - }, - BinOp::GtEqInt => ast::ClauseGuard::GtEqInt { - location, - left, - right, - }, - _ => unreachable!(), - } - }) - .boxed(); - - let and_op = just(Token::AmperAmper); - let conjunction = comparison - .clone() - .then(and_op.then(comparison).repeated()) - .foldl(|left, (_tok, right)| { - let location = left.location().union(right.location()); - let left = Box::new(left); - let right = Box::new(right); - ast::ClauseGuard::And { - location, - left, - right, - } - }); - - let or_op = just(Token::VbarVbar); - conjunction - .clone() - .then(or_op.then(conjunction).repeated()) - .foldl(|left, (_tok, right)| { - let location = left.location().union(right.location()); - let left = Box::new(left); - let right = Box::new(right); - ast::ClauseGuard::Or { - location, - left, - right, - } - }) - }) -} diff --git a/crates/aiken-lang/src/parser/expr/anonymous_binop.rs b/crates/aiken-lang/src/parser/expr/anonymous_binop.rs new file mode 100644 index 000000000..7eaa8ae40 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/anonymous_binop.rs @@ -0,0 +1,89 @@ +use chumsky::prelude::*; + +use crate::{ + ast, + expr::{FnStyle, UntypedExpr}, + parser::{error::ParseError, token::Token}, +}; + +pub fn parser() -> impl Parser { + select! { + Token::EqualEqual => ast::BinOp::Eq, + Token::NotEqual => ast::BinOp::NotEq, + Token::Less => ast::BinOp::LtInt, + Token::LessEqual => ast::BinOp::LtEqInt, + Token::Greater => ast::BinOp::GtInt, + Token::GreaterEqual => ast::BinOp::GtEqInt, + Token::VbarVbar => ast::BinOp::Or, + Token::AmperAmper => ast::BinOp::And, + Token::Plus => ast::BinOp::AddInt, + Token::Minus => ast::BinOp::SubInt, + Token::Slash => ast::BinOp::DivInt, + Token::Star => ast::BinOp::MultInt, + Token::Percent => ast::BinOp::ModInt, + } + .map_with_span(|name, location| { + use ast::BinOp::*; + + let arg_annotation = match name { + Or | And => Some(ast::Annotation::boolean(location)), + Eq | NotEq => None, + LtInt | LtEqInt | GtInt | GtEqInt | AddInt | SubInt | MultInt | DivInt | ModInt => { + Some(ast::Annotation::int(location)) + } + }; + + let return_annotation = match name { + Or | And | Eq | NotEq | LtInt | LtEqInt | GtInt | GtEqInt => { + Some(ast::Annotation::boolean(location)) + } + AddInt | SubInt | MultInt | DivInt | ModInt => Some(ast::Annotation::int(location)), + }; + + let arguments = vec![ + ast::Arg { + arg_name: ast::ArgName::Named { + name: "left".to_string(), + label: "left".to_string(), + location, + is_validator_param: false, + }, + annotation: arg_annotation.clone(), + location, + tipo: (), + }, + ast::Arg { + arg_name: ast::ArgName::Named { + name: "right".to_string(), + label: "right".to_string(), + location, + is_validator_param: false, + }, + annotation: arg_annotation, + location, + tipo: (), + }, + ]; + + let body = UntypedExpr::BinOp { + location, + name, + left: Box::new(UntypedExpr::Var { + location, + name: "left".to_string(), + }), + right: Box::new(UntypedExpr::Var { + location, + name: "right".to_string(), + }), + }; + + UntypedExpr::Fn { + arguments, + body: Box::new(body), + return_annotation, + fn_style: FnStyle::BinOp(name), + location, + } + }) +} diff --git a/crates/aiken-lang/src/parser/expr/anonymous_function.rs b/crates/aiken-lang/src/parser/expr/anonymous_function.rs index 9e8ae29d8..6c25ab9ff 100644 --- a/crates/aiken-lang/src/parser/expr/anonymous_function.rs +++ b/crates/aiken-lang/src/parser/expr/anonymous_function.rs @@ -6,9 +6,9 @@ use crate::{ parser::{annotation, error::ParseError, token::Token}, }; -pub fn parser<'a>( - seq_r: Recursive<'a, Token, UntypedExpr, ParseError>, -) -> impl Parser + 'a { +pub fn parser( + seq_r: Recursive<'_, Token, UntypedExpr, ParseError>, +) -> impl Parser + '_ { just(Token::Fn) .ignore_then( params() diff --git a/crates/aiken-lang/src/parser/expr/mod.rs b/crates/aiken-lang/src/parser/expr/mod.rs index df8ac0feb..d6e098426 100644 --- a/crates/aiken-lang/src/parser/expr/mod.rs +++ b/crates/aiken-lang/src/parser/expr/mod.rs @@ -1,6 +1,7 @@ use chumsky::prelude::*; -use vec1::{vec1, Vec1}; +use vec1::Vec1; +mod anonymous_binop; pub mod anonymous_function; pub mod assignment; mod block; @@ -14,7 +15,9 @@ mod sequence; pub mod string; 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; @@ -27,10 +30,11 @@ pub use sequence::parser as sequence; pub use string::parser as string; pub use tuple::parser as tuple; pub use var::parser as var; +pub use when::parser as when; use crate::{ ast::{self, Span}, - expr::UntypedExpr, + expr::{FnStyle, UntypedExpr}, }; use super::{error::ParseError, token::Token}; @@ -52,171 +56,23 @@ pub fn parser( }), }); - let anon_binop_parser = select! { - Token::EqualEqual => BinOp::Eq, - Token::NotEqual => BinOp::NotEq, - Token::Less => BinOp::LtInt, - Token::LessEqual => BinOp::LtEqInt, - Token::Greater => BinOp::GtInt, - Token::GreaterEqual => BinOp::GtEqInt, - Token::VbarVbar => BinOp::Or, - Token::AmperAmper => BinOp::And, - Token::Plus => BinOp::AddInt, - Token::Minus => BinOp::SubInt, - Token::Slash => BinOp::DivInt, - Token::Star => BinOp::MultInt, - Token::Percent => BinOp::ModInt, - } - .map_with_span(|name, location| { - use BinOp::*; - - let arg_annotation = match name { - Or | And => Some(ast::Annotation::boolean(location)), - Eq | NotEq => None, - LtInt | LtEqInt | GtInt | GtEqInt | AddInt | SubInt | MultInt | DivInt | ModInt => { - Some(ast::Annotation::int(location)) - } - }; - - let return_annotation = match name { - Or | And | Eq | NotEq | LtInt | LtEqInt | GtInt | GtEqInt => { - Some(ast::Annotation::boolean(location)) - } - AddInt | SubInt | MultInt | DivInt | ModInt => Some(ast::Annotation::int(location)), - }; - - let arguments = vec![ - ast::Arg { - arg_name: ast::ArgName::Named { - name: "left".to_string(), - label: "left".to_string(), - location, - is_validator_param: false, - }, - annotation: arg_annotation.clone(), - location, - tipo: (), - }, - ast::Arg { - arg_name: ast::ArgName::Named { - name: "right".to_string(), - label: "right".to_string(), - location, - is_validator_param: false, - }, - annotation: arg_annotation, - location, - tipo: (), - }, - ]; - - let body = UntypedExpr::BinOp { - location, - name, - left: Box::new(UntypedExpr::Var { - location, - name: "left".to_string(), - }), - right: Box::new(UntypedExpr::Var { - location, - name: "right".to_string(), - }), - }; - - UntypedExpr::Fn { - arguments, - body: Box::new(body), - return_annotation, - fn_style: FnStyle::BinOp(name), - location, - } - }); - - let when_clause_parser = pattern_parser() - .then( - just(Token::Vbar) - .ignore_then(pattern_parser()) - .repeated() - .or_not(), - ) - .then(choice(( - just(Token::If) - .ignore_then(when_clause_guard_parser()) - .or_not() - .then_ignore(just(Token::RArrow)), - just(Token::If) - .ignore_then(take_until(just(Token::RArrow))) - .validate(|_value, span, emit| { - emit(ParseError::invalid_when_clause_guard(span)); - None - }), - ))) - // TODO: add hint "Did you mean to wrap a multi line clause in curly braces?" - .then(choice(( - r.clone(), - just(Token::Todo) - .ignore_then( - r.clone() - .then_ignore(one_of(Token::RArrow).not().rewind()) - .or_not(), - ) - .map_with_span(|reason, span| { - UntypedExpr::todo(span, reason.map(flexible_string_literal)) - }), - just(Token::ErrorTerm) - .ignore_then( - r.clone() - .then_ignore(just(Token::RArrow).not().rewind()) - .or_not(), - ) - .map_with_span(|reason, span| { - UntypedExpr::error(span, reason.map(flexible_string_literal)) - }), - ))) - .map_with_span( - |(((pattern, alternative_patterns_opt), guard), then), span| { - let mut patterns = vec1![pattern]; - patterns.append(&mut alternative_patterns_opt.unwrap_or_default()); - ast::UntypedClause { - location: span, - patterns, - guard, - then, - } - }, - ); - - let when_parser = just(Token::When) - // TODO: If subject is empty we should return ParseErrorType::ExpectedExpr, - .ignore_then(r.clone().map(Box::new)) - .then_ignore(just(Token::Is)) - .then_ignore(just(Token::LeftBrace)) - // TODO: If clauses are empty we should return ParseErrorType::NoCaseClause - .then(when_clause_parser.repeated()) - .then_ignore(just(Token::RightBrace)) - .map_with_span(|(subject, clauses), span| UntypedExpr::When { - location: span, - subject, - clauses, - }); - let expr_unit_parser = choice(( string(), int(), - record_update(r), - record(r), + record_update(r.clone()), + record(r.clone()), field_access_constructor, var(), - tuple(r), + tuple(r.clone()), bytearray(), - list(r), - anonymous_function(seq_r), - anon_binop_parser, - block(seq_r), - when_parser, - assignment::let_(r), - assignment::expect(r), - if_else(seq_r, r), + list(r.clone()), + anonymous_function(seq_r.clone()), + anonymous_binop(), + block(seq_r.clone()), + when(r.clone()), + assignment::let_(r.clone()), + assignment::expect(r.clone()), + if_else(seq_r, r.clone()), )); // Parsing a function call into the appropriate structure @@ -262,7 +118,7 @@ pub fn parser( select! { Token::Name { name } => name } .then_ignore(just(Token::Colon)) .or_not() - .then(r.clone()) + .then(r) .map_with_span(|(label, value), span| { ParserArg::Arg(Box::new(ast::CallArg { label, @@ -298,7 +154,8 @@ pub fn parser( .map(|(index, a)| match a { ParserArg::Arg(arg) => *arg, ParserArg::Hole { location, label } => { - let name = format!("{CAPTURE_VARIABLE}__{index}"); + let name = format!("{}__{index}", ast::CAPTURE_VARIABLE); + holes.push(ast::Arg { location: Span::empty(), annotation: None, @@ -316,7 +173,7 @@ pub fn parser( location, value: UntypedExpr::Var { location, - name: format!("{CAPTURE_VARIABLE}__{index}"), + name: format!("{}__{index}", ast::CAPTURE_VARIABLE), }, } } @@ -367,7 +224,7 @@ pub fn parser( // Negate let op = choice(( - just(Token::Bang).to(UnOp::Not), + just(Token::Bang).to(ast::UnOp::Not), just(Token::Minus) // NOTE: Prevent conflict with usage for '-' as a standalone binary op. // This will make '-' parse when used as standalone binop in a function call. @@ -381,7 +238,7 @@ pub fn parser( // // which seems acceptable. .then_ignore(just(Token::Comma).not().rewind()) - .to(UnOp::Negate), + .to(ast::UnOp::Negate), )); let unary = op @@ -397,9 +254,9 @@ pub fn parser( // Product let op = choice(( - just(Token::Star).to(BinOp::MultInt), - just(Token::Slash).to(BinOp::DivInt), - just(Token::Percent).to(BinOp::ModInt), + just(Token::Star).to(ast::BinOp::MultInt), + just(Token::Slash).to(ast::BinOp::DivInt), + just(Token::Percent).to(ast::BinOp::ModInt), )); let product = unary @@ -415,8 +272,8 @@ pub fn parser( // Sum let op = choice(( - just(Token::Plus).to(BinOp::AddInt), - just(Token::Minus).to(BinOp::SubInt), + just(Token::Plus).to(ast::BinOp::AddInt), + just(Token::Minus).to(ast::BinOp::SubInt), )); let sum = product @@ -432,12 +289,12 @@ pub fn parser( // Comparison let op = choice(( - just(Token::EqualEqual).to(BinOp::Eq), - just(Token::NotEqual).to(BinOp::NotEq), - just(Token::Less).to(BinOp::LtInt), - just(Token::Greater).to(BinOp::GtInt), - just(Token::LessEqual).to(BinOp::LtEqInt), - just(Token::GreaterEqual).to(BinOp::GtEqInt), + just(Token::EqualEqual).to(ast::BinOp::Eq), + just(Token::NotEqual).to(ast::BinOp::NotEq), + just(Token::Less).to(ast::BinOp::LtInt), + just(Token::Greater).to(ast::BinOp::GtInt), + just(Token::LessEqual).to(ast::BinOp::LtEqInt), + just(Token::GreaterEqual).to(ast::BinOp::GtEqInt), )); let comparison = sum @@ -452,7 +309,7 @@ pub fn parser( .boxed(); // Conjunction - let op = just(Token::AmperAmper).to(BinOp::And); + let op = just(Token::AmperAmper).to(ast::BinOp::And); let conjunction = comparison .clone() .then(op.then(comparison).repeated()) @@ -465,7 +322,7 @@ pub fn parser( .boxed(); // Disjunction - let op = just(Token::VbarVbar).to(BinOp::Or); + let op = just(Token::VbarVbar).to(ast::BinOp::Or); let disjunction = conjunction .clone() .then(op.then(conjunction).repeated()) diff --git a/crates/aiken-lang/src/parser/expr/when/clause.rs b/crates/aiken-lang/src/parser/expr/when/clause.rs new file mode 100644 index 000000000..474a470f1 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/when/clause.rs @@ -0,0 +1,59 @@ +use chumsky::prelude::*; +use vec1::vec1; + +use crate::{ + ast, + expr::UntypedExpr, + parser::{error::ParseError, expr::string::flexible, pattern, token::Token}, +}; + +use super::guard; + +pub fn parser( + r: Recursive<'_, Token, UntypedExpr, ParseError>, +) -> impl Parser + '_ { + pattern() + .then(just(Token::Vbar).ignore_then(pattern()).repeated().or_not()) + .then(choice(( + just(Token::If) + .ignore_then(guard()) + .or_not() + .then_ignore(just(Token::RArrow)), + just(Token::If) + .ignore_then(take_until(just(Token::RArrow))) + .validate(|_value, span, emit| { + emit(ParseError::invalid_when_clause_guard(span)); + None + }), + ))) + // TODO: add hint "Did you mean to wrap a multi line clause in curly braces?" + .then(choice(( + r.clone(), + just(Token::Todo) + .ignore_then( + r.clone() + .then_ignore(one_of(Token::RArrow).not().rewind()) + .or_not(), + ) + .map_with_span(|reason, span| UntypedExpr::todo(span, reason.map(flexible))), + just(Token::ErrorTerm) + .ignore_then( + r.clone() + .then_ignore(just(Token::RArrow).not().rewind()) + .or_not(), + ) + .map_with_span(|reason, span| UntypedExpr::error(span, reason.map(flexible))), + ))) + .map_with_span( + |(((pattern, alternative_patterns_opt), guard), then), span| { + let mut patterns = vec1![pattern]; + patterns.append(&mut alternative_patterns_opt.unwrap_or_default()); + ast::UntypedClause { + location: span, + patterns, + guard, + then, + } + }, + ) +} diff --git a/crates/aiken-lang/src/parser/expr/when/guard.rs b/crates/aiken-lang/src/parser/expr/when/guard.rs new file mode 100644 index 000000000..afcc118f9 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/when/guard.rs @@ -0,0 +1,122 @@ +use chumsky::prelude::*; + +use crate::{ + ast, + parser::{definitions, error::ParseError, token::Token}, +}; + +pub fn parser() -> impl Parser { + recursive(|r| { + let var_parser = select! { + Token::Name { name } => name, + Token::UpName { name } => name, + } + .map_with_span(|name, span| ast::ClauseGuard::Var { + name, + tipo: (), + location: span, + }); + + let constant_parser = definitions::constant::value().map(ast::ClauseGuard::Constant); + + let block_parser = r + .clone() + .delimited_by(just(Token::LeftParen), just(Token::RightParen)); + + let leaf_parser = choice((var_parser, constant_parser, block_parser)).boxed(); + + let unary_op = just(Token::Bang); + + let unary = unary_op + .map_with_span(|op, span| (op, span)) + .repeated() + .then(leaf_parser) + .foldr(|(_, span), value| ast::ClauseGuard::Not { + location: span.union(value.location()), + value: Box::new(value), + }) + .boxed(); + + let comparison_op = choice(( + just(Token::EqualEqual).to(ast::BinOp::Eq), + just(Token::NotEqual).to(ast::BinOp::NotEq), + just(Token::Less).to(ast::BinOp::LtInt), + just(Token::Greater).to(ast::BinOp::GtInt), + just(Token::LessEqual).to(ast::BinOp::LtEqInt), + just(Token::GreaterEqual).to(ast::BinOp::GtEqInt), + )); + + let comparison = unary + .clone() + .then(comparison_op.then(unary).repeated()) + .foldl(|left, (op, right)| { + let location = left.location().union(right.location()); + let left = Box::new(left); + let right = Box::new(right); + match op { + ast::BinOp::Eq => ast::ClauseGuard::Equals { + location, + left, + right, + }, + ast::BinOp::NotEq => ast::ClauseGuard::NotEquals { + location, + left, + right, + }, + ast::BinOp::LtInt => ast::ClauseGuard::LtInt { + location, + left, + right, + }, + ast::BinOp::GtInt => ast::ClauseGuard::GtInt { + location, + left, + right, + }, + ast::BinOp::LtEqInt => ast::ClauseGuard::LtEqInt { + location, + left, + right, + }, + ast::BinOp::GtEqInt => ast::ClauseGuard::GtEqInt { + location, + left, + right, + }, + _ => unreachable!(), + } + }) + .boxed(); + + let and_op = just(Token::AmperAmper); + let conjunction = comparison + .clone() + .then(and_op.then(comparison).repeated()) + .foldl(|left, (_tok, right)| { + let location = left.location().union(right.location()); + let left = Box::new(left); + let right = Box::new(right); + ast::ClauseGuard::And { + location, + left, + right, + } + }); + + let or_op = just(Token::VbarVbar); + conjunction + .clone() + .then(or_op.then(conjunction).repeated()) + .foldl(|left, (_tok, right)| { + let location = left.location().union(right.location()); + let left = Box::new(left); + let right = Box::new(right); + ast::ClauseGuard::Or { + location, + left, + right, + } + }) + }) +} diff --git a/crates/aiken-lang/src/parser/expr/when/mod.rs b/crates/aiken-lang/src/parser/expr/when/mod.rs new file mode 100644 index 000000000..13c2ed91a --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/when/mod.rs @@ -0,0 +1,30 @@ +use chumsky::prelude::*; + +mod clause; +mod guard; + +pub use clause::parser as clause; +pub use guard::parser as guard; + +use crate::{ + expr::UntypedExpr, + parser::{error::ParseError, token::Token}, +}; + +pub fn parser( + r: Recursive<'_, Token, UntypedExpr, ParseError>, +) -> impl Parser + '_ { + just(Token::When) + // TODO: If subject is empty we should return ParseErrorType::ExpectedExpr, + .ignore_then(r.clone().map(Box::new)) + .then_ignore(just(Token::Is)) + .then_ignore(just(Token::LeftBrace)) + // TODO: If clauses are empty we should return ParseErrorType::NoCaseClause + .then(clause(r).repeated()) + .then_ignore(just(Token::RightBrace)) + .map_with_span(|(subject, clauses), span| UntypedExpr::When { + location: span, + subject, + clauses, + }) +} From f878ef7cef760379c46e997136ec9a59a5d04fda Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 30 Jun 2023 14:57:41 -0400 Subject: [PATCH 10/31] feat: move some token processing to the lexer --- crates/aiken-lang/src/ast.rs | 6 ++ crates/aiken-lang/src/parser.rs | 68 ++-------------- crates/aiken-lang/src/parser/lexer.rs | 78 ++++++++++++++++++- crates/aiken-lang/src/tests/parser.rs | 20 +---- ...lang__tests__parser__windows_newline.snap} | 2 +- 5 files changed, 89 insertions(+), 85 deletions(-) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__snapshot_windows_newline.snap => aiken_lang__tests__parser__windows_newline.snap} (93%) diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 9f98f97c1..a2b1ad895 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -1167,6 +1167,12 @@ impl Span { Self::new((), 0..0) } + pub fn create(i: usize, n: usize) -> Self { + use chumsky::Span; + + Self::new((), i..i + n) + } + pub fn range(&self) -> Range { use chumsky::Span; diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 2a7b628d0..f384de20e 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -13,78 +13,20 @@ pub use definitions::parser as definitions; pub use expr::parser as expression; pub use pattern::parser as pattern; -use crate::ast::{self, Span}; -use chumsky::{chain::Chain, prelude::*}; +use crate::ast; +use chumsky::prelude::*; use error::ParseError; use extra::ModuleExtra; -use token::Token; pub fn module( src: &str, kind: ast::ModuleKind, ) -> Result<(ast::UntypedModule, ModuleExtra), Vec> { - let len = src.as_bytes().len(); + let lexer::LexInfo { tokens, extra } = lexer::run(src)?; - let span = |i, n| Span::new((), i..i + n); + let stream = chumsky::Stream::from_iter(ast::Span::create(tokens.len()), tokens.into_iter()); - let tokens = lexer::lexer().parse(chumsky::Stream::from_iter( - span(len, 1), - src.chars().scan(0, |i, c| { - let start = *i; - let offset = c.len_utf8(); - *i = start + offset; - Some((c, span(start, offset))) - }), - ))?; - - let mut extra = ModuleExtra::new(); - - let mut previous_is_newline = false; - - let tokens = tokens.into_iter().filter_map(|(token, ref span)| { - let current_is_newline = token == Token::NewLine || token == Token::EmptyLine; - let result = match token { - Token::ModuleComment => { - extra.module_comments.push(*span); - None - } - Token::DocComment => { - extra.doc_comments.push(*span); - None - } - Token::Comment => { - extra.comments.push(*span); - None - } - Token::EmptyLine => { - extra.empty_lines.push(span.start); - None - } - Token::LeftParen => { - if previous_is_newline { - Some((Token::NewLineLeftParen, *span)) - } else { - Some((Token::LeftParen, *span)) - } - } - Token::Pipe => { - if previous_is_newline { - Some((Token::NewLinePipe, *span)) - } else { - Some((Token::Pipe, *span)) - } - } - Token::NewLine => None, - _ => Some((token, *span)), - }; - - previous_is_newline = current_is_newline; - - result - }); - - let definitions = - definitions().parse(chumsky::Stream::from_iter(span(tokens.len()), tokens))?; + let definitions = definitions().parse(stream)?; let module = ast::UntypedModule { kind, diff --git a/crates/aiken-lang/src/parser/lexer.rs b/crates/aiken-lang/src/parser/lexer.rs index 110d0335f..f81e82076 100644 --- a/crates/aiken-lang/src/parser/lexer.rs +++ b/crates/aiken-lang/src/parser/lexer.rs @@ -1,11 +1,83 @@ +use chumsky::prelude::*; +use num_bigint::BigInt; +use ordinal::Ordinal; + use super::{ error::ParseError, + extra::ModuleExtra, token::{Base, Token}, }; use crate::ast::Span; -use chumsky::prelude::*; -use num_bigint::BigInt; -use ordinal::Ordinal; + +pub struct LexInfo { + pub tokens: Vec<(Token, Span)>, + pub extra: ModuleExtra, +} + +pub fn run(src: &str) -> Result> { + let len = src.as_bytes().len(); + + let tokens = lexer().parse(chumsky::Stream::from_iter( + Span::create(len, 1), + src.chars().scan(0, |i, c| { + let start = *i; + let offset = c.len_utf8(); + *i = start + offset; + Some((c, Span::create(start, offset))) + }), + ))?; + + let mut extra = ModuleExtra::new(); + + let mut previous_is_newline = false; + + let tokens = tokens + .into_iter() + .filter_map(|(token, ref span)| { + let current_is_newline = token == Token::NewLine || token == Token::EmptyLine; + let result = match token { + Token::ModuleComment => { + extra.module_comments.push(*span); + None + } + Token::DocComment => { + extra.doc_comments.push(*span); + None + } + Token::Comment => { + extra.comments.push(*span); + None + } + Token::EmptyLine => { + extra.empty_lines.push(span.start); + None + } + Token::LeftParen => { + if previous_is_newline { + Some((Token::NewLineLeftParen, *span)) + } else { + Some((Token::LeftParen, *span)) + } + } + Token::Pipe => { + if previous_is_newline { + Some((Token::NewLinePipe, *span)) + } else { + Some((Token::Pipe, *span)) + } + } + Token::NewLine => None, + _ => Some((token, *span)), + }; + + previous_is_newline = current_is_newline; + + result + }) + .collect::>(); + + Ok(LexInfo { tokens, extra }) +} pub fn lexer() -> impl Parser, Error = ParseError> { let base10 = text::int(10).map(|value| Token::Int { diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 5a4f174dc..1eb01af76 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -23,7 +23,7 @@ fn assert_definitions(code: &str, definitions: Vec) { ) } -macro_rules! snapshot_test { +macro_rules! assert_parse { ($name:ident, $code:expr) => { #[test] fn $name() { @@ -41,23 +41,7 @@ macro_rules! snapshot_test { }; } -snapshot_test!(snapshot_windows_newline, "use aiken/list\r\n"); - -#[test] -fn windows_newline() { - let code = "use aiken/list\r\n"; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Use(Use { - location: Span::new((), 0..14), - module: vec!["aiken".to_string(), "list".to_string()], - as_name: None, - unqualified: vec![], - package: (), - })], - ) -} +assert_parse!(windows_newline, "use aiken/list\r\n"); #[test] fn can_handle_comments_at_end_of_file() { diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__snapshot_windows_newline.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__windows_newline.snap similarity index 93% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__snapshot_windows_newline.snap rename to crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__windows_newline.snap index b6e2b5b1c..f8b6b3212 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__snapshot_windows_newline.snap +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__windows_newline.snap @@ -1,7 +1,7 @@ --- source: crates/aiken-lang/src/tests/parser.rs description: "use aiken/list\r\n" -info: snapshot_windows_newline +info: windows_newline --- Module { name: "", From 291dedf4e83003a3aa1d810cb7f3bff1890a0588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cain=C3=A3=20Costa?= Date: Fri, 30 Jun 2023 16:23:02 -0300 Subject: [PATCH 11/31] chore: refactor all parse tests to use assert_parse! --- crates/aiken-lang/src/tests/parser.rs | 4958 +---------------- ...ng__tests__parser__anonymous_function.snap | 110 + ...ts__parser__base16_bytearray_literals.snap | 62 + .../aiken_lang__tests__parser__block.snap | 99 + .../aiken_lang__tests__parser__call.snap | 182 + ...r__can_handle_comments_at_end_of_file.snap | 24 + ...tests__parser__cargo_create_unlabeled.snap | 58 + ...iken_lang__tests__parser__custom_type.snap | 101 + ...lang__tests__parser__double_validator.snap | 110 + ...n_lang__tests__parser__empty_function.snap | 37 + .../aiken_lang__tests__parser__expect.snap | 71 + .../aiken_lang__tests__parser__fail.snap | 54 + ...ken_lang__tests__parser__field_access.snap | 53 + ...ang__tests__parser__first_class_binop.snap | 1212 ++++ ...__parser__function_ambiguous_sequence.snap | 202 + ...ken_lang__tests__parser__function_def.snap | 37 + ..._lang__tests__parser__function_invoke.snap | 55 + ...en_lang__tests__parser__if_expression.snap | 110 + .../aiken_lang__tests__parser__import.snap | 25 + ...ken_lang__tests__parser__import_alias.snap | 27 + ..._tests__parser__int_parsing_hex_bytes.snap | 37 + ...ken_lang__tests__parser__let_bindings.snap | 137 + ...iken_lang__tests__parser__opaque_type.snap | 46 + ...iken_lang__tests__parser__parse_tuple.snap | 117 + ...ken_lang__tests__parser__parse_tuple2.snap | 76 + ...s__parser__parsing_numeric_underscore.snap | 83 + .../aiken_lang__tests__parser__pipeline.snap | 73 + ...sts__parser__plain_bytearray_literals.snap | 36 + ...aiken_lang__tests__parser__plus_binop.snap | 60 + ...n_lang__tests__parser__pub_type_alias.snap | 36 + ..._tests__parser__record_create_labeled.snap | 75 + ...cord_create_labeled_with_field_access.snap | 79 + ...en_lang__tests__parser__record_update.snap | 109 + ...lang__tests__parser__tuple_type_alias.snap | 40 + ...aiken_lang__tests__parser__type_alias.snap | 36 + ...r__type_annotation_with_module_prefix.snap | 57 + ...g__tests__parser__unqualified_imports.snap | 42 + .../aiken_lang__tests__parser__validator.snap | 71 + .../aiken_lang__tests__parser__when.snap | 163 + 39 files changed, 4239 insertions(+), 4721 deletions(-) create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__anonymous_function.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__base16_bytearray_literals.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__block.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__call.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__can_handle_comments_at_end_of_file.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__cargo_create_unlabeled.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__custom_type.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__double_validator.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__empty_function.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__expect.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__fail.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__field_access.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__first_class_binop.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_ambiguous_sequence.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_def.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_invoke.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__if_expression.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import_alias.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__int_parsing_hex_bytes.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__let_bindings.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__opaque_type.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple2.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parsing_numeric_underscore.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pipeline.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plain_bytearray_literals.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plus_binop.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pub_type_alias.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled_with_field_access.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_update.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__tuple_type_alias.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_alias.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_annotation_with_module_prefix.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__unqualified_imports.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__validator.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__when.snap diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 1eb01af76..b06f027f7 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -1,34 +1,11 @@ -use crate::{ - ast::{self, Constant, DataType, Function, ModuleConstant, Span, TypeAlias, Use}, - expr, - parser::{self, token::Base}, -}; -use chumsky::prelude::*; -use indoc::indoc; -use pretty_assertions::assert_eq; -use vec1::vec1; - -fn assert_definitions(code: &str, definitions: Vec) { - let (module, _extra) = parser::module(code, ast::ModuleKind::Validator).unwrap(); - - assert_eq!( - ast::UntypedModule { - docs: vec![], - kind: ast::ModuleKind::Validator, - name: "".to_string(), - type_info: (), - definitions, - }, - module - ) -} +use crate::{ast, parser}; macro_rules! assert_parse { ($name:ident, $code:expr) => { #[test] fn $name() { let (module, _) = - parser::module($code, ast::ModuleKind::Validator).expect("Failed to parse code"); + parser::module(indoc::indoc!{ $code }, ast::ModuleKind::Validator).expect("Failed to parse code"); insta::with_settings!({ info => &stringify!($name), @@ -42,204 +19,47 @@ macro_rules! assert_parse { } assert_parse!(windows_newline, "use aiken/list\r\n"); - -#[test] -fn can_handle_comments_at_end_of_file() { - let code = indoc! {r#" +assert_parse!( + can_handle_comments_at_end_of_file, + r#" use aiken // some comment - // more comments"#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Use(Use { - location: Span::new((), 0..9), - module: vec!["aiken".to_string()], - as_name: None, - unqualified: vec![], - package: (), - })], - ) -} - -#[test] -fn type_annotation_with_module_prefix() { - let code = indoc! {r#" + // more comments"# +); +assert_parse!( + type_annotation_with_module_prefix, + r#" use aiken pub fn go() -> aiken.Option { False } - "#}; - - assert_definitions( - code, - vec![ - ast::UntypedDefinition::Use(ast::Use { - as_name: None, - location: Span::new((), 0..9), - module: vec!["aiken".to_string()], - package: (), - unqualified: vec![], - }), - ast::UntypedDefinition::Fn(ast::Function { - arguments: vec![], - body: expr::UntypedExpr::Var { - location: Span::new((), 48..53), - name: "False".to_string(), - }, - doc: None, - location: Span::new((), 11..43), - name: "go".to_string(), - public: true, - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 26..43), - module: Some("aiken".to_string()), - name: "Option".to_string(), - arguments: vec![ast::Annotation::Constructor { - location: Span::new((), 39..42), - module: None, - name: "Int".to_string(), - arguments: vec![], - }], - }), - return_type: (), - end_position: 54, - can_error: true, - }), - ], - ) -} - -#[test] -fn test_fail() { - let code = indoc! {r#" + "# +); +assert_parse!( + test_fail, + r#" !test invalid_inputs() { expect True = False False } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Test(ast::Function { - arguments: vec![], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 27..55), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 27..46), - value: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 41..46), - name: "False".to_string(), - }), - pattern: ast::UntypedPattern::Constructor { - is_record: false, - location: Span::new((), 34..38), - name: "True".to_string(), - arguments: vec![], - module: None, - constructor: (), - with_spread: false, - tipo: (), - }, - kind: ast::AssignmentKind::Expect, - annotation: None, - }, - expr::UntypedExpr::Var { - location: Span::new((), 50..55), - name: "False".to_string(), - }, - ], - }, - doc: None, - location: Span::new((), 0..22), - name: "invalid_inputs".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 56, - can_error: true, - })], - ); -} - -#[test] -fn validator() { - let code = indoc! {r#" + "# +); +assert_parse!( + validator, + r#" validator { fn foo(datum, rdmr, ctx) { True } } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Validator(ast::Validator { - doc: None, - end_position: 54, - fun: Function { - can_error: true, - arguments: vec![ - ast::Arg { - arg_name: ast::ArgName::Named { - name: "datum".to_string(), - label: "datum".to_string(), - location: Span::new((), 21..26), - is_validator_param: false, - }, - location: Span::new((), 21..26), - annotation: None, - tipo: (), - }, - ast::Arg { - arg_name: ast::ArgName::Named { - name: "rdmr".to_string(), - label: "rdmr".to_string(), - location: Span::new((), 28..32), - is_validator_param: false, - }, - location: Span::new((), 28..32), - annotation: None, - tipo: (), - }, - ast::Arg { - arg_name: ast::ArgName::Named { - name: "ctx".to_string(), - label: "ctx".to_string(), - location: Span::new((), 34..37), - is_validator_param: false, - }, - location: Span::new((), 34..37), - annotation: None, - tipo: (), - }, - ], - body: expr::UntypedExpr::Var { - location: Span::new((), 45..49), - name: "True".to_string(), - }, - doc: None, - location: Span::new((), 14..38), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 52, - }, - other_fun: None, - location: Span::new((), 0..9), - params: vec![], - })], - ) -} - -#[test] -fn double_validator() { - let code = indoc! {r#" + "# +); +assert_parse!( + double_validator, + r#" validator { fn foo(datum, rdmr, ctx) { True @@ -249,597 +69,92 @@ fn double_validator() { True } } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Validator(ast::Validator { - doc: None, - end_position: 90, - fun: Function { - can_error: true, - arguments: vec![ - ast::Arg { - arg_name: ast::ArgName::Named { - name: "datum".to_string(), - label: "datum".to_string(), - location: Span::new((), 21..26), - is_validator_param: false, - }, - location: Span::new((), 21..26), - annotation: None, - tipo: (), - }, - ast::Arg { - arg_name: ast::ArgName::Named { - name: "rdmr".to_string(), - label: "rdmr".to_string(), - location: Span::new((), 28..32), - is_validator_param: false, - }, - location: Span::new((), 28..32), - annotation: None, - tipo: (), - }, - ast::Arg { - arg_name: ast::ArgName::Named { - name: "ctx".to_string(), - label: "ctx".to_string(), - location: Span::new((), 34..37), - is_validator_param: false, - }, - location: Span::new((), 34..37), - annotation: None, - tipo: (), - }, - ], - body: expr::UntypedExpr::Var { - location: Span::new((), 45..49), - name: "True".to_string(), - }, - doc: None, - location: Span::new((), 14..38), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 52, - }, - other_fun: Some(Function { - can_error: true, - arguments: vec![ - ast::Arg { - arg_name: ast::ArgName::Named { - name: "rdmr".to_string(), - label: "rdmr".to_string(), - location: Span::new((), 64..68), - is_validator_param: false, - }, - location: Span::new((), 64..68), - annotation: None, - tipo: (), - }, - ast::Arg { - arg_name: ast::ArgName::Named { - name: "ctx".to_string(), - label: "ctx".to_string(), - location: Span::new((), 70..73), - is_validator_param: false, - }, - location: Span::new((), 70..73), - annotation: None, - tipo: (), - }, - ], - body: expr::UntypedExpr::Var { - location: Span::new((), 81..85), - name: "True".to_string(), - }, - doc: None, - location: Span::new((), 57..74), - name: "bar".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 88, - }), - location: Span::new((), 0..9), - params: vec![], - })], - ) -} - -#[test] -fn import() { - let code = indoc! {r#" + "# +); +assert_parse!( + import, + r#" use std/list - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Use(Use { - location: Span::new((), 0..12), - module: vec!["std".to_string(), "list".to_string()], - as_name: None, - unqualified: vec![], - package: (), - })], - ) -} - -#[test] -fn unqualified_imports() { - let code = indoc! {r#" + "# +); +assert_parse!( + unqualified_imports, + r#" use std/address.{Address as A, thing as w} - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Use(Use { - location: Span::new((), 0..42), - module: vec!["std".to_string(), "address".to_string()], - as_name: None, - unqualified: vec![ - ast::UnqualifiedImport { - as_name: Some("A".to_string()), - location: Span::new((), 17..29), - layer: Default::default(), - name: "Address".to_string(), - }, - ast::UnqualifiedImport { - as_name: Some("w".to_string()), - location: Span::new((), 31..41), - layer: Default::default(), - name: "thing".to_string(), - }, - ], - package: (), - })], - ) -} - -#[test] -fn import_alias() { - let code = indoc! {r#" + "# +); +assert_parse!( + import_alias, + r#" use std/tx as t - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Use(Use { - location: Span::new((), 0..15), - module: vec!["std".to_string(), "tx".to_string()], - as_name: Some("t".to_string()), - unqualified: vec![], - package: (), - })], - ) -} - -#[test] -fn custom_type() { - let code = indoc! {r#" + "# +); +assert_parse!( + custom_type, + r#" type Option { Some(a, Int) None Wow { name: Int, age: Int } } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::DataType(DataType { - constructors: vec![ - ast::RecordConstructor { - location: Span::new((), 19..31), - name: "Some".to_string(), - arguments: vec![ - ast::RecordConstructorArg { - label: None, - annotation: ast::Annotation::Var { - location: Span::new((), 24..25), - name: "a".to_string(), - }, - location: Span::new((), 24..25), - tipo: (), - doc: None, - }, - ast::RecordConstructorArg { - label: None, - annotation: ast::Annotation::Constructor { - location: Span::new((), 27..30), - module: None, - name: "Int".to_string(), - arguments: vec![], - }, - location: Span::new((), 27..30), - tipo: (), - doc: None, - }, - ], - doc: None, - sugar: false, - }, - ast::RecordConstructor { - location: Span::new((), 34..38), - name: "None".to_string(), - arguments: vec![], - doc: None, - sugar: false, - }, - ast::RecordConstructor { - location: Span::new((), 41..68), - name: "Wow".to_string(), - arguments: vec![ - ast::RecordConstructorArg { - label: Some("name".to_string()), - annotation: ast::Annotation::Constructor { - location: Span::new((), 53..56), - module: None, - name: "Int".to_string(), - arguments: vec![], - }, - location: Span::new((), 47..56), - tipo: (), - doc: None, - }, - ast::RecordConstructorArg { - label: Some("age".to_string()), - annotation: ast::Annotation::Constructor { - location: Span::new((), 63..66), - module: None, - name: "Int".to_string(), - arguments: vec![], - }, - location: Span::new((), 58..66), - tipo: (), - doc: None, - }, - ], - doc: None, - sugar: false, - }, - ], - doc: None, - location: Span::new((), 0..70), - name: "Option".to_string(), - opaque: false, - parameters: vec!["a".to_string()], - public: false, - typed_parameters: vec![], - })], - ) -} - -#[test] -fn opaque_type() { - let code = indoc! {r#" + "# +); +assert_parse!( + opaque_type, + r#" pub opaque type User { name: _w } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::DataType(DataType { - constructors: vec![ast::RecordConstructor { - location: Span::new((), 21..35), - name: "User".to_string(), - arguments: vec![ast::RecordConstructorArg { - label: Some("name".to_string()), - annotation: ast::Annotation::Hole { - location: Span::new((), 31..33), - name: "_w".to_string(), - }, - location: Span::new((), 25..33), - tipo: (), - doc: None, - }], - doc: None, - sugar: true, - }], - doc: None, - location: Span::new((), 0..35), - name: "User".to_string(), - opaque: true, - parameters: vec![], - public: true, - typed_parameters: vec![], - })], - ) -} - -#[test] -fn type_alias() { - let code = indoc! {r#" + "# +); +assert_parse!( + type_alias, + r#" type Thing = Option - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::TypeAlias(TypeAlias { - alias: "Thing".to_string(), - annotation: ast::Annotation::Constructor { - location: Span::new((), 13..24), - module: None, - name: "Option".to_string(), - arguments: vec![ast::Annotation::Constructor { - location: Span::new((), 20..23), - module: None, - name: "Int".to_string(), - arguments: vec![], - }], - }, - doc: None, - location: Span::new((), 0..24), - parameters: vec![], - public: false, - tipo: (), - })], - ) -} - -#[test] -fn pub_type_alias() { - let code = indoc! {r#" + "# +); +assert_parse!( + pub_type_alias, + r#" pub type Me = Option - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::TypeAlias(TypeAlias { - alias: "Me".to_string(), - annotation: ast::Annotation::Constructor { - location: Span::new((), 14..28), - module: None, - name: "Option".to_string(), - arguments: vec![ast::Annotation::Constructor { - location: Span::new((), 21..27), - module: None, - name: "String".to_string(), - arguments: vec![], - }], - }, - doc: None, - location: Span::new((), 0..28), - parameters: vec![], - public: true, - tipo: (), - })], - ) -} - -#[test] -fn empty_function() { - let code = indoc! {r#" + "# +); +assert_parse!( + empty_function, + r#" pub fn run() {} - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Trace { - kind: ast::TraceKind::Todo, - location: Span::new((), 0..15), - text: Box::new(expr::UntypedExpr::String { - value: "aiken::todo".to_string(), - location: Span::new((), 0..15), - }), - then: Box::new(expr::UntypedExpr::ErrorTerm { - location: Span::new((), 0..15), - }), - }, - doc: None, - location: Span::new((), 0..12), - name: "run".to_string(), - public: true, - return_annotation: None, - return_type: (), - end_position: 14, - })], - ) -} - -#[test] -fn expect() { - let code = indoc! {r#" + "# +); +assert_parse!( + expect, + r#" pub fn run() { expect Some(x) = something.field x.other_field } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 19..69), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 19..51), - value: expr::UntypedExpr::FieldAccess { - location: Span::new((), 36..51), - label: "field".to_string(), - container: expr::UntypedExpr::Var { - location: Span::new((), 36..45), - name: "something".to_string(), - } - .into(), - } - .into(), - pattern: ast::Pattern::Constructor { - is_record: false, - location: Span::new((), 26..33), - name: "Some".to_string(), - arguments: vec![ast::CallArg { - label: None, - location: Span::new((), 31..32), - value: ast::Pattern::Var { - location: Span::new((), 31..32), - name: "x".to_string(), - }, - }], - module: None, - constructor: (), - with_spread: false, - tipo: (), - }, - kind: ast::AssignmentKind::Expect, - annotation: None, - }, - expr::UntypedExpr::FieldAccess { - location: Span::new((), 56..69), - label: "other_field".to_string(), - container: expr::UntypedExpr::Var { - location: Span::new((), 56..57), - name: "x".to_string(), - } - .into(), - }, - ], - }, - doc: None, - - location: Span::new((), 0..12), - name: "run".to_string(), - public: true, - return_annotation: None, - return_type: (), - end_position: 70, - })], - ) -} - -#[test] -fn plus_binop() { - let code = indoc! {r#" + "# +); +assert_parse!( + plus_binop, + r#" pub fn add_one(a) -> Int { a + 1 } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![ast::Arg { - arg_name: ast::ArgName::Named { - label: "a".to_string(), - name: "a".to_string(), - location: Span::new((), 15..16), - is_validator_param: false, - }, - location: Span::new((), 15..16), - annotation: None, - tipo: (), - }], - body: expr::UntypedExpr::BinOp { - location: Span::new((), 29..34), - name: ast::BinOp::AddInt, - left: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 29..30), - name: "a".to_string(), - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 33..34), - value: "1".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }, - doc: None, - location: Span::new((), 0..24), - name: "add_one".to_string(), - public: true, - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 21..24), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - return_type: (), - end_position: 35, - })], - ) -} - -#[test] -fn pipeline() { - let code = indoc! {r#" + "# +); +assert_parse!( + pipeline, + r#" pub fn thing(thing a: Int) { a + 2 |> add_one |> add_one } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![ast::Arg { - arg_name: ast::ArgName::Named { - name: "a".to_string(), - label: "thing".to_string(), - location: Span::new((), 13..20), - is_validator_param: false, - }, - location: Span::new((), 13..25), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 22..25), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }], - body: expr::UntypedExpr::PipeLine { - one_liner: false, - expressions: vec1::vec1![ - expr::UntypedExpr::BinOp { - location: Span::new((), 31..36), - name: ast::BinOp::AddInt, - left: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 31..32), - name: "a".to_string(), - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 35..36), - value: "2".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }, - expr::UntypedExpr::Var { - location: Span::new((), 42..49), - name: "add_one".to_string(), - }, - expr::UntypedExpr::Var { - location: Span::new((), 55..62), - name: "add_one".to_string(), - }, - ], - }, - doc: None, - location: Span::new((), 0..26), - name: "thing".to_string(), - public: true, - return_annotation: None, - return_type: (), - end_position: 63, - })], - ) -} - -#[test] -fn if_expression() { - let code = indoc! {r#" + "# +); +assert_parse!( + if_expression, + r#" fn ifs() { if True { 1 + 1 @@ -851,111 +166,11 @@ fn if_expression() { 3 } } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::If { - location: Span::new((), 13..106), - branches: vec1::vec1![ - ast::IfBranch { - condition: expr::UntypedExpr::Var { - location: Span::new((), 16..20), - name: "True".to_string(), - }, - body: expr::UntypedExpr::BinOp { - location: Span::new((), 27..32), - name: ast::BinOp::AddInt, - left: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 27..28), - value: "1".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 31..32), - value: "1".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }, - location: Span::new((), 16..36), - }, - ast::IfBranch { - condition: expr::UntypedExpr::BinOp { - location: Span::new((), 45..50), - name: ast::BinOp::LtInt, - left: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 45..46), - name: "a".to_string(), - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 49..50), - value: "4".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }, - body: expr::UntypedExpr::Int { - location: Span::new((), 57..58), - value: "5".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - location: Span::new((), 45..62), - }, - ast::IfBranch { - condition: expr::UntypedExpr::BinOp { - location: Span::new((), 71..77), - name: ast::BinOp::Or, - left: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 71..72), - name: "a".to_string(), - }), - right: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 76..77), - name: "b".to_string(), - }), - }, - body: expr::UntypedExpr::Int { - location: Span::new((), 84..85), - value: "6".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - location: Span::new((), 71..89), - }, - ], - final_else: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 101..102), - value: "3".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }, - doc: None, - location: Span::new((), 0..8), - name: "ifs".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 107, - })], - ) -} - -#[test] -fn let_bindings() { - let code = indoc! {r#" + "# +); +assert_parse!( + let_bindings, + r#" pub fn wow(a: Int) { let x = a + 2 @@ -968,134 +183,11 @@ fn let_bindings() { y } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![ast::Arg { - arg_name: ast::ArgName::Named { - label: "a".to_string(), - name: "a".to_string(), - location: Span::new((), 11..12), - is_validator_param: false, - }, - location: Span::new((), 11..17), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 14..17), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 23..121), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 23..70), - value: Box::new(expr::UntypedExpr::PipeLine { - one_liner: false, - expressions: vec1::vec1![ - expr::UntypedExpr::BinOp { - location: Span::new((), 35..40), - name: ast::BinOp::AddInt, - left: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 35..36), - name: "a".to_string(), - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 39..40), - value: "2".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }, - expr::UntypedExpr::Var { - location: Span::new((), 48..55), - name: "add_one".to_string(), - }, - expr::UntypedExpr::Var { - location: Span::new((), 63..70), - name: "add_one".to_string(), - }, - ], - }), - pattern: ast::Pattern::Var { - location: Span::new((), 27..28), - name: "x".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Assignment { - location: Span::new((), 74..97), - value: Box::new(expr::UntypedExpr::List { - location: Span::new((), 86..97), - elements: vec![ - expr::UntypedExpr::Int { - location: Span::new((), 88..89), - value: "1".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - expr::UntypedExpr::Int { - location: Span::new((), 91..92), - value: "2".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - expr::UntypedExpr::Var { - location: Span::new((), 94..95), - name: "a".to_string(), - }, - ], - tail: None, - }), - pattern: ast::Pattern::Var { - location: Span::new((), 78..83), - name: "thing".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Assignment { - location: Span::new((), 101..116), - value: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 111..116), - name: "thing".to_string(), - }), - pattern: ast::Pattern::Var { - location: Span::new((), 105..108), - name: "idk".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Var { - location: Span::new((), 120..121), - name: "y".to_string(), - }, - ], - }, - doc: None, - location: Span::new((), 0..18), - name: "wow".to_string(), - public: true, - return_annotation: None, - return_type: (), - end_position: 122, - })], - ) -} - -#[test] -fn block() { - let code = indoc! {r#" + "# +); +assert_parse!( + block, + r#" pub fn wow2(a: Int){ let b = { let x = 4 @@ -1105,96 +197,11 @@ fn block() { b } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![ast::Arg { - arg_name: ast::ArgName::Named { - label: "a".to_string(), - name: "a".to_string(), - location: Span::new((), 12..13), - is_validator_param: false, - }, - location: Span::new((), 12..18), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 15..18), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 23..66), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 23..61), - value: Box::new(expr::UntypedExpr::Sequence { - location: Span::new((), 37..57), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 37..46), - value: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 45..46), - value: "4".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - pattern: ast::Pattern::Var { - location: Span::new((), 41..42), - name: "x".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::BinOp { - location: Span::new((), 52..57), - name: ast::BinOp::AddInt, - left: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 52..53), - name: "x".to_string(), - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 56..57), - value: "5".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }, - ], - }), - pattern: ast::Pattern::Var { - location: Span::new((), 27..28), - name: "b".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Var { - location: Span::new((), 65..66), - name: "b".to_string(), - }, - ], - }, - doc: None, - location: Span::new((), 0..19), - name: "wow2".to_string(), - public: true, - return_annotation: None, - return_type: (), - end_position: 67, - })], - ) -} - -#[test] -fn when() { - let code = indoc! {r#" + "# +); +assert_parse!( + when, + r#" pub fn wow2(a: Int){ when a is { 2 -> 3 @@ -1206,303 +213,29 @@ fn when() { _ -> 4 } } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![ast::Arg { - arg_name: ast::ArgName::Named { - label: "a".to_string(), - name: "a".to_string(), - location: Span::new((), 12..13), - is_validator_param: false, - }, - location: Span::new((), 12..18), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 15..18), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }], - body: expr::UntypedExpr::When { - location: Span::new((), 23..132), - subject: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 28..29), - name: "a".to_string(), - }), - clauses: vec![ - ast::UntypedClause { - location: Span::new((), 39..45), - patterns: vec1![ast::Pattern::Int { - location: Span::new((), 39..40), - value: "2".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }], - guard: None, - then: expr::UntypedExpr::Int { - location: Span::new((), 44..45), - value: "3".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - }, - ast::UntypedClause { - location: Span::new((), 50..106), - patterns: vec1![ - ast::Pattern::Int { - location: Span::new((), 50..51), - value: "1".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - ast::Pattern::Int { - location: Span::new((), 54..55), - value: "4".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - ast::Pattern::Int { - location: Span::new((), 58..59), - value: "5".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - ], - guard: None, - then: expr::UntypedExpr::Sequence { - location: Span::new((), 71..100), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 71..86), - value: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 85..86), - value: "5".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - pattern: ast::Pattern::Var { - location: Span::new((), 75..82), - name: "amazing".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Var { - location: Span::new((), 93..100), - name: "amazing".to_string(), - }, - ], - }, - }, - ast::UntypedClause { - location: Span::new((), 111..117), - patterns: vec1![ast::Pattern::Int { - location: Span::new((), 111..112), - value: "3".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }], - guard: None, - then: expr::UntypedExpr::Int { - location: Span::new((), 116..117), - value: "9".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - }, - ast::UntypedClause { - location: Span::new((), 122..128), - patterns: vec1![ast::Pattern::Discard { - name: "_".to_string(), - location: Span::new((), 122..123), - }], - guard: None, - then: expr::UntypedExpr::Int { - location: Span::new((), 127..128), - value: "4".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - }, - ], - }, - doc: None, - location: Span::new((), 0..19), - name: "wow2".to_string(), - public: true, - return_annotation: None, - return_type: (), - end_position: 133, - })], - ) -} - -#[test] -fn anonymous_function() { - let code = indoc! {r#" + "# +); +assert_parse!( + anonymous_function, + r#" pub fn such() -> Int { let add_one = fn (a: Int) -> Int { a + 1 } 2 |> add_one } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 25..83), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 25..67), - value: Box::new(expr::UntypedExpr::Fn { - location: Span::new((), 39..67), - fn_style: expr::FnStyle::Plain, - arguments: vec![ast::Arg { - arg_name: ast::ArgName::Named { - label: "a".to_string(), - name: "a".to_string(), - location: Span::new((), 43..44), - is_validator_param: false, - }, - location: Span::new((), 43..49), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 46..49), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }], - body: Box::new(expr::UntypedExpr::BinOp { - location: Span::new((), 60..65), - name: ast::BinOp::AddInt, - left: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 60..61), - name: "a".to_string(), - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 64..65), - value: "1".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 54..57), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - }), - pattern: ast::Pattern::Var { - location: Span::new((), 29..36), - name: "add_one".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::PipeLine { - one_liner: true, - expressions: vec1::vec1![ - expr::UntypedExpr::Int { - location: Span::new((), 71..72), - value: "2".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - expr::UntypedExpr::Var { - location: Span::new((), 76..83), - name: "add_one".to_string(), - }, - ], - }, - ], - }, - doc: None, - location: Span::new((), 0..20), - name: "such".to_string(), - public: true, - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 17..20), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - return_type: (), - end_position: 84, - })], - ) -} - -#[test] -fn field_access() { - let code = indoc! {r#" + "# +); +assert_parse!( + field_access, + r#" fn name(user: User) { user.name } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![ast::Arg { - arg_name: ast::ArgName::Named { - label: "user".to_string(), - name: "user".to_string(), - location: Span::new((), 8..12), - is_validator_param: false, - }, - location: Span::new((), 8..18), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 14..18), - module: None, - name: "User".to_string(), - arguments: vec![], - }), - tipo: (), - }], - body: expr::UntypedExpr::FieldAccess { - location: Span::new((), 24..33), - label: "name".to_string(), - container: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 24..28), - name: "user".to_string(), - }), - }, - doc: None, - location: Span::new((), 0..19), - name: "name".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 34, - })], - ) -} - -#[test] -fn call() { - let code = indoc! {r#" + "# +); +assert_parse!( + call, + r#" fn calls() { let x = add_one(3) @@ -1510,2174 +243,138 @@ fn call() { map_add_x([ 1, 2, 3 ]) } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 15..108), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 15..33), - value: Box::new(expr::UntypedExpr::Call { - arguments: vec![ast::CallArg { - label: None, - location: Span::new((), 31..32), - value: expr::UntypedExpr::Int { - location: Span::new((), 31..32), - value: "3".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - }], - fun: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 23..30), - name: "add_one".to_string(), - }), - location: Span::new((), 23..33), - }), - pattern: ast::Pattern::Var { - location: Span::new((), 19..20), - name: "x".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Assignment { - location: Span::new((), 37..82), - value: Box::new(expr::UntypedExpr::Fn { - location: Span::new((), 53..82), - fn_style: expr::FnStyle::Capture, - arguments: vec![ast::Arg { - arg_name: ast::ArgName::Named { - label: "_capture__0".to_string(), - name: "_capture__0".to_string(), - location: Span::new((), 0..0), - is_validator_param: false, - }, - location: Span::new((), 0..0), - annotation: None, - tipo: (), - }], - body: Box::new(expr::UntypedExpr::Call { - arguments: vec![ - ast::CallArg { - label: None, - location: Span::new((), 62..63), - value: expr::UntypedExpr::Var { - location: Span::new((), 62..63), - name: "_capture__0".to_string(), - }, - }, - ast::CallArg { - label: None, - location: Span::new((), 65..81), - value: expr::UntypedExpr::Fn { - location: Span::new((), 65..81), - fn_style: expr::FnStyle::Plain, - arguments: vec![ast::Arg { - arg_name: ast::ArgName::Named { - label: "y".to_string(), - name: "y".to_string(), - location: Span::new((), 69..70), - is_validator_param: false, - }, - location: Span::new((), 69..70), - annotation: None, - tipo: (), - }], - body: Box::new(expr::UntypedExpr::BinOp { - location: Span::new((), 74..79), - name: ast::BinOp::AddInt, - left: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 74..75), - name: "x".to_string(), - }), - right: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 78..79), - name: "y".to_string(), - }), - }), - return_annotation: None, - }, - }, - ], - fun: Box::new(expr::UntypedExpr::FieldAccess { - location: Span::new((), 53..61), - label: "map".to_string(), - container: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 53..57), - name: "list".to_string(), - }), - }), - location: Span::new((), 53..82), - }), - return_annotation: None, - }), - pattern: ast::Pattern::Var { - location: Span::new((), 41..50), - name: "map_add_x".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Call { - arguments: vec![ast::CallArg { - label: None, - location: Span::new((), 96..107), - value: expr::UntypedExpr::List { - location: Span::new((), 96..107), - elements: vec![ - expr::UntypedExpr::Int { - location: Span::new((), 98..99), - value: "1".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - expr::UntypedExpr::Int { - location: Span::new((), 101..102), - value: "2".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - expr::UntypedExpr::Int { - location: Span::new((), 104..105), - value: "3".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - ], - tail: None, - }, - }], - fun: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 86..95), - name: "map_add_x".to_string(), - }), - location: Span::new((), 86..108), - }, - ], - }, - doc: None, - location: Span::new((), 0..10), - name: "calls".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 109, - })], - ) -} - -#[test] -fn record_update() { - let code = indoc! {r#" + "# +); +assert_parse!( + record_update, + r#" fn update_name(user: User, name: ByteArray) -> User { User { ..user, name: "Aiken", age } } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![ - ast::Arg { - arg_name: ast::ArgName::Named { - label: "user".to_string(), - name: "user".to_string(), - location: Span::new((), 15..19), - is_validator_param: false, - }, - location: Span::new((), 15..25), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 21..25), - module: None, - name: "User".to_string(), - arguments: vec![], - }), - tipo: (), - }, - ast::Arg { - arg_name: ast::ArgName::Named { - label: "name".to_string(), - name: "name".to_string(), - location: Span::new((), 27..31), - is_validator_param: false, - }, - location: Span::new((), 27..42), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 33..42), - module: None, - name: "ByteArray".to_string(), - arguments: vec![], - }), - tipo: (), - }, - ], - body: expr::UntypedExpr::RecordUpdate { - location: Span::new((), 56..91), - constructor: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 56..60), - name: "User".to_string(), - }), - spread: ast::RecordUpdateSpread { - base: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 65..69), - name: "user".to_string(), - }), - location: Span::new((), 63..69), - }, - arguments: vec![ - ast::UntypedRecordUpdateArg { - label: "name".to_string(), - location: Span::new((), 71..84), - value: expr::UntypedExpr::ByteArray { - location: Span::new((), 77..84), - bytes: String::from("Aiken").into_bytes(), - preferred_format: ast::ByteArrayFormatPreference::Utf8String, - }, - }, - ast::UntypedRecordUpdateArg { - label: "age".to_string(), - location: Span::new((), 86..89), - value: expr::UntypedExpr::Var { - location: Span::new((), 86..89), - name: "age".to_string(), - }, - }, - ], - }, - doc: None, - location: Span::new((), 0..51), - name: "update_name".to_string(), - public: false, - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 47..51), - module: None, - name: "User".to_string(), - arguments: vec![], - }), - return_type: (), - end_position: 92, - })], - ) -} - -#[test] -fn record_create_labeled() { - let code = indoc! {r#" + "# +); +assert_parse!( + record_create_labeled, + r#" fn create() { User { name: "Aiken", age, thing: 2 } } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(ast::Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Call { - arguments: vec![ - ast::CallArg { - label: Some("name".to_string()), - location: Span::new((), 23..36), - value: expr::UntypedExpr::ByteArray { - location: Span::new((), 29..36), - bytes: String::from("Aiken").into_bytes(), - preferred_format: ast::ByteArrayFormatPreference::Utf8String, - }, - }, - ast::CallArg { - label: Some("age".to_string()), - location: Span::new((), 38..41), - value: expr::UntypedExpr::Var { - location: Span::new((), 38..41), - name: "age".to_string(), - }, - }, - ast::CallArg { - label: Some("thing".to_string()), - location: Span::new((), 43..51), - value: expr::UntypedExpr::Int { - location: Span::new((), 50..51), - value: "2".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - }, - ], - fun: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 16..20), - name: "User".to_string(), - }), - location: Span::new((), 16..53), - }, - doc: None, - location: Span::new((), 0..11), - name: "create".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 54, - })], - ) -} - -#[test] -fn record_create_labeled_with_field_access() { - let code = indoc! {r#" + "# +); +assert_parse!( + record_create_labeled_with_field_access, + r#" fn create() { some_module.User { name: "Aiken", age, thing: 2 } } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(ast::Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Call { - arguments: vec![ - ast::CallArg { - label: Some("name".to_string()), - location: Span::new((), 35..48), - value: expr::UntypedExpr::ByteArray { - location: Span::new((), 41..48), - bytes: String::from("Aiken").into_bytes(), - preferred_format: ast::ByteArrayFormatPreference::Utf8String, - }, - }, - ast::CallArg { - label: Some("age".to_string()), - location: Span::new((), 50..53), - value: expr::UntypedExpr::Var { - location: Span::new((), 50..53), - name: "age".to_string(), - }, - }, - ast::CallArg { - label: Some("thing".to_string()), - location: Span::new((), 55..63), - value: expr::UntypedExpr::Int { - location: Span::new((), 62..63), - value: "2".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - }, - ], - fun: Box::new(expr::UntypedExpr::FieldAccess { - location: Span::new((), 16..32), - label: "User".to_string(), - container: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 16..27), - name: "some_module".to_string(), - }), - }), - location: Span::new((), 16..65), - }, - doc: None, - location: Span::new((), 0..11), - name: "create".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 66, - })], - ) -} - -#[test] -fn record_create_unlabeled() { - let code = indoc! {r#" + "# +); +assert_parse!( + cargo_create_unlabeled, + r#" fn create() { some_module.Thing(1, a) } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(ast::Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Call { - arguments: vec![ - ast::CallArg { - label: None, - location: Span::new((), 34..35), - value: expr::UntypedExpr::Int { - location: Span::new((), 34..35), - value: "1".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - }, - ast::CallArg { - label: None, - location: Span::new((), 37..38), - value: expr::UntypedExpr::Var { - location: Span::new((), 37..38), - name: "a".to_string(), - }, - }, - ], - fun: Box::new(expr::UntypedExpr::FieldAccess { - location: Span::new((), 16..33), - label: "Thing".to_string(), - container: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 16..27), - name: "some_module".to_string(), - }), - }), - location: Span::new((), 16..39), - }, - doc: None, - location: Span::new((), 0..11), - name: "create".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 40, - })], - ) -} - -#[test] -fn parse_tuple() { - let code = indoc! {r#" - fn foo() { - let tuple = (1, 2, 3, 4) - tuple.1st + tuple.2nd + tuple.3rd + tuple.4th - } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 13..85), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 13..37), - value: Box::new(expr::UntypedExpr::Tuple { - location: Span::new((), 25..37), - elems: vec![ - expr::UntypedExpr::Int { - location: Span::new((), 26..27), - value: "1".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - expr::UntypedExpr::Int { - location: Span::new((), 29..30), - value: "2".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - expr::UntypedExpr::Int { - location: Span::new((), 32..33), - value: "3".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - expr::UntypedExpr::Int { - location: Span::new((), 35..36), - value: "4".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - ], - }), - pattern: ast::Pattern::Var { - location: Span::new((), 17..22), - name: "tuple".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::BinOp { - location: Span::new((), 40..85), - name: ast::BinOp::AddInt, - left: Box::new(expr::UntypedExpr::BinOp { - location: Span::new((), 40..73), - name: ast::BinOp::AddInt, - left: Box::new(expr::UntypedExpr::BinOp { - location: Span::new((), 40..61), - name: ast::BinOp::AddInt, - left: Box::new(expr::UntypedExpr::TupleIndex { - location: Span::new((), 40..49), - index: 0, - tuple: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 40..45), - name: "tuple".to_string(), - }), - }), - right: Box::new(expr::UntypedExpr::TupleIndex { - location: Span::new((), 52..61), - index: 1, - tuple: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 52..57), - name: "tuple".to_string(), - }), - }), - }), - right: Box::new(expr::UntypedExpr::TupleIndex { - location: Span::new((), 64..73), - index: 2, - tuple: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 64..69), - name: "tuple".to_string(), - }), - }), - }), - right: Box::new(expr::UntypedExpr::TupleIndex { - location: Span::new((), 76..85), - index: 3, - tuple: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 76..81), - name: "tuple".to_string(), - }), - }), - }, - ], - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 86, - })], - ) -} - -#[test] -fn parse_tuple2() { - let code = indoc! {r#" - fn foo() { - let a = foo(14) - (a, 42) - } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 13..38), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 13..28), - value: Box::new(expr::UntypedExpr::Call { - arguments: vec![ast::CallArg { - label: None, - location: Span::new((), 25..27), - value: expr::UntypedExpr::Int { - location: Span::new((), 25..27), - value: "14".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - }], - fun: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 21..24), - name: "foo".to_string(), - }), - location: Span::new((), 21..28), - }), - pattern: ast::Pattern::Var { - location: Span::new((), 17..18), - name: "a".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Tuple { - location: Span::new((), 31..38), - elems: vec![ - expr::UntypedExpr::Var { - location: Span::new((), 32..33), - name: "a".to_string(), - }, - expr::UntypedExpr::Int { - location: Span::new((), 35..37), - value: "42".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - ], - }, - ], - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 39, - })], - ); -} - -#[test] -fn large_integer_constants() { - let code = indoc! {r#" - pub const my_big_int = 999999999999999999999999 - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::ModuleConstant(ModuleConstant { - doc: None, - location: Span::new((), 0..47), - public: true, - name: "my_big_int".to_string(), - annotation: None, - value: Box::new(ast::Constant::Int { - location: Span::new((), 23..47), - value: "999999999999999999999999".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - tipo: (), - })], - ) -} - -#[test] -fn plain_bytearray_literals() { - let code = indoc! {r#" + "# +); +assert_parse!( + parse_tuple, + r#" + fn foo() { + let tuple = (1, 2, 3, 4) + tuple.1st + tuple.2nd + tuple.3rd + tuple.4th + } + "# +); +assert_parse!( + parse_tuple2, + r#" + fn foo() { + let a = foo(14) + (a, 42) + } + "# +); +assert_parse!( + plain_bytearray_literals, + r#" pub const my_policy_id = #[0, 170, 255] - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::ModuleConstant(ModuleConstant { - doc: None, - location: Span::new((), 0..39), - public: true, - name: "my_policy_id".to_string(), - annotation: None, - value: Box::new(Constant::ByteArray { - location: Span::new((), 25..39), - bytes: vec![0, 170, 255], - preferred_format: ast::ByteArrayFormatPreference::ArrayOfBytes(Base::Decimal { - numeric_underscore: false, - }), - }), - tipo: (), - })], - ) -} - -#[test] -fn base16_bytearray_literals() { - let code = indoc! {r#" + "# +); +assert_parse!( + base16_bytearray_literals, + r#" pub const my_policy_id = #"00aaff" pub fn foo() { my_policy_id == #"00aaff" } - "#}; - - assert_definitions( - code, - vec![ - ast::UntypedDefinition::ModuleConstant(ModuleConstant { - doc: None, - location: Span::new((), 0..34), - public: true, - name: "my_policy_id".to_string(), - annotation: None, - value: Box::new(Constant::ByteArray { - location: Span::new((), 25..34), - bytes: vec![0, 170, 255], - preferred_format: ast::ByteArrayFormatPreference::HexadecimalString, - }), - tipo: (), - }), - ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::BinOp { - location: Span::new((), 55..80), - name: ast::BinOp::Eq, - left: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 55..67), - name: "my_policy_id".to_string(), - }), - right: Box::new(expr::UntypedExpr::ByteArray { - location: Span::new((), 71..80), - bytes: vec![0, 170, 255], - preferred_format: ast::ByteArrayFormatPreference::HexadecimalString, - }), - }, - doc: None, - location: Span::new((), 36..48), - name: "foo".to_string(), - public: true, - return_annotation: None, - return_type: (), - end_position: 81, - }), - ], - ) -} - -#[test] -fn function_def() { - let code = indoc! {r#" - fn foo() {} - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - doc: None, - arguments: vec![], - body: expr::UntypedExpr::Trace { - kind: ast::TraceKind::Todo, - location: Span::new((), 0..11), - text: Box::new(expr::UntypedExpr::String { - value: "aiken::todo".to_string(), - location: Span::new((), 0..11), - }), - then: Box::new(expr::UntypedExpr::ErrorTerm { - location: Span::new((), 0..11), - }), - }, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 10, - })], - ) -} - -#[test] -fn function_invoke() { - let code = indoc! {r#" - fn foo() { - let a = bar(42) - } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - doc: None, - arguments: vec![], - body: expr::UntypedExpr::Assignment { - location: Span::new((), 13..28), - kind: ast::AssignmentKind::Let, - annotation: None, - pattern: ast::Pattern::Var { - location: Span::new((), 17..18), - name: "a".to_string(), - }, - value: Box::new(expr::UntypedExpr::Call { - location: Span::new((), 21..28), - fun: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 21..24), - name: "bar".to_string(), - }), - arguments: vec![ast::CallArg { - label: None, - location: Span::new((), 25..27), - value: expr::UntypedExpr::Int { - location: Span::new((), 25..27), - value: "42".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - }], - }), - }, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 29, - })], - ) -} - -#[test] -fn function_ambiguous_sequence() { - let code = indoc! {r#" - fn foo_1() { - let a = bar - (40) - } - - fn foo_2() { - let a = bar - {40} - } - - fn foo_3() { - let a = (40+2) - } - - fn foo_4() { + "# +); +assert_parse!( + function_def, + r#" + fn foo() {} + "# +); +assert_parse!( + function_invoke, + r#" + fn foo() { let a = bar(42) - (a + 14) * 42 - } - "#}; - - assert_definitions( - code, - vec![ - ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 15..32), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 15..26), - value: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 23..26), - name: "bar".to_string(), - }), - pattern: ast::Pattern::Var { - location: Span::new((), 19..20), - name: "a".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Int { - location: Span::new((), 30..32), - value: "40".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - ], - }, - doc: None, - location: Span::new((), 0..10), - name: "foo_1".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 34, - }), - ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 52..69), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 52..63), - value: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 60..63), - name: "bar".to_string(), - }), - pattern: ast::Pattern::Var { - location: Span::new((), 56..57), - name: "a".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Int { - location: Span::new((), 67..69), - value: "40".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - ], - }, - doc: None, - location: Span::new((), 37..47), - name: "foo_2".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 71, - }), - ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Assignment { - location: Span::new((), 89..103), - value: Box::new(expr::UntypedExpr::BinOp { - location: Span::new((), 98..102), - name: ast::BinOp::AddInt, - left: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 98..100), - value: "40".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 101..102), - value: "2".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }), - pattern: ast::Pattern::Var { - location: Span::new((), 93..94), - name: "a".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - doc: None, - location: Span::new((), 74..84), - name: "foo_3".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 104, - }), - ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 122..153), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 122..137), - value: Box::new(expr::UntypedExpr::Call { - arguments: vec![ast::CallArg { - label: None, - location: Span::new((), 134..136), - value: expr::UntypedExpr::Int { - location: Span::new((), 134..136), - value: "42".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - }], - fun: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 130..133), - name: "bar".to_string(), - }), - location: Span::new((), 130..137), - }), - pattern: ast::Pattern::Var { - location: Span::new((), 126..127), - name: "a".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::BinOp { - location: Span::new((), 141..153), - name: ast::BinOp::MultInt, - left: Box::new(expr::UntypedExpr::BinOp { - location: Span::new((), 141..147), - name: ast::BinOp::AddInt, - left: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 141..142), - name: "a".to_string(), - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 145..147), - value: "14".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 151..153), - value: "42".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }, - ], - }, - doc: None, - location: Span::new((), 107..117), - name: "foo_4".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 154, - }), - ], - ) -} - -#[test] -fn tuple_type_alias() { - let code = indoc! {r#" - type RoyaltyToken = - (PolicyId, AssetName) - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::TypeAlias(TypeAlias { - alias: "RoyaltyToken".to_string(), - annotation: ast::Annotation::Tuple { - location: Span::new((), 22..43), - elems: vec![ - ast::Annotation::Constructor { - location: Span::new((), 23..31), - module: None, - name: "PolicyId".to_string(), - arguments: vec![], - }, - ast::Annotation::Constructor { - location: Span::new((), 33..42), - module: None, - name: "AssetName".to_string(), - arguments: vec![], - }, - ], - }, - doc: None, - location: Span::new((), 0..43), - parameters: vec![], - public: false, - tipo: (), - })], - ) -} - -#[test] -fn tuple_pattern() { - let code = indoc! {r#" - fn foo() { - when a is { - (u, dic) -> True - } - } - "#}; - - assert_definitions( - code, - vec![ast::UntypedDefinition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::When { - location: Span::new((), 13..49), - subject: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 18..19), - name: "a".to_string(), - }), - clauses: vec![ast::UntypedClause { - location: Span::new((), 29..45), - patterns: vec1![ast::Pattern::Tuple { - location: Span::new((), 29..37), - elems: vec![ - ast::Pattern::Var { - location: Span::new((), 30..31), - name: "u".to_string(), - }, - ast::Pattern::Var { - location: Span::new((), 33..36), - name: "dic".to_string(), - }, - ], - }], - guard: None, - then: expr::UntypedExpr::Var { - location: Span::new((), 41..45), - name: "True".to_string(), - }, - }], - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 50, - })], - ); -} - -#[test] -fn subtraction_vs_negate() { - let code = indoc! {r#" - fn foo() { - (1-1) == 0 - let x = -2 - bar()-4 - bar(-1) - 42 - } - "#}; - assert_definitions( - code, - vec![ast::Definition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 14..61), - expressions: vec![ - expr::UntypedExpr::BinOp { - location: Span::new((), 14..23), - name: ast::BinOp::Eq, - left: Box::new(expr::UntypedExpr::BinOp { - location: Span::new((), 14..17), - name: ast::BinOp::SubInt, - left: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 14..15), - value: "1".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 16..17), - value: "1".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 22..23), - value: "0".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }, - expr::UntypedExpr::Assignment { - location: Span::new((), 26..36), - value: Box::new(expr::UntypedExpr::UnOp { - op: ast::UnOp::Negate, - location: Span::new((), 34..36), - value: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 35..36), - value: "2".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }), - pattern: ast::Pattern::Var { - location: Span::new((), 30..31), - name: "x".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::BinOp { - location: Span::new((), 39..46), - name: ast::BinOp::SubInt, - left: Box::new(expr::UntypedExpr::Call { - arguments: vec![], - fun: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 39..42), - name: "bar".to_string(), - }), - location: Span::new((), 39..44), - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 45..46), - value: "4".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }, - expr::UntypedExpr::BinOp { - location: Span::new((), 49..61), - name: ast::BinOp::SubInt, - left: Box::new(expr::UntypedExpr::Call { - arguments: vec![ast::CallArg { - label: None, - location: Span::new((), 53..55), - value: expr::UntypedExpr::UnOp { - op: ast::UnOp::Negate, - location: Span::new((), 53..55), - value: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 54..55), - value: "1".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }, - }], - fun: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 49..52), - name: "bar".to_string(), - }), - location: Span::new((), 49..56), - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 59..61), - value: "42".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }, - ], - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 62, - })], - ); -} - -#[test] -fn clause_guards() { - let code = indoc! {r#" - fn foo() { - when a is { - _ if 42 -> Void - _ if bar -> Void - _ if True -> Void - _ if a || b && c -> Void - _ if (a || b) && c -> Void - _ if a <= 42 || b > 14 || "str" -> Void - _ if a == 14 && !b -> Void - _ if !!True -> Void - } - } - "#}; - assert_definitions( - code, - vec![ast::Definition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::When { - location: Span::new((), 13..250), - subject: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 18..19), - name: "a".to_string(), - }), - clauses: vec![ - ast::UntypedClause { - location: Span::new((), 29..44), - patterns: vec1![ast::Pattern::Discard { - name: "_".to_string(), - location: Span::new((), 29..30), - }], - guard: Some(ast::UntypedClauseGuard::Constant(ast::Constant::Int { - location: Span::new((), 34..36), - value: "42".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - })), - then: expr::UntypedExpr::Var { - location: Span::new((), 40..44), - name: "Void".to_string(), - }, - }, - ast::UntypedClause { - location: Span::new((), 49..65), - patterns: vec1![ast::Pattern::Discard { - name: "_".to_string(), - location: Span::new((), 49..50), - }], - guard: Some(ast::UntypedClauseGuard::Var { - location: Span::new((), 54..57), - name: "bar".to_string(), - tipo: (), - }), - then: expr::UntypedExpr::Var { - location: Span::new((), 61..65), - name: "Void".to_string(), - }, - }, - ast::UntypedClause { - location: Span::new((), 70..87), - patterns: vec1![ast::Pattern::Discard { - name: "_".to_string(), - location: Span::new((), 70..71), - }], - guard: Some(ast::UntypedClauseGuard::Var { - location: Span::new((), 75..79), - tipo: (), - name: "True".to_string(), - }), - then: expr::UntypedExpr::Var { - location: Span::new((), 83..87), - name: "Void".to_string(), - }, - }, - ast::UntypedClause { - location: Span::new((), 92..116), - patterns: vec1![ast::Pattern::Discard { - name: "_".to_string(), - location: Span::new((), 92..93), - }], - guard: Some(ast::UntypedClauseGuard::Or { - location: Span::new((), 97..108), - left: Box::new(ast::UntypedClauseGuard::Var { - location: Span::new((), 97..98), - name: "a".to_string(), - tipo: (), - }), - right: Box::new(ast::UntypedClauseGuard::And { - location: Span::new((), 102..108), - left: Box::new(ast::UntypedClauseGuard::Var { - location: Span::new((), 102..103), - name: "b".to_string(), - tipo: (), - }), - right: Box::new(ast::UntypedClauseGuard::Var { - location: Span::new((), 107..108), - name: "c".to_string(), - tipo: (), - }), - }), - }), - then: expr::UntypedExpr::Var { - location: Span::new((), 112..116), - name: "Void".to_string(), - }, - }, - ast::UntypedClause { - location: Span::new((), 121..147), - patterns: vec1![ast::Pattern::Discard { - name: "_".to_string(), - location: Span::new((), 121..122), - }], - guard: Some(ast::UntypedClauseGuard::And { - location: Span::new((), 127..139), - left: Box::new(ast::UntypedClauseGuard::Or { - location: Span::new((), 127..133), - left: Box::new(ast::UntypedClauseGuard::Var { - location: Span::new((), 127..128), - name: "a".to_string(), - tipo: (), - }), - right: Box::new(ast::UntypedClauseGuard::Var { - location: Span::new((), 132..133), - name: "b".to_string(), - tipo: (), - }), - }), - right: Box::new(ast::UntypedClauseGuard::Var { - location: Span::new((), 138..139), - name: "c".to_string(), - tipo: (), - }), - }), - then: expr::UntypedExpr::Var { - location: Span::new((), 143..147), - name: "Void".to_string(), - }, - }, - ast::UntypedClause { - location: Span::new((), 152..191), - patterns: vec1![ast::Pattern::Discard { - name: "_".to_string(), - location: Span::new((), 152..153), - }], - guard: Some(ast::UntypedClauseGuard::Or { - location: Span::new((), 157..183), - left: Box::new(ast::UntypedClauseGuard::Or { - location: Span::new((), 157..174), - left: Box::new(ast::UntypedClauseGuard::LtEqInt { - location: Span::new((), 157..164), - left: Box::new(ast::UntypedClauseGuard::Var { - location: Span::new((), 157..158), - name: "a".to_string(), - tipo: (), - }), - right: Box::new(ast::UntypedClauseGuard::Constant( - ast::Constant::Int { - location: Span::new((), 162..164), - value: "42".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - )), - }), - right: Box::new(ast::UntypedClauseGuard::GtInt { - location: Span::new((), 168..174), - left: Box::new(ast::UntypedClauseGuard::Var { - location: Span::new((), 168..169), - name: "b".to_string(), - tipo: (), - }), - right: Box::new(ast::UntypedClauseGuard::Constant( - ast::Constant::Int { - location: Span::new((), 172..174), - value: "14".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - )), - }), - }), - right: Box::new(ast::UntypedClauseGuard::Constant( - ast::Constant::ByteArray { - location: Span::new((), 178..183), - bytes: String::from("str").into_bytes(), - preferred_format: ast::ByteArrayFormatPreference::Utf8String, - }, - )), - }), - then: expr::UntypedExpr::Var { - location: Span::new((), 187..191), - name: "Void".to_string(), - }, - }, - ast::UntypedClause { - location: Span::new((), 196..222), - patterns: vec1![ast::Pattern::Discard { - name: "_".to_string(), - location: Span::new((), 196..197), - }], - guard: Some(ast::UntypedClauseGuard::And { - location: Span::new((), 201..214), - left: Box::new(ast::UntypedClauseGuard::Equals { - location: Span::new((), 201..208), - left: Box::new(ast::UntypedClauseGuard::Var { - location: Span::new((), 201..202), - name: "a".to_string(), - tipo: (), - }), - right: Box::new(ast::UntypedClauseGuard::Constant( - ast::Constant::Int { - location: Span::new((), 206..208), - value: "14".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }, - )), - }), - right: Box::new(ast::UntypedClauseGuard::Not { - location: Span::new((), 212..214), - value: Box::new(ast::UntypedClauseGuard::Var { - location: Span::new((), 213..214), - name: "b".to_string(), - tipo: (), - }), - }), - }), - then: expr::UntypedExpr::Var { - location: Span::new((), 218..222), - name: "Void".to_string(), - }, - }, - ast::UntypedClause { - location: Span::new((), 227..246), - patterns: vec1![ast::Pattern::Discard { - name: "_".to_string(), - location: Span::new((), 227..228), - }], - guard: Some(ast::UntypedClauseGuard::Not { - location: Span::new((), 232..238), - value: Box::new(ast::UntypedClauseGuard::Not { - location: Span::new((), 233..238), - value: Box::new(ast::UntypedClauseGuard::Var { - location: Span::new((), 234..238), - tipo: (), - name: "True".to_string(), - }), - }), - }), - then: expr::UntypedExpr::Var { - location: Span::new((), 242..246), - name: "Void".to_string(), - }, - }, - ], - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 251, - })], - ); -} - -#[test] -fn scope_logical_expression() { - let code = indoc! {r#" - fn foo() { - let x = !(a && b) - let y = a || b && c || d - x - } - "#}; - assert_definitions( - code, - vec![ast::Definition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 13..61), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 13..30), - value: Box::new(expr::UntypedExpr::UnOp { - op: ast::UnOp::Not, - location: Span::new((), 21..29), - value: Box::new(expr::UntypedExpr::BinOp { - location: Span::new((), 23..29), - name: ast::BinOp::And, - left: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 23..24), - name: "a".to_string(), - }), - right: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 28..29), - name: "b".to_string(), - }), - }), - }), - pattern: ast::Pattern::Var { - location: Span::new((), 17..18), - name: "x".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Assignment { - location: Span::new((), 33..57), - value: Box::new(expr::UntypedExpr::BinOp { - location: Span::new((), 41..57), - name: ast::BinOp::Or, - left: Box::new(expr::UntypedExpr::BinOp { - location: Span::new((), 41..52), - name: ast::BinOp::Or, - left: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 41..42), - name: "a".to_string(), - }), - right: Box::new(expr::UntypedExpr::BinOp { - location: Span::new((), 46..52), - name: ast::BinOp::And, - left: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 46..47), - name: "b".to_string(), - }), - right: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 51..52), - name: "c".to_string(), - }), - }), - }), - right: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 56..57), - name: "d".to_string(), - }), - }), - pattern: ast::Pattern::Var { - location: Span::new((), 37..38), - name: "y".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Var { - location: Span::new((), 60..61), - name: "x".to_string(), - }, - ], - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 62, - })], - ) -} - -#[test] -fn trace_expressions() { - let code = indoc! {r#" - fn foo() { - let msg1 = @"FOO" - trace @"INLINE" - trace msg1 - trace string.concat(msg1, @"BAR") - trace ( 14 + 42 * 1337 ) - Void - } - "#}; - assert_definitions( - code, - vec![ast::Definition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 13..131), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 13..30), - value: Box::new(expr::UntypedExpr::String { - location: Span::new((), 24..30), - value: "FOO".to_string(), - }), - pattern: ast::Pattern::Var { - location: Span::new((), 17..21), - name: "msg1".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Trace { - kind: ast::TraceKind::Trace, - location: Span::new((), 33..131), - then: Box::new(expr::UntypedExpr::Trace { - kind: ast::TraceKind::Trace, - location: Span::new((), 51..131), - then: Box::new(expr::UntypedExpr::Trace { - kind: ast::TraceKind::Trace, - location: Span::new((), 64..131), - then: Box::new(expr::UntypedExpr::Trace { - kind: ast::TraceKind::Trace, - location: Span::new((), 100..131), - then: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 127..131), - name: "Void".to_string(), - }), - text: Box::new(expr::UntypedExpr::BinOp { - location: Span::new((), 108..122), - name: ast::BinOp::AddInt, - left: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 108..110), - value: "14".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - right: Box::new(expr::UntypedExpr::BinOp { - location: Span::new((), 113..122), - name: ast::BinOp::MultInt, - left: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 113..115), - value: "42".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - right: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 118..122), - value: "1337".to_string(), - base: Base::Decimal { - numeric_underscore: false, - }, - }), - }), - }), - }), - text: Box::new(expr::UntypedExpr::Call { - arguments: vec![ - ast::CallArg { - label: None, - location: Span::new((), 84..88), - value: expr::UntypedExpr::Var { - location: Span::new((), 84..88), - name: "msg1".to_string(), - }, - }, - ast::CallArg { - label: None, - location: Span::new((), 90..96), - value: expr::UntypedExpr::String { - location: Span::new((), 90..96), - value: "BAR".to_string(), - }, - }, - ], - fun: Box::new(expr::UntypedExpr::FieldAccess { - location: Span::new((), 70..83), - label: "concat".to_string(), - container: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 70..76), - name: "string".to_string(), - }), - }), - location: Span::new((), 70..97), - }), - }), - text: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 57..61), - name: "msg1".to_string(), - }), - }), - text: Box::new(expr::UntypedExpr::String { - location: Span::new((), 39..48), - value: "INLINE".to_string(), - }), - }, - ], - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 132, - })], - ) -} - -#[test] -fn parse_keyword_error() { - let code = indoc! {r#" - fn foo() { - error @"not implemented" - } - - fn bar() { - when x is { - Something -> Void - _ -> error - } - } - "#}; - assert_definitions( - code, - vec![ - ast::Definition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Trace { - kind: ast::TraceKind::Error, - location: Span::new((), 13..37), - then: Box::new(expr::UntypedExpr::ErrorTerm { - location: Span::new((), 13..37), - }), - text: Box::new(expr::UntypedExpr::String { - location: Span::new((), 19..37), - value: "not implemented".to_string(), - }), - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 38, - }), - ast::Definition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::When { - location: Span::new((), 54..110), - subject: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 59..60), - name: "x".to_string(), - }), - clauses: vec![ - ast::UntypedClause { - location: Span::new((), 72..89), - patterns: vec1![ast::Pattern::Constructor { - is_record: false, - location: Span::new((), 72..81), - name: "Something".to_string(), - arguments: vec![], - module: None, - constructor: (), - with_spread: false, - tipo: (), - }], - guard: None, - then: expr::UntypedExpr::Var { - location: Span::new((), 85..89), - name: "Void".to_string(), - }, - }, - ast::UntypedClause { - location: Span::new((), 96..106), - patterns: vec1![ast::Pattern::Discard { - name: "_".to_string(), - location: Span::new((), 96..97), - }], - guard: None, - then: expr::UntypedExpr::Trace { - kind: ast::TraceKind::Error, - location: Span::new((), 101..106), - then: Box::new(expr::UntypedExpr::ErrorTerm { - location: Span::new((), 101..106), - }), - text: Box::new(expr::UntypedExpr::String { - location: Span::new((), 101..106), - value: "aiken::error".to_string(), - }), - }, - }, - ], - }, - doc: None, - location: Span::new((), 41..49), - name: "bar".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 111, - }), - ], - ) -} - -#[test] -fn parse_keyword_todo() { - let code = indoc! {r#" - fn foo() { - todo @"not implemented" - } - - fn bar() { - when x is { - Foo -> todo - Bar -> True - _ -> False - } - } - "#}; - assert_definitions( - code, - vec![ - ast::Definition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::Trace { - kind: ast::TraceKind::Todo, - location: Span::new((), 13..36), - then: Box::new(expr::UntypedExpr::ErrorTerm { - location: Span::new((), 13..36), - }), - text: Box::new(expr::UntypedExpr::String { - location: Span::new((), 18..36), - value: "not implemented".to_string(), - }), - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 37, - }), - ast::Definition::Fn(Function { - can_error: true, - arguments: vec![], - body: expr::UntypedExpr::When { - location: Span::new((), 53..121), - subject: Box::new(expr::UntypedExpr::Var { - location: Span::new((), 58..59), - name: "x".to_string(), - }), - clauses: vec![ - ast::UntypedClause { - location: Span::new((), 71..82), - patterns: vec1![ast::Pattern::Constructor { - is_record: false, - location: Span::new((), 71..74), - name: "Foo".to_string(), - arguments: vec![], - module: None, - constructor: (), - with_spread: false, - tipo: (), - }], - guard: None, - then: expr::UntypedExpr::Trace { - kind: ast::TraceKind::Todo, - location: Span::new((), 78..82), - then: Box::new(expr::UntypedExpr::ErrorTerm { - location: Span::new((), 78..82), - }), - text: Box::new(expr::UntypedExpr::String { - location: Span::new((), 78..82), - value: "aiken::todo".to_string(), - }), - }, - }, - ast::UntypedClause { - location: Span::new((), 89..100), - patterns: vec1![ast::Pattern::Constructor { - is_record: false, - location: Span::new((), 89..92), - name: "Bar".to_string(), - arguments: vec![], - module: None, - constructor: (), - with_spread: false, - tipo: (), - }], - guard: None, - then: expr::UntypedExpr::Var { - location: Span::new((), 96..100), - name: "True".to_string(), - }, - }, - ast::UntypedClause { - location: Span::new((), 107..117), - patterns: vec1![ast::Pattern::Discard { - name: "_".to_string(), - location: Span::new((), 107..108), - }], - guard: None, - then: expr::UntypedExpr::Var { - location: Span::new((), 112..117), - name: "False".to_string(), - }, - }, - ], - }, - doc: None, - location: Span::new((), 40..48), - name: "bar".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 122, - }), - ], - ) -} - -#[test] -fn brackets_followed_by_parenthesis() { - fn assert_sequence(code: &str) { - let (module, _extra) = parser::module(code, ast::ModuleKind::Validator).unwrap(); - assert!( - matches!( - module.definitions[..], - [ast::Definition::Test(Function { - body: expr::UntypedExpr::Sequence { .. }, - .. - })] - ), - "{}", - code.to_string() - ); - } - - assert_sequence(indoc! {r#" - test foo () { - let a = [] - (x |> y) == [] - } - "#}); - - assert_sequence(indoc! {r#" - test foo () { - let a = [] - (x |> y) == [] - } - "#}); - - assert_sequence(indoc! {r#" - test foo () { - let a = [] - // foo - (x |> y) == [] } - "#}); -} - -#[test] -fn int_parsing_hex() { - let code = indoc! {r#" - fn foo() { - let i = 0xff - } - "#}; - assert_definitions( - code, - vec![ast::Definition::Fn(Function { - arguments: vec![], - body: expr::UntypedExpr::Assignment { - location: Span::new((), 13..25), - value: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 21..25), - value: "255".to_string(), - base: Base::Hexadecimal, - }), - pattern: ast::Pattern::Var { - location: Span::new((), 17..18), - name: "i".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 26, - can_error: true, - })], - ) -} - -#[test] -fn int_parsing_hex_bytes() { - let code = indoc! {r#" + "# +); +assert_parse!( + function_ambiguous_sequence, + r#" + fn foo_1() { + let a = bar + (40) + } + + fn foo_2() { + let a = bar + {40} + } + + fn foo_3() { + let a = (40+2) + } + + fn foo_4() { + let a = bar(42) + (a + 14) * 42 + } + "# +); +assert_parse!( + tuple_type_alias, + r#" + type RoyaltyToken = (PolicyId, AssetName) + "# +); +assert_parse!( + int_parsing_hex_bytes, + r#" fn foo() { #[ 0x01, 0xa2, 0x03 ] } - "#}; - assert_definitions( - code, - vec![ast::Definition::Fn(Function { - arguments: vec![], - body: expr::UntypedExpr::ByteArray { - location: Span::new((), 13..34), - bytes: vec![1, 162, 3], - preferred_format: ast::ByteArrayFormatPreference::ArrayOfBytes(Base::Hexadecimal), - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 35, - can_error: true, - })], - ) -} - -#[test] -fn int_parsing_numeric_underscore() { - let code = indoc! {r#" + "# +); +assert_parse!( + test_parsing_numeric_underscore, + r#" fn foo() { let i = 1_234_567 let j = 1_000_000 let k = -10_000 } - "#}; - assert_definitions( - code, - vec![ast::Definition::Fn(Function { - arguments: vec![], - body: expr::UntypedExpr::Sequence { - location: Span::new((), 17..76), - expressions: vec![ - expr::UntypedExpr::Assignment { - location: Span::new((), 17..34), - value: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 25..34), - value: "1234567".to_string(), - base: Base::Decimal { - numeric_underscore: true, - }, - }), - pattern: ast::Pattern::Var { - location: Span::new((), 21..22), - name: "i".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Assignment { - location: Span::new((), 39..56), - value: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 47..56), - value: "1000000".to_string(), - base: Base::Decimal { - numeric_underscore: true, - }, - }), - pattern: ast::Pattern::Var { - location: Span::new((), 43..44), - name: "j".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - expr::UntypedExpr::Assignment { - location: Span::new((), 61..76), - value: Box::new(expr::UntypedExpr::UnOp { - op: ast::UnOp::Negate, - location: Span::new((), 69..76), - value: Box::new(expr::UntypedExpr::Int { - location: Span::new((), 70..76), - value: "10000".to_string(), - base: Base::Decimal { - numeric_underscore: true, - }, - }), - }), - pattern: ast::Pattern::Var { - location: Span::new((), 65..66), - name: "k".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - ], - }, - doc: None, - location: Span::new((), 2..10), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 77, - can_error: true, - })], - ) -} - -#[test] -fn first_class_binop() { - use ast::{Arg, ArgName::*, BinOp::*, CallArg}; - use expr::UntypedExpr::*; - - let code = indoc! {r#" + "# +); +assert_parse!( + first_class_binop, + r#" fn foo() { compare_with(a, >, b) compare_with(a, >=, b) @@ -3693,1210 +390,29 @@ fn first_class_binop() { compute_with(a, *, b) compute_with(a, %, b) } - "#}; - - assert_definitions( - code, - vec![ast::Definition::Fn(Function { - arguments: vec![], - body: Sequence { - location: Span::new((), 13..328), - expressions: vec![ - Call { - arguments: vec![ - ast::CallArg { - label: None, - location: Span::new((), 26..27), - value: Var { - location: Span::new((), 26..27), - name: "a".to_string(), - }, - }, - ast::CallArg { - label: None, - location: Span::new((), 29..30), - value: Fn { - location: Span::new((), 29..30), - fn_style: expr::FnStyle::BinOp(ast::BinOp::GtInt), - arguments: vec![ - Arg { - arg_name: Named { - name: "left".to_string(), - label: "left".to_string(), - location: Span::new((), 29..30), - is_validator_param: false, - }, - location: Span::new((), 29..30), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 29..30), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right".to_string(), - label: "right".to_string(), - location: Span::new((), 29..30), - is_validator_param: false, - }, - location: Span::new((), 29..30), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 29..30), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - ], - body: Box::new(BinOp { - location: Span::new((), 29..30), - name: GtInt, - left: Box::new(Var { - location: Span::new((), 29..30), - name: "left".to_string(), - }), - right: Box::new(Var { - location: Span::new((), 29..30), - name: "right".to_string(), - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 29..30), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), - }, - }, - CallArg { - label: None, - location: Span::new((), 32..33), - value: Var { - location: Span::new((), 32..33), - name: "b".to_string(), - }, - }, - ], - fun: Box::new(Var { - location: Span::new((), 13..25), - name: "compare_with".to_string(), - }), - location: Span::new((), 13..34), - }, - Call { - arguments: vec![ - ast::CallArg { - label: None, - location: Span::new((), 50..51), - value: Var { - location: Span::new((), 50..51), - name: "a".to_string(), - }, - }, - ast::CallArg { - label: None, - location: Span::new((), 53..55), - value: Fn { - location: Span::new((), 53..55), - fn_style: expr::FnStyle::BinOp(GtEqInt), - arguments: vec![ - Arg { - arg_name: Named { - name: "left".to_string(), - label: "left".to_string(), - location: Span::new((), 53..55), - is_validator_param: false, - }, - location: Span::new((), 53..55), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 53..55), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right".to_string(), - label: "right".to_string(), - location: Span::new((), 53..55), - is_validator_param: false, - }, - location: Span::new((), 53..55), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 53..55), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - ], - body: Box::new(BinOp { - location: Span::new((), 53..55), - name: GtEqInt, - left: Box::new(Var { - location: Span::new((), 53..55), - name: "left".to_string(), - }), - right: Box::new(Var { - location: Span::new((), 53..55), - name: "right".to_string(), - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 53..55), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), - }, - }, - ast::CallArg { - label: None, - location: Span::new((), 57..58), - value: Var { - location: Span::new((), 57..58), - name: "b".to_string(), - }, - }, - ], - fun: Box::new(Var { - location: Span::new((), 37..49), - name: "compare_with".to_string(), - }), - location: Span::new((), 37..59), - }, - Call { - arguments: vec![ - CallArg { - label: None, - location: Span::new((), 75..76), - value: Var { - location: Span::new((), 75..76), - name: "a".to_string(), - }, - }, - CallArg { - label: None, - location: Span::new((), 78..79), - value: Fn { - location: Span::new((), 78..79), - fn_style: expr::FnStyle::BinOp(LtInt), - arguments: vec![ - Arg { - arg_name: Named { - name: "left".to_string(), - label: "left".to_string(), - location: Span::new((), 78..79), - is_validator_param: false, - }, - location: Span::new((), 78..79), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 78..79), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right".to_string(), - label: "right".to_string(), - location: Span::new((), 78..79), - is_validator_param: false, - }, - location: Span::new((), 78..79), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 78..79), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - ], - body: Box::new(BinOp { - location: Span::new((), 78..79), - name: LtInt, - left: Box::new(Var { - location: Span::new((), 78..79), - name: "left".to_string(), - }), - right: Box::new(Var { - location: Span::new((), 78..79), - name: "right".to_string(), - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 78..79), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), - }, - }, - CallArg { - label: None, - location: Span::new((), 81..82), - value: Var { - location: Span::new((), 81..82), - name: "b".to_string(), - }, - }, - ], - fun: Box::new(Var { - location: Span::new((), 62..74), - name: "compare_with".to_string(), - }), - location: Span::new((), 62..83), - }, - Call { - arguments: vec![ - CallArg { - label: None, - location: Span::new((), 99..100), - value: Var { - location: Span::new((), 99..100), - name: "a".to_string(), - }, - }, - CallArg { - label: None, - location: Span::new((), 102..104), - value: Fn { - location: Span::new((), 102..104), - fn_style: expr::FnStyle::BinOp(LtEqInt), - arguments: vec![ - Arg { - arg_name: Named { - name: "left".to_string(), - label: "left".to_string(), - location: Span::new((), 102..104), - is_validator_param: false, - }, - location: Span::new((), 102..104), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 102..104), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right".to_string(), - label: "right".to_string(), - location: Span::new((), 102..104), - is_validator_param: false, - }, - location: Span::new((), 102..104), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 102..104), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - ], - body: Box::new(BinOp { - location: Span::new((), 102..104), - name: LtEqInt, - left: Box::new(Var { - location: Span::new((), 102..104), - name: "left".to_string(), - }), - right: Box::new(Var { - location: Span::new((), 102..104), - name: "right".to_string(), - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 102..104), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), - }, - }, - CallArg { - label: None, - location: Span::new((), 106..107), - value: Var { - location: Span::new((), 106..107), - name: "b".to_string(), - }, - }, - ], - fun: Box::new(Var { - location: Span::new((), 86..98), - name: "compare_with".to_string(), - }), - location: Span::new((), 86..108), - }, - Call { - arguments: vec![ - CallArg { - label: None, - location: Span::new((), 124..125), - value: Var { - location: Span::new((), 124..125), - name: "a".to_string(), - }, - }, - CallArg { - label: None, - location: Span::new((), 127..129), - value: Fn { - location: Span::new((), 127..129), - fn_style: expr::FnStyle::BinOp(Eq), - arguments: vec![ - Arg { - arg_name: Named { - name: "left".to_string(), - label: "left".to_string(), - location: Span::new((), 127..129), - is_validator_param: false, - }, - location: Span::new((), 127..129), - annotation: None, - tipo: (), - }, - Arg { - arg_name: Named { - name: "right".to_string(), - label: "right".to_string(), - location: Span::new((), 127..129), - is_validator_param: false, - }, - location: Span::new((), 127..129), - annotation: None, - tipo: (), - }, - ], - body: Box::new(BinOp { - location: Span::new((), 127..129), - name: Eq, - left: Box::new(Var { - location: Span::new((), 127..129), - name: "left".to_string(), - }), - right: Box::new(Var { - location: Span::new((), 127..129), - name: "right".to_string(), - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 127..129), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), - }, - }, - CallArg { - label: None, - location: Span::new((), 131..132), - value: Var { - location: Span::new((), 131..132), - name: "b".to_string(), - }, - }, - ], - fun: Box::new(Var { - location: Span::new((), 111..123), - name: "compare_with".to_string(), - }), - location: Span::new((), 111..133), - }, - Call { - arguments: vec![ - CallArg { - label: None, - location: Span::new((), 149..150), - value: Var { - location: Span::new((), 149..150), - name: "a".to_string(), - }, - }, - CallArg { - label: None, - location: Span::new((), 152..154), - value: Fn { - location: Span::new((), 152..154), - fn_style: expr::FnStyle::BinOp(NotEq), - arguments: vec![ - Arg { - arg_name: Named { - name: "left".to_string(), - label: "left".to_string(), - location: Span::new((), 152..154), - is_validator_param: false, - }, - location: Span::new((), 152..154), - annotation: None, - tipo: (), - }, - Arg { - arg_name: Named { - name: "right".to_string(), - label: "right".to_string(), - location: Span::new((), 152..154), - is_validator_param: false, - }, - location: Span::new((), 152..154), - annotation: None, - tipo: (), - }, - ], - body: Box::new(BinOp { - location: Span::new((), 152..154), - name: NotEq, - left: Box::new(Var { - location: Span::new((), 152..154), - name: "left".to_string(), - }), - right: Box::new(Var { - location: Span::new((), 152..154), - name: "right".to_string(), - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 152..154), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), - }, - }, - CallArg { - label: None, - location: Span::new((), 156..157), - value: Var { - location: Span::new((), 156..157), - name: "b".to_string(), - }, - }, - ], - fun: Box::new(Var { - location: Span::new((), 136..148), - name: "compare_with".to_string(), - }), - location: Span::new((), 136..158), - }, - Call { - arguments: vec![ - CallArg { - label: None, - location: Span::new((), 174..175), - value: Var { - location: Span::new((), 174..175), - name: "a".to_string(), - }, - }, - CallArg { - label: None, - location: Span::new((), 177..179), - value: Fn { - location: Span::new((), 177..179), - fn_style: expr::FnStyle::BinOp(And), - arguments: vec![ - Arg { - arg_name: Named { - name: "left".to_string(), - label: "left".to_string(), - location: Span::new((), 177..179), - is_validator_param: false, - }, - location: Span::new((), 177..179), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 177..179), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right".to_string(), - label: "right".to_string(), - location: Span::new((), 177..179), - is_validator_param: false, - }, - location: Span::new((), 177..179), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 177..179), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), - tipo: (), - }, - ], - body: Box::new(BinOp { - location: Span::new((), 177..179), - name: And, - left: Box::new(Var { - location: Span::new((), 177..179), - name: "left".to_string(), - }), - right: Box::new(Var { - location: Span::new((), 177..179), - name: "right".to_string(), - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 177..179), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), - }, - }, - CallArg { - label: None, - location: Span::new((), 181..182), - value: Var { - location: Span::new((), 181..182), - name: "b".to_string(), - }, - }, - ], - fun: Box::new(Var { - location: Span::new((), 161..173), - name: "combine_with".to_string(), - }), - location: Span::new((), 161..183), - }, - Call { - arguments: vec![ - CallArg { - label: None, - location: Span::new((), 199..200), - value: Var { - location: Span::new((), 199..200), - name: "a".to_string(), - }, - }, - CallArg { - label: None, - location: Span::new((), 202..204), - value: Fn { - location: Span::new((), 202..204), - fn_style: expr::FnStyle::BinOp(Or), - arguments: vec![ - Arg { - arg_name: Named { - name: "left".to_string(), - label: "left".to_string(), - location: Span::new((), 202..204), - is_validator_param: false, - }, - location: Span::new((), 202..204), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 202..204), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right".to_string(), - label: "right".to_string(), - location: Span::new((), 202..204), - is_validator_param: false, - }, - location: Span::new((), 202..204), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 202..204), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), - tipo: (), - }, - ], - body: Box::new(BinOp { - location: Span::new((), 202..204), - name: Or, - left: Box::new(Var { - location: Span::new((), 202..204), - name: "left".to_string(), - }), - right: Box::new(Var { - location: Span::new((), 202..204), - name: "right".to_string(), - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 202..204), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), - }, - }, - CallArg { - label: None, - location: Span::new((), 206..207), - value: Var { - location: Span::new((), 206..207), - name: "b".to_string(), - }, - }, - ], - fun: Box::new(Var { - location: Span::new((), 186..198), - name: "combine_with".to_string(), - }), - location: Span::new((), 186..208), - }, - Call { - arguments: vec![ - CallArg { - label: None, - location: Span::new((), 224..225), - value: Var { - location: Span::new((), 224..225), - name: "a".to_string(), - }, - }, - CallArg { - label: None, - location: Span::new((), 227..228), - value: Fn { - location: Span::new((), 227..228), - fn_style: expr::FnStyle::BinOp(AddInt), - arguments: vec![ - Arg { - arg_name: Named { - name: "left".to_string(), - label: "left".to_string(), - location: Span::new((), 227..228), - is_validator_param: false, - }, - location: Span::new((), 227..228), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 227..228), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right".to_string(), - label: "right".to_string(), - location: Span::new((), 227..228), - is_validator_param: false, - }, - location: Span::new((), 227..228), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 227..228), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - ], - body: Box::new(BinOp { - location: Span::new((), 227..228), - name: AddInt, - left: Box::new(Var { - location: Span::new((), 227..228), - name: "left".to_string(), - }), - right: Box::new(Var { - location: Span::new((), 227..228), - name: "right".to_string(), - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 227..228), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - }, - }, - CallArg { - label: None, - location: Span::new((), 230..231), - value: Var { - location: Span::new((), 230..231), - name: "b".to_string(), - }, - }, - ], - fun: Box::new(Var { - location: Span::new((), 211..223), - name: "compute_with".to_string(), - }), - location: Span::new((), 211..232), - }, - Call { - arguments: vec![ - CallArg { - label: None, - location: Span::new((), 248..249), - value: Var { - location: Span::new((), 248..249), - name: "a".to_string(), - }, - }, - CallArg { - label: None, - location: Span::new((), 251..252), - value: Fn { - location: Span::new((), 251..252), - fn_style: expr::FnStyle::BinOp(SubInt), - arguments: vec![ - Arg { - arg_name: Named { - name: "left".to_string(), - label: "left".to_string(), - location: Span::new((), 251..252), - is_validator_param: false, - }, - location: Span::new((), 251..252), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 251..252), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right".to_string(), - label: "right".to_string(), - location: Span::new((), 251..252), - is_validator_param: false, - }, - location: Span::new((), 251..252), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 251..252), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - ], - body: Box::new(BinOp { - location: Span::new((), 251..252), - name: SubInt, - left: Box::new(Var { - location: Span::new((), 251..252), - name: "left".to_string(), - }), - right: Box::new(Var { - location: Span::new((), 251..252), - name: "right".to_string(), - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 251..252), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - }, - }, - CallArg { - label: None, - location: Span::new((), 254..255), - value: Var { - location: Span::new((), 254..255), - name: "b".to_string(), - }, - }, - ], - fun: Box::new(Var { - location: Span::new((), 235..247), - name: "compute_with".to_string(), - }), - location: Span::new((), 235..256), - }, - Call { - arguments: vec![ - CallArg { - label: None, - location: Span::new((), 272..273), - value: Var { - location: Span::new((), 272..273), - name: "a".to_string(), - }, - }, - CallArg { - label: None, - location: Span::new((), 275..276), - value: Fn { - location: Span::new((), 275..276), - fn_style: expr::FnStyle::BinOp(DivInt), - arguments: vec![ - Arg { - arg_name: Named { - name: "left".to_string(), - label: "left".to_string(), - location: Span::new((), 275..276), - is_validator_param: false, - }, - location: Span::new((), 275..276), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 275..276), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right".to_string(), - label: "right".to_string(), - location: Span::new((), 275..276), - is_validator_param: false, - }, - location: Span::new((), 275..276), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 275..276), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - ], - body: Box::new(BinOp { - location: Span::new((), 275..276), - name: DivInt, - left: Box::new(Var { - location: Span::new((), 275..276), - name: "left".to_string(), - }), - right: Box::new(Var { - location: Span::new((), 275..276), - name: "right".to_string(), - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 275..276), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - }, - }, - CallArg { - label: None, - location: Span::new((), 278..279), - value: Var { - location: Span::new((), 278..279), - name: "b".to_string(), - }, - }, - ], - fun: Box::new(Var { - location: Span::new((), 259..271), - name: "compute_with".to_string(), - }), - location: Span::new((), 259..280), - }, - Call { - arguments: vec![ - CallArg { - label: None, - location: Span::new((), 296..297), - value: Var { - location: Span::new((), 296..297), - name: "a".to_string(), - }, - }, - CallArg { - label: None, - location: Span::new((), 299..300), - value: Fn { - location: Span::new((), 299..300), - fn_style: expr::FnStyle::BinOp(MultInt), - arguments: vec![ - Arg { - arg_name: Named { - name: "left".to_string(), - label: "left".to_string(), - location: Span::new((), 299..300), - is_validator_param: false, - }, - location: Span::new((), 299..300), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 299..300), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right".to_string(), - label: "right".to_string(), - location: Span::new((), 299..300), - is_validator_param: false, - }, - location: Span::new((), 299..300), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 299..300), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - ], - body: Box::new(BinOp { - location: Span::new((), 299..300), - name: MultInt, - left: Box::new(Var { - location: Span::new((), 299..300), - name: "left".to_string(), - }), - right: Box::new(Var { - location: Span::new((), 299..300), - name: "right".to_string(), - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 299..300), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - }, - }, - CallArg { - label: None, - location: Span::new((), 302..303), - value: Var { - location: Span::new((), 302..303), - name: "b".to_string(), - }, - }, - ], - fun: Box::new(Var { - location: Span::new((), 283..295), - name: "compute_with".to_string(), - }), - location: Span::new((), 283..304), - }, - Call { - arguments: vec![ - CallArg { - label: None, - location: Span::new((), 320..321), - value: Var { - location: Span::new((), 320..321), - name: "a".to_string(), - }, - }, - CallArg { - label: None, - location: Span::new((), 323..324), - value: Fn { - location: Span::new((), 323..324), - fn_style: expr::FnStyle::BinOp(ModInt), - arguments: vec![ - Arg { - arg_name: Named { - name: "left".to_string(), - label: "left".to_string(), - location: Span::new((), 323..324), - is_validator_param: false, - }, - location: Span::new((), 323..324), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 323..324), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right".to_string(), - label: "right".to_string(), - location: Span::new((), 323..324), - is_validator_param: false, - }, - location: Span::new((), 323..324), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 323..324), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - tipo: (), - }, - ], - body: Box::new(BinOp { - location: Span::new((), 323..324), - name: ModInt, - left: Box::new(Var { - location: Span::new((), 323..324), - name: "left".to_string(), - }), - right: Box::new(Var { - location: Span::new((), 323..324), - name: "right".to_string(), - }), - }), - return_annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 323..324), - module: None, - name: "Int".to_string(), - arguments: vec![], - }), - }, - }, - CallArg { - label: None, - location: Span::new((), 326..327), - value: Var { - location: Span::new((), 326..327), - name: "b".to_string(), - }, - }, - ], - fun: Box::new(Var { - location: Span::new((), 307..319), - name: "compute_with".to_string(), - }), - location: Span::new((), 307..328), - }, - ], - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 329, - can_error: true, - })], - ); -} + "# +); #[test] fn parse_unicode_offset_1() { - use expr::UntypedExpr::*; - - let code = indoc! {r#" - fn foo() { - let x = "★" - x - } - "#}; - - assert_definitions( - code, - vec![ast::Definition::Fn(Function { - arguments: vec![], - body: Sequence { - location: Span::new((), 13..30), - expressions: vec![ - Assignment { - location: Span::new((), 13..26), - value: Box::new(ByteArray { - location: Span::new((), 21..26), - bytes: vec![226, 152, 133], - preferred_format: ast::ByteArrayFormatPreference::Utf8String, - }), - pattern: ast::Pattern::Var { - location: Span::new((), 17..18), - name: "x".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - Var { - location: Span::new((), 29..30), - name: "x".to_string(), - }, - ], - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 31, - can_error: true, - })], - ) + assert_module!( + r#" + fn foo() { + let x = "★" + x + } + "# + ); } #[test] fn parse_unicode_offset_2() { - use expr::UntypedExpr::*; - - let code = indoc! {r#" - fn foo() { - let x = "*" - x - } - "#}; - - assert_definitions( - code, - vec![ast::Definition::Fn(Function { - arguments: vec![], - body: Sequence { - location: Span::new((), 13..28), - expressions: vec![ - Assignment { - location: Span::new((), 13..24), - value: Box::new(ByteArray { - location: Span::new((), 21..24), - bytes: vec![42], - preferred_format: ast::ByteArrayFormatPreference::Utf8String, - }), - pattern: ast::Pattern::Var { - location: Span::new((), 17..18), - name: "x".to_string(), - }, - kind: ast::AssignmentKind::Let, - annotation: None, - }, - Var { - location: Span::new((), 27..28), - name: "x".to_string(), - }, - ], - }, - doc: None, - location: Span::new((), 0..8), - name: "foo".to_string(), - public: false, - return_annotation: None, - return_type: (), - end_position: 29, - can_error: true, - })], - ) + assert_module!( + r#" + fn foo() { + let x = "*" + x + } + "# + ); } diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__anonymous_function.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__anonymous_function.snap new file mode 100644 index 000000000..450af3be0 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__anonymous_function.snap @@ -0,0 +1,110 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n pub fn such() -> Int {\n let add_one = fn (a: Int) -> Int { a + 1 }\n\n 2 |> add_one\n }\n " +info: anonymous_function +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Sequence { + location: 25..83, + expressions: [ + Assignment { + location: 25..67, + value: Fn { + location: 39..67, + fn_style: Plain, + arguments: [ + Arg { + arg_name: Named { + name: "a", + label: "a", + location: 43..44, + is_validator_param: false, + }, + location: 43..49, + annotation: Some( + Constructor { + location: 46..49, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 60..65, + name: AddInt, + left: Var { + location: 60..61, + name: "a", + }, + right: Int { + location: 64..65, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + return_annotation: Some( + Constructor { + location: 54..57, + module: None, + name: "Int", + arguments: [], + }, + ), + }, + pattern: Var { + location: 29..36, + name: "add_one", + }, + kind: Let, + annotation: None, + }, + PipeLine { + expressions: [ + Int { + location: 71..72, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + Var { + location: 76..83, + name: "add_one", + }, + ], + one_liner: true, + }, + ], + }, + doc: None, + location: 0..20, + name: "such", + public: true, + return_annotation: Some( + Constructor { + location: 17..20, + module: None, + name: "Int", + arguments: [], + }, + ), + return_type: (), + end_position: 84, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__base16_bytearray_literals.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__base16_bytearray_literals.snap new file mode 100644 index 000000000..fe0b51dd8 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__base16_bytearray_literals.snap @@ -0,0 +1,62 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n pub const my_policy_id = #\"00aaff\"\n\n pub fn foo() {\n my_policy_id == #\"00aaff\"\n }\n " +info: base16_bytearray_literals +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + ModuleConstant( + ModuleConstant { + doc: None, + location: 0..34, + public: true, + name: "my_policy_id", + annotation: None, + value: ByteArray { + location: 25..34, + bytes: [ + 0, + 170, + 255, + ], + preferred_format: HexadecimalString, + }, + tipo: (), + }, + ), + Fn( + Function { + arguments: [], + body: BinOp { + location: 55..80, + name: Eq, + left: Var { + location: 55..67, + name: "my_policy_id", + }, + right: ByteArray { + location: 71..80, + bytes: [ + 0, + 170, + 255, + ], + preferred_format: HexadecimalString, + }, + }, + doc: None, + location: 36..48, + name: "foo", + public: true, + return_annotation: None, + return_type: (), + end_position: 81, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__block.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__block.snap new file mode 100644 index 000000000..74efb4711 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__block.snap @@ -0,0 +1,99 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n pub fn wow2(a: Int){\n let b = {\n let x = 4\n\n x + 5\n }\n\n b\n }\n " +info: block +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [ + Arg { + arg_name: Named { + name: "a", + label: "a", + location: 12..13, + is_validator_param: false, + }, + location: 12..18, + annotation: Some( + Constructor { + location: 15..18, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: Sequence { + location: 23..66, + expressions: [ + Assignment { + location: 23..61, + value: Sequence { + location: 37..57, + expressions: [ + Assignment { + location: 37..46, + value: Int { + location: 45..46, + value: "4", + base: Decimal { + numeric_underscore: false, + }, + }, + pattern: Var { + location: 41..42, + name: "x", + }, + kind: Let, + annotation: None, + }, + BinOp { + location: 52..57, + name: AddInt, + left: Var { + location: 52..53, + name: "x", + }, + right: Int { + location: 56..57, + value: "5", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + }, + pattern: Var { + location: 27..28, + name: "b", + }, + kind: Let, + annotation: None, + }, + Var { + location: 65..66, + name: "b", + }, + ], + }, + doc: None, + location: 0..19, + name: "wow2", + public: true, + return_annotation: None, + return_type: (), + end_position: 67, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__call.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__call.snap new file mode 100644 index 000000000..35518d7e5 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__call.snap @@ -0,0 +1,182 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn calls() {\n let x = add_one(3)\n\n let map_add_x = list.map(_, fn (y) { x + y })\n\n map_add_x([ 1, 2, 3 ])\n }\n " +info: call +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Sequence { + location: 15..108, + expressions: [ + Assignment { + location: 15..33, + value: Call { + arguments: [ + CallArg { + label: None, + location: 31..32, + value: Int { + location: 31..32, + value: "3", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + fun: Var { + location: 23..30, + name: "add_one", + }, + location: 23..33, + }, + pattern: Var { + location: 19..20, + name: "x", + }, + kind: Let, + annotation: None, + }, + Assignment { + location: 37..82, + value: Fn { + location: 53..82, + fn_style: Capture, + arguments: [ + Arg { + arg_name: Named { + name: "_capture__0", + label: "_capture__0", + location: 0..0, + is_validator_param: false, + }, + location: 0..0, + annotation: None, + tipo: (), + }, + ], + body: Call { + arguments: [ + CallArg { + label: None, + location: 62..63, + value: Var { + location: 62..63, + name: "_capture__0", + }, + }, + CallArg { + label: None, + location: 65..81, + value: Fn { + location: 65..81, + fn_style: Plain, + arguments: [ + Arg { + arg_name: Named { + name: "y", + label: "y", + location: 69..70, + is_validator_param: false, + }, + location: 69..70, + annotation: None, + tipo: (), + }, + ], + body: BinOp { + location: 74..79, + name: AddInt, + left: Var { + location: 74..75, + name: "x", + }, + right: Var { + location: 78..79, + name: "y", + }, + }, + return_annotation: None, + }, + }, + ], + fun: FieldAccess { + location: 53..61, + label: "map", + container: Var { + location: 53..57, + name: "list", + }, + }, + location: 53..82, + }, + return_annotation: None, + }, + pattern: Var { + location: 41..50, + name: "map_add_x", + }, + kind: Let, + annotation: None, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 96..107, + value: List { + location: 96..107, + elements: [ + Int { + location: 98..99, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 101..102, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 104..105, + value: "3", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + tail: None, + }, + }, + ], + fun: Var { + location: 86..95, + name: "map_add_x", + }, + location: 86..108, + }, + ], + }, + doc: None, + location: 0..10, + name: "calls", + public: false, + return_annotation: None, + return_type: (), + end_position: 109, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__can_handle_comments_at_end_of_file.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__can_handle_comments_at_end_of_file.snap new file mode 100644 index 000000000..0de6c356a --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__can_handle_comments_at_end_of_file.snap @@ -0,0 +1,24 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n use aiken\n\n // some comment\n // more comments" +info: can_handle_comments_at_end_of_file +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Use( + Use { + as_name: None, + location: 0..9, + module: [ + "aiken", + ], + package: (), + unqualified: [], + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__cargo_create_unlabeled.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__cargo_create_unlabeled.snap new file mode 100644 index 000000000..9f0d8f384 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__cargo_create_unlabeled.snap @@ -0,0 +1,58 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn create() {\n some_module.Thing(1, a)\n }\n " +info: cargo_create_unlabeled +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Call { + arguments: [ + CallArg { + label: None, + location: 34..35, + value: Int { + location: 34..35, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + CallArg { + label: None, + location: 37..38, + value: Var { + location: 37..38, + name: "a", + }, + }, + ], + fun: FieldAccess { + location: 16..33, + label: "Thing", + container: Var { + location: 16..27, + name: "some_module", + }, + }, + location: 16..39, + }, + doc: None, + location: 0..11, + name: "create", + public: false, + return_annotation: None, + return_type: (), + end_position: 40, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__custom_type.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__custom_type.snap new file mode 100644 index 000000000..b8954b8e2 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__custom_type.snap @@ -0,0 +1,101 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n type Option {\n Some(a, Int)\n None\n Wow { name: Int, age: Int }\n }\n " +info: custom_type +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + DataType( + DataType { + constructors: [ + RecordConstructor { + location: 19..31, + name: "Some", + arguments: [ + RecordConstructorArg { + label: None, + annotation: Var { + location: 24..25, + name: "a", + }, + location: 24..25, + tipo: (), + doc: None, + }, + RecordConstructorArg { + label: None, + annotation: Constructor { + location: 27..30, + module: None, + name: "Int", + arguments: [], + }, + location: 27..30, + tipo: (), + doc: None, + }, + ], + doc: None, + sugar: false, + }, + RecordConstructor { + location: 34..38, + name: "None", + arguments: [], + doc: None, + sugar: false, + }, + RecordConstructor { + location: 41..68, + name: "Wow", + arguments: [ + RecordConstructorArg { + label: Some( + "name", + ), + annotation: Constructor { + location: 53..56, + module: None, + name: "Int", + arguments: [], + }, + location: 47..56, + tipo: (), + doc: None, + }, + RecordConstructorArg { + label: Some( + "age", + ), + annotation: Constructor { + location: 63..66, + module: None, + name: "Int", + arguments: [], + }, + location: 58..66, + tipo: (), + doc: None, + }, + ], + doc: None, + sugar: false, + }, + ], + doc: None, + location: 0..70, + name: "Option", + opaque: false, + parameters: [ + "a", + ], + public: false, + typed_parameters: [], + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__double_validator.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__double_validator.snap new file mode 100644 index 000000000..680156014 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__double_validator.snap @@ -0,0 +1,110 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n validator {\n fn foo(datum, rdmr, ctx) {\n True\n }\n\n fn bar(rdmr, ctx) {\n True\n }\n }\n " +info: double_validator +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Validator( + Validator { + doc: None, + end_position: 90, + fun: Function { + arguments: [ + Arg { + arg_name: Named { + name: "datum", + label: "datum", + location: 21..26, + is_validator_param: false, + }, + location: 21..26, + annotation: None, + tipo: (), + }, + Arg { + arg_name: Named { + name: "rdmr", + label: "rdmr", + location: 28..32, + is_validator_param: false, + }, + location: 28..32, + annotation: None, + tipo: (), + }, + Arg { + arg_name: Named { + name: "ctx", + label: "ctx", + location: 34..37, + is_validator_param: false, + }, + location: 34..37, + annotation: None, + tipo: (), + }, + ], + body: Var { + location: 45..49, + name: "True", + }, + doc: None, + location: 14..38, + name: "foo", + public: false, + return_annotation: None, + return_type: (), + end_position: 52, + can_error: true, + }, + other_fun: Some( + Function { + arguments: [ + Arg { + arg_name: Named { + name: "rdmr", + label: "rdmr", + location: 64..68, + is_validator_param: false, + }, + location: 64..68, + annotation: None, + tipo: (), + }, + Arg { + arg_name: Named { + name: "ctx", + label: "ctx", + location: 70..73, + is_validator_param: false, + }, + location: 70..73, + annotation: None, + tipo: (), + }, + ], + body: Var { + location: 81..85, + name: "True", + }, + doc: None, + location: 57..74, + name: "bar", + public: false, + return_annotation: None, + return_type: (), + end_position: 88, + can_error: true, + }, + ), + location: 0..9, + params: [], + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__empty_function.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__empty_function.snap new file mode 100644 index 000000000..cc09bb21c --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__empty_function.snap @@ -0,0 +1,37 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n pub fn run() {}\n " +info: empty_function +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Trace { + kind: Todo, + location: 0..15, + then: ErrorTerm { + location: 0..15, + }, + text: String { + location: 0..15, + value: "aiken::todo", + }, + }, + doc: None, + location: 0..12, + name: "run", + public: true, + return_annotation: None, + return_type: (), + end_position: 14, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__expect.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__expect.snap new file mode 100644 index 000000000..ffd54e163 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__expect.snap @@ -0,0 +1,71 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n pub fn run() {\n expect Some(x) = something.field\n x.other_field\n }\n " +info: expect +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Sequence { + location: 19..69, + expressions: [ + Assignment { + location: 19..51, + value: FieldAccess { + location: 36..51, + label: "field", + container: Var { + location: 36..45, + name: "something", + }, + }, + pattern: Constructor { + is_record: false, + location: 26..33, + name: "Some", + arguments: [ + CallArg { + label: None, + location: 31..32, + value: Var { + location: 31..32, + name: "x", + }, + }, + ], + module: None, + constructor: (), + with_spread: false, + tipo: (), + }, + kind: Expect, + annotation: None, + }, + FieldAccess { + location: 56..69, + label: "other_field", + container: Var { + location: 56..57, + name: "x", + }, + }, + ], + }, + doc: None, + location: 0..12, + name: "run", + public: true, + return_annotation: None, + return_type: (), + end_position: 70, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__fail.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__fail.snap new file mode 100644 index 000000000..a4b0dfc82 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__fail.snap @@ -0,0 +1,54 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n !test invalid_inputs() {\n expect True = False\n\n False\n }\n " +info: test_fail +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Test( + Function { + arguments: [], + body: Sequence { + location: 27..55, + expressions: [ + Assignment { + location: 27..46, + value: Var { + location: 41..46, + name: "False", + }, + pattern: Constructor { + is_record: false, + location: 34..38, + name: "True", + arguments: [], + module: None, + constructor: (), + with_spread: false, + tipo: (), + }, + kind: Expect, + annotation: None, + }, + Var { + location: 50..55, + name: "False", + }, + ], + }, + doc: None, + location: 0..22, + name: "invalid_inputs", + public: false, + return_annotation: None, + return_type: (), + end_position: 56, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__field_access.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__field_access.snap new file mode 100644 index 000000000..6b932afa3 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__field_access.snap @@ -0,0 +1,53 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn name(user: User) {\n user.name\n }\n " +info: field_access +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [ + Arg { + arg_name: Named { + name: "user", + label: "user", + location: 8..12, + is_validator_param: false, + }, + location: 8..18, + annotation: Some( + Constructor { + location: 14..18, + module: None, + name: "User", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: FieldAccess { + location: 24..33, + label: "name", + container: Var { + location: 24..28, + name: "user", + }, + }, + doc: None, + location: 0..19, + name: "name", + public: false, + return_annotation: None, + return_type: (), + end_position: 34, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__first_class_binop.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__first_class_binop.snap new file mode 100644 index 000000000..56043b21c --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__first_class_binop.snap @@ -0,0 +1,1212 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn foo() {\n compare_with(a, >, b)\n compare_with(a, >=, b)\n compare_with(a, <, b)\n compare_with(a, <=, b)\n compare_with(a, ==, b)\n compare_with(a, !=, b)\n combine_with(a, &&, b)\n combine_with(a, ||, b)\n compute_with(a, +, b)\n compute_with(a, -, b)\n compute_with(a, /, b)\n compute_with(a, *, b)\n compute_with(a, %, b)\n }\n " +info: first_class_binop +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Sequence { + location: 13..328, + expressions: [ + Call { + arguments: [ + CallArg { + label: None, + location: 26..27, + value: Var { + location: 26..27, + name: "a", + }, + }, + CallArg { + label: None, + location: 29..30, + value: Fn { + location: 29..30, + fn_style: BinOp( + GtInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 29..30, + is_validator_param: false, + }, + location: 29..30, + annotation: Some( + Constructor { + location: 29..30, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 29..30, + is_validator_param: false, + }, + location: 29..30, + annotation: Some( + Constructor { + location: 29..30, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 29..30, + name: GtInt, + left: Var { + location: 29..30, + name: "left", + }, + right: Var { + location: 29..30, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 29..30, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 32..33, + value: Var { + location: 32..33, + name: "b", + }, + }, + ], + fun: Var { + location: 13..25, + name: "compare_with", + }, + location: 13..34, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 50..51, + value: Var { + location: 50..51, + name: "a", + }, + }, + CallArg { + label: None, + location: 53..55, + value: Fn { + location: 53..55, + fn_style: BinOp( + GtEqInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 53..55, + is_validator_param: false, + }, + location: 53..55, + annotation: Some( + Constructor { + location: 53..55, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 53..55, + is_validator_param: false, + }, + location: 53..55, + annotation: Some( + Constructor { + location: 53..55, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 53..55, + name: GtEqInt, + left: Var { + location: 53..55, + name: "left", + }, + right: Var { + location: 53..55, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 53..55, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 57..58, + value: Var { + location: 57..58, + name: "b", + }, + }, + ], + fun: Var { + location: 37..49, + name: "compare_with", + }, + location: 37..59, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 75..76, + value: Var { + location: 75..76, + name: "a", + }, + }, + CallArg { + label: None, + location: 78..79, + value: Fn { + location: 78..79, + fn_style: BinOp( + LtInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 78..79, + is_validator_param: false, + }, + location: 78..79, + annotation: Some( + Constructor { + location: 78..79, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 78..79, + is_validator_param: false, + }, + location: 78..79, + annotation: Some( + Constructor { + location: 78..79, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 78..79, + name: LtInt, + left: Var { + location: 78..79, + name: "left", + }, + right: Var { + location: 78..79, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 78..79, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 81..82, + value: Var { + location: 81..82, + name: "b", + }, + }, + ], + fun: Var { + location: 62..74, + name: "compare_with", + }, + location: 62..83, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 99..100, + value: Var { + location: 99..100, + name: "a", + }, + }, + CallArg { + label: None, + location: 102..104, + value: Fn { + location: 102..104, + fn_style: BinOp( + LtEqInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 102..104, + is_validator_param: false, + }, + location: 102..104, + annotation: Some( + Constructor { + location: 102..104, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 102..104, + is_validator_param: false, + }, + location: 102..104, + annotation: Some( + Constructor { + location: 102..104, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 102..104, + name: LtEqInt, + left: Var { + location: 102..104, + name: "left", + }, + right: Var { + location: 102..104, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 102..104, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 106..107, + value: Var { + location: 106..107, + name: "b", + }, + }, + ], + fun: Var { + location: 86..98, + name: "compare_with", + }, + location: 86..108, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 124..125, + value: Var { + location: 124..125, + name: "a", + }, + }, + CallArg { + label: None, + location: 127..129, + value: Fn { + location: 127..129, + fn_style: BinOp( + Eq, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 127..129, + is_validator_param: false, + }, + location: 127..129, + annotation: None, + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 127..129, + is_validator_param: false, + }, + location: 127..129, + annotation: None, + tipo: (), + }, + ], + body: BinOp { + location: 127..129, + name: Eq, + left: Var { + location: 127..129, + name: "left", + }, + right: Var { + location: 127..129, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 127..129, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 131..132, + value: Var { + location: 131..132, + name: "b", + }, + }, + ], + fun: Var { + location: 111..123, + name: "compare_with", + }, + location: 111..133, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 149..150, + value: Var { + location: 149..150, + name: "a", + }, + }, + CallArg { + label: None, + location: 152..154, + value: Fn { + location: 152..154, + fn_style: BinOp( + NotEq, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 152..154, + is_validator_param: false, + }, + location: 152..154, + annotation: None, + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 152..154, + is_validator_param: false, + }, + location: 152..154, + annotation: None, + tipo: (), + }, + ], + body: BinOp { + location: 152..154, + name: NotEq, + left: Var { + location: 152..154, + name: "left", + }, + right: Var { + location: 152..154, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 152..154, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 156..157, + value: Var { + location: 156..157, + name: "b", + }, + }, + ], + fun: Var { + location: 136..148, + name: "compare_with", + }, + location: 136..158, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 174..175, + value: Var { + location: 174..175, + name: "a", + }, + }, + CallArg { + label: None, + location: 177..179, + value: Fn { + location: 177..179, + fn_style: BinOp( + And, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 177..179, + is_validator_param: false, + }, + location: 177..179, + annotation: Some( + Constructor { + location: 177..179, + module: None, + name: "Bool", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 177..179, + is_validator_param: false, + }, + location: 177..179, + annotation: Some( + Constructor { + location: 177..179, + module: None, + name: "Bool", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 177..179, + name: And, + left: Var { + location: 177..179, + name: "left", + }, + right: Var { + location: 177..179, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 177..179, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 181..182, + value: Var { + location: 181..182, + name: "b", + }, + }, + ], + fun: Var { + location: 161..173, + name: "combine_with", + }, + location: 161..183, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 199..200, + value: Var { + location: 199..200, + name: "a", + }, + }, + CallArg { + label: None, + location: 202..204, + value: Fn { + location: 202..204, + fn_style: BinOp( + Or, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 202..204, + is_validator_param: false, + }, + location: 202..204, + annotation: Some( + Constructor { + location: 202..204, + module: None, + name: "Bool", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 202..204, + is_validator_param: false, + }, + location: 202..204, + annotation: Some( + Constructor { + location: 202..204, + module: None, + name: "Bool", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 202..204, + name: Or, + left: Var { + location: 202..204, + name: "left", + }, + right: Var { + location: 202..204, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 202..204, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 206..207, + value: Var { + location: 206..207, + name: "b", + }, + }, + ], + fun: Var { + location: 186..198, + name: "combine_with", + }, + location: 186..208, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 224..225, + value: Var { + location: 224..225, + name: "a", + }, + }, + CallArg { + label: None, + location: 227..228, + value: Fn { + location: 227..228, + fn_style: BinOp( + AddInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 227..228, + is_validator_param: false, + }, + location: 227..228, + annotation: Some( + Constructor { + location: 227..228, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 227..228, + is_validator_param: false, + }, + location: 227..228, + annotation: Some( + Constructor { + location: 227..228, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 227..228, + name: AddInt, + left: Var { + location: 227..228, + name: "left", + }, + right: Var { + location: 227..228, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 227..228, + module: None, + name: "Int", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 230..231, + value: Var { + location: 230..231, + name: "b", + }, + }, + ], + fun: Var { + location: 211..223, + name: "compute_with", + }, + location: 211..232, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 248..249, + value: Var { + location: 248..249, + name: "a", + }, + }, + CallArg { + label: None, + location: 251..252, + value: Fn { + location: 251..252, + fn_style: BinOp( + SubInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 251..252, + is_validator_param: false, + }, + location: 251..252, + annotation: Some( + Constructor { + location: 251..252, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 251..252, + is_validator_param: false, + }, + location: 251..252, + annotation: Some( + Constructor { + location: 251..252, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 251..252, + name: SubInt, + left: Var { + location: 251..252, + name: "left", + }, + right: Var { + location: 251..252, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 251..252, + module: None, + name: "Int", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 254..255, + value: Var { + location: 254..255, + name: "b", + }, + }, + ], + fun: Var { + location: 235..247, + name: "compute_with", + }, + location: 235..256, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 272..273, + value: Var { + location: 272..273, + name: "a", + }, + }, + CallArg { + label: None, + location: 275..276, + value: Fn { + location: 275..276, + fn_style: BinOp( + DivInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 275..276, + is_validator_param: false, + }, + location: 275..276, + annotation: Some( + Constructor { + location: 275..276, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 275..276, + is_validator_param: false, + }, + location: 275..276, + annotation: Some( + Constructor { + location: 275..276, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 275..276, + name: DivInt, + left: Var { + location: 275..276, + name: "left", + }, + right: Var { + location: 275..276, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 275..276, + module: None, + name: "Int", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 278..279, + value: Var { + location: 278..279, + name: "b", + }, + }, + ], + fun: Var { + location: 259..271, + name: "compute_with", + }, + location: 259..280, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 296..297, + value: Var { + location: 296..297, + name: "a", + }, + }, + CallArg { + label: None, + location: 299..300, + value: Fn { + location: 299..300, + fn_style: BinOp( + MultInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 299..300, + is_validator_param: false, + }, + location: 299..300, + annotation: Some( + Constructor { + location: 299..300, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 299..300, + is_validator_param: false, + }, + location: 299..300, + annotation: Some( + Constructor { + location: 299..300, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 299..300, + name: MultInt, + left: Var { + location: 299..300, + name: "left", + }, + right: Var { + location: 299..300, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 299..300, + module: None, + name: "Int", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 302..303, + value: Var { + location: 302..303, + name: "b", + }, + }, + ], + fun: Var { + location: 283..295, + name: "compute_with", + }, + location: 283..304, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 320..321, + value: Var { + location: 320..321, + name: "a", + }, + }, + CallArg { + label: None, + location: 323..324, + value: Fn { + location: 323..324, + fn_style: BinOp( + ModInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 323..324, + is_validator_param: false, + }, + location: 323..324, + annotation: Some( + Constructor { + location: 323..324, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 323..324, + is_validator_param: false, + }, + location: 323..324, + annotation: Some( + Constructor { + location: 323..324, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 323..324, + name: ModInt, + left: Var { + location: 323..324, + name: "left", + }, + right: Var { + location: 323..324, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 323..324, + module: None, + name: "Int", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 326..327, + value: Var { + location: 326..327, + name: "b", + }, + }, + ], + fun: Var { + location: 307..319, + name: "compute_with", + }, + location: 307..328, + }, + ], + }, + doc: None, + location: 0..8, + name: "foo", + public: false, + return_annotation: None, + return_type: (), + end_position: 329, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_ambiguous_sequence.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_ambiguous_sequence.snap new file mode 100644 index 000000000..5eab570c3 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_ambiguous_sequence.snap @@ -0,0 +1,202 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn foo_1() {\n let a = bar\n (40)\n }\n\n fn foo_2() {\n let a = bar\n {40}\n }\n\n fn foo_3() {\n let a = (40+2)\n }\n\n fn foo_4() {\n let a = bar(42)\n (a + 14) * 42\n }\n " +info: function_ambiguous_sequence +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Sequence { + location: 15..32, + expressions: [ + Assignment { + location: 15..26, + value: Var { + location: 23..26, + name: "bar", + }, + pattern: Var { + location: 19..20, + name: "a", + }, + kind: Let, + annotation: None, + }, + Int { + location: 30..32, + value: "40", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + }, + doc: None, + location: 0..10, + name: "foo_1", + public: false, + return_annotation: None, + return_type: (), + end_position: 34, + can_error: true, + }, + ), + Fn( + Function { + arguments: [], + body: Sequence { + location: 52..69, + expressions: [ + Assignment { + location: 52..63, + value: Var { + location: 60..63, + name: "bar", + }, + pattern: Var { + location: 56..57, + name: "a", + }, + kind: Let, + annotation: None, + }, + Int { + location: 67..69, + value: "40", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + }, + doc: None, + location: 37..47, + name: "foo_2", + public: false, + return_annotation: None, + return_type: (), + end_position: 71, + can_error: true, + }, + ), + Fn( + Function { + arguments: [], + body: Assignment { + location: 89..103, + value: BinOp { + location: 98..102, + name: AddInt, + left: Int { + location: 98..100, + value: "40", + base: Decimal { + numeric_underscore: false, + }, + }, + right: Int { + location: 101..102, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + pattern: Var { + location: 93..94, + name: "a", + }, + kind: Let, + annotation: None, + }, + doc: None, + location: 74..84, + name: "foo_3", + public: false, + return_annotation: None, + return_type: (), + end_position: 104, + can_error: true, + }, + ), + Fn( + Function { + arguments: [], + body: Sequence { + location: 122..153, + expressions: [ + Assignment { + location: 122..137, + value: Call { + arguments: [ + CallArg { + label: None, + location: 134..136, + value: Int { + location: 134..136, + value: "42", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + fun: Var { + location: 130..133, + name: "bar", + }, + location: 130..137, + }, + pattern: Var { + location: 126..127, + name: "a", + }, + kind: Let, + annotation: None, + }, + BinOp { + location: 141..153, + name: MultInt, + left: BinOp { + location: 141..147, + name: AddInt, + left: Var { + location: 141..142, + name: "a", + }, + right: Int { + location: 145..147, + value: "14", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + right: Int { + location: 151..153, + value: "42", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + }, + doc: None, + location: 107..117, + name: "foo_4", + public: false, + return_annotation: None, + return_type: (), + end_position: 154, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_def.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_def.snap new file mode 100644 index 000000000..0ee47d90a --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_def.snap @@ -0,0 +1,37 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn foo() {}\n " +info: function_def +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Trace { + kind: Todo, + location: 0..11, + then: ErrorTerm { + location: 0..11, + }, + text: String { + location: 0..11, + value: "aiken::todo", + }, + }, + doc: None, + location: 0..8, + name: "foo", + public: false, + return_annotation: None, + return_type: (), + end_position: 10, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_invoke.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_invoke.snap new file mode 100644 index 000000000..fad161a3c --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_invoke.snap @@ -0,0 +1,55 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn foo() {\n let a = bar(42)\n }\n " +info: function_invoke +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Assignment { + location: 15..30, + value: Call { + arguments: [ + CallArg { + label: None, + location: 27..29, + value: Int { + location: 27..29, + value: "42", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + fun: Var { + location: 23..26, + name: "bar", + }, + location: 23..30, + }, + pattern: Var { + location: 19..20, + name: "a", + }, + kind: Let, + annotation: None, + }, + doc: None, + location: 0..8, + name: "foo", + public: false, + return_annotation: None, + return_type: (), + end_position: 31, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__if_expression.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__if_expression.snap new file mode 100644 index 000000000..e031484da --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__if_expression.snap @@ -0,0 +1,110 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn ifs() {\n if True {\n 1 + 1\n } else if a < 4 {\n 5\n } else if a || b {\n 6\n } else {\n 3\n }\n }\n " +info: if_expression +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: If { + location: 13..106, + branches: [ + IfBranch { + condition: Var { + location: 16..20, + name: "True", + }, + body: BinOp { + location: 27..32, + name: AddInt, + left: Int { + location: 27..28, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + right: Int { + location: 31..32, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + location: 16..36, + }, + IfBranch { + condition: BinOp { + location: 45..50, + name: LtInt, + left: Var { + location: 45..46, + name: "a", + }, + right: Int { + location: 49..50, + value: "4", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + body: Int { + location: 57..58, + value: "5", + base: Decimal { + numeric_underscore: false, + }, + }, + location: 45..62, + }, + IfBranch { + condition: BinOp { + location: 71..77, + name: Or, + left: Var { + location: 71..72, + name: "a", + }, + right: Var { + location: 76..77, + name: "b", + }, + }, + body: Int { + location: 84..85, + value: "6", + base: Decimal { + numeric_underscore: false, + }, + }, + location: 71..89, + }, + ], + final_else: Int { + location: 101..102, + value: "3", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + doc: None, + location: 0..8, + name: "ifs", + public: false, + return_annotation: None, + return_type: (), + end_position: 107, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import.snap new file mode 100644 index 000000000..bb9e4663c --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import.snap @@ -0,0 +1,25 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n use std/list\n " +info: import +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Use( + Use { + as_name: None, + location: 0..12, + module: [ + "std", + "list", + ], + package: (), + unqualified: [], + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import_alias.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import_alias.snap new file mode 100644 index 000000000..e61aa49ee --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import_alias.snap @@ -0,0 +1,27 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n use std/tx as t\n " +info: import_alias +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Use( + Use { + as_name: Some( + "t", + ), + location: 0..15, + module: [ + "std", + "tx", + ], + package: (), + unqualified: [], + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__int_parsing_hex_bytes.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__int_parsing_hex_bytes.snap new file mode 100644 index 000000000..6d8eed84e --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__int_parsing_hex_bytes.snap @@ -0,0 +1,37 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn foo() {\n #[ 0x01, 0xa2, 0x03 ]\n }\n " +info: int_parsing_hex_bytes +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: ByteArray { + location: 13..34, + bytes: [ + 1, + 162, + 3, + ], + preferred_format: ArrayOfBytes( + Hexadecimal, + ), + }, + doc: None, + location: 0..8, + name: "foo", + public: false, + return_annotation: None, + return_type: (), + end_position: 35, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__let_bindings.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__let_bindings.snap new file mode 100644 index 000000000..aa4e73c79 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__let_bindings.snap @@ -0,0 +1,137 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n pub fn wow(a: Int) {\n let x =\n a + 2\n |> add_one\n |> add_one\n\n let thing = [ 1, 2, a ]\n\n let idk = thing\n\n y\n }\n " +info: let_bindings +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [ + Arg { + arg_name: Named { + name: "a", + label: "a", + location: 11..12, + is_validator_param: false, + }, + location: 11..17, + annotation: Some( + Constructor { + location: 14..17, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: Sequence { + location: 23..121, + expressions: [ + Assignment { + location: 23..70, + value: PipeLine { + expressions: [ + BinOp { + location: 35..40, + name: AddInt, + left: Var { + location: 35..36, + name: "a", + }, + right: Int { + location: 39..40, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + Var { + location: 48..55, + name: "add_one", + }, + Var { + location: 63..70, + name: "add_one", + }, + ], + one_liner: false, + }, + pattern: Var { + location: 27..28, + name: "x", + }, + kind: Let, + annotation: None, + }, + Assignment { + location: 74..97, + value: List { + location: 86..97, + elements: [ + Int { + location: 88..89, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 91..92, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + Var { + location: 94..95, + name: "a", + }, + ], + tail: None, + }, + pattern: Var { + location: 78..83, + name: "thing", + }, + kind: Let, + annotation: None, + }, + Assignment { + location: 101..116, + value: Var { + location: 111..116, + name: "thing", + }, + pattern: Var { + location: 105..108, + name: "idk", + }, + kind: Let, + annotation: None, + }, + Var { + location: 120..121, + name: "y", + }, + ], + }, + doc: None, + location: 0..18, + name: "wow", + public: true, + return_annotation: None, + return_type: (), + end_position: 122, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__opaque_type.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__opaque_type.snap new file mode 100644 index 000000000..5d2546d74 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__opaque_type.snap @@ -0,0 +1,46 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n pub opaque type User {\n name: _w\n }\n " +info: opaque_type +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + DataType( + DataType { + constructors: [ + RecordConstructor { + location: 21..35, + name: "User", + arguments: [ + RecordConstructorArg { + label: Some( + "name", + ), + annotation: Hole { + location: 31..33, + name: "_w", + }, + location: 25..33, + tipo: (), + doc: None, + }, + ], + doc: None, + sugar: true, + }, + ], + doc: None, + location: 0..35, + name: "User", + opaque: true, + parameters: [], + public: true, + typed_parameters: [], + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple.snap new file mode 100644 index 000000000..5c6d780eb --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple.snap @@ -0,0 +1,117 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn foo() {\n let tuple = (1, 2, 3, 4)\n tuple.1st + tuple.2nd + tuple.3rd + tuple.4th\n }\n " +info: parse_tuple +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Sequence { + location: 13..85, + expressions: [ + Assignment { + location: 13..37, + value: Tuple { + location: 25..37, + elems: [ + Int { + location: 26..27, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 29..30, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 32..33, + value: "3", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 35..36, + value: "4", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + }, + pattern: Var { + location: 17..22, + name: "tuple", + }, + kind: Let, + annotation: None, + }, + BinOp { + location: 40..85, + name: AddInt, + left: BinOp { + location: 40..73, + name: AddInt, + left: BinOp { + location: 40..61, + name: AddInt, + left: TupleIndex { + location: 40..49, + index: 0, + tuple: Var { + location: 40..45, + name: "tuple", + }, + }, + right: TupleIndex { + location: 52..61, + index: 1, + tuple: Var { + location: 52..57, + name: "tuple", + }, + }, + }, + right: TupleIndex { + location: 64..73, + index: 2, + tuple: Var { + location: 64..69, + name: "tuple", + }, + }, + }, + right: TupleIndex { + location: 76..85, + index: 3, + tuple: Var { + location: 76..81, + name: "tuple", + }, + }, + }, + ], + }, + doc: None, + location: 0..8, + name: "foo", + public: false, + return_annotation: None, + return_type: (), + end_position: 86, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple2.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple2.snap new file mode 100644 index 000000000..5cd5d8f03 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple2.snap @@ -0,0 +1,76 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn foo() {\n let a = foo(14)\n (a, 42)\n }\n " +info: parse_tuple2 +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Sequence { + location: 13..38, + expressions: [ + Assignment { + location: 13..28, + value: Call { + arguments: [ + CallArg { + label: None, + location: 25..27, + value: Int { + location: 25..27, + value: "14", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + fun: Var { + location: 21..24, + name: "foo", + }, + location: 21..28, + }, + pattern: Var { + location: 17..18, + name: "a", + }, + kind: Let, + annotation: None, + }, + Tuple { + location: 31..38, + elems: [ + Var { + location: 32..33, + name: "a", + }, + Int { + location: 35..37, + value: "42", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + }, + ], + }, + doc: None, + location: 0..8, + name: "foo", + public: false, + return_annotation: None, + return_type: (), + end_position: 39, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parsing_numeric_underscore.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parsing_numeric_underscore.snap new file mode 100644 index 000000000..0b2989841 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parsing_numeric_underscore.snap @@ -0,0 +1,83 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn foo() {\n let i = 1_234_567\n let j = 1_000_000\n let k = -10_000\n }\n " +info: test_parsing_numeric_underscore +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Sequence { + location: 17..76, + expressions: [ + Assignment { + location: 17..34, + value: Int { + location: 25..34, + value: "1234567", + base: Decimal { + numeric_underscore: true, + }, + }, + pattern: Var { + location: 21..22, + name: "i", + }, + kind: Let, + annotation: None, + }, + Assignment { + location: 39..56, + value: Int { + location: 47..56, + value: "1000000", + base: Decimal { + numeric_underscore: true, + }, + }, + pattern: Var { + location: 43..44, + name: "j", + }, + kind: Let, + annotation: None, + }, + Assignment { + location: 61..76, + value: UnOp { + op: Negate, + location: 69..76, + value: Int { + location: 70..76, + value: "10000", + base: Decimal { + numeric_underscore: true, + }, + }, + }, + pattern: Var { + location: 65..66, + name: "k", + }, + kind: Let, + annotation: None, + }, + ], + }, + doc: None, + location: 2..10, + name: "foo", + public: false, + return_annotation: None, + return_type: (), + end_position: 77, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pipeline.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pipeline.snap new file mode 100644 index 000000000..cdeb27a8b --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pipeline.snap @@ -0,0 +1,73 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n pub fn thing(thing a: Int) {\n a + 2\n |> add_one\n |> add_one\n }\n " +info: pipeline +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [ + Arg { + arg_name: Named { + name: "a", + label: "thing", + location: 13..20, + is_validator_param: false, + }, + location: 13..25, + annotation: Some( + Constructor { + location: 22..25, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: PipeLine { + expressions: [ + BinOp { + location: 31..36, + name: AddInt, + left: Var { + location: 31..32, + name: "a", + }, + right: Int { + location: 35..36, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + Var { + location: 42..49, + name: "add_one", + }, + Var { + location: 55..62, + name: "add_one", + }, + ], + one_liner: false, + }, + doc: None, + location: 0..26, + name: "thing", + public: true, + return_annotation: None, + return_type: (), + end_position: 63, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plain_bytearray_literals.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plain_bytearray_literals.snap new file mode 100644 index 000000000..5178dc9ab --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plain_bytearray_literals.snap @@ -0,0 +1,36 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n pub const my_policy_id = #[0, 170, 255]\n " +info: plain_bytearray_literals +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + ModuleConstant( + ModuleConstant { + doc: None, + location: 0..39, + public: true, + name: "my_policy_id", + annotation: None, + value: ByteArray { + location: 25..39, + bytes: [ + 0, + 170, + 255, + ], + preferred_format: ArrayOfBytes( + Decimal { + numeric_underscore: false, + }, + ), + }, + tipo: (), + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plus_binop.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plus_binop.snap new file mode 100644 index 000000000..9ff39e325 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plus_binop.snap @@ -0,0 +1,60 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n pub fn add_one(a) -> Int {\n a + 1\n }\n " +info: plus_binop +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [ + Arg { + arg_name: Named { + name: "a", + label: "a", + location: 15..16, + is_validator_param: false, + }, + location: 15..16, + annotation: None, + tipo: (), + }, + ], + body: BinOp { + location: 29..34, + name: AddInt, + left: Var { + location: 29..30, + name: "a", + }, + right: Int { + location: 33..34, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + doc: None, + location: 0..24, + name: "add_one", + public: true, + return_annotation: Some( + Constructor { + location: 21..24, + module: None, + name: "Int", + arguments: [], + }, + ), + return_type: (), + end_position: 35, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pub_type_alias.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pub_type_alias.snap new file mode 100644 index 000000000..0037e397f --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pub_type_alias.snap @@ -0,0 +1,36 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n pub type Me = Option\n " +info: pub_type_alias +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + TypeAlias( + TypeAlias { + alias: "Me", + annotation: Constructor { + location: 14..28, + module: None, + name: "Option", + arguments: [ + Constructor { + location: 21..27, + module: None, + name: "String", + arguments: [], + }, + ], + }, + doc: None, + location: 0..28, + parameters: [], + public: true, + tipo: (), + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled.snap new file mode 100644 index 000000000..ade46c033 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled.snap @@ -0,0 +1,75 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn create() {\n User { name: \"Aiken\", age, thing: 2 }\n }\n " +info: record_create_labeled +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Call { + arguments: [ + CallArg { + label: Some( + "name", + ), + location: 23..36, + value: ByteArray { + location: 29..36, + bytes: [ + 65, + 105, + 107, + 101, + 110, + ], + preferred_format: Utf8String, + }, + }, + CallArg { + label: Some( + "age", + ), + location: 38..41, + value: Var { + location: 38..41, + name: "age", + }, + }, + CallArg { + label: Some( + "thing", + ), + location: 43..51, + value: Int { + location: 50..51, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + fun: Var { + location: 16..20, + name: "User", + }, + location: 16..53, + }, + doc: None, + location: 0..11, + name: "create", + public: false, + return_annotation: None, + return_type: (), + end_position: 54, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled_with_field_access.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled_with_field_access.snap new file mode 100644 index 000000000..4f2351d5a --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled_with_field_access.snap @@ -0,0 +1,79 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn create() {\n some_module.User { name: \"Aiken\", age, thing: 2 }\n }\n " +info: record_create_labeled_with_field_access +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Call { + arguments: [ + CallArg { + label: Some( + "name", + ), + location: 35..48, + value: ByteArray { + location: 41..48, + bytes: [ + 65, + 105, + 107, + 101, + 110, + ], + preferred_format: Utf8String, + }, + }, + CallArg { + label: Some( + "age", + ), + location: 50..53, + value: Var { + location: 50..53, + name: "age", + }, + }, + CallArg { + label: Some( + "thing", + ), + location: 55..63, + value: Int { + location: 62..63, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + fun: FieldAccess { + location: 16..32, + label: "User", + container: Var { + location: 16..27, + name: "some_module", + }, + }, + location: 16..65, + }, + doc: None, + location: 0..11, + name: "create", + public: false, + return_annotation: None, + return_type: (), + end_position: 66, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_update.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_update.snap new file mode 100644 index 000000000..70cf105f3 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_update.snap @@ -0,0 +1,109 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n fn update_name(user: User, name: ByteArray) -> User {\n User { ..user, name: \"Aiken\", age }\n }\n " +info: record_update +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [ + Arg { + arg_name: Named { + name: "user", + label: "user", + location: 15..19, + is_validator_param: false, + }, + location: 15..25, + annotation: Some( + Constructor { + location: 21..25, + module: None, + name: "User", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "name", + label: "name", + location: 27..31, + is_validator_param: false, + }, + location: 27..42, + annotation: Some( + Constructor { + location: 33..42, + module: None, + name: "ByteArray", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: RecordUpdate { + location: 56..91, + constructor: Var { + location: 56..60, + name: "User", + }, + spread: RecordUpdateSpread { + base: Var { + location: 65..69, + name: "user", + }, + location: 63..69, + }, + arguments: [ + UntypedRecordUpdateArg { + label: "name", + location: 71..84, + value: ByteArray { + location: 77..84, + bytes: [ + 65, + 105, + 107, + 101, + 110, + ], + preferred_format: Utf8String, + }, + }, + UntypedRecordUpdateArg { + label: "age", + location: 86..89, + value: Var { + location: 86..89, + name: "age", + }, + }, + ], + }, + doc: None, + location: 0..51, + name: "update_name", + public: false, + return_annotation: Some( + Constructor { + location: 47..51, + module: None, + name: "User", + arguments: [], + }, + ), + return_type: (), + end_position: 92, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__tuple_type_alias.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__tuple_type_alias.snap new file mode 100644 index 000000000..d0bb97804 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__tuple_type_alias.snap @@ -0,0 +1,40 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n type RoyaltyToken = (PolicyId, AssetName)\n " +info: tuple_type_alias +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + TypeAlias( + TypeAlias { + alias: "RoyaltyToken", + annotation: Tuple { + location: 20..41, + elems: [ + Constructor { + location: 21..29, + module: None, + name: "PolicyId", + arguments: [], + }, + Constructor { + location: 31..40, + module: None, + name: "AssetName", + arguments: [], + }, + ], + }, + doc: None, + location: 0..41, + parameters: [], + public: false, + tipo: (), + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_alias.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_alias.snap new file mode 100644 index 000000000..e36b18a53 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_alias.snap @@ -0,0 +1,36 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n type Thing = Option\n " +info: type_alias +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + TypeAlias( + TypeAlias { + alias: "Thing", + annotation: Constructor { + location: 13..24, + module: None, + name: "Option", + arguments: [ + Constructor { + location: 20..23, + module: None, + name: "Int", + arguments: [], + }, + ], + }, + doc: None, + location: 0..24, + parameters: [], + public: false, + tipo: (), + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_annotation_with_module_prefix.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_annotation_with_module_prefix.snap new file mode 100644 index 000000000..140a0ba32 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_annotation_with_module_prefix.snap @@ -0,0 +1,57 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n use aiken\n\n pub fn go() -> aiken.Option {\n False\n }\n " +info: type_annotation_with_module_prefix +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Use( + Use { + as_name: None, + location: 0..9, + module: [ + "aiken", + ], + package: (), + unqualified: [], + }, + ), + Fn( + Function { + arguments: [], + body: Var { + location: 48..53, + name: "False", + }, + doc: None, + location: 11..43, + name: "go", + public: true, + return_annotation: Some( + Constructor { + location: 26..43, + module: Some( + "aiken", + ), + name: "Option", + arguments: [ + Constructor { + location: 39..42, + module: None, + name: "Int", + arguments: [], + }, + ], + }, + ), + return_type: (), + end_position: 54, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__unqualified_imports.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__unqualified_imports.snap new file mode 100644 index 000000000..3f1559264 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__unqualified_imports.snap @@ -0,0 +1,42 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n use std/address.{Address as A, thing as w}\n " +info: unqualified_imports +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Use( + Use { + as_name: None, + location: 0..42, + module: [ + "std", + "address", + ], + package: (), + unqualified: [ + UnqualifiedImport { + location: 17..29, + name: "Address", + as_name: Some( + "A", + ), + layer: Value, + }, + UnqualifiedImport { + location: 31..41, + name: "thing", + as_name: Some( + "w", + ), + layer: Value, + }, + ], + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__validator.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__validator.snap new file mode 100644 index 000000000..c3bb017ae --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__validator.snap @@ -0,0 +1,71 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n validator {\n fn foo(datum, rdmr, ctx) {\n True\n }\n }\n " +info: validator +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Validator( + Validator { + doc: None, + end_position: 54, + fun: Function { + arguments: [ + Arg { + arg_name: Named { + name: "datum", + label: "datum", + location: 21..26, + is_validator_param: false, + }, + location: 21..26, + annotation: None, + tipo: (), + }, + Arg { + arg_name: Named { + name: "rdmr", + label: "rdmr", + location: 28..32, + is_validator_param: false, + }, + location: 28..32, + annotation: None, + tipo: (), + }, + Arg { + arg_name: Named { + name: "ctx", + label: "ctx", + location: 34..37, + is_validator_param: false, + }, + location: 34..37, + annotation: None, + tipo: (), + }, + ], + body: Var { + location: 45..49, + name: "True", + }, + doc: None, + location: 14..38, + name: "foo", + public: false, + return_annotation: None, + return_type: (), + end_position: 52, + can_error: true, + }, + other_fun: None, + location: 0..9, + params: [], + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__when.snap b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__when.snap new file mode 100644 index 000000000..48653cbe1 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__when.snap @@ -0,0 +1,163 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "\n pub fn wow2(a: Int){\n when a is {\n 2 -> 3\n 1 | 4 | 5 -> {\n let amazing = 5\n amazing\n }\n 3 -> 9\n _ -> 4\n }\n }\n " +info: when +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [ + Arg { + arg_name: Named { + name: "a", + label: "a", + location: 12..13, + is_validator_param: false, + }, + location: 12..18, + annotation: Some( + Constructor { + location: 15..18, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: When { + location: 23..132, + subject: Var { + location: 28..29, + name: "a", + }, + clauses: [ + UntypedClause { + location: 39..45, + patterns: [ + Int { + location: 39..40, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + guard: None, + then: Int { + location: 44..45, + value: "3", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + UntypedClause { + location: 50..106, + patterns: [ + Int { + location: 50..51, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 54..55, + value: "4", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 58..59, + value: "5", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + guard: None, + then: Sequence { + location: 71..100, + expressions: [ + Assignment { + location: 71..86, + value: Int { + location: 85..86, + value: "5", + base: Decimal { + numeric_underscore: false, + }, + }, + pattern: Var { + location: 75..82, + name: "amazing", + }, + kind: Let, + annotation: None, + }, + Var { + location: 93..100, + name: "amazing", + }, + ], + }, + }, + UntypedClause { + location: 111..117, + patterns: [ + Int { + location: 111..112, + value: "3", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + guard: None, + then: Int { + location: 116..117, + value: "9", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + UntypedClause { + location: 122..128, + patterns: [ + Discard { + name: "_", + location: 122..123, + }, + ], + guard: None, + then: Int { + location: 127..128, + value: "4", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + }, + doc: None, + location: 0..19, + name: "wow2", + public: true, + return_annotation: None, + return_type: (), + end_position: 133, + can_error: true, + }, + ), + ], + kind: Validator, +} From 8a7df7f66b120a3e0b27e0bf1cb7f6227628d27c Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 30 Jun 2023 16:12:51 -0400 Subject: [PATCH 12/31] test: add empty list test --- crates/aiken-lang/src/parser/expr/list.rs | 13 ++ .../src/parser/expr/snapshots/empty_list.snap | 9 + crates/aiken-lang/src/parser/utils.rs | 23 ++ crates/aiken-lang/src/tipo/pretty.rs | 2 +- crates/aiken-project/src/blueprint/mod.rs | 2 +- crates/aiken-project/src/blueprint/schema.rs | 2 +- .../aiken-project/src/blueprint/validator.rs | 2 +- crates/flat-rs/tests/flat_test.rs | 197 +++++++++--------- crates/flat-rs/tests/zigzag_test.rs | 31 ++- crates/uplc/src/flat.rs | 2 +- crates/uplc/src/machine/cost_model.rs | 2 +- crates/uplc/src/optimize/shrinker.rs | 2 +- crates/uplc/src/parser.rs | 2 +- 13 files changed, 164 insertions(+), 125 deletions(-) create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/empty_list.snap diff --git a/crates/aiken-lang/src/parser/expr/list.rs b/crates/aiken-lang/src/parser/expr/list.rs index 87f37bbe4..3349b9a15 100644 --- a/crates/aiken-lang/src/parser/expr/list.rs +++ b/crates/aiken-lang/src/parser/expr/list.rs @@ -27,3 +27,16 @@ pub fn parser( tail, }) } + +#[cfg(test)] +mod tests { + + use chumsky::Parser; + + use crate::assert_expr; + + #[test] + fn empty_list() { + assert_expr!("[]"); + } +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/empty_list.snap b/crates/aiken-lang/src/parser/expr/snapshots/empty_list.snap new file mode 100644 index 000000000..8874c6b64 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/empty_list.snap @@ -0,0 +1,9 @@ +--- +source: crates/aiken-lang/src/parser/expr/list.rs +description: "Code:\n\n[]" +--- +List { + location: 0..2, + elements: [], + tail: None, +} diff --git a/crates/aiken-lang/src/parser/utils.rs b/crates/aiken-lang/src/parser/utils.rs index 4c7b5c6bd..a76987ed2 100644 --- a/crates/aiken-lang/src/parser/utils.rs +++ b/crates/aiken-lang/src/parser/utils.rs @@ -93,3 +93,26 @@ pub fn type_name_with_args() -> impl Parser>) ), ) } + +#[cfg(test)] +#[macro_use] +mod macros { + #[macro_export] + macro_rules! assert_expr { + ($code:expr) => { + let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run($code).unwrap(); + + let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len()), tokens.into_iter()); + + let result = $crate::parser::expr::sequence().parse(stream).unwrap(); + + insta::with_settings!({ + description => concat!("Code:\n\n", $code), + prepend_module_to_snapshot => false, + omit_expression => true + }, { + insta::assert_debug_snapshot!(result); + }); + }; + } +} diff --git a/crates/aiken-lang/src/tipo/pretty.rs b/crates/aiken-lang/src/tipo/pretty.rs index 8dc9d29e1..c851d2199 100644 --- a/crates/aiken-lang/src/tipo/pretty.rs +++ b/crates/aiken-lang/src/tipo/pretty.rs @@ -168,7 +168,7 @@ fn qualify_type_name(module: &String, typ_name: &str) -> Document<'static> { } #[cfg(test)] -mod test { +mod tests { use std::cell::RefCell; use pretty_assertions::assert_eq; diff --git a/crates/aiken-project/src/blueprint/mod.rs b/crates/aiken-project/src/blueprint/mod.rs index 168953c8d..8998ae8dd 100644 --- a/crates/aiken-project/src/blueprint/mod.rs +++ b/crates/aiken-project/src/blueprint/mod.rs @@ -140,7 +140,7 @@ impl From<&Config> for Preamble { } #[cfg(test)] -mod test { +mod tests { use super::*; use aiken_lang::builtins; use schema::{Data, Declaration, Items, Schema}; diff --git a/crates/aiken-project/src/blueprint/schema.rs b/crates/aiken-project/src/blueprint/schema.rs index 4bbfedbbd..16a42e269 100644 --- a/crates/aiken-project/src/blueprint/schema.rs +++ b/crates/aiken-project/src/blueprint/schema.rs @@ -1033,7 +1033,7 @@ Here's the types I followed and that led me to this problem: } #[cfg(test)] -pub mod test { +pub mod tests { use super::*; use proptest::prelude::*; use serde_json::{self, json, Value}; diff --git a/crates/aiken-project/src/blueprint/validator.rs b/crates/aiken-project/src/blueprint/validator.rs index eb962ae71..656511104 100644 --- a/crates/aiken-project/src/blueprint/validator.rs +++ b/crates/aiken-project/src/blueprint/validator.rs @@ -177,7 +177,7 @@ impl Validator { } #[cfg(test)] -mod test { +mod tests { use assert_json_diff::assert_json_eq; use serde_json::{self, json}; diff --git a/crates/flat-rs/tests/flat_test.rs b/crates/flat-rs/tests/flat_test.rs index 3faa5b3bd..cee23e052 100644 --- a/crates/flat-rs/tests/flat_test.rs +++ b/crates/flat-rs/tests/flat_test.rs @@ -1,126 +1,123 @@ -#[cfg(test)] -mod test { - use flat_rs::filler::Filler; - use flat_rs::{decode, encode}; - use proptest::prelude::*; - - prop_compose! { - fn arb_big_vec()(size in 255..300, element in any::()) -> Vec { - (0..size).map(|_| element).collect() - } +use flat_rs::filler::Filler; +use flat_rs::{decode, encode}; +use proptest::prelude::*; + +prop_compose! { + fn arb_big_vec()(size in 255..300, element in any::()) -> Vec { + (0..size).map(|_| element).collect() } +} - #[test] - fn encode_bool() { - let bytes = encode(&true).unwrap(); +#[test] +fn encode_bool() { + let bytes = encode(&true).unwrap(); + + assert_eq!(bytes, vec![0b10000001]); + + let decoded: bool = decode(bytes.as_slice()).unwrap(); + + assert!(decoded); + + let bytes = encode(&false).unwrap(); - assert_eq!(bytes, vec![0b10000001]); + assert_eq!(bytes, vec![0b00000001]); - let decoded: bool = decode(bytes.as_slice()).unwrap(); + let decoded: bool = decode(bytes.as_slice()).unwrap(); - assert!(decoded); + assert!(!decoded); +} + +#[test] +fn encode_u8() { + let bytes = encode(&3_u8).unwrap(); - let bytes = encode(&false).unwrap(); + assert_eq!(bytes, vec![0b00000011, 0b00000001]); - assert_eq!(bytes, vec![0b00000001]); + let decoded: u8 = decode(bytes.as_slice()).unwrap(); + + assert_eq!(decoded, 3_u8); +} + +proptest! { + #[test] + fn encode_isize(x: isize) { + let bytes = encode(&x).unwrap(); + let decoded: isize = decode(&bytes).unwrap(); + assert_eq!(decoded, x); + } - let decoded: bool = decode(bytes.as_slice()).unwrap(); + #[test] + fn encode_usize(x: usize) { + let bytes = encode(&x).unwrap(); + let decoded: usize = decode(&bytes).unwrap(); + assert_eq!(decoded, x); + } - assert!(!decoded); + #[test] + fn encode_char(c: char) { + let bytes = encode(&c).unwrap(); + let decoded: char = decode(&bytes).unwrap(); + assert_eq!(decoded, c); } #[test] - fn encode_u8() { - let bytes = encode(&3_u8).unwrap(); + fn encode_string(str: String) { + let bytes = encode(&str).unwrap(); + let decoded: String = decode(&bytes).unwrap(); + assert_eq!(decoded, str); + } - assert_eq!(bytes, vec![0b00000011, 0b00000001]); + #[test] + fn encode_vec_u8(xs: Vec) { + let bytes = encode(&xs).unwrap(); + let decoded: Vec = decode(&bytes).unwrap(); + assert_eq!(decoded, xs); + } - let decoded: u8 = decode(bytes.as_slice()).unwrap(); + #[test] + fn encode_big_vec_u8(xs in arb_big_vec()) { + let bytes = encode(&xs).unwrap(); + let decoded: Vec = decode(&bytes).unwrap(); + assert_eq!(decoded, xs); + } - assert_eq!(decoded, 3_u8); + #[test] + fn encode_arr_u8(xs: Vec) { + let bytes = encode(&xs.as_slice()).unwrap(); + let decoded: Vec = decode(&bytes).unwrap(); + assert_eq!(decoded, xs); } - proptest! { - #[test] - fn encode_isize(x: isize) { - let bytes = encode(&x).unwrap(); - let decoded: isize = decode(&bytes).unwrap(); - assert_eq!(decoded, x); - } - - #[test] - fn encode_usize(x: usize) { - let bytes = encode(&x).unwrap(); - let decoded: usize = decode(&bytes).unwrap(); - assert_eq!(decoded, x); - } - - #[test] - fn encode_char(c: char) { - let bytes = encode(&c).unwrap(); - let decoded: char = decode(&bytes).unwrap(); - assert_eq!(decoded, c); - } - - #[test] - fn encode_string(str: String) { - let bytes = encode(&str).unwrap(); - let decoded: String = decode(&bytes).unwrap(); - assert_eq!(decoded, str); - } - - #[test] - fn encode_vec_u8(xs: Vec) { - let bytes = encode(&xs).unwrap(); - let decoded: Vec = decode(&bytes).unwrap(); - assert_eq!(decoded, xs); - } - - #[test] - fn encode_big_vec_u8(xs in arb_big_vec()) { - let bytes = encode(&xs).unwrap(); - let decoded: Vec = decode(&bytes).unwrap(); - assert_eq!(decoded, xs); - } - - #[test] - fn encode_arr_u8(xs: Vec) { - let bytes = encode(&xs.as_slice()).unwrap(); - let decoded: Vec = decode(&bytes).unwrap(); - assert_eq!(decoded, xs); - } - - #[test] - fn encode_big_arr_u8(xs in arb_big_vec()) { - let bytes = encode(&xs.as_slice()).unwrap(); - let decoded: Vec = decode(&bytes).unwrap(); - assert_eq!(decoded, xs); - } - - #[test] - fn encode_boxed(c: char) { - let boxed = Box::new(c); - let bytes = encode(&boxed).unwrap(); - let decoded: char = decode(&bytes).unwrap(); - assert_eq!(decoded, c); - } + #[test] + fn encode_big_arr_u8(xs in arb_big_vec()) { + let bytes = encode(&xs.as_slice()).unwrap(); + let decoded: Vec = decode(&bytes).unwrap(); + assert_eq!(decoded, xs); } #[test] - fn encode_filler() { - let bytes = encode(&Filler::FillerEnd).unwrap(); + fn encode_boxed(c: char) { + let boxed = Box::new(c); + let bytes = encode(&boxed).unwrap(); + let decoded: char = decode(&bytes).unwrap(); + assert_eq!(decoded, c); + } +} - assert_eq!(bytes, vec![0b0000001, 0b00000001]); +#[test] +fn encode_filler() { + let bytes = encode(&Filler::FillerEnd).unwrap(); - let bytes = encode(&Filler::FillerStart(Box::new(Filler::FillerEnd))).unwrap(); + assert_eq!(bytes, vec![0b0000001, 0b00000001]); - assert_eq!(bytes, vec![0b0000001, 0b00000001]); + let bytes = encode(&Filler::FillerStart(Box::new(Filler::FillerEnd))).unwrap(); - let bytes = encode(&Filler::FillerStart(Box::new(Filler::FillerStart( - Box::new(Filler::FillerEnd), - )))) - .unwrap(); + assert_eq!(bytes, vec![0b0000001, 0b00000001]); - assert_eq!(bytes, vec![0b0000001, 0b00000001]); - } + let bytes = encode(&Filler::FillerStart(Box::new(Filler::FillerStart( + Box::new(Filler::FillerEnd), + )))) + .unwrap(); + + assert_eq!(bytes, vec![0b0000001, 0b00000001]); } diff --git a/crates/flat-rs/tests/zigzag_test.rs b/crates/flat-rs/tests/zigzag_test.rs index f67acc770..5f6522dad 100644 --- a/crates/flat-rs/tests/zigzag_test.rs +++ b/crates/flat-rs/tests/zigzag_test.rs @@ -1,21 +1,18 @@ -#[cfg(test)] -mod test { - use flat_rs::zigzag::{to_isize, to_usize}; - use proptest::prelude::*; +use flat_rs::zigzag::{to_isize, to_usize}; +use proptest::prelude::*; - proptest! { - #[test] - fn zigzag(i: isize) { - let u = to_usize(i); - let converted_i = to_isize(u); - assert_eq!(converted_i, i); - } +proptest! { + #[test] + fn zigzag(i: isize) { + let u = to_usize(i); + let converted_i = to_isize(u); + assert_eq!(converted_i, i); + } - #[test] - fn zagzig(u: usize) { - let i = to_isize(u); - let converted_u = to_usize(i); - assert_eq!(converted_u, u); - } + #[test] + fn zagzig(u: usize) { + let i = to_isize(u); + let converted_u = to_usize(i); + assert_eq!(converted_u, u); } } diff --git a/crates/uplc/src/flat.rs b/crates/uplc/src/flat.rs index a84d19587..159ecdac9 100644 --- a/crates/uplc/src/flat.rs +++ b/crates/uplc/src/flat.rs @@ -808,7 +808,7 @@ pub fn decode_constant_tag(d: &mut Decoder) -> Result { } #[cfg(test)] -mod test { +mod tests { use super::{Constant, Program, Term}; use crate::{ ast::{DeBruijn, Name, Type}, diff --git a/crates/uplc/src/machine/cost_model.rs b/crates/uplc/src/machine/cost_model.rs index bfe648b2e..d50b89ecc 100644 --- a/crates/uplc/src/machine/cost_model.rs +++ b/crates/uplc/src/machine/cost_model.rs @@ -3213,7 +3213,7 @@ impl TryFrom for StepKind { } #[cfg(test)] -mod test { +mod tests { use super::*; use pretty_assertions::assert_eq; diff --git a/crates/uplc/src/optimize/shrinker.rs b/crates/uplc/src/optimize/shrinker.rs index bfb75a5a1..2b2a52ac0 100644 --- a/crates/uplc/src/optimize/shrinker.rs +++ b/crates/uplc/src/optimize/shrinker.rs @@ -503,7 +503,7 @@ fn replace_identity_usage(term: &Term, original: Rc) -> Term { } #[cfg(test)] -mod test { +mod tests { use pallas_primitives::babbage::{BigInt, PlutusData}; use pretty_assertions::assert_eq; diff --git a/crates/uplc/src/parser.rs b/crates/uplc/src/parser.rs index 370dd5f44..bfc9c4829 100644 --- a/crates/uplc/src/parser.rs +++ b/crates/uplc/src/parser.rs @@ -282,7 +282,7 @@ peg::parser! { } #[cfg(test)] -mod test { +mod tests { use num_bigint::BigInt; use crate::ast::{Constant, Name, Program, Term, Type, Unique}; From da0b969865dfbe328f85c2fd56eef3a0308fdf44 Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 30 Jun 2023 16:54:19 -0400 Subject: [PATCH 13/31] test: adjust snapshots --- crates/aiken-lang/src/tests/parser.rs | 594 +++++++++++------- ..._function.snap => anonymous_function.snap} | 3 +- ...ls.snap => base16_bytearray_literals.snap} | 3 +- ...__tests__parser__block.snap => block.snap} | 3 +- ...ng__tests__parser__call.snap => call.snap} | 3 +- ...> can_handle_comments_at_end_of_file.snap} | 3 +- ...beled.snap => cargo_create_unlabeled.snap} | 3 +- ...ser__custom_type.snap => custom_type.snap} | 3 +- ...e_validator.snap => double_validator.snap} | 3 +- ...mpty_function.snap => empty_function.snap} | 3 +- ...tests__parser__expect.snap => expect.snap} | 3 +- ...ng__tests__parser__fail.snap => fail.snap} | 3 +- ...r__field_access.snap => field_access.snap} | 3 +- ...lass_binop.snap => first_class_binop.snap} | 3 +- ....snap => function_ambiguous_sequence.snap} | 3 +- ...r__function_def.snap => function_def.snap} | 3 +- ...ction_invoke.snap => function_invoke.snap} | 3 +- ..._if_expression.snap => if_expression.snap} | 3 +- ...tests__parser__import.snap => import.snap} | 3 +- ...r__import_alias.snap => import_alias.snap} | 3 +- ..._bytes.snap => int_parsing_hex_bytes.snap} | 3 +- ...r__let_bindings.snap => let_bindings.snap} | 3 +- ...ser__opaque_type.snap => opaque_type.snap} | 3 +- ...ser__parse_tuple.snap => parse_tuple.snap} | 3 +- ...r__parse_tuple2.snap => parse_tuple2.snap} | 3 +- ...e.snap => parsing_numeric_underscore.snap} | 29 +- ...s__parser__pipeline.snap => pipeline.snap} | 3 +- ...als.snap => plain_bytearray_literals.snap} | 3 +- ...arser__plus_binop.snap => plus_binop.snap} | 3 +- ...ub_type_alias.snap => pub_type_alias.snap} | 3 +- ...abeled.snap => record_create_labeled.snap} | 3 +- ...ord_create_labeled_with_field_access.snap} | 3 +- ..._record_update.snap => record_update.snap} | 3 +- ..._type_alias.snap => tuple_type_alias.snap} | 3 +- ...arser__type_alias.snap => type_alias.snap} | 3 +- ...> type_annotation_with_module_prefix.snap} | 3 +- ..._imports.snap => unqualified_imports.snap} | 3 +- ..._parser__validator.snap => validator.snap} | 3 +- ...ng__tests__parser__when.snap => when.snap} | 3 +- ...dows_newline.snap => windows_newline.snap} | 3 +- 40 files changed, 406 insertions(+), 331 deletions(-) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__anonymous_function.snap => anonymous_function.snap} (96%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__base16_bytearray_literals.snap => base16_bytearray_literals.snap} (90%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__block.snap => block.snap} (95%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__call.snap => call.snap} (97%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__can_handle_comments_at_end_of_file.snap => can_handle_comments_at_end_of_file.snap} (76%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__cargo_create_unlabeled.snap => cargo_create_unlabeled.snap} (93%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__custom_type.snap => custom_type.snap} (95%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__double_validator.snap => double_validator.snap} (94%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__empty_function.snap => empty_function.snap} (92%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__expect.snap => expect.snap} (94%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__fail.snap => fail.snap} (92%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__field_access.snap => field_access.snap} (93%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__first_class_binop.snap => first_class_binop.snap} (99%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__function_ambiguous_sequence.snap => function_ambiguous_sequence.snap} (96%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__function_def.snap => function_def.snap} (93%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__function_invoke.snap => function_invoke.snap} (93%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__if_expression.snap => if_expression.snap} (94%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__import.snap => import.snap} (88%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__import_alias.snap => import_alias.snap} (88%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__int_parsing_hex_bytes.snap => int_parsing_hex_bytes.snap} (87%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__let_bindings.snap => let_bindings.snap} (95%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__opaque_type.snap => opaque_type.snap} (92%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__parse_tuple.snap => parse_tuple.snap} (96%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__parse_tuple2.snap => parse_tuple2.snap} (95%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__parsing_numeric_underscore.snap => parsing_numeric_underscore.snap} (74%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__pipeline.snap => pipeline.snap} (94%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__plain_bytearray_literals.snap => plain_bytearray_literals.snap} (89%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__plus_binop.snap => plus_binop.snap} (94%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__pub_type_alias.snap => pub_type_alias.snap} (91%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__record_create_labeled.snap => record_create_labeled.snap} (94%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__record_create_labeled_with_field_access.snap => record_create_labeled_with_field_access.snap} (93%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__record_update.snap => record_update.snap} (95%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__tuple_type_alias.snap => tuple_type_alias.snap} (91%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__type_alias.snap => type_alias.snap} (92%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__type_annotation_with_module_prefix.snap => type_annotation_with_module_prefix.snap} (90%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__unqualified_imports.snap => unqualified_imports.snap} (91%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__validator.snap => validator.snap} (94%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__when.snap => when.snap} (96%) rename crates/aiken-lang/src/tests/snapshots/{aiken_lang__tests__parser__windows_newline.snap => windows_newline.snap} (88%) diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index b06f027f7..fd6b0ee5c 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -1,65 +1,79 @@ use crate::{ast, parser}; macro_rules! assert_parse { - ($name:ident, $code:expr) => { - #[test] - fn $name() { - let (module, _) = - parser::module(indoc::indoc!{ $code }, ast::ModuleKind::Validator).expect("Failed to parse code"); - - insta::with_settings!({ - info => &stringify!($name), - description => $code, - omit_expression => true - }, { - insta::assert_debug_snapshot!(module); - }); - } + ($code:expr) => { + let (module, _) = + parser::module(indoc::indoc!{ $code }, ast::ModuleKind::Validator).expect("Failed to parse code"); + + insta::with_settings!({ + description => concat!("Code:\n\n", indoc::indoc! { $code }), + prepend_module_to_snapshot => false, + omit_expression => true + }, { + insta::assert_debug_snapshot!(module); + }); }; } -assert_parse!(windows_newline, "use aiken/list\r\n"); -assert_parse!( - can_handle_comments_at_end_of_file, - r#" - use aiken +#[test] +fn windows_newline() { + assert_parse!("use aiken/list\r\n"); +} - // some comment - // more comments"# -); -assert_parse!( - type_annotation_with_module_prefix, - r#" - use aiken +#[test] +fn can_handle_comments_at_end_of_file() { + assert_parse!( + r#" + use aiken - pub fn go() -> aiken.Option { - False - } - "# -); -assert_parse!( - test_fail, - r#" - !test invalid_inputs() { - expect True = False - - False - } - "# -); -assert_parse!( - validator, - r#" + // some comment + // more comments"# + ); +} + +#[test] +fn type_annotation_with_module_prefix() { + assert_parse!( + r#" + use aiken + + pub fn go() -> aiken.Option { + False + } + "# + ); +} + +#[test] +fn test_fail() { + assert_parse!( + r#" + !test invalid_inputs() { + expect True = False + + False + } + "# + ); +} + +#[test] +fn validator() { + assert_parse!( + r#" validator { fn foo(datum, rdmr, ctx) { True } } - "# -); -assert_parse!( - double_validator, - r#" + "# + ); +} + +#[test] +fn double_validator() { + assert_parse!( + r#" validator { fn foo(datum, rdmr, ctx) { True @@ -69,92 +83,128 @@ assert_parse!( True } } - "# -); -assert_parse!( - import, - r#" + "# + ); +} + +#[test] +fn import() { + assert_parse!( + r#" use std/list - "# -); -assert_parse!( - unqualified_imports, - r#" + "# + ); +} + +#[test] +fn unqualified_imports() { + assert_parse!( + r#" use std/address.{Address as A, thing as w} - "# -); -assert_parse!( - import_alias, - r#" + "# + ); +} + +#[test] +fn import_alias() { + assert_parse!( + r#" use std/tx as t - "# -); -assert_parse!( - custom_type, - r#" + "# + ); +} + +#[test] +fn custom_type() { + assert_parse!( + r#" type Option { Some(a, Int) None Wow { name: Int, age: Int } } - "# -); -assert_parse!( - opaque_type, - r#" + "# + ); +} + +#[test] +fn opaque_type() { + assert_parse!( + r#" pub opaque type User { name: _w } - "# -); -assert_parse!( - type_alias, - r#" + "# + ); +} + +#[test] +fn type_alias() { + assert_parse!( + r#" type Thing = Option - "# -); -assert_parse!( - pub_type_alias, - r#" + "# + ); +} + +#[test] +fn pub_type_alias() { + assert_parse!( + r#" pub type Me = Option - "# -); -assert_parse!( - empty_function, - r#" + "# + ); +} + +#[test] +fn empty_function() { + assert_parse!( + r#" pub fn run() {} - "# -); -assert_parse!( - expect, - r#" + "# + ); +} + +#[test] +fn expect() { + assert_parse!( + r#" pub fn run() { expect Some(x) = something.field x.other_field } - "# -); -assert_parse!( - plus_binop, - r#" + "# + ); +} + +#[test] +fn plus_binop() { + assert_parse!( + r#" pub fn add_one(a) -> Int { a + 1 } - "# -); -assert_parse!( - pipeline, - r#" + "# + ); +} + +#[test] +fn pipeline() { + assert_parse!( + r#" pub fn thing(thing a: Int) { a + 2 |> add_one |> add_one } - "# -); -assert_parse!( - if_expression, - r#" + "# + ); +} + +#[test] +fn if_expression() { + assert_parse!( + r#" fn ifs() { if True { 1 + 1 @@ -167,10 +217,13 @@ assert_parse!( } } "# -); -assert_parse!( - let_bindings, - r#" + ); +} + +#[test] +fn let_bindings() { + assert_parse!( + r#" pub fn wow(a: Int) { let x = a + 2 @@ -183,11 +236,14 @@ assert_parse!( y } - "# -); -assert_parse!( - block, - r#" + "# + ); +} + +#[test] +fn block() { + assert_parse!( + r#" pub fn wow2(a: Int){ let b = { let x = 4 @@ -197,11 +253,14 @@ assert_parse!( b } - "# -); -assert_parse!( - when, - r#" + "# + ); +} + +#[test] +fn when() { + assert_parse!( + r#" pub fn wow2(a: Int){ when a is { 2 -> 3 @@ -213,29 +272,38 @@ assert_parse!( _ -> 4 } } - "# -); -assert_parse!( - anonymous_function, - r#" + "# + ); +} + +#[test] +fn anonymous_function() { + assert_parse!( + r#" pub fn such() -> Int { let add_one = fn (a: Int) -> Int { a + 1 } 2 |> add_one } - "# -); -assert_parse!( - field_access, - r#" + "# + ); +} + +#[test] +fn field_access() { + assert_parse!( + r#" fn name(user: User) { user.name } - "# -); -assert_parse!( - call, - r#" + "# + ); +} + +#[test] +fn call() { + assert_parse!( + r#" fn calls() { let x = add_one(3) @@ -243,138 +311,183 @@ assert_parse!( map_add_x([ 1, 2, 3 ]) } - "# -); -assert_parse!( - record_update, - r#" + "# + ); +} + +#[test] +fn record_update() { + assert_parse!( + r#" fn update_name(user: User, name: ByteArray) -> User { User { ..user, name: "Aiken", age } } - "# -); -assert_parse!( - record_create_labeled, - r#" + "# + ); +} + +#[test] +fn record_create_labeled() { + assert_parse!( + r#" fn create() { User { name: "Aiken", age, thing: 2 } } - "# -); -assert_parse!( - record_create_labeled_with_field_access, - r#" + "# + ); +} + +#[test] +fn record_create_labeled_with_field_access() { + assert_parse!( + r#" fn create() { some_module.User { name: "Aiken", age, thing: 2 } } - "# -); -assert_parse!( - cargo_create_unlabeled, - r#" + "# + ); +} + +#[test] +fn cargo_create_unlabeled() { + assert_parse!( + r#" fn create() { some_module.Thing(1, a) } - "# -); -assert_parse!( - parse_tuple, - r#" + "# + ); +} + +#[test] +fn parse_tuple() { + assert_parse!( + r#" fn foo() { let tuple = (1, 2, 3, 4) tuple.1st + tuple.2nd + tuple.3rd + tuple.4th } - "# -); -assert_parse!( - parse_tuple2, - r#" + "# + ); +} + +#[test] +fn parse_tuple2() { + assert_parse!( + r#" fn foo() { let a = foo(14) (a, 42) } - "# -); -assert_parse!( - plain_bytearray_literals, - r#" + "# + ); +} + +#[test] +fn plain_bytearray_literals() { + assert_parse!( + r#" pub const my_policy_id = #[0, 170, 255] - "# -); -assert_parse!( - base16_bytearray_literals, - r#" + "# + ); +} + +#[test] +fn base16_bytearray_literals() { + assert_parse!( + r#" pub const my_policy_id = #"00aaff" pub fn foo() { my_policy_id == #"00aaff" } - "# -); -assert_parse!( - function_def, - r#" + "# + ); +} + +#[test] +fn function_def() { + assert_parse!( + r#" fn foo() {} - "# -); -assert_parse!( - function_invoke, - r#" + "# + ); +} + +#[test] +fn function_invoke() { + assert_parse!( + r#" fn foo() { let a = bar(42) } - "# -); -assert_parse!( - function_ambiguous_sequence, - r#" - fn foo_1() { - let a = bar - (40) - } - - fn foo_2() { - let a = bar - {40} - } - - fn foo_3() { - let a = (40+2) - } - - fn foo_4() { - let a = bar(42) - (a + 14) * 42 - } - "# -); -assert_parse!( - tuple_type_alias, - r#" - type RoyaltyToken = (PolicyId, AssetName) - "# -); -assert_parse!( - int_parsing_hex_bytes, - r#" - fn foo() { - #[ 0x01, 0xa2, 0x03 ] - } - "# -); -assert_parse!( - test_parsing_numeric_underscore, - r#" - fn foo() { - let i = 1_234_567 - let j = 1_000_000 - let k = -10_000 + "# + ); +} + +#[test] +fn function_ambiguous_sequence() { + assert_parse!( + r#" + fn foo_1() { + let a = bar + (40) } - "# -); -assert_parse!( - first_class_binop, - r#" + + fn foo_2() { + let a = bar + {40} + } + + fn foo_3() { + let a = (40+2) + } + + fn foo_4() { + let a = bar(42) + (a + 14) * 42 + } + "# + ); +} + +#[test] +fn tuple_type_alias() { + assert_parse!( + r#" + type RoyaltyToken = (PolicyId, AssetName) + "# + ); +} + +#[test] +fn int_parsing_hex_bytes() { + assert_parse!( + r#" + fn foo() { + #[ 0x01, 0xa2, 0x03 ] + } + "# + ); +} + +#[test] +fn test_parsing_numeric_underscore() { + assert_parse!( + r#" + fn foo() { + let i = 1_234_567 + let j = 1_000_000 + let k = -10_000 + } + "# + ); +} + +#[test] +fn first_class_binop() { + assert_parse!( + r#" fn foo() { compare_with(a, >, b) compare_with(a, >=, b) @@ -391,7 +504,8 @@ assert_parse!( compute_with(a, %, b) } "# -); + ); +} #[test] fn parse_unicode_offset_1() { diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__anonymous_function.snap b/crates/aiken-lang/src/tests/snapshots/anonymous_function.snap similarity index 96% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__anonymous_function.snap rename to crates/aiken-lang/src/tests/snapshots/anonymous_function.snap index 450af3be0..93b8c216d 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__anonymous_function.snap +++ b/crates/aiken-lang/src/tests/snapshots/anonymous_function.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n pub fn such() -> Int {\n let add_one = fn (a: Int) -> Int { a + 1 }\n\n 2 |> add_one\n }\n " -info: anonymous_function +description: "Code:\n\npub fn such() -> Int {\n let add_one = fn (a: Int) -> Int { a + 1 }\n\n 2 |> add_one\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__base16_bytearray_literals.snap b/crates/aiken-lang/src/tests/snapshots/base16_bytearray_literals.snap similarity index 90% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__base16_bytearray_literals.snap rename to crates/aiken-lang/src/tests/snapshots/base16_bytearray_literals.snap index fe0b51dd8..19784cc66 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__base16_bytearray_literals.snap +++ b/crates/aiken-lang/src/tests/snapshots/base16_bytearray_literals.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n pub const my_policy_id = #\"00aaff\"\n\n pub fn foo() {\n my_policy_id == #\"00aaff\"\n }\n " -info: base16_bytearray_literals +description: "Code:\n\npub const my_policy_id = #\"00aaff\"\n\npub fn foo() {\n my_policy_id == #\"00aaff\"\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__block.snap b/crates/aiken-lang/src/tests/snapshots/block.snap similarity index 95% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__block.snap rename to crates/aiken-lang/src/tests/snapshots/block.snap index 74efb4711..cbb844856 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__block.snap +++ b/crates/aiken-lang/src/tests/snapshots/block.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n pub fn wow2(a: Int){\n let b = {\n let x = 4\n\n x + 5\n }\n\n b\n }\n " -info: block +description: "Code:\n\npub fn wow2(a: Int){\n let b = {\n let x = 4\n\n x + 5\n }\n\n b\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__call.snap b/crates/aiken-lang/src/tests/snapshots/call.snap similarity index 97% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__call.snap rename to crates/aiken-lang/src/tests/snapshots/call.snap index 35518d7e5..78f7d70fa 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__call.snap +++ b/crates/aiken-lang/src/tests/snapshots/call.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn calls() {\n let x = add_one(3)\n\n let map_add_x = list.map(_, fn (y) { x + y })\n\n map_add_x([ 1, 2, 3 ])\n }\n " -info: call +description: "Code:\n\nfn calls() {\n let x = add_one(3)\n\n let map_add_x = list.map(_, fn (y) { x + y })\n\n map_add_x([ 1, 2, 3 ])\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__can_handle_comments_at_end_of_file.snap b/crates/aiken-lang/src/tests/snapshots/can_handle_comments_at_end_of_file.snap similarity index 76% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__can_handle_comments_at_end_of_file.snap rename to crates/aiken-lang/src/tests/snapshots/can_handle_comments_at_end_of_file.snap index 0de6c356a..c25d4eff7 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__can_handle_comments_at_end_of_file.snap +++ b/crates/aiken-lang/src/tests/snapshots/can_handle_comments_at_end_of_file.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n use aiken\n\n // some comment\n // more comments" -info: can_handle_comments_at_end_of_file +description: "Code:\n\nuse aiken\n\n// some comment\n// more comments" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__cargo_create_unlabeled.snap b/crates/aiken-lang/src/tests/snapshots/cargo_create_unlabeled.snap similarity index 93% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__cargo_create_unlabeled.snap rename to crates/aiken-lang/src/tests/snapshots/cargo_create_unlabeled.snap index 9f0d8f384..4aa1c2ddb 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__cargo_create_unlabeled.snap +++ b/crates/aiken-lang/src/tests/snapshots/cargo_create_unlabeled.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn create() {\n some_module.Thing(1, a)\n }\n " -info: cargo_create_unlabeled +description: "Code:\n\nfn create() {\n some_module.Thing(1, a)\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__custom_type.snap b/crates/aiken-lang/src/tests/snapshots/custom_type.snap similarity index 95% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__custom_type.snap rename to crates/aiken-lang/src/tests/snapshots/custom_type.snap index b8954b8e2..1954253cd 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__custom_type.snap +++ b/crates/aiken-lang/src/tests/snapshots/custom_type.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n type Option {\n Some(a, Int)\n None\n Wow { name: Int, age: Int }\n }\n " -info: custom_type +description: "Code:\n\ntype Option {\n Some(a, Int)\n None\n Wow { name: Int, age: Int }\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__double_validator.snap b/crates/aiken-lang/src/tests/snapshots/double_validator.snap similarity index 94% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__double_validator.snap rename to crates/aiken-lang/src/tests/snapshots/double_validator.snap index 680156014..3886e7e44 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__double_validator.snap +++ b/crates/aiken-lang/src/tests/snapshots/double_validator.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n validator {\n fn foo(datum, rdmr, ctx) {\n True\n }\n\n fn bar(rdmr, ctx) {\n True\n }\n }\n " -info: double_validator +description: "Code:\n\nvalidator {\n fn foo(datum, rdmr, ctx) {\n True\n }\n\n fn bar(rdmr, ctx) {\n True\n }\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__empty_function.snap b/crates/aiken-lang/src/tests/snapshots/empty_function.snap similarity index 92% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__empty_function.snap rename to crates/aiken-lang/src/tests/snapshots/empty_function.snap index cc09bb21c..b2c2873ad 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__empty_function.snap +++ b/crates/aiken-lang/src/tests/snapshots/empty_function.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n pub fn run() {}\n " -info: empty_function +description: "Code:\n\npub fn run() {}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__expect.snap b/crates/aiken-lang/src/tests/snapshots/expect.snap similarity index 94% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__expect.snap rename to crates/aiken-lang/src/tests/snapshots/expect.snap index ffd54e163..51329710c 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__expect.snap +++ b/crates/aiken-lang/src/tests/snapshots/expect.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n pub fn run() {\n expect Some(x) = something.field\n x.other_field\n }\n " -info: expect +description: "Code:\n\npub fn run() {\n expect Some(x) = something.field\n x.other_field\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__fail.snap b/crates/aiken-lang/src/tests/snapshots/fail.snap similarity index 92% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__fail.snap rename to crates/aiken-lang/src/tests/snapshots/fail.snap index a4b0dfc82..d983cfc72 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__fail.snap +++ b/crates/aiken-lang/src/tests/snapshots/fail.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n !test invalid_inputs() {\n expect True = False\n\n False\n }\n " -info: test_fail +description: "Code:\n\n!test invalid_inputs() {\n expect True = False\n\n False\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__field_access.snap b/crates/aiken-lang/src/tests/snapshots/field_access.snap similarity index 93% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__field_access.snap rename to crates/aiken-lang/src/tests/snapshots/field_access.snap index 6b932afa3..c5caa08aa 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__field_access.snap +++ b/crates/aiken-lang/src/tests/snapshots/field_access.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn name(user: User) {\n user.name\n }\n " -info: field_access +description: "Code:\n\nfn name(user: User) {\n user.name\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__first_class_binop.snap b/crates/aiken-lang/src/tests/snapshots/first_class_binop.snap similarity index 99% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__first_class_binop.snap rename to crates/aiken-lang/src/tests/snapshots/first_class_binop.snap index 56043b21c..679c61cc3 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__first_class_binop.snap +++ b/crates/aiken-lang/src/tests/snapshots/first_class_binop.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn foo() {\n compare_with(a, >, b)\n compare_with(a, >=, b)\n compare_with(a, <, b)\n compare_with(a, <=, b)\n compare_with(a, ==, b)\n compare_with(a, !=, b)\n combine_with(a, &&, b)\n combine_with(a, ||, b)\n compute_with(a, +, b)\n compute_with(a, -, b)\n compute_with(a, /, b)\n compute_with(a, *, b)\n compute_with(a, %, b)\n }\n " -info: first_class_binop +description: "Code:\n\nfn foo() {\n compare_with(a, >, b)\n compare_with(a, >=, b)\n compare_with(a, <, b)\n compare_with(a, <=, b)\n compare_with(a, ==, b)\n compare_with(a, !=, b)\n combine_with(a, &&, b)\n combine_with(a, ||, b)\n compute_with(a, +, b)\n compute_with(a, -, b)\n compute_with(a, /, b)\n compute_with(a, *, b)\n compute_with(a, %, b)\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_ambiguous_sequence.snap b/crates/aiken-lang/src/tests/snapshots/function_ambiguous_sequence.snap similarity index 96% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_ambiguous_sequence.snap rename to crates/aiken-lang/src/tests/snapshots/function_ambiguous_sequence.snap index 5eab570c3..487295126 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_ambiguous_sequence.snap +++ b/crates/aiken-lang/src/tests/snapshots/function_ambiguous_sequence.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn foo_1() {\n let a = bar\n (40)\n }\n\n fn foo_2() {\n let a = bar\n {40}\n }\n\n fn foo_3() {\n let a = (40+2)\n }\n\n fn foo_4() {\n let a = bar(42)\n (a + 14) * 42\n }\n " -info: function_ambiguous_sequence +description: "Code:\n\nfn foo_1() {\n let a = bar\n (40)\n}\n\nfn foo_2() {\n let a = bar\n {40}\n}\n\nfn foo_3() {\n let a = (40+2)\n}\n\nfn foo_4() {\n let a = bar(42)\n (a + 14) * 42\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_def.snap b/crates/aiken-lang/src/tests/snapshots/function_def.snap similarity index 93% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_def.snap rename to crates/aiken-lang/src/tests/snapshots/function_def.snap index 0ee47d90a..c9909b7e0 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_def.snap +++ b/crates/aiken-lang/src/tests/snapshots/function_def.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn foo() {}\n " -info: function_def +description: "Code:\n\nfn foo() {}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_invoke.snap b/crates/aiken-lang/src/tests/snapshots/function_invoke.snap similarity index 93% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_invoke.snap rename to crates/aiken-lang/src/tests/snapshots/function_invoke.snap index fad161a3c..2f115edc7 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__function_invoke.snap +++ b/crates/aiken-lang/src/tests/snapshots/function_invoke.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn foo() {\n let a = bar(42)\n }\n " -info: function_invoke +description: "Code:\n\nfn foo() {\n let a = bar(42)\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__if_expression.snap b/crates/aiken-lang/src/tests/snapshots/if_expression.snap similarity index 94% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__if_expression.snap rename to crates/aiken-lang/src/tests/snapshots/if_expression.snap index e031484da..e2b3b319f 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__if_expression.snap +++ b/crates/aiken-lang/src/tests/snapshots/if_expression.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn ifs() {\n if True {\n 1 + 1\n } else if a < 4 {\n 5\n } else if a || b {\n 6\n } else {\n 3\n }\n }\n " -info: if_expression +description: "Code:\n\nfn ifs() {\n if True {\n 1 + 1\n } else if a < 4 {\n 5\n } else if a || b {\n 6\n } else {\n 3\n }\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import.snap b/crates/aiken-lang/src/tests/snapshots/import.snap similarity index 88% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import.snap rename to crates/aiken-lang/src/tests/snapshots/import.snap index bb9e4663c..2137b6354 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import.snap +++ b/crates/aiken-lang/src/tests/snapshots/import.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n use std/list\n " -info: import +description: "Code:\n\nuse std/list\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import_alias.snap b/crates/aiken-lang/src/tests/snapshots/import_alias.snap similarity index 88% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import_alias.snap rename to crates/aiken-lang/src/tests/snapshots/import_alias.snap index e61aa49ee..71ba94d42 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__import_alias.snap +++ b/crates/aiken-lang/src/tests/snapshots/import_alias.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n use std/tx as t\n " -info: import_alias +description: "Code:\n\nuse std/tx as t\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__int_parsing_hex_bytes.snap b/crates/aiken-lang/src/tests/snapshots/int_parsing_hex_bytes.snap similarity index 87% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__int_parsing_hex_bytes.snap rename to crates/aiken-lang/src/tests/snapshots/int_parsing_hex_bytes.snap index 6d8eed84e..e5969d3f5 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__int_parsing_hex_bytes.snap +++ b/crates/aiken-lang/src/tests/snapshots/int_parsing_hex_bytes.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn foo() {\n #[ 0x01, 0xa2, 0x03 ]\n }\n " -info: int_parsing_hex_bytes +description: "Code:\n\nfn foo() {\n #[ 0x01, 0xa2, 0x03 ]\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__let_bindings.snap b/crates/aiken-lang/src/tests/snapshots/let_bindings.snap similarity index 95% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__let_bindings.snap rename to crates/aiken-lang/src/tests/snapshots/let_bindings.snap index aa4e73c79..f3ee30da6 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__let_bindings.snap +++ b/crates/aiken-lang/src/tests/snapshots/let_bindings.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n pub fn wow(a: Int) {\n let x =\n a + 2\n |> add_one\n |> add_one\n\n let thing = [ 1, 2, a ]\n\n let idk = thing\n\n y\n }\n " -info: let_bindings +description: "Code:\n\npub fn wow(a: Int) {\n let x =\n a + 2\n |> add_one\n |> add_one\n\n let thing = [ 1, 2, a ]\n\n let idk = thing\n\n y\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__opaque_type.snap b/crates/aiken-lang/src/tests/snapshots/opaque_type.snap similarity index 92% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__opaque_type.snap rename to crates/aiken-lang/src/tests/snapshots/opaque_type.snap index 5d2546d74..1f0426e1f 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__opaque_type.snap +++ b/crates/aiken-lang/src/tests/snapshots/opaque_type.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n pub opaque type User {\n name: _w\n }\n " -info: opaque_type +description: "Code:\n\npub opaque type User {\n name: _w\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple.snap b/crates/aiken-lang/src/tests/snapshots/parse_tuple.snap similarity index 96% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple.snap rename to crates/aiken-lang/src/tests/snapshots/parse_tuple.snap index 5c6d780eb..12489418a 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple.snap +++ b/crates/aiken-lang/src/tests/snapshots/parse_tuple.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn foo() {\n let tuple = (1, 2, 3, 4)\n tuple.1st + tuple.2nd + tuple.3rd + tuple.4th\n }\n " -info: parse_tuple +description: "Code:\n\nfn foo() {\n let tuple = (1, 2, 3, 4)\n tuple.1st + tuple.2nd + tuple.3rd + tuple.4th\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple2.snap b/crates/aiken-lang/src/tests/snapshots/parse_tuple2.snap similarity index 95% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple2.snap rename to crates/aiken-lang/src/tests/snapshots/parse_tuple2.snap index 5cd5d8f03..0a4185b72 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parse_tuple2.snap +++ b/crates/aiken-lang/src/tests/snapshots/parse_tuple2.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn foo() {\n let a = foo(14)\n (a, 42)\n }\n " -info: parse_tuple2 +description: "Code:\n\nfn foo() {\n let a = foo(14)\n (a, 42)\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parsing_numeric_underscore.snap b/crates/aiken-lang/src/tests/snapshots/parsing_numeric_underscore.snap similarity index 74% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parsing_numeric_underscore.snap rename to crates/aiken-lang/src/tests/snapshots/parsing_numeric_underscore.snap index 0b2989841..a2c4b26eb 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__parsing_numeric_underscore.snap +++ b/crates/aiken-lang/src/tests/snapshots/parsing_numeric_underscore.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn foo() {\n let i = 1_234_567\n let j = 1_000_000\n let k = -10_000\n }\n " -info: test_parsing_numeric_underscore +description: "Code:\n\nfn foo() {\n let i = 1_234_567\n let j = 1_000_000\n let k = -10_000\n}\n" --- Module { name: "", @@ -12,47 +11,47 @@ Module { Function { arguments: [], body: Sequence { - location: 17..76, + location: 13..68, expressions: [ Assignment { - location: 17..34, + location: 13..30, value: Int { - location: 25..34, + location: 21..30, value: "1234567", base: Decimal { numeric_underscore: true, }, }, pattern: Var { - location: 21..22, + location: 17..18, name: "i", }, kind: Let, annotation: None, }, Assignment { - location: 39..56, + location: 33..50, value: Int { - location: 47..56, + location: 41..50, value: "1000000", base: Decimal { numeric_underscore: true, }, }, pattern: Var { - location: 43..44, + location: 37..38, name: "j", }, kind: Let, annotation: None, }, Assignment { - location: 61..76, + location: 53..68, value: UnOp { op: Negate, - location: 69..76, + location: 61..68, value: Int { - location: 70..76, + location: 62..68, value: "10000", base: Decimal { numeric_underscore: true, @@ -60,7 +59,7 @@ Module { }, }, pattern: Var { - location: 65..66, + location: 57..58, name: "k", }, kind: Let, @@ -69,12 +68,12 @@ Module { ], }, doc: None, - location: 2..10, + location: 0..8, name: "foo", public: false, return_annotation: None, return_type: (), - end_position: 77, + end_position: 69, can_error: true, }, ), diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pipeline.snap b/crates/aiken-lang/src/tests/snapshots/pipeline.snap similarity index 94% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pipeline.snap rename to crates/aiken-lang/src/tests/snapshots/pipeline.snap index cdeb27a8b..0112b871e 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pipeline.snap +++ b/crates/aiken-lang/src/tests/snapshots/pipeline.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n pub fn thing(thing a: Int) {\n a + 2\n |> add_one\n |> add_one\n }\n " -info: pipeline +description: "Code:\n\npub fn thing(thing a: Int) {\n a + 2\n |> add_one\n |> add_one\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plain_bytearray_literals.snap b/crates/aiken-lang/src/tests/snapshots/plain_bytearray_literals.snap similarity index 89% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plain_bytearray_literals.snap rename to crates/aiken-lang/src/tests/snapshots/plain_bytearray_literals.snap index 5178dc9ab..c97df391d 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plain_bytearray_literals.snap +++ b/crates/aiken-lang/src/tests/snapshots/plain_bytearray_literals.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n pub const my_policy_id = #[0, 170, 255]\n " -info: plain_bytearray_literals +description: "Code:\n\npub const my_policy_id = #[0, 170, 255]\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plus_binop.snap b/crates/aiken-lang/src/tests/snapshots/plus_binop.snap similarity index 94% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plus_binop.snap rename to crates/aiken-lang/src/tests/snapshots/plus_binop.snap index 9ff39e325..9f52dc68e 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__plus_binop.snap +++ b/crates/aiken-lang/src/tests/snapshots/plus_binop.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n pub fn add_one(a) -> Int {\n a + 1\n }\n " -info: plus_binop +description: "Code:\n\npub fn add_one(a) -> Int {\n a + 1\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pub_type_alias.snap b/crates/aiken-lang/src/tests/snapshots/pub_type_alias.snap similarity index 91% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pub_type_alias.snap rename to crates/aiken-lang/src/tests/snapshots/pub_type_alias.snap index 0037e397f..b727d5a1a 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__pub_type_alias.snap +++ b/crates/aiken-lang/src/tests/snapshots/pub_type_alias.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n pub type Me = Option\n " -info: pub_type_alias +description: "Code:\n\npub type Me = Option\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled.snap b/crates/aiken-lang/src/tests/snapshots/record_create_labeled.snap similarity index 94% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled.snap rename to crates/aiken-lang/src/tests/snapshots/record_create_labeled.snap index ade46c033..1661a9c09 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled.snap +++ b/crates/aiken-lang/src/tests/snapshots/record_create_labeled.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn create() {\n User { name: \"Aiken\", age, thing: 2 }\n }\n " -info: record_create_labeled +description: "Code:\n\nfn create() {\n User { name: \"Aiken\", age, thing: 2 }\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled_with_field_access.snap b/crates/aiken-lang/src/tests/snapshots/record_create_labeled_with_field_access.snap similarity index 93% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled_with_field_access.snap rename to crates/aiken-lang/src/tests/snapshots/record_create_labeled_with_field_access.snap index 4f2351d5a..36383a8b5 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_create_labeled_with_field_access.snap +++ b/crates/aiken-lang/src/tests/snapshots/record_create_labeled_with_field_access.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn create() {\n some_module.User { name: \"Aiken\", age, thing: 2 }\n }\n " -info: record_create_labeled_with_field_access +description: "Code:\n\nfn create() {\n some_module.User { name: \"Aiken\", age, thing: 2 }\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_update.snap b/crates/aiken-lang/src/tests/snapshots/record_update.snap similarity index 95% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_update.snap rename to crates/aiken-lang/src/tests/snapshots/record_update.snap index 70cf105f3..ce8ccca7f 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__record_update.snap +++ b/crates/aiken-lang/src/tests/snapshots/record_update.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n fn update_name(user: User, name: ByteArray) -> User {\n User { ..user, name: \"Aiken\", age }\n }\n " -info: record_update +description: "Code:\n\nfn update_name(user: User, name: ByteArray) -> User {\n User { ..user, name: \"Aiken\", age }\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__tuple_type_alias.snap b/crates/aiken-lang/src/tests/snapshots/tuple_type_alias.snap similarity index 91% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__tuple_type_alias.snap rename to crates/aiken-lang/src/tests/snapshots/tuple_type_alias.snap index d0bb97804..46cf6c7cc 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__tuple_type_alias.snap +++ b/crates/aiken-lang/src/tests/snapshots/tuple_type_alias.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n type RoyaltyToken = (PolicyId, AssetName)\n " -info: tuple_type_alias +description: "Code:\n\ntype RoyaltyToken = (PolicyId, AssetName)\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_alias.snap b/crates/aiken-lang/src/tests/snapshots/type_alias.snap similarity index 92% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_alias.snap rename to crates/aiken-lang/src/tests/snapshots/type_alias.snap index e36b18a53..e86befd93 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_alias.snap +++ b/crates/aiken-lang/src/tests/snapshots/type_alias.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n type Thing = Option\n " -info: type_alias +description: "Code:\n\ntype Thing = Option\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_annotation_with_module_prefix.snap b/crates/aiken-lang/src/tests/snapshots/type_annotation_with_module_prefix.snap similarity index 90% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_annotation_with_module_prefix.snap rename to crates/aiken-lang/src/tests/snapshots/type_annotation_with_module_prefix.snap index 140a0ba32..bd0a32639 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__type_annotation_with_module_prefix.snap +++ b/crates/aiken-lang/src/tests/snapshots/type_annotation_with_module_prefix.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n use aiken\n\n pub fn go() -> aiken.Option {\n False\n }\n " -info: type_annotation_with_module_prefix +description: "Code:\n\nuse aiken\n\npub fn go() -> aiken.Option {\n False\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__unqualified_imports.snap b/crates/aiken-lang/src/tests/snapshots/unqualified_imports.snap similarity index 91% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__unqualified_imports.snap rename to crates/aiken-lang/src/tests/snapshots/unqualified_imports.snap index 3f1559264..b8d080d8c 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__unqualified_imports.snap +++ b/crates/aiken-lang/src/tests/snapshots/unqualified_imports.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n use std/address.{Address as A, thing as w}\n " -info: unqualified_imports +description: "Code:\n\nuse std/address.{Address as A, thing as w}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__validator.snap b/crates/aiken-lang/src/tests/snapshots/validator.snap similarity index 94% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__validator.snap rename to crates/aiken-lang/src/tests/snapshots/validator.snap index c3bb017ae..96070233e 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__validator.snap +++ b/crates/aiken-lang/src/tests/snapshots/validator.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n validator {\n fn foo(datum, rdmr, ctx) {\n True\n }\n }\n " -info: validator +description: "Code:\n\nvalidator {\n fn foo(datum, rdmr, ctx) {\n True\n }\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__when.snap b/crates/aiken-lang/src/tests/snapshots/when.snap similarity index 96% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__when.snap rename to crates/aiken-lang/src/tests/snapshots/when.snap index 48653cbe1..387ffad84 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__when.snap +++ b/crates/aiken-lang/src/tests/snapshots/when.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "\n pub fn wow2(a: Int){\n when a is {\n 2 -> 3\n 1 | 4 | 5 -> {\n let amazing = 5\n amazing\n }\n 3 -> 9\n _ -> 4\n }\n }\n " -info: when +description: "Code:\n\npub fn wow2(a: Int){\n when a is {\n 2 -> 3\n 1 | 4 | 5 -> {\n let amazing = 5\n amazing\n }\n 3 -> 9\n _ -> 4\n }\n}\n" --- Module { name: "", diff --git a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__windows_newline.snap b/crates/aiken-lang/src/tests/snapshots/windows_newline.snap similarity index 88% rename from crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__windows_newline.snap rename to crates/aiken-lang/src/tests/snapshots/windows_newline.snap index f8b6b3212..0e6ab80d0 100644 --- a/crates/aiken-lang/src/tests/snapshots/aiken_lang__tests__parser__windows_newline.snap +++ b/crates/aiken-lang/src/tests/snapshots/windows_newline.snap @@ -1,7 +1,6 @@ --- source: crates/aiken-lang/src/tests/parser.rs -description: "use aiken/list\r\n" -info: windows_newline +description: "Code:\n\nuse aiken/list\r\n" --- Module { name: "", From 715752718d71ed9bd92f766716575da2b808fa08 Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 30 Jun 2023 17:21:03 -0400 Subject: [PATCH 14/31] test: assert_module --- crates/aiken-lang/src/parser/utils.rs | 54 +++++++++------ crates/aiken-lang/src/tests/parser.rs | 95 +++++++++++---------------- 2 files changed, 73 insertions(+), 76 deletions(-) diff --git a/crates/aiken-lang/src/parser/utils.rs b/crates/aiken-lang/src/parser/utils.rs index a76987ed2..e75838831 100644 --- a/crates/aiken-lang/src/parser/utils.rs +++ b/crates/aiken-lang/src/parser/utils.rs @@ -94,25 +94,37 @@ pub fn type_name_with_args() -> impl Parser>) ) } -#[cfg(test)] -#[macro_use] -mod macros { - #[macro_export] - macro_rules! assert_expr { - ($code:expr) => { - let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run($code).unwrap(); - - let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len()), tokens.into_iter()); - - let result = $crate::parser::expr::sequence().parse(stream).unwrap(); - - insta::with_settings!({ - description => concat!("Code:\n\n", $code), - prepend_module_to_snapshot => false, - omit_expression => true - }, { - insta::assert_debug_snapshot!(result); - }); - }; - } +#[macro_export] +macro_rules! assert_expr { + ($code:expr) => { + let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run($code).unwrap(); + + let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len()), tokens.into_iter()); + + let result = $crate::parser::expr::sequence().parse(stream).unwrap(); + + insta::with_settings!({ + description => concat!("Code:\n\n", $code), + prepend_module_to_snapshot => false, + omit_expression => true + }, { + insta::assert_debug_snapshot!(result); + }); + }; +} + +#[macro_export] +macro_rules! assert_module { + ($code:expr) => { + let (module, _) = + parser::module(indoc::indoc!{ $code }, ast::ModuleKind::Validator).expect("Failed to parse code"); + + insta::with_settings!({ + description => concat!("Code:\n\n", indoc::indoc! { $code }), + prepend_module_to_snapshot => false, + omit_expression => true + }, { + insta::assert_debug_snapshot!(module); + }); + }; } diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index fd6b0ee5c..3f5e50963 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -1,28 +1,13 @@ -use crate::{ast, parser}; - -macro_rules! assert_parse { - ($code:expr) => { - let (module, _) = - parser::module(indoc::indoc!{ $code }, ast::ModuleKind::Validator).expect("Failed to parse code"); - - insta::with_settings!({ - description => concat!("Code:\n\n", indoc::indoc! { $code }), - prepend_module_to_snapshot => false, - omit_expression => true - }, { - insta::assert_debug_snapshot!(module); - }); - }; -} +use crate::{assert_module, ast, parser}; #[test] fn windows_newline() { - assert_parse!("use aiken/list\r\n"); + assert_module!("use aiken/list\r\n"); } #[test] fn can_handle_comments_at_end_of_file() { - assert_parse!( + assert_module!( r#" use aiken @@ -33,7 +18,7 @@ fn can_handle_comments_at_end_of_file() { #[test] fn type_annotation_with_module_prefix() { - assert_parse!( + assert_module!( r#" use aiken @@ -46,7 +31,7 @@ fn type_annotation_with_module_prefix() { #[test] fn test_fail() { - assert_parse!( + assert_module!( r#" !test invalid_inputs() { expect True = False @@ -59,7 +44,7 @@ fn test_fail() { #[test] fn validator() { - assert_parse!( + assert_module!( r#" validator { fn foo(datum, rdmr, ctx) { @@ -72,7 +57,7 @@ fn validator() { #[test] fn double_validator() { - assert_parse!( + assert_module!( r#" validator { fn foo(datum, rdmr, ctx) { @@ -89,7 +74,7 @@ fn double_validator() { #[test] fn import() { - assert_parse!( + assert_module!( r#" use std/list "# @@ -98,7 +83,7 @@ fn import() { #[test] fn unqualified_imports() { - assert_parse!( + assert_module!( r#" use std/address.{Address as A, thing as w} "# @@ -107,7 +92,7 @@ fn unqualified_imports() { #[test] fn import_alias() { - assert_parse!( + assert_module!( r#" use std/tx as t "# @@ -116,7 +101,7 @@ fn import_alias() { #[test] fn custom_type() { - assert_parse!( + assert_module!( r#" type Option { Some(a, Int) @@ -129,7 +114,7 @@ fn custom_type() { #[test] fn opaque_type() { - assert_parse!( + assert_module!( r#" pub opaque type User { name: _w @@ -140,7 +125,7 @@ fn opaque_type() { #[test] fn type_alias() { - assert_parse!( + assert_module!( r#" type Thing = Option "# @@ -149,7 +134,7 @@ fn type_alias() { #[test] fn pub_type_alias() { - assert_parse!( + assert_module!( r#" pub type Me = Option "# @@ -158,7 +143,7 @@ fn pub_type_alias() { #[test] fn empty_function() { - assert_parse!( + assert_module!( r#" pub fn run() {} "# @@ -167,7 +152,7 @@ fn empty_function() { #[test] fn expect() { - assert_parse!( + assert_module!( r#" pub fn run() { expect Some(x) = something.field @@ -179,7 +164,7 @@ fn expect() { #[test] fn plus_binop() { - assert_parse!( + assert_module!( r#" pub fn add_one(a) -> Int { a + 1 @@ -190,7 +175,7 @@ fn plus_binop() { #[test] fn pipeline() { - assert_parse!( + assert_module!( r#" pub fn thing(thing a: Int) { a + 2 @@ -203,7 +188,7 @@ fn pipeline() { #[test] fn if_expression() { - assert_parse!( + assert_module!( r#" fn ifs() { if True { @@ -222,7 +207,7 @@ fn if_expression() { #[test] fn let_bindings() { - assert_parse!( + assert_module!( r#" pub fn wow(a: Int) { let x = @@ -242,7 +227,7 @@ fn let_bindings() { #[test] fn block() { - assert_parse!( + assert_module!( r#" pub fn wow2(a: Int){ let b = { @@ -259,7 +244,7 @@ fn block() { #[test] fn when() { - assert_parse!( + assert_module!( r#" pub fn wow2(a: Int){ when a is { @@ -278,7 +263,7 @@ fn when() { #[test] fn anonymous_function() { - assert_parse!( + assert_module!( r#" pub fn such() -> Int { let add_one = fn (a: Int) -> Int { a + 1 } @@ -291,7 +276,7 @@ fn anonymous_function() { #[test] fn field_access() { - assert_parse!( + assert_module!( r#" fn name(user: User) { user.name @@ -302,7 +287,7 @@ fn field_access() { #[test] fn call() { - assert_parse!( + assert_module!( r#" fn calls() { let x = add_one(3) @@ -317,7 +302,7 @@ fn call() { #[test] fn record_update() { - assert_parse!( + assert_module!( r#" fn update_name(user: User, name: ByteArray) -> User { User { ..user, name: "Aiken", age } @@ -328,7 +313,7 @@ fn record_update() { #[test] fn record_create_labeled() { - assert_parse!( + assert_module!( r#" fn create() { User { name: "Aiken", age, thing: 2 } @@ -339,7 +324,7 @@ fn record_create_labeled() { #[test] fn record_create_labeled_with_field_access() { - assert_parse!( + assert_module!( r#" fn create() { some_module.User { name: "Aiken", age, thing: 2 } @@ -350,7 +335,7 @@ fn record_create_labeled_with_field_access() { #[test] fn cargo_create_unlabeled() { - assert_parse!( + assert_module!( r#" fn create() { some_module.Thing(1, a) @@ -361,7 +346,7 @@ fn cargo_create_unlabeled() { #[test] fn parse_tuple() { - assert_parse!( + assert_module!( r#" fn foo() { let tuple = (1, 2, 3, 4) @@ -373,7 +358,7 @@ fn parse_tuple() { #[test] fn parse_tuple2() { - assert_parse!( + assert_module!( r#" fn foo() { let a = foo(14) @@ -385,7 +370,7 @@ fn parse_tuple2() { #[test] fn plain_bytearray_literals() { - assert_parse!( + assert_module!( r#" pub const my_policy_id = #[0, 170, 255] "# @@ -394,7 +379,7 @@ fn plain_bytearray_literals() { #[test] fn base16_bytearray_literals() { - assert_parse!( + assert_module!( r#" pub const my_policy_id = #"00aaff" @@ -407,7 +392,7 @@ fn base16_bytearray_literals() { #[test] fn function_def() { - assert_parse!( + assert_module!( r#" fn foo() {} "# @@ -416,7 +401,7 @@ fn function_def() { #[test] fn function_invoke() { - assert_parse!( + assert_module!( r#" fn foo() { let a = bar(42) @@ -427,7 +412,7 @@ fn function_invoke() { #[test] fn function_ambiguous_sequence() { - assert_parse!( + assert_module!( r#" fn foo_1() { let a = bar @@ -453,7 +438,7 @@ fn function_ambiguous_sequence() { #[test] fn tuple_type_alias() { - assert_parse!( + assert_module!( r#" type RoyaltyToken = (PolicyId, AssetName) "# @@ -462,7 +447,7 @@ fn tuple_type_alias() { #[test] fn int_parsing_hex_bytes() { - assert_parse!( + assert_module!( r#" fn foo() { #[ 0x01, 0xa2, 0x03 ] @@ -473,7 +458,7 @@ fn int_parsing_hex_bytes() { #[test] fn test_parsing_numeric_underscore() { - assert_parse!( + assert_module!( r#" fn foo() { let i = 1_234_567 @@ -486,7 +471,7 @@ fn test_parsing_numeric_underscore() { #[test] fn first_class_binop() { - assert_parse!( + assert_module!( r#" fn foo() { compare_with(a, >, b) From f9c099a9235867576fa6cdb622e344ec2c38e4ac Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 1 Jul 2023 14:40:34 -0400 Subject: [PATCH 15/31] test: add indoc to assert_expr macro --- crates/aiken-lang/src/parser/utils.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/aiken-lang/src/parser/utils.rs b/crates/aiken-lang/src/parser/utils.rs index e75838831..7fd5b38c1 100644 --- a/crates/aiken-lang/src/parser/utils.rs +++ b/crates/aiken-lang/src/parser/utils.rs @@ -97,14 +97,14 @@ pub fn type_name_with_args() -> impl Parser>) #[macro_export] macro_rules! assert_expr { ($code:expr) => { - let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run($code).unwrap(); + let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap(); let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len()), tokens.into_iter()); let result = $crate::parser::expr::sequence().parse(stream).unwrap(); insta::with_settings!({ - description => concat!("Code:\n\n", $code), + description => concat!("Code:\n\n", indoc::indoc! { $code }), prepend_module_to_snapshot => false, omit_expression => true }, { From 44d0432560e61284191b6c992687ba9be95afe62 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 1 Jul 2023 14:54:55 -0400 Subject: [PATCH 16/31] test(parser): int list --- crates/aiken-lang/src/parser/expr/list.rs | 5 +++ .../src/parser/expr/snapshots/int_list.snap | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/int_list.snap diff --git a/crates/aiken-lang/src/parser/expr/list.rs b/crates/aiken-lang/src/parser/expr/list.rs index 3349b9a15..69dd4b227 100644 --- a/crates/aiken-lang/src/parser/expr/list.rs +++ b/crates/aiken-lang/src/parser/expr/list.rs @@ -39,4 +39,9 @@ mod tests { fn empty_list() { assert_expr!("[]"); } + + #[test] + fn int_list() { + assert_expr!("[1, 2, 3]"); + } } diff --git a/crates/aiken-lang/src/parser/expr/snapshots/int_list.snap b/crates/aiken-lang/src/parser/expr/snapshots/int_list.snap new file mode 100644 index 000000000..a2040224b --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/int_list.snap @@ -0,0 +1,31 @@ +--- +source: crates/aiken-lang/src/parser/expr/list.rs +description: "Code:\n\n[1, 2, 3]" +--- +List { + location: 0..9, + elements: [ + Int { + location: 1..2, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 4..5, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 7..8, + value: "3", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + tail: None, +} From baf807ca2df7e5fb9f5a42b487309efe7ed9f20c Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 1 Jul 2023 15:00:48 -0400 Subject: [PATCH 17/31] test(parser): list spread --- crates/aiken-lang/src/parser/expr/list.rs | 5 ++++ .../parser/expr/snapshots/list_spread.snap | 30 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/list_spread.snap diff --git a/crates/aiken-lang/src/parser/expr/list.rs b/crates/aiken-lang/src/parser/expr/list.rs index 69dd4b227..8e76eb9f2 100644 --- a/crates/aiken-lang/src/parser/expr/list.rs +++ b/crates/aiken-lang/src/parser/expr/list.rs @@ -44,4 +44,9 @@ mod tests { fn int_list() { assert_expr!("[1, 2, 3]"); } + + #[test] + fn list_spread() { + assert_expr!("[1, 2, ..[]]"); + } } diff --git a/crates/aiken-lang/src/parser/expr/snapshots/list_spread.snap b/crates/aiken-lang/src/parser/expr/snapshots/list_spread.snap new file mode 100644 index 000000000..2009bc420 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/list_spread.snap @@ -0,0 +1,30 @@ +--- +source: crates/aiken-lang/src/parser/expr/list.rs +description: "Code:\n\n[1, 2, ..[]]" +--- +List { + location: 0..12, + elements: [ + Int { + location: 1..2, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 4..5, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + tail: Some( + List { + location: 9..11, + elements: [], + tail: None, + }, + ), +} From 6b05d6a91e06777c8574a7b738fc7eb6f8bd1adc Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 3 Jul 2023 14:58:04 -0400 Subject: [PATCH 18/31] test(parser): rename definitions to definition and more tests --- crates/aiken-lang/src/parser.rs | 6 +- .../{definitions => definition}/constant.rs | 0 .../{definitions => definition}/data_type.rs | 0 .../{definitions => definition}/function.rs | 0 .../{definitions => definition}/import.rs | 26 +++++ .../parser/{definitions => definition}/mod.rs | 4 +- .../definition/snapshots/import_alias.snap | 18 +++ .../definition/snapshots/import_basic.snap | 16 +++ .../snapshots/import_unqualified.snap | 33 ++++++ .../{definitions => definition}/test.rs | 0 .../{definitions => definition}/type_alias.rs | 0 .../{definitions => definition}/validator.rs | 0 crates/aiken-lang/src/parser/expr/if_else.rs | 22 ++++ crates/aiken-lang/src/parser/expr/int.rs | 35 ++++++ crates/aiken-lang/src/parser/expr/list.rs | 1 - .../parser/expr/snapshots/if_else_basic.snap | 66 +++++++++++ .../parser/expr/snapshots/int_hex_bytes.snap | 9 ++ .../parser/expr/snapshots/int_literal.snap | 11 ++ .../parser/expr/snapshots/int_negative.snap | 15 +++ .../snapshots/int_numeric_underscore.snap | 61 ++++++++++ .../aiken-lang/src/parser/expr/when/guard.rs | 4 +- crates/aiken-lang/src/parser/utils.rs | 19 +++ crates/aiken-lang/src/tests/parser.rs | 61 ---------- .../src/tests/snapshots/if_expression.snap | 109 ------------------ .../src/tests/snapshots/import.snap | 24 ---- .../snapshots/int_parsing_hex_bytes.snap | 36 ------ .../snapshots/parsing_numeric_underscore.snap | 82 ------------- .../tests/snapshots/unqualified_imports.snap | 41 ------- 28 files changed, 337 insertions(+), 362 deletions(-) rename crates/aiken-lang/src/parser/{definitions => definition}/constant.rs (100%) rename crates/aiken-lang/src/parser/{definitions => definition}/data_type.rs (100%) rename crates/aiken-lang/src/parser/{definitions => definition}/function.rs (100%) rename crates/aiken-lang/src/parser/{definitions => definition}/import.rs (79%) rename crates/aiken-lang/src/parser/{definitions => definition}/mod.rs (82%) create mode 100644 crates/aiken-lang/src/parser/definition/snapshots/import_alias.snap create mode 100644 crates/aiken-lang/src/parser/definition/snapshots/import_basic.snap create mode 100644 crates/aiken-lang/src/parser/definition/snapshots/import_unqualified.snap rename crates/aiken-lang/src/parser/{definitions => definition}/test.rs (100%) rename crates/aiken-lang/src/parser/{definitions => definition}/type_alias.rs (100%) rename crates/aiken-lang/src/parser/{definitions => definition}/validator.rs (100%) create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/if_else_basic.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/int_hex_bytes.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/int_literal.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/int_negative.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/int_numeric_underscore.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/if_expression.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/import.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/int_parsing_hex_bytes.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/parsing_numeric_underscore.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/unqualified_imports.snap diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index f384de20e..408f6068c 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -1,5 +1,5 @@ mod annotation; -pub mod definitions; +pub mod definition; pub mod error; pub mod expr; pub mod extra; @@ -9,7 +9,7 @@ pub mod token; mod utils; pub use annotation::parser as annotation; -pub use definitions::parser as definitions; +pub use definition::parser as definition; pub use expr::parser as expression; pub use pattern::parser as pattern; @@ -26,7 +26,7 @@ pub fn module( let stream = chumsky::Stream::from_iter(ast::Span::create(tokens.len()), tokens.into_iter()); - let definitions = definitions().parse(stream)?; + let definitions = definition().repeated().then_ignore(end()).parse(stream)?; let module = ast::UntypedModule { kind, diff --git a/crates/aiken-lang/src/parser/definitions/constant.rs b/crates/aiken-lang/src/parser/definition/constant.rs similarity index 100% rename from crates/aiken-lang/src/parser/definitions/constant.rs rename to crates/aiken-lang/src/parser/definition/constant.rs diff --git a/crates/aiken-lang/src/parser/definitions/data_type.rs b/crates/aiken-lang/src/parser/definition/data_type.rs similarity index 100% rename from crates/aiken-lang/src/parser/definitions/data_type.rs rename to crates/aiken-lang/src/parser/definition/data_type.rs diff --git a/crates/aiken-lang/src/parser/definitions/function.rs b/crates/aiken-lang/src/parser/definition/function.rs similarity index 100% rename from crates/aiken-lang/src/parser/definitions/function.rs rename to crates/aiken-lang/src/parser/definition/function.rs diff --git a/crates/aiken-lang/src/parser/definitions/import.rs b/crates/aiken-lang/src/parser/definition/import.rs similarity index 79% rename from crates/aiken-lang/src/parser/definitions/import.rs rename to crates/aiken-lang/src/parser/definition/import.rs index af401954d..6ac7186c5 100644 --- a/crates/aiken-lang/src/parser/definitions/import.rs +++ b/crates/aiken-lang/src/parser/definition/import.rs @@ -55,3 +55,29 @@ pub fn parser() -> impl Parser impl Parser, Error = ParseError> { +pub fn parser() -> impl Parser { choice(( import(), data_type(), @@ -29,6 +29,4 @@ pub fn parser() -> impl Parser, Error = Parse test(), constant(), )) - .repeated() - .then_ignore(end()) } diff --git a/crates/aiken-lang/src/parser/definition/snapshots/import_alias.snap b/crates/aiken-lang/src/parser/definition/snapshots/import_alias.snap new file mode 100644 index 000000000..72220ba3f --- /dev/null +++ b/crates/aiken-lang/src/parser/definition/snapshots/import_alias.snap @@ -0,0 +1,18 @@ +--- +source: crates/aiken-lang/src/parser/definition/import.rs +description: "Code:\n\nuse aiken/list as foo" +--- +Use( + Use { + as_name: Some( + "foo", + ), + location: 0..21, + module: [ + "aiken", + "list", + ], + package: (), + unqualified: [], + }, +) diff --git a/crates/aiken-lang/src/parser/definition/snapshots/import_basic.snap b/crates/aiken-lang/src/parser/definition/snapshots/import_basic.snap new file mode 100644 index 000000000..49015b8f2 --- /dev/null +++ b/crates/aiken-lang/src/parser/definition/snapshots/import_basic.snap @@ -0,0 +1,16 @@ +--- +source: crates/aiken-lang/src/parser/definition/import.rs +description: "Code:\n\nuse aiken/list" +--- +Use( + Use { + as_name: None, + location: 0..14, + module: [ + "aiken", + "list", + ], + package: (), + unqualified: [], + }, +) diff --git a/crates/aiken-lang/src/parser/definition/snapshots/import_unqualified.snap b/crates/aiken-lang/src/parser/definition/snapshots/import_unqualified.snap new file mode 100644 index 000000000..d001f3bc6 --- /dev/null +++ b/crates/aiken-lang/src/parser/definition/snapshots/import_unqualified.snap @@ -0,0 +1,33 @@ +--- +source: crates/aiken-lang/src/parser/definition/import.rs +description: "Code:\n\nuse std/address.{Address as A, thing as w}\n" +--- +Use( + Use { + as_name: None, + location: 0..42, + module: [ + "std", + "address", + ], + package: (), + unqualified: [ + UnqualifiedImport { + location: 17..29, + name: "Address", + as_name: Some( + "A", + ), + layer: Value, + }, + UnqualifiedImport { + location: 31..41, + name: "thing", + as_name: Some( + "w", + ), + layer: Value, + }, + ], + }, +) diff --git a/crates/aiken-lang/src/parser/definitions/test.rs b/crates/aiken-lang/src/parser/definition/test.rs similarity index 100% rename from crates/aiken-lang/src/parser/definitions/test.rs rename to crates/aiken-lang/src/parser/definition/test.rs diff --git a/crates/aiken-lang/src/parser/definitions/type_alias.rs b/crates/aiken-lang/src/parser/definition/type_alias.rs similarity index 100% rename from crates/aiken-lang/src/parser/definitions/type_alias.rs rename to crates/aiken-lang/src/parser/definition/type_alias.rs diff --git a/crates/aiken-lang/src/parser/definitions/validator.rs b/crates/aiken-lang/src/parser/definition/validator.rs similarity index 100% rename from crates/aiken-lang/src/parser/definitions/validator.rs rename to crates/aiken-lang/src/parser/definition/validator.rs diff --git a/crates/aiken-lang/src/parser/expr/if_else.rs b/crates/aiken-lang/src/parser/expr/if_else.rs index 0d1868187..6cc763440 100644 --- a/crates/aiken-lang/src/parser/expr/if_else.rs +++ b/crates/aiken-lang/src/parser/expr/if_else.rs @@ -46,3 +46,25 @@ pub fn parser<'a>( } }) } + +#[cfg(test)] +mod tests { + use chumsky::Parser; + + use crate::assert_expr; + + #[test] + fn if_else_basic() { + assert_expr!( + r#" + if True { + 1 + 1 + } else if a < 1 { + 3 + } else { + 4 + } + "# + ); + } +} diff --git a/crates/aiken-lang/src/parser/expr/int.rs b/crates/aiken-lang/src/parser/expr/int.rs index aa73a32d2..e81db1412 100644 --- a/crates/aiken-lang/src/parser/expr/int.rs +++ b/crates/aiken-lang/src/parser/expr/int.rs @@ -14,3 +14,38 @@ pub fn parser() -> impl Parser { } }) } + +#[cfg(test)] +mod tests { + use chumsky::Parser; + + use crate::assert_expr; + + #[test] + fn int_literal() { + assert_expr!("1"); + } + + #[test] + fn int_negative() { + assert_expr!("-1"); + } + + #[test] + fn int_numeric_underscore() { + assert_expr!( + r#" + { + let i = 1_234_567 + let j = 1_000_000 + let k = -10_000 + } + "# + ); + } + + #[test] + fn int_hex_bytes() { + assert_expr!(r#"0x01"#); + } +} diff --git a/crates/aiken-lang/src/parser/expr/list.rs b/crates/aiken-lang/src/parser/expr/list.rs index 8e76eb9f2..c6a3b2d50 100644 --- a/crates/aiken-lang/src/parser/expr/list.rs +++ b/crates/aiken-lang/src/parser/expr/list.rs @@ -30,7 +30,6 @@ pub fn parser( #[cfg(test)] mod tests { - use chumsky::Parser; use crate::assert_expr; diff --git a/crates/aiken-lang/src/parser/expr/snapshots/if_else_basic.snap b/crates/aiken-lang/src/parser/expr/snapshots/if_else_basic.snap new file mode 100644 index 000000000..32cbd0343 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/if_else_basic.snap @@ -0,0 +1,66 @@ +--- +source: crates/aiken-lang/src/parser/expr/if_else.rs +description: "Code:\n\nif True {\n 1 + 1\n} else if a < 1 {\n 3\n} else {\n 4\n}\n" +--- +If { + location: 0..54, + branches: [ + IfBranch { + condition: Var { + location: 3..7, + name: "True", + }, + body: BinOp { + location: 12..17, + name: AddInt, + left: Int { + location: 12..13, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + right: Int { + location: 16..17, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + location: 3..19, + }, + IfBranch { + condition: BinOp { + location: 28..33, + name: LtInt, + left: Var { + location: 28..29, + name: "a", + }, + right: Int { + location: 32..33, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + body: Int { + location: 38..39, + value: "3", + base: Decimal { + numeric_underscore: false, + }, + }, + location: 28..41, + }, + ], + final_else: Int { + location: 51..52, + value: "4", + base: Decimal { + numeric_underscore: false, + }, + }, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/int_hex_bytes.snap b/crates/aiken-lang/src/parser/expr/snapshots/int_hex_bytes.snap new file mode 100644 index 000000000..56ab77ece --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/int_hex_bytes.snap @@ -0,0 +1,9 @@ +--- +source: crates/aiken-lang/src/parser/expr/int.rs +description: "Code:\n\n0x01" +--- +Int { + location: 0..4, + value: "1", + base: Hexadecimal, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/int_literal.snap b/crates/aiken-lang/src/parser/expr/snapshots/int_literal.snap new file mode 100644 index 000000000..da7cb54b4 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/int_literal.snap @@ -0,0 +1,11 @@ +--- +source: crates/aiken-lang/src/parser/expr/int.rs +description: "Code:\n\n1" +--- +Int { + location: 0..1, + value: "1", + base: Decimal { + numeric_underscore: false, + }, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/int_negative.snap b/crates/aiken-lang/src/parser/expr/snapshots/int_negative.snap new file mode 100644 index 000000000..fcf40a1d8 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/int_negative.snap @@ -0,0 +1,15 @@ +--- +source: crates/aiken-lang/src/parser/expr/int.rs +description: "Code:\n\n-1" +--- +UnOp { + op: Negate, + location: 0..2, + value: Int { + location: 1..2, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/int_numeric_underscore.snap b/crates/aiken-lang/src/parser/expr/snapshots/int_numeric_underscore.snap new file mode 100644 index 000000000..566e732ec --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/int_numeric_underscore.snap @@ -0,0 +1,61 @@ +--- +source: crates/aiken-lang/src/parser/expr/int.rs +description: "Code:\n\n{\n let i = 1_234_567\n let j = 1_000_000\n let k = -10_000\n}\n" +--- +Sequence { + location: 4..59, + expressions: [ + Assignment { + location: 4..21, + value: Int { + location: 12..21, + value: "1234567", + base: Decimal { + numeric_underscore: true, + }, + }, + pattern: Var { + location: 8..9, + name: "i", + }, + kind: Let, + annotation: None, + }, + Assignment { + location: 24..41, + value: Int { + location: 32..41, + value: "1000000", + base: Decimal { + numeric_underscore: true, + }, + }, + pattern: Var { + location: 28..29, + name: "j", + }, + kind: Let, + annotation: None, + }, + Assignment { + location: 44..59, + value: UnOp { + op: Negate, + location: 52..59, + value: Int { + location: 53..59, + value: "10000", + base: Decimal { + numeric_underscore: true, + }, + }, + }, + pattern: Var { + location: 48..49, + name: "k", + }, + kind: Let, + annotation: None, + }, + ], +} diff --git a/crates/aiken-lang/src/parser/expr/when/guard.rs b/crates/aiken-lang/src/parser/expr/when/guard.rs index afcc118f9..95459ebc3 100644 --- a/crates/aiken-lang/src/parser/expr/when/guard.rs +++ b/crates/aiken-lang/src/parser/expr/when/guard.rs @@ -2,7 +2,7 @@ use chumsky::prelude::*; use crate::{ ast, - parser::{definitions, error::ParseError, token::Token}, + parser::{definition, error::ParseError, token::Token}, }; pub fn parser() -> impl Parser { @@ -17,7 +17,7 @@ pub fn parser() -> impl Parser { + let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap(); + + let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len()), tokens.into_iter()); + + let result = $crate::parser::definition().parse(stream).unwrap(); + + insta::with_settings!({ + description => concat!("Code:\n\n", indoc::indoc! { $code }), + prepend_module_to_snapshot => false, + omit_expression => true + }, { + insta::assert_debug_snapshot!(result); + }); + }; +} diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 3f5e50963..c10705c9d 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -72,24 +72,6 @@ fn double_validator() { ); } -#[test] -fn import() { - assert_module!( - r#" - use std/list - "# - ); -} - -#[test] -fn unqualified_imports() { - assert_module!( - r#" - use std/address.{Address as A, thing as w} - "# - ); -} - #[test] fn import_alias() { assert_module!( @@ -186,25 +168,6 @@ fn pipeline() { ); } -#[test] -fn if_expression() { - assert_module!( - r#" - fn ifs() { - if True { - 1 + 1 - } else if a < 4 { - 5 - } else if a || b { - 6 - } else { - 3 - } - } - "# - ); -} - #[test] fn let_bindings() { assert_module!( @@ -445,30 +408,6 @@ fn tuple_type_alias() { ); } -#[test] -fn int_parsing_hex_bytes() { - assert_module!( - r#" - fn foo() { - #[ 0x01, 0xa2, 0x03 ] - } - "# - ); -} - -#[test] -fn test_parsing_numeric_underscore() { - assert_module!( - r#" - fn foo() { - let i = 1_234_567 - let j = 1_000_000 - let k = -10_000 - } - "# - ); -} - #[test] fn first_class_binop() { assert_module!( diff --git a/crates/aiken-lang/src/tests/snapshots/if_expression.snap b/crates/aiken-lang/src/tests/snapshots/if_expression.snap deleted file mode 100644 index e2b3b319f..000000000 --- a/crates/aiken-lang/src/tests/snapshots/if_expression.snap +++ /dev/null @@ -1,109 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn ifs() {\n if True {\n 1 + 1\n } else if a < 4 {\n 5\n } else if a || b {\n 6\n } else {\n 3\n }\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: If { - location: 13..106, - branches: [ - IfBranch { - condition: Var { - location: 16..20, - name: "True", - }, - body: BinOp { - location: 27..32, - name: AddInt, - left: Int { - location: 27..28, - value: "1", - base: Decimal { - numeric_underscore: false, - }, - }, - right: Int { - location: 31..32, - value: "1", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - location: 16..36, - }, - IfBranch { - condition: BinOp { - location: 45..50, - name: LtInt, - left: Var { - location: 45..46, - name: "a", - }, - right: Int { - location: 49..50, - value: "4", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - body: Int { - location: 57..58, - value: "5", - base: Decimal { - numeric_underscore: false, - }, - }, - location: 45..62, - }, - IfBranch { - condition: BinOp { - location: 71..77, - name: Or, - left: Var { - location: 71..72, - name: "a", - }, - right: Var { - location: 76..77, - name: "b", - }, - }, - body: Int { - location: 84..85, - value: "6", - base: Decimal { - numeric_underscore: false, - }, - }, - location: 71..89, - }, - ], - final_else: Int { - location: 101..102, - value: "3", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - doc: None, - location: 0..8, - name: "ifs", - public: false, - return_annotation: None, - return_type: (), - end_position: 107, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/import.snap b/crates/aiken-lang/src/tests/snapshots/import.snap deleted file mode 100644 index 2137b6354..000000000 --- a/crates/aiken-lang/src/tests/snapshots/import.snap +++ /dev/null @@ -1,24 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nuse std/list\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Use( - Use { - as_name: None, - location: 0..12, - module: [ - "std", - "list", - ], - package: (), - unqualified: [], - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/int_parsing_hex_bytes.snap b/crates/aiken-lang/src/tests/snapshots/int_parsing_hex_bytes.snap deleted file mode 100644 index e5969d3f5..000000000 --- a/crates/aiken-lang/src/tests/snapshots/int_parsing_hex_bytes.snap +++ /dev/null @@ -1,36 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn foo() {\n #[ 0x01, 0xa2, 0x03 ]\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: ByteArray { - location: 13..34, - bytes: [ - 1, - 162, - 3, - ], - preferred_format: ArrayOfBytes( - Hexadecimal, - ), - }, - doc: None, - location: 0..8, - name: "foo", - public: false, - return_annotation: None, - return_type: (), - end_position: 35, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/parsing_numeric_underscore.snap b/crates/aiken-lang/src/tests/snapshots/parsing_numeric_underscore.snap deleted file mode 100644 index a2c4b26eb..000000000 --- a/crates/aiken-lang/src/tests/snapshots/parsing_numeric_underscore.snap +++ /dev/null @@ -1,82 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn foo() {\n let i = 1_234_567\n let j = 1_000_000\n let k = -10_000\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: Sequence { - location: 13..68, - expressions: [ - Assignment { - location: 13..30, - value: Int { - location: 21..30, - value: "1234567", - base: Decimal { - numeric_underscore: true, - }, - }, - pattern: Var { - location: 17..18, - name: "i", - }, - kind: Let, - annotation: None, - }, - Assignment { - location: 33..50, - value: Int { - location: 41..50, - value: "1000000", - base: Decimal { - numeric_underscore: true, - }, - }, - pattern: Var { - location: 37..38, - name: "j", - }, - kind: Let, - annotation: None, - }, - Assignment { - location: 53..68, - value: UnOp { - op: Negate, - location: 61..68, - value: Int { - location: 62..68, - value: "10000", - base: Decimal { - numeric_underscore: true, - }, - }, - }, - pattern: Var { - location: 57..58, - name: "k", - }, - kind: Let, - annotation: None, - }, - ], - }, - doc: None, - location: 0..8, - name: "foo", - public: false, - return_annotation: None, - return_type: (), - end_position: 69, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/unqualified_imports.snap b/crates/aiken-lang/src/tests/snapshots/unqualified_imports.snap deleted file mode 100644 index b8d080d8c..000000000 --- a/crates/aiken-lang/src/tests/snapshots/unqualified_imports.snap +++ /dev/null @@ -1,41 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nuse std/address.{Address as A, thing as w}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Use( - Use { - as_name: None, - location: 0..42, - module: [ - "std", - "address", - ], - package: (), - unqualified: [ - UnqualifiedImport { - location: 17..29, - name: "Address", - as_name: Some( - "A", - ), - layer: Value, - }, - UnqualifiedImport { - location: 31..41, - name: "thing", - as_name: Some( - "w", - ), - layer: Value, - }, - ], - }, - ), - ], - kind: Validator, -} From bd8c13c3727a38e138fce5c6211fddd64fef1150 Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 3 Jul 2023 15:09:31 -0400 Subject: [PATCH 19/31] test(parser): move over the validator tests and some misc tests to parser --- crates/aiken-lang/src/parser.rs | 21 ++++ .../src/parser/definition/import.rs | 2 - .../snapshots/double_validator.snap | 101 ++++++++++++++++ .../definition/snapshots/validator.snap | 62 ++++++++++ .../src/parser/definition/validator.rs | 35 ++++++ crates/aiken-lang/src/parser/expr/if_else.rs | 2 - crates/aiken-lang/src/parser/expr/int.rs | 2 - crates/aiken-lang/src/parser/expr/list.rs | 2 - crates/aiken-lang/src/parser/utils.rs | 6 +- .../can_handle_comments_at_end_of_file.snap | 2 +- .../snapshots/windows_newline.snap | 2 +- crates/aiken-lang/src/tests/parser.rs | 57 +-------- .../src/tests/snapshots/double_validator.snap | 109 ------------------ .../src/tests/snapshots/import_alias.snap | 26 ----- .../src/tests/snapshots/validator.snap | 70 ----------- 15 files changed, 227 insertions(+), 272 deletions(-) create mode 100644 crates/aiken-lang/src/parser/definition/snapshots/double_validator.snap create mode 100644 crates/aiken-lang/src/parser/definition/snapshots/validator.snap rename crates/aiken-lang/src/{tests => }/snapshots/can_handle_comments_at_end_of_file.snap (90%) rename crates/aiken-lang/src/{tests => }/snapshots/windows_newline.snap (90%) delete mode 100644 crates/aiken-lang/src/tests/snapshots/double_validator.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/import_alias.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/validator.snap diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 408f6068c..f73b950c3 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -38,3 +38,24 @@ pub fn module( Ok((module, extra)) } + +#[cfg(test)] +mod tests { + use crate::assert_module; + + #[test] + fn windows_newline() { + assert_module!("use aiken/list\r\n"); + } + + #[test] + fn can_handle_comments_at_end_of_file() { + assert_module!( + r#" + use aiken + + // some comment + // more comments"# + ); + } +} diff --git a/crates/aiken-lang/src/parser/definition/import.rs b/crates/aiken-lang/src/parser/definition/import.rs index 6ac7186c5..2b1d959d7 100644 --- a/crates/aiken-lang/src/parser/definition/import.rs +++ b/crates/aiken-lang/src/parser/definition/import.rs @@ -58,8 +58,6 @@ pub fn parser() -> impl Parser impl Parser( #[cfg(test)] mod tests { - use chumsky::Parser; - use crate::assert_expr; #[test] diff --git a/crates/aiken-lang/src/parser/expr/int.rs b/crates/aiken-lang/src/parser/expr/int.rs index e81db1412..6e4f628cc 100644 --- a/crates/aiken-lang/src/parser/expr/int.rs +++ b/crates/aiken-lang/src/parser/expr/int.rs @@ -17,8 +17,6 @@ pub fn parser() -> impl Parser { #[cfg(test)] mod tests { - use chumsky::Parser; - use crate::assert_expr; #[test] diff --git a/crates/aiken-lang/src/parser/expr/list.rs b/crates/aiken-lang/src/parser/expr/list.rs index c6a3b2d50..d11af859d 100644 --- a/crates/aiken-lang/src/parser/expr/list.rs +++ b/crates/aiken-lang/src/parser/expr/list.rs @@ -30,8 +30,6 @@ pub fn parser( #[cfg(test)] mod tests { - use chumsky::Parser; - use crate::assert_expr; #[test] diff --git a/crates/aiken-lang/src/parser/utils.rs b/crates/aiken-lang/src/parser/utils.rs index 521e7004e..a4150279e 100644 --- a/crates/aiken-lang/src/parser/utils.rs +++ b/crates/aiken-lang/src/parser/utils.rs @@ -97,6 +97,8 @@ pub fn type_name_with_args() -> impl Parser>) #[macro_export] macro_rules! assert_expr { ($code:expr) => { + use chumsky::Parser; + let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap(); let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len()), tokens.into_iter()); @@ -117,7 +119,7 @@ macro_rules! assert_expr { macro_rules! assert_module { ($code:expr) => { let (module, _) = - parser::module(indoc::indoc!{ $code }, ast::ModuleKind::Validator).expect("Failed to parse code"); + $crate::parser::module(indoc::indoc!{ $code }, $crate::ast::ModuleKind::Validator).expect("Failed to parse code"); insta::with_settings!({ description => concat!("Code:\n\n", indoc::indoc! { $code }), @@ -132,6 +134,8 @@ macro_rules! assert_module { #[macro_export] macro_rules! assert_definition { ($code:expr) => { + use chumsky::Parser; + let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap(); let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len()), tokens.into_iter()); diff --git a/crates/aiken-lang/src/tests/snapshots/can_handle_comments_at_end_of_file.snap b/crates/aiken-lang/src/snapshots/can_handle_comments_at_end_of_file.snap similarity index 90% rename from crates/aiken-lang/src/tests/snapshots/can_handle_comments_at_end_of_file.snap rename to crates/aiken-lang/src/snapshots/can_handle_comments_at_end_of_file.snap index c25d4eff7..c4b7b173b 100644 --- a/crates/aiken-lang/src/tests/snapshots/can_handle_comments_at_end_of_file.snap +++ b/crates/aiken-lang/src/snapshots/can_handle_comments_at_end_of_file.snap @@ -1,5 +1,5 @@ --- -source: crates/aiken-lang/src/tests/parser.rs +source: crates/aiken-lang/src/parser.rs description: "Code:\n\nuse aiken\n\n// some comment\n// more comments" --- Module { diff --git a/crates/aiken-lang/src/tests/snapshots/windows_newline.snap b/crates/aiken-lang/src/snapshots/windows_newline.snap similarity index 90% rename from crates/aiken-lang/src/tests/snapshots/windows_newline.snap rename to crates/aiken-lang/src/snapshots/windows_newline.snap index 0e6ab80d0..70e7d0e62 100644 --- a/crates/aiken-lang/src/tests/snapshots/windows_newline.snap +++ b/crates/aiken-lang/src/snapshots/windows_newline.snap @@ -1,5 +1,5 @@ --- -source: crates/aiken-lang/src/tests/parser.rs +source: crates/aiken-lang/src/parser.rs description: "Code:\n\nuse aiken/list\r\n" --- Module { diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index c10705c9d..8b9e6d593 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -1,20 +1,4 @@ -use crate::{assert_module, ast, parser}; - -#[test] -fn windows_newline() { - assert_module!("use aiken/list\r\n"); -} - -#[test] -fn can_handle_comments_at_end_of_file() { - assert_module!( - r#" - use aiken - - // some comment - // more comments"# - ); -} +use crate::assert_module; #[test] fn type_annotation_with_module_prefix() { @@ -42,45 +26,6 @@ fn test_fail() { ); } -#[test] -fn validator() { - assert_module!( - r#" - validator { - fn foo(datum, rdmr, ctx) { - True - } - } - "# - ); -} - -#[test] -fn double_validator() { - assert_module!( - r#" - validator { - fn foo(datum, rdmr, ctx) { - True - } - - fn bar(rdmr, ctx) { - True - } - } - "# - ); -} - -#[test] -fn import_alias() { - assert_module!( - r#" - use std/tx as t - "# - ); -} - #[test] fn custom_type() { assert_module!( diff --git a/crates/aiken-lang/src/tests/snapshots/double_validator.snap b/crates/aiken-lang/src/tests/snapshots/double_validator.snap deleted file mode 100644 index 3886e7e44..000000000 --- a/crates/aiken-lang/src/tests/snapshots/double_validator.snap +++ /dev/null @@ -1,109 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nvalidator {\n fn foo(datum, rdmr, ctx) {\n True\n }\n\n fn bar(rdmr, ctx) {\n True\n }\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Validator( - Validator { - doc: None, - end_position: 90, - fun: Function { - arguments: [ - Arg { - arg_name: Named { - name: "datum", - label: "datum", - location: 21..26, - is_validator_param: false, - }, - location: 21..26, - annotation: None, - tipo: (), - }, - Arg { - arg_name: Named { - name: "rdmr", - label: "rdmr", - location: 28..32, - is_validator_param: false, - }, - location: 28..32, - annotation: None, - tipo: (), - }, - Arg { - arg_name: Named { - name: "ctx", - label: "ctx", - location: 34..37, - is_validator_param: false, - }, - location: 34..37, - annotation: None, - tipo: (), - }, - ], - body: Var { - location: 45..49, - name: "True", - }, - doc: None, - location: 14..38, - name: "foo", - public: false, - return_annotation: None, - return_type: (), - end_position: 52, - can_error: true, - }, - other_fun: Some( - Function { - arguments: [ - Arg { - arg_name: Named { - name: "rdmr", - label: "rdmr", - location: 64..68, - is_validator_param: false, - }, - location: 64..68, - annotation: None, - tipo: (), - }, - Arg { - arg_name: Named { - name: "ctx", - label: "ctx", - location: 70..73, - is_validator_param: false, - }, - location: 70..73, - annotation: None, - tipo: (), - }, - ], - body: Var { - location: 81..85, - name: "True", - }, - doc: None, - location: 57..74, - name: "bar", - public: false, - return_annotation: None, - return_type: (), - end_position: 88, - can_error: true, - }, - ), - location: 0..9, - params: [], - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/import_alias.snap b/crates/aiken-lang/src/tests/snapshots/import_alias.snap deleted file mode 100644 index 71ba94d42..000000000 --- a/crates/aiken-lang/src/tests/snapshots/import_alias.snap +++ /dev/null @@ -1,26 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nuse std/tx as t\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Use( - Use { - as_name: Some( - "t", - ), - location: 0..15, - module: [ - "std", - "tx", - ], - package: (), - unqualified: [], - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/validator.snap b/crates/aiken-lang/src/tests/snapshots/validator.snap deleted file mode 100644 index 96070233e..000000000 --- a/crates/aiken-lang/src/tests/snapshots/validator.snap +++ /dev/null @@ -1,70 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nvalidator {\n fn foo(datum, rdmr, ctx) {\n True\n }\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Validator( - Validator { - doc: None, - end_position: 54, - fun: Function { - arguments: [ - Arg { - arg_name: Named { - name: "datum", - label: "datum", - location: 21..26, - is_validator_param: false, - }, - location: 21..26, - annotation: None, - tipo: (), - }, - Arg { - arg_name: Named { - name: "rdmr", - label: "rdmr", - location: 28..32, - is_validator_param: false, - }, - location: 28..32, - annotation: None, - tipo: (), - }, - Arg { - arg_name: Named { - name: "ctx", - label: "ctx", - location: 34..37, - is_validator_param: false, - }, - location: 34..37, - annotation: None, - tipo: (), - }, - ], - body: Var { - location: 45..49, - name: "True", - }, - doc: None, - location: 14..38, - name: "foo", - public: false, - return_annotation: None, - return_type: (), - end_position: 52, - can_error: true, - }, - other_fun: None, - location: 0..9, - params: [], - }, - ), - ], - kind: Validator, -} From a75bcff5c88b6c27b760cdabb29df84a693f1675 Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 3 Jul 2023 16:44:18 -0400 Subject: [PATCH 20/31] test(parser): type alias, anon fn, record update and more --- crates/aiken-lang/src/parser/annotation.rs | 10 ++ .../src/parser/definition/function.rs | 23 +++ .../src/parser/definition/snapshots/fail.snap | 45 ++++++ .../definition/snapshots/function_empty.snap | 28 ++++ .../snapshots/function_non_public.snap | 28 ++++ .../snapshots/type_alias_basic.snap | 20 +++ .../definition/snapshots/type_alias_pub.snap | 20 +++ .../snapshots/type_alias_tuple.snap | 31 ++++ .../aiken-lang/src/parser/definition/test.rs | 18 +++ .../src/parser/definition/type_alias.rs | 29 ++++ .../src/parser/expr/anonymous_function.rs | 10 ++ .../aiken-lang/src/parser/expr/assignment.rs | 10 ++ .../src/parser/expr/record_update.rs | 10 ++ .../snapshots/anonymous_function_basic.snap | 51 +++++++ .../parser/expr/snapshots/let_bindings.snap | 37 +++++ .../expr/snapshots/record_update_basic.snap | 43 ++++++ .../type_annotation_with_module_prefix.snap | 19 +++ crates/aiken-lang/src/parser/utils.rs | 21 +++ crates/aiken-lang/src/tests/parser.rs | 106 -------------- .../tests/snapshots/anonymous_function.snap | 109 -------------- .../src/tests/snapshots/empty_function.snap | 36 ----- .../aiken-lang/src/tests/snapshots/fail.snap | 53 ------- .../src/tests/snapshots/let_bindings.snap | 136 ------------------ .../src/tests/snapshots/pub_type_alias.snap | 35 ----- .../src/tests/snapshots/record_update.snap | 108 -------------- .../src/tests/snapshots/tuple_type_alias.snap | 39 ----- .../src/tests/snapshots/type_alias.snap | 35 ----- .../type_annotation_with_module_prefix.snap | 56 -------- 28 files changed, 453 insertions(+), 713 deletions(-) create mode 100644 crates/aiken-lang/src/parser/definition/snapshots/fail.snap create mode 100644 crates/aiken-lang/src/parser/definition/snapshots/function_empty.snap create mode 100644 crates/aiken-lang/src/parser/definition/snapshots/function_non_public.snap create mode 100644 crates/aiken-lang/src/parser/definition/snapshots/type_alias_basic.snap create mode 100644 crates/aiken-lang/src/parser/definition/snapshots/type_alias_pub.snap create mode 100644 crates/aiken-lang/src/parser/definition/snapshots/type_alias_tuple.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/anonymous_function_basic.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/let_bindings.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/record_update_basic.snap create mode 100644 crates/aiken-lang/src/parser/snapshots/type_annotation_with_module_prefix.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/anonymous_function.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/empty_function.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/fail.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/let_bindings.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/pub_type_alias.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/record_update.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/tuple_type_alias.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/type_alias.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/type_annotation_with_module_prefix.snap diff --git a/crates/aiken-lang/src/parser/annotation.rs b/crates/aiken-lang/src/parser/annotation.rs index c19aeff4b..7b0f52444 100644 --- a/crates/aiken-lang/src/parser/annotation.rs +++ b/crates/aiken-lang/src/parser/annotation.rs @@ -89,3 +89,13 @@ pub fn parser() -> impl Parser { )) }) } + +#[cfg(test)] +mod tests { + use crate::assert_annotation; + + #[test] + fn type_annotation_with_module_prefix() { + assert_annotation!("aiken.Option"); + } +} diff --git a/crates/aiken-lang/src/parser/definition/function.rs b/crates/aiken-lang/src/parser/definition/function.rs index 4b61939eb..235e433a7 100644 --- a/crates/aiken-lang/src/parser/definition/function.rs +++ b/crates/aiken-lang/src/parser/definition/function.rs @@ -87,3 +87,26 @@ pub fn param(is_validator_param: bool) -> impl Parser impl Parser impl Parser impl Parser { arg_name, }) } + +#[cfg(test)] +mod tests { + use crate::assert_expr; + + #[test] + fn anonymous_function_basic() { + assert_expr!(r#"fn (a: Int) -> Int { a + 1 }"#); + } +} diff --git a/crates/aiken-lang/src/parser/expr/assignment.rs b/crates/aiken-lang/src/parser/expr/assignment.rs index 4b25431ac..0759f223d 100644 --- a/crates/aiken-lang/src/parser/expr/assignment.rs +++ b/crates/aiken-lang/src/parser/expr/assignment.rs @@ -43,3 +43,13 @@ fn assignment( }, ) } + +#[cfg(test)] +mod tests { + use crate::assert_expr; + + #[test] + fn let_bindings() { + assert_expr!(r#"let thing = [ 1, 2, a ]"#); + } +} diff --git a/crates/aiken-lang/src/parser/expr/record_update.rs b/crates/aiken-lang/src/parser/expr/record_update.rs index 11dc9ea6d..4565be127 100644 --- a/crates/aiken-lang/src/parser/expr/record_update.rs +++ b/crates/aiken-lang/src/parser/expr/record_update.rs @@ -84,3 +84,13 @@ pub fn parser( } }) } + +#[cfg(test)] +mod tests { + use crate::assert_expr; + + #[test] + fn record_update_basic() { + assert_expr!(r#"User { ..user, name: "Aiken", age }"#); + } +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/anonymous_function_basic.snap b/crates/aiken-lang/src/parser/expr/snapshots/anonymous_function_basic.snap new file mode 100644 index 000000000..da64b10fb --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/anonymous_function_basic.snap @@ -0,0 +1,51 @@ +--- +source: crates/aiken-lang/src/parser/expr/anonymous_function.rs +description: "Code:\n\nfn (a: Int) -> Int { a + 1 }" +--- +Fn { + location: 0..28, + fn_style: Plain, + arguments: [ + Arg { + arg_name: Named { + name: "a", + label: "a", + location: 4..5, + is_validator_param: false, + }, + location: 4..10, + annotation: Some( + Constructor { + location: 7..10, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 21..26, + name: AddInt, + left: Var { + location: 21..22, + name: "a", + }, + right: Int { + location: 25..26, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + return_annotation: Some( + Constructor { + location: 15..18, + module: None, + name: "Int", + arguments: [], + }, + ), +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/let_bindings.snap b/crates/aiken-lang/src/parser/expr/snapshots/let_bindings.snap new file mode 100644 index 000000000..71e556c84 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/let_bindings.snap @@ -0,0 +1,37 @@ +--- +source: crates/aiken-lang/src/parser/expr/assignment.rs +description: "Code:\n\nlet thing = [ 1, 2, a ]" +--- +Assignment { + location: 0..23, + value: List { + location: 12..23, + elements: [ + Int { + location: 14..15, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 17..18, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + Var { + location: 20..21, + name: "a", + }, + ], + tail: None, + }, + pattern: Var { + location: 4..9, + name: "thing", + }, + kind: Let, + annotation: None, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/record_update_basic.snap b/crates/aiken-lang/src/parser/expr/snapshots/record_update_basic.snap new file mode 100644 index 000000000..69e8ebb80 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/record_update_basic.snap @@ -0,0 +1,43 @@ +--- +source: crates/aiken-lang/src/parser/expr/record_update.rs +description: "Code:\n\nUser { ..user, name: \"Aiken\", age }" +--- +RecordUpdate { + location: 0..35, + constructor: Var { + location: 0..4, + name: "User", + }, + spread: RecordUpdateSpread { + base: Var { + location: 9..13, + name: "user", + }, + location: 7..13, + }, + arguments: [ + UntypedRecordUpdateArg { + label: "name", + location: 15..28, + value: ByteArray { + location: 21..28, + bytes: [ + 65, + 105, + 107, + 101, + 110, + ], + preferred_format: Utf8String, + }, + }, + UntypedRecordUpdateArg { + label: "age", + location: 30..33, + value: Var { + location: 30..33, + name: "age", + }, + }, + ], +} diff --git a/crates/aiken-lang/src/parser/snapshots/type_annotation_with_module_prefix.snap b/crates/aiken-lang/src/parser/snapshots/type_annotation_with_module_prefix.snap new file mode 100644 index 000000000..44243da96 --- /dev/null +++ b/crates/aiken-lang/src/parser/snapshots/type_annotation_with_module_prefix.snap @@ -0,0 +1,19 @@ +--- +source: crates/aiken-lang/src/parser/annotation.rs +description: "Code:\n\naiken.Option" +--- +Constructor { + location: 0..17, + module: Some( + "aiken", + ), + name: "Option", + arguments: [ + Constructor { + location: 13..16, + module: None, + name: "Int", + arguments: [], + }, + ], +} diff --git a/crates/aiken-lang/src/parser/utils.rs b/crates/aiken-lang/src/parser/utils.rs index a4150279e..2e1716ce2 100644 --- a/crates/aiken-lang/src/parser/utils.rs +++ b/crates/aiken-lang/src/parser/utils.rs @@ -115,6 +115,27 @@ macro_rules! assert_expr { }; } +#[macro_export] +macro_rules! assert_annotation { + ($code:expr) => { + use chumsky::Parser; + + let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap(); + + let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len()), tokens.into_iter()); + + let result = $crate::parser::annotation().parse(stream).unwrap(); + + insta::with_settings!({ + description => concat!("Code:\n\n", indoc::indoc! { $code }), + prepend_module_to_snapshot => false, + omit_expression => true + }, { + insta::assert_debug_snapshot!(result); + }); + }; +} + #[macro_export] macro_rules! assert_module { ($code:expr) => { diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 8b9e6d593..e927ac2dc 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -1,31 +1,5 @@ use crate::assert_module; -#[test] -fn type_annotation_with_module_prefix() { - assert_module!( - r#" - use aiken - - pub fn go() -> aiken.Option { - False - } - "# - ); -} - -#[test] -fn test_fail() { - assert_module!( - r#" - !test invalid_inputs() { - expect True = False - - False - } - "# - ); -} - #[test] fn custom_type() { assert_module!( @@ -50,33 +24,6 @@ fn opaque_type() { ); } -#[test] -fn type_alias() { - assert_module!( - r#" - type Thing = Option - "# - ); -} - -#[test] -fn pub_type_alias() { - assert_module!( - r#" - pub type Me = Option - "# - ); -} - -#[test] -fn empty_function() { - assert_module!( - r#" - pub fn run() {} - "# - ); -} - #[test] fn expect() { assert_module!( @@ -113,26 +60,6 @@ fn pipeline() { ); } -#[test] -fn let_bindings() { - assert_module!( - r#" - pub fn wow(a: Int) { - let x = - a + 2 - |> add_one - |> add_one - - let thing = [ 1, 2, a ] - - let idk = thing - - y - } - "# - ); -} - #[test] fn block() { assert_module!( @@ -169,19 +96,6 @@ fn when() { ); } -#[test] -fn anonymous_function() { - assert_module!( - r#" - pub fn such() -> Int { - let add_one = fn (a: Int) -> Int { a + 1 } - - 2 |> add_one - } - "# - ); -} - #[test] fn field_access() { assert_module!( @@ -208,17 +122,6 @@ fn call() { ); } -#[test] -fn record_update() { - assert_module!( - r#" - fn update_name(user: User, name: ByteArray) -> User { - User { ..user, name: "Aiken", age } - } - "# - ); -} - #[test] fn record_create_labeled() { assert_module!( @@ -344,15 +247,6 @@ fn function_ambiguous_sequence() { ); } -#[test] -fn tuple_type_alias() { - assert_module!( - r#" - type RoyaltyToken = (PolicyId, AssetName) - "# - ); -} - #[test] fn first_class_binop() { assert_module!( diff --git a/crates/aiken-lang/src/tests/snapshots/anonymous_function.snap b/crates/aiken-lang/src/tests/snapshots/anonymous_function.snap deleted file mode 100644 index 93b8c216d..000000000 --- a/crates/aiken-lang/src/tests/snapshots/anonymous_function.snap +++ /dev/null @@ -1,109 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\npub fn such() -> Int {\n let add_one = fn (a: Int) -> Int { a + 1 }\n\n 2 |> add_one\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: Sequence { - location: 25..83, - expressions: [ - Assignment { - location: 25..67, - value: Fn { - location: 39..67, - fn_style: Plain, - arguments: [ - Arg { - arg_name: Named { - name: "a", - label: "a", - location: 43..44, - is_validator_param: false, - }, - location: 43..49, - annotation: Some( - Constructor { - location: 46..49, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: BinOp { - location: 60..65, - name: AddInt, - left: Var { - location: 60..61, - name: "a", - }, - right: Int { - location: 64..65, - value: "1", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - return_annotation: Some( - Constructor { - location: 54..57, - module: None, - name: "Int", - arguments: [], - }, - ), - }, - pattern: Var { - location: 29..36, - name: "add_one", - }, - kind: Let, - annotation: None, - }, - PipeLine { - expressions: [ - Int { - location: 71..72, - value: "2", - base: Decimal { - numeric_underscore: false, - }, - }, - Var { - location: 76..83, - name: "add_one", - }, - ], - one_liner: true, - }, - ], - }, - doc: None, - location: 0..20, - name: "such", - public: true, - return_annotation: Some( - Constructor { - location: 17..20, - module: None, - name: "Int", - arguments: [], - }, - ), - return_type: (), - end_position: 84, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/empty_function.snap b/crates/aiken-lang/src/tests/snapshots/empty_function.snap deleted file mode 100644 index b2c2873ad..000000000 --- a/crates/aiken-lang/src/tests/snapshots/empty_function.snap +++ /dev/null @@ -1,36 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\npub fn run() {}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: Trace { - kind: Todo, - location: 0..15, - then: ErrorTerm { - location: 0..15, - }, - text: String { - location: 0..15, - value: "aiken::todo", - }, - }, - doc: None, - location: 0..12, - name: "run", - public: true, - return_annotation: None, - return_type: (), - end_position: 14, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/fail.snap b/crates/aiken-lang/src/tests/snapshots/fail.snap deleted file mode 100644 index d983cfc72..000000000 --- a/crates/aiken-lang/src/tests/snapshots/fail.snap +++ /dev/null @@ -1,53 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\n!test invalid_inputs() {\n expect True = False\n\n False\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Test( - Function { - arguments: [], - body: Sequence { - location: 27..55, - expressions: [ - Assignment { - location: 27..46, - value: Var { - location: 41..46, - name: "False", - }, - pattern: Constructor { - is_record: false, - location: 34..38, - name: "True", - arguments: [], - module: None, - constructor: (), - with_spread: false, - tipo: (), - }, - kind: Expect, - annotation: None, - }, - Var { - location: 50..55, - name: "False", - }, - ], - }, - doc: None, - location: 0..22, - name: "invalid_inputs", - public: false, - return_annotation: None, - return_type: (), - end_position: 56, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/let_bindings.snap b/crates/aiken-lang/src/tests/snapshots/let_bindings.snap deleted file mode 100644 index f3ee30da6..000000000 --- a/crates/aiken-lang/src/tests/snapshots/let_bindings.snap +++ /dev/null @@ -1,136 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\npub fn wow(a: Int) {\n let x =\n a + 2\n |> add_one\n |> add_one\n\n let thing = [ 1, 2, a ]\n\n let idk = thing\n\n y\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [ - Arg { - arg_name: Named { - name: "a", - label: "a", - location: 11..12, - is_validator_param: false, - }, - location: 11..17, - annotation: Some( - Constructor { - location: 14..17, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: Sequence { - location: 23..121, - expressions: [ - Assignment { - location: 23..70, - value: PipeLine { - expressions: [ - BinOp { - location: 35..40, - name: AddInt, - left: Var { - location: 35..36, - name: "a", - }, - right: Int { - location: 39..40, - value: "2", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - Var { - location: 48..55, - name: "add_one", - }, - Var { - location: 63..70, - name: "add_one", - }, - ], - one_liner: false, - }, - pattern: Var { - location: 27..28, - name: "x", - }, - kind: Let, - annotation: None, - }, - Assignment { - location: 74..97, - value: List { - location: 86..97, - elements: [ - Int { - location: 88..89, - value: "1", - base: Decimal { - numeric_underscore: false, - }, - }, - Int { - location: 91..92, - value: "2", - base: Decimal { - numeric_underscore: false, - }, - }, - Var { - location: 94..95, - name: "a", - }, - ], - tail: None, - }, - pattern: Var { - location: 78..83, - name: "thing", - }, - kind: Let, - annotation: None, - }, - Assignment { - location: 101..116, - value: Var { - location: 111..116, - name: "thing", - }, - pattern: Var { - location: 105..108, - name: "idk", - }, - kind: Let, - annotation: None, - }, - Var { - location: 120..121, - name: "y", - }, - ], - }, - doc: None, - location: 0..18, - name: "wow", - public: true, - return_annotation: None, - return_type: (), - end_position: 122, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/pub_type_alias.snap b/crates/aiken-lang/src/tests/snapshots/pub_type_alias.snap deleted file mode 100644 index b727d5a1a..000000000 --- a/crates/aiken-lang/src/tests/snapshots/pub_type_alias.snap +++ /dev/null @@ -1,35 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\npub type Me = Option\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - TypeAlias( - TypeAlias { - alias: "Me", - annotation: Constructor { - location: 14..28, - module: None, - name: "Option", - arguments: [ - Constructor { - location: 21..27, - module: None, - name: "String", - arguments: [], - }, - ], - }, - doc: None, - location: 0..28, - parameters: [], - public: true, - tipo: (), - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/record_update.snap b/crates/aiken-lang/src/tests/snapshots/record_update.snap deleted file mode 100644 index ce8ccca7f..000000000 --- a/crates/aiken-lang/src/tests/snapshots/record_update.snap +++ /dev/null @@ -1,108 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn update_name(user: User, name: ByteArray) -> User {\n User { ..user, name: \"Aiken\", age }\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [ - Arg { - arg_name: Named { - name: "user", - label: "user", - location: 15..19, - is_validator_param: false, - }, - location: 15..25, - annotation: Some( - Constructor { - location: 21..25, - module: None, - name: "User", - arguments: [], - }, - ), - tipo: (), - }, - Arg { - arg_name: Named { - name: "name", - label: "name", - location: 27..31, - is_validator_param: false, - }, - location: 27..42, - annotation: Some( - Constructor { - location: 33..42, - module: None, - name: "ByteArray", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: RecordUpdate { - location: 56..91, - constructor: Var { - location: 56..60, - name: "User", - }, - spread: RecordUpdateSpread { - base: Var { - location: 65..69, - name: "user", - }, - location: 63..69, - }, - arguments: [ - UntypedRecordUpdateArg { - label: "name", - location: 71..84, - value: ByteArray { - location: 77..84, - bytes: [ - 65, - 105, - 107, - 101, - 110, - ], - preferred_format: Utf8String, - }, - }, - UntypedRecordUpdateArg { - label: "age", - location: 86..89, - value: Var { - location: 86..89, - name: "age", - }, - }, - ], - }, - doc: None, - location: 0..51, - name: "update_name", - public: false, - return_annotation: Some( - Constructor { - location: 47..51, - module: None, - name: "User", - arguments: [], - }, - ), - return_type: (), - end_position: 92, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/tuple_type_alias.snap b/crates/aiken-lang/src/tests/snapshots/tuple_type_alias.snap deleted file mode 100644 index 46cf6c7cc..000000000 --- a/crates/aiken-lang/src/tests/snapshots/tuple_type_alias.snap +++ /dev/null @@ -1,39 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\ntype RoyaltyToken = (PolicyId, AssetName)\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - TypeAlias( - TypeAlias { - alias: "RoyaltyToken", - annotation: Tuple { - location: 20..41, - elems: [ - Constructor { - location: 21..29, - module: None, - name: "PolicyId", - arguments: [], - }, - Constructor { - location: 31..40, - module: None, - name: "AssetName", - arguments: [], - }, - ], - }, - doc: None, - location: 0..41, - parameters: [], - public: false, - tipo: (), - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/type_alias.snap b/crates/aiken-lang/src/tests/snapshots/type_alias.snap deleted file mode 100644 index e86befd93..000000000 --- a/crates/aiken-lang/src/tests/snapshots/type_alias.snap +++ /dev/null @@ -1,35 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\ntype Thing = Option\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - TypeAlias( - TypeAlias { - alias: "Thing", - annotation: Constructor { - location: 13..24, - module: None, - name: "Option", - arguments: [ - Constructor { - location: 20..23, - module: None, - name: "Int", - arguments: [], - }, - ], - }, - doc: None, - location: 0..24, - parameters: [], - public: false, - tipo: (), - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/type_annotation_with_module_prefix.snap b/crates/aiken-lang/src/tests/snapshots/type_annotation_with_module_prefix.snap deleted file mode 100644 index bd0a32639..000000000 --- a/crates/aiken-lang/src/tests/snapshots/type_annotation_with_module_prefix.snap +++ /dev/null @@ -1,56 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nuse aiken\n\npub fn go() -> aiken.Option {\n False\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Use( - Use { - as_name: None, - location: 0..9, - module: [ - "aiken", - ], - package: (), - unqualified: [], - }, - ), - Fn( - Function { - arguments: [], - body: Var { - location: 48..53, - name: "False", - }, - doc: None, - location: 11..43, - name: "go", - public: true, - return_annotation: Some( - Constructor { - location: 26..43, - module: Some( - "aiken", - ), - name: "Option", - arguments: [ - Constructor { - location: 39..42, - module: None, - name: "Int", - arguments: [], - }, - ], - }, - ), - return_type: (), - end_position: 54, - can_error: true, - }, - ), - ], - kind: Validator, -} From 8a6c81493c0fad1e0a42578c55359e9ac68b1a23 Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 3 Jul 2023 16:48:52 -0400 Subject: [PATCH 21/31] test(parser): record create --- crates/aiken-lang/src/parser/expr/record.rs | 20 +++++ .../expr/snapshots/record_create_labeled.snap | 53 +++++++++++++ ...cord_create_labeled_with_field_access.snap | 57 ++++++++++++++ .../snapshots/record_create_unlabeled.snap | 36 +++++++++ crates/aiken-lang/src/tests/parser.rs | 33 -------- .../snapshots/cargo_create_unlabeled.snap | 57 -------------- .../snapshots/record_create_labeled.snap | 74 ------------------ ...cord_create_labeled_with_field_access.snap | 78 ------------------- 8 files changed, 166 insertions(+), 242 deletions(-) create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/record_create_labeled.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/record_create_labeled_with_field_access.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/record_create_unlabeled.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/cargo_create_unlabeled.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/record_create_labeled.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/record_create_labeled_with_field_access.snap diff --git a/crates/aiken-lang/src/parser/expr/record.rs b/crates/aiken-lang/src/parser/expr/record.rs index d27184f76..e8473a125 100644 --- a/crates/aiken-lang/src/parser/expr/record.rs +++ b/crates/aiken-lang/src/parser/expr/record.rs @@ -150,3 +150,23 @@ pub fn parser( } }) } + +#[cfg(test)] +mod tests { + use crate::assert_expr; + + #[test] + fn record_create_labeled() { + assert_expr!(r#"User { name: "Aiken", age, thing: 2 }"#); + } + + #[test] + fn record_create_labeled_with_field_access() { + assert_expr!(r#"some_module.User { name: "Aiken", age, thing: 2 }"#); + } + + #[test] + fn record_create_unlabeled() { + assert_expr!(r#"some_module.Thing(1, a)"#); + } +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/record_create_labeled.snap b/crates/aiken-lang/src/parser/expr/snapshots/record_create_labeled.snap new file mode 100644 index 000000000..13c2c4337 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/record_create_labeled.snap @@ -0,0 +1,53 @@ +--- +source: crates/aiken-lang/src/parser/expr/record.rs +description: "Code:\n\nUser { name: \"Aiken\", age, thing: 2 }" +--- +Call { + arguments: [ + CallArg { + label: Some( + "name", + ), + location: 7..20, + value: ByteArray { + location: 13..20, + bytes: [ + 65, + 105, + 107, + 101, + 110, + ], + preferred_format: Utf8String, + }, + }, + CallArg { + label: Some( + "age", + ), + location: 22..25, + value: Var { + location: 22..25, + name: "age", + }, + }, + CallArg { + label: Some( + "thing", + ), + location: 27..35, + value: Int { + location: 34..35, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + fun: Var { + location: 0..4, + name: "User", + }, + location: 0..37, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/record_create_labeled_with_field_access.snap b/crates/aiken-lang/src/parser/expr/snapshots/record_create_labeled_with_field_access.snap new file mode 100644 index 000000000..f1f46e9bf --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/record_create_labeled_with_field_access.snap @@ -0,0 +1,57 @@ +--- +source: crates/aiken-lang/src/parser/expr/record.rs +description: "Code:\n\nsome_module.User { name: \"Aiken\", age, thing: 2 }" +--- +Call { + arguments: [ + CallArg { + label: Some( + "name", + ), + location: 19..32, + value: ByteArray { + location: 25..32, + bytes: [ + 65, + 105, + 107, + 101, + 110, + ], + preferred_format: Utf8String, + }, + }, + CallArg { + label: Some( + "age", + ), + location: 34..37, + value: Var { + location: 34..37, + name: "age", + }, + }, + CallArg { + label: Some( + "thing", + ), + location: 39..47, + value: Int { + location: 46..47, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + fun: FieldAccess { + location: 0..16, + label: "User", + container: Var { + location: 0..11, + name: "some_module", + }, + }, + location: 0..49, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/record_create_unlabeled.snap b/crates/aiken-lang/src/parser/expr/snapshots/record_create_unlabeled.snap new file mode 100644 index 000000000..2f9f629c5 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/record_create_unlabeled.snap @@ -0,0 +1,36 @@ +--- +source: crates/aiken-lang/src/parser/expr/record.rs +description: "Code:\n\nsome_module.Thing(1, a)" +--- +Call { + arguments: [ + CallArg { + label: None, + location: 18..19, + value: Int { + location: 18..19, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + CallArg { + label: None, + location: 21..22, + value: Var { + location: 21..22, + name: "a", + }, + }, + ], + fun: FieldAccess { + location: 0..17, + label: "Thing", + container: Var { + location: 0..11, + name: "some_module", + }, + }, + location: 0..23, +} diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index e927ac2dc..54ffec992 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -122,39 +122,6 @@ fn call() { ); } -#[test] -fn record_create_labeled() { - assert_module!( - r#" - fn create() { - User { name: "Aiken", age, thing: 2 } - } - "# - ); -} - -#[test] -fn record_create_labeled_with_field_access() { - assert_module!( - r#" - fn create() { - some_module.User { name: "Aiken", age, thing: 2 } - } - "# - ); -} - -#[test] -fn cargo_create_unlabeled() { - assert_module!( - r#" - fn create() { - some_module.Thing(1, a) - } - "# - ); -} - #[test] fn parse_tuple() { assert_module!( diff --git a/crates/aiken-lang/src/tests/snapshots/cargo_create_unlabeled.snap b/crates/aiken-lang/src/tests/snapshots/cargo_create_unlabeled.snap deleted file mode 100644 index 4aa1c2ddb..000000000 --- a/crates/aiken-lang/src/tests/snapshots/cargo_create_unlabeled.snap +++ /dev/null @@ -1,57 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn create() {\n some_module.Thing(1, a)\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: Call { - arguments: [ - CallArg { - label: None, - location: 34..35, - value: Int { - location: 34..35, - value: "1", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - CallArg { - label: None, - location: 37..38, - value: Var { - location: 37..38, - name: "a", - }, - }, - ], - fun: FieldAccess { - location: 16..33, - label: "Thing", - container: Var { - location: 16..27, - name: "some_module", - }, - }, - location: 16..39, - }, - doc: None, - location: 0..11, - name: "create", - public: false, - return_annotation: None, - return_type: (), - end_position: 40, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/record_create_labeled.snap b/crates/aiken-lang/src/tests/snapshots/record_create_labeled.snap deleted file mode 100644 index 1661a9c09..000000000 --- a/crates/aiken-lang/src/tests/snapshots/record_create_labeled.snap +++ /dev/null @@ -1,74 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn create() {\n User { name: \"Aiken\", age, thing: 2 }\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: Call { - arguments: [ - CallArg { - label: Some( - "name", - ), - location: 23..36, - value: ByteArray { - location: 29..36, - bytes: [ - 65, - 105, - 107, - 101, - 110, - ], - preferred_format: Utf8String, - }, - }, - CallArg { - label: Some( - "age", - ), - location: 38..41, - value: Var { - location: 38..41, - name: "age", - }, - }, - CallArg { - label: Some( - "thing", - ), - location: 43..51, - value: Int { - location: 50..51, - value: "2", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - ], - fun: Var { - location: 16..20, - name: "User", - }, - location: 16..53, - }, - doc: None, - location: 0..11, - name: "create", - public: false, - return_annotation: None, - return_type: (), - end_position: 54, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/record_create_labeled_with_field_access.snap b/crates/aiken-lang/src/tests/snapshots/record_create_labeled_with_field_access.snap deleted file mode 100644 index 36383a8b5..000000000 --- a/crates/aiken-lang/src/tests/snapshots/record_create_labeled_with_field_access.snap +++ /dev/null @@ -1,78 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn create() {\n some_module.User { name: \"Aiken\", age, thing: 2 }\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: Call { - arguments: [ - CallArg { - label: Some( - "name", - ), - location: 35..48, - value: ByteArray { - location: 41..48, - bytes: [ - 65, - 105, - 107, - 101, - 110, - ], - preferred_format: Utf8String, - }, - }, - CallArg { - label: Some( - "age", - ), - location: 50..53, - value: Var { - location: 50..53, - name: "age", - }, - }, - CallArg { - label: Some( - "thing", - ), - location: 55..63, - value: Int { - location: 62..63, - value: "2", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - ], - fun: FieldAccess { - location: 16..32, - label: "User", - container: Var { - location: 16..27, - name: "some_module", - }, - }, - location: 16..65, - }, - doc: None, - location: 0..11, - name: "create", - public: false, - return_annotation: None, - return_type: (), - end_position: 66, - can_error: true, - }, - ), - ], - kind: Validator, -} From b25db429beda0d3b1c915f8bb0aa65c60e4b30f5 Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 3 Jul 2023 16:55:09 -0400 Subject: [PATCH 22/31] test(parser): anon binop and ambiguous sequence --- crates/aiken-lang/src/parser.rs | 26 + .../src/parser/expr/anonymous_binop.rs | 25 + .../expr/snapshots/first_class_binop.snap | 1190 ++++++++++++++++ .../function_ambiguous_sequence.snap | 2 +- crates/aiken-lang/src/tests/parser.rs | 58 - .../tests/snapshots/first_class_binop.snap | 1211 ----------------- .../src/tests/snapshots/function_def.snap | 36 - 7 files changed, 1242 insertions(+), 1306 deletions(-) create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/first_class_binop.snap rename crates/aiken-lang/src/{tests => }/snapshots/function_ambiguous_sequence.snap (99%) delete mode 100644 crates/aiken-lang/src/tests/snapshots/first_class_binop.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/function_def.snap diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index f73b950c3..7914487d6 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -58,4 +58,30 @@ mod tests { // more comments"# ); } + + #[test] + fn function_ambiguous_sequence() { + assert_module!( + r#" + fn foo_1() { + let a = bar + (40) + } + + fn foo_2() { + let a = bar + {40} + } + + fn foo_3() { + let a = (40+2) + } + + fn foo_4() { + let a = bar(42) + (a + 14) * 42 + } + "# + ); + } } diff --git a/crates/aiken-lang/src/parser/expr/anonymous_binop.rs b/crates/aiken-lang/src/parser/expr/anonymous_binop.rs index 7eaa8ae40..e55da029a 100644 --- a/crates/aiken-lang/src/parser/expr/anonymous_binop.rs +++ b/crates/aiken-lang/src/parser/expr/anonymous_binop.rs @@ -87,3 +87,28 @@ pub fn parser() -> impl Parser { } }) } + +#[cfg(test)] +mod tests { + use crate::assert_expr; + + #[test] + fn first_class_binop() { + assert_expr!( + r#" + compare_with(a, >, b) + compare_with(a, >=, b) + compare_with(a, <, b) + compare_with(a, <=, b) + compare_with(a, ==, b) + compare_with(a, !=, b) + combine_with(a, &&, b) + combine_with(a, ||, b) + compute_with(a, +, b) + compute_with(a, -, b) + compute_with(a, /, b) + compute_with(a, *, b) + compute_with(a, %, b)"# + ); + } +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/first_class_binop.snap b/crates/aiken-lang/src/parser/expr/snapshots/first_class_binop.snap new file mode 100644 index 000000000..45a675bd0 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/first_class_binop.snap @@ -0,0 +1,1190 @@ +--- +source: crates/aiken-lang/src/parser/expr/anonymous_binop.rs +description: "Code:\n\ncompare_with(a, >, b)\ncompare_with(a, >=, b)\ncompare_with(a, <, b)\ncompare_with(a, <=, b)\ncompare_with(a, ==, b)\ncompare_with(a, !=, b)\ncombine_with(a, &&, b)\ncombine_with(a, ||, b)\ncompute_with(a, +, b)\ncompute_with(a, -, b)\ncompute_with(a, /, b)\ncompute_with(a, *, b)\ncompute_with(a, %, b)" +--- +Sequence { + location: 0..291, + expressions: [ + Call { + arguments: [ + CallArg { + label: None, + location: 13..14, + value: Var { + location: 13..14, + name: "a", + }, + }, + CallArg { + label: None, + location: 16..17, + value: Fn { + location: 16..17, + fn_style: BinOp( + GtInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 16..17, + is_validator_param: false, + }, + location: 16..17, + annotation: Some( + Constructor { + location: 16..17, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 16..17, + is_validator_param: false, + }, + location: 16..17, + annotation: Some( + Constructor { + location: 16..17, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 16..17, + name: GtInt, + left: Var { + location: 16..17, + name: "left", + }, + right: Var { + location: 16..17, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 16..17, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 19..20, + value: Var { + location: 19..20, + name: "b", + }, + }, + ], + fun: Var { + location: 0..12, + name: "compare_with", + }, + location: 0..21, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 35..36, + value: Var { + location: 35..36, + name: "a", + }, + }, + CallArg { + label: None, + location: 38..40, + value: Fn { + location: 38..40, + fn_style: BinOp( + GtEqInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 38..40, + is_validator_param: false, + }, + location: 38..40, + annotation: Some( + Constructor { + location: 38..40, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 38..40, + is_validator_param: false, + }, + location: 38..40, + annotation: Some( + Constructor { + location: 38..40, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 38..40, + name: GtEqInt, + left: Var { + location: 38..40, + name: "left", + }, + right: Var { + location: 38..40, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 38..40, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 42..43, + value: Var { + location: 42..43, + name: "b", + }, + }, + ], + fun: Var { + location: 22..34, + name: "compare_with", + }, + location: 22..44, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 58..59, + value: Var { + location: 58..59, + name: "a", + }, + }, + CallArg { + label: None, + location: 61..62, + value: Fn { + location: 61..62, + fn_style: BinOp( + LtInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 61..62, + is_validator_param: false, + }, + location: 61..62, + annotation: Some( + Constructor { + location: 61..62, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 61..62, + is_validator_param: false, + }, + location: 61..62, + annotation: Some( + Constructor { + location: 61..62, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 61..62, + name: LtInt, + left: Var { + location: 61..62, + name: "left", + }, + right: Var { + location: 61..62, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 61..62, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 64..65, + value: Var { + location: 64..65, + name: "b", + }, + }, + ], + fun: Var { + location: 45..57, + name: "compare_with", + }, + location: 45..66, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 80..81, + value: Var { + location: 80..81, + name: "a", + }, + }, + CallArg { + label: None, + location: 83..85, + value: Fn { + location: 83..85, + fn_style: BinOp( + LtEqInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 83..85, + is_validator_param: false, + }, + location: 83..85, + annotation: Some( + Constructor { + location: 83..85, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 83..85, + is_validator_param: false, + }, + location: 83..85, + annotation: Some( + Constructor { + location: 83..85, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 83..85, + name: LtEqInt, + left: Var { + location: 83..85, + name: "left", + }, + right: Var { + location: 83..85, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 83..85, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 87..88, + value: Var { + location: 87..88, + name: "b", + }, + }, + ], + fun: Var { + location: 67..79, + name: "compare_with", + }, + location: 67..89, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 103..104, + value: Var { + location: 103..104, + name: "a", + }, + }, + CallArg { + label: None, + location: 106..108, + value: Fn { + location: 106..108, + fn_style: BinOp( + Eq, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 106..108, + is_validator_param: false, + }, + location: 106..108, + annotation: None, + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 106..108, + is_validator_param: false, + }, + location: 106..108, + annotation: None, + tipo: (), + }, + ], + body: BinOp { + location: 106..108, + name: Eq, + left: Var { + location: 106..108, + name: "left", + }, + right: Var { + location: 106..108, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 106..108, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 110..111, + value: Var { + location: 110..111, + name: "b", + }, + }, + ], + fun: Var { + location: 90..102, + name: "compare_with", + }, + location: 90..112, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 126..127, + value: Var { + location: 126..127, + name: "a", + }, + }, + CallArg { + label: None, + location: 129..131, + value: Fn { + location: 129..131, + fn_style: BinOp( + NotEq, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 129..131, + is_validator_param: false, + }, + location: 129..131, + annotation: None, + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 129..131, + is_validator_param: false, + }, + location: 129..131, + annotation: None, + tipo: (), + }, + ], + body: BinOp { + location: 129..131, + name: NotEq, + left: Var { + location: 129..131, + name: "left", + }, + right: Var { + location: 129..131, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 129..131, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 133..134, + value: Var { + location: 133..134, + name: "b", + }, + }, + ], + fun: Var { + location: 113..125, + name: "compare_with", + }, + location: 113..135, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 149..150, + value: Var { + location: 149..150, + name: "a", + }, + }, + CallArg { + label: None, + location: 152..154, + value: Fn { + location: 152..154, + fn_style: BinOp( + And, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 152..154, + is_validator_param: false, + }, + location: 152..154, + annotation: Some( + Constructor { + location: 152..154, + module: None, + name: "Bool", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 152..154, + is_validator_param: false, + }, + location: 152..154, + annotation: Some( + Constructor { + location: 152..154, + module: None, + name: "Bool", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 152..154, + name: And, + left: Var { + location: 152..154, + name: "left", + }, + right: Var { + location: 152..154, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 152..154, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 156..157, + value: Var { + location: 156..157, + name: "b", + }, + }, + ], + fun: Var { + location: 136..148, + name: "combine_with", + }, + location: 136..158, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 172..173, + value: Var { + location: 172..173, + name: "a", + }, + }, + CallArg { + label: None, + location: 175..177, + value: Fn { + location: 175..177, + fn_style: BinOp( + Or, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 175..177, + is_validator_param: false, + }, + location: 175..177, + annotation: Some( + Constructor { + location: 175..177, + module: None, + name: "Bool", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 175..177, + is_validator_param: false, + }, + location: 175..177, + annotation: Some( + Constructor { + location: 175..177, + module: None, + name: "Bool", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 175..177, + name: Or, + left: Var { + location: 175..177, + name: "left", + }, + right: Var { + location: 175..177, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 175..177, + module: None, + name: "Bool", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 179..180, + value: Var { + location: 179..180, + name: "b", + }, + }, + ], + fun: Var { + location: 159..171, + name: "combine_with", + }, + location: 159..181, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 195..196, + value: Var { + location: 195..196, + name: "a", + }, + }, + CallArg { + label: None, + location: 198..199, + value: Fn { + location: 198..199, + fn_style: BinOp( + AddInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 198..199, + is_validator_param: false, + }, + location: 198..199, + annotation: Some( + Constructor { + location: 198..199, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 198..199, + is_validator_param: false, + }, + location: 198..199, + annotation: Some( + Constructor { + location: 198..199, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 198..199, + name: AddInt, + left: Var { + location: 198..199, + name: "left", + }, + right: Var { + location: 198..199, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 198..199, + module: None, + name: "Int", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 201..202, + value: Var { + location: 201..202, + name: "b", + }, + }, + ], + fun: Var { + location: 182..194, + name: "compute_with", + }, + location: 182..203, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 217..218, + value: Var { + location: 217..218, + name: "a", + }, + }, + CallArg { + label: None, + location: 220..221, + value: Fn { + location: 220..221, + fn_style: BinOp( + SubInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 220..221, + is_validator_param: false, + }, + location: 220..221, + annotation: Some( + Constructor { + location: 220..221, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 220..221, + is_validator_param: false, + }, + location: 220..221, + annotation: Some( + Constructor { + location: 220..221, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 220..221, + name: SubInt, + left: Var { + location: 220..221, + name: "left", + }, + right: Var { + location: 220..221, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 220..221, + module: None, + name: "Int", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 223..224, + value: Var { + location: 223..224, + name: "b", + }, + }, + ], + fun: Var { + location: 204..216, + name: "compute_with", + }, + location: 204..225, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 239..240, + value: Var { + location: 239..240, + name: "a", + }, + }, + CallArg { + label: None, + location: 242..243, + value: Fn { + location: 242..243, + fn_style: BinOp( + DivInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 242..243, + is_validator_param: false, + }, + location: 242..243, + annotation: Some( + Constructor { + location: 242..243, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 242..243, + is_validator_param: false, + }, + location: 242..243, + annotation: Some( + Constructor { + location: 242..243, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 242..243, + name: DivInt, + left: Var { + location: 242..243, + name: "left", + }, + right: Var { + location: 242..243, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 242..243, + module: None, + name: "Int", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 245..246, + value: Var { + location: 245..246, + name: "b", + }, + }, + ], + fun: Var { + location: 226..238, + name: "compute_with", + }, + location: 226..247, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 261..262, + value: Var { + location: 261..262, + name: "a", + }, + }, + CallArg { + label: None, + location: 264..265, + value: Fn { + location: 264..265, + fn_style: BinOp( + MultInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 264..265, + is_validator_param: false, + }, + location: 264..265, + annotation: Some( + Constructor { + location: 264..265, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 264..265, + is_validator_param: false, + }, + location: 264..265, + annotation: Some( + Constructor { + location: 264..265, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 264..265, + name: MultInt, + left: Var { + location: 264..265, + name: "left", + }, + right: Var { + location: 264..265, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 264..265, + module: None, + name: "Int", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 267..268, + value: Var { + location: 267..268, + name: "b", + }, + }, + ], + fun: Var { + location: 248..260, + name: "compute_with", + }, + location: 248..269, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 283..284, + value: Var { + location: 283..284, + name: "a", + }, + }, + CallArg { + label: None, + location: 286..287, + value: Fn { + location: 286..287, + fn_style: BinOp( + ModInt, + ), + arguments: [ + Arg { + arg_name: Named { + name: "left", + label: "left", + location: 286..287, + is_validator_param: false, + }, + location: 286..287, + annotation: Some( + Constructor { + location: 286..287, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right", + label: "right", + location: 286..287, + is_validator_param: false, + }, + location: 286..287, + annotation: Some( + Constructor { + location: 286..287, + module: None, + name: "Int", + arguments: [], + }, + ), + tipo: (), + }, + ], + body: BinOp { + location: 286..287, + name: ModInt, + left: Var { + location: 286..287, + name: "left", + }, + right: Var { + location: 286..287, + name: "right", + }, + }, + return_annotation: Some( + Constructor { + location: 286..287, + module: None, + name: "Int", + arguments: [], + }, + ), + }, + }, + CallArg { + label: None, + location: 289..290, + value: Var { + location: 289..290, + name: "b", + }, + }, + ], + fun: Var { + location: 270..282, + name: "compute_with", + }, + location: 270..291, + }, + ], +} diff --git a/crates/aiken-lang/src/tests/snapshots/function_ambiguous_sequence.snap b/crates/aiken-lang/src/snapshots/function_ambiguous_sequence.snap similarity index 99% rename from crates/aiken-lang/src/tests/snapshots/function_ambiguous_sequence.snap rename to crates/aiken-lang/src/snapshots/function_ambiguous_sequence.snap index 487295126..0f6292b05 100644 --- a/crates/aiken-lang/src/tests/snapshots/function_ambiguous_sequence.snap +++ b/crates/aiken-lang/src/snapshots/function_ambiguous_sequence.snap @@ -1,5 +1,5 @@ --- -source: crates/aiken-lang/src/tests/parser.rs +source: crates/aiken-lang/src/parser.rs description: "Code:\n\nfn foo_1() {\n let a = bar\n (40)\n}\n\nfn foo_2() {\n let a = bar\n {40}\n}\n\nfn foo_3() {\n let a = (40+2)\n}\n\nfn foo_4() {\n let a = bar(42)\n (a + 14) * 42\n}\n" --- Module { diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 54ffec992..7bc5c6c26 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -168,15 +168,6 @@ fn base16_bytearray_literals() { ); } -#[test] -fn function_def() { - assert_module!( - r#" - fn foo() {} - "# - ); -} - #[test] fn function_invoke() { assert_module!( @@ -188,55 +179,6 @@ fn function_invoke() { ); } -#[test] -fn function_ambiguous_sequence() { - assert_module!( - r#" - fn foo_1() { - let a = bar - (40) - } - - fn foo_2() { - let a = bar - {40} - } - - fn foo_3() { - let a = (40+2) - } - - fn foo_4() { - let a = bar(42) - (a + 14) * 42 - } - "# - ); -} - -#[test] -fn first_class_binop() { - assert_module!( - r#" - fn foo() { - compare_with(a, >, b) - compare_with(a, >=, b) - compare_with(a, <, b) - compare_with(a, <=, b) - compare_with(a, ==, b) - compare_with(a, !=, b) - combine_with(a, &&, b) - combine_with(a, ||, b) - compute_with(a, +, b) - compute_with(a, -, b) - compute_with(a, /, b) - compute_with(a, *, b) - compute_with(a, %, b) - } - "# - ); -} - #[test] fn parse_unicode_offset_1() { assert_module!( diff --git a/crates/aiken-lang/src/tests/snapshots/first_class_binop.snap b/crates/aiken-lang/src/tests/snapshots/first_class_binop.snap deleted file mode 100644 index 679c61cc3..000000000 --- a/crates/aiken-lang/src/tests/snapshots/first_class_binop.snap +++ /dev/null @@ -1,1211 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn foo() {\n compare_with(a, >, b)\n compare_with(a, >=, b)\n compare_with(a, <, b)\n compare_with(a, <=, b)\n compare_with(a, ==, b)\n compare_with(a, !=, b)\n combine_with(a, &&, b)\n combine_with(a, ||, b)\n compute_with(a, +, b)\n compute_with(a, -, b)\n compute_with(a, /, b)\n compute_with(a, *, b)\n compute_with(a, %, b)\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: Sequence { - location: 13..328, - expressions: [ - Call { - arguments: [ - CallArg { - label: None, - location: 26..27, - value: Var { - location: 26..27, - name: "a", - }, - }, - CallArg { - label: None, - location: 29..30, - value: Fn { - location: 29..30, - fn_style: BinOp( - GtInt, - ), - arguments: [ - Arg { - arg_name: Named { - name: "left", - label: "left", - location: 29..30, - is_validator_param: false, - }, - location: 29..30, - annotation: Some( - Constructor { - location: 29..30, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right", - label: "right", - location: 29..30, - is_validator_param: false, - }, - location: 29..30, - annotation: Some( - Constructor { - location: 29..30, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: BinOp { - location: 29..30, - name: GtInt, - left: Var { - location: 29..30, - name: "left", - }, - right: Var { - location: 29..30, - name: "right", - }, - }, - return_annotation: Some( - Constructor { - location: 29..30, - module: None, - name: "Bool", - arguments: [], - }, - ), - }, - }, - CallArg { - label: None, - location: 32..33, - value: Var { - location: 32..33, - name: "b", - }, - }, - ], - fun: Var { - location: 13..25, - name: "compare_with", - }, - location: 13..34, - }, - Call { - arguments: [ - CallArg { - label: None, - location: 50..51, - value: Var { - location: 50..51, - name: "a", - }, - }, - CallArg { - label: None, - location: 53..55, - value: Fn { - location: 53..55, - fn_style: BinOp( - GtEqInt, - ), - arguments: [ - Arg { - arg_name: Named { - name: "left", - label: "left", - location: 53..55, - is_validator_param: false, - }, - location: 53..55, - annotation: Some( - Constructor { - location: 53..55, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right", - label: "right", - location: 53..55, - is_validator_param: false, - }, - location: 53..55, - annotation: Some( - Constructor { - location: 53..55, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: BinOp { - location: 53..55, - name: GtEqInt, - left: Var { - location: 53..55, - name: "left", - }, - right: Var { - location: 53..55, - name: "right", - }, - }, - return_annotation: Some( - Constructor { - location: 53..55, - module: None, - name: "Bool", - arguments: [], - }, - ), - }, - }, - CallArg { - label: None, - location: 57..58, - value: Var { - location: 57..58, - name: "b", - }, - }, - ], - fun: Var { - location: 37..49, - name: "compare_with", - }, - location: 37..59, - }, - Call { - arguments: [ - CallArg { - label: None, - location: 75..76, - value: Var { - location: 75..76, - name: "a", - }, - }, - CallArg { - label: None, - location: 78..79, - value: Fn { - location: 78..79, - fn_style: BinOp( - LtInt, - ), - arguments: [ - Arg { - arg_name: Named { - name: "left", - label: "left", - location: 78..79, - is_validator_param: false, - }, - location: 78..79, - annotation: Some( - Constructor { - location: 78..79, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right", - label: "right", - location: 78..79, - is_validator_param: false, - }, - location: 78..79, - annotation: Some( - Constructor { - location: 78..79, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: BinOp { - location: 78..79, - name: LtInt, - left: Var { - location: 78..79, - name: "left", - }, - right: Var { - location: 78..79, - name: "right", - }, - }, - return_annotation: Some( - Constructor { - location: 78..79, - module: None, - name: "Bool", - arguments: [], - }, - ), - }, - }, - CallArg { - label: None, - location: 81..82, - value: Var { - location: 81..82, - name: "b", - }, - }, - ], - fun: Var { - location: 62..74, - name: "compare_with", - }, - location: 62..83, - }, - Call { - arguments: [ - CallArg { - label: None, - location: 99..100, - value: Var { - location: 99..100, - name: "a", - }, - }, - CallArg { - label: None, - location: 102..104, - value: Fn { - location: 102..104, - fn_style: BinOp( - LtEqInt, - ), - arguments: [ - Arg { - arg_name: Named { - name: "left", - label: "left", - location: 102..104, - is_validator_param: false, - }, - location: 102..104, - annotation: Some( - Constructor { - location: 102..104, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right", - label: "right", - location: 102..104, - is_validator_param: false, - }, - location: 102..104, - annotation: Some( - Constructor { - location: 102..104, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: BinOp { - location: 102..104, - name: LtEqInt, - left: Var { - location: 102..104, - name: "left", - }, - right: Var { - location: 102..104, - name: "right", - }, - }, - return_annotation: Some( - Constructor { - location: 102..104, - module: None, - name: "Bool", - arguments: [], - }, - ), - }, - }, - CallArg { - label: None, - location: 106..107, - value: Var { - location: 106..107, - name: "b", - }, - }, - ], - fun: Var { - location: 86..98, - name: "compare_with", - }, - location: 86..108, - }, - Call { - arguments: [ - CallArg { - label: None, - location: 124..125, - value: Var { - location: 124..125, - name: "a", - }, - }, - CallArg { - label: None, - location: 127..129, - value: Fn { - location: 127..129, - fn_style: BinOp( - Eq, - ), - arguments: [ - Arg { - arg_name: Named { - name: "left", - label: "left", - location: 127..129, - is_validator_param: false, - }, - location: 127..129, - annotation: None, - tipo: (), - }, - Arg { - arg_name: Named { - name: "right", - label: "right", - location: 127..129, - is_validator_param: false, - }, - location: 127..129, - annotation: None, - tipo: (), - }, - ], - body: BinOp { - location: 127..129, - name: Eq, - left: Var { - location: 127..129, - name: "left", - }, - right: Var { - location: 127..129, - name: "right", - }, - }, - return_annotation: Some( - Constructor { - location: 127..129, - module: None, - name: "Bool", - arguments: [], - }, - ), - }, - }, - CallArg { - label: None, - location: 131..132, - value: Var { - location: 131..132, - name: "b", - }, - }, - ], - fun: Var { - location: 111..123, - name: "compare_with", - }, - location: 111..133, - }, - Call { - arguments: [ - CallArg { - label: None, - location: 149..150, - value: Var { - location: 149..150, - name: "a", - }, - }, - CallArg { - label: None, - location: 152..154, - value: Fn { - location: 152..154, - fn_style: BinOp( - NotEq, - ), - arguments: [ - Arg { - arg_name: Named { - name: "left", - label: "left", - location: 152..154, - is_validator_param: false, - }, - location: 152..154, - annotation: None, - tipo: (), - }, - Arg { - arg_name: Named { - name: "right", - label: "right", - location: 152..154, - is_validator_param: false, - }, - location: 152..154, - annotation: None, - tipo: (), - }, - ], - body: BinOp { - location: 152..154, - name: NotEq, - left: Var { - location: 152..154, - name: "left", - }, - right: Var { - location: 152..154, - name: "right", - }, - }, - return_annotation: Some( - Constructor { - location: 152..154, - module: None, - name: "Bool", - arguments: [], - }, - ), - }, - }, - CallArg { - label: None, - location: 156..157, - value: Var { - location: 156..157, - name: "b", - }, - }, - ], - fun: Var { - location: 136..148, - name: "compare_with", - }, - location: 136..158, - }, - Call { - arguments: [ - CallArg { - label: None, - location: 174..175, - value: Var { - location: 174..175, - name: "a", - }, - }, - CallArg { - label: None, - location: 177..179, - value: Fn { - location: 177..179, - fn_style: BinOp( - And, - ), - arguments: [ - Arg { - arg_name: Named { - name: "left", - label: "left", - location: 177..179, - is_validator_param: false, - }, - location: 177..179, - annotation: Some( - Constructor { - location: 177..179, - module: None, - name: "Bool", - arguments: [], - }, - ), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right", - label: "right", - location: 177..179, - is_validator_param: false, - }, - location: 177..179, - annotation: Some( - Constructor { - location: 177..179, - module: None, - name: "Bool", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: BinOp { - location: 177..179, - name: And, - left: Var { - location: 177..179, - name: "left", - }, - right: Var { - location: 177..179, - name: "right", - }, - }, - return_annotation: Some( - Constructor { - location: 177..179, - module: None, - name: "Bool", - arguments: [], - }, - ), - }, - }, - CallArg { - label: None, - location: 181..182, - value: Var { - location: 181..182, - name: "b", - }, - }, - ], - fun: Var { - location: 161..173, - name: "combine_with", - }, - location: 161..183, - }, - Call { - arguments: [ - CallArg { - label: None, - location: 199..200, - value: Var { - location: 199..200, - name: "a", - }, - }, - CallArg { - label: None, - location: 202..204, - value: Fn { - location: 202..204, - fn_style: BinOp( - Or, - ), - arguments: [ - Arg { - arg_name: Named { - name: "left", - label: "left", - location: 202..204, - is_validator_param: false, - }, - location: 202..204, - annotation: Some( - Constructor { - location: 202..204, - module: None, - name: "Bool", - arguments: [], - }, - ), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right", - label: "right", - location: 202..204, - is_validator_param: false, - }, - location: 202..204, - annotation: Some( - Constructor { - location: 202..204, - module: None, - name: "Bool", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: BinOp { - location: 202..204, - name: Or, - left: Var { - location: 202..204, - name: "left", - }, - right: Var { - location: 202..204, - name: "right", - }, - }, - return_annotation: Some( - Constructor { - location: 202..204, - module: None, - name: "Bool", - arguments: [], - }, - ), - }, - }, - CallArg { - label: None, - location: 206..207, - value: Var { - location: 206..207, - name: "b", - }, - }, - ], - fun: Var { - location: 186..198, - name: "combine_with", - }, - location: 186..208, - }, - Call { - arguments: [ - CallArg { - label: None, - location: 224..225, - value: Var { - location: 224..225, - name: "a", - }, - }, - CallArg { - label: None, - location: 227..228, - value: Fn { - location: 227..228, - fn_style: BinOp( - AddInt, - ), - arguments: [ - Arg { - arg_name: Named { - name: "left", - label: "left", - location: 227..228, - is_validator_param: false, - }, - location: 227..228, - annotation: Some( - Constructor { - location: 227..228, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right", - label: "right", - location: 227..228, - is_validator_param: false, - }, - location: 227..228, - annotation: Some( - Constructor { - location: 227..228, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: BinOp { - location: 227..228, - name: AddInt, - left: Var { - location: 227..228, - name: "left", - }, - right: Var { - location: 227..228, - name: "right", - }, - }, - return_annotation: Some( - Constructor { - location: 227..228, - module: None, - name: "Int", - arguments: [], - }, - ), - }, - }, - CallArg { - label: None, - location: 230..231, - value: Var { - location: 230..231, - name: "b", - }, - }, - ], - fun: Var { - location: 211..223, - name: "compute_with", - }, - location: 211..232, - }, - Call { - arguments: [ - CallArg { - label: None, - location: 248..249, - value: Var { - location: 248..249, - name: "a", - }, - }, - CallArg { - label: None, - location: 251..252, - value: Fn { - location: 251..252, - fn_style: BinOp( - SubInt, - ), - arguments: [ - Arg { - arg_name: Named { - name: "left", - label: "left", - location: 251..252, - is_validator_param: false, - }, - location: 251..252, - annotation: Some( - Constructor { - location: 251..252, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right", - label: "right", - location: 251..252, - is_validator_param: false, - }, - location: 251..252, - annotation: Some( - Constructor { - location: 251..252, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: BinOp { - location: 251..252, - name: SubInt, - left: Var { - location: 251..252, - name: "left", - }, - right: Var { - location: 251..252, - name: "right", - }, - }, - return_annotation: Some( - Constructor { - location: 251..252, - module: None, - name: "Int", - arguments: [], - }, - ), - }, - }, - CallArg { - label: None, - location: 254..255, - value: Var { - location: 254..255, - name: "b", - }, - }, - ], - fun: Var { - location: 235..247, - name: "compute_with", - }, - location: 235..256, - }, - Call { - arguments: [ - CallArg { - label: None, - location: 272..273, - value: Var { - location: 272..273, - name: "a", - }, - }, - CallArg { - label: None, - location: 275..276, - value: Fn { - location: 275..276, - fn_style: BinOp( - DivInt, - ), - arguments: [ - Arg { - arg_name: Named { - name: "left", - label: "left", - location: 275..276, - is_validator_param: false, - }, - location: 275..276, - annotation: Some( - Constructor { - location: 275..276, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right", - label: "right", - location: 275..276, - is_validator_param: false, - }, - location: 275..276, - annotation: Some( - Constructor { - location: 275..276, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: BinOp { - location: 275..276, - name: DivInt, - left: Var { - location: 275..276, - name: "left", - }, - right: Var { - location: 275..276, - name: "right", - }, - }, - return_annotation: Some( - Constructor { - location: 275..276, - module: None, - name: "Int", - arguments: [], - }, - ), - }, - }, - CallArg { - label: None, - location: 278..279, - value: Var { - location: 278..279, - name: "b", - }, - }, - ], - fun: Var { - location: 259..271, - name: "compute_with", - }, - location: 259..280, - }, - Call { - arguments: [ - CallArg { - label: None, - location: 296..297, - value: Var { - location: 296..297, - name: "a", - }, - }, - CallArg { - label: None, - location: 299..300, - value: Fn { - location: 299..300, - fn_style: BinOp( - MultInt, - ), - arguments: [ - Arg { - arg_name: Named { - name: "left", - label: "left", - location: 299..300, - is_validator_param: false, - }, - location: 299..300, - annotation: Some( - Constructor { - location: 299..300, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right", - label: "right", - location: 299..300, - is_validator_param: false, - }, - location: 299..300, - annotation: Some( - Constructor { - location: 299..300, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: BinOp { - location: 299..300, - name: MultInt, - left: Var { - location: 299..300, - name: "left", - }, - right: Var { - location: 299..300, - name: "right", - }, - }, - return_annotation: Some( - Constructor { - location: 299..300, - module: None, - name: "Int", - arguments: [], - }, - ), - }, - }, - CallArg { - label: None, - location: 302..303, - value: Var { - location: 302..303, - name: "b", - }, - }, - ], - fun: Var { - location: 283..295, - name: "compute_with", - }, - location: 283..304, - }, - Call { - arguments: [ - CallArg { - label: None, - location: 320..321, - value: Var { - location: 320..321, - name: "a", - }, - }, - CallArg { - label: None, - location: 323..324, - value: Fn { - location: 323..324, - fn_style: BinOp( - ModInt, - ), - arguments: [ - Arg { - arg_name: Named { - name: "left", - label: "left", - location: 323..324, - is_validator_param: false, - }, - location: 323..324, - annotation: Some( - Constructor { - location: 323..324, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - Arg { - arg_name: Named { - name: "right", - label: "right", - location: 323..324, - is_validator_param: false, - }, - location: 323..324, - annotation: Some( - Constructor { - location: 323..324, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: BinOp { - location: 323..324, - name: ModInt, - left: Var { - location: 323..324, - name: "left", - }, - right: Var { - location: 323..324, - name: "right", - }, - }, - return_annotation: Some( - Constructor { - location: 323..324, - module: None, - name: "Int", - arguments: [], - }, - ), - }, - }, - CallArg { - label: None, - location: 326..327, - value: Var { - location: 326..327, - name: "b", - }, - }, - ], - fun: Var { - location: 307..319, - name: "compute_with", - }, - location: 307..328, - }, - ], - }, - doc: None, - location: 0..8, - name: "foo", - public: false, - return_annotation: None, - return_type: (), - end_position: 329, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/function_def.snap b/crates/aiken-lang/src/tests/snapshots/function_def.snap deleted file mode 100644 index c9909b7e0..000000000 --- a/crates/aiken-lang/src/tests/snapshots/function_def.snap +++ /dev/null @@ -1,36 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn foo() {}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: Trace { - kind: Todo, - location: 0..11, - then: ErrorTerm { - location: 0..11, - }, - text: String { - location: 0..11, - value: "aiken::todo", - }, - }, - doc: None, - location: 0..8, - name: "foo", - public: false, - return_annotation: None, - return_type: (), - end_position: 10, - can_error: true, - }, - ), - ], - kind: Validator, -} From 47567c5e6fa115e3791df3c1e10b54d425bd6c00 Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 4 Jul 2023 17:15:46 -0400 Subject: [PATCH 23/31] test(parser): some adjustments after rebase with @ktorz fix --- crates/aiken-lang/src/parser.rs | 2 +- crates/aiken-lang/src/parser/utils.rs | 6 +-- .../snapshots/parse_unicode_offset_1.snap | 52 +++++++++++++++++++ .../snapshots/parse_unicode_offset_2.snap | 50 ++++++++++++++++++ 4 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_1.snap create mode 100644 crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_2.snap diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 7914487d6..dc4b04b3c 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -24,7 +24,7 @@ pub fn module( ) -> Result<(ast::UntypedModule, ModuleExtra), Vec> { let lexer::LexInfo { tokens, extra } = lexer::run(src)?; - let stream = chumsky::Stream::from_iter(ast::Span::create(tokens.len()), tokens.into_iter()); + let stream = chumsky::Stream::from_iter(ast::Span::create(tokens.len(), 1), tokens.into_iter()); let definitions = definition().repeated().then_ignore(end()).parse(stream)?; diff --git a/crates/aiken-lang/src/parser/utils.rs b/crates/aiken-lang/src/parser/utils.rs index 2e1716ce2..0ce3e404b 100644 --- a/crates/aiken-lang/src/parser/utils.rs +++ b/crates/aiken-lang/src/parser/utils.rs @@ -101,7 +101,7 @@ macro_rules! assert_expr { let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap(); - let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len()), tokens.into_iter()); + let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter()); let result = $crate::parser::expr::sequence().parse(stream).unwrap(); @@ -122,7 +122,7 @@ macro_rules! assert_annotation { let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap(); - let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len()), tokens.into_iter()); + let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter()); let result = $crate::parser::annotation().parse(stream).unwrap(); @@ -159,7 +159,7 @@ macro_rules! assert_definition { let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap(); - let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len()), tokens.into_iter()); + let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter()); let result = $crate::parser::definition().parse(stream).unwrap(); diff --git a/crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_1.snap b/crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_1.snap new file mode 100644 index 000000000..fd21a8e8c --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_1.snap @@ -0,0 +1,52 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "Code:\n\nfn foo() {\n let x = \"★\"\n x\n}\n" +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Sequence { + location: 13..30, + expressions: [ + Assignment { + location: 13..26, + value: ByteArray { + location: 21..26, + bytes: [ + 226, + 152, + 133, + ], + preferred_format: Utf8String, + }, + pattern: Var { + location: 17..18, + name: "x", + }, + kind: Let, + annotation: None, + }, + Var { + location: 29..30, + name: "x", + }, + ], + }, + doc: None, + location: 0..8, + name: "foo", + public: false, + return_annotation: None, + return_type: (), + end_position: 31, + can_error: true, + }, + ), + ], + kind: Validator, +} diff --git a/crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_2.snap b/crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_2.snap new file mode 100644 index 000000000..f0186e940 --- /dev/null +++ b/crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_2.snap @@ -0,0 +1,50 @@ +--- +source: crates/aiken-lang/src/tests/parser.rs +description: "Code:\n\nfn foo() {\n let x = \"*\"\n x\n}\n" +--- +Module { + name: "", + docs: [], + type_info: (), + definitions: [ + Fn( + Function { + arguments: [], + body: Sequence { + location: 13..28, + expressions: [ + Assignment { + location: 13..24, + value: ByteArray { + location: 21..24, + bytes: [ + 42, + ], + preferred_format: Utf8String, + }, + pattern: Var { + location: 17..18, + name: "x", + }, + kind: Let, + annotation: None, + }, + Var { + location: 27..28, + name: "x", + }, + ], + }, + doc: None, + location: 0..8, + name: "foo", + public: false, + return_annotation: None, + return_type: (), + end_position: 29, + can_error: true, + }, + ), + ], + kind: Validator, +} From 5e8edcb3400cc6d9e6c5d2e8c5d57f0becc1060f Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 4 Jul 2023 17:48:48 -0400 Subject: [PATCH 24/31] test(parser): finish moving tests to their correct modules --- crates/aiken-lang/src/parser.rs | 24 +++ .../src/parser/definition/data_type.rs | 29 +++ .../definition/snapshots/custom_type.snap | 92 ++++++++ .../definition/snapshots/opaque_type.snap | 37 ++++ .../aiken-lang/src/parser/expr/assignment.rs | 7 +- crates/aiken-lang/src/parser/expr/block.rs | 18 ++ .../aiken-lang/src/parser/expr/bytearray.rs | 20 ++ crates/aiken-lang/src/parser/expr/mod.rs | 39 ++++ .../parser/expr/snapshots/block_basic.snap | 49 +++++ .../expr/snapshots/bytearray_base16.snap | 13 ++ .../expr/snapshots/bytearray_basic.snap | 17 ++ .../snapshots/bytearray_utf8_encoded.snap | 15 ++ .../src/parser/expr/snapshots/expect.snap | 36 ++++ .../parser/expr/snapshots/field_access.snap | 12 ++ .../expr/snapshots/function_invoke.snap | 160 ++++++++++++++ .../parser/expr/snapshots/parse_tuple.snap | 95 ++++++++ .../parser/expr/snapshots/parse_tuple2.snap | 54 +++++ .../src/parser/expr/snapshots/pipeline.snap | 32 +++ .../src/parser/expr/snapshots/plus_binop.snap | 19 ++ .../parser/expr/snapshots/string_basic.snap | 8 + crates/aiken-lang/src/parser/expr/string.rs | 10 + crates/aiken-lang/src/parser/expr/tuple.rs | 25 +++ crates/aiken-lang/src/parser/expr/when/mod.rs | 22 ++ .../expr/when/snapshots/when_basic.snap | 140 ++++++++++++ .../snapshots/parse_unicode_offset_1.snap | 2 +- .../snapshots/parse_unicode_offset_2.snap | 2 +- crates/aiken-lang/src/tests/mod.rs | 1 - crates/aiken-lang/src/tests/parser.rs | 204 ------------------ .../snapshots/base16_bytearray_literals.snap | 61 ------ .../aiken-lang/src/tests/snapshots/block.snap | 98 --------- .../aiken-lang/src/tests/snapshots/call.snap | 181 ---------------- .../src/tests/snapshots/custom_type.snap | 100 --------- .../src/tests/snapshots/expect.snap | 70 ------ .../src/tests/snapshots/field_access.snap | 52 ----- .../src/tests/snapshots/function_invoke.snap | 54 ----- .../src/tests/snapshots/opaque_type.snap | 45 ---- .../src/tests/snapshots/parse_tuple.snap | 116 ---------- .../src/tests/snapshots/parse_tuple2.snap | 75 ------- .../src/tests/snapshots/pipeline.snap | 72 ------- .../snapshots/plain_bytearray_literals.snap | 35 --- .../src/tests/snapshots/plus_binop.snap | 59 ----- .../aiken-lang/src/tests/snapshots/when.snap | 162 -------------- 42 files changed, 974 insertions(+), 1388 deletions(-) create mode 100644 crates/aiken-lang/src/parser/definition/snapshots/custom_type.snap create mode 100644 crates/aiken-lang/src/parser/definition/snapshots/opaque_type.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/block_basic.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/bytearray_base16.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/bytearray_basic.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/bytearray_utf8_encoded.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/expect.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/field_access.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/function_invoke.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/parse_tuple.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/parse_tuple2.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/pipeline.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/plus_binop.snap create mode 100644 crates/aiken-lang/src/parser/expr/snapshots/string_basic.snap create mode 100644 crates/aiken-lang/src/parser/expr/when/snapshots/when_basic.snap rename crates/aiken-lang/src/{tests => }/snapshots/parse_unicode_offset_1.snap (97%) rename crates/aiken-lang/src/{tests => }/snapshots/parse_unicode_offset_2.snap (96%) delete mode 100644 crates/aiken-lang/src/tests/parser.rs delete mode 100644 crates/aiken-lang/src/tests/snapshots/base16_bytearray_literals.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/block.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/call.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/custom_type.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/expect.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/field_access.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/function_invoke.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/opaque_type.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/parse_tuple.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/parse_tuple2.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/pipeline.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/plain_bytearray_literals.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/plus_binop.snap delete mode 100644 crates/aiken-lang/src/tests/snapshots/when.snap diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index dc4b04b3c..78c1e87c6 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -84,4 +84,28 @@ mod tests { "# ); } + + #[test] + fn parse_unicode_offset_1() { + assert_module!( + r#" + fn foo() { + let x = "★" + x + } + "# + ); + } + + #[test] + fn parse_unicode_offset_2() { + assert_module!( + r#" + fn foo() { + let x = "*" + x + } + "# + ); + } } diff --git a/crates/aiken-lang/src/parser/definition/data_type.rs b/crates/aiken-lang/src/parser/definition/data_type.rs index 14083cf00..cc9ff73ed 100644 --- a/crates/aiken-lang/src/parser/definition/data_type.rs +++ b/crates/aiken-lang/src/parser/definition/data_type.rs @@ -92,3 +92,32 @@ fn labeled_constructor_type_args( .allow_trailing() .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)) } + +#[cfg(test)] +mod tests { + use crate::assert_definition; + + #[test] + fn custom_type() { + assert_definition!( + r#" + type Option { + Some(a, Int) + None + Wow { name: Int, age: Int } + } + "# + ); + } + + #[test] + fn opaque_type() { + assert_definition!( + r#" + pub opaque type User { + name: _w + } + "# + ); + } +} diff --git a/crates/aiken-lang/src/parser/definition/snapshots/custom_type.snap b/crates/aiken-lang/src/parser/definition/snapshots/custom_type.snap new file mode 100644 index 000000000..53760f6ce --- /dev/null +++ b/crates/aiken-lang/src/parser/definition/snapshots/custom_type.snap @@ -0,0 +1,92 @@ +--- +source: crates/aiken-lang/src/parser/definition/data_type.rs +description: "Code:\n\ntype Option {\n Some(a, Int)\n None\n Wow { name: Int, age: Int }\n}\n" +--- +DataType( + DataType { + constructors: [ + RecordConstructor { + location: 19..31, + name: "Some", + arguments: [ + RecordConstructorArg { + label: None, + annotation: Var { + location: 24..25, + name: "a", + }, + location: 24..25, + tipo: (), + doc: None, + }, + RecordConstructorArg { + label: None, + annotation: Constructor { + location: 27..30, + module: None, + name: "Int", + arguments: [], + }, + location: 27..30, + tipo: (), + doc: None, + }, + ], + doc: None, + sugar: false, + }, + RecordConstructor { + location: 34..38, + name: "None", + arguments: [], + doc: None, + sugar: false, + }, + RecordConstructor { + location: 41..68, + name: "Wow", + arguments: [ + RecordConstructorArg { + label: Some( + "name", + ), + annotation: Constructor { + location: 53..56, + module: None, + name: "Int", + arguments: [], + }, + location: 47..56, + tipo: (), + doc: None, + }, + RecordConstructorArg { + label: Some( + "age", + ), + annotation: Constructor { + location: 63..66, + module: None, + name: "Int", + arguments: [], + }, + location: 58..66, + tipo: (), + doc: None, + }, + ], + doc: None, + sugar: false, + }, + ], + doc: None, + location: 0..70, + name: "Option", + opaque: false, + parameters: [ + "a", + ], + public: false, + typed_parameters: [], + }, +) diff --git a/crates/aiken-lang/src/parser/definition/snapshots/opaque_type.snap b/crates/aiken-lang/src/parser/definition/snapshots/opaque_type.snap new file mode 100644 index 000000000..2ed29bba1 --- /dev/null +++ b/crates/aiken-lang/src/parser/definition/snapshots/opaque_type.snap @@ -0,0 +1,37 @@ +--- +source: crates/aiken-lang/src/parser/definition/data_type.rs +description: "Code:\n\npub opaque type User {\n name: _w\n}\n" +--- +DataType( + DataType { + constructors: [ + RecordConstructor { + location: 21..35, + name: "User", + arguments: [ + RecordConstructorArg { + label: Some( + "name", + ), + annotation: Hole { + location: 31..33, + name: "_w", + }, + location: 25..33, + tipo: (), + doc: None, + }, + ], + doc: None, + sugar: true, + }, + ], + doc: None, + location: 0..35, + name: "User", + opaque: true, + parameters: [], + public: true, + typed_parameters: [], + }, +) diff --git a/crates/aiken-lang/src/parser/expr/assignment.rs b/crates/aiken-lang/src/parser/expr/assignment.rs index 0759f223d..c9bd1c1cb 100644 --- a/crates/aiken-lang/src/parser/expr/assignment.rs +++ b/crates/aiken-lang/src/parser/expr/assignment.rs @@ -50,6 +50,11 @@ mod tests { #[test] fn let_bindings() { - assert_expr!(r#"let thing = [ 1, 2, a ]"#); + assert_expr!("let thing = [ 1, 2, a ]"); + } + + #[test] + fn expect() { + assert_expr!("expect Some(x) = something.field"); } } diff --git a/crates/aiken-lang/src/parser/expr/block.rs b/crates/aiken-lang/src/parser/expr/block.rs index 320723f25..e555de05f 100644 --- a/crates/aiken-lang/src/parser/expr/block.rs +++ b/crates/aiken-lang/src/parser/expr/block.rs @@ -18,3 +18,21 @@ pub fn parser( ), )) } + +#[cfg(test)] +mod tests { + use crate::assert_expr; + + #[test] + fn block_basic() { + assert_expr!( + r#" + let b = { + let x = 4 + + x + 5 + } + "# + ); + } +} diff --git a/crates/aiken-lang/src/parser/expr/bytearray.rs b/crates/aiken-lang/src/parser/expr/bytearray.rs index 66b356f7b..756f63127 100644 --- a/crates/aiken-lang/src/parser/expr/bytearray.rs +++ b/crates/aiken-lang/src/parser/expr/bytearray.rs @@ -12,3 +12,23 @@ pub fn parser() -> impl Parser { preferred_format, }) } + +#[cfg(test)] +mod tests { + use crate::assert_expr; + + #[test] + fn bytearray_basic() { + assert_expr!("#[0, 170, 255]"); + } + + #[test] + fn bytearray_base16() { + assert_expr!("#\"00aaff\""); + } + + #[test] + fn bytearray_utf8_encoded() { + assert_expr!("\"aiken\""); + } +} diff --git a/crates/aiken-lang/src/parser/expr/mod.rs b/crates/aiken-lang/src/parser/expr/mod.rs index d6e098426..db134436f 100644 --- a/crates/aiken-lang/src/parser/expr/mod.rs +++ b/crates/aiken-lang/src/parser/expr/mod.rs @@ -364,3 +364,42 @@ pub fn parser( }) }) } + +#[cfg(test)] +mod tests { + use crate::assert_expr; + + #[test] + fn plus_binop() { + assert_expr!("a + 1"); + } + + #[test] + fn pipeline() { + assert_expr!( + r#" + a + 2 + |> add_one + |> add_one + "# + ); + } + + #[test] + fn field_access() { + assert_expr!("user.name"); + } + + #[test] + fn function_invoke() { + assert_expr!( + r#" + let x = add_one(3) + + let map_add_x = list.map(_, fn (y) { x + y }) + + map_add_x([ 1, 2, 3 ]) + "# + ); + } +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/block_basic.snap b/crates/aiken-lang/src/parser/expr/snapshots/block_basic.snap new file mode 100644 index 000000000..7d07f7180 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/block_basic.snap @@ -0,0 +1,49 @@ +--- +source: crates/aiken-lang/src/parser/expr/block.rs +description: "Code:\n\nlet b = {\n let x = 4\n\n x + 5\n}\n" +--- +Assignment { + location: 0..32, + value: Sequence { + location: 12..30, + expressions: [ + Assignment { + location: 12..21, + value: Int { + location: 20..21, + value: "4", + base: Decimal { + numeric_underscore: false, + }, + }, + pattern: Var { + location: 16..17, + name: "x", + }, + kind: Let, + annotation: None, + }, + BinOp { + location: 25..30, + name: AddInt, + left: Var { + location: 25..26, + name: "x", + }, + right: Int { + location: 29..30, + value: "5", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + }, + pattern: Var { + location: 4..5, + name: "b", + }, + kind: Let, + annotation: None, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/bytearray_base16.snap b/crates/aiken-lang/src/parser/expr/snapshots/bytearray_base16.snap new file mode 100644 index 000000000..36d36a2a9 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/bytearray_base16.snap @@ -0,0 +1,13 @@ +--- +source: crates/aiken-lang/src/parser/expr/bytearray.rs +description: "Code:\n\n#\"00aaff\"" +--- +ByteArray { + location: 0..9, + bytes: [ + 0, + 170, + 255, + ], + preferred_format: HexadecimalString, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/bytearray_basic.snap b/crates/aiken-lang/src/parser/expr/snapshots/bytearray_basic.snap new file mode 100644 index 000000000..58c6b407f --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/bytearray_basic.snap @@ -0,0 +1,17 @@ +--- +source: crates/aiken-lang/src/parser/expr/bytearray.rs +description: "Code:\n\n#[0, 170, 255]" +--- +ByteArray { + location: 0..14, + bytes: [ + 0, + 170, + 255, + ], + preferred_format: ArrayOfBytes( + Decimal { + numeric_underscore: false, + }, + ), +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/bytearray_utf8_encoded.snap b/crates/aiken-lang/src/parser/expr/snapshots/bytearray_utf8_encoded.snap new file mode 100644 index 000000000..611eefcf8 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/bytearray_utf8_encoded.snap @@ -0,0 +1,15 @@ +--- +source: crates/aiken-lang/src/parser/expr/bytearray.rs +description: "Code:\n\n\"aiken\"" +--- +ByteArray { + location: 0..7, + bytes: [ + 97, + 105, + 107, + 101, + 110, + ], + preferred_format: Utf8String, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/expect.snap b/crates/aiken-lang/src/parser/expr/snapshots/expect.snap new file mode 100644 index 000000000..440e0855c --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/expect.snap @@ -0,0 +1,36 @@ +--- +source: crates/aiken-lang/src/parser/expr/assignment.rs +description: "Code:\n\nexpect Some(x) = something.field" +--- +Assignment { + location: 0..32, + value: FieldAccess { + location: 17..32, + label: "field", + container: Var { + location: 17..26, + name: "something", + }, + }, + pattern: Constructor { + is_record: false, + location: 7..14, + name: "Some", + arguments: [ + CallArg { + label: None, + location: 12..13, + value: Var { + location: 12..13, + name: "x", + }, + }, + ], + module: None, + constructor: (), + with_spread: false, + tipo: (), + }, + kind: Expect, + annotation: None, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/field_access.snap b/crates/aiken-lang/src/parser/expr/snapshots/field_access.snap new file mode 100644 index 000000000..f429b58ed --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/field_access.snap @@ -0,0 +1,12 @@ +--- +source: crates/aiken-lang/src/parser/expr/mod.rs +description: "Code:\n\nuser.name" +--- +FieldAccess { + location: 0..9, + label: "name", + container: Var { + location: 0..4, + name: "user", + }, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/function_invoke.snap b/crates/aiken-lang/src/parser/expr/snapshots/function_invoke.snap new file mode 100644 index 000000000..411a7f4f0 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/function_invoke.snap @@ -0,0 +1,160 @@ +--- +source: crates/aiken-lang/src/parser/expr/mod.rs +description: "Code:\n\nlet x = add_one(3)\n\nlet map_add_x = list.map(_, fn (y) { x + y })\n\nmap_add_x([ 1, 2, 3 ])\n" +--- +Sequence { + location: 0..89, + expressions: [ + Assignment { + location: 0..18, + value: Call { + arguments: [ + CallArg { + label: None, + location: 16..17, + value: Int { + location: 16..17, + value: "3", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + fun: Var { + location: 8..15, + name: "add_one", + }, + location: 8..18, + }, + pattern: Var { + location: 4..5, + name: "x", + }, + kind: Let, + annotation: None, + }, + Assignment { + location: 20..65, + value: Fn { + location: 36..65, + fn_style: Capture, + arguments: [ + Arg { + arg_name: Named { + name: "_capture__0", + label: "_capture__0", + location: 0..0, + is_validator_param: false, + }, + location: 0..0, + annotation: None, + tipo: (), + }, + ], + body: Call { + arguments: [ + CallArg { + label: None, + location: 45..46, + value: Var { + location: 45..46, + name: "_capture__0", + }, + }, + CallArg { + label: None, + location: 48..64, + value: Fn { + location: 48..64, + fn_style: Plain, + arguments: [ + Arg { + arg_name: Named { + name: "y", + label: "y", + location: 52..53, + is_validator_param: false, + }, + location: 52..53, + annotation: None, + tipo: (), + }, + ], + body: BinOp { + location: 57..62, + name: AddInt, + left: Var { + location: 57..58, + name: "x", + }, + right: Var { + location: 61..62, + name: "y", + }, + }, + return_annotation: None, + }, + }, + ], + fun: FieldAccess { + location: 36..44, + label: "map", + container: Var { + location: 36..40, + name: "list", + }, + }, + location: 36..65, + }, + return_annotation: None, + }, + pattern: Var { + location: 24..33, + name: "map_add_x", + }, + kind: Let, + annotation: None, + }, + Call { + arguments: [ + CallArg { + label: None, + location: 77..88, + value: List { + location: 77..88, + elements: [ + Int { + location: 79..80, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 82..83, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 85..86, + value: "3", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + tail: None, + }, + }, + ], + fun: Var { + location: 67..76, + name: "map_add_x", + }, + location: 67..89, + }, + ], +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple.snap b/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple.snap new file mode 100644 index 000000000..857616c86 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple.snap @@ -0,0 +1,95 @@ +--- +source: crates/aiken-lang/src/parser/expr/tuple.rs +description: "Code:\n\nlet tuple = (1, 2, 3, 4)\ntuple.1st + tuple.2nd + tuple.3rd + tuple.4th\n" +--- +Sequence { + location: 0..70, + expressions: [ + Assignment { + location: 0..24, + value: Tuple { + location: 12..24, + elems: [ + Int { + location: 13..14, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 16..17, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 19..20, + value: "3", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 22..23, + value: "4", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + }, + pattern: Var { + location: 4..9, + name: "tuple", + }, + kind: Let, + annotation: None, + }, + BinOp { + location: 25..70, + name: AddInt, + left: BinOp { + location: 25..58, + name: AddInt, + left: BinOp { + location: 25..46, + name: AddInt, + left: TupleIndex { + location: 25..34, + index: 0, + tuple: Var { + location: 25..30, + name: "tuple", + }, + }, + right: TupleIndex { + location: 37..46, + index: 1, + tuple: Var { + location: 37..42, + name: "tuple", + }, + }, + }, + right: TupleIndex { + location: 49..58, + index: 2, + tuple: Var { + location: 49..54, + name: "tuple", + }, + }, + }, + right: TupleIndex { + location: 61..70, + index: 3, + tuple: Var { + location: 61..66, + name: "tuple", + }, + }, + }, + ], +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple2.snap b/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple2.snap new file mode 100644 index 000000000..634d61840 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/parse_tuple2.snap @@ -0,0 +1,54 @@ +--- +source: crates/aiken-lang/src/parser/expr/tuple.rs +description: "Code:\n\nlet a = foo(14)\n(a, 42)\n" +--- +Sequence { + location: 0..23, + expressions: [ + Assignment { + location: 0..15, + value: Call { + arguments: [ + CallArg { + label: None, + location: 12..14, + value: Int { + location: 12..14, + value: "14", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], + fun: Var { + location: 8..11, + name: "foo", + }, + location: 8..15, + }, + pattern: Var { + location: 4..5, + name: "a", + }, + kind: Let, + annotation: None, + }, + Tuple { + location: 16..23, + elems: [ + Var { + location: 17..18, + name: "a", + }, + Int { + location: 20..22, + value: "42", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + }, + ], +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/pipeline.snap b/crates/aiken-lang/src/parser/expr/snapshots/pipeline.snap new file mode 100644 index 000000000..f98b6a8ce --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/pipeline.snap @@ -0,0 +1,32 @@ +--- +source: crates/aiken-lang/src/parser/expr/mod.rs +description: "Code:\n\na + 2\n|> add_one\n|> add_one\n" +--- +PipeLine { + expressions: [ + BinOp { + location: 0..5, + name: AddInt, + left: Var { + location: 0..1, + name: "a", + }, + right: Int { + location: 4..5, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + Var { + location: 9..16, + name: "add_one", + }, + Var { + location: 20..27, + name: "add_one", + }, + ], + one_liner: false, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/plus_binop.snap b/crates/aiken-lang/src/parser/expr/snapshots/plus_binop.snap new file mode 100644 index 000000000..397597ec9 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/plus_binop.snap @@ -0,0 +1,19 @@ +--- +source: crates/aiken-lang/src/parser/expr/mod.rs +description: "Code:\n\na + 1" +--- +BinOp { + location: 0..5, + name: AddInt, + left: Var { + location: 0..1, + name: "a", + }, + right: Int { + location: 4..5, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, +} diff --git a/crates/aiken-lang/src/parser/expr/snapshots/string_basic.snap b/crates/aiken-lang/src/parser/expr/snapshots/string_basic.snap new file mode 100644 index 000000000..1cdd1ba91 --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/snapshots/string_basic.snap @@ -0,0 +1,8 @@ +--- +source: crates/aiken-lang/src/parser/expr/string.rs +description: "Code:\n\n@\"aiken\"" +--- +String { + location: 0..8, + value: "aiken", +} diff --git a/crates/aiken-lang/src/parser/expr/string.rs b/crates/aiken-lang/src/parser/expr/string.rs index ca5c94bab..d1c07acfd 100644 --- a/crates/aiken-lang/src/parser/expr/string.rs +++ b/crates/aiken-lang/src/parser/expr/string.rs @@ -31,3 +31,13 @@ pub fn flexible(expr: UntypedExpr) -> UntypedExpr { _ => expr, } } + +#[cfg(test)] +mod tests { + use crate::assert_expr; + + #[test] + fn string_basic() { + assert_expr!("@\"aiken\""); + } +} diff --git a/crates/aiken-lang/src/parser/expr/tuple.rs b/crates/aiken-lang/src/parser/expr/tuple.rs index 6f1e1bbf2..3b02f9479 100644 --- a/crates/aiken-lang/src/parser/expr/tuple.rs +++ b/crates/aiken-lang/src/parser/expr/tuple.rs @@ -21,3 +21,28 @@ pub fn parser( elems, }) } + +#[cfg(test)] +mod tests { + use crate::assert_expr; + + #[test] + fn parse_tuple() { + assert_expr!( + r#" + let tuple = (1, 2, 3, 4) + tuple.1st + tuple.2nd + tuple.3rd + tuple.4th + "# + ); + } + + #[test] + fn parse_tuple2() { + assert_expr!( + r#" + let a = foo(14) + (a, 42) + "# + ); + } +} diff --git a/crates/aiken-lang/src/parser/expr/when/mod.rs b/crates/aiken-lang/src/parser/expr/when/mod.rs index 13c2ed91a..53b476278 100644 --- a/crates/aiken-lang/src/parser/expr/when/mod.rs +++ b/crates/aiken-lang/src/parser/expr/when/mod.rs @@ -28,3 +28,25 @@ pub fn parser( clauses, }) } + +#[cfg(test)] +mod tests { + use crate::assert_expr; + + #[test] + fn when_basic() { + assert_expr!( + r#" + when a is { + 2 if x > 1 -> 3 + 1 | 4 | 5 -> { + let amazing = 5 + amazing + } + 3 -> 9 + _ -> 4 + } + "# + ); + } +} diff --git a/crates/aiken-lang/src/parser/expr/when/snapshots/when_basic.snap b/crates/aiken-lang/src/parser/expr/when/snapshots/when_basic.snap new file mode 100644 index 000000000..7d5056b6f --- /dev/null +++ b/crates/aiken-lang/src/parser/expr/when/snapshots/when_basic.snap @@ -0,0 +1,140 @@ +--- +source: crates/aiken-lang/src/parser/expr/when/mod.rs +description: "Code:\n\nwhen a is {\n 2 if x > 1 -> 3\n 1 | 4 | 5 -> {\n let amazing = 5\n amazing\n }\n 3 -> 9\n _ -> 4\n}\n" +--- +When { + location: 0..102, + subject: Var { + location: 5..6, + name: "a", + }, + clauses: [ + UntypedClause { + location: 14..29, + patterns: [ + Int { + location: 14..15, + value: "2", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + guard: Some( + GtInt { + location: 19..24, + left: Var { + location: 19..20, + tipo: (), + name: "x", + }, + right: Constant( + Int { + location: 23..24, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + ), + }, + ), + then: Int { + location: 28..29, + value: "3", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + UntypedClause { + location: 32..82, + patterns: [ + Int { + location: 32..33, + value: "1", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 36..37, + value: "4", + base: Decimal { + numeric_underscore: false, + }, + }, + Int { + location: 40..41, + value: "5", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + guard: None, + then: Sequence { + location: 51..78, + expressions: [ + Assignment { + location: 51..66, + value: Int { + location: 65..66, + value: "5", + base: Decimal { + numeric_underscore: false, + }, + }, + pattern: Var { + location: 55..62, + name: "amazing", + }, + kind: Let, + annotation: None, + }, + Var { + location: 71..78, + name: "amazing", + }, + ], + }, + }, + UntypedClause { + location: 85..91, + patterns: [ + Int { + location: 85..86, + value: "3", + base: Decimal { + numeric_underscore: false, + }, + }, + ], + guard: None, + then: Int { + location: 90..91, + value: "9", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + UntypedClause { + location: 94..100, + patterns: [ + Discard { + name: "_", + location: 94..95, + }, + ], + guard: None, + then: Int { + location: 99..100, + value: "4", + base: Decimal { + numeric_underscore: false, + }, + }, + }, + ], +} diff --git a/crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_1.snap b/crates/aiken-lang/src/snapshots/parse_unicode_offset_1.snap similarity index 97% rename from crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_1.snap rename to crates/aiken-lang/src/snapshots/parse_unicode_offset_1.snap index fd21a8e8c..b7e862150 100644 --- a/crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_1.snap +++ b/crates/aiken-lang/src/snapshots/parse_unicode_offset_1.snap @@ -1,5 +1,5 @@ --- -source: crates/aiken-lang/src/tests/parser.rs +source: crates/aiken-lang/src/parser.rs description: "Code:\n\nfn foo() {\n let x = \"★\"\n x\n}\n" --- Module { diff --git a/crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_2.snap b/crates/aiken-lang/src/snapshots/parse_unicode_offset_2.snap similarity index 96% rename from crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_2.snap rename to crates/aiken-lang/src/snapshots/parse_unicode_offset_2.snap index f0186e940..b8bb738fd 100644 --- a/crates/aiken-lang/src/tests/snapshots/parse_unicode_offset_2.snap +++ b/crates/aiken-lang/src/snapshots/parse_unicode_offset_2.snap @@ -1,5 +1,5 @@ --- -source: crates/aiken-lang/src/tests/parser.rs +source: crates/aiken-lang/src/parser.rs description: "Code:\n\nfn foo() {\n let x = \"*\"\n x\n}\n" --- Module { diff --git a/crates/aiken-lang/src/tests/mod.rs b/crates/aiken-lang/src/tests/mod.rs index 0b33bec12..18af907b8 100644 --- a/crates/aiken-lang/src/tests/mod.rs +++ b/crates/aiken-lang/src/tests/mod.rs @@ -1,4 +1,3 @@ mod check; mod format; mod lexer; -mod parser; diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs deleted file mode 100644 index 7bc5c6c26..000000000 --- a/crates/aiken-lang/src/tests/parser.rs +++ /dev/null @@ -1,204 +0,0 @@ -use crate::assert_module; - -#[test] -fn custom_type() { - assert_module!( - r#" - type Option { - Some(a, Int) - None - Wow { name: Int, age: Int } - } - "# - ); -} - -#[test] -fn opaque_type() { - assert_module!( - r#" - pub opaque type User { - name: _w - } - "# - ); -} - -#[test] -fn expect() { - assert_module!( - r#" - pub fn run() { - expect Some(x) = something.field - x.other_field - } - "# - ); -} - -#[test] -fn plus_binop() { - assert_module!( - r#" - pub fn add_one(a) -> Int { - a + 1 - } - "# - ); -} - -#[test] -fn pipeline() { - assert_module!( - r#" - pub fn thing(thing a: Int) { - a + 2 - |> add_one - |> add_one - } - "# - ); -} - -#[test] -fn block() { - assert_module!( - r#" - pub fn wow2(a: Int){ - let b = { - let x = 4 - - x + 5 - } - - b - } - "# - ); -} - -#[test] -fn when() { - assert_module!( - r#" - pub fn wow2(a: Int){ - when a is { - 2 -> 3 - 1 | 4 | 5 -> { - let amazing = 5 - amazing - } - 3 -> 9 - _ -> 4 - } - } - "# - ); -} - -#[test] -fn field_access() { - assert_module!( - r#" - fn name(user: User) { - user.name - } - "# - ); -} - -#[test] -fn call() { - assert_module!( - r#" - fn calls() { - let x = add_one(3) - - let map_add_x = list.map(_, fn (y) { x + y }) - - map_add_x([ 1, 2, 3 ]) - } - "# - ); -} - -#[test] -fn parse_tuple() { - assert_module!( - r#" - fn foo() { - let tuple = (1, 2, 3, 4) - tuple.1st + tuple.2nd + tuple.3rd + tuple.4th - } - "# - ); -} - -#[test] -fn parse_tuple2() { - assert_module!( - r#" - fn foo() { - let a = foo(14) - (a, 42) - } - "# - ); -} - -#[test] -fn plain_bytearray_literals() { - assert_module!( - r#" - pub const my_policy_id = #[0, 170, 255] - "# - ); -} - -#[test] -fn base16_bytearray_literals() { - assert_module!( - r#" - pub const my_policy_id = #"00aaff" - - pub fn foo() { - my_policy_id == #"00aaff" - } - "# - ); -} - -#[test] -fn function_invoke() { - assert_module!( - r#" - fn foo() { - let a = bar(42) - } - "# - ); -} - -#[test] -fn parse_unicode_offset_1() { - assert_module!( - r#" - fn foo() { - let x = "★" - x - } - "# - ); -} - -#[test] -fn parse_unicode_offset_2() { - assert_module!( - r#" - fn foo() { - let x = "*" - x - } - "# - ); -} diff --git a/crates/aiken-lang/src/tests/snapshots/base16_bytearray_literals.snap b/crates/aiken-lang/src/tests/snapshots/base16_bytearray_literals.snap deleted file mode 100644 index 19784cc66..000000000 --- a/crates/aiken-lang/src/tests/snapshots/base16_bytearray_literals.snap +++ /dev/null @@ -1,61 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\npub const my_policy_id = #\"00aaff\"\n\npub fn foo() {\n my_policy_id == #\"00aaff\"\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - ModuleConstant( - ModuleConstant { - doc: None, - location: 0..34, - public: true, - name: "my_policy_id", - annotation: None, - value: ByteArray { - location: 25..34, - bytes: [ - 0, - 170, - 255, - ], - preferred_format: HexadecimalString, - }, - tipo: (), - }, - ), - Fn( - Function { - arguments: [], - body: BinOp { - location: 55..80, - name: Eq, - left: Var { - location: 55..67, - name: "my_policy_id", - }, - right: ByteArray { - location: 71..80, - bytes: [ - 0, - 170, - 255, - ], - preferred_format: HexadecimalString, - }, - }, - doc: None, - location: 36..48, - name: "foo", - public: true, - return_annotation: None, - return_type: (), - end_position: 81, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/block.snap b/crates/aiken-lang/src/tests/snapshots/block.snap deleted file mode 100644 index cbb844856..000000000 --- a/crates/aiken-lang/src/tests/snapshots/block.snap +++ /dev/null @@ -1,98 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\npub fn wow2(a: Int){\n let b = {\n let x = 4\n\n x + 5\n }\n\n b\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [ - Arg { - arg_name: Named { - name: "a", - label: "a", - location: 12..13, - is_validator_param: false, - }, - location: 12..18, - annotation: Some( - Constructor { - location: 15..18, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: Sequence { - location: 23..66, - expressions: [ - Assignment { - location: 23..61, - value: Sequence { - location: 37..57, - expressions: [ - Assignment { - location: 37..46, - value: Int { - location: 45..46, - value: "4", - base: Decimal { - numeric_underscore: false, - }, - }, - pattern: Var { - location: 41..42, - name: "x", - }, - kind: Let, - annotation: None, - }, - BinOp { - location: 52..57, - name: AddInt, - left: Var { - location: 52..53, - name: "x", - }, - right: Int { - location: 56..57, - value: "5", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - ], - }, - pattern: Var { - location: 27..28, - name: "b", - }, - kind: Let, - annotation: None, - }, - Var { - location: 65..66, - name: "b", - }, - ], - }, - doc: None, - location: 0..19, - name: "wow2", - public: true, - return_annotation: None, - return_type: (), - end_position: 67, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/call.snap b/crates/aiken-lang/src/tests/snapshots/call.snap deleted file mode 100644 index 78f7d70fa..000000000 --- a/crates/aiken-lang/src/tests/snapshots/call.snap +++ /dev/null @@ -1,181 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn calls() {\n let x = add_one(3)\n\n let map_add_x = list.map(_, fn (y) { x + y })\n\n map_add_x([ 1, 2, 3 ])\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: Sequence { - location: 15..108, - expressions: [ - Assignment { - location: 15..33, - value: Call { - arguments: [ - CallArg { - label: None, - location: 31..32, - value: Int { - location: 31..32, - value: "3", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - ], - fun: Var { - location: 23..30, - name: "add_one", - }, - location: 23..33, - }, - pattern: Var { - location: 19..20, - name: "x", - }, - kind: Let, - annotation: None, - }, - Assignment { - location: 37..82, - value: Fn { - location: 53..82, - fn_style: Capture, - arguments: [ - Arg { - arg_name: Named { - name: "_capture__0", - label: "_capture__0", - location: 0..0, - is_validator_param: false, - }, - location: 0..0, - annotation: None, - tipo: (), - }, - ], - body: Call { - arguments: [ - CallArg { - label: None, - location: 62..63, - value: Var { - location: 62..63, - name: "_capture__0", - }, - }, - CallArg { - label: None, - location: 65..81, - value: Fn { - location: 65..81, - fn_style: Plain, - arguments: [ - Arg { - arg_name: Named { - name: "y", - label: "y", - location: 69..70, - is_validator_param: false, - }, - location: 69..70, - annotation: None, - tipo: (), - }, - ], - body: BinOp { - location: 74..79, - name: AddInt, - left: Var { - location: 74..75, - name: "x", - }, - right: Var { - location: 78..79, - name: "y", - }, - }, - return_annotation: None, - }, - }, - ], - fun: FieldAccess { - location: 53..61, - label: "map", - container: Var { - location: 53..57, - name: "list", - }, - }, - location: 53..82, - }, - return_annotation: None, - }, - pattern: Var { - location: 41..50, - name: "map_add_x", - }, - kind: Let, - annotation: None, - }, - Call { - arguments: [ - CallArg { - label: None, - location: 96..107, - value: List { - location: 96..107, - elements: [ - Int { - location: 98..99, - value: "1", - base: Decimal { - numeric_underscore: false, - }, - }, - Int { - location: 101..102, - value: "2", - base: Decimal { - numeric_underscore: false, - }, - }, - Int { - location: 104..105, - value: "3", - base: Decimal { - numeric_underscore: false, - }, - }, - ], - tail: None, - }, - }, - ], - fun: Var { - location: 86..95, - name: "map_add_x", - }, - location: 86..108, - }, - ], - }, - doc: None, - location: 0..10, - name: "calls", - public: false, - return_annotation: None, - return_type: (), - end_position: 109, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/custom_type.snap b/crates/aiken-lang/src/tests/snapshots/custom_type.snap deleted file mode 100644 index 1954253cd..000000000 --- a/crates/aiken-lang/src/tests/snapshots/custom_type.snap +++ /dev/null @@ -1,100 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\ntype Option {\n Some(a, Int)\n None\n Wow { name: Int, age: Int }\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - DataType( - DataType { - constructors: [ - RecordConstructor { - location: 19..31, - name: "Some", - arguments: [ - RecordConstructorArg { - label: None, - annotation: Var { - location: 24..25, - name: "a", - }, - location: 24..25, - tipo: (), - doc: None, - }, - RecordConstructorArg { - label: None, - annotation: Constructor { - location: 27..30, - module: None, - name: "Int", - arguments: [], - }, - location: 27..30, - tipo: (), - doc: None, - }, - ], - doc: None, - sugar: false, - }, - RecordConstructor { - location: 34..38, - name: "None", - arguments: [], - doc: None, - sugar: false, - }, - RecordConstructor { - location: 41..68, - name: "Wow", - arguments: [ - RecordConstructorArg { - label: Some( - "name", - ), - annotation: Constructor { - location: 53..56, - module: None, - name: "Int", - arguments: [], - }, - location: 47..56, - tipo: (), - doc: None, - }, - RecordConstructorArg { - label: Some( - "age", - ), - annotation: Constructor { - location: 63..66, - module: None, - name: "Int", - arguments: [], - }, - location: 58..66, - tipo: (), - doc: None, - }, - ], - doc: None, - sugar: false, - }, - ], - doc: None, - location: 0..70, - name: "Option", - opaque: false, - parameters: [ - "a", - ], - public: false, - typed_parameters: [], - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/expect.snap b/crates/aiken-lang/src/tests/snapshots/expect.snap deleted file mode 100644 index 51329710c..000000000 --- a/crates/aiken-lang/src/tests/snapshots/expect.snap +++ /dev/null @@ -1,70 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\npub fn run() {\n expect Some(x) = something.field\n x.other_field\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: Sequence { - location: 19..69, - expressions: [ - Assignment { - location: 19..51, - value: FieldAccess { - location: 36..51, - label: "field", - container: Var { - location: 36..45, - name: "something", - }, - }, - pattern: Constructor { - is_record: false, - location: 26..33, - name: "Some", - arguments: [ - CallArg { - label: None, - location: 31..32, - value: Var { - location: 31..32, - name: "x", - }, - }, - ], - module: None, - constructor: (), - with_spread: false, - tipo: (), - }, - kind: Expect, - annotation: None, - }, - FieldAccess { - location: 56..69, - label: "other_field", - container: Var { - location: 56..57, - name: "x", - }, - }, - ], - }, - doc: None, - location: 0..12, - name: "run", - public: true, - return_annotation: None, - return_type: (), - end_position: 70, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/field_access.snap b/crates/aiken-lang/src/tests/snapshots/field_access.snap deleted file mode 100644 index c5caa08aa..000000000 --- a/crates/aiken-lang/src/tests/snapshots/field_access.snap +++ /dev/null @@ -1,52 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn name(user: User) {\n user.name\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [ - Arg { - arg_name: Named { - name: "user", - label: "user", - location: 8..12, - is_validator_param: false, - }, - location: 8..18, - annotation: Some( - Constructor { - location: 14..18, - module: None, - name: "User", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: FieldAccess { - location: 24..33, - label: "name", - container: Var { - location: 24..28, - name: "user", - }, - }, - doc: None, - location: 0..19, - name: "name", - public: false, - return_annotation: None, - return_type: (), - end_position: 34, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/function_invoke.snap b/crates/aiken-lang/src/tests/snapshots/function_invoke.snap deleted file mode 100644 index 2f115edc7..000000000 --- a/crates/aiken-lang/src/tests/snapshots/function_invoke.snap +++ /dev/null @@ -1,54 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn foo() {\n let a = bar(42)\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: Assignment { - location: 15..30, - value: Call { - arguments: [ - CallArg { - label: None, - location: 27..29, - value: Int { - location: 27..29, - value: "42", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - ], - fun: Var { - location: 23..26, - name: "bar", - }, - location: 23..30, - }, - pattern: Var { - location: 19..20, - name: "a", - }, - kind: Let, - annotation: None, - }, - doc: None, - location: 0..8, - name: "foo", - public: false, - return_annotation: None, - return_type: (), - end_position: 31, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/opaque_type.snap b/crates/aiken-lang/src/tests/snapshots/opaque_type.snap deleted file mode 100644 index 1f0426e1f..000000000 --- a/crates/aiken-lang/src/tests/snapshots/opaque_type.snap +++ /dev/null @@ -1,45 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\npub opaque type User {\n name: _w\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - DataType( - DataType { - constructors: [ - RecordConstructor { - location: 21..35, - name: "User", - arguments: [ - RecordConstructorArg { - label: Some( - "name", - ), - annotation: Hole { - location: 31..33, - name: "_w", - }, - location: 25..33, - tipo: (), - doc: None, - }, - ], - doc: None, - sugar: true, - }, - ], - doc: None, - location: 0..35, - name: "User", - opaque: true, - parameters: [], - public: true, - typed_parameters: [], - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/parse_tuple.snap b/crates/aiken-lang/src/tests/snapshots/parse_tuple.snap deleted file mode 100644 index 12489418a..000000000 --- a/crates/aiken-lang/src/tests/snapshots/parse_tuple.snap +++ /dev/null @@ -1,116 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn foo() {\n let tuple = (1, 2, 3, 4)\n tuple.1st + tuple.2nd + tuple.3rd + tuple.4th\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: Sequence { - location: 13..85, - expressions: [ - Assignment { - location: 13..37, - value: Tuple { - location: 25..37, - elems: [ - Int { - location: 26..27, - value: "1", - base: Decimal { - numeric_underscore: false, - }, - }, - Int { - location: 29..30, - value: "2", - base: Decimal { - numeric_underscore: false, - }, - }, - Int { - location: 32..33, - value: "3", - base: Decimal { - numeric_underscore: false, - }, - }, - Int { - location: 35..36, - value: "4", - base: Decimal { - numeric_underscore: false, - }, - }, - ], - }, - pattern: Var { - location: 17..22, - name: "tuple", - }, - kind: Let, - annotation: None, - }, - BinOp { - location: 40..85, - name: AddInt, - left: BinOp { - location: 40..73, - name: AddInt, - left: BinOp { - location: 40..61, - name: AddInt, - left: TupleIndex { - location: 40..49, - index: 0, - tuple: Var { - location: 40..45, - name: "tuple", - }, - }, - right: TupleIndex { - location: 52..61, - index: 1, - tuple: Var { - location: 52..57, - name: "tuple", - }, - }, - }, - right: TupleIndex { - location: 64..73, - index: 2, - tuple: Var { - location: 64..69, - name: "tuple", - }, - }, - }, - right: TupleIndex { - location: 76..85, - index: 3, - tuple: Var { - location: 76..81, - name: "tuple", - }, - }, - }, - ], - }, - doc: None, - location: 0..8, - name: "foo", - public: false, - return_annotation: None, - return_type: (), - end_position: 86, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/parse_tuple2.snap b/crates/aiken-lang/src/tests/snapshots/parse_tuple2.snap deleted file mode 100644 index 0a4185b72..000000000 --- a/crates/aiken-lang/src/tests/snapshots/parse_tuple2.snap +++ /dev/null @@ -1,75 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\nfn foo() {\n let a = foo(14)\n (a, 42)\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [], - body: Sequence { - location: 13..38, - expressions: [ - Assignment { - location: 13..28, - value: Call { - arguments: [ - CallArg { - label: None, - location: 25..27, - value: Int { - location: 25..27, - value: "14", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - ], - fun: Var { - location: 21..24, - name: "foo", - }, - location: 21..28, - }, - pattern: Var { - location: 17..18, - name: "a", - }, - kind: Let, - annotation: None, - }, - Tuple { - location: 31..38, - elems: [ - Var { - location: 32..33, - name: "a", - }, - Int { - location: 35..37, - value: "42", - base: Decimal { - numeric_underscore: false, - }, - }, - ], - }, - ], - }, - doc: None, - location: 0..8, - name: "foo", - public: false, - return_annotation: None, - return_type: (), - end_position: 39, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/pipeline.snap b/crates/aiken-lang/src/tests/snapshots/pipeline.snap deleted file mode 100644 index 0112b871e..000000000 --- a/crates/aiken-lang/src/tests/snapshots/pipeline.snap +++ /dev/null @@ -1,72 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\npub fn thing(thing a: Int) {\n a + 2\n |> add_one\n |> add_one\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [ - Arg { - arg_name: Named { - name: "a", - label: "thing", - location: 13..20, - is_validator_param: false, - }, - location: 13..25, - annotation: Some( - Constructor { - location: 22..25, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: PipeLine { - expressions: [ - BinOp { - location: 31..36, - name: AddInt, - left: Var { - location: 31..32, - name: "a", - }, - right: Int { - location: 35..36, - value: "2", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - Var { - location: 42..49, - name: "add_one", - }, - Var { - location: 55..62, - name: "add_one", - }, - ], - one_liner: false, - }, - doc: None, - location: 0..26, - name: "thing", - public: true, - return_annotation: None, - return_type: (), - end_position: 63, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/plain_bytearray_literals.snap b/crates/aiken-lang/src/tests/snapshots/plain_bytearray_literals.snap deleted file mode 100644 index c97df391d..000000000 --- a/crates/aiken-lang/src/tests/snapshots/plain_bytearray_literals.snap +++ /dev/null @@ -1,35 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\npub const my_policy_id = #[0, 170, 255]\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - ModuleConstant( - ModuleConstant { - doc: None, - location: 0..39, - public: true, - name: "my_policy_id", - annotation: None, - value: ByteArray { - location: 25..39, - bytes: [ - 0, - 170, - 255, - ], - preferred_format: ArrayOfBytes( - Decimal { - numeric_underscore: false, - }, - ), - }, - tipo: (), - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/plus_binop.snap b/crates/aiken-lang/src/tests/snapshots/plus_binop.snap deleted file mode 100644 index 9f52dc68e..000000000 --- a/crates/aiken-lang/src/tests/snapshots/plus_binop.snap +++ /dev/null @@ -1,59 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\npub fn add_one(a) -> Int {\n a + 1\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [ - Arg { - arg_name: Named { - name: "a", - label: "a", - location: 15..16, - is_validator_param: false, - }, - location: 15..16, - annotation: None, - tipo: (), - }, - ], - body: BinOp { - location: 29..34, - name: AddInt, - left: Var { - location: 29..30, - name: "a", - }, - right: Int { - location: 33..34, - value: "1", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - doc: None, - location: 0..24, - name: "add_one", - public: true, - return_annotation: Some( - Constructor { - location: 21..24, - module: None, - name: "Int", - arguments: [], - }, - ), - return_type: (), - end_position: 35, - can_error: true, - }, - ), - ], - kind: Validator, -} diff --git a/crates/aiken-lang/src/tests/snapshots/when.snap b/crates/aiken-lang/src/tests/snapshots/when.snap deleted file mode 100644 index 387ffad84..000000000 --- a/crates/aiken-lang/src/tests/snapshots/when.snap +++ /dev/null @@ -1,162 +0,0 @@ ---- -source: crates/aiken-lang/src/tests/parser.rs -description: "Code:\n\npub fn wow2(a: Int){\n when a is {\n 2 -> 3\n 1 | 4 | 5 -> {\n let amazing = 5\n amazing\n }\n 3 -> 9\n _ -> 4\n }\n}\n" ---- -Module { - name: "", - docs: [], - type_info: (), - definitions: [ - Fn( - Function { - arguments: [ - Arg { - arg_name: Named { - name: "a", - label: "a", - location: 12..13, - is_validator_param: false, - }, - location: 12..18, - annotation: Some( - Constructor { - location: 15..18, - module: None, - name: "Int", - arguments: [], - }, - ), - tipo: (), - }, - ], - body: When { - location: 23..132, - subject: Var { - location: 28..29, - name: "a", - }, - clauses: [ - UntypedClause { - location: 39..45, - patterns: [ - Int { - location: 39..40, - value: "2", - base: Decimal { - numeric_underscore: false, - }, - }, - ], - guard: None, - then: Int { - location: 44..45, - value: "3", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - UntypedClause { - location: 50..106, - patterns: [ - Int { - location: 50..51, - value: "1", - base: Decimal { - numeric_underscore: false, - }, - }, - Int { - location: 54..55, - value: "4", - base: Decimal { - numeric_underscore: false, - }, - }, - Int { - location: 58..59, - value: "5", - base: Decimal { - numeric_underscore: false, - }, - }, - ], - guard: None, - then: Sequence { - location: 71..100, - expressions: [ - Assignment { - location: 71..86, - value: Int { - location: 85..86, - value: "5", - base: Decimal { - numeric_underscore: false, - }, - }, - pattern: Var { - location: 75..82, - name: "amazing", - }, - kind: Let, - annotation: None, - }, - Var { - location: 93..100, - name: "amazing", - }, - ], - }, - }, - UntypedClause { - location: 111..117, - patterns: [ - Int { - location: 111..112, - value: "3", - base: Decimal { - numeric_underscore: false, - }, - }, - ], - guard: None, - then: Int { - location: 116..117, - value: "9", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - UntypedClause { - location: 122..128, - patterns: [ - Discard { - name: "_", - location: 122..123, - }, - ], - guard: None, - then: Int { - location: 127..128, - value: "4", - base: Decimal { - numeric_underscore: false, - }, - }, - }, - ], - }, - doc: None, - location: 0..19, - name: "wow2", - public: true, - return_annotation: None, - return_type: (), - end_position: 133, - can_error: true, - }, - ), - ], - kind: Validator, -} From 93e010b345bca54974124ab248250d1824019935 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 5 Jul 2023 13:57:34 +0200 Subject: [PATCH 25/31] Replace 'public' utils with a more generic 'optional_flag' The 'public' util was arguably not really adding much except a layer of indirection. In the end, one useful parsing behavior to abstract is the idea of 'optional flag' that we use for both 'pub' and 'opaque' keywords. --- .../src/parser/definition/constant.rs | 5 +- .../src/parser/definition/data_type.rs | 49 +++++++++---------- .../src/parser/definition/function.rs | 7 ++- .../src/parser/definition/type_alias.rs | 7 ++- crates/aiken-lang/src/parser/utils.rs | 4 +- 5 files changed, 34 insertions(+), 38 deletions(-) diff --git a/crates/aiken-lang/src/parser/definition/constant.rs b/crates/aiken-lang/src/parser/definition/constant.rs index 137111ae6..093c9d690 100644 --- a/crates/aiken-lang/src/parser/definition/constant.rs +++ b/crates/aiken-lang/src/parser/definition/constant.rs @@ -6,8 +6,7 @@ use crate::{ }; pub fn parser() -> impl Parser { - utils::public() - .or_not() + utils::optional_flag(Token::Pub) .then_ignore(just(Token::Const)) .then(select! {Token::Name{name} => name}) .then( @@ -21,7 +20,7 @@ pub fn parser() -> impl Parser impl Parser impl Parser { - utils::public() - .or_not() + utils::optional_flag(Token::Pub) .then_ignore(just(Token::Fn)) .then(select! {Token::Name {name} => name}) .then( @@ -25,7 +24,7 @@ pub fn parser() -> impl Parser impl Parser impl Parser { - utils::public() - .or_not() + utils::optional_flag(Token::Pub) .then(utils::type_name_with_args()) .then_ignore(just(Token::Equal)) .then(annotation()) - .map_with_span(|((opt_pub, (alias, parameters)), annotation), span| { + .map_with_span(|((public, (alias, parameters)), annotation), span| { ast::UntypedDefinition::TypeAlias(ast::TypeAlias { alias, annotation, doc: None, location: span, parameters: parameters.unwrap_or_default(), - public: opt_pub.is_some(), + public, tipo: (), }) }) diff --git a/crates/aiken-lang/src/parser/utils.rs b/crates/aiken-lang/src/parser/utils.rs index 0ce3e404b..600997699 100644 --- a/crates/aiken-lang/src/parser/utils.rs +++ b/crates/aiken-lang/src/parser/utils.rs @@ -7,8 +7,8 @@ use super::{ token::{Base, Token}, }; -pub fn public() -> impl Parser { - just(Token::Pub).ignored() +pub fn optional_flag(token: Token) -> impl Parser { + just(token).ignored().or_not().map(|v| v.is_some()) } pub fn bytearray( From 44eb501d7858f384a5f5ab7a266d1e51018ab553 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 5 Jul 2023 14:01:13 +0200 Subject: [PATCH 26/31] Favor pattern-match over if-else when parsing assignment kinds Equality on a union-type is potentially dangerous as the compiler won't complain if we add a new case that we don't cover. Reversing the assignment by yielding a `Token` for a given `AssignmentKind`. This way we can use a pattern-match that got us covered for future cases. --- crates/aiken-lang/src/parser/expr/assignment.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/aiken-lang/src/parser/expr/assignment.rs b/crates/aiken-lang/src/parser/expr/assignment.rs index c9bd1c1cb..8e86ae16e 100644 --- a/crates/aiken-lang/src/parser/expr/assignment.rs +++ b/crates/aiken-lang/src/parser/expr/assignment.rs @@ -9,23 +9,22 @@ use crate::{ pub fn let_( r: Recursive<'_, Token, UntypedExpr, ParseError>, ) -> impl Parser + '_ { - assignment(r, Token::Let) + assignment(r, ast::AssignmentKind::Let) } pub fn expect( r: Recursive<'_, Token, UntypedExpr, ParseError>, ) -> impl Parser + '_ { - assignment(r, Token::Expect) + assignment(r, ast::AssignmentKind::Expect) } fn assignment( r: Recursive<'_, Token, UntypedExpr, ParseError>, - keyword: Token, + kind: ast::AssignmentKind, ) -> impl Parser + '_ { - let kind = if keyword == Token::Let { - ast::AssignmentKind::Let - } else { - ast::AssignmentKind::Expect + let keyword = match kind { + ast::AssignmentKind::Let => Token::Let, + ast::AssignmentKind::Expect => Token::Expect, }; just(keyword) From e15cdaf2484d497f3dfca89d6ace42e007c20063 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 5 Jul 2023 14:10:47 +0200 Subject: [PATCH 27/31] Move 'utils::bytearray' to 'expr/bytearray' --- .../src/parser/definition/constant.rs | 12 ++- .../aiken-lang/src/parser/expr/bytearray.rs | 84 ++++++++++++++++++- crates/aiken-lang/src/parser/expr/mod.rs | 2 +- crates/aiken-lang/src/parser/utils.rs | 77 +---------------- 4 files changed, 89 insertions(+), 86 deletions(-) diff --git a/crates/aiken-lang/src/parser/definition/constant.rs b/crates/aiken-lang/src/parser/definition/constant.rs index 093c9d690..8963707b3 100644 --- a/crates/aiken-lang/src/parser/definition/constant.rs +++ b/crates/aiken-lang/src/parser/definition/constant.rs @@ -2,7 +2,7 @@ use chumsky::prelude::*; use crate::{ ast, - parser::{annotation, error::ParseError, token::Token, utils}, + parser::{annotation, error::ParseError, expr::bytearray::bytearray, token::Token, utils}, }; pub fn parser() -> impl Parser { @@ -48,12 +48,10 @@ pub fn value() -> impl Parser { }); let constant_bytearray_parser = - utils::bytearray().map_with_span(|(preferred_format, bytes), span| { - ast::Constant::ByteArray { - location: span, - bytes, - preferred_format, - } + bytearray().map_with_span(|(preferred_format, bytes), span| ast::Constant::ByteArray { + location: span, + bytes, + preferred_format, }); choice(( diff --git a/crates/aiken-lang/src/parser/expr/bytearray.rs b/crates/aiken-lang/src/parser/expr/bytearray.rs index 756f63127..0b46c4db5 100644 --- a/crates/aiken-lang/src/parser/expr/bytearray.rs +++ b/crates/aiken-lang/src/parser/expr/bytearray.rs @@ -1,18 +1,98 @@ use chumsky::prelude::*; use crate::{ + ast, expr::UntypedExpr, - parser::{error::ParseError, token::Token, utils}, + parser::{ + error::{self, ParseError}, + token::{Base, Token}, + }, }; pub fn parser() -> impl Parser { - utils::bytearray().map_with_span(|(preferred_format, bytes), span| UntypedExpr::ByteArray { + bytearray().map_with_span(|(preferred_format, bytes), span| UntypedExpr::ByteArray { location: span, bytes, preferred_format, }) } +pub fn bytearray( +) -> impl Parser), Error = ParseError> { + choice((array_of_bytes(), hex_string(), utf8_string())) +} + +pub fn array_of_bytes( +) -> impl Parser), Error = ParseError> { + just(Token::Hash) + .ignore_then( + select! {Token::Int {value, base, ..} => (value, base)} + .validate(|(value, base), span, emit| { + let byte: u8 = match value.parse() { + Ok(b) => b, + Err(_) => { + emit(ParseError::expected_input_found( + span, + None, + Some(error::Pattern::Byte), + )); + 0 + } + }; + (byte, base) + }) + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftSquare), just(Token::RightSquare)), + ) + .validate(|bytes, span, emit| { + let base = bytes.iter().fold(Ok(None), |acc, (_, base)| match acc { + Ok(None) => Ok(Some(base)), + Ok(Some(previous_base)) if previous_base == base => Ok(Some(base)), + _ => Err(()), + }); + + let base = match base { + Err(()) => { + emit(ParseError::hybrid_notation_in_bytearray(span)); + Base::Decimal { + numeric_underscore: false, + } + } + Ok(None) => Base::Decimal { + numeric_underscore: false, + }, + Ok(Some(base)) => *base, + }; + + (bytes.into_iter().map(|(b, _)| b).collect::>(), base) + }) + .map(|(bytes, base)| (ast::ByteArrayFormatPreference::ArrayOfBytes(base), bytes)) +} + +pub fn hex_string( +) -> impl Parser), Error = ParseError> { + just(Token::Hash) + .ignore_then( + select! {Token::ByteString {value} => value}.validate(|value, span, emit| { + match hex::decode(value) { + Ok(bytes) => bytes, + Err(_) => { + emit(ParseError::malformed_base16_string_literal(span)); + vec![] + } + } + }), + ) + .map(|token| (ast::ByteArrayFormatPreference::HexadecimalString, token)) +} + +pub fn utf8_string( +) -> impl Parser), Error = ParseError> { + select! {Token::ByteString {value} => value.into_bytes() } + .map(|token| (ast::ByteArrayFormatPreference::Utf8String, token)) +} + #[cfg(test)] mod tests { use crate::assert_expr; diff --git a/crates/aiken-lang/src/parser/expr/mod.rs b/crates/aiken-lang/src/parser/expr/mod.rs index db134436f..054550217 100644 --- a/crates/aiken-lang/src/parser/expr/mod.rs +++ b/crates/aiken-lang/src/parser/expr/mod.rs @@ -5,7 +5,7 @@ mod anonymous_binop; pub mod anonymous_function; pub mod assignment; mod block; -mod bytearray; +pub(crate) mod bytearray; mod if_else; mod int; mod list; diff --git a/crates/aiken-lang/src/parser/utils.rs b/crates/aiken-lang/src/parser/utils.rs index 600997699..f45a639e1 100644 --- a/crates/aiken-lang/src/parser/utils.rs +++ b/crates/aiken-lang/src/parser/utils.rs @@ -1,86 +1,11 @@ use chumsky::prelude::*; -use crate::ast; - -use super::{ - error::{self, ParseError}, - token::{Base, Token}, -}; +use super::{error::ParseError, token::Token}; pub fn optional_flag(token: Token) -> impl Parser { just(token).ignored().or_not().map(|v| v.is_some()) } -pub fn bytearray( -) -> impl Parser), Error = ParseError> { - let bytearray_list_parser = just(Token::Hash) - .ignore_then( - select! {Token::Int {value, base, ..} => (value, base)} - .validate(|(value, base), span, emit| { - let byte: u8 = match value.parse() { - Ok(b) => b, - Err(_) => { - emit(ParseError::expected_input_found( - span, - None, - Some(error::Pattern::Byte), - )); - 0 - } - }; - (byte, base) - }) - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftSquare), just(Token::RightSquare)), - ) - .validate(|bytes, span, emit| { - let base = bytes.iter().fold(Ok(None), |acc, (_, base)| match acc { - Ok(None) => Ok(Some(base)), - Ok(Some(previous_base)) if previous_base == base => Ok(Some(base)), - _ => Err(()), - }); - - let base = match base { - Err(()) => { - emit(ParseError::hybrid_notation_in_bytearray(span)); - Base::Decimal { - numeric_underscore: false, - } - } - Ok(None) => Base::Decimal { - numeric_underscore: false, - }, - Ok(Some(base)) => *base, - }; - - (bytes.into_iter().map(|(b, _)| b).collect::>(), base) - }) - .map(|(bytes, base)| (ast::ByteArrayFormatPreference::ArrayOfBytes(base), bytes)); - - let bytearray_hexstring_parser = - just(Token::Hash) - .ignore_then(select! {Token::ByteString {value} => value}.validate( - |value, span, emit| match hex::decode(value) { - Ok(bytes) => bytes, - Err(_) => { - emit(ParseError::malformed_base16_string_literal(span)); - vec![] - } - }, - )) - .map(|token| (ast::ByteArrayFormatPreference::HexadecimalString, token)); - - let bytearray_utf8_parser = select! {Token::ByteString {value} => value.into_bytes() } - .map(|token| (ast::ByteArrayFormatPreference::Utf8String, token)); - - choice(( - bytearray_list_parser, - bytearray_hexstring_parser, - bytearray_utf8_parser, - )) -} - pub fn type_name_with_args() -> impl Parser>), Error = ParseError> { just(Token::Type).ignore_then( From 66296df9c3886bab77d0a8d2281b2dbef19d3d57 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 5 Jul 2023 14:36:23 +0200 Subject: [PATCH 28/31] Move parsing of literals under new 'literal' parser module group Also moved the logic for 'int' and 'string' there though it is trivial. Yet, for bytearray, it tidies things nicely by removing them from the 'utils' module. --- crates/aiken-lang/src/parser.rs | 1 + .../src/parser/definition/constant.rs | 6 +- .../aiken-lang/src/parser/expr/bytearray.rs | 89 +------------------ crates/aiken-lang/src/parser/expr/int.rs | 12 ++- crates/aiken-lang/src/parser/expr/string.rs | 4 +- .../src/parser/literal/bytearray.rs | 87 ++++++++++++++++++ crates/aiken-lang/src/parser/literal/int.rs | 10 +++ crates/aiken-lang/src/parser/literal/mod.rs | 3 + .../aiken-lang/src/parser/literal/string.rs | 7 ++ 9 files changed, 123 insertions(+), 96 deletions(-) create mode 100644 crates/aiken-lang/src/parser/literal/bytearray.rs create mode 100644 crates/aiken-lang/src/parser/literal/int.rs create mode 100644 crates/aiken-lang/src/parser/literal/mod.rs create mode 100644 crates/aiken-lang/src/parser/literal/string.rs diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 78c1e87c6..d4af7017c 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -4,6 +4,7 @@ pub mod error; pub mod expr; pub mod extra; pub mod lexer; +pub mod literal; pub mod pattern; pub mod token; mod utils; diff --git a/crates/aiken-lang/src/parser/definition/constant.rs b/crates/aiken-lang/src/parser/definition/constant.rs index 8963707b3..1e2629ed5 100644 --- a/crates/aiken-lang/src/parser/definition/constant.rs +++ b/crates/aiken-lang/src/parser/definition/constant.rs @@ -2,7 +2,9 @@ use chumsky::prelude::*; use crate::{ ast, - parser::{annotation, error::ParseError, expr::bytearray::bytearray, token::Token, utils}, + parser::{ + annotation, error::ParseError, literal::bytearray::parser as bytearray, token::Token, utils, + }, }; pub fn parser() -> impl Parser { @@ -48,7 +50,7 @@ pub fn value() -> impl Parser { }); let constant_bytearray_parser = - bytearray().map_with_span(|(preferred_format, bytes), span| ast::Constant::ByteArray { + bytearray(|bytes, preferred_format, span| ast::Constant::ByteArray { location: span, bytes, preferred_format, diff --git a/crates/aiken-lang/src/parser/expr/bytearray.rs b/crates/aiken-lang/src/parser/expr/bytearray.rs index 0b46c4db5..2829b8dc3 100644 --- a/crates/aiken-lang/src/parser/expr/bytearray.rs +++ b/crates/aiken-lang/src/parser/expr/bytearray.rs @@ -1,98 +1,17 @@ use chumsky::prelude::*; -use crate::{ - ast, - expr::UntypedExpr, - parser::{ - error::{self, ParseError}, - token::{Base, Token}, - }, +use crate::parser::{ + error::ParseError, expr::UntypedExpr, literal::bytearray::parser as bytearray, token::Token, }; pub fn parser() -> impl Parser { - bytearray().map_with_span(|(preferred_format, bytes), span| UntypedExpr::ByteArray { - location: span, + bytearray(|bytes, preferred_format, location| UntypedExpr::ByteArray { + location, bytes, preferred_format, }) } -pub fn bytearray( -) -> impl Parser), Error = ParseError> { - choice((array_of_bytes(), hex_string(), utf8_string())) -} - -pub fn array_of_bytes( -) -> impl Parser), Error = ParseError> { - just(Token::Hash) - .ignore_then( - select! {Token::Int {value, base, ..} => (value, base)} - .validate(|(value, base), span, emit| { - let byte: u8 = match value.parse() { - Ok(b) => b, - Err(_) => { - emit(ParseError::expected_input_found( - span, - None, - Some(error::Pattern::Byte), - )); - 0 - } - }; - (byte, base) - }) - .separated_by(just(Token::Comma)) - .allow_trailing() - .delimited_by(just(Token::LeftSquare), just(Token::RightSquare)), - ) - .validate(|bytes, span, emit| { - let base = bytes.iter().fold(Ok(None), |acc, (_, base)| match acc { - Ok(None) => Ok(Some(base)), - Ok(Some(previous_base)) if previous_base == base => Ok(Some(base)), - _ => Err(()), - }); - - let base = match base { - Err(()) => { - emit(ParseError::hybrid_notation_in_bytearray(span)); - Base::Decimal { - numeric_underscore: false, - } - } - Ok(None) => Base::Decimal { - numeric_underscore: false, - }, - Ok(Some(base)) => *base, - }; - - (bytes.into_iter().map(|(b, _)| b).collect::>(), base) - }) - .map(|(bytes, base)| (ast::ByteArrayFormatPreference::ArrayOfBytes(base), bytes)) -} - -pub fn hex_string( -) -> impl Parser), Error = ParseError> { - just(Token::Hash) - .ignore_then( - select! {Token::ByteString {value} => value}.validate(|value, span, emit| { - match hex::decode(value) { - Ok(bytes) => bytes, - Err(_) => { - emit(ParseError::malformed_base16_string_literal(span)); - vec![] - } - } - }), - ) - .map(|token| (ast::ByteArrayFormatPreference::HexadecimalString, token)) -} - -pub fn utf8_string( -) -> impl Parser), Error = ParseError> { - select! {Token::ByteString {value} => value.into_bytes() } - .map(|token| (ast::ByteArrayFormatPreference::Utf8String, token)) -} - #[cfg(test)] mod tests { use crate::assert_expr; diff --git a/crates/aiken-lang/src/parser/expr/int.rs b/crates/aiken-lang/src/parser/expr/int.rs index 6e4f628cc..5ed56f13d 100644 --- a/crates/aiken-lang/src/parser/expr/int.rs +++ b/crates/aiken-lang/src/parser/expr/int.rs @@ -2,16 +2,14 @@ use chumsky::prelude::*; use crate::{ expr::UntypedExpr, - parser::{error::ParseError, token::Token}, + parser::{error::ParseError, literal::int::parser as int, token::Token}, }; pub fn parser() -> impl Parser { - select! { Token::Int {value, base} => (value, base)}.map_with_span(|(value, base), span| { - UntypedExpr::Int { - location: span, - value, - base, - } + int().map_with_span(|(value, base), span| UntypedExpr::Int { + location: span, + value, + base, }) } diff --git a/crates/aiken-lang/src/parser/expr/string.rs b/crates/aiken-lang/src/parser/expr/string.rs index d1c07acfd..f1361d646 100644 --- a/crates/aiken-lang/src/parser/expr/string.rs +++ b/crates/aiken-lang/src/parser/expr/string.rs @@ -3,11 +3,11 @@ use chumsky::prelude::*; use crate::{ ast, expr::UntypedExpr, - parser::{error::ParseError, token::Token}, + parser::{error::ParseError, literal::string::parser as string, token::Token}, }; pub fn parser() -> impl Parser { - select! {Token::String {value} => value}.map_with_span(|value, span| UntypedExpr::String { + string().map_with_span(|value, span| UntypedExpr::String { location: span, value, }) diff --git a/crates/aiken-lang/src/parser/literal/bytearray.rs b/crates/aiken-lang/src/parser/literal/bytearray.rs new file mode 100644 index 000000000..5516b9819 --- /dev/null +++ b/crates/aiken-lang/src/parser/literal/bytearray.rs @@ -0,0 +1,87 @@ +use chumsky::prelude::*; + +use crate::{ + ast, + parser::{ + error::{self, ParseError}, + token::{Base, Token}, + }, +}; + +pub fn parser( + into: impl Fn(Vec, ast::ByteArrayFormatPreference, ast::Span) -> A, +) -> impl Parser { + choice((array_of_bytes(), hex_string(), utf8_string())) + .map_with_span(move |(preferred_format, bytes), span| into(bytes, preferred_format, span)) +} + +pub fn array_of_bytes( +) -> impl Parser), Error = ParseError> { + just(Token::Hash) + .ignore_then( + select! {Token::Int {value, base, ..} => (value, base)} + .validate(|(value, base), span, emit| { + let byte: u8 = match value.parse() { + Ok(b) => b, + Err(_) => { + emit(ParseError::expected_input_found( + span, + None, + Some(error::Pattern::Byte), + )); + 0 + } + }; + (byte, base) + }) + .separated_by(just(Token::Comma)) + .allow_trailing() + .delimited_by(just(Token::LeftSquare), just(Token::RightSquare)), + ) + .validate(|bytes, span, emit| { + let base = bytes.iter().fold(Ok(None), |acc, (_, base)| match acc { + Ok(None) => Ok(Some(base)), + Ok(Some(previous_base)) if previous_base == base => Ok(Some(base)), + _ => Err(()), + }); + + let base = match base { + Err(()) => { + emit(ParseError::hybrid_notation_in_bytearray(span)); + Base::Decimal { + numeric_underscore: false, + } + } + Ok(None) => Base::Decimal { + numeric_underscore: false, + }, + Ok(Some(base)) => *base, + }; + + (bytes.into_iter().map(|(b, _)| b).collect::>(), base) + }) + .map(|(bytes, base)| (ast::ByteArrayFormatPreference::ArrayOfBytes(base), bytes)) +} + +pub fn hex_string( +) -> impl Parser), Error = ParseError> { + just(Token::Hash) + .ignore_then( + select! {Token::ByteString {value} => value}.validate(|value, span, emit| { + match hex::decode(value) { + Ok(bytes) => bytes, + Err(_) => { + emit(ParseError::malformed_base16_string_literal(span)); + vec![] + } + } + }), + ) + .map(|token| (ast::ByteArrayFormatPreference::HexadecimalString, token)) +} + +pub fn utf8_string( +) -> impl Parser), Error = ParseError> { + select! {Token::ByteString {value} => value.into_bytes() } + .map(|token| (ast::ByteArrayFormatPreference::Utf8String, token)) +} diff --git a/crates/aiken-lang/src/parser/literal/int.rs b/crates/aiken-lang/src/parser/literal/int.rs new file mode 100644 index 000000000..05e6cdb1c --- /dev/null +++ b/crates/aiken-lang/src/parser/literal/int.rs @@ -0,0 +1,10 @@ +use chumsky::prelude::*; + +use crate::parser::{ + error::ParseError, + token::{Base, Token}, +}; + +pub fn parser() -> impl Parser { + select! { Token::Int {value, base} => (value, base)} +} diff --git a/crates/aiken-lang/src/parser/literal/mod.rs b/crates/aiken-lang/src/parser/literal/mod.rs new file mode 100644 index 000000000..6a67a32e4 --- /dev/null +++ b/crates/aiken-lang/src/parser/literal/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod bytearray; +pub(crate) mod int; +pub(crate) mod string; diff --git a/crates/aiken-lang/src/parser/literal/string.rs b/crates/aiken-lang/src/parser/literal/string.rs new file mode 100644 index 000000000..d1d88cf85 --- /dev/null +++ b/crates/aiken-lang/src/parser/literal/string.rs @@ -0,0 +1,7 @@ +use chumsky::prelude::*; + +use crate::parser::{error::ParseError, token::Token}; + +pub fn parser() -> impl Parser { + select! {Token::String {value} => value} +} From 4f6defcf3e4f458fe2facf1ba28964ab535492ea Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 5 Jul 2023 14:42:14 +0200 Subject: [PATCH 29/31] =?UTF-8?q?rename:=20'r'=20=E2=86=92=20'expression'?= =?UTF-8?q?=20&=20'seq=5Fr'=20=E2=86=92=20'sequence'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Better readability. --- crates/aiken-lang/src/parser/annotation.rs | 16 +++++--- .../src/parser/expr/anonymous_function.rs | 4 +- crates/aiken-lang/src/parser/expr/block.rs | 6 +-- crates/aiken-lang/src/parser/expr/if_else.rs | 40 +++++++++++-------- crates/aiken-lang/src/parser/expr/mod.rs | 26 ++++++------ crates/aiken-lang/src/parser/expr/sequence.rs | 14 +++---- .../aiken-lang/src/parser/expr/when/guard.rs | 4 +- crates/aiken-lang/src/parser/pattern/mod.rs | 18 +++++---- 8 files changed, 71 insertions(+), 57 deletions(-) diff --git a/crates/aiken-lang/src/parser/annotation.rs b/crates/aiken-lang/src/parser/annotation.rs index 7b0f52444..8afdac558 100644 --- a/crates/aiken-lang/src/parser/annotation.rs +++ b/crates/aiken-lang/src/parser/annotation.rs @@ -5,7 +5,7 @@ use crate::ast; use super::{error::ParseError, token::Token}; pub fn parser() -> impl Parser { - recursive(|r| { + recursive(|expression| { choice(( // Type hole select! {Token::DiscardName { name } => name}.map_with_span(|name, span| { @@ -15,7 +15,8 @@ pub fn parser() -> impl Parser { } }), // Tuple - r.clone() + expression + .clone() .separated_by(just(Token::Comma)) .at_least(2) .allow_trailing() @@ -30,13 +31,14 @@ pub fn parser() -> impl Parser { // Function just(Token::Fn) .ignore_then( - r.clone() + expression + .clone() .separated_by(just(Token::Comma)) .allow_trailing() .delimited_by(just(Token::LeftParen), just(Token::RightParen)), ) .then_ignore(just(Token::RArrow)) - .then(r.clone()) + .then(expression.clone()) .map_with_span(|(arguments, ret), span| ast::Annotation::Fn { location: span, arguments, @@ -45,7 +47,8 @@ pub fn parser() -> impl Parser { // Constructor function select! {Token::UpName { name } => name} .then( - r.clone() + expression + .clone() .separated_by(just(Token::Comma)) .allow_trailing() .delimited_by(just(Token::Less), just(Token::Greater)) @@ -63,7 +66,8 @@ pub fn parser() -> impl Parser { just(Token::Dot) .ignore_then(select! {Token::UpName {name} => name}) .then( - r.separated_by(just(Token::Comma)) + expression + .separated_by(just(Token::Comma)) .allow_trailing() .delimited_by(just(Token::Less), just(Token::Greater)) .or_not(), diff --git a/crates/aiken-lang/src/parser/expr/anonymous_function.rs b/crates/aiken-lang/src/parser/expr/anonymous_function.rs index 5485a75e7..39bdb0448 100644 --- a/crates/aiken-lang/src/parser/expr/anonymous_function.rs +++ b/crates/aiken-lang/src/parser/expr/anonymous_function.rs @@ -7,7 +7,7 @@ use crate::{ }; pub fn parser( - seq_r: Recursive<'_, Token, UntypedExpr, ParseError>, + sequence: Recursive<'_, Token, UntypedExpr, ParseError>, ) -> impl Parser + '_ { just(Token::Fn) .ignore_then( @@ -17,7 +17,7 @@ pub fn parser( .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))) + .then(sequence.delimited_by(just(Token::LeftBrace), just(Token::RightBrace))) .map_with_span( |((arguments, return_annotation), body), span| UntypedExpr::Fn { arguments, diff --git a/crates/aiken-lang/src/parser/expr/block.rs b/crates/aiken-lang/src/parser/expr/block.rs index e555de05f..f40424e74 100644 --- a/crates/aiken-lang/src/parser/expr/block.rs +++ b/crates/aiken-lang/src/parser/expr/block.rs @@ -6,13 +6,13 @@ use crate::{ }; pub fn parser( - seq_r: Recursive<'_, Token, UntypedExpr, ParseError>, + sequence: Recursive<'_, Token, UntypedExpr, ParseError>, ) -> impl Parser + '_ { choice(( - seq_r + sequence .clone() .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), - seq_r.clone().delimited_by( + sequence.clone().delimited_by( choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), just(Token::RightParen), ), diff --git a/crates/aiken-lang/src/parser/expr/if_else.rs b/crates/aiken-lang/src/parser/expr/if_else.rs index d0efcfec1..3d9c5deb9 100644 --- a/crates/aiken-lang/src/parser/expr/if_else.rs +++ b/crates/aiken-lang/src/parser/expr/if_else.rs @@ -9,31 +9,37 @@ use crate::{ use super::block; pub fn parser<'a>( - seq_r: Recursive<'a, Token, UntypedExpr, ParseError>, - r: Recursive<'a, Token, UntypedExpr, ParseError>, + sequence: Recursive<'a, Token, UntypedExpr, ParseError>, + expression: Recursive<'a, Token, UntypedExpr, ParseError>, ) -> impl Parser + 'a { just(Token::If) - .ignore_then(r.clone().then(block(seq_r.clone())).map_with_span( - |(condition, body), span| ast::IfBranch { - condition, - body, - location: span, - }, - )) + .ignore_then( + expression + .clone() + .then(block(sequence.clone())) + .map_with_span(|(condition, body), span| ast::IfBranch { + condition, + body, + location: span, + }), + ) .then( just(Token::Else) .ignore_then(just(Token::If)) - .ignore_then(r.clone().then(block(seq_r.clone())).map_with_span( - |(condition, body), span| ast::IfBranch { - condition, - body, - location: span, - }, - )) + .ignore_then( + expression + .clone() + .then(block(sequence.clone())) + .map_with_span(|(condition, body), span| ast::IfBranch { + condition, + body, + location: span, + }), + ) .repeated(), ) .then_ignore(just(Token::Else)) - .then(block(seq_r)) + .then(block(sequence)) .map_with_span(|((first, alternative_branches), final_else), span| { let mut branches = vec1::vec1![first]; diff --git a/crates/aiken-lang/src/parser/expr/mod.rs b/crates/aiken-lang/src/parser/expr/mod.rs index 054550217..37d676bff 100644 --- a/crates/aiken-lang/src/parser/expr/mod.rs +++ b/crates/aiken-lang/src/parser/expr/mod.rs @@ -40,9 +40,9 @@ use crate::{ use super::{error::ParseError, token::Token}; pub fn parser( - seq_r: Recursive<'_, Token, UntypedExpr, ParseError>, + sequence: Recursive<'_, Token, UntypedExpr, ParseError>, ) -> impl Parser + '_ { - recursive(|r| { + recursive(|expression| { let field_access_constructor = select! {Token::Name { name } => name} .map_with_span(|module, span| (module, span)) .then_ignore(just(Token::Dot)) @@ -59,20 +59,20 @@ pub fn parser( let expr_unit_parser = choice(( string(), int(), - record_update(r.clone()), - record(r.clone()), + record_update(expression.clone()), + record(expression.clone()), field_access_constructor, var(), - tuple(r.clone()), + tuple(expression.clone()), bytearray(), - list(r.clone()), - anonymous_function(seq_r.clone()), + list(expression.clone()), + anonymous_function(sequence.clone()), anonymous_binop(), - block(seq_r.clone()), - when(r.clone()), - assignment::let_(r.clone()), - assignment::expect(r.clone()), - if_else(seq_r, r.clone()), + 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 @@ -118,7 +118,7 @@ pub fn parser( select! { Token::Name { name } => name } .then_ignore(just(Token::Colon)) .or_not() - .then(r) + .then(expression) .map_with_span(|(label, value), span| { ParserArg::Arg(Box::new(ast::CallArg { label, diff --git a/crates/aiken-lang/src/parser/expr/sequence.rs b/crates/aiken-lang/src/parser/expr/sequence.rs index 38e5eab24..68e5c0758 100644 --- a/crates/aiken-lang/src/parser/expr/sequence.rs +++ b/crates/aiken-lang/src/parser/expr/sequence.rs @@ -7,11 +7,11 @@ use crate::{ }; pub fn parser() -> impl Parser { - recursive(|r| { + recursive(|expression| { choice(( just(Token::Trace) - .ignore_then(super::parser(r.clone())) - .then(r.clone()) + .ignore_then(super::parser(expression.clone())) + .then(expression.clone()) .map_with_span(|(text, then_), span| UntypedExpr::Trace { kind: TraceKind::Trace, location: span, @@ -19,17 +19,17 @@ pub fn parser() -> impl Parser { text: Box::new(super::string::flexible(text)), }), just(Token::ErrorTerm) - .ignore_then(super::parser(r.clone()).or_not()) + .ignore_then(super::parser(expression.clone()).or_not()) .map_with_span(|reason, span| { UntypedExpr::error(span, reason.map(super::string::flexible)) }), just(Token::Todo) - .ignore_then(super::parser(r.clone()).or_not()) + .ignore_then(super::parser(expression.clone()).or_not()) .map_with_span(|reason, span| { UntypedExpr::todo(span, reason.map(super::string::flexible)) }), - super::parser(r.clone()) - .then(r.repeated()) + super::parser(expression.clone()) + .then(expression.repeated()) .foldl(|current, next| current.append_in_sequence(next)), )) }) diff --git a/crates/aiken-lang/src/parser/expr/when/guard.rs b/crates/aiken-lang/src/parser/expr/when/guard.rs index 95459ebc3..c7b445dd8 100644 --- a/crates/aiken-lang/src/parser/expr/when/guard.rs +++ b/crates/aiken-lang/src/parser/expr/when/guard.rs @@ -6,7 +6,7 @@ use crate::{ }; pub fn parser() -> impl Parser { - recursive(|r| { + recursive(|expression| { let var_parser = select! { Token::Name { name } => name, Token::UpName { name } => name, @@ -19,7 +19,7 @@ pub fn parser() -> impl Parser impl Parser { - recursive(|r| { + recursive(|expression| { let record_constructor_pattern_arg_parser = choice(( select! {Token::Name {name} => name} .then_ignore(just(Token::Colon)) - .then(r.clone()) + .then(expression.clone()) .map_with_span(|(name, pattern), span| ast::CallArg { location: span, label: Some(name), @@ -37,7 +37,7 @@ pub fn parser() -> impl Parser { ) .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)); - let tuple_constructor_pattern_arg_parser = r + let tuple_constructor_pattern_arg_parser = expression .clone() .map(|pattern| ast::CallArg { location: pattern.location(), @@ -121,7 +121,8 @@ pub fn parser() -> impl Parser { base, }, ), - r.clone() + expression + .clone() .separated_by(just(Token::Comma)) .allow_trailing() .delimited_by( @@ -133,10 +134,13 @@ pub fn parser() -> impl Parser { elems, }), just(Token::LeftSquare) - .ignore_then(r.clone().separated_by(just(Token::Comma))) + .ignore_then(expression.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).ignore_then( + just(Token::DotDot) + .ignore_then(expression.clone().or_not()) + .or_not(), + ), just(Token::Comma).ignored().or_not().map(|_| None), ))) .then_ignore(just(Token::RightSquare)) From a306d6e9f2223076042d1772753e1abce9f90444 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 5 Jul 2023 15:18:07 +0200 Subject: [PATCH 30/31] Move chain and chained parsing into their own submodule Alleviate a bit more the top-level expression parser. Note that we probably need a bit more disciplined in what we export and at what level because there doesn't seem to be much logic as for whether a parser is private, exported to the crate only or to the wide open. I'd be in favor of exporting everything by default. --- crates/aiken-lang/src/parser.rs | 1 + crates/aiken-lang/src/parser/chain/call.rs | 38 ++++ .../src/parser/chain/field_access.rs | 30 +++ crates/aiken-lang/src/parser/chain/mod.rs | 22 ++ .../src/parser/chain/tuple_index.rs | 23 +++ crates/aiken-lang/src/parser/expr/chained.rs | 133 +++++++++++++ crates/aiken-lang/src/parser/expr/mod.rs | 188 +----------------- 7 files changed, 255 insertions(+), 180 deletions(-) create mode 100644 crates/aiken-lang/src/parser/chain/call.rs create mode 100644 crates/aiken-lang/src/parser/chain/field_access.rs create mode 100644 crates/aiken-lang/src/parser/chain/mod.rs create mode 100644 crates/aiken-lang/src/parser/chain/tuple_index.rs create mode 100644 crates/aiken-lang/src/parser/expr/chained.rs diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index d4af7017c..a3813a82c 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 000000000..e2e0c0dd2 --- /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 000000000..14f84bce5 --- /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 000000000..baad45165 --- /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 000000000..e3ce7f24b --- /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 000000000..2a12e96da --- /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 37d676bff..96d361f2f 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()), From e331b3449b6526384e60352e17eb4814ed8168af Mon Sep 17 00:00:00 2001 From: rvcas Date: Wed, 5 Jul 2023 12:06:03 -0400 Subject: [PATCH 31/31] chore: clippy fix --- crates/aiken-lang/src/parser/chain/call.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/aiken-lang/src/parser/chain/call.rs b/crates/aiken-lang/src/parser/chain/call.rs index e2e0c0dd2..42e7b2226 100644 --- a/crates/aiken-lang/src/parser/chain/call.rs +++ b/crates/aiken-lang/src/parser/chain/call.rs @@ -7,9 +7,9 @@ use crate::{ parser::{token::Token, ParseError}, }; -pub(crate) fn parser<'a>( - expression: Recursive<'a, Token, UntypedExpr, ParseError>, -) -> impl Parser + 'a { +pub(crate) fn parser( + expression: Recursive<'_, Token, UntypedExpr, ParseError>, +) -> impl Parser + '_ { choice(( select! { Token::Name { name } => name } .then_ignore(just(Token::Colon))