]> de.git.xonotic.org Git - xonotic/gmqcc.git/blob - lex.c
More parsing & parse tree
[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",
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                 return ch;
169                 
170         lex_unget(ch, file);
171         return ' ';
172 }
173
174 static int lex_skipchr(struct lex_file *file) {
175         int ch;
176         int it;
177         
178         lex_clear(file);
179         lex_addch('\'', file);
180         
181         for (it = 0; it < 2 && ((ch = lex_inget(file)) != '\''); it++) {
182                 lex_addch(ch, file);
183                 
184                 if (ch == '\n')
185                         return ERROR_LEX;
186                 if (ch == '\\')
187                         lex_addch(lex_getch(file), file);
188         }
189         lex_addch('\'', file);
190         lex_addch('\0', file);
191         
192         if (it > 2)
193                 return ERROR_LEX;
194                 
195         return LEX_CHRLIT;
196 }
197
198 static int lex_skipstr(struct lex_file *file) {
199         int ch;
200         lex_clear(file);
201         lex_addch('"', file);
202         
203         while ((ch = lex_getch(file)) != '"') {
204                 if (ch == '\n' || ch == EOF)
205                         return ERROR_LEX;
206                         
207                 lex_addch(ch, file);
208                 if (ch == '\\')
209                         lex_addch(lex_inget(file), file);
210         }
211         
212         lex_addch('"', file);
213         lex_addch('\0', file);
214         
215         return LEX_STRLIT;
216 }
217 static int lex_skipcmt(struct lex_file *file) {
218         int ch;
219         lex_clear(file);
220         ch = lex_getch(file);
221         
222         if (ch == '/') {
223                 lex_addch('/', file);
224                 lex_addch('/', file);
225                 
226                 while ((ch = lex_getch(file)) != '\n') {
227                         if (ch == '\\') {
228                                 lex_addch(ch, file);
229                                 lex_addch(lex_getch(file), file);
230                         } else {
231                                 lex_addch(ch, file);
232                         }
233                 }
234                 lex_addch('\0', file);
235                 return LEX_COMMENT;
236         }
237         
238         if (ch != '*') {
239                 lex_unget(ch, file);
240                 return '/';
241         }
242         
243         lex_addch('/', file);
244         
245         /* hate this */
246         do {
247                 lex_addch(ch, file);
248                 while ((ch = lex_getch(file)) != '*') {
249                         if (ch == EOF)
250                                 return error(ERROR_LEX, "malformatted comment", " ");
251                         else
252                                 lex_addch(ch, file);
253                 }
254                 lex_addch(ch, file);
255         } while ((ch = lex_getch(file)) != '/');
256         
257         lex_addch('/',  file);
258         lex_addch('\0', file);
259         
260         return LEX_COMMENT;
261 }
262
263 static int lex_getsource(struct lex_file *file) {
264         int ch = lex_get(file);
265         
266         /* skip char/string/comment */
267         switch (ch) {
268                 case '\'': return lex_skipchr(file);
269                 case '"':  return lex_skipstr(file);
270                 case '/':  return lex_skipcmt(file);
271                 default:   return ch;
272         }
273 }
274
275 int lex_token(struct lex_file *file) {
276         int ch = lex_getsource(file);
277         int it;
278         
279         /* valid identifier */
280         if (ch > 0 && (ch == '_' || isalpha(ch))) {
281                 lex_clear(file);
282                 while (ch > 0 && (isalpha(ch) || ch == '_')) {
283                         lex_addch(ch, file);
284                         ch = lex_getsource(file);
285                 }
286                 lex_unget(ch,   file);
287                 lex_addch('\0', file);
288                 
289                 /* look inside the table for a keyword .. */
290                 for (it = 0; it < sizeof(lex_keywords)/sizeof(*lex_keywords); it++)
291                         if (!strncmp(file->lastok, lex_keywords[it], sizeof(lex_keywords[it])))
292                                 return it;
293                                 
294                 return LEX_IDENT;
295         }
296         return ch;
297 }
298
299 void lex_reset(struct lex_file *file) {
300         file->current = 0;
301         file->last    = 0;
302         file->length  = file->size;
303         fseek(file->file, 0, SEEK_SET);
304         
305         memset(file->peek,   0, sizeof(file->peek  ));
306         memset(file->lastok, 0, sizeof(file->lastok));
307 }
308
309 int lex_debug(struct lex_file *file) {
310         int list_do       = 0;
311         int list_else     = 0;
312         int list_if       = 0;
313         int list_while    = 0;
314         int list_break    = 0;
315         int list_continue = 0;
316         int list_return   = 0;
317         int list_goto     = 0;
318         int list_for      = 0;
319         int token         = 0;
320         printf("===========================\nTOKENS:   \n===========================\n");
321         while ((token = lex_token(file)) != ERROR_LEX && file->length >= 0) {
322                 if (token != -1) {
323                         switch (token) {
324                                 case 0: list_do      ++; break;
325                                 case 1: list_else    ++; break;
326                                 case 2: list_if      ++; break;
327                                 case 3: list_while   ++; break;
328                                 case 4: list_break   ++; break;
329                                 case 5: list_continue++; break;
330                                 case 6: list_return  ++; break;
331                                 case 7: list_goto    ++; break;
332                                 case 8: list_for     ++; break;
333                         }
334                 }
335                 if (token >= 33 && token <= 126)
336                         putchar(token);
337         }
338         printf("\n===========================\nBRANCHES \n===========================\n");
339         printf("\t if       % 8d\n", list_if);
340         printf("\t else     % 8d\n", list_else);
341         printf("===========================\nLOOPS      \n===========================\n");
342         printf("\t for      % 8d\n", list_for);
343         printf("\t while    % 8d\n", list_while);
344         printf("\t do       % 8d\n", list_do);
345         printf("===========================\nSTATEMENTS \n===========================\n");
346         printf("\t break    % 8d\n", list_break);
347         printf("\t continue % 8d\n", list_continue);
348         printf("\t return   % 8d\n", list_return);
349         printf("\t goto     % 8d\n", list_goto);
350         printf("===========================\nIDENTIFIERS\n===========================\n");
351         lex_reset(file);
352         while ((token = lex_token(file)) != ERROR_LEX && file->length >= 0)
353                 if (token == LEX_IDENT)
354                         printf("%s ", file->lastok);
355         fputc('\n', stdout);
356         lex_reset(file);
357         return 1;
358 }