]> git.xonotic.org Git - xonotic/gmqcc.git/blob - lex.c
Some new types, and lexer changes
[xonotic/gmqcc.git] / lex.c
1 /*
2  * Copyright (C) 2012 
3  *      Dale Weiler
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is furnished to do
10  * so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 #include <stdio.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27 #include <string.h>
28 #include "gmqcc.h"
29
30 static const char *const lex_keywords[] = {
31         "do",    "else",     "if",     "while",
32         "break", "continue", "return", "goto",
33         "for",
34         
35         /* types */
36         "int",
37         "bool",
38         "void",
39         "string",
40         "float",
41         "vector",
42         "entity"
43 };
44
45 struct lex_file *lex_open(const char *name) {
46         struct lex_file *lex = mem_a(sizeof(struct lex_file));
47         if (lex) {
48                 lex->file = fopen(name, "r");
49                 fseek(lex->file, 0, SEEK_END);
50                 lex->length = ftell(lex->file);
51                 lex->size   = lex->length; /* copy, this is never changed */
52                 fseek(lex->file, 0, SEEK_SET);
53                 lex->last = 0;
54                 
55                 memset(lex->peek, 0, sizeof(lex->peek));
56         }
57         return lex;
58 }
59
60 int lex_close(struct lex_file *file) {
61         int ret = -1;
62         if (file) {
63                 ret = fclose(file->file);
64                 mem_d(file);
65         }
66         return ret;
67 }
68
69 static void lex_addch(int ch, struct lex_file *file) {
70         if (file->current <  sizeof(file->lastok)-1)
71                 file->lastok[file->current++] = (char)ch;
72         if (file->current == sizeof(file->lastok)-1)
73                 file->lastok[file->current]   = (char)'\0';
74 }
75 static inline void lex_clear(struct lex_file *file) {
76         file->current = 0;
77 }
78
79 /*
80  * read in inget/unget character from a lexer stream.
81  * This doesn't play with file streams, the lexer has
82  * it's own internal state for this.
83  */
84 static int lex_inget(struct lex_file *file) {
85         file->length --;
86         if (file->last > 0)
87                 return file->peek[--file->last];
88         return fgetc(file->file);
89 }
90 static void lex_unget(int ch, struct lex_file *file) {
91         if (file->last < sizeof(file->peek))
92                 file->peek[file->last++] = ch;
93         file->length ++;
94 }
95
96 /*
97  * This is trigraph and digraph support, a feature not qc compiler
98  * supports.  Moving up in this world!
99  */
100 static int lex_trigraph(struct lex_file *file) {
101         int  ch;
102         if ((ch = lex_inget(file)) != '?') {
103                 lex_unget(ch, file);
104                 return '?';
105         }
106         
107         ch = lex_inget(file);
108         switch (ch) {
109                 case '(' : return '[' ;
110                 case ')' : return ']' ;
111                 case '/' : return '\\';
112                 case '\'': return '^' ;
113                 case '<' : return '{' ;
114                 case '>' : return '}' ;
115                 case '!' : return '|' ;
116                 case '-' : return '~' ;
117                 case '=' : return '#' ;
118                 default:
119                         lex_unget('?', file);
120                         lex_unget(ch , file);
121                         return '?';
122         }
123         return '?';
124 }
125 static int lex_digraph(struct lex_file *file, int first) {
126         int ch = lex_inget(file);
127         switch (first) {
128                 case '<':
129                         if (ch == '%') return '{';
130                         if (ch == ':') return '[';
131                         break;
132                 case '%':
133                         if (ch == '>') return '}';
134                         if (ch == ':') return '#';
135                         break;
136                 case ':':
137                         if (ch == '>') return ']';
138                         break;
139         }
140         
141         lex_unget(ch, file);
142         return first;
143 }
144
145 static int lex_getch(struct lex_file *file) {
146         int ch = lex_inget(file);
147         if (ch == '?')
148                 return lex_trigraph(file);
149         if (ch == '<' || ch == ':' || ch == '%')
150                 return lex_digraph (file, ch);
151                 
152         return ch;
153 }
154
155 static int lex_get(struct lex_file *file) {
156         int ch;
157         if (!isspace(ch = lex_getch(file)))
158                 return ch;
159         
160         /* skip over all spaces */
161         while (isspace(ch) && ch != '\n')
162                 ch = lex_getch(file);
163                 
164         if (ch == '\n')
165                 return ch;
166                 
167         lex_unget(ch, file);
168         return ' ';
169 }
170
171 static int lex_skipchr(struct lex_file *file) {
172         int ch;
173         int it;
174         
175         lex_clear(file);
176         lex_addch('\'', file);
177         
178         for (it = 0; it < 2 && ((ch = lex_inget(file)) != '\''); it++) {
179                 lex_addch(ch, file);
180                 
181                 if (ch == '\n')
182                         return ERROR_LEX;
183                 if (ch == '\\')
184                         lex_addch(lex_getch(file), file);
185         }
186         lex_addch('\'', file);
187         lex_addch('\0', file);
188         
189         if (it > 2)
190                 return ERROR_LEX;
191                 
192         return LEX_CHRLIT;
193 }
194
195 static int lex_skipstr(struct lex_file *file) {
196         int ch;
197         lex_clear(file);
198         lex_addch('"', file);
199         
200         while ((ch = lex_getch(file)) != '"') {
201                 if (ch == '\n' || ch == EOF)
202                         return ERROR_LEX;
203                         
204                 lex_addch(ch, file);
205                 if (ch == '\\')
206                         lex_addch(lex_inget(file), file);
207         }
208         
209         lex_addch('"', file);
210         lex_addch('\0', file);
211         
212         return LEX_STRLIT;
213 }
214 static int lex_skipcmt(struct lex_file *file) {
215         int ch;
216         lex_clear(file);
217         ch = lex_getch(file);
218         
219         if (ch == '/') {
220                 lex_addch('/', file);
221                 lex_addch('/', file);
222                 
223                 while ((ch = lex_getch(file)) != '\n') {
224                         if (ch == '\\') {
225                                 lex_addch(ch, file);
226                                 lex_addch(lex_getch(file), file);
227                         } else {
228                                 lex_addch(ch, file);
229                         }
230                 }
231                 lex_addch('\0', file);
232                 return LEX_COMMENT;
233         }
234         
235         if (ch != '*') {
236                 lex_unget(ch, file);
237                 return '/';
238         }
239         
240         lex_addch('/', file);
241         
242         /* hate this */
243         do {
244                 lex_addch(ch, file);
245                 while ((ch = lex_getch(file)) != '*') {
246                         if (ch == EOF)
247                                 return error(ERROR_LEX, "malformatted comment"," ");
248                         else
249                                 lex_addch(ch, file);
250                 }
251                 lex_addch(ch, file);
252         } while ((ch = lex_getch(file)) != '/');
253         
254         lex_addch('/',  file);
255         lex_addch('\0', file);
256         
257         return LEX_COMMENT;
258 }
259
260 static int lex_getsource(struct lex_file *file) {
261         int ch = lex_get(file);
262         
263         /* skip char/string/comment */
264         switch (ch) {
265                 case '\'': return lex_skipchr(file);
266                 case '"':  return lex_skipstr(file);
267                 case '/':  return lex_skipcmt(file);
268                 default:   return ch;
269         }
270 }
271
272 int lex_token(struct lex_file *file) {
273         int ch = lex_getsource(file);
274         int it;
275         
276         /* valid identifier */
277         if (ch > 0 && (ch == '_' || isalpha(ch))) {
278                 lex_clear(file);
279                 while (ch > 0 && (isalpha(ch) || isdigit(ch) || ch == '_')) {
280                         lex_addch(ch, file);
281                         ch = lex_getsource(file);
282                 }
283                 lex_unget(ch,   file);
284                 lex_addch('\0', file);
285                 
286                 /* look inside the table for a keyword .. */
287                 for (it = 0; it < sizeof(lex_keywords)/sizeof(*lex_keywords); it++)
288                         if (!strncmp(file->lastok, lex_keywords[it], sizeof(lex_keywords[it])))
289                                 return it;
290                                 
291                 return LEX_IDENT;
292         }
293         return ch;
294 }
295
296 void lex_reset(struct lex_file *file) {
297         file->current = 0;
298         file->last    = 0;
299         file->length  = file->size;
300         fseek(file->file, 0, SEEK_SET);
301         
302         memset(file->peek,   0, sizeof(file->peek  ));
303         memset(file->lastok, 0, sizeof(file->lastok));
304 }
305
306 int lex_debug(struct lex_file *file) {
307         int list_do       = 0;
308         int list_else     = 0;
309         int list_if       = 0;
310         int list_while    = 0;
311         int list_break    = 0;
312         int list_continue = 0;
313         int list_return   = 0;
314         int list_goto     = 0;
315         int list_for      = 0;
316         int token         = 0;
317         printf("===========================\nTOKENS:   \n===========================\n");
318         while ((token = lex_token(file)) != ERROR_LEX && file->length >= 0) {
319                 if (token != -1) {
320                         switch (token) {
321                                 case 0: list_do      ++; break;
322                                 case 1: list_else    ++; break;
323                                 case 2: list_if      ++; break;
324                                 case 3: list_while   ++; break;
325                                 case 4: list_break   ++; break;
326                                 case 5: list_continue++; break;
327                                 case 6: list_return  ++; break;
328                                 case 7: list_goto    ++; break;
329                                 case 8: list_for     ++; break;
330                         }
331                 }
332                 if (token >= 33 && token <= 126)
333                         putchar(token);
334         }
335         printf("\n===========================\nBRANCHES \n===========================\n");
336         printf("\t if       % 8d\n", list_if);
337         printf("\t else     % 8d\n", list_else);
338         printf("===========================\nLOOPS      \n===========================\n");
339         printf("\t for      % 8d\n", list_for);
340         printf("\t while    % 8d\n", list_while);
341         printf("\t do       % 8d\n", list_do);
342         printf("===========================\nSTATEMENTS \n===========================\n");
343         printf("\t break    % 8d\n", list_break);
344         printf("\t continue % 8d\n", list_continue);
345         printf("\t return   % 8d\n", list_return);
346         printf("\t goto     % 8d\n", list_goto);
347         printf("===========================\nIDENTIFIERS\n===========================\n");
348         lex_reset(file);
349         while ((token = lex_token(file)) != ERROR_LEX && file->length >= 0)
350                 if (token == LEX_IDENT)
351                         printf("%s ", file->lastok);
352         lex_reset(file);
353         return 1;
354 }