The OpenD Programming Language

1 /++
2 A simple I/O routines around `<stdio.h>`.
3 
4 The implementation is CTFE-friendly.
5 +/
6 module mir.stdio;
7 
8 static import core.stdc.stdio;
9 
10 import mir.exception: toMutable;
11 
12 /// Writes values in a text form
13 void writeln(string separator = "", Args...)(auto ref const Args args)
14     if (Args.length > 0)
15 {
16     dout.write!separator(args);
17     dout << endl;
18 }
19 
20 /// ditto
21 void write(string separator = "", Args...)(auto ref const Args args)
22     if (Args.length > 0)
23 {
24     dout.write!separator(args);
25 }
26 
27 /// Writes values in a text form using nothrow $(LREF tout)
28 void dump(string separator = " ", Args...)(auto ref const Args args)
29     if (Args.length > 0)
30 {
31     tout.write!separator(args);
32     tout << endl;
33 }
34 
35 /// Standart output
36 File dout()() @trusted nothrow @nogc @property
37 {
38     version(LDC)
39         pragma(inline, true);
40     return File(__ctfe ? null : core.stdc.stdio.stdout);
41 }
42 
43 /// ditto
44 File derr()() @trusted nothrow @nogc @property
45 {
46     version(LDC)
47         pragma(inline, true);
48     return File(__ctfe ? null : core.stdc.stdio.stderr);
49 }
50 
51 ///
52 version(mir_test)
53 @safe @nogc
54 unittest
55 {
56     dout << "mir.stdio.dout test! - @nogc I/O" << endl;
57     derr << "mir.stdio.derr test! - @nogc I/O" << endl;
58 }
59 
60 /++
61 Nothrow standart output to use in pair with `debug` expression in nothrow
62 and pure code for testing purpose.
63 
64 See_also: $(LREF AssumeNothrowFile)
65 +/
66 AssumeNothrowFile tout()() @trusted nothrow @nogc @property
67 {
68     version(LDC)
69         pragma(inline, true);
70     return AssumeNothrowFile(__ctfe ? null : core.stdc.stdio.stdout);
71 }
72 
73 /// ditto
74 AssumeNothrowFile terr()() @trusted nothrow @nogc @property
75 {
76     version(LDC)
77         pragma(inline, true);
78     return AssumeNothrowFile(__ctfe ? null : core.stdc.stdio.stderr);
79 }
80 
81 ///
82 version(mir_test)
83 pure @safe @nogc nothrow
84 unittest
85 {
86     debug tout << "mir.stdio.tout test! - @nogc nothrow I/O" << endl;
87     debug terr << "mir.stdio.terr test! - @nogc nothrow I/O" << endl;
88 }
89 
90 /++
91 When used as `file << endl` it adds new line flushes the stream.
92 +/
93 enum NewLine
94 {
95     lf = "\n",
96     lf_cf = "\r\n",
97 }
98 
99 /// ditto
100 enum endl = NewLine.lf;
101 
102 /++
103 +/
104 struct File
105 {
106     ///
107     core.stdc.stdio.FILE* fp;
108 
109     mixin FileMembers;
110 
111 @trusted @nogc:
112 
113     /++
114     Throws: $(LREF FileException)
115     +/
116     void rawWrite(scope const(void)[] data) scope
117         in (__ctfe || fp !is null)
118     {
119         if (__ctfe)
120             return;
121         core.stdc.stdio.fwrite(data.ptr, 1, data.length, fp);
122         if (core.stdc.stdio.ferror(fp))
123             throw writeException.toMutable;
124     }
125 
126     /++
127     Throws: $(LREF FileException)
128     +/
129     void flush() scope
130         in (__ctfe || fp !is null)
131     {
132         if (__ctfe)
133             return;
134         core.stdc.stdio.fflush(fp);
135         if (core.stdc.stdio.ferror(fp))
136             throw writeException.toMutable;
137     }
138 }
139 
140 /++
141 Nothrow File implementation for testing purposes.
142 See_also: $(LREF tout), $(LREF terr)
143 +/
144 struct AssumeNothrowFile
145 {
146     ///
147     core.stdc.stdio.FILE* fp;
148 
149     mixin FileMembers;
150 
151 @trusted @nogc nothrow:
152 
153     /++
154     Throws: $(LREF FileError)
155     +/
156     void rawWrite(scope const(void)[] data) scope
157         in (__ctfe || fp !is null)
158     {
159         if (__ctfe)
160             return;
161         core.stdc.stdio.fwrite(data.ptr, 1, data.length, fp);
162         if (core.stdc.stdio.ferror(fp))
163             throw writeError.toMutable;
164     }
165 
166     /++
167     Throws: $(LREF FileError)
168     +/
169     void flush() scope
170         in (__ctfe || fp !is null)
171     {
172         if (__ctfe)
173             return;
174         core.stdc.stdio.fflush(fp);
175         if (core.stdc.stdio.ferror(fp))
176             throw writeError.toMutable;
177     }
178 }
179 
180 ///
181 mixin template FileMembers()
182 {
183     ///
184     void put(C)(const C data) scope
185         if (is(C == char) || is(C == wchar) | is(C == dchar))
186     {
187         C[1] array = [data];
188         this.rawWrite(array);
189     }
190 
191     ///
192     void put(C)(scope const(C)[] data) scope
193         if (is(C == char) || is(C == wchar) | is(C == dchar))
194     {
195         this.rawWrite(data);
196     }
197 
198     ///
199     template opBinary(string op : "<<")
200     {
201         ///
202         ref opBinary(T)(auto ref const T value) scope return
203         {
204             if (__ctfe)
205                 return this;
206             import mir.format: print;
207             print!char(this, value);
208             return this;
209         }
210 
211         /// Prints new line and flushes the stream
212         ref opBinary(NewLine endl) scope return
213         {
214             if (__ctfe)
215                 return this;
216             this.put(endl);
217             this.flush;
218             return this;
219         }
220     }
221 
222     /// Writes values in a text form
223     void writeln(string separator = "", Args...)(auto ref const Args args) scope
224         if (Args.length > 0)
225     {
226         write(args);
227         this << endl;
228     }
229 
230     /// ditto
231     void write(string separator = "", Args...)(auto ref const Args args) scope
232         if (Args.length > 0)
233     {
234         pragma(inline, false);
235         if (__ctfe)
236             return;
237         import mir.format: print, printStaticString;
238         foreach (i, ref arg; args)
239         {
240             print!char(this, arg);
241             static if (separator.length && i + 1 < args.length)
242             {
243                 printStaticString!char(this, separator);
244             }
245         }
246     }
247 }
248 
249 /++
250 File Exception
251 +/
252 class FileException : Exception
253 {
254     ///
255     this(
256         string msg,
257         string file = __FILE__,
258         size_t line = __LINE__,
259         Throwable next = null) pure nothrow @nogc @safe
260     {
261         super(msg, file, line, next);
262     }
263 
264     ///
265     this(
266         string msg,
267         Throwable next,
268         string file = __FILE__,
269         size_t line = __LINE__,
270         ) pure nothrow @nogc @safe
271     {
272         this(msg, file, line, next);
273     }
274 
275     FileException toMutable() @trusted pure nothrow @nogc const
276     {
277         return cast() this;
278     }
279 
280     alias toMutable this;
281 }
282 
283 /++
284 File Error
285 +/
286 class FileError : Error
287 {
288     ///
289     this(
290         string msg,
291         string file = __FILE__,
292         size_t line = __LINE__,
293         Throwable next = null) pure nothrow @nogc @safe
294     {
295         super(msg, file, line, next);
296     }
297 
298     ///
299     this(
300         string msg,
301         Throwable next,
302         string file = __FILE__,
303         size_t line = __LINE__,
304         ) pure nothrow @nogc @safe
305     {
306         this(msg, file, line, next);
307     }
308 
309     FileError toMutable() @trusted pure nothrow @nogc const
310     {
311         return cast() this;
312     }
313 
314     alias toMutable this;
315 }
316 
317 private static immutable writeException = new FileException("Error on file write");
318 private static immutable flushException = new FileException("Error on file flush");
319 private static immutable writeError = new FileError("Error on file write");
320 private static immutable flushError = new FileError("Error on file flush");
321 
322 version(mir_test)
323 @safe unittest
324 {
325     static struct ParameterSpec
326     {
327         string name;
328         string type;
329         string default_;
330     }
331 
332     static struct FunctionOverloadSpec
333     {
334         ParameterSpec[] parameters;
335         string doc;
336         string test;
337         size_t minArgs;
338     }
339 
340     import mir.algebraic;
341     import mir.stdio;
342     if (false)
343         Algebraic!(FunctionOverloadSpec, ParameterSpec).init.writeln; // error
344 }