feat(parser): add support for and/or chaining

This commit is contained in:
rvcas 2023-08-14 17:18:11 -04:00 committed by Lucas
parent 4a1ae9f412
commit ab3a418b9c
9 changed files with 53 additions and 9 deletions

View File

@ -116,7 +116,11 @@ fn str_to_keyword(word: &str) -> Option<Token> {
"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<LogicalOpChainKind> 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(

View File

@ -533,6 +533,12 @@ pub enum UntypedExpr {
location: Span,
value: Box<Self>,
},
LogicalOpChain {
kind: LogicalOpChainKind,
expressions: Vec<Self>,
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,

View File

@ -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(),

View File

@ -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;

View File

@ -222,11 +222,12 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, 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,

View File

@ -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 => ".",

View File

@ -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() {

View File

@ -250,7 +250,7 @@ fn format_nested_function_calls() {
_ -> fail "expected inline datum"
},
]
|> list.and
|> list.and_func
}
"#
);

View File

@ -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
}