]> git.tdb.fi Git - libs/datafile.git/blob - source/loader.cpp
Use C++11 features to manipulate containers
[libs/datafile.git] / source / loader.cpp
1 #include <msp/core/raii.h>
2 #include <msp/strings/format.h>
3 #include "except.h"
4 #include "loader.h"
5 #include "type.h"
6
7 using namespace std;
8
9 namespace {
10
11 int signature_match(char s, char a)
12 {
13         if(s==a)
14                 return 3;
15         if(s==Msp::DataFile::IntType::signature && a==Msp::DataFile::FloatType::signature)
16                 return 1;
17         return 0;
18 }
19
20 int signature_match(const string &st_sig, const string &act_sig)
21 {
22         if(act_sig=="*")
23                 return 1;
24         else if(act_sig.size()==2 && act_sig[1]=='*')
25         {
26                 int match = 3;
27                 for(auto i=st_sig.begin(); (i!=st_sig.end() && match); ++i)
28                         match = min(match, signature_match(*i, act_sig[0]));
29
30                 return match;
31         }
32         else if(st_sig.size()==act_sig.size())
33         {
34                 int match = 3;
35                 for(unsigned i=0; (i<st_sig.size() && match); ++i)
36                         match = min(match, signature_match(st_sig[i], act_sig[i]));
37
38                 return match ? match+1 : 0;
39         }
40         else
41                 return 0;
42 }
43
44 }
45
46
47 namespace Msp {
48 namespace DataFile {
49
50 Loader::Loader():
51         actions(0),
52         cur_st(0),
53         direct(false),
54         check_sub_loads(false)
55 { }
56
57 void Loader::load(Parser &p)
58 {
59         if(!actions)
60                 throw logic_error("no actions");
61
62         while(p)
63         {
64                 if(p.peek(0))
65                         load_direct(p, 0);
66                 else if(p)  // Peek may have processed an __end, so recheck goodness
67                 {
68                         // Parse in raw mode so we can peek immediately after a mode change
69                         Statement st = p.parse(true);
70                         if(st.valid && !st.control)
71                                 load_statement(st);
72                 }
73         }
74         finish();
75 }
76
77 void Loader::load(const Statement &st)
78 {
79         if(!actions)
80                 throw logic_error("no actions");
81
82         for(const Statement &s: st.sub)
83                 load_statement(s);
84         finish();
85 }
86
87 void Loader::load_direct(Parser &p, unsigned l)
88 {
89         SetForScope<Parser *> set_parser(cur_parser, &p);
90         SetForScope<unsigned> set_level(cur_level, l);
91
92         while(p)
93         {
94                 const StatementKey *key = p.peek(l);
95                 if(!key)
96                         break;
97
98                 LoaderAction *act = find_action(*key);
99                 if(act)
100                 {
101                         SetFlag set_direct(direct);
102                         if(!p.parse_and_load(l, *this, *act))
103                                 throw logic_error("direct load failed");
104                 }
105                 else
106                         load_statement(p.parse());
107         }
108 }
109
110 void Loader::load_statement(const Statement &st)
111 {
112         SetForScope<const Statement *> set_cst(cur_st, &st);
113
114         try
115         {
116                 StatementKey key(st.keyword, st.get_signature());
117
118                 if(!aux_loaders.empty() && !has_action(key))
119                 {
120                         for(Loader *l: aux_loaders)
121                                 if(l->has_action(key))
122                                         return l->load_statement(st);
123                 }
124
125                 LoaderAction *act = find_action(key);
126                 if(act)
127                 {
128                         sub_loaded = false;
129                         act->execute(*this, st);
130                         if(check_sub_loads && !st.sub.empty() && !sub_loaded)
131                                 throw logic_error("substatements ignored");
132                 }
133         }
134         catch(const data_error &e)
135         {
136                 if(e.get_source()!=st.source)
137                         throw data_error(st.source, st.line, e);
138                 else
139                         throw;
140         }
141         catch(const exception &e)
142         {
143                 throw data_error(st.source, st.line, e);
144         }
145 }
146
147 void Loader::load_sub_with(Loader &ldr)
148 {
149         if(direct)
150         {
151                 ldr.load_direct(*cur_parser, cur_level+1);
152                 ldr.finish();
153         }
154         else if(cur_st)
155         {
156                 ldr.load(*cur_st);
157                 sub_loaded = true;
158         }
159         else
160                 throw logic_error("no current statement");
161 }
162
163 void Loader::set_actions(ActionMap &a)
164 {
165         if(actions)
166                 throw logic_error("actions already set");
167
168         actions = &a;
169         if(a.empty())
170                 init_actions();
171 }
172
173 void Loader::add(const string &kwd, LoaderAction *act)
174 {
175         if(!actions)
176                 actions = &local_actions;
177
178         StatementKey key(kwd, (act ? act->get_signature() : "*"));
179         ActionMap::iterator i = actions->find(key);
180         if(i!=actions->end())
181         {
182                 delete i->second;
183                 i->second = act;
184         }
185         else
186                 (*actions)[key] = act;
187 }
188
189 void Loader::add_auxiliary_loader(Loader &ldr)
190 {
191         aux_loaders.push_back(&ldr);
192 }
193
194 bool Loader::has_action(const StatementKey &key) const
195 {
196         if(!actions)
197                 return false;
198
199         auto i = actions->lower_bound(StatementKey(key.keyword, string()));
200         for(; (i!=actions->end() && i->first.keyword==key.keyword); ++i)
201                 if(signature_match(key.signature, i->first.signature))
202                         return true;
203         return false;
204 }
205
206 LoaderAction *Loader::find_action(const StatementKey &key) const
207 {
208         if(!actions)
209                 return 0;
210
211         auto begin = actions->lower_bound(StatementKey(key.keyword, string()));
212         auto end = actions->upper_bound(StatementKey(key.keyword, "~"));
213
214         if(begin==end)
215                 throw unknown_keyword(key.keyword);
216
217         LoaderAction *act = 0;
218         int match = 0;
219         for(auto i=begin; i!=end; ++i)
220         {
221                 int m = signature_match(key.signature, i->first.signature);
222                 if(m>match)
223                 {
224                         act = i->second;
225                         match = m;
226                 }
227         }
228
229         if(!match)
230                 throw invalid_signature(key.keyword, key.signature);
231
232         return act;
233 }
234
235 const string &Loader::get_source() const
236 {
237         if(!cur_st)
238                 throw logic_error("no current statement");
239         return cur_st->source;
240 }
241
242 const string &Loader::get_keyword() const
243 {
244         if(!cur_st)
245                 throw logic_error("no current statement");
246         return cur_st->keyword;
247 }
248
249
250 Loader::ActionMap::~ActionMap()
251 {
252         for(const auto &kvp: *this)
253                 delete kvp.second;
254 }
255
256 } // namespace DataFile
257 } // namespace Msp