author | Michael Krelin <hacker@klever.net> | 2011-08-26 21:22:00 (UTC) |
---|---|---|
committer | Michael Krelin <hacker@klever.net> | 2011-08-26 21:22:00 (UTC) |
commit | 1d00b262ddf0d6c3207a4b796d48899ed79bffcd (patch) (unidiff) | |
tree | 03d745873ba13ffb0e2fe1831ecb41f7c0b05758 /src | |
download | cliche-1d00b262ddf0d6c3207a4b796d48899ed79bffcd.zip cliche-1d00b262ddf0d6c3207a4b796d48899ed79bffcd.tar.gz cliche-1d00b262ddf0d6c3207a4b796d48899ed79bffcd.tar.bz2 |
initial commit into the public repository0.0
Signed-off-by: Michael Krelin <hacker@klever.net>
-rw-r--r-- | src/.gitignore | 3 | ||||
-rw-r--r-- | src/Makefile.am | 15 | ||||
-rw-r--r-- | src/cliche.1 | 99 | ||||
-rw-r--r-- | src/cliche.rl | 331 |
4 files changed, 448 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..090e0ca --- a/dev/null +++ b/src/.gitignore | |||
@@ -0,0 +1,3 @@ | |||
1 | /COPYING.cc | ||
2 | /cliche | ||
3 | /cliche.cc | ||
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..d5102a7 --- a/dev/null +++ b/src/Makefile.am | |||
@@ -0,0 +1,15 @@ | |||
1 | bin_PROGRAMS = cliche | ||
2 | man_MANS = cliche.1 | ||
3 | |||
4 | EXTRA_DIST=$(man_MANS) | ||
5 | |||
6 | cliche_SOURCES = cliche.rl COPYING.cc | ||
7 | CLEANFILES=cliche.cc | ||
8 | |||
9 | COPYING.cc: ${top_srcdir}/COPYING | ||
10 | (echo 'const char *COPYING=' \ | ||
11 | && $(SED) -e 's/"/\\"/g' -e 's/^/\"/' -e 's/$$/\\n\"/' \ | ||
12 | && echo ';') <$< >$@ || (rm $@;exit 1) | ||
13 | |||
14 | .rl.cc: | ||
15 | $(RAGEL) -C -o $@ $< | ||
diff --git a/src/cliche.1 b/src/cliche.1 new file mode 100644 index 0000000..af61a15 --- a/dev/null +++ b/src/cliche.1 | |||
@@ -0,0 +1,99 @@ | |||
1 | .TH cliche 1 "August 27th, 2011" "cliche" "Klever Group (http://www.klever.net/)" | ||
2 | .hla en | ||
3 | |||
4 | .SH NAME | ||
5 | |||
6 | cliche \- A tinimalistc template preprocessor for c++ | ||
7 | |||
8 | .SH SYNOPSYS | ||
9 | |||
10 | \fBcliche\fR | ||
11 | [\fB-h\fR] [\fB--help\fR] [\fB--usage\fR] | ||
12 | [\fB-V\fR] [\fB--version\fR] | ||
13 | [\fB-L\fR] [\fB--license\fR] | ||
14 | [\fB-o\fR \fIfile\fR] [\fB--output=\fR\fIfile\fR] | ||
15 | [\fB-t\fR \fIcode\fR|\fIoutput\fR] [\fB--top=\fR\fIcode\fR|\fIoutput\fR] | ||
16 | [\fB-C\fR] [\fB-O\fR] | ||
17 | |||
18 | .SH DESCRIPTION | ||
19 | |||
20 | cliche preprocesses its input to produce c++ code streaming | ||
21 | out the template while executing embedded c++ code and | ||
22 | expressions. | ||
23 | |||
24 | An example of cliche input file may look like this: | ||
25 | |||
26 | .ti 1 | ||
27 | #include <iostream> | ||
28 | .ti 1 | ||
29 | int main() { | ||
30 | .ti 2 | ||
31 | for(int i=0;i<5;++i) { | ||
32 | .ti 3 | ||
33 | <%output> | ||
34 | .ti 4 | ||
35 | <i>Whoa, it's <b><% i %></b> already!</i><br/> | ||
36 | .ti 3 | ||
37 | </%output> | ||
38 | .ti 2 | ||
39 | } | ||
40 | .ti 1 | ||
41 | } | ||
42 | |||
43 | .SH OPTIONS | ||
44 | |||
45 | .TP | ||
46 | \fB-o\fR \fIfile\fR, \fB--output=\fR\fIfile\fR | ||
47 | Write output to the specified file. | ||
48 | .TP | ||
49 | \fB-t\fR \fIcode\fR|\fIoutput\fR, \fB--top=\fR\fIcode\fR|\fIoutput\fR | ||
50 | Expect input to have code or output (as if in <%code></%code> or <%output></%output> block) on the top-level. Code templates are suitable for feeding into compiler as is, whereas output templates may be better suited for #include. | ||
51 | .TP | ||
52 | \fB-C\fR | ||
53 | Same as \fB-t code\fR. | ||
54 | .TP | ||
55 | \fB-O\fR | ||
56 | Same as \fB-t output\fR. | ||
57 | .TP | ||
58 | \fB-h\fR, \fB--help\fR, \fB--usage\fR | ||
59 | Display short usage instructions and exit. | ||
60 | .TP | ||
61 | \fB-V\fR, \fB--version\fR | ||
62 | Report version and exit. | ||
63 | .TP | ||
64 | \fB-L\fR, \fB--license\fR | ||
65 | Show licensing terms. | ||
66 | |||
67 | .SH EXIT STATUS | ||
68 | Unsurprisingly, \fBcliche\fR returns zero in case of success and non-zero otherwise. | ||
69 | |||
70 | .SH AUTHOR | ||
71 | |||
72 | Written by Michael Krelin <hacker@klever.net> | ||
73 | |||
74 | |||
75 | .SH COPYRIGHT | ||
76 | |||
77 | Copyright (c) 2011 Klever Group (http://www.klever.net/) | ||
78 | |||
79 | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
80 | this software and associated documentation files (the "Software"), to deal in | ||
81 | the Software without restriction, including without limitation the rights to | ||
82 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
83 | of the Software, and to permit persons to whom the Software is furnished to do | ||
84 | so, subject to the following conditions: | ||
85 | |||
86 | The above copyright notice and this permission notice shall be included in all | ||
87 | copies or substantial portions of the Software. | ||
88 | |||
89 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
90 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
91 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
92 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
93 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
94 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
95 | SOFTWARE. | ||
96 | |||
97 | .SH BUGS | ||
98 | |||
99 | You tell me. Send reports to <cliche-bugs@klever.net> | ||
diff --git a/src/cliche.rl b/src/cliche.rl new file mode 100644 index 0000000..1f1f07f --- a/dev/null +++ b/src/cliche.rl | |||
@@ -0,0 +1,331 @@ | |||
1 | #include <getopt.h> | ||
2 | #include <iostream> | ||
3 | #include <cstring> | ||
4 | #include <cassert> | ||
5 | #include <algorithm> | ||
6 | #include <fstream> | ||
7 | |||
8 | #include "config.h" | ||
9 | |||
10 | struct line_counting_monkey { | ||
11 | int n; | ||
12 | const char *p; | ||
13 | const char *fn; | ||
14 | |||
15 | line_counting_monkey(const char *fn_,const char *p_) : fn(fn_), n(1), p(p_) { } | ||
16 | |||
17 | int lineno(const char *pp) { | ||
18 | if(pp>p) { | ||
19 | n += std::count(p,pp,'\n'); | ||
20 | p = pp; | ||
21 | } | ||
22 | return n; | ||
23 | } | ||
24 | }; | ||
25 | |||
26 | struct code_writing_monkey { | ||
27 | std::ostream& o; | ||
28 | enum omode_type { | ||
29 | om_code = 0, om_output, om_inline, om_literal, | ||
30 | oms | ||
31 | }; | ||
32 | omode_type omode; | ||
33 | int last_anchor, since_anchor; | ||
34 | |||
35 | code_writing_monkey(std::ostream& o_) : o(o_), omode(om_code), last_anchor(-1), since_anchor(0) { } | ||
36 | |||
37 | void modify(omode_type om,line_counting_monkey* lcm=0,const char *p=0) { | ||
38 | static const char *om_transitions[oms][oms] = { | ||
39 | // To: From: | ||
40 | // code output inline literal | ||
41 | { "", "CLICHE_OUTPUT_LITERAL(\n", "CLICHE_STREAM << (", "" }, // code | ||
42 | { ");\n", "", ");\n(CLICHE_STREAM) << (", 0 }, // output | ||
43 | { ");\n", ");\nCLICHE_OUTPUT_LITERAL(\n", "", 0 }, // inline | ||
44 | { " ", 0, 0, "" }, // literal | ||
45 | }; | ||
46 | assert(0 <= omode && omode < oms); | ||
47 | assert(0 <= om && om < oms); | ||
48 | const char *t = om_transitions[omode][om]; | ||
49 | assert(t); // TODO: complain? | ||
50 | o << t; | ||
51 | since_anchor += std::count(t,t+strlen(t),'\n'); | ||
52 | if(lcm && t && *t && om!=omode && p) anchor(*lcm,p); | ||
53 | omode = om; | ||
54 | } | ||
55 | |||
56 | void prologue() { | ||
57 | assert(omode==om_code); | ||
58 | o << | ||
59 | "#ifndef CLICHE_STREAM\n" | ||
60 | "# define CLICHE_STREAM (std::cout)\n" | ||
61 | "# define CLICHE_STREAM_AUTODEFINED\n" | ||
62 | "#endif\n" | ||
63 | "#ifndef CLICHE_OUTPUT_LITERAL\n" | ||
64 | "# define CLICHE_OUTPUT_LITERAL(sl) (CLICHE_STREAM).write((sl),sizeof(sl)-sizeof(\"\"))\n" | ||
65 | "#endif\n"; | ||
66 | } | ||
67 | void epilogue() { | ||
68 | modify(om_code); | ||
69 | o << "\n" | ||
70 | "#ifdef CLICHE_STREAM_AUTODEFINED\n" | ||
71 | "# undef CLICHE_STREAM\n" | ||
72 | "# undef CLICHE_STREAM_AUTODEFINED\n" | ||
73 | "#endif\n"; | ||
74 | } | ||
75 | |||
76 | void monkey(const char *d,size_t l=0) { | ||
77 | if(!(l || (l=strlen(d)))) return; | ||
78 | if(omode!=om_output && omode!=om_literal) { | ||
79 | since_anchor += std::count(d,d+l,'\n'); | ||
80 | o.write(d,l); | ||
81 | return; | ||
82 | } | ||
83 | o.put('"'); | ||
84 | const char *p=d; | ||
85 | while(l--) { | ||
86 | char c; | ||
87 | switch(*d) { | ||
88 | case '\r': c='r'; break; | ||
89 | case '\n': c='n'; break; | ||
90 | case '\t': c='t'; break; | ||
91 | case '\a': c='a'; break; | ||
92 | case '\b': c='b'; break; | ||
93 | case '\v': c='v'; break; | ||
94 | case '\f': c='f'; break; | ||
95 | case '\'': case '\"': case '\\': c=*d; break; | ||
96 | case 0: c='0'; break; | ||
97 | default: c=0; break; | ||
98 | }; | ||
99 | if(!c) { | ||
100 | ++d; | ||
101 | continue; | ||
102 | } | ||
103 | if(p!=d) o.write(p,d-p); | ||
104 | o.put('\\'); | ||
105 | if(c=='0') | ||
106 | o.write("000",3); | ||
107 | else | ||
108 | o.put(c); | ||
109 | p=++d; | ||
110 | } | ||
111 | if(p!=d) o.write(p,d-p); | ||
112 | o.write("\"\n",2); ++since_anchor; | ||
113 | } | ||
114 | |||
115 | void monkey_as(omode_type om,const char *d,size_t l=0,line_counting_monkey *lcm=0,const char *p=0) { modify(om,lcm,p); monkey(d,l); } | ||
116 | |||
117 | void anchor(line_counting_monkey& lcm,const char *p) { | ||
118 | // modify(om_code); | ||
119 | int l = lcm.lineno(p); | ||
120 | if(last_anchor>0 && last_anchor+since_anchor==l) return; | ||
121 | o << "\n#line " << (since_anchor=0,last_anchor=l) << " \"" << lcm.fn << "\"\n"; | ||
122 | } | ||
123 | }; | ||
124 | |||
125 | |||
126 | %%{ | ||
127 | machine cliche; | ||
128 | |||
129 | linebreak = /[\r\n]/; | ||
130 | |||
131 | action monkey { | ||
132 | cwm.monkey(ts,te-ts); | ||
133 | } | ||
134 | action monkey_code { | ||
135 | cwm.monkey_as(cwm.om_code,ts,te-ts,&lcm,p); | ||
136 | } | ||
137 | action monkey_output { | ||
138 | cwm.monkey_as(cwm.om_output,ts,te-ts,&lcm,p); | ||
139 | } | ||
140 | action monkey_literal { | ||
141 | cwm.monkey_as(cwm.om_literal,ts,te-ts,&lcm,p); | ||
142 | } | ||
143 | |||
144 | slashstar_comment := | ||
145 | ( any* :>> '*/' ) ${ cwm.monkey(fpc,1); } @{ fret; }; | ||
146 | |||
147 | outputblock := |* | ||
148 | '%' (^linebreak)* linebreak { cwm.monkey_as(cwm.om_code,ts+1,te-ts-1,&lcm,p); }; | ||
149 | any=> { fhold; fcall outputline; }; | ||
150 | |||
151 | *|; | ||
152 | outputline := |* | ||
153 | (^linebreak)* linebreak -- ('</%output>' | '<%code>' | ('<%' space) ) { cwm.monkey_as(cwm.om_output,ts,te-ts,&lcm,p); fret; }; | ||
154 | '<%code>' { cwm.modify(cwm.om_code,&lcm,p); fcall codeblock; }; | ||
155 | '</%output>' { --top; fret; }; | ||
156 | '<%' space { cwm.modify(cwm.om_inline,&lcm,p); fcall inlineblock; }; | ||
157 | (^linebreak)+ -- ( '%' | '<' ) => monkey_output; | ||
158 | any => monkey_output; | ||
159 | *|; | ||
160 | |||
161 | inlineblock := |* | ||
162 | space '%>' { cwm.modify(cwm.om_code,&lcm,p); fret; }; | ||
163 | "'" ( [^'\\] | /\\./ )* "'" => monkey; | ||
164 | '"' ( [^"\\] | /\\./ )* '"' => monkey; | ||
165 | '/*' { cwm.monkey("/*",2); fcall slashstar_comment; }; | ||
166 | '//' (^linebreak)* (linebreak) => monkey; | ||
167 | any => monkey; | ||
168 | *|; | ||
169 | |||
170 | literalblock := |* | ||
171 | any => { fhold; fcall literalline; }; | ||
172 | *|; | ||
173 | literalline := |* | ||
174 | (^linebreak)* linebreak -- ('</%literal>' | ('<%' space) ) { cwm.monkey_as(cwm.om_literal,ts,te-ts,&lcm,p); fret; }; | ||
175 | '</%literal>' { --top; fret; }; | ||
176 | '<%' space { cwm.modify(cwm.om_code,&lcm,p); fcall inlineblock; }; | ||
177 | (^linebreak)+ -- ( '%' | '<' ) => monkey_literal; | ||
178 | any => monkey_literal; | ||
179 | *|; | ||
180 | |||
181 | codeblock := |* | ||
182 | '<%output>'{ fcall outputblock; }; | ||
183 | '<%literal>'{ fcall literalblock; }; | ||
184 | '</%code>'{ fret; }; | ||
185 | "'" ( [^'\\] | /\\./ )* "'" => monkey_code; | ||
186 | '"' ( [^"\\] | /\\./ )* '"' => monkey_code; | ||
187 | '/*' { cwm.monkey("/*",2); fcall slashstar_comment; }; | ||
188 | '//' (^linebreak)* (linebreak) => monkey_code; | ||
189 | any => monkey_code; | ||
190 | *|; | ||
191 | |||
192 | main := any >{ | ||
193 | fhold; | ||
194 | switch(topmode) { | ||
195 | case code_writing_monkey::om_output: fgoto outputblock; | ||
196 | case code_writing_monkey::om_code: fgoto codeblock; | ||
197 | default: ;/* TODO: WTD? */ | ||
198 | }; | ||
199 | }; | ||
200 | }%% | ||
201 | |||
202 | %% write data; | ||
203 | |||
204 | static const char *biname = 0; | ||
205 | static void display_usage() { | ||
206 | std::cerr << PACKAGE " Version " VERSION "\n" | ||
207 | "Copyright (c) 2011 Klever Group\n" | ||
208 | "\n" | ||
209 | " " << biname << " [otpions] [input-file]\n" | ||
210 | "\n" | ||
211 | #ifdef HAVE_GETOPT_LONG | ||
212 | " -h, --help\n" | ||
213 | " --usage display this text\n" | ||
214 | " -V, --version display version number\n" | ||
215 | " -L, --license show license\n" | ||
216 | " -o <file>, --output=<file> write output to the named file\n" | ||
217 | " -t code|output, --top=code|output\n" | ||
218 | #else | ||
219 | " -h display this text\n" | ||
220 | " -V display version number\n" | ||
221 | " -L show license\n" | ||
222 | " -o <file> write output to the named file\n" | ||
223 | " -t code|output\n" | ||
224 | #endif | ||
225 | " set toplevel processing mode [output]\n" | ||
226 | " -C same as -t=code\n" | ||
227 | " -O same as -t=output (default)\n" | ||
228 | "\n"; | ||
229 | } | ||
230 | |||
231 | int main(int argc,char *argv[]) { | ||
232 | biname = *argv; | ||
233 | std::string ofile; | ||
234 | code_writing_monkey::omode_type topmode = code_writing_monkey::om_output; | ||
235 | while(true) { | ||
236 | static const char shopts[] = "hVLo:t:CO"; | ||
237 | #if HAVE_GETOPT_LONG | ||
238 | static struct option opts[] = { | ||
239 | { "help", no_argument, 0, 'h' }, | ||
240 | { "usage", no_argument, 0, 'h' }, | ||
241 | { "version", no_argument, 0, 'V' }, | ||
242 | { "license", no_argument, 0, 'L' }, | ||
243 | { "output", required_argument, 0, 'o' }, | ||
244 | { "top", required_argument, 0, 't' }, | ||
245 | { NULL, 0, 0, 0 } | ||
246 | }; | ||
247 | int c = getopt_long(argc,argv,shopts,opts,NULL); | ||
248 | #else | ||
249 | int c = getopt(argc,argv,shopts); | ||
250 | #endif | ||
251 | if(c==-1) break; | ||
252 | switch(c) { | ||
253 | case 't': | ||
254 | if(!strcasecmp(optarg,"code")) { | ||
255 | topmode = code_writing_monkey::om_code; | ||
256 | break; | ||
257 | }else if(!strcasecmp(optarg,"output")) { | ||
258 | topmode = code_writing_monkey::om_output; | ||
259 | break; | ||
260 | } | ||
261 | std::cerr << "Unkown '" << optarg << "' mode" << std::endl; | ||
262 | case '?': /* unknown option */ | ||
263 | case 'h': display_usage(); exit(0); break; | ||
264 | case 'V': std::cerr << VERSION << std::endl; exit(0); break; | ||
265 | case 'L': | ||
266 | extern const char *COPYING; | ||
267 | std::cerr << COPYING << std::endl; | ||
268 | exit(0); break; | ||
269 | case 'o': ofile = optarg; break; | ||
270 | case 'C': topmode = code_writing_monkey::om_code; break; | ||
271 | case 'O': topmode = code_writing_monkey::om_output; break; | ||
272 | default: | ||
273 | std::cerr << "Huh?" << std::endl; | ||
274 | exit(1); break; | ||
275 | } | ||
276 | } | ||
277 | #undef LS | ||
278 | if((optind+1)!=argc) { | ||
279 | display_usage(); exit(1); | ||
280 | /* TODO: or use stdin if no parameter specified? */ | ||
281 | } | ||
282 | |||
283 | std::string ifile = argv[optind]; | ||
284 | if(ofile.empty()) ofile = ifile+".cc"; | ||
285 | std::ifstream ist(ifile.c_str(),std::ios::in); | ||
286 | std::ofstream ost(ofile.c_str(),std::ios::out); | ||
287 | if(!ost) { | ||
288 | std::cerr << "failed to open '" << ofile << "' for writing" << std::endl; | ||
289 | exit(2); | ||
290 | } | ||
291 | |||
292 | int cs, act; | ||
293 | char *ts, *te; | ||
294 | int stack[128], top=0; | ||
295 | %% write init; | ||
296 | char input[512]; | ||
297 | int have = 0; | ||
298 | char *eof = 0; | ||
299 | code_writing_monkey cwm(ost); | ||
300 | cwm.prologue(); | ||
301 | line_counting_monkey lcm(ifile.c_str(),input); | ||
302 | cwm.anchor(lcm,0); | ||
303 | while(!eof) { | ||
304 | if(have==sizeof(input)) { | ||
305 | std::cerr << "No space to read in" << std::endl; | ||
306 | break; | ||
307 | } | ||
308 | char *p = input+have; | ||
309 | int lw = sizeof(input)-have; | ||
310 | int lp = ist.read(p,lw).gcount(); | ||
311 | char *pe = p+lp; | ||
312 | eof = (lp==lw)?0:pe; | ||
313 | %%write exec; | ||
314 | if(cs==cliche_error) { | ||
315 | std::cerr << "cliche error" << std::endl; | ||
316 | break; | ||
317 | } | ||
318 | if(ts) { | ||
319 | lcm.lineno(ts); | ||
320 | te = ((char*)memmove(input,ts,have=pe-ts)) + (te-ts); | ||
321 | ts = input; | ||
322 | }else{ | ||
323 | lcm.lineno(pe); | ||
324 | have = 0; | ||
325 | } | ||
326 | lcm.p = input; | ||
327 | } | ||
328 | cwm.epilogue(); | ||
329 | return 0; | ||
330 | } | ||
331 | /* vim:set ft=ragel ts=8 sw=8 cin si ai: */ | ||