mirror of
https://github.com/zeek/zeek.git
synced 2025-10-03 23:28:20 +00:00
773 lines
22 KiB
Text
773 lines
22 KiB
Text
|
|
@node Statements and Expressions
|
|
@chapter Statements and Expressions
|
|
|
|
You express Bro's analysis of network traffic using @emph{event handlers},
|
|
which, as discussed in XX,
|
|
are essentially subroutines written in Bro's policy scripting
|
|
language. In this chapter we discuss the different types of statements
|
|
and expressions available for expressing event handlers and the auxiliary
|
|
functions they use.
|
|
|
|
@menu
|
|
* Statements::
|
|
* Expressions::
|
|
@end menu
|
|
|
|
@node Statements,
|
|
@section Statements
|
|
|
|
@cindex statements
|
|
Bro functions and event handlers are written in an imperative style, and
|
|
the statements available for doing so are similar to those provided in C.
|
|
@cindex statements, semi-colon termination
|
|
@cindex semi-colon statement termination
|
|
@cindex statements, multi-line
|
|
@cindex whitespace, in statements
|
|
As in C, statements are terminated with a semi-colon. There are no
|
|
restrictions on how many lines a statement can span. Whitespace can appear
|
|
between any of the syntactic components in a statement, and its presence
|
|
always serves as a separator (that is, a single syntactic component cannot
|
|
in general contain embedded whitespace, unless it is escaped in some form,
|
|
such as appearing inside a string literal).
|
|
|
|
Bro provides the following types of statements:
|
|
|
|
@command{expression}
|
|
@cindex expression
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
@emph{expr} ;
|
|
@end quotation
|
|
As in C, an expression by itself can also be used as a statement.
|
|
For example, assignments, calling functions, and scheduling
|
|
timers are all expressions; they also are often used as statements.
|
|
@end quotation
|
|
|
|
@command{print}
|
|
@cindex print statement
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
print @emph{file} @emph{expr-list} ;
|
|
@end quotation
|
|
The expressions are converted to a list of strings, which are then
|
|
printed as a comma-separated list. If the first expression is of
|
|
type , then the other expressions are printed to
|
|
the corresponding file; otherwise they're written to
|
|
@cindex stdout
|
|
@emph{stdout}.
|
|
|
|
For control over how the strings are formatted, see the @code{fmt}
|
|
function.
|
|
@end quotation
|
|
|
|
@command{alarm}
|
|
@cindex alarm statement
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
alarm @emph{expr-list} ;
|
|
@end quotation
|
|
The expressions are converted to a list of strings, which are then
|
|
logged as a comma-separated list. ``Logging'' means recording the
|
|
values to @file{bro-alarm-file}. In addition, if Bro is reading
|
|
@cindex live traffic
|
|
@cindex traffic, live vs. recorded
|
|
@emph{live} network traffic (as opposed to from a trace file), then
|
|
the messages are also reported via
|
|
@cindex syslog
|
|
@emph{syslog(3)} at level
|
|
@emph{LOG_NOTICE}. If the message does not already
|
|
include a timestamp, one is added.
|
|
|
|
See the @code{alarm} module for a discussion of controlling logging
|
|
behavior from your policy script. In particular, an important feature of
|
|
the @code{alarm} statement is that prior to logging the giving string(s),
|
|
Bro first invokes @command{alarm-hook} to determine whether to suppress
|
|
the logging.
|
|
@end quotation
|
|
|
|
@command{event}
|
|
@cindex event statement
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
event @emph{expr} ( @emph{expr-list*} ) ;
|
|
@end quotation
|
|
Evaluates @emph{expr} to obtain an event handler and queues an event
|
|
for it with the value corresponding to the optional comma-separated
|
|
list of values given by @emph{expr-list}.
|
|
|
|
@emph{Note:} @code{event} statements look syntactically just like function calls, other than the
|
|
keyword ``@code{event}''. However, @command{function-call-expr}, while queueing an event is not, since it does not return a value.
|
|
@end quotation
|
|
|
|
@command{if}
|
|
@cindex if statement
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
if ( @emph{expr} ) @emph{stmt} @*
|
|
if ( @emph{expr} ) @emph{stmt} else @emph{stmt2}
|
|
@end quotation
|
|
Evaluates @emph{expr}, which must yield a @command{bool} value. If true,
|
|
executes @emph{stmt}. For the second form, if false, executes @emph{stmt2}.
|
|
@end quotation
|
|
|
|
@command{for}
|
|
@cindex for statement
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
for ( @emph{var} in @emph{expr} ) @emph{stmt}
|
|
@end quotation
|
|
Iterates over the indices of @emph{expr}, which must evaluate to either
|
|
a @code{set} or a @code{table}. For each iteration, @emph{var} is
|
|
set to one of the indices and @emph{stmt} is executed. @emph{var} needn't
|
|
have been previously declared (in which case its type is implicitly inferred
|
|
from that of the indices of @emph{expr}), and must not be a global variable.
|
|
|
|
If @emph{expr} is a @code{set}, then the indices correspond to the
|
|
members of the set. If @emph{expr} is a @code{table}, then they correspond
|
|
to the indices of the table.
|
|
|
|
@emph{Deficiency: You can only use @code{for} statements to iterate over sets and tables with a single, non-compound index type. You can't iterate over multi-dimensional or compound indices. }
|
|
|
|
@emph{Deficiency: Bro lacks ways of controlling the order in which it iterates over the indices. }
|
|
@end quotation
|
|
|
|
@command{next}
|
|
@cindex next statement
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
next ;
|
|
@end quotation
|
|
Only valid within a @code{for} statement. When executed, causes the
|
|
loop to proceed to the next iteration value (i.e., the next index value).
|
|
@end quotation
|
|
|
|
@command{break}
|
|
@cindex break statement
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
break ;
|
|
@end quotation
|
|
Only valid within a @code{for} statement. When executed, causes the
|
|
loop to immediately exit.
|
|
@end quotation
|
|
|
|
@command{return}
|
|
@cindex return statement
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
return @emph{expr} ;
|
|
@end quotation
|
|
Immediately exits the current function or event handler. For a function,
|
|
returns the value @emph{expr} (which is omitted if the function does
|
|
not return a value, or for event handlers).
|
|
@end quotation
|
|
|
|
@command{add}
|
|
@cindex add statement
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
add @emph{expr1} @emph{expr2} ;
|
|
@end quotation
|
|
Adds the element specified by @emph{expr2} to the
|
|
set given by @emph{expr1}. For example,
|
|
@example
|
|
global active_hosts: set[addr, port];
|
|
...
|
|
add active_hosts[1.44.33.7, 80/tcp];
|
|
@end example
|
|
|
|
adds an element corresponding to the pair
|
|
1.44.33.7 and 80/tcp to the set active_hosts.
|
|
@end quotation
|
|
|
|
@command{delete}
|
|
@cindex delete statement
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
delete @emph{expr1} [@emph{expr2}] ;
|
|
@end quotation
|
|
Deletes the corresponding value, where @emph{expr1} corresponds
|
|
to a set or table, and @emph{expr2} an element/index of the
|
|
set/table. If the element is not in the set/table, does nothing.
|
|
@end quotation
|
|
|
|
@command{compound}
|
|
@cindex compound statement
|
|
@quotation
|
|
Compound statements are formed from a list of (zero or more)
|
|
statements enclosed in
|
|
@code{@{@}}'s:
|
|
@quotation
|
|
@{ @emph{statement*} @}
|
|
@end quotation
|
|
@end quotation
|
|
|
|
@command{null}
|
|
@cindex null statement
|
|
@quotation
|
|
A lone:
|
|
@quotation
|
|
;
|
|
@end quotation
|
|
denotes an empty, do-nothing statement.
|
|
@end quotation
|
|
|
|
@cindex variables, local
|
|
@cindex local variables
|
|
@cindex variables, constant
|
|
@cindex constant variables
|
|
|
|
@command{local,const}
|
|
@cindex local
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
local @emph{var} : @emph{type} = @emph{initialization} @emph{attributes} ; @*
|
|
const @emph{var} : @emph{type} = @emph{initialization} @emph{attributes} ;
|
|
@end quotation
|
|
Declares a local variable with the given type, initialization, and
|
|
attributes, all of which are optional. The syntax of these fields is the
|
|
same as for @command{global-vars}. The
|
|
second form likewise declares a local variable, but one which is
|
|
@emph{constant}: trying to assign a new value to it results in an error.
|
|
@emph{Deficiency:Currently, this @code{const} restriction isn't detected/enforced. }
|
|
|
|
@cindex variables, scope
|
|
|
|
@emph{Unlike with C} the scope of a local variable is from the point of declaration to the end of the encompassing function or event handler.
|
|
@end quotation
|
|
|
|
@cindex statements
|
|
|
|
@node Expressions,
|
|
@section Expressions
|
|
|
|
@cindex expressions|(
|
|
Expressions in Bro are very similar to those in C, with similar precedence:
|
|
|
|
@cindex left parenthesis operator( operator
|
|
@cindex operator, left parenthesis( parenthesis
|
|
@cindex right parenthesis operator) operator
|
|
@cindex operator, right parenthesis) parenthesis
|
|
@cindex parentheses operators()
|
|
|
|
@command{parenthesized}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
( @emph{expr} )
|
|
@end quotation
|
|
Parentheses are used as usual to override precedence.
|
|
@end quotation
|
|
|
|
@command{constant}
|
|
@cindex constant
|
|
@quotation
|
|
Any constant value is an expression.
|
|
@end quotation
|
|
|
|
@command{variable}
|
|
@cindex variable
|
|
@quotation
|
|
The name of a @emph{variable} is an expression.
|
|
@end quotation
|
|
|
|
@command{clone}
|
|
@cindex clone operator
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
copy( @emph{expr} )
|
|
@end quotation
|
|
Produces a clone, or deep copy, of the value produced by the expression
|
|
it is applied to.
|
|
@end quotation
|
|
|
|
@command{increment,decrement}
|
|
@cindex increment
|
|
@cindex decrement
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
++ @emph{expr}
|
|
@*
|
|
-- @emph{expr}
|
|
@end quotation
|
|
Increments or decrements the given expression, which must correspond
|
|
to an assignable value (variable, table element, or record element)
|
|
and of a number type.
|
|
|
|
Yields the value of the expression after the increment.
|
|
|
|
@emph{Unlike with C, these operators only are defined for ``pre''-increment/decrement; there is no post-increment/decrement.}
|
|
@end quotation
|
|
|
|
@command{negation}
|
|
@cindex negation
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
! @emph{expr} @*
|
|
- @emph{expr}
|
|
@end quotation
|
|
Yields the boolean
|
|
or arithmetic negation for values of boolean
|
|
or @emph{numeric} (or @emph{interval}) types, respectively.
|
|
@end quotation
|
|
|
|
@command{positivation}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
+ @emph{expr}
|
|
@end quotation
|
|
Yields the value of @emph{expr}, which must be of type @emph{numeric}
|
|
or @emph{interval}.
|
|
|
|
The point of this operator is to explicitly convert a value of type count
|
|
to int. For example, suppose you want to declare a local variable
|
|
code to be of type int, but initialized to the value 2.
|
|
If you used:
|
|
@example
|
|
local code = 2;
|
|
@end example
|
|
|
|
then Bro's implicit typing would make it of type count, because
|
|
that's the type of a
|
|
@command{numeric-constants}.
|
|
You could instead use:
|
|
@example
|
|
local code = +2;
|
|
@end example
|
|
|
|
to direct the type inferencing to instead assign a type of int
|
|
to code. Or, of course, you could specify the type explicitly:
|
|
@example
|
|
local code:int = 2;
|
|
@end example
|
|
@end quotation
|
|
|
|
@command{arithmetic}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
@emph{expr1} + @emph{expr2} @*
|
|
@emph{expr1} - @emph{expr2} @*
|
|
@emph{expr1} * @emph{expr2} @*
|
|
@emph{expr1} / @emph{expr2} @*
|
|
@emph{expr1} % @emph{expr2}
|
|
@end quotation
|
|
The usual C arithmetic operators,
|
|
defined for numeric types, except
|
|
modulus (@code{%}) is only defined for integral types.
|
|
@end quotation
|
|
|
|
@cindex & short-circuit&&@ short-circuit ``and''
|
|
@cindex short-circuit1-circuit && ``and'' operator
|
|
@cindex and operator&& ``and'' operator
|
|
@cindex operator, and&& ``and''
|
|
@cindex & or short-circuit"|"|@ short-circuit ``or''
|
|
@cindex short-circuit2-circuit "|"| ``or'' operator
|
|
@cindex or operator"|"| ``or'' operator
|
|
@cindex operator, or"|"| ``or''
|
|
|
|
@command{logical}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
@emph{expr1} @code{&&} @emph{expr2} @*
|
|
@emph{expr1} @code{||} @emph{expr2}
|
|
@end quotation
|
|
The usual C logical operators, defined for boolean types.
|
|
@end quotation
|
|
|
|
@cindex == equality operator==@ equality operator
|
|
@cindex == inequality operator", =@ inequality operator
|
|
|
|
@command{equality}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
@emph{expr1} @code{==} @emph{expr2} \
|
|
@emph{expr1} @code{"!=} @emph{expr2}
|
|
@end quotation
|
|
@command{rel-operators},
|
|
Compares two values for equality or inequality, yielding a @code{bool} value. Defined for all non-compound types except pattern.
|
|
@end quotation
|
|
|
|
@cindex == less-than operator<@ @ less-than operator
|
|
@cindex == less-than-or-equal operator<=@ less-or-equal operator
|
|
@cindex == z operator>@ @ greater-than operator
|
|
@cindex == zz operator>=@ greater-or-equal operator
|
|
|
|
@command{relational}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
@emph{expr1} @code{<} @emph{expr2} \
|
|
@emph{expr1} @code{<=} @emph{expr2} \
|
|
@emph{expr1} @code{>} @emph{expr2} \
|
|
@emph{expr1} @code{>=} @emph{expr2}
|
|
@end quotation
|
|
Compares two values for magnitude ordering,
|
|
yielding a bool value. Defined for values of type @emph{numeric},
|
|
time, interval, port, or addr.
|
|
|
|
@emph{Note:} TCP port values are considered less than UDP port values.
|
|
|
|
@emph{Note:} IPv4 addr values less than IPv6 addr values.
|
|
|
|
@emph{Deficiency: Should also be defined at for @command{string} values. }
|
|
@end quotation
|
|
|
|
@command{conditional}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
@emph{expr1} ? @emph{expr2} : @emph{expr3}
|
|
@end quotation
|
|
Evaluates @emph{expr1} and, if true, evaluates and yields
|
|
@emph{expr2}, otherwise evaluates and yields
|
|
@emph{expr3}.
|
|
@emph{expr2} and @emph{expr3} must have compatible
|
|
types.
|
|
@end quotation
|
|
|
|
@command{assignment}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
@emph{expr1} = @emph{expr2}
|
|
@end quotation
|
|
Assigns the value of @emph{expr2} to the storage defined
|
|
by
|
|
@emph{expr1}, which must be an assignable value
|
|
(variable, table element, or record element). Yields the assigned value.
|
|
@end quotation
|
|
|
|
@cindex left parenthesis operator( operator
|
|
@cindex operator, left parenthesis( parenthesis
|
|
@cindex right parenthesis operator) operator
|
|
@cindex operator, right parenthesis) parenthesis
|
|
@cindex parentheses operators()
|
|
|
|
@cindex invocation, function
|
|
@cindex function invocation
|
|
|
|
@command{function call}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
@emph{expr1} ( @emph{expr-list2} )
|
|
@end quotation
|
|
Evaluates @emph{expr1} to obtain a value of type @code{function},
|
|
which is then invoked with its arguments bound left-to-right to the values
|
|
obtained from the comma-separated list of expressions
|
|
@emph{expr-list2}. Each element of @emph{expr-list2}
|
|
must be assignment-compatible with the corresponding formal argument
|
|
in the type of @emph{expr1}. The list may (and must) be empty if the
|
|
function does not take any parameters.
|
|
@end quotation
|
|
|
|
@cindex functions, anonymous
|
|
|
|
@command{anonymous function}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
function ( @emph{parameters} ) @emph{body}
|
|
@end quotation
|
|
Defines an @emph{anonymous function}, which, in abstract terms, is how
|
|
you specify a constant of type @code{function}. @emph{parameters} has
|
|
the syntax of parameter declarations for
|
|
@command{functions}, as does @emph{body},
|
|
which is just a list of statements enclosed in braces.
|
|
|
|
Anonymous functions can be used anywhere you'd usually instead use a
|
|
function declared in the usual direct fashion. For example, consider the
|
|
function:
|
|
@example
|
|
function demo(msg: string): bool
|
|
@{
|
|
if ( msg == "do the demo" )
|
|
@{
|
|
print "got it";
|
|
return T;
|
|
@}
|
|
else
|
|
return F;
|
|
@}
|
|
@end example
|
|
|
|
You could instead declare demo as a global variable of type @code{function}:
|
|
@example
|
|
global demo: function(msg: string): bool;
|
|
@end example
|
|
|
|
and then later assign to it an anonymous function:
|
|
@example
|
|
demo = function (msg: string): bool
|
|
@{
|
|
if ( msg == "do the demo" )
|
|
@{
|
|
print "got it";
|
|
return T;
|
|
@}
|
|
else
|
|
return F;
|
|
@};
|
|
@end example
|
|
|
|
You can even call the anonymous function directly:
|
|
@example
|
|
(function (msg: string): bool
|
|
@{
|
|
if ( msg == "do the demo" )
|
|
@{
|
|
print "got it";
|
|
return T;
|
|
@}
|
|
else
|
|
return F;
|
|
@})("do the demo")
|
|
@end example
|
|
|
|
though to do so you need to enclose the function in parentheses to
|
|
avoid confusing Bro's parser.
|
|
|
|
One particularly handy form of anonymous function is that used
|
|
for @command{&default}.
|
|
@end quotation
|
|
|
|
@cindex timers
|
|
@cindex events, scheduling
|
|
@cindex scheduling events
|
|
|
|
@command{event scheduling}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
schedule @emph{expr1} @code{@{} @emph{expr2} ( @emph{expr-list3} ) @code{@}}
|
|
@end quotation
|
|
Evaluates @emph{expr1} to obtain a value of type @command{interval},
|
|
and schedules the event given by @emph{expr2} with parameters
|
|
@emph{expr-list3} for that time. Note that the expressions are
|
|
all evaluated and bound at the time of execution of the schedule
|
|
expression; evaluation is @emph{not} deferred until the future execution
|
|
of the event handler.
|
|
|
|
For example, we could define the following event handler:
|
|
@example
|
|
event once_in_a_blue_moon(moon_phase: interval)
|
|
@{
|
|
print fmt("wow, a blue moon - phase %s", moon_phase);
|
|
@}
|
|
@end example
|
|
|
|
and then we could schedule delivery of the event for 6 hours from
|
|
the present, with a moon_phase of 12 days, using:
|
|
@example
|
|
schedule +6 hr @{ once_in_a_blue_moon(12 days) @};
|
|
@end example
|
|
|
|
@emph{Note: The syntax is admittedly a bit clunky. In particular, it's easy to @emph{(i)} forget to include the braces (which are needed to avoid confusing Bro's parser), @emph{(ii)} forget the final semi-colon if the schedule expression is being used as an expression-statement, or @emph{(iii)} erroneously place a semi-colon after the event specification but before the closing brace.}
|
|
|
|
@cindex timer expiration
|
|
@cindex expiration, timer
|
|
|
|
Timer invocation is inexact. In general, Bro uses arriving packets to
|
|
serve as its clock (when reading a trace file off-line, this is still the
|
|
case---the timestamp of the latest packet read from the trace is used as
|
|
the notion of ``now''). Once this clock reaches or passes the time
|
|
associated with a queued event, Bro will invoke the event handler,
|
|
which is termed ``expiring'' the timer. (However, Bro will only
|
|
invoke @command{max-timer-expires} timers per packet, and these
|
|
include its own internal timers for managing connection state, so this can
|
|
also delay invocation.)
|
|
|
|
It will also expire all pending timers (whose time has not yet arrived)
|
|
when Bro terminates; if you don't want those event handlers to activate
|
|
in this instance, you need to test @command{done-with-network}.
|
|
|
|
You would think that @code{schedule} should just be a statement like
|
|
@command{event-invocation} is,
|
|
rather than an expression. But it actually does return a value, of the
|
|
undocumented type timer.
|
|
@cindex possible future changes, type
|
|
In the future, Bro may provide mechanisms for manipulating such
|
|
timers; for example, to cancel them if you no longer want them to expire.
|
|
@end quotation
|
|
|
|
@command{index}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
@emph{expr1} [ @emph{expr-list2} ]
|
|
@end quotation
|
|
Returns the sub-value of @emph{expr1} indexed by
|
|
the value of @emph{expr-list2}, which must be compatible with the index
|
|
type of @emph{expr1}.
|
|
|
|
@emph{expr-list2} is a comma-separated list of expressions
|
|
(with at least one expression listed) whose values
|
|
are matched left-to-right against the index types of @emph{expr1}.
|
|
|
|
The only type of value that can be indexed
|
|
in this fashion is a table. @emph{Note:} set's cannot be indexed because they do not yield any value. Use @code{in} to test for set membership.
|
|
@end quotation
|
|
|
|
@command{membership}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
@emph{expr1} in @emph{expr2} @*
|
|
@emph{expr1} !in @emph{expr2}
|
|
@end quotation
|
|
Yields true (false, respectively)
|
|
if the index @emph{expr1} is present in
|
|
the @code{table} or @code{set} @emph{expr2}.
|
|
|
|
For example, if notice_level is a table index by an address
|
|
and yielding a count:
|
|
@example
|
|
global notice_level: table[addr] of count;
|
|
@end example
|
|
|
|
then we could test whether the address 127.0.0.1 is present using:
|
|
@example
|
|
127.0.0.1 in notice_level
|
|
@end example
|
|
|
|
For table's and set's indexed by multiple dimensions,
|
|
you enclose @emph{expr1} in brackets. For example,
|
|
if we have:
|
|
@example
|
|
global connection_seen: set[addr, addr];
|
|
@end example
|
|
|
|
then we could test for the presence of the element indexed by
|
|
8.1.14.2 and 129.186.0.77 using:
|
|
@example
|
|
[8.1.14.2, 129.186.0.77] in connection_seen
|
|
@end example
|
|
|
|
We can also instead use a corresponding record type.
|
|
If we had
|
|
@example
|
|
local t = [$x = 8.1.14.2, $y = 129.186.0.77]
|
|
@end example
|
|
|
|
then we could test:
|
|
@example
|
|
t in connection_seen
|
|
@end example
|
|
@end quotation
|
|
|
|
@cindex == equality operator==@ equality operator
|
|
@cindex == inequality operator", =@ inequality operator
|
|
|
|
@command{pattern matching}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
@emph{expr1} == @emph{expr2} @*
|
|
@emph{expr1} "!= @emph{expr2} @*
|
|
@emph{expr1} in @emph{expr2} @*
|
|
@emph{expr1} "!in @emph{expr2}
|
|
@end quotation
|
|
As discussed for @command{pattern values}.
|
|
the first two forms yield true (false) if
|
|
the @code{pattern} @emph{expr1} exactly matches the string
|
|
@emph{expr2}. (You can also list the @code{string} value
|
|
on the left-hand side of the operator and the @code{pattern} on the right.)
|
|
|
|
The second two forms yield true (false) if
|
|
the pattern @emph{expr1} is present within the string
|
|
@emph{expr2}. (For these, you @emph{must} list the pattern
|
|
as the left-hand operand.)
|
|
@end quotation
|
|
|
|
@cindex $$@ record field access operator
|
|
|
|
@command{record field access}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
@emph{expr} $ @emph{field-name}
|
|
@end quotation
|
|
Returns the given field @emph{field-name} of the record
|
|
@emph{expr}. If the record does not contain the
|
|
given field, a compile-time error results.
|
|
@end quotation
|
|
|
|
@cindex $$@ record constructor operator
|
|
|
|
@command{record constructor}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
[ @emph{field-constructor-list} ]
|
|
@end quotation
|
|
|
|
Constructs a @code{record} value. The @emph{field-constructor-list} is
|
|
a comma-separated list of individual field constructors, which have the syntax:
|
|
@quotation
|
|
$ @emph{field-name} = @emph{expr}
|
|
@end quotation
|
|
|
|
For example,
|
|
@example
|
|
[$foo = 3, $bar = 23/tcp]
|
|
@end example
|
|
|
|
yields a @code{record} with two fields, @code{foo} of type @code{count} and
|
|
@code{bar} of type @code{port}. The values used in the constructor needn't
|
|
be constants, however; they can be any expression of an assignable type.
|
|
@end quotation
|
|
|
|
@cindex ?$?$@ record field test
|
|
|
|
@command{record field test}
|
|
@quotation
|
|
Syntax:
|
|
@quotation
|
|
@emph{expr} @code{?$} @emph{field-name}
|
|
@end quotation
|
|
Returns true if the given field has been set in the record yielded by
|
|
@emph{expr}. Note that @emph{field-name} @emph{must} correspond to
|
|
one of the fields in the record type of @emph{expr} (otherwise, the
|
|
expression would always be false). The point of this operator is
|
|
to test whether an @emph{&optional} field of a record has been
|
|
assigned to.
|
|
|
|
For example, suppose we have:
|
|
@example
|
|
type rap_sheet: record @{
|
|
num_scans: count &optional;
|
|
first_activity: time;
|
|
@};
|
|
global the_goods: table[addr] of rap_sheet;
|
|
@end example
|
|
|
|
and we want to test whether the address held in the variable perp
|
|
exists in the_goods and, if so, whether num_scans has been
|
|
assigned to, then we could use:
|
|
@example
|
|
perp in the_goods && the_goods[perp]?$num_scans
|
|
@end example
|
|
@end quotation
|
|
|
|
@cindex expressions
|
|
|