From 083b7fcb5f417c9796c1b394b3b2871cc90f05d7 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Tue, 27 Dec 2022 20:30:50 -0500 Subject: [PATCH] feat: support negation of int * add unary op * parse, typecheck, and code gen it * express boolean not as unary op as well, previously called negate Co-authored-by: rvcas --- crates/aiken-lang/src/air.rs | 7 ++++--- crates/aiken-lang/src/ast.rs | 8 +++++++ crates/aiken-lang/src/expr.rs | 22 ++++++++++++-------- crates/aiken-lang/src/format.rs | 2 +- crates/aiken-lang/src/parser.rs | 13 +++++++----- crates/aiken-lang/src/parser/lexer.rs | 4 ---- crates/aiken-lang/src/tipo/expr.rs | 22 +++++++++++++++----- crates/aiken-lang/src/uplc.rs | 30 +++++++++++++++++++-------- 8 files changed, 72 insertions(+), 36 deletions(-) diff --git a/crates/aiken-lang/src/air.rs b/crates/aiken-lang/src/air.rs index ea58ef04..037a41d3 100644 --- a/crates/aiken-lang/src/air.rs +++ b/crates/aiken-lang/src/air.rs @@ -3,7 +3,7 @@ use std::{collections::HashSet, sync::Arc}; use uplc::builtins::DefaultFunction; use crate::{ - ast::{AssignmentKind, BinOp, TypedRecordUpdateArg}, + ast::{AssignmentKind, BinOp, TypedRecordUpdateArg, UnOp}, tipo::{Type, ValueConstructor}, }; @@ -229,8 +229,9 @@ pub enum Air { args: Vec, }, - Negate { + UnOp { scope: Vec, + op: UnOp, }, TupleAccessor { @@ -276,7 +277,7 @@ impl Air { | Air::ErrorTerm { scope, .. } | Air::Record { scope, .. } | Air::RecordUpdate { scope, .. } - | Air::Negate { scope, .. } + | Air::UnOp { scope, .. } | Air::Trace { scope, .. } | Air::TupleAccessor { scope, .. } | Air::TupleIndex { scope, .. } diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 093326fb..86b559fa 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -613,6 +613,14 @@ pub enum BinOp { ModInt, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum UnOp { + // ! + Not, + // - + Negate, +} + impl BinOp { pub fn precedence(&self) -> u8 { // Ensure that this matches the other precedence function for guards diff --git a/crates/aiken-lang/src/expr.rs b/crates/aiken-lang/src/expr.rs index 3b14b287..3feab45c 100644 --- a/crates/aiken-lang/src/expr.rs +++ b/crates/aiken-lang/src/expr.rs @@ -5,9 +5,10 @@ use vec1::Vec1; use crate::{ ast::{ Annotation, Arg, AssignmentKind, BinOp, CallArg, Clause, DefinitionLocation, IfBranch, - Pattern, RecordUpdateSpread, Span, TodoKind, TypedRecordUpdateArg, UntypedRecordUpdateArg, + Pattern, RecordUpdateSpread, Span, TodoKind, TypedRecordUpdateArg, UnOp, + UntypedRecordUpdateArg, }, - builtins::{bool, void}, + builtins::void, tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor}, }; @@ -161,16 +162,17 @@ pub enum TypedExpr { args: Vec, }, - Negate { + UnOp { location: Span, value: Box, + tipo: Arc, + op: UnOp, }, } impl TypedExpr { pub fn tipo(&self) -> Arc { match self { - Self::Negate { .. } => bool(), Self::Var { constructor, .. } => constructor.tipo.clone(), Self::Trace { then, .. } => then.tipo(), Self::Fn { tipo, .. } @@ -181,6 +183,7 @@ impl TypedExpr { | Self::List { tipo, .. } | Self::Call { tipo, .. } | Self::If { tipo, .. } + | Self::UnOp { tipo, .. } | Self::BinOp { tipo, .. } | Self::Tuple { tipo, .. } | Self::String { tipo, .. } @@ -224,7 +227,7 @@ impl TypedExpr { | TypedExpr::ErrorTerm { .. } | TypedExpr::BinOp { .. } | TypedExpr::Tuple { .. } - | TypedExpr::Negate { .. } + | TypedExpr::UnOp { .. } | TypedExpr::String { .. } | TypedExpr::Sequence { .. } | TypedExpr::Pipeline { .. } @@ -267,7 +270,7 @@ impl TypedExpr { | Self::BinOp { location, .. } | Self::Tuple { location, .. } | Self::String { location, .. } - | Self::Negate { location, .. } + | Self::UnOp { location, .. } | Self::Pipeline { location, .. } | Self::ByteArray { location, .. } | Self::Assignment { location, .. } @@ -304,7 +307,7 @@ impl TypedExpr { | Self::BinOp { location, .. } | Self::Tuple { location, .. } | Self::String { location, .. } - | Self::Negate { location, .. } + | Self::UnOp { location, .. } | Self::Sequence { location, .. } | Self::Pipeline { location, .. } | Self::ByteArray { location, .. } @@ -436,7 +439,8 @@ pub enum UntypedExpr { arguments: Vec, }, - Negate { + UnOp { + op: UnOp, location: Span, value: Box, }, @@ -511,7 +515,7 @@ impl UntypedExpr { | Self::TupleIndex { location, .. } | Self::FieldAccess { location, .. } | Self::RecordUpdate { location, .. } - | Self::Negate { location, .. } + | Self::UnOp { location, .. } | Self::If { location, .. } => *location, Self::Sequence { location, diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index ee6ba4cb..e6b73f90 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -695,7 +695,7 @@ impl<'comments> Formatter<'comments> { UntypedExpr::Var { name, .. } => name.to_doc(), - UntypedExpr::Negate { value, .. } => self.negate(value), + UntypedExpr::UnOp { value, .. } => self.negate(value), UntypedExpr::Fn { is_capture: true, diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 84a1f35a..81857b79 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -7,7 +7,7 @@ pub mod lexer; pub mod token; use crate::{ - ast::{self, BinOp, Span, TodoKind, UntypedDefinition, CAPTURE_VARIABLE}, + ast::{self, BinOp, Span, TodoKind, UnOp, UntypedDefinition, CAPTURE_VARIABLE}, expr, }; @@ -1221,14 +1221,17 @@ pub fn expr_parser( }); // Negate - let op = just(Token::Bang); + let op = choice(( + just(Token::Bang).to(UnOp::Not), + just(Token::Minus).to(UnOp::Negate), + )); let unary = op - .ignored() - .map_with_span(|_, span| span) + .map_with_span(|op, span| (op, span)) .repeated() .then(chained) - .foldr(|span, value| expr::UntypedExpr::Negate { + .foldr(|(un_op, span), value| expr::UntypedExpr::UnOp { + op: un_op, location: span.union(value.location()), value: Box::new(value), }) diff --git a/crates/aiken-lang/src/parser/lexer.rs b/crates/aiken-lang/src/parser/lexer.rs index 3c05fd0d..966161dd 100644 --- a/crates/aiken-lang/src/parser/lexer.rs +++ b/crates/aiken-lang/src/parser/lexer.rs @@ -9,13 +9,9 @@ use super::{error::ParseError, token::Token}; pub fn lexer() -> impl Parser, Error = ParseError> { let int = choice(( text::int(10), - text::int(16), just("-") .ignore_then(text::int(10)) .map(|value: String| format!("-{}", &value)), - just("-") - .ignore_then(text::int(16)) - .map(|value: String| format!("-{}", &value)), )) .map(|value| Token::Int { value }); diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index ffafd25f..a851cb69 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -6,7 +6,7 @@ use crate::{ ast::{ Annotation, Arg, ArgName, AssignmentKind, BinOp, CallArg, Clause, ClauseGuard, Constant, RecordUpdateSpread, Span, TodoKind, TypedArg, TypedCallArg, TypedClause, TypedClauseGuard, - TypedConstant, TypedIfBranch, TypedMultiPattern, TypedRecordUpdateArg, UntypedArg, + TypedConstant, TypedIfBranch, TypedMultiPattern, TypedRecordUpdateArg, UnOp, UntypedArg, UntypedClause, UntypedClauseGuard, UntypedConstant, UntypedIfBranch, UntypedMultiPattern, UntypedPattern, UntypedRecordUpdateArg, }, @@ -345,7 +345,11 @@ impl<'a, 'b> ExprTyper<'a, 'b> { arguments: args, } => self.infer_record_update(*constructor, spread, args, location), - UntypedExpr::Negate { location, value } => self.infer_negate(location, value), + UntypedExpr::UnOp { + location, + value, + op, + } => self.infer_un_op(location, value, op), } } @@ -534,18 +538,26 @@ impl<'a, 'b> ExprTyper<'a, 'b> { }) } - fn infer_negate( + fn infer_un_op( &mut self, location: Span, value: Box, + op: UnOp, ) -> Result { let value = self.infer(*value)?; - self.unify(bool(), value.tipo(), value.location())?; + let tipo = match op { + UnOp::Not => bool(), + UnOp::Negate => int(), + }; - Ok(TypedExpr::Negate { + self.unify(tipo.clone(), value.tipo(), value.location())?; + + Ok(TypedExpr::UnOp { location, value: Box::new(value), + op, + tipo, }) } diff --git a/crates/aiken-lang/src/uplc.rs b/crates/aiken-lang/src/uplc.rs index f1a4e800..1bb54c1c 100644 --- a/crates/aiken-lang/src/uplc.rs +++ b/crates/aiken-lang/src/uplc.rs @@ -24,7 +24,7 @@ use crate::{ air::Air, ast::{ ArgName, AssignmentKind, BinOp, Clause, Pattern, Span, TypedArg, TypedDataType, - TypedFunction, + TypedFunction, UnOp, }, builder::{ check_when_pattern_needs, constants_ir, convert_constants_to_data, convert_data_to_type, @@ -496,9 +496,10 @@ impl<'a> CodeGenerator<'a> { }); } TypedExpr::RecordUpdate { .. } => todo!(), - TypedExpr::Negate { value, .. } => { - ir_stack.push(Air::Negate { + TypedExpr::UnOp { value, op, .. } => { + ir_stack.push(Air::UnOp { scope: scope.clone(), + op: *op, }); self.build_ir(value, ir_stack, scope); @@ -3925,14 +3926,25 @@ impl<'a> CodeGenerator<'a> { } Air::Record { .. } => todo!(), Air::RecordUpdate { .. } => todo!(), - Air::Negate { .. } => { + Air::UnOp { op, .. } => { let value = arg_stack.pop().unwrap(); - let term = if_else( - value, - Term::Constant(UplcConstant::Bool(false)), - Term::Constant(UplcConstant::Bool(true)), - ); + let term = match op { + UnOp::Not => if_else( + value, + Term::Constant(UplcConstant::Bool(false)), + Term::Constant(UplcConstant::Bool(true)), + ), + UnOp::Negate => Term::Apply { + function: Term::Apply { + function: Term::Builtin(DefaultFunction::SubtractInteger).into(), + argument: Term::Constant(UplcConstant::Integer(0)).into(), + } + .into(), + argument: value.into(), + }, + }; + arg_stack.push(term); } Air::TupleIndex { .. } => todo!(),