ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/lib/format.b/
implement Format;
include "sys.m";
sys: Sys;
include "bufio.m";
bufio: Bufio;
Iobuf: import bufio;
include "sexprs.m";
sexprs: Sexprs;
Sexp: import sexprs;
include "format.m";
# possible addition?
# se2spec(se: list of ref Sexp): (array of Fmtspec, string)
init()
{
sys = load Sys Sys->PATH;
sexprs = load Sexprs Sexprs->PATH;
sexprs->init();
bufio = load Bufio Bufio->PATH;
}
spec2se(spec: array of Fmtspec): list of ref Sexp
{
l: list of ref Sexp;
for(i := len spec - 1; i >= 0; i--){
if((sp := spec[i]).fields != nil)
l = ref Sexp.List(ref Sexp.String(sp.name, nil) :: spec2se(sp.fields)) :: l;
else if(sp.name != nil)
l = ref Sexp.String(sp.name, nil) :: l;
}
return l;
}
spec2fmt(specs: array of Fmtspec): array of Fmt
{
if(specs == nil)
return nil;
f := array[len specs] of Fmt;
for(i := 0; i < len specs; i++){
if(specs[i].name == nil)
f[i].kind = -1;
else
f[i] = (i, spec2fmt(specs[i].fields));
}
return f;
}
se2fmt(spec: array of Fmtspec, se: ref Sexp): (array of Fmt, string)
{
if(!se.islist())
return (nil, "format must be a list");
return ses2fmt(spec, se.els());
}
ses2fmt(spec: array of Fmtspec, els: list of ref Sexp): (array of Fmt, string)
{
a := array[len els] of Fmt;
for(i := 0; els != nil; els = tl els){
name := (hd els).op();
for(j := 0; j < len spec; j++)
if(spec[j].name == name)
break;
if(j == len spec)
return (nil, sys->sprint("format name %#q not found", name));
sp := spec[j];
if((hd els).islist() == 0)
a[i++] = Fmt(j, spec2fmt(sp.fields));
else if(sp.fields == nil)
return (nil, sys->sprint("unexpected list %#q", name));
else{
(f, err) := ses2fmt(sp.fields, (hd els).args());
if(f == nil)
return (nil, err);
a[i++] = Fmt(j, f);
}
}
return (a, nil);
}
rec2val(spec: array of Fmtspec, se: ref Sexprs->Sexp): (array of Fmtval, string)
{
if(se.islist() == 0)
return (nil, "expected list of fields; got "+se.text());
els := se.els();
if(len els > len spec)
return (nil, sys->sprint("too many fields found, expected %d, got %s", len spec, se.text()));
a := array[len spec] of Fmtval;
err: string;
for(i := 0; i < len spec; i++){
f := spec[i];
if(f.name == nil)
continue;
if(els == nil)
return (nil, sys->sprint("too few fields found, expected %d, got %s", len spec, se.text()));
el := hd els;
if(f.fields == nil)
a[i].val = el;
else{
if(el.islist() == 0)
return (nil, "expected list of elements; got "+el.text());
vl := el.els();
a[i].recs = recs := array[len vl] of array of Fmtval;
for(j := 0; vl != nil; vl = tl vl){
(recs[j++], err) = rec2val(spec[i].fields, hd vl);
if(err != nil)
return (nil, err);
}
}
els = tl els;
}
return (a, nil);
}
Fmtval.text(v: self Fmtval): string
{
return v.val.astext();
}
Fmtfile.new(spec: array of Fmtspec): Fmtfile
{
return (spec, (ref Sexp.List(spec2se(spec))).pack());
}
Fmtfile.open(f: self Fmtfile, name: string): ref Bufio->Iobuf
{
fd := sys->open(name, Sys->ORDWR);
if(fd == nil){
sys->werrstr(sys->sprint("open failed: %r"));
return nil;
}
if(sys->write(fd, f.descr, len f.descr) == -1){
sys->werrstr(sys->sprint("format write failed: %r"));
return nil;
}
sys->seek(fd, big 0, Sys->SEEKSTART);
return bufio->fopen(fd, Sys->OREAD);
}
Fmtfile.read(f: self Fmtfile, iob: ref Iobuf): (array of Fmtval, string)
{
(se, err) := Sexp.read(iob);
if(se == nil)
return (nil, err);
return rec2val(f.spec, se);
}