impl unary

This commit is contained in:
collin 2022-06-13 18:35:18 -07:00
parent 831e011f42
commit 6c759b7174
8 changed files with 170 additions and 88 deletions

View File

@ -15,22 +15,57 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use leo_span::Symbol;
/// A unary operator for a unary expression.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum UnaryOperation {
/// The logical negation operator, i.e., `!`.
/// For example, it transforms `true` to `false`.
Not,
/// The arithmetic negation operator, i.e., `-`.
/// Absolute value checking for overflow, i.e. `.abs()`.
Abs,
/// Absolute value wrapping around at the boundary of the type, i.e. `.abs_wrapped()`.
AbsWrapped,
/// Double operation, i.e. `.double()`.
Double,
/// Multiplicative inverse, i.e. `.inv()`.
Inverse,
/// Negate operation, i.e. `.neg()`.
Negate,
/// Bitwise NOT, i.e. `!`, `.not()`.
Not,
/// Square operation, i.e. `.square()`.
Square,
/// Square root operation, i.e. `.sqrt()`.
SquareRoot,
}
impl UnaryOperation {
/// Returns a `UnaryOperation` from the given `Symbol`.
pub fn from_symbol(symbol: &Symbol) -> Option<UnaryOperation> {
Some(match symbol.as_u32() {
0 => UnaryOperation::Abs,
1 => UnaryOperation::AbsWrapped,
2 => UnaryOperation::Double,
3 => UnaryOperation::Inverse,
4 => UnaryOperation::Negate,
5 => UnaryOperation::Not,
6 => UnaryOperation::Square,
7 => UnaryOperation::SquareRoot,
_ => return None,
})
}
}
impl AsRef<str> for UnaryOperation {
fn as_ref(&self) -> &'static str {
match self {
UnaryOperation::Not => "!",
UnaryOperation::Abs => "abs",
UnaryOperation::AbsWrapped => "abs_wrapped",
UnaryOperation::Double => "double",
UnaryOperation::Inverse => "inv",
UnaryOperation::Negate => "-",
UnaryOperation::Not => "!",
UnaryOperation::Square => ".square",
UnaryOperation::SquareRoot => "sqrt"
}
}
}
@ -39,7 +74,7 @@ impl AsRef<str> for UnaryOperation {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UnaryExpression {
/// The inner expression `op` is applied to.
pub inner: Box<Expression>,
pub receiver: Box<Expression>,
/// The unary operator to apply to `inner`.
pub op: UnaryOperation,
/// The span covering `op inner`.
@ -48,7 +83,7 @@ pub struct UnaryExpression {
impl fmt::Display for UnaryExpression {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.op.as_ref(), self.inner)
write!(f, "{}{}", self.op.as_ref(), self.receiver)
}
}

View File

@ -55,7 +55,7 @@ impl TryFrom<(Type, Expression)> for InputValue {
}
}
(type_, Expression::Unary(unary)) if unary.op == UnaryOperation::Negate => {
InputValue::try_from((type_, *unary.inner))?
InputValue::try_from((type_, *unary.receiver))?
}
(_type_, expr) => return Err(InputError::illegal_expression(&expr, expr.span()).into()),
})

View File

@ -93,7 +93,7 @@ impl<R: ReconstructingReducer> ReconstructingDirector<R> {
}
pub fn reduce_unary(&mut self, unary: &UnaryExpression) -> Result<UnaryExpression> {
let inner = self.reduce_expression(&unary.inner)?;
let inner = self.reduce_expression(&unary.receiver)?;
self.reducer.reduce_unary(unary, inner, unary.op.clone())
}

View File

@ -89,7 +89,7 @@ pub trait ReconstructingReducer {
op: UnaryOperation,
) -> Result<UnaryExpression> {
Ok(UnaryExpression {
inner: Box::new(inner),
receiver: Box::new(inner),
op,
span: unary.span,
})

View File

@ -72,7 +72,7 @@ pub trait ExpressionVisitorDirector<'a>: VisitorDirector<'a> {
fn visit_unary(&mut self, input: &'a UnaryExpression, additional: &Self::AdditionalInput) -> Option<Self::Output> {
if let VisitResult::VisitChildren = self.visitor_ref().visit_unary(input) {
self.visit_expression(&input.inner, additional);
self.visit_expression(&input.receiver, additional);
}
None
}

View File

@ -222,7 +222,7 @@ impl ParserContext<'_> {
inner = Expression::Unary(UnaryExpression {
span: op_span + inner.span(),
op,
inner: Box::new(inner),
receiver: Box::new(inner),
});
}
Ok(inner)
@ -236,7 +236,22 @@ impl ParserContext<'_> {
self.bump();
// Check if the method exists.
if let Some(operator) = BinaryOperation::from_symbol(&method) {
if let Some(operator) = UnaryOperation::from_symbol(&method) {
// Handle unary operators.
// Parse left parenthesis `(`.
self.expect(&Token::LeftParen)?;
// Parse right parenthesis `)`.
let right_span = self.expect(&Token::RightParen)?;
return Ok(Expression::Unary(UnaryExpression {
span: receiver.span() + right_span,
op: operator,
receiver: Box::new(receiver),
}));
} else if let Some(operator) = BinaryOperation::from_symbol(&method) {
// Handle binary operators.
// Parse left parenthesis `(`.
@ -245,7 +260,7 @@ impl ParserContext<'_> {
// Parse operand.
let operand = self.parse_expression()?;
// Parse close parenthesis `)`.
// Parse right parenthesis `)`.
let right_span = self.expect(&Token::RightParen)?;
return Ok(Expression::Binary(BinaryExpression {

View File

@ -64,7 +64,7 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
fn visit_identifier(&mut self, input: &'a Identifier, expected: &Self::AdditionalInput) -> Option<Self::Output> {
if let VisitResult::VisitChildren = self.visitor.visit_identifier(input) {
return if let Some(var) = self.visitor.symbol_table.clone().lookup_variable(&input.name) {
Some(self.visitor.assert_type(*var.type_, expected, var.span))
Some(self.visitor.assert_expected_option(*var.type_, expected, var.span))
} else {
self.visitor
.handler
@ -79,9 +79,9 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
fn visit_value(&mut self, input: &'a ValueExpression, expected: &Self::AdditionalInput) -> Option<Self::Output> {
if let VisitResult::VisitChildren = self.visitor.visit_value(input) {
return Some(match input {
ValueExpression::Address(_, _) => self.visitor.assert_type(Type::Address, expected, input.span()),
ValueExpression::Boolean(_, _) => self.visitor.assert_type(Type::Boolean, expected, input.span()),
ValueExpression::Field(_, _) => self.visitor.assert_type(Type::Field, expected, input.span()),
ValueExpression::Address(_, _) => self.visitor.assert_expected_option(Type::Address, expected, input.span()),
ValueExpression::Boolean(_, _) => self.visitor.assert_expected_option(Type::Boolean, expected, input.span()),
ValueExpression::Field(_, _) => self.visitor.assert_expected_option(Type::Field, expected, input.span()),
ValueExpression::Integer(type_, str_content, _) => {
match type_ {
IntegerType::I8 => {
@ -172,11 +172,11 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
_ => {}
}
self.visitor
.assert_type(Type::IntegerType(*type_), expected, input.span())
.assert_expected_option(Type::IntegerType(*type_), expected, input.span())
}
ValueExpression::Group(_) => self.visitor.assert_type(Type::Group, expected, input.span()),
ValueExpression::Scalar(_, _) => self.visitor.assert_type(Type::Scalar, expected, input.span()),
ValueExpression::String(_, _) => self.visitor.assert_type(Type::String, expected, input.span()),
ValueExpression::Group(_) => self.visitor.assert_expected_option(Type::Group, expected, input.span()),
ValueExpression::Scalar(_, _) => self.visitor.assert_expected_option(Type::Scalar, expected, input.span()),
ValueExpression::String(_, _) => self.visitor.assert_expected_option(Type::String, expected, input.span()),
});
}
@ -192,7 +192,7 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
return match input.op {
BinaryOperation::Nand | BinaryOperation::Nor => {
// Assert equal boolean types.
self.visitor.assert_type(Type::Boolean, destination, input.span());
self.visitor.assert_expected_option(Type::Boolean, destination, input.span());
let t1 = self.visit_expression(&input.left, destination);
let t2 = self.visit_expression(&input.right, destination);
@ -233,31 +233,22 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
// Allow `group` * `scalar` multiplication.
match (t1, t2) {
(Some(Type::Group), other) => {
self.visitor.assert_type_exact(destination, &Type::Group, input.span());
self.visitor
.assert_type_exact(&other, &Type::Scalar, input.right.span());
Some(Type::Group)
.assert_expected_type(&other, Type::Scalar, input.right.span());
Some(self.visitor.assert_expected_type(destination, Type::Group, input.span()))
}
(other, Some(Type::Group)) => {
self.visitor.assert_type_exact(destination, &Type::Group, input.span());
self.visitor.assert_type_exact(&other, &Type::Scalar, input.left.span());
Some(Type::Group)
self.visitor.assert_expected_type(&other, Type::Scalar, input.left.span());
Some(self.visitor.assert_expected_type(destination, Type::Group, input.span()))
}
(Some(t1), Some(t2)) => {
(t1, t2) => {
// Assert equal field or integer types.
self.visitor.assert_type(t1, destination, input.left.span());
self.visitor.assert_type(t2, destination, input.right.span());
return_incorrect_type(Some(t1), Some(t2), destination)
self.visitor.assert_field_int_type(destination, input.span());
return_incorrect_type(t1, t2, destination)
}
(Some(type_), None) => {
self.visitor.assert_type(type_, destination, input.left.span());
None
}
(None, Some(type_)) => {
self.visitor.assert_type(type_, destination, input.right.span());
None
}
(None, None) => None,
}
}
BinaryOperation::Div => {
@ -279,20 +270,17 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
// Allow field * field.
match (t1, t2) {
(Some(Type::Field), type_) => {
self.visitor.assert_type_exact(&type_, &Type::Field, input.right.span());
self.visitor.assert_type_exact(destination, &Type::Field, input.span());
Some(Type::Field)
self.visitor.assert_expected_type(&type_, Type::Field, input.right.span());
Some(self.visitor.assert_expected_type(destination, Type::Field, input.span()))
}
(type_, Some(Type::Field)) => {
self.visitor.assert_type_exact(&type_, &Type::Field, input.left.span());
self.visitor.assert_type_exact(destination, &Type::Field, input.span());
Some(Type::Field)
self.visitor.assert_expected_type(&type_, Type::Field, input.left.span());
Some(self.visitor.assert_expected_type(destination, Type::Field, input.span()))
}
(Some(t1), t2) => {
// Allow integer t2 magnitude (u8, u16, u32)
self.visitor.assert_magnitude_type(&t2, input.right.span());
self.visitor.assert_type_exact(destination, &t1, input.span());
Some(t1)
Some(self.visitor.assert_expected_type(destination, t1, input.span()))
}
(None, t2) => {
// Allow integer t2 magnitude (u8, u16, u32)
@ -302,19 +290,16 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
}
}
BinaryOperation::Eq | BinaryOperation::Neq => {
// Assert equal address, boolean, field, group, scalar, or integer types.
// Assert first and second address, boolean, field, group, scalar, or integer types.
let t1 = self.visit_expression(&input.left, &None);
let t2 = self.visit_expression(&input.right, &None);
self.visitor.assert_eq_types(t1, t2, input.span());
Some(Type::Boolean)
// Assert destination is boolean.
Some(self.visitor.assert_expected_type(destination, Type::Boolean, input.span()))
}
BinaryOperation::Lt | BinaryOperation::Gt | BinaryOperation::Le | BinaryOperation::Ge => {
// Assert the destination type is boolean.
self.visitor
.assert_type_exact(destination, &Type::Boolean, input.span());
// Assert left and right are equal field, scalar, or integer types.
let t1 = self.visit_expression(&input.left, &None);
self.visitor.assert_field_scalar_int_type(&t1, input.left.span());
@ -324,7 +309,9 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
self.visitor.assert_eq_types(t1, t2, input.span());
Some(Type::Boolean)
// Assert destination is boolean.
Some(self.visitor
.assert_expected_type(destination, Type::Boolean, input.span()))
}
BinaryOperation::AddWrapped
| BinaryOperation::SubWrapped
@ -358,17 +345,33 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
None
}
fn visit_unary(&mut self, input: &'a UnaryExpression, expected: &Self::AdditionalInput) -> Option<Self::Output> {
fn visit_unary(&mut self, input: &'a UnaryExpression, destination: &Self::AdditionalInput) -> Option<Self::Output> {
match input.op {
UnaryOperation::Not => {
self.visitor.assert_type(Type::Boolean, expected, input.span());
self.visit_expression(&input.inner, expected)
UnaryOperation::Abs => {
// Assert integer type only.
self.visitor.assert_int_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
UnaryOperation::AbsWrapped => {
// Assert integer type only.
self.visitor.assert_int_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
UnaryOperation::Double => {
// Assert field and group type only.
self.visitor.assert_field_group_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
UnaryOperation::Inverse => {
// Assert field type only.
self.visitor.assert_expected_type(destination, Type::Field, input.span());
self.visit_expression(&input.receiver, destination)
}
UnaryOperation::Negate => {
let prior_negate_state = self.visitor.negate;
self.visitor.negate = true;
let type_ = self.visit_expression(&input.inner, expected);
let type_ = self.visit_expression(&input.receiver, destination);
self.visitor.negate = prior_negate_state;
match type_.as_ref() {
Some(
@ -385,11 +388,26 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
Some(t) => self
.visitor
.handler
.emit_err(TypeCheckerError::type_is_not_negatable(t, input.inner.span()).into()),
.emit_err(TypeCheckerError::type_is_not_negatable(t, input.receiver.span()).into()),
_ => {}
};
type_
}
UnaryOperation::Not => {
// Assert boolean, integer types only.
self.visitor.assert_bool_int_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
UnaryOperation::Square => {
// Assert field type only.
self.visitor.assert_expected_type(destination, Type::Field, input.span());
self.visit_expression(&input.receiver, destination)
}
UnaryOperation::SquareRoot => {
// Assert field and scalar types only.
self.visitor.assert_field_scalar_type(destination, input.span());
self.visit_expression(&input.receiver, destination)
}
}
}
@ -414,7 +432,7 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
match &*input.function {
Expression::Identifier(ident) => {
if let Some(func) = self.visitor.symbol_table.clone().lookup_fn(&ident.name) {
let ret = self.visitor.assert_type(func.output, expected, func.span());
let ret = self.visitor.assert_expected_option(func.output, expected, func.span());
if func.input.len() != input.arguments.len() {
self.visitor.handler.emit_err(

View File

@ -79,6 +79,10 @@ const FIELD_GROUP_INT_TYPES: [Type; 12] = create_type_superset(FIELD_INT_TYPES,
const FIELD_GROUP_SCALAR_INT_TYPES: [Type; 13] = create_type_superset(FIELD_GROUP_INT_TYPES, [Type::Scalar]);
const FIELD_GROUP_TYPES: [Type; 2] = [Type::Field, Type::Group];
const FIELD_SCALAR_TYPES: [Type; 2] = [Type::Field, Type::Scalar];
impl<'a> TypeChecker<'a> {
/// Returns a new type checker given a symbol table and error handler.
pub fn new(symbol_table: &'a mut SymbolTable<'a>, handler: &'a Handler) -> Self {
@ -116,28 +120,28 @@ impl<'a> TypeChecker<'a> {
}
}
/// Returns the given type if it equals the expected type or the expected type is none.
/// Use this method if the expected type is an unknown Option.
pub(crate) fn assert_type(&mut self, type_: Type, expected: &Option<Type>, span: Span) -> Type {
/// Returns the given `actual` type and emits an error if the `expected` type does not match.
pub(crate) fn assert_expected_option(&mut self, actual: Type, expected: &Option<Type>, span: Span) -> Type {
if let Some(expected) = expected {
if &type_ != expected {
if &actual != expected {
self.handler
.emit_err(TypeCheckerError::type_should_be(type_, expected, span).into());
.emit_err(TypeCheckerError::type_should_be(actual, expected, span).into());
}
}
type_
actual
}
/// Emits an error if the given type_ does not match the expected type.
/// Use this method if the expected type is known.
pub(crate) fn assert_type_exact(&mut self, type_: &Option<Type>, expected: &Type, span: Span) {
if let Some(type_) = type_ {
if type_ != expected {
/// Returns the given `expected` type and emits an error if the `actual` type does not match.
pub(crate) fn assert_expected_type(&mut self, actual: &Option<Type>, expected: Type, span: Span) -> Type {
if let Some(actual) = actual {
if actual != &expected {
self.handler
.emit_err(TypeCheckerError::type_should_be(type_, expected, span).into());
.emit_err(TypeCheckerError::type_should_be(actual, expected, span).into());
}
}
expected
}
/// Emits an error to the error handler if the given type is not equal to any of the expected types.
@ -161,24 +165,15 @@ impl<'a> TypeChecker<'a> {
self.assert_one_of_types(type_, &BOOL_INT_TYPES, span)
}
/// Emits an error to the handler if the given type is not an integer.
pub(crate) fn assert_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &INT_TYPES, span)
}
/// Emits an error to the handler if the given type is not a magnitude (u8, u16, u32).
pub(crate) fn assert_magnitude_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &MAGNITUDE_TYPES, span)
}
/// Emits an error to the handler if the given type is not a field or integer.
pub(crate) fn assert_field_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &FIELD_INT_TYPES, span)
}
/// Emits an error to the handler if the given type is not a field, scalar, or integer.
pub(crate) fn assert_field_scalar_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &FIELD_SCALAR_INT_TYPES, span)
/// Emits an error to the handler if the given type is not a field or group.
pub(crate) fn assert_field_group_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &FIELD_GROUP_TYPES, span)
}
/// Emits an error to the handler if the given type is not a field, group, or integer.
@ -190,4 +185,23 @@ impl<'a> TypeChecker<'a> {
pub(crate) fn assert_field_group_scalar_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &FIELD_GROUP_SCALAR_INT_TYPES, span)
}
/// Emits an error to the handler if the given type is not a field or scalar.
pub(crate) fn assert_field_scalar_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &FIELD_SCALAR_TYPES, span)
}
/// Emits an error to the handler if the given type is not a field, scalar, or integer.
pub(crate) fn assert_field_scalar_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &FIELD_SCALAR_INT_TYPES, span)
}
/// Emits an error to the handler if the given type is not an integer.
pub(crate) fn assert_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &INT_TYPES, span)
}
/// Emits an error to the handler if the given type is not a magnitude (u8, u16, u32).
pub(crate) fn assert_magnitude_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &MAGNITUDE_TYPES, span)
}
}