Skip to content

Commit

Permalink
Document continue, break, and return syntax.
Browse files Browse the repository at this point in the history
Added a brief description to the tutorial and language reference.
  • Loading branch information
ryzhyk committed Feb 6, 2020
1 parent 8683016 commit 297a9bb
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 9 deletions.
4 changes: 4 additions & 0 deletions doc/language_reference/language_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,9 @@ term ::= "_" (* wildcard *)
| match_term (* match term *)
| ite_term (* if-then-else term *)
| for_term (* for-loop *)
| "continue" (* abort current loop iteration *)
| "break" (* break out of a loop *)
| return_term (* return from a function *)
| vardecl_term (* local variable declaration *)
```

Expand Down Expand Up @@ -398,6 +401,7 @@ apply_term ::= func_name "(" [expr (,expr)*] ")"
var_term ::= var_name
ite_term ::= "if" term term [ "else" term ]
for_term ::= "for" "(" var_name "in" expr ")" term
return_term ::= "return" [expr]
vardecl_term ::= "var" var_name
match_term ::= "match" "(" expr ")" "{" match_clause (,match_clause)*"}"
Expand Down
49 changes: 42 additions & 7 deletions doc/tutorial/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,12 @@ Evaluation order can be controlled using several constructs:

1. A for loop

1. `continue` terminates current loop iteration

1. `break` terminates a loop

1. `return [expr]` returns from a function

The following example illustrates the first four of these constructs. Loops are explained [below](#container-types-flatmap-and-for-loops).

```
Expand All @@ -621,20 +627,20 @@ function addr_port(ip: ip_addr_t, proto: string, preferred_port: bit<16>): strin
if (preferred_port != 0)
preferred_port // return preferred_port if specified
else
16'd80 // assume HTTP otherwise
return "${ip}:80" // assume HTTP otherwise
}
}; // semicolon required for sequencing
// Return the address:port string
"${ip}:${port}"
}
```

The result computed by a function is the result of the last expression
evaluated. There is no `return` statement. If the `else` is
missing the value `()` (empty tuple) is used for the `else` branch.
In `match` expressions the patterns must cover
all possible cases (for instance, the match expression above would not
be correct without the last "catch-all" (`_`) case).
The result computed by a function is the value of the last expression evaluated
or the value produced by the first `return` statement encountered when
evaluating the function. If the `else` is missing the value `()` (empty tuple)
is used for the `else` branch. In `match` expressions the patterns must cover
all possible cases (for instance, the match expression above would not be
correct without the last "catch-all" (`_`) case).

DDlog variables must always be initialized when declared. In this
example, the `port` variable is assigned the result of the `match`
Expand Down Expand Up @@ -841,6 +847,35 @@ Loops can only iterate over container types: sets, maps, and vectors. When iter
and vectors, the loop variable (`s` in the above example) has the same type as elements of the container
(e.g., `string`). When iterating over maps, the loop variable is a 2-tuple `(key,value)`.

A `continue` statement used anywhere inside the body of a loop terminates the
current loop iteration:

```
// Returns only even elements of the vector.
function evens(vec: Vec<bigint>): Vec<bigint> = {
var res: Vec<bigint> = vec_empty();
for (x in vec) {
if (x % 2 != 0) { continue };
vec_push(res, x)
};
res
}
```

A `break` statement used anywhere inside the body of a loop terminates the loop:

```
// Returns prefix of `vec` before the first occurrence of value `v`.
function prefixBefore(vec: Vec<'A>, v: 'A): Vec<'A> = {
var res: Vec<'A> = vec_empty();
for (x in vec) {
if (x == v) { break };
vec_push(res, x)
};
res
}
```

#### Rules with multiple heads

The following program computes the sums and products of pairs of values from `X`:
Expand Down
34 changes: 33 additions & 1 deletion test/datalog_tests/tutorial.ast.expected
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ typedef Bytes = Bytes{b3: bit<8>, b2: bit<8>, b1: bit<8>, b0: bit<8>}
typedef Category = CategoryStarWars{} | CategoryOther{}
typedef Endpoint = Endpoint{ip: ip_addr_t, proto: string, preferred_port: bit<16>}
typedef EndpointString = EndpointString{s: string}
typedef Evens = Evens{evens_and_odds: std.Vec<bigint>, evens: std.Vec<bigint>}
typedef EvensAndOdds = EvensAndOdds{vec: std.Vec<bigint>}
typedef First5 = First5{str: string}
typedef Flow = Flow{lr: bigint, stage: stage, prio: bigint, matchStr: string, actionStr: string}
typedef Flow1 = Flow1{lr: bigint, stage: stage, prio: bigint, matchStr: string, actionStr: string}
Expand All @@ -30,6 +32,7 @@ typedef Packet = Packet{pkt: eth_pkt_t}
typedef Person = Person{name: string, nationality: string, occupation: string}
typedef Phrases = Phrases{phrase: string}
typedef Pow2 = Pow2{p: string}
typedef Prefix = Prefix{vec: std.Vec<string>}
typedef Price = Price{item: string, vendor: string, price: bit<64>}
typedef Product = Product{x: bit<16>, y: bit<16>, prod: bit<16>}
typedef SanitizedEndpoint = SanitizedEndpoint{ep: string}
Expand All @@ -43,6 +46,7 @@ typedef TopScore = TopScore{school: string, top_score: bit<16>}
typedef UDPDstPort = UDPDstPort{port: bit<16>}
typedef UDPDstPort2 = UDPDstPort2{port: bit<16>}
typedef UUID = bit<128>
typedef Vector = Vector{vec: std.Vec<string>, sep: string}
typedef Word1 = Word1{word: string, cat: Category}
typedef Word2 = Word2{word: string, cat: Category}
typedef WorstPrice = WorstPrice{item: string, price: bit<64>}
Expand Down Expand Up @@ -79,7 +83,7 @@ function addr_port (ip: ip_addr_t, proto: string, preferred_port: bit<16>): stri
_ -> if (preferred_port != 16'd0) {
preferred_port
} else {
16'd80
return (("" ++ ip_addr_t2string(ip)) ++ ":80")
}
};
((("" ++ ip_addr_t2string(ip)) ++ ":") ++ std.__builtin_2string(port)))
Expand All @@ -97,6 +101,17 @@ function best_vendor (g: std.Group<(string, bit<64>)>): (string, bit<64>) =
}
};
(min_vendor, min_price))))
function evens (vec: std.Vec<bigint>): std.Vec<bigint> =
((var res: std.Vec<bigint>) = std.vec_empty();
(for (x in vec) {
(if ((x % 2) != 0) {
continue
} else {
()
};
std.vec_push(res, x))
};
res))
function ip_addr_t2string (ip: ip_addr_t): string =
((((((("" ++ std.__builtin_2string(ip.addr[31:24])) ++ ".") ++ std.__builtin_2string(ip.addr[23:16])) ++ ".") ++ std.__builtin_2string(ip.addr[15:8])) ++ ".") ++ std.__builtin_2string(ip.addr[7:0]))
function ip_from_bytes (b3: bit<8>, b2: bit<8>, b1: bit<8>, b0: bit<8>): ip_addr_t =
Expand Down Expand Up @@ -132,6 +147,17 @@ function pkt_udp_port2 (pkt: eth_pkt_t): std.Option<bit<16>> =
EthPacket{.src=_, .dst=_, .payload=EthIP6{.ip6=IP6Pkt{.ttl=_, .src=_, .dst=_, .payload=IPUDP{.udp=UDPPkt{.src=_, .dst=var port, .len=_}}}}} -> std.Some{.x=port},
_ -> std.None{}
}
function prefixBefore (vec: std.Vec<'A>, v: 'A): std.Vec<'A> =
((var res: std.Vec<'A>) = std.vec_empty();
(for (x in vec) {
(if (x == v) {
break
} else {
()
};
std.vec_push(res, x))
};
res))
extern function split (s: string, sep: string): std.Vec<string>
function split_ip_list (x: string): std.Vec<string> =
split(x, " ")
Expand Down Expand Up @@ -264,6 +290,8 @@ input relation Blacklisted [Blacklisted]
input relation Bytes [Bytes]
input relation Endpoint [Endpoint]
output relation EndpointString [EndpointString]
output relation Evens [Evens]
input relation EvensAndOdds [EvensAndOdds]
output relation First5 [First5]
output relation Flow [Flow]
output relation Flow1 [Flow1]
Expand All @@ -286,6 +314,7 @@ input relation Packet [Packet]
input relation Person [Person]
output relation Phrases [Phrases]
output relation Pow2 [Pow2]
output relation Prefix [Prefix]
input relation Price [Price]
output relation Product [Product]
output relation SanitizedEndpoint [SanitizedEndpoint]
Expand All @@ -299,6 +328,7 @@ output relation TargetAudience [Person]
output relation TopScore [TopScore]
output relation UDPDstPort [UDPDstPort]
output relation UDPDstPort2 [UDPDstPort2]
input relation Vector [Vector]
input relation Word1 [Word1]
input relation Word2 [Word2]
output relation WorstPrice [WorstPrice]
Expand All @@ -316,6 +346,8 @@ First5(.str=string_slice_unsafe(p, 64'd0, 64'd5)) :- Phrases(.phrase=p).
SanitizedEndpoint(.ep=endpoint) :- Endpoint(.ip=ip, .proto=proto, .preferred_port=preferred_port), var endpoint = addr_port(ip, proto, preferred_port), not Blacklisted(.ep=endpoint).
HostIP(.host=host, .addr=addr) :- HostAddress(.host=host, .addrs=addrs), var addr = FlatMap(split_ip_list(addrs)).
HostIPVSep(.host=host, .addrs=vaddrs) :- HostAddress(.host=host, .addrs=addrs), var vaddrs = vsep(split_ip_list(addrs)).
Evens(.evens_and_odds=vec, .evens=evens(vec)) :- EvensAndOdds(.vec=vec).
Prefix(.vec=prefixBefore(vec, sep)) :- Vector(.vec=vec, .sep=sep).
Sum(.x=x, .y=y, .sum=(x + y)),
Product(.x=x, .y=y, .prod=(x * y)) :- X(.x=x), X(.x=y).
BestPrice(.item=item, .price=best_price) :- Price(.item=item, .vendor=_, .price=price), var best_price = Aggregate((item), std.group_min(price)).
Expand Down
16 changes: 16 additions & 0 deletions test/datalog_tests/tutorial.dat
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,22 @@ dump HostIP;
echo HostIPVSep:;
dump HostIPVSep;

start;
insert EvensAndOdds([0,1,2,3,4,5]),
insert EvensAndOdds([1,3,5,7]),
commit;

echo Evens:;
dump Evens;

start;
insert Vector(["a", "--", "b"], "--"),
insert Vector(["--", "a", "b"], "--"),
insert Vector(["a", "b", "--"], "--"),
commit;

echo Prefix:;
dump Prefix;

start;

Expand Down
36 changes: 35 additions & 1 deletion test/datalog_tests/tutorial.dl
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ function addr_port(ip: ip_addr_t,
if (preferred_port != 0)
preferred_port
else
16'd80 // assume HTTP
return "${ip}:80" // assume HTTP
}
};
"${ip}:${port}"
Expand Down Expand Up @@ -196,6 +196,40 @@ output relation HostIPVSep(host: bit<64>, addrs: string)
HostIPVSep(host, vaddrs) :- HostAddress(host, addrs),
var vaddrs = vsep(split_ip_list(addrs)).

/*
* Example: `continue` and `break` statements.
*/

// Returns only even elements of the vector.
function evens(vec: Vec<bigint>): Vec<bigint> = {
var res: Vec<bigint> = vec_empty();
for (x in vec) {
if (x % 2 != 0) { continue };
vec_push(res, x)
};
res
}

input relation EvensAndOdds(vec: Vec<bigint>)
output relation Evens(evens_and_odds: Vec<bigint>, evens: Vec<bigint>)

Evens(vec, evens(vec)) :- EvensAndOdds(vec).

// Returns prefix of `vec` before the first occurrence of value `v`.
function prefixBefore(vec: Vec<'A>, v: 'A): Vec<'A> = {
var res: Vec<'A> = vec_empty();
for (x in vec) {
if (x == v) { break };
vec_push(res, x)
};
res
}

input relation Vector(vec: Vec<string>, sep: string)
output relation Prefix(vec: Vec<string>)

Prefix(prefixBefore(vec, sep)) :- Vector(vec, sep).

/*
* Example: Multiple heads
*/
Expand Down
7 changes: 7 additions & 0 deletions test/datalog_tests/tutorial.dump.expected
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ HostIPVSep:
HostIPVSep{1,"10.10.10.101\n10.10.10.102\n"}
HostIPVSep{2,"192.168.0.1\n"}
HostIPVSep{3,"192.168.0.3\n192.168.0.4\n192.168.0.5\n192.168.0.6\n"}
Evens:
Evens{[0,1,2,3,4,5],[0,2,4]}
Evens{[1,3,5,7],[]}
Prefix:
Prefix{[]}
Prefix{["a"]}
Prefix{["a","b"]}
Sum:
Sum{10,10,20}
Sum{10,20,30}
Expand Down

0 comments on commit 297a9bb

Please sign in to comment.