]> git.xonotic.org Git - xonotic/gmqcc.git/blob - lex.c
Cleaups and README
[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 /*
31  * Keywords are multichar, punctuation lexing is a bit more complicated
32  * than keyword lexing.
33  */
34 static const char *const lex_keywords[] = {
35         "do",    "else",     "if",     "while",
36         "break", "continue", "return", "goto",
37         "for",   "typedef",
38         
39         /* types */
40         "int",
41         "void",
42         "string",
43         "float",
44         "vector",
45         "entity",
46 };
47
48 struct lex_file *lex_open(FILE *fp) {
49         struct lex_file *lex = mem_a(sizeof(struct lex_file));
50         if (lex) {
51                 lex->file = fp;
52                 fseek(lex->file, 0, SEEK_END);
53                 lex->length = ftell(lex->file);
54                 lex->size   = lex->length; /* copy, this is never changed */
55                 fseek(lex->file, 0, SEEK_SET);
56                 lex->last = 0;
57                 
58                 memset(lex->peek, 0, sizeof(lex->peek));
59         }
60         return lex;
61 }
62
63 int lex_close(struct lex_file *file) {
64         int ret = -1;
65         if (file) {
66                 ret = fclose(file->file);
67                 mem_d(file);
68         }
69         return ret;
70 }
71
72 static void lex_addch(int ch, struct lex_file *file) {
73         if (file->current <  sizeof(file->lastok)-1)
74                 file->lastok[file->current++] = (char)ch;
75         if (file->current == sizeof(file->lastok)-1)
76                 file->lastok[file->current]   = (char)'\0';
77 }
78 static inline void lex_clear(struct lex_file *file) {
79         file->current = 0;
80 }
81
82 /*
83  * read in inget/unget character from a lexer stream.
84  * This doesn't play with file streams, the lexer has
85  * it's own internal state for this.
86  */
87 static int lex_inget(struct lex_file *file) {
88         file->length --;
89         if (file->last > 0)
90                 return file->peek[--file->last];
91         return fgetc(file->file);
92 }
93 static void lex_unget(int ch, struct lex_file *file) {
94         if (file->last < sizeof(file->peek))
95                 file->peek[file->last++] = ch;
96         file->length ++;
97 }
98
99 /*
100  * This is trigraph and digraph support, a feature not qc compiler
101  * supports.  Moving up in this world!
102  */
103 static int lex_trigraph(struct lex_file *file) {
104         int  ch;
105         if ((ch = lex_inget(file)) != '?') {
106                 lex_unget(ch, file);
107                 return '?';
108         }
109         
110         ch = lex_inget(file);
111         switch (ch) {
112                 case '(' : return '[' ;
113                 case ')' : return ']' ;
114                 case '/' : return '\\';
115                 case '\'': return '^' ;
116                 case '<' : return '{' ;
117                 case '>' : return '}' ;
118                 case '!' : return '|' ;
119                 case '-' : return '~' ;
120                 case '=' : return '#' ;
121                 default:
122                         lex_unget('?', file);
123                         lex_unget(ch , file);
124                         return '?';
125         }
126         return '?';
127 }
128 static int lex_digraph(struct lex_file *file, int first) {
129         int ch = lex_inget(file);
130         switch (first) {
131                 case '<':
132                         if (ch == '%') return '{';
133                         if (ch == ':') return '[';
134                         break;
135                 case '%':
136                         if (ch == '>') return '}';
137                         if (ch == ':') return '#';
138                         break;
139                 case ':':
140                         if (ch == '>') return ']';
141                         break;
142         }
143         
144         lex_unget(ch, file);
145         return first;
146 }
147
148 static int lex_getch(struct lex_file *file) {
149         int ch = lex_inget(file);
150         if (ch == '?')
151                 return lex_trigraph(file);
152         if (ch == '<' || ch == ':' || ch == '%')
153                 return lex_digraph (file, ch);
154                 
155         return ch;
156 }
157
158 static int lex_get(struct lex_file *file) {
159         int ch;
160         if (!isspace(ch = lex_getch(file)))
161                 return ch;
162         
163         /* skip over all spaces */
164         while (isspace(ch) && ch != '\n')
165                 ch = lex_getch(file);
166                 
167         if (ch == '\n') {
168                 file->line ++;
169                 return ch;
170         }
171                 
172         lex_unget(ch, file);
173         return ' ';
174 }
175
176 static int lex_skipchr(struct lex_file *file) {
177         int ch;
178         int it;
179         
180         lex_clear(file);
181         lex_addch('\'', file);
182         
183         for (it = 0; it < 2 && ((ch = lex_inget(file)) != '\''); it++) {
184                 lex_addch(ch, file);
185                 
186                 if (ch == '\n')
187                         return ERROR_LEX;
188                 if (ch == '\\')
189                         lex_addch(lex_getch(file), file);
190         }
191         lex_addch('\'', file);
192         lex_addch('\0', file);
193         
194         if (it > 2)
195                 return ERROR_LEX;
196                 
197         return LEX_CHRLIT;
198 }
199
200 static int lex_skipstr(struct lex_file *file) {
201         int ch;
202         lex_clear(file);
203         lex_addch('"', file);
204         
205         while ((ch = lex_getch(file)) != '"') {
206                 if (ch == '\n' || ch == EOF)
207                         return ERROR_LEX;
208                         
209                 lex_addch(ch, file);
210                 if (ch == '\\')
211                         lex_addch(lex_inget(file), file);
212         }
213         
214         lex_addch('"', file);
215         lex_addch('\0', file);
216         
217         return LEX_STRLIT;
218 }
219 static int lex_skipcmt(struct lex_file *file) {
220         int ch;
221         lex_clear(file);
222         ch = lex_getch(file);
223         
224         if (ch == '/') {
225                 lex_addch('/', file);
226                 lex_addch('/', file);
227                 
228                 while ((ch = lex_getch(file)) != '\n') {
229                         if (ch == '\\') {
230                                 lex_addch(ch, file);
231                                 lex_addch(lex_getch(file), file);
232                         } else {
233                                 lex_addch(ch, file);
234                         }
235                 }
236                 lex_addch('\0', file);
237                 return LEX_COMMENT;
238         }
239         
240         if (ch != '*') {
241                 lex_unget(ch, file);
242                 return '/';
243         }
244         
245         lex_addch('/', file);
246         
247         /* hate this */
248         do {
249                 lex_addch(ch, file);
250                 while ((ch = lex_getch(file)) != '*') {
251                         if (ch == EOF)
252                                 return error(ERROR_LEX, "malformatted comment at line %d", file->line);
253                         else
254                                 lex_addch(ch, file);
255                 }
256                 lex_addch(ch, file);
257         } while ((ch = lex_getch(file)) != '/');
258         
259         lex_addch('/',  file);
260         lex_addch('\0', file);
261         
262         return LEX_COMMENT;
263 }
264
265 static int lex_getsource(struct lex_file *file) {
266         int ch = lex_get(file);
267         
268         /* skip char/string/comment */
269         switch (ch) {
270                 case '\'': return lex_skipchr(file);
271                 case '"':  return lex_skipstr(file);
272                 case '/':  return lex_skipcmt(file);
273                 default:   return ch;
274         }
275 }
276
277 int lex_token(struct lex_file *file) {
278         int ch = lex_getsource(file);
279         int it;
280         
281         /* valid identifier */
282         if (ch > 0 && (ch == '_' || isalpha(ch))) {
283                 lex_clear(file);
284                 while (ch > 0 && (isalpha(ch) || ch == '_')) {
285                         lex_addch(ch, file);
286                         ch = lex_getsource(file);
287                 }
288                 lex_unget(ch,   file);
289                 lex_addch('\0', file);
290                 
291                 /* look inside the table for a keyword .. */
292                 for (it = 0; it < sizeof(lex_keywords)/sizeof(*lex_keywords); it++)
293                         if (!strncmp(file->lastok, lex_keywords[it], sizeof(lex_keywords[it])))
294                                 return it;
295                                 
296                 /* try the hashtable for typedefs? */
297                 if (typedef_find(file->lastok))
298                         for (it = 0; it < sizeof(lex_keywords)/sizeof(*lex_keywords); it++)
299                                 if (!strncmp(typedef_find(file->lastok)->name, lex_keywords[it], sizeof(lex_keywords[it])))
300                                         return it;
301                                 
302                 return LEX_IDENT;
303         }
304         return ch;
305 }
306
307 void lex_reset(struct lex_file *file) {
308         file->current = 0;
309         file->last    = 0;
310         file->length  = file->size;
311         fseek(file->file, 0, SEEK_SET);
312         
313         memset(file->peek,   0, sizeof(file->peek  ));
314         memset(file->lastok, 0, sizeof(file->lastok));
315 }
316
317 int lex_debug(struct lex_file *file) {
318         int list_do       = 0;
319         int list_else     = 0;
320         int list_if       = 0;
321         int list_while    = 0;
322         int list_break    = 0;
323         int list_continue = 0;
324         int list_return   = 0;
325         int list_goto     = 0;
326         int list_for      = 0;
327         int token         = 0;
328         printf("===========================\nTOKENS:   \n===========================\n");
329         while ((token = lex_token(file)) != ERROR_LEX && file->length >= 0) {
330                 if (token != -1) {
331                         switch (token) {
332                                 case 0: list_do      ++; break;
333                                 case 1: list_else    ++; break;
334                                 case 2: list_if      ++; break;
335                                 case 3: list_while   ++; break;
336                                 case 4: list_break   ++; break;
337                                 case 5: list_continue++; break;
338                                 case 6: list_return  ++; break;
339                                 case 7: list_goto    ++; break;
340                                 case 8: list_for     ++; break;
341                         }
342                 }
343                 if (token >= 33 && token <= 126)
344                         putchar(token);
345         }
346         printf("\n===========================\nBRANCHES \n===========================\n");
347         printf("\t if       % 8d\n", list_if);
348         printf("\t else     % 8d\n", list_else);
349         printf("===========================\nLOOPS      \n===========================\n");
350         printf("\t for      % 8d\n", list_for);
351         printf("\t while    % 8d\n", list_while);
352         printf("\t do       % 8d\n", list_do);
353         printf("===========================\nSTATEMENTS \n===========================\n");
354         printf("\t break    % 8d\n", list_break);
355         printf("\t continue % 8d\n", list_continue);
356         printf("\t return   % 8d\n", list_return);
357         printf("\t goto     % 8d\n", list_goto);
358         printf("===========================\nIDENTIFIERS\n===========================\n");
359         lex_reset(file);
360         while ((token = lex_token(file)) != ERROR_LEX && file->length >= 0)
361                 if (token == LEX_IDENT)
362                         printf("%s ", file->lastok);
363         fputc('\n', stdout);
364         lex_reset(file);
365         return 1;
366 }