A record consists of a set of fields , each represented by one element in an sexprs (2) list. The content of a field can be a simple value, or it can hold a list containing any number of sub-records, each holding a set of fields, recursively defined as above.
The Fmtspec type defines the format for a field in a record. Name gives the name of the field, and fields gives the structure of the fields in any sub-records it contains (for a field with a simple value, this will be nil). Thus an array of Fmtspec values can define the structure of all the fields in a record. Here is an example structure specification:
.EX Name, Address, Phone: con iota; Number, Kind: con iota; spec := array[] of { Address => Fmtspec("address", nil), Name => Fmtspec("name", nil), Phone => Fmtspec("phone", array[] of { Kind => Fmtspec("kind", nil), Number => Fmtspec("number", nil), }), };By placing each field in the structure specification at a known index, a link is made from the symbolic constants in the program to the textual field names.
A structure specification may also be represented by a list of S-expressions, where each member of the list names a field in the structure. If a member is itself a list, it specifies a field containing sub-records: its first member gives the name of the field, and subsequent members specify the fields in its sub-records. For example, the above specification could be written as the S-expression:
.EX (name address (phone number kind))The Fmt type also defines a record structure, but "with respect" to an existing Fmtspec structure specification. An Fmt value represents a field, and kind holds the index of that field in the original structure specification.
Se2fmt converts from an S-expression list, se (a structure specification), to a set of Fmt values. The specification must be a subset of spec ; i.e. each field in se must exist in spec . Se2fmt returns a tuple ( "f, err" ) . If the specification is bad, f will be nil, and err describes the error. Otherwise, each member of f gives a field specified by se . For example, given the above structure definition, after executing:
.EX se := Sexp.parse("(address (phone number))").t0; (f, err) := se2fmt(spec, se);f [0].kind will hold the symbolic constant Address , and f [1].fields[0].kind will hold Number .
Spec2se converts from a structure representation to its S-expression equivalent. Spec2fmt converts it to an array of Fmt structures mirroring it (equivalent to, but more efficient than, se2fmt(spec2se( spec )).t0)
For example, a record corresponding to the structure specification
.EX (name address (phone kind number))might look like:
.EX ("Jonny Dawes" "24 Crag Lane" ((home "+44 7924 43535") (office "034 433 645")))Rec2val cracks such a record, rec , into its component values, checking that it is structurally compatible with the specification, spec , and returning a tuple ( "fields, err" ) . If it failed, fields will be nil, and err describes the error. Otherwise each member of fields , say v , holds the value of its equivalent field in spec . For fields without sub-records, v .val holds the field's value; otherwise v .recs holds an array with one member for each sub-record, each holding an array of fields defined recursively as above.
Some file servers provide files to which a format specification may be written, and which provide a sequence of records in that format when read. The Fmtfile type provides support for such files. It provides the following operations:
Fmtfile.new( spec ) returns a new Fmtfile value that can be used to open files and read records conforming to the given spec .
f .open(name) opens such a file, writes the format specification to it, and returns an Iobuf (see bufio (2)) ready for reading the file.
f .read reads a record from the file; its return is the same as that from rec2val .