src/lexer.cc

changeset 88
5def6ff8b466
parent 87
8f65914e7046
child 89
029a330a9bef
equal deleted inserted replaced
87:8f65914e7046 88:5def6ff8b466
1 /*
2 Copyright 2012-2014 Santeri Piippo
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 3. The name of the author may not be used to endorse or promote products
15 derived from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <cstring>
30 #include "lexer.h"
31
32 static string_list g_file_name_stack;
33 static lexer* g_main_lexer = null;
34
35 // =============================================================================
36 //
37 lexer::lexer()
38 {
39 assert (g_main_lexer == null);
40 g_main_lexer = this;
41 }
42
43 // =============================================================================
44 //
45 lexer::~lexer()
46 {
47 g_main_lexer = null;
48 }
49
50 // =============================================================================
51 //
52 void lexer::process_file (string file_name)
53 {
54 g_file_name_stack << file_name;
55 FILE* fp = fopen (file_name, "r");
56
57 if (fp == null)
58 error ("couldn't open %1 for reading: %2", file_name, strerror (errno));
59
60 lexer_scanner sc (fp);
61 check_file_header (sc);
62
63 while (sc.get_next_token())
64 {
65 // Preprocessor commands:
66 if (sc.get_token_type() == tk_hash)
67 {
68 must_get_next_from_scanner (sc, tk_symbol);
69
70 if (sc.get_token_text() == "include")
71 {
72 must_get_next_from_scanner (sc, tk_string);
73 string file_name = sc.get_token_text();
74
75 if (g_file_name_stack.contains (file_name))
76 error ("attempted to #include %1 recursively", sc.get_token_text());
77
78 process_file (file_name);
79 }
80 else
81 error ("unknown preprocessor directive \"#%1\"", sc.get_token_text());
82 }
83 else
84 {
85 token tok;
86 tok.file = file_name;
87 tok.line = sc.get_line();
88 tok.column = sc.get_column();
89 tok.type = sc.get_token_type();
90 tok.text = sc.get_token_text();
91
92 // devf ("Token #%1: %2:%3:%4: %5 (%6)\n", m_tokens.size(),
93 // tok.file, tok.line, tok.column, describe_token (&tok), describe_token_type (tok.type));
94
95 m_tokens << tok;
96 }
97 }
98
99 m_token_position = m_tokens.begin() - 1;
100 g_file_name_stack.remove (file_name);
101 }
102
103 // ============================================================================
104 //
105 static bool is_valid_header (string header)
106 {
107 if (header.ends_with ("\n"))
108 header.remove_from_end (1);
109
110 string_list tokens = header.split (" ");
111
112 if (tokens.size() != 2 || tokens[0] != "#!botc" || tokens[1].empty())
113 return false;
114
115 string_list nums = tokens[1].split (".");
116
117 if (nums.size() == 2)
118 nums << "0";
119 elif (nums.size() != 3)
120 return false;
121
122 bool ok_a, ok_b, ok_c;
123 long major = nums[0].to_long (&ok_a);
124 long minor = nums[1].to_long (&ok_b);
125 long patch = nums[2].to_long (&ok_c);
126
127 if (!ok_a || !ok_b || !ok_c)
128 return false;
129
130 if (VERSION_NUMBER < MAKE_VERSION_NUMBER (major, minor, patch))
131 error ("The script file requires " APPNAME " v%1, this is v%2",
132 make_version_string (major, minor, patch), get_version_string (e_short_form));
133
134 return true;
135 }
136
137 // ============================================================================
138 //
139 void lexer::check_file_header (lexer_scanner& sc)
140 {
141 if (!is_valid_header (sc.read_line()))
142 error ("Not a valid botscript file! File must start with '#!botc <version>'");
143 }
144
145 // =============================================================================
146 //
147 bool lexer::get_next (e_token req)
148 {
149 iterator pos = m_token_position;
150
151 if (m_tokens.is_empty())
152 return false;
153
154 m_token_position++;
155
156 if (is_at_end() || (req != tk_any && get_token_type() != req))
157 {
158 m_token_position = pos;
159 return false;
160 }
161
162 return true;
163 }
164
165 // =============================================================================
166 //
167 void lexer::must_get_next (e_token tt)
168 {
169 if (!get_next())
170 error ("unexpected EOF");
171
172 if (tt != tk_any)
173 must_be (tt);
174 }
175
176 // =============================================================================
177 // eugh..
178 //
179 void lexer::must_get_next_from_scanner (lexer_scanner& sc, e_token tt)
180 {
181 if (!sc.get_next_token())
182 error ("unexpected EOF");
183
184 if (tt != tk_any && sc.get_token_type() != tt)
185 { // TODO
186 token tok;
187 tok.type = sc.get_token_type();
188 tok.text = sc.get_token_text();
189
190 error ("at %1:%2: expected %3, got %4",
191 g_file_name_stack.last(),
192 sc.get_line(),
193 describe_token_type (tt),
194 describe_token (&tok));
195 }
196 }
197
198 // =============================================================================
199 //
200 void lexer::must_get_any_of (const list<e_token>& toks)
201 {
202 if (!get_next())
203 error ("unexpected EOF");
204
205 for (e_token tok : toks)
206 if (get_token_type() == tok)
207 return;
208
209 string toknames;
210
211 for (const e_token& tok_type : toks)
212 {
213 if (&tok_type == &toks.last())
214 toknames += " or ";
215 elif (toknames.is_empty() == false)
216 toknames += ", ";
217
218 toknames += describe_token_type (tok_type);
219 }
220
221 error ("expected %1, got %2", toknames, describe_token (get_token()));
222 }
223
224 // =============================================================================
225 //
226 int lexer::get_one_symbol (const string_list& syms)
227 {
228 if (!get_next())
229 error ("unexpected EOF");
230
231 if (get_token_type() == tk_symbol)
232 {
233 for (int i = 0; i < syms.size(); ++i)
234 {
235 if (syms[i] == get_token()->text)
236 return i;
237 }
238 }
239
240 error ("expected one of %1, got %2", syms, describe_token (get_token()));
241 return -1;
242 }
243
244 // =============================================================================
245 //
246 void lexer::must_be (e_token tok)
247 {
248 if (get_token_type() != tok)
249 error ("expected %1, got %2", describe_token_type (tok),
250 describe_token (get_token()));
251 }
252
253 // =============================================================================
254 //
255 string lexer::describe_token_private (e_token tok_type, lexer::token* tok)
256 {
257 if (tok_type < tk_last_named_token)
258 return "\"" + lexer_scanner::get_token_string (tok_type) + "\"";
259
260 switch (tok_type)
261 {
262 case tk_symbol: return tok ? tok->text : "a symbol";
263 case tk_number: return tok ? tok->text : "a number";
264 case tk_string: return tok ? ("\"" + tok->text + "\"") : "a string";
265 case tk_any: return tok ? tok->text : "any token";
266 default: break;
267 }
268
269 return "";
270 }
271
272 // =============================================================================
273 //
274 bool lexer::peek_next (lexer::token* tk)
275 {
276 iterator pos = m_token_position;
277 bool r = get_next();
278
279 if (r && tk != null)
280 *tk = *m_token_position;
281
282 m_token_position = pos;
283 return r;
284 }
285
286 // =============================================================================
287 //
288 lexer* lexer::get_current_lexer()
289 {
290 return g_main_lexer;
291 }
292
293 // =============================================================================
294 //
295 string lexer::peek_next_string (int a)
296 {
297 if (m_token_position + a >= m_tokens.end())
298 return "";
299
300 iterator oldpos = m_token_position;
301 m_token_position += a;
302 string result = get_token()->text;
303 m_token_position = oldpos;
304 return result;
305 }

mercurial