diff options
Diffstat (limited to 'appl/lib/format.b')
| -rw-r--r-- | appl/lib/format.b | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/appl/lib/format.b b/appl/lib/format.b new file mode 100644 index 00000000..5bd5d918 --- /dev/null +++ b/appl/lib/format.b @@ -0,0 +1,147 @@ +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); +} |
