]> git.tdb.fi Git - libs/datafile.git/blob - source/loader.cpp
Cosmetic changes
[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 void Loader::load(Parser &p)
51 {
52         if(!actions)
53                 throw logic_error("no actions");
54
55         while(p)
56         {
57                 if(p.peek(0))
58                         load_direct(p, 0);
59                 else if(p)  // Peek may have processed an __end, so recheck goodness
60                 {
61                         // Parse in raw mode so we can peek immediately after a mode change
62                         Statement st = p.parse(true);
63                         if(st.valid && !st.control)
64                                 load_statement(st);
65                 }
66         }
67         finish();
68         for(Loader *l: aux_loaders)
69                 l->finish();
70 }
71
72 void Loader::load(const Statement &st)
73 {
74         if(!actions)
75                 throw logic_error("no actions");
76
77         for(const Statement &s: st.sub)
78                 load_statement(s);
79         finish();
80         for(Loader *l: aux_loaders)
81                 l->finish();
82 }
83
84 void Loader::load_direct(Parser &p, unsigned l)
85 {
86         SetForScope<Parser *> set_parser(cur_parser, &p);
87         SetForScope<unsigned> set_level(cur_level, l);
88
89         while(p)
90         {
91                 const StatementKey *key = p.peek(l);
92                 if(!key)
93                         break;
94
95                 LoaderAction *act = find_action(*key);
96                 if(act)
97                 {
98                         SetFlag set_direct(direct);
99                         if(!p.parse_and_load(l, *this, *act))
100                                 throw logic_error("direct load failed");
101                 }
102                 else
103                         load_statement(p.parse());
104         }
105 }
106
107 void Loader::load_statement(const Statement &st)
108 {
109         SetForScope<const Statement *> set_cst(cur_st, &st);
110
111         try
112         {
113                 StatementKey key(st.keyword, st.get_signature());
114
115                 if(!aux_loaders.empty() && !has_action(key))
116                 {
117                         for(Loader *l: aux_loaders)
118                                 if(l->has_action(key))
119                                         return l->load_statement(st);
120                 }
121
122                 LoaderAction *act = find_action(key);
123                 if(act)
124                 {
125                         sub_loaded = false;
126                         act->execute(*this, st);
127                         if(check_sub_loads && !st.sub.empty() && !sub_loaded)
128                                 throw logic_error("substatements ignored");
129                 }
130         }
131         catch(const data_error &e)
132         {
133                 if(e.get_source()!=st.source)
134                         throw data_error(st.source, st.line, e);
135                 else
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                 for(Loader *l: ldr.aux_loaders)
151                         l->finish();
152         }
153         else if(cur_st)
154         {
155                 ldr.load(*cur_st);
156                 sub_loaded = true;
157         }
158         else
159                 throw logic_error("no current statement");
160 }
161
162 void Loader::set_actions(ActionMap &a)
163 {
164         if(actions)
165                 throw logic_error("actions already set");
166
167         actions = &a;
168         if(a.empty())
169                 init_actions();
170 }
171
172 void Loader::add(const string &kwd, LoaderAction *act)
173 {
174         if(!actions)
175                 actions = &local_actions;
176
177         StatementKey key(kwd, (act ? act->get_signature() : "*"));
178         ActionMap::iterator i = actions->find(key);
179         if(i!=actions->end())
180         {
181                 delete i->second;
182                 i->second = act;
183         }
184         else
185                 (*actions)[key] = act;
186 }
187
188 void Loader::add_auxiliary_loader(Loader &ldr)
189 {
190         aux_loaders.push_back(&ldr);
191 }
192
193 bool Loader::has_action(const StatementKey &key) const
194 {
195         if(!actions)
196                 return false;
197
198         auto i = actions->lower_bound(StatementKey(key.keyword, string()));
199         for(; (i!=actions->end() && i->first.keyword==key.keyword); ++i)
200                 if(signature_match(key.signature, i->first.signature))
201                         return true;
202         return false;
203 }
204
205 LoaderAction *Loader::find_action(const StatementKey &key) const
206 {
207         if(!actions)
208                 return nullptr;
209
210         auto begin = actions->lower_bound(StatementKey(key.keyword, string()));
211         auto end = actions->upper_bound(StatementKey(key.keyword, "~"));
212
213         if(begin==end)
214                 throw unknown_keyword(key.keyword);
215
216         LoaderAction *act = nullptr;
217         int match = 0;
218         for(auto i=begin; i!=end; ++i)
219         {
220                 int m = signature_match(key.signature, i->first.signature);
221                 if(m>match)
222                 {
223                         act = i->second;
224                         match = m;
225                 }
226         }
227
228         if(!match)
229                 throw invalid_signature(key.keyword, key.signature);
230
231         return act;
232 }
233
234 const string &Loader::get_source() const
235 {
236         if(!cur_st)
237                 throw logic_error("no current statement");
238         return cur_st->source;
239 }
240
241 const string &Loader::get_keyword() const
242 {
243         if(!cur_st)
244                 throw logic_error("no current statement");
245         return cur_st->keyword;
246 }
247
248
249 Loader::ActionMap::~ActionMap()
250 {
251         for(const auto &kvp: *this)
252                 delete kvp.second;
253 }
254
255 } // namespace DataFile
256 } // namespace Msp