xref: /inferno-os/appl/lib/format.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Format;
2include "sys.m";
3	sys: Sys;
4include "bufio.m";
5	bufio: Bufio;
6	Iobuf: import bufio;
7include "sexprs.m";
8	sexprs: Sexprs;
9	Sexp: import sexprs;
10include "format.m";
11
12# possible addition?
13# se2spec(se: list of ref Sexp): (array of Fmtspec, string)
14
15init()
16{
17	sys = load Sys Sys->PATH;
18	sexprs = load Sexprs Sexprs->PATH;
19	sexprs->init();
20	bufio = load Bufio Bufio->PATH;
21}
22
23spec2se(spec: array of Fmtspec): list of ref Sexp
24{
25	l: list of ref Sexp;
26	for(i := len spec - 1; i >= 0; i--){
27		if((sp := spec[i]).fields != nil)
28			l = ref Sexp.List(ref Sexp.String(sp.name, nil) :: spec2se(sp.fields)) :: l;
29		else if(sp.name != nil)
30			l = ref Sexp.String(sp.name, nil) :: l;
31	}
32	return l;
33}
34
35spec2fmt(specs: array of Fmtspec): array of Fmt
36{
37	if(specs == nil)
38		return nil;
39	f := array[len specs] of Fmt;
40	for(i := 0; i < len specs; i++){
41		if(specs[i].name == nil)
42			f[i].kind = -1;
43		else
44			f[i] = (i, spec2fmt(specs[i].fields));
45	}
46	return f;
47}
48
49
50se2fmt(spec: array of Fmtspec, se: ref Sexp): (array of Fmt, string)
51{
52	if(!se.islist())
53		return (nil, "format must be a list");
54	return ses2fmt(spec, se.els());
55}
56
57ses2fmt(spec: array of Fmtspec, els: list of ref Sexp): (array of Fmt, string)
58{
59	a := array[len els] of Fmt;
60	for(i := 0; els != nil; els = tl els){
61		name := (hd els).op();
62		for(j := 0; j < len spec; j++)
63			if(spec[j].name == name)
64				break;
65		if(j == len spec)
66			return (nil, sys->sprint("format name %#q not found", name));
67		sp := spec[j];
68		if((hd els).islist() == 0)
69			a[i++] = Fmt(j, spec2fmt(sp.fields));
70		else if(sp.fields == nil)
71			return (nil, sys->sprint("unexpected list %#q", name));
72		else{
73			(f, err) := ses2fmt(sp.fields, (hd els).args());
74			if(f == nil)
75				return (nil, err);
76			a[i++] = Fmt(j, f);
77		}
78	}
79	return (a, nil);
80}
81
82rec2val(spec: array of Fmtspec, se: ref Sexprs->Sexp): (array of Fmtval, string)
83{
84	if(se.islist() == 0)
85		return (nil, "expected list of fields; got "+se.text());
86	els := se.els();
87	if(len els > len spec)
88		return (nil, sys->sprint("too many fields found, expected %d, got %s", len spec, se.text()));
89	a := array[len spec] of Fmtval;
90	err: string;
91	for(i := 0; i < len spec; i++){
92		f := spec[i];
93		if(f.name == nil)
94			continue;
95		if(els == nil)
96			return (nil, sys->sprint("too few fields found, expected %d, got %s", len spec, se.text()));
97		el := hd els;
98		if(f.fields == nil)
99			a[i].val = el;
100		else{
101			if(el.islist() == 0)
102				return (nil, "expected list of elements; got "+el.text());
103			vl := el.els();
104			a[i].recs = recs := array[len vl] of array of Fmtval;
105			for(j := 0; vl != nil; vl = tl vl){
106				(recs[j++], err) = rec2val(spec[i].fields, hd vl);
107				if(err != nil)
108					return (nil, err);
109			}
110		}
111		els = tl els;
112	}
113	return (a, nil);
114}
115
116Fmtval.text(v: self Fmtval): string
117{
118	return v.val.astext();
119}
120
121Fmtfile.new(spec: array of Fmtspec): Fmtfile
122{
123	return (spec, (ref Sexp.List(spec2se(spec))).pack());
124}
125
126Fmtfile.open(f: self Fmtfile, name: string): ref Bufio->Iobuf
127{
128	fd := sys->open(name, Sys->ORDWR);
129	if(fd == nil){
130		sys->werrstr(sys->sprint("open failed: %r"));
131		return nil;
132	}
133	if(sys->write(fd, f.descr, len f.descr) == -1){
134		sys->werrstr(sys->sprint("format write failed: %r"));
135		return nil;
136	}
137	sys->seek(fd, big 0, Sys->SEEKSTART);
138	return bufio->fopen(fd, Sys->OREAD);
139}
140
141Fmtfile.read(f: self Fmtfile, iob: ref Iobuf): (array of Fmtval, string)
142{
143	(se, err) := Sexp.read(iob);
144	if(se == nil)
145		return (nil, err);
146	return rec2val(f.spec, se);
147}
148