mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Add type expressions to Zeek script
Closes #4173 This is not particularly useful for Zeek script, since they just act as aliases. The main utility is to easily pass primitive types into BIFs.
This commit is contained in:
parent
cb44a6ca53
commit
f28a50717b
10 changed files with 148 additions and 34 deletions
4
NEWS
4
NEWS
|
@ -16,6 +16,10 @@ New Functionality
|
||||||
Zeek now raises a warning when a script declares these events while this
|
Zeek now raises a warning when a script declares these events while this
|
||||||
option is set to true.
|
option is set to true.
|
||||||
|
|
||||||
|
- Type expressions were added to Zeek script. Within scripts, a type may be
|
||||||
|
assigned to a local or global then later used as a type. The type cannot be
|
||||||
|
redefined. This allows types to be directly passed into BIFs without aliasing.
|
||||||
|
|
||||||
Changed Functionality
|
Changed Functionality
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
|
20
src/Expr.cc
20
src/Expr.cc
|
@ -4764,6 +4764,26 @@ void IsExpr::ExprDescribe(ODesc* d) const {
|
||||||
t->Describe(d);
|
t->Describe(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypeExpr::TypeExpr(TypePtr t) : Expr(EXPR_TYPE), ty(std::move(t)) {
|
||||||
|
SetType(make_intrusive<TypeType>(ty));
|
||||||
|
SetLocationInfo(ty->GetLocationInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
ValPtr TypeExpr::Eval(Frame* /* f */) const { return make_intrusive<TypeVal>(ty, true); }
|
||||||
|
|
||||||
|
TraversalCode TypeExpr::Traverse(TraversalCallback* cb) const {
|
||||||
|
TraversalCode tc = cb->PreExpr(this);
|
||||||
|
HANDLE_TC_EXPR_PRE(tc);
|
||||||
|
|
||||||
|
tc = ty->Traverse(cb);
|
||||||
|
HANDLE_TC_EXPR_PRE(tc);
|
||||||
|
|
||||||
|
tc = cb->PostExpr(this);
|
||||||
|
HANDLE_TC_EXPR_POST(tc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypeExpr::ExprDescribe(ODesc* d) const { ty->Describe(d); }
|
||||||
|
|
||||||
ExprPtr get_assign_expr(ExprPtr op1, ExprPtr op2, bool is_init) {
|
ExprPtr get_assign_expr(ExprPtr op1, ExprPtr op2, bool is_init) {
|
||||||
ExprPtr e;
|
ExprPtr e;
|
||||||
|
|
||||||
|
|
20
src/Expr.h
20
src/Expr.h
|
@ -94,6 +94,7 @@ enum ExprTag : int {
|
||||||
EXPR_CAST,
|
EXPR_CAST,
|
||||||
EXPR_IS,
|
EXPR_IS,
|
||||||
EXPR_INDEX_SLICE_ASSIGN,
|
EXPR_INDEX_SLICE_ASSIGN,
|
||||||
|
EXPR_TYPE,
|
||||||
|
|
||||||
// The following types of expressions are only created for ASTs
|
// The following types of expressions are only created for ASTs
|
||||||
// transformed to reduced form; they aren't germane for ASTs produced
|
// transformed to reduced form; they aren't germane for ASTs produced
|
||||||
|
@ -1672,6 +1673,25 @@ protected:
|
||||||
ExprPtr Duplicate() override;
|
ExprPtr Duplicate() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An expression representing a type without a bound identifier. This is necessary
|
||||||
|
* in order to use many primitives directly as values into BIFs without aliases.
|
||||||
|
*/
|
||||||
|
class TypeExpr final : public Expr {
|
||||||
|
public:
|
||||||
|
TypeExpr(TypePtr t);
|
||||||
|
|
||||||
|
ValPtr Eval(Frame* f) const override;
|
||||||
|
TraversalCode Traverse(TraversalCallback* cb) const override;
|
||||||
|
ExprPtr Duplicate() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ExprDescribe(ODesc* d) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TypePtr ty;
|
||||||
|
};
|
||||||
|
|
||||||
// Assigns v1[v2] = v3. Returns an error message, or nullptr on success.
|
// Assigns v1[v2] = v3. Returns an error message, or nullptr on success.
|
||||||
// Factored out so that compiled code can call it as well as the interpreter.
|
// Factored out so that compiled code can call it as well as the interpreter.
|
||||||
extern const char* assign_to_index(ValPtr v1, ValPtr v2, ValPtr v3, bool& iterators_invalidated);
|
extern const char* assign_to_index(ValPtr v1, ValPtr v2, ValPtr v3, bool& iterators_invalidated);
|
||||||
|
|
92
src/parse.y
92
src/parse.y
|
@ -64,7 +64,7 @@
|
||||||
%type <expr> expr opt_expr rhs opt_init anonymous_function lambda_body index_slice opt_deprecated when_condition
|
%type <expr> expr opt_expr rhs opt_init anonymous_function lambda_body index_slice opt_deprecated when_condition
|
||||||
%type <event_expr> event
|
%type <event_expr> event
|
||||||
%type <stmt> stmt stmt_list func_body for_head
|
%type <stmt> stmt stmt_list func_body for_head
|
||||||
%type <type> type opt_type enum_body
|
%type <type> simple_type type opt_type enum_body
|
||||||
%type <func_type> func_hdr func_params
|
%type <func_type> func_hdr func_params
|
||||||
%type <type_l> type_list
|
%type <type_l> type_list
|
||||||
%type <type_decl> type_decl formal_args_decl
|
%type <type_decl> type_decl formal_args_decl
|
||||||
|
@ -766,6 +766,11 @@ expr:
|
||||||
" in arbitrary expression contexts, only"
|
" in arbitrary expression contexts, only"
|
||||||
" as a statement");
|
" as a statement");
|
||||||
|
|
||||||
|
// Type types cannot be reassigned because that could require parse-time
|
||||||
|
// control flow to resolve a variable's type
|
||||||
|
if ( $1->Tag() == EXPR_NAME && $1->GetType()->Tag() == TYPE_TYPE )
|
||||||
|
$1->Error("type expression variables cannot be assigned to");
|
||||||
|
|
||||||
$$ = get_assign_expr({AdoptRef{}, $1}, {AdoptRef{}, $4}, in_init).release();
|
$$ = get_assign_expr({AdoptRef{}, $1}, {AdoptRef{}, $4}, in_init).release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1025,6 +1030,11 @@ expr:
|
||||||
set_location(@1);
|
set_location(@1);
|
||||||
$$ = new ConstExpr({AdoptRef{}, $1});
|
$$ = new ConstExpr({AdoptRef{}, $1});
|
||||||
}
|
}
|
||||||
|
| simple_type
|
||||||
|
{
|
||||||
|
set_location(@1);
|
||||||
|
$$ = new TypeExpr({AdoptRef{}, $1});
|
||||||
|
}
|
||||||
|
|
||||||
| '/' { begin_RE(); } TOK_PATTERN_TEXT TOK_PATTERN_END
|
| '/' { begin_RE(); } TOK_PATTERN_TEXT TOK_PATTERN_END
|
||||||
{
|
{
|
||||||
|
@ -1153,63 +1163,75 @@ enum_body_elem:
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
type:
|
simple_type:
|
||||||
TOK_BOOL {
|
TOK_BOOL
|
||||||
|
{
|
||||||
set_location(@1);
|
set_location(@1);
|
||||||
$$ = base_type(TYPE_BOOL)->Ref();
|
$$ = base_type(TYPE_BOOL)->Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
| TOK_INT {
|
| TOK_INT
|
||||||
|
{
|
||||||
set_location(@1);
|
set_location(@1);
|
||||||
$$ = base_type(TYPE_INT)->Ref();
|
$$ = base_type(TYPE_INT)->Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
| TOK_COUNT {
|
| TOK_COUNT
|
||||||
|
{
|
||||||
set_location(@1);
|
set_location(@1);
|
||||||
$$ = base_type(TYPE_COUNT)->Ref();
|
$$ = base_type(TYPE_COUNT)->Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
| TOK_DOUBLE {
|
| TOK_DOUBLE
|
||||||
|
{
|
||||||
set_location(@1);
|
set_location(@1);
|
||||||
$$ = base_type(TYPE_DOUBLE)->Ref();
|
$$ = base_type(TYPE_DOUBLE)->Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
| TOK_TIME {
|
| TOK_TIME
|
||||||
|
{
|
||||||
set_location(@1);
|
set_location(@1);
|
||||||
$$ = base_type(TYPE_TIME)->Ref();
|
$$ = base_type(TYPE_TIME)->Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
| TOK_INTERVAL {
|
| TOK_INTERVAL
|
||||||
|
{
|
||||||
set_location(@1);
|
set_location(@1);
|
||||||
$$ = base_type(TYPE_INTERVAL)->Ref();
|
$$ = base_type(TYPE_INTERVAL)->Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
| TOK_STRING {
|
| TOK_STRING
|
||||||
|
{
|
||||||
set_location(@1);
|
set_location(@1);
|
||||||
$$ = base_type(TYPE_STRING)->Ref();
|
$$ = base_type(TYPE_STRING)->Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
| TOK_PATTERN {
|
| TOK_PATTERN
|
||||||
|
{
|
||||||
set_location(@1);
|
set_location(@1);
|
||||||
$$ = base_type(TYPE_PATTERN)->Ref();
|
$$ = base_type(TYPE_PATTERN)->Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
| TOK_PORT {
|
| TOK_PORT
|
||||||
|
{
|
||||||
set_location(@1);
|
set_location(@1);
|
||||||
$$ = base_type(TYPE_PORT)->Ref();
|
$$ = base_type(TYPE_PORT)->Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
| TOK_ADDR {
|
| TOK_ADDR
|
||||||
|
{
|
||||||
set_location(@1);
|
set_location(@1);
|
||||||
$$ = base_type(TYPE_ADDR)->Ref();
|
$$ = base_type(TYPE_ADDR)->Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
| TOK_SUBNET {
|
| TOK_SUBNET
|
||||||
|
{
|
||||||
set_location(@1);
|
set_location(@1);
|
||||||
$$ = base_type(TYPE_SUBNET)->Ref();
|
$$ = base_type(TYPE_SUBNET)->Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
| TOK_ANY {
|
| TOK_ANY
|
||||||
|
{
|
||||||
set_location(@1);
|
set_location(@1);
|
||||||
$$ = base_type(TYPE_ANY)->Ref();
|
$$ = base_type(TYPE_ANY)->Ref();
|
||||||
}
|
}
|
||||||
|
@ -1265,24 +1287,6 @@ type:
|
||||||
$$ = new VectorType({AdoptRef{}, $3});
|
$$ = new VectorType({AdoptRef{}, $3});
|
||||||
}
|
}
|
||||||
|
|
||||||
| TOK_FUNCTION func_params
|
|
||||||
{
|
|
||||||
set_location(@1, @2);
|
|
||||||
$$ = $2;
|
|
||||||
}
|
|
||||||
|
|
||||||
| TOK_EVENT '(' formal_args ')'
|
|
||||||
{
|
|
||||||
set_location(@1, @3);
|
|
||||||
$$ = new FuncType({AdoptRef{}, $3}, nullptr, FUNC_FLAVOR_EVENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
| TOK_HOOK '(' formal_args ')'
|
|
||||||
{
|
|
||||||
set_location(@1, @3);
|
|
||||||
$$ = new FuncType({AdoptRef{}, $3}, base_type(TYPE_BOOL), FUNC_FLAVOR_HOOK);
|
|
||||||
}
|
|
||||||
|
|
||||||
| TOK_FILE TOK_OF type
|
| TOK_FILE TOK_OF type
|
||||||
{
|
{
|
||||||
set_location(@1, @3);
|
set_location(@1, @3);
|
||||||
|
@ -1301,9 +1305,31 @@ type:
|
||||||
$$ = new OpaqueType($3);
|
$$ = new OpaqueType($3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type:
|
||||||
|
simple_type
|
||||||
|
| TOK_FUNCTION func_params
|
||||||
|
{
|
||||||
|
set_location(@1, @2);
|
||||||
|
$$ = $2;
|
||||||
|
}
|
||||||
|
|
||||||
|
| TOK_HOOK '(' formal_args ')'
|
||||||
|
{
|
||||||
|
set_location(@1, @3);
|
||||||
|
$$ = new FuncType({AdoptRef{}, $3}, base_type(TYPE_BOOL), FUNC_FLAVOR_HOOK);
|
||||||
|
}
|
||||||
|
|
||||||
|
| TOK_EVENT '(' formal_args ')'
|
||||||
|
{
|
||||||
|
set_location(@1, @3);
|
||||||
|
$$ = new FuncType({AdoptRef{}, $3}, nullptr, FUNC_FLAVOR_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
| resolve_id
|
| resolve_id
|
||||||
{
|
{
|
||||||
if ( ! $1 || ! ($$ = $1->IsType() ? $1->GetType().get() : nullptr) )
|
if ( $1 && $1->GetType()->Tag() == TYPE_TYPE )
|
||||||
|
$$ = $1->GetType()->AsTypeType()->GetType()->Ref();
|
||||||
|
else if ( ! $1 || ! ($$ = $1->IsType() ? $1->GetType().get() : nullptr) )
|
||||||
{
|
{
|
||||||
NullStmt here;
|
NullStmt here;
|
||||||
if ( $1 )
|
if ( $1 )
|
||||||
|
|
|
@ -3393,6 +3393,8 @@ TraversalCode NopExpr::Traverse(TraversalCallback* cb) const {
|
||||||
HANDLE_TC_EXPR_POST(tc);
|
HANDLE_TC_EXPR_POST(tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExprPtr TypeExpr::Duplicate() { return SetSucc(new TypeExpr(ty)); }
|
||||||
|
|
||||||
static bool same_singletons(ExprPtr e1, ExprPtr e2) {
|
static bool same_singletons(ExprPtr e1, ExprPtr e2) {
|
||||||
auto e1t = e1->Tag();
|
auto e1t = e1->Tag();
|
||||||
auto e2t = e2->Tag();
|
auto e2t = e2->Tag();
|
||||||
|
|
|
@ -793,6 +793,7 @@ bool has_AST_node_unknown_to_script_opt(const ProfileFunc* prof, bool /* is_ZAM
|
||||||
EXPR_CAST,
|
EXPR_CAST,
|
||||||
EXPR_IS,
|
EXPR_IS,
|
||||||
// EXPR_INDEX_SLICE_ASSIGN,
|
// EXPR_INDEX_SLICE_ASSIGN,
|
||||||
|
// EXPR_TYPE,
|
||||||
EXPR_INLINE,
|
EXPR_INLINE,
|
||||||
// EXPR_APPEND_TO,
|
// EXPR_APPEND_TO,
|
||||||
// EXPR_INDEX_ASSIGN,
|
// EXPR_INDEX_ASSIGN,
|
||||||
|
@ -804,7 +805,7 @@ bool has_AST_node_unknown_to_script_opt(const ProfileFunc* prof, bool /* is_ZAM
|
||||||
// EXPR_ANY_INDEX,
|
// EXPR_ANY_INDEX,
|
||||||
// EXPR_SCRIPT_OPT_BUILTIN,
|
// EXPR_SCRIPT_OPT_BUILTIN,
|
||||||
// EXPR_NOP,
|
// EXPR_NOP,
|
||||||
#define SCRIPT_OPT_NUM_EXPRS 70
|
#define SCRIPT_OPT_NUM_EXPRS 71
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
error in <...>/type-expr-assignment.zeek, line 8: type expression variables cannot be assigned to (str)
|
6
testing/btest/Baseline/language.type-expr/.stdout
Normal file
6
testing/btest/Baseline/language.type-expr/.stdout
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
hi there :)
|
||||||
|
hey!!
|
||||||
|
42
|
||||||
|
[v=aoeu, valid=T]
|
||||||
|
type
|
10
testing/btest/language/type-expr-assignment.zeek
Normal file
10
testing/btest/language/type-expr-assignment.zeek
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# @TEST-DOC: Ensure redefining a type expression ID is an error
|
||||||
|
# @TEST-EXEC-FAIL: zeek -b %INPUT
|
||||||
|
# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff .stderr
|
||||||
|
|
||||||
|
event zeek_init()
|
||||||
|
{
|
||||||
|
local str = string;
|
||||||
|
str = count;
|
||||||
|
local my_string: str; # This will still be a string
|
||||||
|
}
|
23
testing/btest/language/type-expr.zeek
Normal file
23
testing/btest/language/type-expr.zeek
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# @TEST-DOC: Test valid use of type expressions in scripts
|
||||||
|
# @TEST-EXEC: zeek -b %INPUT
|
||||||
|
# @TEST-EXEC: TEST_DIFF_CANONIFIER= btest-diff .stdout
|
||||||
|
|
||||||
|
global global_str = string;
|
||||||
|
global my_global_string: global_str = "hey!!";
|
||||||
|
|
||||||
|
event zeek_init()
|
||||||
|
{
|
||||||
|
local str = string;
|
||||||
|
local my_string: str = "hi there :)";
|
||||||
|
print my_string;
|
||||||
|
print my_global_string;
|
||||||
|
|
||||||
|
local integer = int;
|
||||||
|
local my_int: integer = 41;
|
||||||
|
my_int += 1;
|
||||||
|
print my_int;
|
||||||
|
|
||||||
|
# Try a couple of functions that take types
|
||||||
|
print from_json("\"aoeu\"", string);
|
||||||
|
print type_name(string);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue