6.6 KB


Rumble is Brawler's own event-driven language. Designed specifically to define rules (called sinks) and inject events to trigger the defined sinks. Rumble is an interpreted language with a dynamic type system.


Source code is Unicode text encoded in UTF-8. Single language statements are separated by a semicolon or a newline.

Constant values are usually enclosed in double quotes "" or single quotes '', both supporting escape sequences. Constant values can also be provided as raw strings prefixing a single or double quote with an 'r'. A raw string can contain any character including newlines and does not contain escape sequences.

Blocks are denoted with curley brackets. Most language constructs (conditions, loops, etc.) are very similar to other languages. All function calls start with an '@' sign. Comments start with a '#' sign and comment out the rest of the line.


Identifiers name program entities such as variables and sink names. An identifier is a sequence of one or more letters and digits.


The following Keywords are reserved and cannot be used as Identifiers:

Sink Definition

Boolean operators


Condition operators

	like        : String matches regex
	in          : List contains value
	beginswith  : String beginns with 
	endswith    : String ends with
	notin       : List does not contain

Constant terminals


Conditional statements


Loop statements


Operators and punctuation

The following symbols are operators and punctuation:

Condition operators

	>=    : Greater or equals
	<=    : Greater or equals
	!=    : Not equal
	==    : Equal
	>     : Greater than
	<     : Less than

Grouping symbols


Sequence symbols


Arithmetic operators

	+     : Arithmetic plus
	-     : Arithmetic minus
	*     : Arithmetic times
	/     : Arithmetic divide
	//    : Arithmetic integer divide
	%     : Arithmetic modulus

Assignment statement


Function call statement


Data structure access


Variable Assignments

A variable is a storage location for holding a value. Variables can hold single values (strings and numbers) or structures like an array or a map.

A variable is assinged with the assign operator ':='

a := 1
b := "test"
c := [1,2,3]
d := {1:2,3:4}

Multi-assignments are possible using lists:

[a, b] := [1, 2]


Variables and constants can be combined with operators to form expressions.

5 >= a or 6 < b

Variable contents are interpreted depending on the situation so the following will evaluate to true:

a := "2"
3 == 1 + a

Boolean expressions can also be formed with variables:

a := 1 == 1
b := false or a

Accessing structures

Structures can be access via a '.' and a constant or a '$' and an expression.

An array value is accessed by a number indice starting with index 0. A map value is accessed by its key.

The following examples return always the number 2:

c := [1,2,3]

d := {1:2,3:4}

x := {1:[1,2,3],3:4}

Built-in functions

Several build in functions are available by default:

@log(level, message) Write a log message.


@log("Info", "Value of a is: ", a)

@len(list) Return the size of a list.



@range(start, end, step) Iterator function for loops.


for a in @range(2, 10, 1) {
	@log("Info", "->", a)

@rootevent(name, kind, state, finish_event) Start a new event cascade. Can optionally have a 4th parameter which specifies a finish event which is injected after the event cascade has finished. The event has the state of the root event.


@rootevent("Start Event", "core.start", {
	"arg1" : "some value"

@event(name, kind, state, priority) Inject an event into an existing event cascade. This function can only be called if an active monitor is available (i.e. in the code block of a sink definition). The 4th parameter is optional.


@event("Another Event", "", {
	"arg1" : "some value"

Loop statements

All loops in Rumble are done with the 'for' block statement. Counting loops are done with the 'range' function. The following code iterates from 2 until 10 in steps of 2:

for a in @range(2, 10, 2) {
	@log("Info", "->", a)

Conditional loops are done using conditions after the for statement:

a := 10
for a > 0 {
	@log("Info", "->", a)
	a := a - 1

It is possible to loop over lists and even have multiple assignments:

for [a,b] in [[1,1], [2,2], [3,3]] {
	@log("Info", " ", a, " == ", b)


x := { "c":0, "a":2, "b":4}
for [a, b] in x {
	@log("Info", "->", a, "-", b)

Conditional statements

The "if" statement specifies the conditional execution of multiple branches:

if a == 1 {
    a := a + 1	
} elif a == 2 {
	a := a + 2
} else {
    a := 99


Event sinks trigger on events. An event is either injected via 'event' and 'rootevent' or via external sources (e.g. a datastore).

The code inside the sink has access to some default objects:

	event     : The event which triggered the sink
	monitor   : The current monitor of the event
	processor : The current processor which executes the code

Events have the following structure:

    name  : A name which identifies the event
    kind  : The event kind
    state : A map containing additional information

A sink definition must contain the following information:

  • [name] A name which identifies the sink.
  • [kindmatch] Match on event kinds: A list of strings in dot notation which describes event kinds. May contain '*' characters as wildcards.
  • [scopematch] Match on event cascade scope: A list of strings in dot notation which describe the required scopes which are required for this rule to trigger. The included / excluded scopes for an event are stored in its monitor.
  • [statematch] Match on event state: A simple list of required key / value states in the event state. Nil values can be used as wildcards (i.e. match is only on key).
  • [priority] Sinks are sorted by their priority (0 being the highest) before their code is executed.
  • [suppresses] A list of sinks (identified by their name) which should be suppressed if this sink executes.


sink "rule1" 
    Some description.
    kindmatch [ "core.*" ],
	scopematch [ "analysis.simple", "analysis.deep" ],
	statematch { "val" : NULL },
	priority 10,
	suppresses [ "rule2" ]
    # Some code