Expr: Remove vector scalar operations

These seem to have been deprecated since 2018, so do it now.
Unfortunately the message didn't contain a version when it'll
be removed, but it's been long enough.
This commit is contained in:
Arne Welzel 2023-03-17 12:03:58 +01:00
parent 12d5dca70f
commit 5ef62b2de8
12 changed files with 112 additions and 282 deletions

View file

@ -1287,25 +1287,6 @@ void BinaryExpr::PromoteForInterval(ExprPtr& op)
op = make_intrusive<ArithCoerceExpr>(op, TYPE_DOUBLE); op = make_intrusive<ArithCoerceExpr>(op, TYPE_DOUBLE);
} }
bool BinaryExpr::IsScalarAggregateOp() const
{
const bool is_vec1 = IsAggr(op1->GetType()->Tag()) || is_list(op1);
const bool is_vec2 = IsAggr(op2->GetType()->Tag()) || is_list(op2);
const bool either_vec = is_vec1 || is_vec2;
const bool both_vec = is_vec1 && is_vec2;
return either_vec && ! both_vec;
}
void BinaryExpr::CheckScalarAggOp() const
{
if ( ! IsError() && IsScalarAggregateOp() )
{
reporter->Warning("mixing vector and scalar operands is deprecated (%s) (%s)",
type_name(op1->GetType()->Tag()), type_name(op2->GetType()->Tag()));
}
}
bool BinaryExpr::CheckForRHSList() bool BinaryExpr::CheckForRHSList()
{ {
if ( op2->Tag() != EXPR_LIST ) if ( op2->Tag() != EXPR_LIST )
@ -1420,25 +1401,11 @@ IncrExpr::IncrExpr(ExprTag arg_tag, ExprPtr arg_op) : UnaryExpr(arg_tag, arg_op-
return; return;
const auto& t = op->GetType(); const auto& t = op->GetType();
if ( IsVector(t->Tag()) )
{
if ( ! IsIntegral(t->AsVectorType()->Yield()->Tag()) )
ExprError("vector elements must be integral for increment operator");
else
{
reporter->Warning("increment/decrement operations for vectors deprecated");
SetType(t);
}
}
else
{
if ( ! IsIntegral(t->Tag()) ) if ( ! IsIntegral(t->Tag()) )
ExprError("requires an integral operand"); ExprError("requires an integral operand");
else else
SetType(t); SetType(t);
} }
}
ValPtr IncrExpr::DoSingleEval(Frame* f, Val* v) const ValPtr IncrExpr::DoSingleEval(Frame* f, Val* v) const
{ {
@ -1469,27 +1436,10 @@ ValPtr IncrExpr::Eval(Frame* f) const
if ( ! v ) if ( ! v )
return nullptr; return nullptr;
if ( is_vector(v) )
{
VectorValPtr v_vec{NewRef{}, v->AsVectorVal()};
for ( unsigned int i = 0; i < v_vec->Size(); ++i )
{
auto elt = v_vec->ValAt(i);
if ( elt )
v_vec->Assign(i, DoSingleEval(f, elt.get()));
}
op->Assign(f, std::move(v_vec));
return v;
}
else
{
auto new_v = DoSingleEval(f, v.get()); auto new_v = DoSingleEval(f, v.get());
op->Assign(f, new_v); op->Assign(f, new_v);
return new_v; return new_v;
} }
}
ComplementExpr::ComplementExpr(ExprPtr arg_op) : UnaryExpr(EXPR_COMPLEMENT, std::move(arg_op)) ComplementExpr::ComplementExpr(ExprPtr arg_op) : UnaryExpr(EXPR_COMPLEMENT, std::move(arg_op))
{ {
@ -1634,14 +1584,18 @@ AddExpr::AddExpr(ExprPtr arg_op1, ExprPtr arg_op2)
return; return;
TypeTag bt1 = op1->GetType()->Tag(); TypeTag bt1 = op1->GetType()->Tag();
if ( IsVector(bt1) )
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
TypeTag bt2 = op2->GetType()->Tag(); TypeTag bt2 = op2->GetType()->Tag();
if ( IsVector(bt2) ) if ( IsVector(bt1) && IsVector(bt2) )
{
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
bt2 = op2->GetType()->AsVectorType()->Yield()->Tag(); bt2 = op2->GetType()->AsVectorType()->Yield()->Tag();
}
else if ( IsVector(bt1) || IsVector(bt2) )
{
ExprError("cannot mix vector and scalar operands");
return;
}
TypePtr base_result_type; TypePtr base_result_type;
@ -1656,11 +1610,9 @@ AddExpr::AddExpr(ExprPtr arg_op1, ExprPtr arg_op2)
else else
ExprError("requires arithmetic operands"); ExprError("requires arithmetic operands");
CheckScalarAggOp();
if ( base_result_type ) if ( base_result_type )
{ {
if ( is_vector(op1) || is_vector(op2) ) if ( is_vector(op1) )
SetType(make_intrusive<VectorType>(std::move(base_result_type))); SetType(make_intrusive<VectorType>(std::move(base_result_type)));
else else
SetType(std::move(base_result_type)); SetType(std::move(base_result_type));
@ -1798,12 +1750,18 @@ SubExpr::SubExpr(ExprPtr arg_op1, ExprPtr arg_op2)
const auto& t2 = op2->GetType(); const auto& t2 = op2->GetType();
TypeTag bt1 = t1->Tag(); TypeTag bt1 = t1->Tag();
if ( IsVector(bt1) )
bt1 = t1->AsVectorType()->Yield()->Tag();
TypeTag bt2 = t2->Tag(); TypeTag bt2 = t2->Tag();
if ( IsVector(bt2) )
if ( IsVector(bt1) && IsVector(bt2) )
{
bt1 = t1->AsVectorType()->Yield()->Tag();
bt2 = t2->AsVectorType()->Yield()->Tag(); bt2 = t2->AsVectorType()->Yield()->Tag();
}
else if ( IsVector(bt1) || IsVector(bt2) )
{
ExprError("cannot mix vector and scalar operands");
return;
}
TypePtr base_result_type; TypePtr base_result_type;
@ -1827,11 +1785,9 @@ SubExpr::SubExpr(ExprPtr arg_op1, ExprPtr arg_op2)
else else
ExprError("requires arithmetic operands"); ExprError("requires arithmetic operands");
CheckScalarAggOp();
if ( base_result_type ) if ( base_result_type )
{ {
if ( is_vector(op1) || is_vector(op2) ) if ( is_vector(op1) )
SetType(make_intrusive<VectorType>(std::move(base_result_type))); SetType(make_intrusive<VectorType>(std::move(base_result_type)));
else else
SetType(std::move(base_result_type)); SetType(std::move(base_result_type));
@ -1902,14 +1858,18 @@ TimesExpr::TimesExpr(ExprPtr arg_op1, ExprPtr arg_op2)
Canonicalize(); Canonicalize();
TypeTag bt1 = op1->GetType()->Tag(); TypeTag bt1 = op1->GetType()->Tag();
if ( IsVector(bt1) )
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
TypeTag bt2 = op2->GetType()->Tag(); TypeTag bt2 = op2->GetType()->Tag();
if ( IsVector(bt2) ) if ( IsVector(bt1) && IsVector(bt2) )
{
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
bt2 = op2->GetType()->AsVectorType()->Yield()->Tag(); bt2 = op2->GetType()->AsVectorType()->Yield()->Tag();
}
else if ( IsVector(bt1) || IsVector(bt2) )
{
ExprError("cannot mix vector and scalar operands");
return;
}
if ( bt1 == TYPE_INTERVAL || bt2 == TYPE_INTERVAL ) if ( bt1 == TYPE_INTERVAL || bt2 == TYPE_INTERVAL )
{ {
@ -1922,8 +1882,6 @@ TimesExpr::TimesExpr(ExprPtr arg_op1, ExprPtr arg_op2)
PromoteType(max_type(bt1, bt2), is_vector(op1) || is_vector(op2)); PromoteType(max_type(bt1, bt2), is_vector(op1) || is_vector(op2));
else else
ExprError("requires arithmetic operands"); ExprError("requires arithmetic operands");
CheckScalarAggOp();
} }
void TimesExpr::Canonicalize() void TimesExpr::Canonicalize()
@ -1940,14 +1898,18 @@ DivideExpr::DivideExpr(ExprPtr arg_op1, ExprPtr arg_op2)
return; return;
TypeTag bt1 = op1->GetType()->Tag(); TypeTag bt1 = op1->GetType()->Tag();
if ( IsVector(bt1) )
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
TypeTag bt2 = op2->GetType()->Tag(); TypeTag bt2 = op2->GetType()->Tag();
if ( IsVector(bt2) ) if ( IsVector(bt1) && IsVector(bt2) )
{
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
bt2 = op2->GetType()->AsVectorType()->Yield()->Tag(); bt2 = op2->GetType()->AsVectorType()->Yield()->Tag();
}
else if ( IsVector(bt1) || IsVector(bt2) )
{
ExprError("cannot mix vector and scalar operands");
return;
}
if ( bt1 == TYPE_INTERVAL || bt2 == TYPE_INTERVAL ) if ( bt1 == TYPE_INTERVAL || bt2 == TYPE_INTERVAL )
{ {
@ -1972,8 +1934,6 @@ DivideExpr::DivideExpr(ExprPtr arg_op1, ExprPtr arg_op2)
else else
ExprError("requires arithmetic operands"); ExprError("requires arithmetic operands");
CheckScalarAggOp();
} }
ValPtr DivideExpr::AddrFold(Val* v1, Val* v2) const ValPtr DivideExpr::AddrFold(Val* v1, Val* v2) const
@ -2008,21 +1968,23 @@ ModExpr::ModExpr(ExprPtr arg_op1, ExprPtr arg_op2)
return; return;
TypeTag bt1 = op1->GetType()->Tag(); TypeTag bt1 = op1->GetType()->Tag();
if ( IsVector(bt1) )
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
TypeTag bt2 = op2->GetType()->Tag(); TypeTag bt2 = op2->GetType()->Tag();
if ( IsVector(bt2) ) if ( IsVector(bt1) && IsVector(bt2) )
{
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
bt2 = op2->GetType()->AsVectorType()->Yield()->Tag(); bt2 = op2->GetType()->AsVectorType()->Yield()->Tag();
}
else if ( IsVector(bt1) || IsVector(bt2) )
{
ExprError("cannot mix vector and scalar operands");
return;
}
if ( BothIntegral(bt1, bt2) ) if ( BothIntegral(bt1, bt2) )
PromoteType(max_type(bt1, bt2), is_vector(op1) || is_vector(op2)); PromoteType(max_type(bt1, bt2), is_vector(op1) || is_vector(op2));
else else
ExprError("requires integral operands"); ExprError("requires integral operands");
CheckScalarAggOp();
} }
BoolExpr::BoolExpr(ExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2) BoolExpr::BoolExpr(ExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2)
@ -2032,23 +1994,23 @@ BoolExpr::BoolExpr(ExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2)
return; return;
TypeTag bt1 = op1->GetType()->Tag(); TypeTag bt1 = op1->GetType()->Tag();
if ( IsVector(bt1) )
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
TypeTag bt2 = op2->GetType()->Tag(); TypeTag bt2 = op2->GetType()->Tag();
if ( IsVector(bt2) ) if ( IsVector(bt1) && IsVector(bt2) )
{
bt1 = op1->GetType()->AsVectorType()->Yield()->Tag();
bt2 = op2->GetType()->AsVectorType()->Yield()->Tag(); bt2 = op2->GetType()->AsVectorType()->Yield()->Tag();
}
else if ( IsVector(bt1) || IsVector(bt2) )
{
ExprError("cannot mix vector and scalar operands");
return;
}
if ( BothBool(bt1, bt2) ) if ( BothBool(bt1, bt2) )
{ {
if ( is_vector(op1) || is_vector(op2) ) if ( is_vector(op1) )
{
if ( ! (is_vector(op1) && is_vector(op2)) )
reporter->Warning("mixing vector and scalar operands is deprecated");
SetType(make_intrusive<VectorType>(base_type(TYPE_BOOL))); SetType(make_intrusive<VectorType>(base_type(TYPE_BOOL)));
}
else else
SetType(base_type(TYPE_BOOL)); SetType(base_type(TYPE_BOOL));
} }
@ -2095,46 +2057,7 @@ ValPtr BoolExpr::Eval(Frame* f) const
if ( ! is_vec1 && ! is_vec2 ) if ( ! is_vec1 && ! is_vec2 )
return DoSingleEval(f, std::move(v1), op2.get()); return DoSingleEval(f, std::move(v1), op2.get());
// Handle scalar op vector or vector op scalar // Both are vectors.
// We can't short-circuit everything since we need to eval
// a vector in order to find out its length.
if ( ! (is_vec1 && is_vec2) )
{ // Only one is a vector.
ValPtr scalar_v;
VectorValPtr vector_v;
if ( is_vec1 )
{
scalar_v = op2->Eval(f);
vector_v = {AdoptRef{}, v1.release()->AsVectorVal()};
}
else
{
scalar_v = std::move(v1);
vector_v = {AdoptRef{}, op2->Eval(f).release()->AsVectorVal()};
}
if ( ! scalar_v || ! vector_v )
return nullptr;
VectorValPtr result;
// It's either an EXPR_AND_AND or an EXPR_OR_OR.
bool is_and = (tag == EXPR_AND_AND);
if ( scalar_v->IsZero() == is_and )
{
result = make_intrusive<VectorVal>(GetType<VectorType>());
result->Resize(vector_v->Size());
result->AssignRepeat(0, result->Size(), std::move(scalar_v));
}
else
result = std::move(vector_v);
return result;
}
// Only case remaining: both are vectors.
auto v2 = op2->Eval(f); auto v2 = op2->Eval(f);
if ( ! v2 ) if ( ! v2 )
@ -2206,8 +2129,6 @@ BitExpr::BitExpr(ExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2)
return; // because following scalar check isn't apt return; // because following scalar check isn't apt
} }
CheckScalarAggOp();
if ( (bt1 == TYPE_COUNT) && (bt2 == TYPE_COUNT) ) if ( (bt1 == TYPE_COUNT) && (bt2 == TYPE_COUNT) )
{ {
if ( is_vector(op1) || is_vector(op2) ) if ( is_vector(op1) || is_vector(op2) )
@ -2248,16 +2169,21 @@ EqExpr::EqExpr(ExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2)
const auto& t1 = op1->GetType(); const auto& t1 = op1->GetType();
const auto& t2 = op2->GetType(); const auto& t2 = op2->GetType();
TypeTag bt1 = t1->Tag(); TypeTag bt1 = t1->Tag();
if ( IsVector(bt1) )
bt1 = t1->AsVectorType()->Yield()->Tag();
TypeTag bt2 = t2->Tag(); TypeTag bt2 = t2->Tag();
if ( IsVector(bt2) )
bt2 = t2->AsVectorType()->Yield()->Tag();
if ( is_vector(op1) || is_vector(op2) ) if ( IsVector(bt1) && IsVector(bt2) )
{
bt1 = t1->AsVectorType()->Yield()->Tag();
bt2 = t2->AsVectorType()->Yield()->Tag();
}
else if ( IsVector(bt1) || IsVector(bt2) )
{
ExprError("cannot mix vector and scalar operands");
return;
}
if ( is_vector(op1) )
SetType(make_intrusive<VectorType>(base_type(TYPE_BOOL))); SetType(make_intrusive<VectorType>(base_type(TYPE_BOOL)));
else else
SetType(base_type(TYPE_BOOL)); SetType(base_type(TYPE_BOOL));
@ -2310,8 +2236,6 @@ EqExpr::EqExpr(ExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2)
else else
ExprError("type clash in comparison"); ExprError("type clash in comparison");
CheckScalarAggOp();
} }
void EqExpr::Canonicalize() void EqExpr::Canonicalize()
@ -2363,16 +2287,21 @@ RelExpr::RelExpr(ExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2)
const auto& t1 = op1->GetType(); const auto& t1 = op1->GetType();
const auto& t2 = op2->GetType(); const auto& t2 = op2->GetType();
TypeTag bt1 = t1->Tag(); TypeTag bt1 = t1->Tag();
if ( IsVector(bt1) )
bt1 = t1->AsVectorType()->Yield()->Tag();
TypeTag bt2 = t2->Tag(); TypeTag bt2 = t2->Tag();
if ( IsVector(bt2) )
bt2 = t2->AsVectorType()->Yield()->Tag();
if ( is_vector(op1) || is_vector(op2) ) if ( IsVector(bt1) && IsVector(bt2) )
{
bt1 = t1->AsVectorType()->Yield()->Tag();
bt2 = t2->AsVectorType()->Yield()->Tag();
}
else if ( IsVector(bt1) || IsVector(bt2) )
{
ExprError("cannot mix vector and scalar operands");
return;
}
if ( is_vector(op1) )
SetType(make_intrusive<VectorType>(base_type(TYPE_BOOL))); SetType(make_intrusive<VectorType>(base_type(TYPE_BOOL)));
else else
SetType(base_type(TYPE_BOOL)); SetType(base_type(TYPE_BOOL));
@ -2392,8 +2321,6 @@ RelExpr::RelExpr(ExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2)
else if ( bt1 != TYPE_TIME && bt1 != TYPE_INTERVAL && bt1 != TYPE_PORT && bt1 != TYPE_ADDR && else if ( bt1 != TYPE_TIME && bt1 != TYPE_INTERVAL && bt1 != TYPE_PORT && bt1 != TYPE_ADDR &&
bt1 != TYPE_STRING ) bt1 != TYPE_STRING )
ExprError("illegal comparison"); ExprError("illegal comparison");
CheckScalarAggOp();
} }
void RelExpr::Canonicalize() void RelExpr::Canonicalize()

View file

@ -624,14 +624,6 @@ protected:
void ExprDescribe(ODesc* d) const override; void ExprDescribe(ODesc* d) const override;
// Reports on if this BinaryExpr involves a scalar and aggregate
// type (vec, list, table, record).
bool IsScalarAggregateOp() const;
// Warns about deprecated scalar vector operations like
// `[1, 2, 3] == 1` or `["a", "b", "c"] + "a"`.
void CheckScalarAggOp() const;
// For assignment operations (=, +=, -=) checks for a valid // For assignment operations (=, +=, -=) checks for a valid
// expression-list on the RHS (op2), potentially transforming // expression-list on the RHS (op2), potentially transforming
// op2 in the process. Returns true if the list is present // op2 in the process. Returns true if the list is present

View file

@ -1,16 +0,0 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
warning in <...>/scalar-vector.zeek, line 12: mixing vector and scalar operands is deprecated (string) (vector)
warning in <...>/scalar-vector.zeek, line 13: mixing vector and scalar operands is deprecated (vector) (string)
warning in <...>/scalar-vector.zeek, line 14: mixing vector and scalar operands is deprecated (string) (vector)
warning in <...>/scalar-vector.zeek, line 18: mixing vector and scalar operands is deprecated (count) (vector)
warning in <...>/scalar-vector.zeek, line 19: mixing vector and scalar operands is deprecated (count) (vector)
warning in <...>/scalar-vector.zeek, line 20: mixing vector and scalar operands is deprecated (vector) (count)
warning in <...>/scalar-vector.zeek, line 21: mixing vector and scalar operands is deprecated (vector) (count)
[F, T, F]
[aa, ba, ca]
[aa, ab, ac]
[F, T, F]
[2, 4, 6]
[1, 0, 1]
[0, 1, 1]
[1, 2, 3, 1]

View file

@ -0,0 +1,3 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
error in <...>/incr-vec-expr-error.test, line 16: requires an integral operand (++vec)
error in <...>/incr-vec-expr-error.test, line 17: requires an integral operand (++v$c)

View file

@ -1,6 +0,0 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
[0, 0, 0]
[a=0, b=test, c=[1, 2, 3]]
[1, 1, 1]
[a=1, b=test, c=[1, 2, 3]]
[a=1, b=test, c=[2, 3, 4]]

View file

@ -1,4 +0,0 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
warning in <...>/vector-deprecated.zeek, line 19: mixing vector and scalar operands is deprecated (vector) (string)
warning in <...>/vector-deprecated.zeek, line 22: mixing vector and scalar operands is deprecated (string) (vector)
warning in <...>/vector-deprecated.zeek, line 25: mixing vector and scalar operands is deprecated (string) (vector)

View file

@ -1,4 +0,0 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
+ operator [string] (PASS)
== operator [string] (PASS)
== operator [string] (PASS)

View file

@ -1,25 +0,0 @@
# Skip this test when using script optimization, as that generate hard
# errors in addition to warnings.
# @TEST-REQUIRES: test "${ZEEK_ZAM}" != "1"
# @TEST-REQUIRES: test "${ZEEK_USE_CPP}" != "1"
#
# @TEST-EXEC: zeek -b %INPUT > out 2>&1
# @TEST-EXEC: TEST_DIFF_CANONIFIER="$SCRIPTS/diff-remove-abspath" btest-diff out
event zeek_init()
{
const sv = vector("a", "b", "c");
print sv == "b";
print sv + "a";
print "a" + sv;
const nv = vector(1, 2, 3);
print nv == 2;
print nv * 2;
print nv % 2;
print nv / 2;
const also_nv = nv += 1;
print nv;
}

View file

@ -0,0 +1,17 @@
# @TEST-DOC: Support for incrementing vectors using IncrExpr has been removed.
# @TEST-EXEC-FAIL: zeek -b %INPUT >out 2>&1
# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff out
type rec: record {
a: count;
b: string;
c: vector of count;
};
global vec: vector of count = vector(0,0,0);
global v: rec = [$a=0, $b="test", $c=vector(1,2,3)];
++v$a;
++vec;
++v$c;

View file

@ -1,28 +0,0 @@
# @TEST-REQUIRES: test "${ZEEK_USE_CPP}" != "1"
# @TEST-EXEC: zeek -b %INPUT >out
# @TEST-EXEC: btest-diff out
type rec: record {
a: count;
b: string;
c: vector of count;
};
global vec: vector of count = vector(0,0,0);
global v: rec = [$a=0, $b="test", $c=vector(1,2,3)];
print vec;
print v;
++vec;
print vec;
++v$a;
print v;
++v$c;
print v;

View file

@ -1,28 +0,0 @@
# @TEST-REQUIRES: test "${ZEEK_USE_CPP}" != "1"
# @TEST-EXEC: zeek -b %INPUT >out
# @TEST-EXEC: btest-diff out
# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff .stderr
function test_case(msg: string, expect: bool)
{
print fmt("%s (%s)", msg, expect ? "PASS" : "FAIL");
}
event zeek_init()
{
local v6 = vector( 10, 20, 30 );
local v16 = v6;
v16 += 40;
local vs1 = vector( "foo", "bar" );
local vss2 = vs1 + "@";
test_case( "+ operator [string]", vss2[0] == "foo@" && vss2[1] == "bar@" );
local vss4 = (vs1 == "bar");
test_case( "== operator [string]", vss4[0] == F && vss4[1] == T );
local vss5 = ("bar" == vs1);
test_case( "== operator [string]", vss5[0] == F && vss5[1] == T );
# !=, <, >, <=, >= are handled the same as ==, skipping tests
}

View file

@ -8,7 +8,9 @@ local n = 0;
vec[5] = "five"; vec[5] = "five";
vec[7] = "seven"; vec[7] = "seven";
print vec; print vec;
vec = vec + ".exe";
for ( i in vec )
vec[i] += ".exe";
for ( c in ten ) for ( c in ten )
{ {