Provide access to the keyword of the current statement
[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 bool signature_match(char s, char a)
12 {
13         if(s==a)
14                 return true;
15         if(s==Msp::DataFile::IntType::signature && a==Msp::DataFile::FloatType::signature)
16                 return true;
17         return false;
18 }
19
20 bool signature_match(const string &st_sig, const string &act_sig)
21 {
22         if(act_sig=="*")
23                 return true;
24         else if(act_sig.size()==2 && act_sig[1]=='*')
25         {
26                 for(string::const_iterator i=st_sig.begin(); i!=st_sig.end(); ++i)
27                         if(!signature_match(*i, act_sig[0]))
28                                 return false;
29
30                 return true;
31         }
32         else if(st_sig.size()==act_sig.size())
33         {
34                 for(unsigned i=0; i<st_sig.size(); ++i)
35                         if(!signature_match(st_sig[i], act_sig[i]))
36                                 return false;
37
38                 return true;
39         }
40         else
41                 return false;
42 }
43
44 }
45
46
47 namespace Msp {
48 namespace DataFile {
49
50 Loader::Loader():
51         cur_st(0),
52         direct(false),
53         check_sub_loads(false)
54 { }
55
56 Loader::~Loader()
57 {
58         for(ActionMap::iterator i=actions.begin(); i!=actions.end(); ++i)
59                 delete i->second;
60 }
61
62 void Loader::load(Parser &p)
63 {
64         while(p)
65         {
66                 if(p.peek(0))
67                         load_direct(p, 0);
68                 else if(p)  // Peek may have processed an __end, so recheck goodness
69                 {
70                         // Parse in raw mode so we can peek immediately after a mode change
71                         Statement st = p.parse(true);
72                         if(st.valid && !st.control)
73                                 load_statement(st);
74                 }
75         }
76         finish();
77 }
78
79 void Loader::load(const Statement &st)
80 {
81         for(list<Statement>::const_iterator i=st.sub.begin(); i!=st.sub.end(); ++i)
82                 load_statement(*i);
83         finish();
84 }
85
86 void Loader::load_direct(Parser &p, unsigned l)
87 {
88         SetForScope<Parser *> set_parser(cur_parser, &p);
89         SetForScope<unsigned> set_level(cur_level, l);
90
91         while(p)
92         {
93                 const StatementKey *key = p.peek(l);
94                 if(!key)
95                         break;
96
97                 LoaderAction *act = find_action(*key);
98                 if(act)
99                 {
100                         SetFlag set_direct(direct);
101                         if(!p.parse_and_load(l, *this, *act))
102                                 throw logic_error("direct load failed");
103                 }
104                 else
105                         load_statement(p.parse());
106         }
107 }
108
109 void Loader::load_statement(const Statement &st)
110 {
111         SetForScope<const Statement *> set_cst(cur_st, &st);
112
113         try
114         {
115                 StatementKey key(st.keyword, st.get_signature());
116
117                 if(!aux_loaders.empty() && !has_action(key))
118                 {
119                         for(list<Loader *>::const_iterator i=aux_loaders.begin(); i!=aux_loaders.end(); ++i)
120                                 if((*i)->has_action(key))
121                                         return (*i)->load_statement(st);
122                 }
123
124                 LoaderAction *act = find_action(key);
125                 if(act)
126                 {
127                         sub_loaded = false;
128                         act->execute(*this, st);
129                         if(check_sub_loads && !st.sub.empty() && !sub_loaded)
130                                 throw logic_error("substatements ignored");
131                 }
132         }
133         catch(const data_error &)
134         {
135                 throw;
136         }
137         catch(const exception &e)
138         {
139                 throw data_error(st.source, st.line, e);
140         }
141 }
142
143 void Loader::load_sub_with(Loader &ldr)
144 {
145         if(direct)
146         {
147                 ldr.load_direct(*cur_parser, cur_level+1);
148                 ldr.finish();
149         }
150         else if(cur_st)
151         {
152                 ldr.load(*cur_st);
153                 sub_loaded = true;
154         }
155         else
156                 throw logic_error("no current statement");
157 }
158
159 void Loader::add(const string &kwd, LoaderAction *act)
160 {
161         StatementKey key(kwd, (act ? act->get_signature() : "*"));
162         ActionMap::iterator i = actions.find(key);
163         if(i!=actions.end())
164         {
165                 delete i->second;
166                 i->second = act;
167         }
168         else
169                 actions[key] = act;
170 }
171
172 void Loader::add_auxiliary_loader(Loader &ldr)
173 {
174         aux_loaders.push_back(&ldr);
175 }
176
177 bool Loader::has_action(const StatementKey &key) const
178 {
179         ActionMap::const_iterator i = actions.lower_bound(StatementKey(key.keyword, string()));
180         for(; (i!=actions.end() && i->first.keyword==key.keyword); ++i)
181                 if(signature_match(key.signature, i->first.signature))
182                         return true;
183         return false;
184 }
185
186 LoaderAction *Loader::find_action(const StatementKey &key) const
187 {
188         ActionMap::const_iterator begin = actions.lower_bound(StatementKey(key.keyword, string()));
189         ActionMap::const_iterator end = actions.upper_bound(StatementKey(key.keyword, "~"));
190
191         if(begin==end)
192                 throw unknown_keyword(key.keyword);
193
194         for(ActionMap::const_iterator i=begin; i!=end; ++i)
195                 if(signature_match(key.signature, i->first.signature))
196                         return i->second;
197
198         throw invalid_signature(key.keyword, key.signature);
199 }
200
201 const string &Loader::get_source() const
202 {
203         if(!cur_st)
204                 throw logic_error("no current statement");
205         return cur_st->source;
206 }
207
208 const string &Loader::get_keyword() const
209 {
210         if(!cur_st)
211                 throw logic_error("no current statement");
212         return cur_st->keyword;
213 }
214
215 } // namespace DataFile
216 } // namespace Msp