This is an example of using the low-level interface for implementing a domain-specific language that maps to protobufs.
In this example we demonstrate managing a recursive structure like
XML. The structure shown in xml_proto/1
below, is similar to the structure returned by load_xml_file/2,
which is part of the SGML library. We supply three message_sequence
decorators: kv_pair
, xml_element
, and aux_xml_element
.
These are treated as first class host types.
:- multifile protobufs:message_sequence//3. protobufs:message_sequence(Type, Tag, Value) --> { my_message_sequence(Type, Value, Proto) }, protobufs:message_sequence(embedded, Tag, Proto), !. % % On encode, the value type determines the tag. And on decode % the tag to determines the value type. % guard(Type, Value) :- ( nonvar(Value) -> is_of_type(Type, Value); true ). my_message_sequence(kv_pair, Key=Value, Proto) :- Proto = protobuf([atom(30, Key), X]), ( ( guard(integer, Value), X = integer(31, Value) ) ; ( guard(float, Value), X = double(32, Value) ) ; ( guard(atom, Value), X = atom(33, Value)) ). my_message_sequence(xml_element, element(Name, Attributes, Contents), Proto) :- Proto = protobuf([ atom(21, Name), repeated(22, kv_pair(Attributes)), repeated(23, aux_xml_element(Contents))]). my_message_sequence(aux_xml_element, Contents, Proto) :- Contents = element(_Name, _Attributes, _ElementContents), Proto = protobuf([xml_element(40, Contents)]). my_message_sequence(aux_xml_element, Contents, Proto) :- Proto = protobuf([atom(43, Contents)]). xml_proto([element(space1, [foo='1', bar='2'], [fum, bar, element(space2, [fum=3.1415, bum= -14], ['more stuff for you']), element(space2b, [], [this, is, embedded, also]), to, you])]). test_xml(X, Y) :- Proto = protobuf([repeated(20, xml_element(X))]), protobuf_message(Proto, Y). % And test it: ?- xml_proto(X), test_xml(X,Y), test_xml(Z,Y), Z == X. X = Z, Z = [element(space1, [foo='1', bar='2'], [fum, bar, element(space2, [fum=3.1415, bum= -14], ['more stuff for you'] ), element(space2b, [], [this, is|...] ), to, you])], Y = [162, 1, 193, 1, 170, 1, 6, 115, 112|...],
A protobuf description that is compatible with the above wire stream follows:
message kv_pair { required string key = 30; optional sint64 int_value = 31; optional double float_value = 32; optional string atom_value = 33; } message aux_xml_element { optional string atom = 43; optional xml_element element = 40; } message xml_element { required string name = 21; repeated kv_pair attributes = 22; repeated aux_xml_element contents = 23; } message XMLFile { repeated xml_element elements = 20; }
Verify the wire stream using the protobuf compiler's decoder:
$ protoc --decode=XMLFile pb_vector.proto <tmp98.tmp elements { name: "space1" attributes { key: "foo" atom_value: "1" } attributes { key: "bar" atom_value: "2" } contents { atom: "fum" } contents { atom: "bar" } contents { element { name: "space2" attributes { key: "fum" float_value: 3.1415 } attributes { key: "bum" int_value: -14 } contents { atom: "more stuff for you" } } } contents { element { name: "space2b" contents { atom: "this" } contents { atom: "is" } contents { atom: "embedded" } contents { atom: "also" } } } contents { atom: "to" } contents { atom: "you" } }