# Operations corresponding to unary expressions.

# Direct assignment of an existing value.
assign-op Assign
class V

# The same, but where the assignment target (LHS) is a record field.
assign-op Field-LHS-Assign
op1-read
class F

unary-expr-op Clone
no-const
op-type X
set-type $$
set-type2 $1
eval	auto v = $1.ToVal(Z_TYPE2)->Clone();
	AssignTarget($$, BuildVal(v, Z_TYPE))

unary-expr-op Size
no-const
op-type I U D A N S T V *
explicit-result-type
set-type $$
set-type2 $1
eval-type I	$$ = ZVal(zeek_int_t($1 < 0 ? -$1 : $1));
eval-type U	$$ = ZVal($1);
eval-type D	$$ = ZVal($1 < 0 ? -$1 : $1);
eval-type A	$$ = ZVal(zeek_uint_t($1->AsAddr().GetFamily() == IPv4 ? 32 : 128));
eval-type N	$$ = ZVal(pow(2.0, double(128 - $1->AsSubNet().LengthIPv6())));
eval-type S	$$ = ZVal(zeek_uint_t($1->Len()));
eval-type T	$$ = ZVal(zeek_uint_t($1->Size()));
eval-type V	$$ = ZVal(zeek_uint_t($1->Size()));
eval	auto v = $1.ToVal(Z_TYPE2)->SizeVal();
	$$ = BuildVal(v, Z_TYPE);

unary-expr-op Not
op-type I
eval ! $1

unary-expr-op Complement
op-type U
eval ~ $1

unary-expr-op Positive
op-type I U D
vector
eval $1

unary-expr-op Negate
op-type I U D
vector
eval -$1

op IncrI
op1-read-write
class V
op-types I
eval	++$$;

op IncrU
op1-read-write
class V
op-types U
eval	++$$;

op DecrI
op1-read-write
class V
op-types I
eval	--$$;

op DecrU
op1-read-write
class V
op-types U
eval	auto& u = $$;
	if ( u == 0 )
		WARN("count underflow");
	--u;

unary-op AppendTo
# Note, even though it feels like appending both reads and modifies
# its first operand, for our purposes it just reads it (to get the
# aggregate), and then modifies its *content* but not the operand's
# value itself.
op1-read
set-type $1
eval	auto vv = $1.AsVector();
	if ( vv->Size() == 0 )
		/* Use the slightly more expensive Assign(), since it
		 * knows how to deal with empty vectors that do not yet
		 * have concrete types.
		 */
		vv->Assign(0, $2.ToVal(Z_TYPE));
	else
		{
		vv->RawVec().push_back(CopyVal($2));
		vv->Modified();
		}

# For vectors-of-any, we always go through the Assign() interface because
# it's needed for tracking the potentially differing types.
unary-op AppendToAnyVec
op1-read
set-type $1
eval	auto vv = $1.AsVector();
	vv->Assign(vv->Size(), $2.ToVal(Z_TYPE));

internal-op AddPatternToField
classes VVi VCi
op1-read
op-types R P I
eval	auto r = $$;
	auto fpat = r->GetField($2)->AsPatternVal();
	if ( fpat )
		{
		$1->AddTo(fpat, false);
		r->Modified();
		}
	else
		ERROR(util::fmt("field value missing: $%s", r->GetType()->AsRecordType()->FieldName($2)));

unary-op ExtendPattern
op1-read
eval	$1.AsPattern()->AddTo($$.AsPattern(), false);

unary-op AddVecToVec
op1-read
eval	if ( ! $1.AsVector()->AddTo($$.AsVector(), false) )
		ERROR("incompatible vector element assignment");

unary-op AddTableToTable
op1-read
eval	auto t = $$.AsTable();
	auto v = $1.AsTable();
	if ( v->Size() > 0 )
		{
		v->AddTo(t, false);
		t->Modified();
		}

unary-op RemoveTableFromTable
op1-read
eval	auto t = $$.AsTable();
	auto v = $1.AsTable();
	if ( v->Size() > 0 )
		{
		v->RemoveFrom(t);
		t->Modified();
		}

unary-expr-op Cast
op-type X
set-type $$
set-type2 $1
eval	EvalCast($$, $1.ToVal(Z_TYPE2))

macro EvalCast(lhs, rhs)
	std::string error;
	auto res = cast_value(rhs, Z_TYPE, error);
	if ( res )
		AssignTarget(lhs, BuildVal(res, Z_TYPE))
	else
		ERROR(error.c_str());

direct-unary-op Is Is

internal-op Is
class VV
op-types I X
eval	auto rhs = $1.ToVal(Z_TYPE2).get();
	$$ = can_cast_value_to_type(rhs, Z_TYPE.get());
