1.6 Alternation, Aggregation, Encapsulation, and Enumeration
1.6.1 Alternation
The protobuf grammar provides a reserved word, optional
,
that indicates that the production rule that it refers to may appear
once or not at all in a protobuf message. Since Prolog has its own means
of alternation, this reserved word is not supported on the Prolog side.
It is anticipated that customary Prolog mechanisms for nondeterminism
(e.g. backtracking) will be used to generate and test alternatives.
1.6.2 Aggregation
It is possible to specify homogeneous vectors of things (e.g. lists
of numbers) using the repeated
attribute. You specify a
repeated field as follows:
repeated(22, float([1,2,3,4])), repeated(23, enum(tank_state([empty, half_full, full]))).
The first clause above, will cause all four items in the list to be encoded in the wire-stream as IEEE-754 32-bit floating point numbers, all with tag 22. The decoder will aggregate all items in the wire-stream with tag 22 into a list as above. Likewise, the all items listed in the second clause will be encoded in the wire-stream according to the mapping defined in an enumeration (described below) tank_state/2, each with tag 23.
Notes:
Beware that there is no explicit means to encode an empty set. The
protobuf specification provides that a repeated
field may
match a tag zero or more times. The empty set, while legal, produces no
output on encode. While decoding a repeated
term, failure
to match the specified tag will yield an empty set of the specified host
type.
The protobuf grammar provides a variant of the repeated
field known as "packed." Packed, repeated fields are currently not
supported by our interpreter.
1.6.3 Encapsulation and Enumeration
It is possible to embed one protocol buffer specification inside
another using the embedded
term. The following example
shows a vector of numbers being placed in an envelope that contains a
command enumeration.
Enumerations are a compact method of sending tokens from one system
to another. Most occupy only two bytes in the wire-stream. An
enumeration requires that you specify a callable predicate like commands/2,
below. The first argument is an atom specifying the name of token, and
the second is an non-negative integer that specifies the token's value.
These must of course, match a corresponding enumeration in the
.proto
file.
Note: You must expose this predicate to the protobufs module by assigning it explicitly.
protobufs:commands(Key, Value) :- commands(Key, Value). commands(square, 1). commands(decimate, 2). commands(transform, 3). commands(inverse_transform, 4). basic_vector(Type, Proto) :- vector_type(Type, Tag), Proto = protobuf([ repeated(Tag, Type) ]). send_command(Command, Vector, Msg) :- basic_vector(Vector, Proto1), Proto = protobuf([enum(1, commands(Command)), embedded(2, Proto1)]), protobuf_message(Proto, Msg).
Use it as follows:
?- send_command(square, double([1,22,3,4]), Msg). Msg = [8, 1, 18, 36, 17, 0, 0, 0, 0, 0, 0, 240, 63, 17, 0, 0, 0, 0, 0, 0, 54, 64, 17, 0, 0, 0, 0, 0, 0, 8, 64, 17, 0, 0, 0, 0, 0, 0, 16, 64]. ?- send_command(Cmd, V, $Msg). Cmd = square, V = double([1.0, 22.0, 3.0, 4.0]) .
Compatibility Note: The protobuf grammar (protobuf-2.1.0) permits enumerations to assume negative values. This requires them to be encoded as integers. But Google's own Golden Message unit-test framework has enumerations encoded as unsigned. Consequently, parsers that encode them as integers cannot properly parse the Golden Message. So it's probably a good idea to avoid negative values in enumerations. Our parser forbids it anyway.
1.6.4 Heterogeneous Collections
Using Protocol Buffers, it is quite an easy matter to specify fixed data structures and homogeneous vectors like one might find in languages like C++ and Java. It is however, quite another matter to interwork with these languages when requirements call for working with compound structures, arrays of compound structures, or unstructured collections (e.g. bags) of data.
At bottom, a wire-stream is nothing more than a concatenated stream of primitive wire type strings. As long as you can associate a tag with its host type in advance, you will have no difficulty in decoding the message. You do this by supplying the structure. Tell the parser what is possible and let the parser figure it out on its own, one production at a time. An example may be found in the appendix.