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:
parent
542e39f093
commit
083b7fcb5f
|
@ -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, .. }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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),
|
||||||
})
|
})
|
||||||
|
|
|
@ -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 });
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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!(),
|
||||||
|
|
Loading…
Reference in New Issue