Add array-style index accessor for strings. Addresses #422.

The index expression can take up to two indices for the start and end
index of the substring to return (e.g. "mystring[1,3]").  Negative
indices are allowed, with -1 representing the last character in the
string.  The indexing is not cyclic -- if the starting index is >= the
length of the string an empty string is returned, and if the ending
index is >= the length of the string then it's interpreted as the last
index of the string.  Assigning to substrings accessed like this isn't
allowed.
This commit is contained in:
Jon Siwek 2012-12-20 17:13:06 -06:00
parent ea6b62f586
commit 55c515d50a
5 changed files with 115 additions and 10 deletions

View file

@ -2808,11 +2808,17 @@ IndexExpr::IndexExpr(Expr* arg_op1, ListExpr* arg_op2)
SetError("not an index type"); SetError("not an index type");
else if ( ! op1->Type()->YieldType() ) else if ( ! op1->Type()->YieldType() )
{
if ( IsString(op1->Type()->Tag()) &&
match_type == MATCHES_INDEX_SCALAR )
SetType(base_type(TYPE_STRING));
else
// It's a set - so indexing it yields void. We don't // It's a set - so indexing it yields void. We don't
// directly generate an error message, though, since this // directly generate an error message, though, since this
// expression might be part of an add/delete statement, // expression might be part of an add/delete statement,
// rather than yielding a value. // rather than yielding a value.
SetType(base_type(TYPE_VOID)); SetType(base_type(TYPE_VOID));
}
else if ( match_type == MATCHES_INDEX_SCALAR ) else if ( match_type == MATCHES_INDEX_SCALAR )
SetType(op1->Type()->YieldType()->Ref()); SetType(op1->Type()->YieldType()->Ref());
@ -2888,6 +2894,9 @@ void IndexExpr::Delete(Frame* f)
Expr* IndexExpr::MakeLvalue() Expr* IndexExpr::MakeLvalue()
{ {
if ( IsString(op1->Type()->Tag()) )
ExprError("cannot assign to string index expression");
return new RefExpr(this); return new RefExpr(this);
} }
@ -2961,10 +2970,34 @@ Val* IndexExpr::Fold(Val* v1, Val* v2) const
Val* v = 0; Val* v = 0;
if ( v1->Type()->Tag() == TYPE_VECTOR ) switch ( v1->Type()->Tag() ) {
case TYPE_VECTOR:
v = v1->AsVectorVal()->Lookup(v2); v = v1->AsVectorVal()->Lookup(v2);
else break;
case TYPE_TABLE:
v = v1->AsTableVal()->Lookup(v2); v = v1->AsTableVal()->Lookup(v2);
break;
case TYPE_STRING:
{
const ListVal* lv = v2->AsListVal();
const BroString* s = v1->AsString();
int len = s->Len();
bro_int_t first = lv->Index(0)->AsInt();
bro_int_t last = lv->Length() > 1 ? lv->Index(1)->AsInt() : first;
if ( first < 0 )
first += len;
if ( last < 0 )
last += len;
BroString* substring = s->GetSubstring(first, last - first + 1);
return new StringVal(substring ? substring : new BroString(""));
break;
}
default:
Error("type cannot be indexed");
break;
}
if ( v ) if ( v )
return v->Ref(); return v->Ref();
@ -2991,14 +3024,22 @@ void IndexExpr::Assign(Frame* f, Val* v, Opcode op)
return; return;
} }
if ( v1->Type()->Tag() == TYPE_VECTOR ) switch ( v1->Type()->Tag() ) {
{ case TYPE_VECTOR:
if ( ! v1->AsVectorVal()->Assign(v2, v, this, op) ) if ( ! v1->AsVectorVal()->Assign(v2, v, this, op) )
Internal("assignment failed"); Internal("assignment failed");
} break;
case TYPE_TABLE:
else if ( ! v1->AsTableVal()->Assign(v2, v, op) ) if ( ! v1->AsTableVal()->Assign(v2, v, op) )
Internal("assignment failed"); Internal("assignment failed");
break;
case TYPE_STRING:
Internal("assignment via string index accessor not allowed");
break;
default:
Internal("bad index expression type in assignment");
break;
}
Unref(v1); Unref(v1);
Unref(v2); Unref(v2);

View file

@ -114,8 +114,16 @@ BroType::~BroType()
delete [] type_id; delete [] type_id;
} }
int BroType::MatchesIndex(ListExpr*& /* index */) const int BroType::MatchesIndex(ListExpr*& index) const
{ {
if ( Tag() == TYPE_STRING )
{
if ( index->Exprs().length() != 1 && index->Exprs().length() != 2 )
return DOES_NOT_MATCH_INDEX;
if ( check_and_promote_exprs_to_type(index, ::base_type(TYPE_INT)) )
return MATCHES_INDEX_SCALAR;
}
return DOES_NOT_MATCH_INDEX; return DOES_NOT_MATCH_INDEX;
} }

View file

@ -0,0 +1,13 @@
1
12
123456
0123456789
8
789
9
9
9
2
1

View file

@ -0,0 +1,26 @@
# Needs perftools support.
#
# @TEST-GROUP: leaks
#
# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks
#
# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local bro -m -b -r $TRACES/wikipedia.trace %INPUT
event new_connection(c: connection)
{
local s = "0123456789";
print s[1];
print s[1,2];
print s[1,6];
print s[0,20];
print s[-2];
print s[-3,-1];
print s[-1,-10];
print s[-1,0];
print s[-1,5];
print s[20, 23];
print s[-20, 23];
print s[0,5][2];
print s[0,5][1,3][0];
}

View file

@ -0,0 +1,17 @@
# @TEST-EXEC: bro -b %INPUT >out
# @TEST-EXEC: btest-diff out
local s = "0123456789";
print s[1];
print s[1,2];
print s[1,6];
print s[0,20];
print s[-2];
print s[-3,-1];
print s[-1,-10];
print s[-1,0];
print s[-1,5];
print s[20, 23];
print s[-20, 23];
print s[0,5][2];
print s[0,5][1,3][0];