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 crate::{
ast::{AssignmentKind, BinOp, TypedRecordUpdateArg},
ast::{AssignmentKind, BinOp, TypedRecordUpdateArg, UnOp},
tipo::{Type, ValueConstructor},
};
@ -229,8 +229,9 @@ pub enum Air {
args: Vec<TypedRecordUpdateArg>,
},
Negate {
UnOp {
scope: Vec<u64>,
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, .. }

View File

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

View File

@ -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<TypedRecordUpdateArg>,
},
Negate {
UnOp {
location: Span,
value: Box<Self>,
tipo: Arc<Type>,
op: UnOp,
},
}
impl TypedExpr {
pub fn tipo(&self) -> Arc<Type> {
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<UntypedRecordUpdateArg>,
},
Negate {
UnOp {
op: UnOp,
location: Span,
value: Box<Self>,
},
@ -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,

View File

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

View File

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

View File

@ -9,13 +9,9 @@ use super::{error::ParseError, token::Token};
pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, 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 });

View File

@ -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<UntypedExpr>,
op: UnOp,
) -> Result<TypedExpr, Error> {
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,
})
}

View File

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