Add a shortcut for wrapping data for a collection file
[libs/datafile.git] / tool / compiler.cpp
1 #include <msp/datafile/parser.h>
2 #include <msp/fs/dir.h>
3 #include <msp/fs/utils.h>
4 #include <msp/strings/regex.h>
5 #include "compiler.h"
6
7 using namespace std;
8 using namespace Msp;
9
10 Compiler::Compiler(DataFile::Writer &w):
11         writer(w),
12         reset_src(false)
13 {
14         add("file",     &Compiler::file);
15         add("for_each", &Compiler::for_each);
16         add("write",    &Compiler::write);
17 }
18
19 void Compiler::file(const string &fn)
20 {
21         File fl(*this, FS::dirname(get_source())/fn);
22         load_sub_with(fl);
23 }
24
25 void Compiler::for_each(const vector<string> &patterns)
26 {
27         ForEach fe(*this, FS::dirname(get_source()), list<string>(patterns.begin(), patterns.end()));
28         load_sub_with(fe);
29 }
30
31 void Compiler::write(const DataFile::Statement &st)
32 {
33         if(reset_src)
34         {
35                 writer.write((DataFile::Statement("__src"), string()));
36                 reset_src = false;
37         }
38
39         for(list<DataFile::Statement>::const_iterator i = st.sub.begin(); i!=st.sub.end(); ++i)
40                 writer.write(*i);
41 }
42
43 bool Compiler::process_statement(const FS::Path &fn, DataFile::Statement &st)
44 {
45         if(st.keyword=="_content")
46                 return true;
47
48         if(st.keyword=="_extension")
49         {
50                 string ext = FS::extpart(FS::basename(fn));
51                 if(ext.empty() || ext.size()==1)
52                         throw invalid_argument("Compiler::process_statement");
53                 st.keyword = ext.substr(1);
54         }
55
56         for(vector<DataFile::Value>::iterator i = st.args.begin(); i!=st.args.end(); ++i)
57                 if(i->get_signature()==DataFile::StringType::signature)
58                 {
59                         string value = i->get<string>();
60                         if(value=="$filename")
61                                 *i = DataFile::Value(FS::basename(fn.str()));
62                         else if(value=="$content")
63                         {
64                                 IO::File in(fn.str());
65                                 string data;
66                                 while(!in.eof())
67                                 {
68                                         char buf[4096];
69                                         unsigned len = in.read(buf, sizeof(buf));
70                                         data.append(buf, len);
71                                 }
72                                 *i = DataFile::Value(data);
73                         }
74                 }
75
76         for(list<DataFile::Statement>::iterator i = st.sub.begin(); i!=st.sub.end();)
77         {
78                 if(process_statement(fn, *i))
79                 {
80                         IO::File in(fn.str());
81                         IO::Buffered buf(in);
82
83                         DataFile::Parser parser(in, fn.str());
84                         while(parser)
85                         {
86                                 DataFile::Statement ss = parser.parse();
87                                 if(ss.valid)
88                                         st.sub.insert(i, ss);
89                         }
90                         i = st.sub.erase(i);
91                 }
92                 else
93                         ++i;
94         }
95
96         return false;
97 }
98
99 void Compiler::process_file(const FS::Path &fn, const list<DataFile::Statement> &st)
100 {
101         writer.write((DataFile::Statement("__src"), FS::basename(fn.str())));
102         reset_src = true;
103
104         if(st.empty())
105                 process_file(fn);
106         else
107         {
108                 for(list<DataFile::Statement>::const_iterator i = st.begin(); i!=st.end(); ++i)
109                 {
110                         if(i->keyword=="_content")
111                                 process_file(fn);
112                         else
113                         {
114                                 DataFile::Statement s = *i;
115                                 process_statement(fn, s);
116                                 writer.write(s);
117                         }
118                 }
119         }
120 }
121
122 void Compiler::process_file(const FS::Path &fn)
123 {
124         IO::File in(fn.str());
125         IO::Buffered buf(in);
126
127         DataFile::Parser parser(in, fn.str());
128         while(parser)
129         {
130                 DataFile::Statement st = parser.parse();
131                 if(st.valid)
132                         writer.write(st);
133         }
134 }
135
136
137 File::File(Compiler &c, const FS::Path &fn):
138         compiler(c),
139         filename(fn)
140 {
141         add("write", &File::write);
142 }
143
144 void File::finish()
145 {
146         compiler.process_file(filename, write_st);
147 }
148
149 void File::write(const DataFile::Statement &st)
150 {
151         write_st.insert(write_st.end(), st.sub.begin(), st.sub.end());
152 }
153
154
155 ForEach::ForEach(Compiler &c, const FS::Path &b, const list<string> &p):
156         compiler(c),
157         base(b),
158         patterns(p)
159 {
160         add("exclude", &ForEach::exclude);
161         add("pattern", &ForEach::pattern);
162         add("wrap",    &ForEach::wrap);
163         add("wrap",    &ForEach::wrap_keyword);
164         add("write",   &ForEach::write);
165 }
166
167 void ForEach::finish()
168 {
169         list<string> files = FS::list_files(base);
170         for(list<string>::iterator i = files.begin(); i!=files.end(); ++i)
171         {
172                 bool match = false;
173                 for(list<string>::const_iterator j = patterns.begin(); (j!=patterns.end() && !match); ++j)
174                         match = Regex(*j).match(*i);
175                 for(list<string>::const_iterator j = excludes.begin(); (j!=excludes.end() && match); ++j)
176                         match = !Regex(*j).match(*i);
177                 if(match)
178                         compiler.process_file(base / *i, write_st);
179         }
180 }
181
182 void ForEach::exclude(const string &p)
183 {
184         excludes.push_back(p);
185 }
186
187 void ForEach::pattern(const string &p)
188 {
189         patterns.push_back(p);
190 }
191
192 void ForEach::wrap()
193 {
194         wrap_keyword("_extension");
195 }
196
197 void ForEach::wrap_keyword(const string &kwd)
198 {
199         DataFile::Statement st(kwd);
200         st.append("$filename");
201         st.sub.push_back(DataFile::Statement("_content"));
202         write_st.push_back(st);
203 }
204
205 void ForEach::write(const DataFile::Statement &st)
206 {
207         write_st.insert(write_st.end(), st.sub.begin(), st.sub.end());
208 }