From ab3a418b9c16444967e69194187a6ec92b613b7d Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 14 Aug 2023 17:18:11 -0400 Subject: [PATCH] feat(parser): add support for and/or chaining --- crates/aiken-lang/src/ast.rs | 28 +++++++++++++++++++ crates/aiken-lang/src/expr.rs | 7 +++++ crates/aiken-lang/src/parser/expr/chained.rs | 4 ++- crates/aiken-lang/src/parser/expr/mod.rs | 2 ++ crates/aiken-lang/src/parser/lexer.rs | 7 +++-- crates/aiken-lang/src/parser/token.rs | 4 +++ crates/aiken-lang/src/tests/check.rs | 4 +-- crates/aiken-lang/src/tests/format.rs | 2 +- .../format_nested_function_calls.snap | 4 +-- 9 files changed, 53 insertions(+), 9 deletions(-) diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 19cfc423..684acf81 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -116,7 +116,11 @@ fn str_to_keyword(word: &str) -> Option { "type" => Some(Token::Type), "trace" => Some(Token::Trace), "test" => Some(Token::Test), + // TODO: remove this in a future release "error" => Some(Token::Fail), + "fail" => Some(Token::Fail), + "and" => Some(Token::And), + "or" => Some(Token::Or), "validator" => Some(Token::Validator), _ => None, } @@ -779,6 +783,15 @@ pub enum BinOp { ModInt, } +impl From for BinOp { + fn from(value: LogicalOpChainKind) -> Self { + match value { + LogicalOpChainKind::And => BinOp::And, + LogicalOpChainKind::Or => BinOp::Or, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UnOp { // ! @@ -1225,6 +1238,21 @@ impl chumsky::Span for Span { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum LogicalOpChainKind { + And, + Or, +} + +impl Display for LogicalOpChainKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LogicalOpChainKind::And => write!(f, "and"), + LogicalOpChainKind::Or => write!(f, "or"), + } + } +} + #[derive(Debug, thiserror::Error, Diagnostic)] pub enum Error { #[error( diff --git a/crates/aiken-lang/src/expr.rs b/crates/aiken-lang/src/expr.rs index 32bb738d..bac25db3 100644 --- a/crates/aiken-lang/src/expr.rs +++ b/crates/aiken-lang/src/expr.rs @@ -533,6 +533,12 @@ pub enum UntypedExpr { location: Span, value: Box, }, + + LogicalOpChain { + kind: LogicalOpChainKind, + expressions: Vec, + location: Span, + }, } pub const DEFAULT_TODO_STR: &str = "aiken::todo"; @@ -716,6 +722,7 @@ impl UntypedExpr { | Self::FieldAccess { location, .. } | Self::RecordUpdate { location, .. } | Self::UnOp { location, .. } + | Self::LogicalOpChain { location, .. } | Self::If { location, .. } => *location, Self::Sequence { location, diff --git a/crates/aiken-lang/src/parser/expr/chained.rs b/crates/aiken-lang/src/parser/expr/chained.rs index b09927e3..05b7f893 100644 --- a/crates/aiken-lang/src/parser/expr/chained.rs +++ b/crates/aiken-lang/src/parser/expr/chained.rs @@ -1,6 +1,5 @@ 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; @@ -14,6 +13,7 @@ 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 super::{and_or_chain, anonymous_binop::parser as anonymous_binop}; use crate::{ expr::UntypedExpr, @@ -33,6 +33,7 @@ pub fn parser<'a>( field_access::parser(), call(expression.clone()), )); + chain_start(sequence, expression) .then(chain.repeated()) .foldl(|expr, chain| match chain { @@ -60,6 +61,7 @@ pub fn chain_start<'a>( record_update(expression.clone()), record(expression.clone()), field_access::constructor(), + and_or_chain(expression.clone()), var(), tuple(expression.clone()), bytearray(), diff --git a/crates/aiken-lang/src/parser/expr/mod.rs b/crates/aiken-lang/src/parser/expr/mod.rs index 91cf0ca2..710c0737 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; +mod and_or_chain; mod anonymous_binop; pub mod anonymous_function; pub mod assignment; @@ -19,6 +20,7 @@ mod tuple; mod var; pub mod when; +pub use and_or_chain::parser as and_or_chain; pub use anonymous_function::parser as anonymous_function; pub use block::parser as block; pub use bytearray::parser as bytearray; diff --git a/crates/aiken-lang/src/parser/lexer.rs b/crates/aiken-lang/src/parser/lexer.rs index 4940efa7..2bef030f 100644 --- a/crates/aiken-lang/src/parser/lexer.rs +++ b/crates/aiken-lang/src/parser/lexer.rs @@ -222,11 +222,12 @@ pub fn lexer() -> impl Parser, Error = ParseError> { let keyword = text::ident().map(|s: String| match s.as_str() { "trace" => Token::Trace, - "fail" => Token::Fail, - // TODO: delete this eventually + // TODO: remove this in a future release "error" => Token::Fail, + "fail" => Token::Fail, "as" => Token::As, - "assert" => Token::Expect, + "and" => Token::And, + "or" => Token::Or, "expect" => Token::Expect, "const" => Token::Const, "fn" => Token::Fn, diff --git a/crates/aiken-lang/src/parser/token.rs b/crates/aiken-lang/src/parser/token.rs index 412ed5c3..cae2665b 100644 --- a/crates/aiken-lang/src/parser/token.rs +++ b/crates/aiken-lang/src/parser/token.rs @@ -56,6 +56,8 @@ pub enum Token { Vbar, // '|' VbarVbar, // '||' AmperAmper, // '&&' + And, // and + Or, // or NewLinePipe, // '↳|>' Pipe, // '|>' Dot, // '.' @@ -143,6 +145,8 @@ impl fmt::Display for Token { Token::Vbar => "|", Token::VbarVbar => "||", Token::AmperAmper => "&&", + Token::And => "and", + Token::Or => "or", Token::NewLinePipe => "↳|>", Token::Pipe => "|>", Token::Dot => ".", diff --git a/crates/aiken-lang/src/tests/check.rs b/crates/aiken-lang/src/tests/check.rs index 496b302e..c45df4c3 100644 --- a/crates/aiken-lang/src/tests/check.rs +++ b/crates/aiken-lang/src/tests/check.rs @@ -945,12 +945,12 @@ fn trace_non_strings() { #[test] fn trace_if_false_ok() { let source_code = r#" - fn or(a: Bool, b: Bool) { + fn or_func(a: Bool, b: Bool) { (a || b)? } test foo() { - or(True, False)? + or_func(True, False)? } test bar() { diff --git a/crates/aiken-lang/src/tests/format.rs b/crates/aiken-lang/src/tests/format.rs index 92bf0d7a..79cc289f 100644 --- a/crates/aiken-lang/src/tests/format.rs +++ b/crates/aiken-lang/src/tests/format.rs @@ -250,7 +250,7 @@ fn format_nested_function_calls() { _ -> fail "expected inline datum" }, ] - |> list.and + |> list.and_func } "# ); diff --git a/crates/aiken-lang/src/tests/snapshots/format_nested_function_calls.snap b/crates/aiken-lang/src/tests/snapshots/format_nested_function_calls.snap index d69f74c1..5da8ceeb 100644 --- a/crates/aiken-lang/src/tests/snapshots/format_nested_function_calls.snap +++ b/crates/aiken-lang/src/tests/snapshots/format_nested_function_calls.snap @@ -1,6 +1,6 @@ --- source: crates/aiken-lang/src/tests/format.rs -description: "Code:\n\nfn foo(output) {\n [\n output.address.stake_credential == Some(\n Inline(\n VerificationKeyCredential(\n #\"66666666666666666666666666666666666666666666666666666666\",\n ))\n )\n ,\n when output.datum is {\n InlineDatum(_) -> True\n _ -> fail \"expected inline datum\"\n },\n ]\n |> list.and\n}\n" +description: "Code:\n\nfn foo(output) {\n [\n output.address.stake_credential == Some(\n Inline(\n VerificationKeyCredential(\n #\"66666666666666666666666666666666666666666666666666666666\",\n ))\n )\n ,\n when output.datum is {\n InlineDatum(_) -> True\n _ -> fail \"expected inline datum\"\n },\n ]\n |> list.and_func\n}\n" --- fn foo(output) { [ @@ -16,6 +16,6 @@ fn foo(output) { _ -> fail @"expected inline datum" }, ] - |> list.and + |> list.and_func }