ref: 3866717cbb020199d58171c1c0cdd7382a74ee82
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); }