-rw-r--r-- | .gitignore | 18 | ||||
-rw-r--r-- | AUTHORS | 3 | ||||
-rw-r--r-- | COPYING | 19 | ||||
-rw-r--r-- | ChangeLog | 0 | ||||
-rw-r--r-- | Makefile.am | 15 | ||||
-rw-r--r-- | NEWS.xml | 6 | ||||
-rw-r--r-- | NEWS.xsl | 24 | ||||
-rw-r--r-- | README | 16 | ||||
-rw-r--r-- | autogen.bash | 6 | ||||
-rw-r--r-- | configure.ac | 23 | ||||
-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 | ||||
-rw-r--r-- | test/.gitignore | 22 | ||||
-rw-r--r-- | test/Makefile.am | 32 | ||||
-rw-r--r-- | test/r01_closing_at_the_line_start.clichec | 16 | ||||
-rw-r--r-- | test/r01_closing_at_the_line_start.expected | 8 | ||||
-rw-r--r-- | test/r02_anchor_after_literal.clichec | 8 | ||||
-rw-r--r-- | test/r02_anchor_after_literal.expected | 3 | ||||
-rw-r--r-- | test/r03_percent_after_empty.clichec | 8 | ||||
-rw-r--r-- | test/r03_percent_after_empty.expected | 6 | ||||
-rw-r--r-- | test/r04_too_seamless_transitions.clichec | 9 | ||||
-rw-r--r-- | test/r04_too_seamless_transitions.expected | 3 |
24 files changed, 693 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8523ddd --- a/dev/null +++ b/.gitignore | |||
@@ -0,0 +1,18 @@ | |||
1 | /INSTALL | ||
2 | Makefile | ||
3 | Makefile.in | ||
4 | /aclocal.m4 | ||
5 | /autom4te.cache/ | ||
6 | /config.h | ||
7 | /config.h.in | ||
8 | /config.log | ||
9 | /config.status | ||
10 | /configure | ||
11 | /depcomp | ||
12 | /install-sh | ||
13 | /missing | ||
14 | /stamp-h1 | ||
15 | .deps/ | ||
16 | /NEWS | ||
17 | *.o | ||
18 | *.cliche.cc | ||
@@ -0,0 +1,3 @@ | |||
1 | Klever dissected: | ||
2 | Michael 'hacker' Krelin <hacker@klever.net> | ||
3 | Leonid Ivanov <kamel@klever.net> | ||
@@ -0,0 +1,19 @@ | |||
1 | Copyright (c) 2011 Klever Group (http://www.klever.net/) | ||
2 | |||
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
4 | this software and associated documentation files (the "Software"), to deal in | ||
5 | the Software without restriction, including without limitation the rights to | ||
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
7 | of the Software, and to permit persons to whom the Software is furnished to do | ||
8 | so, subject to the following conditions: | ||
9 | |||
10 | The above copyright notice and this permission notice shall be included in all | ||
11 | copies or substantial portions of the Software. | ||
12 | |||
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
19 | SOFTWARE. | ||
diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 --- a/dev/null +++ b/ChangeLog | |||
diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..77e96ad --- a/dev/null +++ b/Makefile.am | |||
@@ -0,0 +1,15 @@ | |||
1 | SUBDIRS=src test | ||
2 | |||
3 | all-local: NEWS | ||
4 | |||
5 | NEWS: NEWS.xsl NEWS.xml | ||
6 | $(XSLTPROC) -o $@ $^ | ||
7 | EXTRA_DIST = NEWS.xml NEWS.xsl | ||
8 | |||
9 | ISSUEFILES = $$(find ${top_srcdir} -type f '(' \ | ||
10 | -name '*.rl' -or -name '*.h' \ | ||
11 | ')' ) \ | ||
12 | ${top_srcdir}/configure.ac | ||
13 | issues: todo fixme xxx | ||
14 | todo fixme xxx: | ||
15 | @grep --color=auto -in '$@:' ${ISSUEFILES} || true | ||
diff --git a/NEWS.xml b/NEWS.xml new file mode 100644 index 0000000..106fbbd --- a/dev/null +++ b/NEWS.xml | |||
@@ -0,0 +1,6 @@ | |||
1 | <?xml version="1.0" encoding="us-ascii"?> | ||
2 | <news> | ||
3 | <version version="0.0" date="Aug 27th, 2011"> | ||
4 | <ni>Initial release</ni> | ||
5 | </version> | ||
6 | </news> | ||
diff --git a/NEWS.xsl b/NEWS.xsl new file mode 100644 index 0000000..7c71307 --- a/dev/null +++ b/NEWS.xsl | |||
@@ -0,0 +1,24 @@ | |||
1 | <?xml version="1.0" encoding="us-ascii"?> | ||
2 | <xsl:stylesheet version="1.0" | ||
3 | xmlns:xsl="http://www.w3.org/1999/XSL/Transform" | ||
4 | > | ||
5 | <xsl:output | ||
6 | method="text" | ||
7 | encoding="us-ascii" | ||
8 | media-type="text/plain" /> | ||
9 | |||
10 | <xsl:template match="news"> | ||
11 | <xsl:apply-templates/> | ||
12 | </xsl:template> | ||
13 | <xsl:template match="version"> | ||
14 | <xsl:value-of select="concat(@version,' (',@date,')
')"/> | ||
15 | <xsl:apply-templates/> | ||
16 | </xsl:template> | ||
17 | <xsl:template match="ni"> | ||
18 | <xsl:text> - </xsl:text> | ||
19 | <xsl:apply-templates mode="text"/> | ||
20 | <xsl:text>
</xsl:text> | ||
21 | </xsl:template> | ||
22 | <xsl:template match="*|text()"/> | ||
23 | |||
24 | </xsl:stylesheet> | ||
@@ -0,0 +1,16 @@ | |||
1 | Constructs: | ||
2 | |||
3 | code embedded in output: | ||
4 | <%code> ++i; </%code> | ||
5 | % ++i; | ||
6 | |||
7 | output embedded in code: | ||
8 | int i=0; | ||
9 | <%output>whoa, it's <% i++ %>!</%output> | ||
10 | |||
11 | embed literal in the code: | ||
12 | #define URL "http://www.klever.net/" | ||
13 | <%literal> | ||
14 | a text that is going to be there is a string | ||
15 | with some possibilities <% URL %> | ||
16 | </%literal> | ||
diff --git a/autogen.bash b/autogen.bash new file mode 100644 index 0000000..14798fd --- a/dev/null +++ b/autogen.bash | |||
@@ -0,0 +1,6 @@ | |||
1 | #!/bin/bash | ||
2 | aclocal \ | ||
3 | && autoheader \ | ||
4 | && automake -a \ | ||
5 | && autoconf \ | ||
6 | && ./configure "$@" | ||
diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..4ecdb31 --- a/dev/null +++ b/configure.ac | |||
@@ -0,0 +1,23 @@ | |||
1 | AC_INIT([cliche],[0.0],[cliche-bugs@klever.net]) | ||
2 | AC_CONFIG_SRCDIR([src/cliche.rl]) | ||
3 | AC_CONFIG_HEADERS([config.h]) | ||
4 | AM_INIT_AUTOMAKE([check-news dist-bzip2 parallel-tests color-tests]) | ||
5 | |||
6 | AC_PROG_INSTALL | ||
7 | AC_PROG_CXX | ||
8 | AC_PATH_PROG([RAGEL],[ragel],[false]) | ||
9 | test "$RAGEL" = "false" && AC_MSG_ERROR([No ragel FSM compiler found]) | ||
10 | AC_PROG_SED | ||
11 | |||
12 | AC_PATH_PROG([XSLTPROC],[xsltproc],[true]) | ||
13 | |||
14 | AC_CHECK_FUNC([getopt_long],[ | ||
15 | AC_DEFINE([HAVE_GETOPT_LONG],[1],[define to make use of getopt_long]) | ||
16 | ]) | ||
17 | |||
18 | AC_CONFIG_FILES([ | ||
19 | Makefile | ||
20 | src/Makefile | ||
21 | test/Makefile | ||
22 | ]) | ||
23 | AC_OUTPUT | ||
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: */ | ||
diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..54191cb --- a/dev/null +++ b/test/.gitignore | |||
@@ -0,0 +1,22 @@ | |||
1 | /test-suite.log | ||
2 | |||
3 | /r01_closing_at_the_line_start | ||
4 | /r01_closing_at_the_line_start.out | ||
5 | /r01_closing_at_the_line_start.diff | ||
6 | /r01_closing_at_the_line_start.check | ||
7 | /r01_closing_at_the_line_start.log | ||
8 | /r02_anchor_after_literal | ||
9 | /r02_anchor_after_literal.out | ||
10 | /r02_anchor_after_literal.diff | ||
11 | /r02_anchor_after_literal.check | ||
12 | /r02_anchor_after_literal.log | ||
13 | /r03_percent_after_empty | ||
14 | /r03_percent_after_empty.out | ||
15 | /r03_percent_after_empty.diff | ||
16 | /r03_percent_after_empty.check | ||
17 | /r03_percent_after_empty.log | ||
18 | /r04_too_seamless_transitions | ||
19 | /r04_too_seamless_transitions.out | ||
20 | /r04_too_seamless_transitions.diff | ||
21 | /r04_too_seamless_transitions.check | ||
22 | /r04_too_seamless_transitions.log | ||
diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..705278d --- a/dev/null +++ b/test/Makefile.am | |||
@@ -0,0 +1,32 @@ | |||
1 | TESTS=\ | ||
2 | r01_closing_at_the_line_start.clichec \ | ||
3 | r02_anchor_after_literal.clichec \ | ||
4 | r03_percent_after_empty.clichec \ | ||
5 | r04_too_seamless_transitions.clichec | ||
6 | |||
7 | EXTRA_DIST=$(TESTS) | ||
8 | CLEANFILES = $(basename $(TESTS)) \ | ||
9 | $(foreach s,.out .diff,$(addsuffix $s,$(basename $(TESTS)))) | ||
10 | .INTERMEDIATE: $(CLEANFILES) | ||
11 | |||
12 | TEST_EXTENSIONS=.clichec | ||
13 | CLICHEC_LOG_COMPILER=test_clichec() { \ | ||
14 | $(MAKE) "$$(basename $$1 .clichec)"{,.{out,diff,check}} ;\ | ||
15 | }; test_clichec | ||
16 | |||
17 | gitignore: .gitignore | ||
18 | .gitignore: Makefile | ||
19 | for t in ${TESTS} ; do for f in "$${t%.*}"{,.{out,diff,check,log}} ; do \ | ||
20 | grep -q "^/$$f" .gitignore || echo "/$$f" >>.gitignore ;\ | ||
21 | done done | ||
22 | |||
23 | CLICHE=${top_builddir}/src/cliche | ||
24 | SUFFIXES=.clichec .cc .out .diff .check | ||
25 | .clichec.cc: | ||
26 | $(CLICHE) -C -o $@ $< | ||
27 | %.out: % | ||
28 | ${builddir}/$< >$@ | ||
29 | %.diff: ${builddir}/%.out ${srcdir}/%.expected | ||
30 | @-diff -u "$*.expected" "$*.out" >"$@" | ||
31 | %.check: %.diff | ||
32 | @test -s "$<" && { cat "$*.out";cp "$*.out" "$*.unexpected" ; exit 1 ; } || true | ||
diff --git a/test/r01_closing_at_the_line_start.clichec b/test/r01_closing_at_the_line_start.clichec new file mode 100644 index 0000000..11ebd8f --- a/dev/null +++ b/test/r01_closing_at_the_line_start.clichec | |||
@@ -0,0 +1,16 @@ | |||
1 | #include <iostream> | ||
2 | int main() { | ||
3 | const char *t0 = | ||
4 | <%literal> | ||
5 | |||
6 | test-literal | ||
7 | |||
8 | </%literal>; | ||
9 | std::cout << t0; | ||
10 | |||
11 | <%output> | ||
12 | |||
13 | test-output | ||
14 | |||
15 | </%output> | ||
16 | } | ||
diff --git a/test/r01_closing_at_the_line_start.expected b/test/r01_closing_at_the_line_start.expected new file mode 100644 index 0000000..7854811 --- a/dev/null +++ b/test/r01_closing_at_the_line_start.expected | |||
@@ -0,0 +1,8 @@ | |||
1 | |||
2 | |||
3 | test-literal | ||
4 | |||
5 | |||
6 | |||
7 | test-output | ||
8 | |||
diff --git a/test/r02_anchor_after_literal.clichec b/test/r02_anchor_after_literal.clichec new file mode 100644 index 0000000..c2049c0 --- a/dev/null +++ b/test/r02_anchor_after_literal.clichec | |||
@@ -0,0 +1,8 @@ | |||
1 | #include <iostream> | ||
2 | int main() { | ||
3 | const char *l3 = <%literal>line 3</%literal> ":"; int nl3 = __LINE__; | ||
4 | std::cout << l3 << nl3 << std::endl; | ||
5 | {<%output>line 5 (<% __LINE__ %>)</%output>} | ||
6 | int l6 = __LINE__; | ||
7 | std::cout << std::endl << "line 6: " << l6 << std::endl; | ||
8 | }; | ||
diff --git a/test/r02_anchor_after_literal.expected b/test/r02_anchor_after_literal.expected new file mode 100644 index 0000000..b53c949 --- a/dev/null +++ b/test/r02_anchor_after_literal.expected | |||
@@ -0,0 +1,3 @@ | |||
1 | line 3:3 | ||
2 | line 5 (5) | ||
3 | line 6: 6 | ||
diff --git a/test/r03_percent_after_empty.clichec b/test/r03_percent_after_empty.clichec new file mode 100644 index 0000000..62c82eb --- a/dev/null +++ b/test/r03_percent_after_empty.clichec | |||
@@ -0,0 +1,8 @@ | |||
1 | #include <iostream> | ||
2 | int main() {<%output> | ||
3 | Test for broken handling of '^% ' line following the empty line. | ||
4 | Like this: | ||
5 | |||
6 | % std::cout << 1 << std::endl; | ||
7 | |||
8 | </%output>} | ||
diff --git a/test/r03_percent_after_empty.expected b/test/r03_percent_after_empty.expected new file mode 100644 index 0000000..f2ee7b2 --- a/dev/null +++ b/test/r03_percent_after_empty.expected | |||
@@ -0,0 +1,6 @@ | |||
1 | |||
2 | Test for broken handling of '^% ' line following the empty line. | ||
3 | Like this: | ||
4 | |||
5 | 1 | ||
6 | |||
diff --git a/test/r04_too_seamless_transitions.clichec b/test/r04_too_seamless_transitions.clichec new file mode 100644 index 0000000..c313013 --- a/dev/null +++ b/test/r04_too_seamless_transitions.clichec | |||
@@ -0,0 +1,9 @@ | |||
1 | #include <iostream> | ||
2 | int main() { | ||
3 | int a=1,b=2; | ||
4 | <%output> | ||
5 | The transition like this - <% a %><% b %> should not glue 'ab' in the source together. | ||
6 | Really. | ||
7 | </%output> | ||
8 | } | ||
9 | |||
diff --git a/test/r04_too_seamless_transitions.expected b/test/r04_too_seamless_transitions.expected new file mode 100644 index 0000000..2ab4e9f --- a/dev/null +++ b/test/r04_too_seamless_transitions.expected | |||
@@ -0,0 +1,3 @@ | |||
1 | |||
2 | The transition like this - 12 should not glue 'ab' in the source together. | ||
3 | Really. | ||