Skip to content

Commit

Permalink
Merge pull request #302 from aiken-lang/when-clause-guards
Browse files Browse the repository at this point in the history
When clause guards
  • Loading branch information
rvcas authored Jan 30, 2023
2 parents f742103 + 60ad3da commit 0e65fec
Show file tree
Hide file tree
Showing 28 changed files with 732 additions and 100 deletions.
34 changes: 15 additions & 19 deletions crates/aiken-lang/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,11 @@ pub type TypedClauseGuard = ClauseGuard<Arc<Type>, String>;

#[derive(Debug, Clone, PartialEq)]
pub enum ClauseGuard<Type, RecordTag> {
Not {
location: Span,
value: Box<Self>,
},

Equals {
location: Span,
left: Box<Self>,
Expand Down Expand Up @@ -861,23 +866,17 @@ pub enum ClauseGuard<Type, RecordTag> {
name: String,
},

// TupleIndex {
// location: Span,
// index: u64,
// tipo: Type,
// tuple: Box<Self>,
// },
Constant(Constant<Type, RecordTag>),
}

impl<A, B> ClauseGuard<A, B> {
pub fn location(&self) -> Span {
match self {
ClauseGuard::Constant(constant) => constant.location(),
ClauseGuard::Or { location, .. }
ClauseGuard::Not { location, .. }
| ClauseGuard::Or { location, .. }
| ClauseGuard::And { location, .. }
| ClauseGuard::Var { location, .. }
// | ClauseGuard::TupleIndex { location, .. }
| ClauseGuard::Equals { location, .. }
| ClauseGuard::NotEquals { location, .. }
| ClauseGuard::GtInt { location, .. }
Expand All @@ -890,17 +889,15 @@ impl<A, B> ClauseGuard<A, B> {
pub fn precedence(&self) -> u8 {
// Ensure that this matches the other precedence function for guards
match self {
ClauseGuard::Or { .. } => 1,
ClauseGuard::And { .. } => 2,

ClauseGuard::Equals { .. } | ClauseGuard::NotEquals { .. } => 3,

ClauseGuard::Not { .. } => 1,
ClauseGuard::Or { .. } => 2,
ClauseGuard::And { .. } => 3,
ClauseGuard::Equals { .. } | ClauseGuard::NotEquals { .. } => 4,
ClauseGuard::GtInt { .. }
| ClauseGuard::GtEqInt { .. }
| ClauseGuard::LtInt { .. }
| ClauseGuard::LtEqInt { .. } => 4,

ClauseGuard::Constant(_) | ClauseGuard::Var { .. } => 5,
| ClauseGuard::LtEqInt { .. } => 5,
ClauseGuard::Constant(_) | ClauseGuard::Var { .. } => 6,
}
}
}
Expand All @@ -909,10 +906,9 @@ impl TypedClauseGuard {
pub fn tipo(&self) -> Arc<Type> {
match self {
ClauseGuard::Var { tipo, .. } => tipo.clone(),
// ClauseGuard::TupleIndex { type_, .. } => type_.clone(),
ClauseGuard::Constant(constant) => constant.tipo(),

ClauseGuard::Or { .. }
ClauseGuard::Not { .. }
| ClauseGuard::Or { .. }
| ClauseGuard::And { .. }
| ClauseGuard::Equals { .. }
| ClauseGuard::NotEquals { .. }
Expand Down
120 changes: 118 additions & 2 deletions crates/aiken-lang/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ use uplc::{

use crate::{
air::Air,
ast::{AssignmentKind, Clause, Constant, DataType, Pattern, Span, TypedArg, TypedDataType},
ast::{
AssignmentKind, BinOp, Clause, ClauseGuard, Constant, DataType, Pattern, Span, TypedArg,
TypedDataType, UnOp,
},
expr::TypedExpr,
tipo::{PatternConstructor, Type, TypeVar, ValueConstructorVariant},
tipo::{PatternConstructor, Type, TypeVar, ValueConstructor, ValueConstructorVariant},
};

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -1707,3 +1710,116 @@ pub fn replace_opaque_type(t: &mut Arc<Type>, data_types: IndexMap<DataTypeKey,
}
}
}

pub fn handle_clause_guard(
clause_guard: &ClauseGuard<Arc<Type>, String>,
clause_guard_vec: &mut Vec<Air>,
scope: Vec<u64>,
) {
match clause_guard {
ClauseGuard::Not { value, .. } => {
clause_guard_vec.push(Air::UnOp {
scope: scope.clone(),
op: UnOp::Not,
});

handle_clause_guard(value, clause_guard_vec, scope);
}
ClauseGuard::Equals { left, right, .. } => {
clause_guard_vec.push(Air::BinOp {
scope: scope.clone(),
name: BinOp::Eq,
count: 2,
tipo: left.tipo(),
});
handle_clause_guard(left, clause_guard_vec, scope.clone());
handle_clause_guard(right, clause_guard_vec, scope);
}
ClauseGuard::NotEquals { left, right, .. } => {
clause_guard_vec.push(Air::BinOp {
scope: scope.clone(),
name: BinOp::NotEq,
count: 2,
tipo: left.tipo(),
});
handle_clause_guard(left, clause_guard_vec, scope.clone());
handle_clause_guard(right, clause_guard_vec, scope);
}
ClauseGuard::GtInt { left, right, .. } => {
clause_guard_vec.push(Air::BinOp {
scope: scope.clone(),
name: BinOp::GtInt,
count: 2,
tipo: left.tipo(),
});
handle_clause_guard(left, clause_guard_vec, scope.clone());
handle_clause_guard(right, clause_guard_vec, scope);
}
ClauseGuard::GtEqInt { left, right, .. } => {
clause_guard_vec.push(Air::BinOp {
scope: scope.clone(),
name: BinOp::GtEqInt,
count: 2,
tipo: left.tipo(),
});
handle_clause_guard(left, clause_guard_vec, scope.clone());
handle_clause_guard(right, clause_guard_vec, scope);
}
ClauseGuard::LtInt { left, right, .. } => {
clause_guard_vec.push(Air::BinOp {
scope: scope.clone(),
name: BinOp::LtInt,
count: 2,
tipo: left.tipo(),
});
handle_clause_guard(left, clause_guard_vec, scope.clone());
handle_clause_guard(right, clause_guard_vec, scope);
}
ClauseGuard::LtEqInt { left, right, .. } => {
clause_guard_vec.push(Air::BinOp {
scope: scope.clone(),
name: BinOp::LtEqInt,
count: 2,
tipo: left.tipo(),
});
handle_clause_guard(left, clause_guard_vec, scope.clone());
handle_clause_guard(right, clause_guard_vec, scope);
}
ClauseGuard::Or { left, right, .. } => {
clause_guard_vec.push(Air::BinOp {
scope: scope.clone(),
name: BinOp::Or,
count: 2,
tipo: left.tipo(),
});
handle_clause_guard(left, clause_guard_vec, scope.clone());
handle_clause_guard(right, clause_guard_vec, scope);
}
ClauseGuard::And { left, right, .. } => {
clause_guard_vec.push(Air::BinOp {
scope: scope.clone(),
name: BinOp::And,
count: 2,
tipo: left.tipo(),
});
handle_clause_guard(left, clause_guard_vec, scope.clone());
handle_clause_guard(right, clause_guard_vec, scope);
}
ClauseGuard::Var { tipo, name, .. } => {
clause_guard_vec.push(Air::Var {
scope,
constructor: ValueConstructor::public(
tipo.clone(),
ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
),
name: name.clone(),
variant_name: String::new(),
});
}
ClauseGuard::Constant(constant) => {
constants_ir(constant, clause_guard_vec, scope);
}
}
}
3 changes: 3 additions & 0 deletions crates/aiken-lang/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1539,6 +1539,9 @@ impl<'comments> Formatter<'comments> {

fn clause_guard<'a>(&mut self, clause_guard: &'a UntypedClauseGuard) -> Document<'a> {
match clause_guard {
ClauseGuard::Not { value, .. } => {
docvec!["!", self.clause_guard(value)]
}
ClauseGuard::And { left, right, .. } => {
self.clause_guard_bin_op(" && ", clause_guard.precedence(), left, right)
}
Expand Down
142 changes: 131 additions & 11 deletions crates/aiken-lang/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -989,9 +989,6 @@ pub fn expr_parser(
},
);

// TODO: do guards later
// let when_clause_guard_parser = just(Token::If);

let when_clause_parser = pattern_parser()
.separated_by(just(Token::Comma))
.at_least(1)
Expand All @@ -1005,20 +1002,29 @@ pub fn expr_parser(
.repeated()
.or_not(),
)
// TODO: do guards later
// .then(when_clause_guard_parser)
.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_ignore(just(Token::RArrow))
.then(r.clone())
.map_with_span(|((patterns, alternative_patterns_opt), then), span| {
ast::UntypedClause {
.map_with_span(
|(((patterns, alternative_patterns_opt), guard), then), span| ast::UntypedClause {
location: span,
pattern: patterns,
alternative_patterns: alternative_patterns_opt.unwrap_or_default(),
guard: None,
guard,
then,
}
});
},
);

let when_parser = just(Token::When)
// TODO: If subject is empty we should return ParseErrorType::ExpectedExpr,
Expand Down Expand Up @@ -1373,6 +1379,120 @@ pub fn expr_parser(
})
}

pub fn when_clause_guard_parser() -> impl Parser<Token, ast::ClauseGuard<(), ()>, 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 = constant_value_parser().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 logical_op = choice((
just(Token::AmperAmper).to(BinOp::And),
just(Token::VbarVbar).to(BinOp::Or),
));

comparison
.clone()
.then(logical_op.then(comparison).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::And => ast::ClauseGuard::And {
location,
left,
right,
},
BinOp::Or => ast::ClauseGuard::Or {
location,
left,
right,
},
_ => unreachable!(),
}
})
})
}

pub fn type_parser() -> impl Parser<Token, ast::Annotation, Error = ParseError> {
recursive(|r| {
choice((
Expand Down
Loading

0 comments on commit 0e65fec

Please sign in to comment.