]> git.tdb.fi Git - libs/datafile.git/blob - source/loader.cpp
Make Loader::ActionMap delete the actions
[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(string::const_iterator 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(list<Statement>::const_iterator i=st.sub.begin(); i!=st.sub.end(); ++i)
83                 load_statement(*i);
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(vector<Loader *>::const_iterator i=aux_loaders.begin(); i!=aux_loaders.end(); ++i)
121                                 if((*i)->has_action(key))
122                                         return (*i)->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 &)
135         {
136                 throw;
137         }
138         catch(const exception &e)
139         {
140                 throw data_error(st.source, st.line, e);
141         }
142 }
143
144 void Loader::load_sub_with(Loader &ldr)
145 {
146         if(direct)
147         {
148                 ldr.load_direct(*cur_parser, cur_level+1);
149                 ldr.finish();
150         }
151         else if(cur_st)
152         {
153                 ldr.load(*cur_st);
154                 sub_loaded = true;
155         }
156         else
157                 throw logic_error("no current statement");
158 }
159
160 void Loader::set_actions(ActionMap &a)
161 {
162         if(actions)
163                 throw logic_error("actions already set");
164
165         actions = &a;
166         if(a.empty())
167                 init_actions();
168 }
169
170 void Loader::add(const string &kwd, LoaderAction *act)
171 {
172         if(!actions)
173                 actions = &local_actions;
174
175         StatementKey key(kwd, (act ? act->get_signature() : "*"));
176         ActionMap::iterator i = actions->find(key);
177         if(i!=actions->end())
178         {
179                 delete i->second;
180                 i->second = act;
181         }
182         else
183                 (*actions)[key] = act;
184 }
185
186 void Loader::add_auxiliary_loader(Loader &ldr)
187 {
188         aux_loaders.push_back(&ldr);
189 }
190
191 bool Loader::has_action(const StatementKey &key) const
192 {
193         if(!actions)
194                 return false;
195
196         ActionMap::const_iterator i = actions->lower_bound(StatementKey(key.keyword, string()));
197         for(; (i!=actions->end() && i->first.keyword==key.keyword); ++i)
198                 if(signature_match(key.signature, i->first.signature))
199                         return true;
200         return false;
201 }
202
203 LoaderAction *Loader::find_action(const StatementKey &key) const
204 {
205         if(!actions)
206                 return 0;
207
208         ActionMap::const_iterator begin = actions->lower_bound(StatementKey(key.keyword, string()));
209         ActionMap::const_iterator end = actions->upper_bound(StatementKey(key.keyword, "~"));
210
211         if(begin==end)
212                 throw unknown_keyword(key.keyword);
213
214         LoaderAction *act = 0;
215         int match = 0;
216         for(ActionMap::const_iterator i=begin; i!=end; ++i)
217         {
218                 int m = signature_match(key.signature, i->first.signature);
219                 if(m>match)
220                 {
221                         act = i->second;
222                         match = m;
223                 }
224         }
225
226         if(!match)
227                 throw invalid_signature(key.keyword, key.signature);
228
229         return act;
230 }
231
232 const string &Loader::get_source() const
233 {
234         if(!cur_st)
235                 throw logic_error("no current statement");
236         return cur_st->source;
237 }
238
239 const string &Loader::get_keyword() const
240 {
241         if(!cur_st)
242                 throw logic_error("no current statement");
243         return cur_st->keyword;
244 }
245
246
247 Loader::ActionMap::~ActionMap()
248 {
249         for(iterator i=begin(); i!=end(); ++i)
250                 delete i->second;
251 }
252
253 } // namespace DataFile
254 } // namespace Msp