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 <x@rvcas.dev>
This commit is contained in:
Kasey White 2022-12-27 20:30:50 -05:00 committed by Lucas
parent 542e39f093
commit 083b7fcb5f
8 changed files with 72 additions and 36 deletions

View File

@ -3,7 +3,7 @@ use std::{collections::HashSet, sync::Arc};
use uplc::builtins::DefaultFunction; use uplc::builtins::DefaultFunction;
use crate::{ use crate::{
ast::{AssignmentKind, BinOp, TypedRecordUpdateArg}, ast::{AssignmentKind, BinOp, TypedRecordUpdateArg, UnOp},
tipo::{Type, ValueConstructor}, tipo::{Type, ValueConstructor},
}; };
@ -229,8 +229,9 @@ pub enum Air {
args: Vec<TypedRecordUpdateArg>, args: Vec<TypedRecordUpdateArg>,
}, },
Negate { UnOp {
scope: Vec<u64>, scope: Vec<u64>,
op: UnOp,
}, },
TupleAccessor { TupleAccessor {
@ -276,7 +277,7 @@ impl Air {
| Air::ErrorTerm { scope, .. } | Air::ErrorTerm { scope, .. }
| Air::Record { scope, .. } | Air::Record { scope, .. }
| Air::RecordUpdate { scope, .. } | Air::RecordUpdate { scope, .. }
| Air::Negate { scope, .. } | Air::UnOp { scope, .. }
| Air::Trace { scope, .. } | Air::Trace { scope, .. }
| Air::TupleAccessor { scope, .. } | Air::TupleAccessor { scope, .. }
| Air::TupleIndex { scope, .. } | Air::TupleIndex { scope, .. }

View File

@ -613,6 +613,14 @@ pub enum BinOp {
ModInt, ModInt,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnOp {
// !
Not,
// -
Negate,
}
impl BinOp { impl BinOp {
pub fn precedence(&self) -> u8 { pub fn precedence(&self) -> u8 {
// Ensure that this matches the other precedence function for guards // Ensure that this matches the other precedence function for guards

View File

@ -5,9 +5,10 @@ use vec1::Vec1;
use crate::{ use crate::{
ast::{ ast::{
Annotation, Arg, AssignmentKind, BinOp, CallArg, Clause, DefinitionLocation, IfBranch, 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}, tipo::{ModuleValueConstructor, PatternConstructor, Type, ValueConstructor},
}; };
@ -161,16 +162,17 @@ pub enum TypedExpr {
args: Vec<TypedRecordUpdateArg>, args: Vec<TypedRecordUpdateArg>,
}, },
Negate { UnOp {
location: Span, location: Span,
value: Box<Self>, value: Box<Self>,
tipo: Arc<Type>,
op: UnOp,
}, },
} }
impl TypedExpr { impl TypedExpr {
pub fn tipo(&self) -> Arc<Type> { pub fn tipo(&self) -> Arc<Type> {
match self { match self {
Self::Negate { .. } => bool(),
Self::Var { constructor, .. } => constructor.tipo.clone(), Self::Var { constructor, .. } => constructor.tipo.clone(),
Self::Trace { then, .. } => then.tipo(), Self::Trace { then, .. } => then.tipo(),
Self::Fn { tipo, .. } Self::Fn { tipo, .. }
@ -181,6 +183,7 @@ impl TypedExpr {
| Self::List { tipo, .. } | Self::List { tipo, .. }
| Self::Call { tipo, .. } | Self::Call { tipo, .. }
| Self::If { tipo, .. } | Self::If { tipo, .. }
| Self::UnOp { tipo, .. }
| Self::BinOp { tipo, .. } | Self::BinOp { tipo, .. }
| Self::Tuple { tipo, .. } | Self::Tuple { tipo, .. }
| Self::String { tipo, .. } | Self::String { tipo, .. }
@ -224,7 +227,7 @@ impl TypedExpr {
| TypedExpr::ErrorTerm { .. } | TypedExpr::ErrorTerm { .. }
| TypedExpr::BinOp { .. } | TypedExpr::BinOp { .. }
| TypedExpr::Tuple { .. } | TypedExpr::Tuple { .. }
| TypedExpr::Negate { .. } | TypedExpr::UnOp { .. }
| TypedExpr::String { .. } | TypedExpr::String { .. }
| TypedExpr::Sequence { .. } | TypedExpr::Sequence { .. }
| TypedExpr::Pipeline { .. } | TypedExpr::Pipeline { .. }
@ -267,7 +270,7 @@ impl TypedExpr {
| Self::BinOp { location, .. } | Self::BinOp { location, .. }
| Self::Tuple { location, .. } | Self::Tuple { location, .. }
| Self::String { location, .. } | Self::String { location, .. }
| Self::Negate { location, .. } | Self::UnOp { location, .. }
| Self::Pipeline { location, .. } | Self::Pipeline { location, .. }
| Self::ByteArray { location, .. } | Self::ByteArray { location, .. }
| Self::Assignment { location, .. } | Self::Assignment { location, .. }
@ -304,7 +307,7 @@ impl TypedExpr {
| Self::BinOp { location, .. } | Self::BinOp { location, .. }
| Self::Tuple { location, .. } | Self::Tuple { location, .. }
| Self::String { location, .. } | Self::String { location, .. }
| Self::Negate { location, .. } | Self::UnOp { location, .. }
| Self::Sequence { location, .. } | Self::Sequence { location, .. }
| Self::Pipeline { location, .. } | Self::Pipeline { location, .. }
| Self::ByteArray { location, .. } | Self::ByteArray { location, .. }
@ -436,7 +439,8 @@ pub enum UntypedExpr {
arguments: Vec<UntypedRecordUpdateArg>, arguments: Vec<UntypedRecordUpdateArg>,
}, },
Negate { UnOp {
op: UnOp,
location: Span, location: Span,
value: Box<Self>, value: Box<Self>,
}, },
@ -511,7 +515,7 @@ impl UntypedExpr {
| Self::TupleIndex { location, .. } | Self::TupleIndex { location, .. }
| Self::FieldAccess { location, .. } | Self::FieldAccess { location, .. }
| Self::RecordUpdate { location, .. } | Self::RecordUpdate { location, .. }
| Self::Negate { location, .. } | Self::UnOp { location, .. }
| Self::If { location, .. } => *location, | Self::If { location, .. } => *location,
Self::Sequence { Self::Sequence {
location, location,

View File

@ -695,7 +695,7 @@ impl<'comments> Formatter<'comments> {
UntypedExpr::Var { name, .. } => name.to_doc(), UntypedExpr::Var { name, .. } => name.to_doc(),
UntypedExpr::Negate { value, .. } => self.negate(value), UntypedExpr::UnOp { value, .. } => self.negate(value),
UntypedExpr::Fn { UntypedExpr::Fn {
is_capture: true, is_capture: true,

View File

@ -7,7 +7,7 @@ pub mod lexer;
pub mod token; pub mod token;
use crate::{ use crate::{
ast::{self, BinOp, Span, TodoKind, UntypedDefinition, CAPTURE_VARIABLE}, ast::{self, BinOp, Span, TodoKind, UnOp, UntypedDefinition, CAPTURE_VARIABLE},
expr, expr,
}; };
@ -1221,14 +1221,17 @@ pub fn expr_parser(
}); });
// Negate // Negate
let op = just(Token::Bang); let op = choice((
just(Token::Bang).to(UnOp::Not),
just(Token::Minus).to(UnOp::Negate),
));
let unary = op let unary = op
.ignored() .map_with_span(|op, span| (op, span))
.map_with_span(|_, span| span)
.repeated() .repeated()
.then(chained) .then(chained)
.foldr(|span, value| expr::UntypedExpr::Negate { .foldr(|(un_op, span), value| expr::UntypedExpr::UnOp {
op: un_op,
location: span.union(value.location()), location: span.union(value.location()),
value: Box::new(value), value: Box::new(value),
}) })

View File

@ -9,13 +9,9 @@ use super::{error::ParseError, token::Token};
pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> { pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
let int = choice(( let int = choice((
text::int(10), text::int(10),
text::int(16),
just("-") just("-")
.ignore_then(text::int(10)) .ignore_then(text::int(10))
.map(|value: String| format!("-{}", &value)), .map(|value: String| format!("-{}", &value)),
just("-")
.ignore_then(text::int(16))
.map(|value: String| format!("-{}", &value)),
)) ))
.map(|value| Token::Int { value }); .map(|value| Token::Int { value });

View File

@ -6,7 +6,7 @@ use crate::{
ast::{ ast::{
Annotation, Arg, ArgName, AssignmentKind, BinOp, CallArg, Clause, ClauseGuard, Constant, Annotation, Arg, ArgName, AssignmentKind, BinOp, CallArg, Clause, ClauseGuard, Constant,
RecordUpdateSpread, Span, TodoKind, TypedArg, TypedCallArg, TypedClause, TypedClauseGuard, RecordUpdateSpread, Span, TodoKind, TypedArg, TypedCallArg, TypedClause, TypedClauseGuard,
TypedConstant, TypedIfBranch, TypedMultiPattern, TypedRecordUpdateArg, UntypedArg, TypedConstant, TypedIfBranch, TypedMultiPattern, TypedRecordUpdateArg, UnOp, UntypedArg,
UntypedClause, UntypedClauseGuard, UntypedConstant, UntypedIfBranch, UntypedMultiPattern, UntypedClause, UntypedClauseGuard, UntypedConstant, UntypedIfBranch, UntypedMultiPattern,
UntypedPattern, UntypedRecordUpdateArg, UntypedPattern, UntypedRecordUpdateArg,
}, },
@ -345,7 +345,11 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
arguments: args, arguments: args,
} => self.infer_record_update(*constructor, spread, args, location), } => 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, &mut self,
location: Span, location: Span,
value: Box<UntypedExpr>, value: Box<UntypedExpr>,
op: UnOp,
) -> Result<TypedExpr, Error> { ) -> Result<TypedExpr, Error> {
let value = self.infer(*value)?; 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, location,
value: Box::new(value), value: Box::new(value),
op,
tipo,
}) })
} }

View File

@ -24,7 +24,7 @@ use crate::{
air::Air, air::Air,
ast::{ ast::{
ArgName, AssignmentKind, BinOp, Clause, Pattern, Span, TypedArg, TypedDataType, ArgName, AssignmentKind, BinOp, Clause, Pattern, Span, TypedArg, TypedDataType,
TypedFunction, TypedFunction, UnOp,
}, },
builder::{ builder::{
check_when_pattern_needs, constants_ir, convert_constants_to_data, convert_data_to_type, 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::RecordUpdate { .. } => todo!(),
TypedExpr::Negate { value, .. } => { TypedExpr::UnOp { value, op, .. } => {
ir_stack.push(Air::Negate { ir_stack.push(Air::UnOp {
scope: scope.clone(), scope: scope.clone(),
op: *op,
}); });
self.build_ir(value, ir_stack, scope); self.build_ir(value, ir_stack, scope);
@ -3925,14 +3926,25 @@ impl<'a> CodeGenerator<'a> {
} }
Air::Record { .. } => todo!(), Air::Record { .. } => todo!(),
Air::RecordUpdate { .. } => todo!(), Air::RecordUpdate { .. } => todo!(),
Air::Negate { .. } => { Air::UnOp { op, .. } => {
let value = arg_stack.pop().unwrap(); let value = arg_stack.pop().unwrap();
let term = if_else( let term = match op {
UnOp::Not => if_else(
value, value,
Term::Constant(UplcConstant::Bool(false)), Term::Constant(UplcConstant::Bool(false)),
Term::Constant(UplcConstant::Bool(true)), 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); arg_stack.push(term);
} }
Air::TupleIndex { .. } => todo!(), Air::TupleIndex { .. } => todo!(),