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