]> git.xonotic.org Git - xonotic/gmqcc.git/blob - lex.c
01b2ec73b0369398bf4fb625bbba16fdf89df5a1
[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                 return ch;
169         lex_unget(ch, file);
170         return ' ';
171 }
172
173 static int lex_skipchr(struct lex_file *file) {
174         int ch;
175         int it;
176         
177         lex_clear(file);
178         lex_addch('\'', file);
179         
180         for (it = 0; it < 2 && ((ch = lex_inget(file)) != '\''); it++) {
181                 lex_addch(ch, file);
182                 
183                 if (ch == '\n')
184                         return ERROR_LEX;
185                 if (ch == '\\')
186                         lex_addch(lex_getch(file), file);
187         }
188         lex_addch('\'', file);
189         lex_addch('\0', file);
190         
191         if (it > 2)
192                 return ERROR_LEX;
193                 
194         return LEX_CHRLIT;
195 }
196
197 static int lex_skipstr(struct lex_file *file) {
198         int ch;
199         lex_clear(file);
200         lex_addch('"', file);
201         
202         while ((ch = lex_getch(file)) != '"') {
203                 if (ch == '\n' || ch == EOF)
204                         return ERROR_LEX;
205                         
206                 lex_addch(ch, file);
207                 if (ch == '\\')
208                         lex_addch(lex_inget(file), file);
209         }
210         
211         lex_addch('"', file);
212         lex_addch('\0', file);
213         
214         return LEX_STRLIT;
215 }
216 static int lex_skipcmt(struct lex_file *file) {
217         int ch;
218         lex_clear(file);
219         ch = lex_getch(file);
220         
221         if (ch == '/') {
222                 lex_addch('/', file);
223                 lex_addch('/', file);
224                 
225                 while ((ch = lex_getch(file)) != '\n') {
226                         if (ch == '\\') {
227                                 lex_addch(ch, file);
228                                 lex_addch(lex_getch(file), file);
229                         } else {
230                                 lex_addch(ch, file);
231                         }
232                 }
233                 lex_addch('\0', file);
234                 return LEX_COMMENT;
235         }
236         
237         if (ch != '*') {
238                 lex_unget(ch, file);
239                 return '/';
240         }
241         
242         lex_addch('/', file);
243         
244         /* hate this */
245         do {
246                 lex_addch(ch, file);
247                 while ((ch = lex_getch(file)) != '*') {
248                         if (ch == EOF)
249                                 return error(ERROR_LEX, "malformatted comment at line %d", file->line);
250                         else
251                                 lex_addch(ch, file);
252                 }
253                 lex_addch(ch, file);
254         } while ((ch = lex_getch(file)) != '/');
255         
256         lex_addch('/',  file);
257         lex_addch('\0', file);
258         
259         return LEX_COMMENT;
260 }
261
262 static int lex_getsource(struct lex_file *file) {
263         int ch = lex_get(file);
264         
265         /* skip char/string/comment */
266         switch (ch) {
267                 case '\'': return lex_skipchr(file);
268                 case '"':  return lex_skipstr(file);
269                 case '/':  return lex_skipcmt(file);
270                 default:   return ch;
271         }
272 }
273
274 int lex_token(struct lex_file *file) {
275         int ch = lex_getsource(file);
276         int it;
277         
278         /* valid identifier */
279         if (ch > 0 && (ch == '_' || isalpha(ch))) {
280                 lex_clear(file);
281                 while (ch > 0 && (isalpha(ch) || ch == '_')) {
282                         lex_addch(ch, file);
283                         ch = lex_getsource(file);
284                 }
285                 lex_unget(ch,   file);
286                 lex_addch('\0', file);
287                 
288                 /* look inside the table for a keyword .. */
289                 for (it = 0; it < sizeof(lex_keywords)/sizeof(*lex_keywords); it++)
290                         if (!strncmp(file->lastok, lex_keywords[it], sizeof(lex_keywords[it])))
291                                 return it;
292                                 
293                 /* try the hashtable for typedefs? */
294                 if (typedef_find(file->lastok))
295                         for (it = 0; it < sizeof(lex_keywords)/sizeof(*lex_keywords); it++)
296                                 if (!strncmp(typedef_find(file->lastok)->name, lex_keywords[it], sizeof(lex_keywords[it])))
297                                         return it;
298                 
299                         
300                 return LEX_IDENT;
301         }
302         return ch;
303 }
304
305 void lex_reset(struct lex_file *file) {
306         file->current = 0;
307         file->last    = 0;
308         file->length  = file->size;
309         fseek(file->file, 0, SEEK_SET);
310         
311         memset(file->peek,   0, sizeof(file->peek  ));
312         memset(file->lastok, 0, sizeof(file->lastok));
313 }