]> git.xonotic.org Git - xonotic/gmqcc.git/blob - parse.c
3cbde9ef5009df31a33d2a76d2e539d38963ee39
[xonotic/gmqcc.git] / parse.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 <limits.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include "gmqcc.h"
28
29 /* compile-time constant for type constants */
30 typedef struct {
31         char *name;
32         int   type;
33         float value[3];
34         char *string; /* string value if constant is string literal */
35 } constant;
36 VECTOR_MAKE(constant, compile_constants);
37
38 void compile_constant_debug() {
39         int iter = 0;
40         for(; iter < compile_constants_elements; iter++) {
41                 constant *c = &compile_constants_data[iter];
42                 switch(c->type) {
43                         case TYPE_FLOAT:  printf("constant: %s FLOAT   %f\n",       c->name, c->value[0]);                           break;
44                         case TYPE_VECTOR: printf("constant: %s VECTOR {%f,%f,%f}\n",c->name, c->value[0], c->value[1], c->value[2]); break;
45                         case TYPE_STRING: printf("constant: %s STRING  %s\n",       c->name, c->string); break;
46                         case TYPE_VOID:   printf("constant: %s VOID    %s\n",       c->name, c->string); break;
47                 }
48         }
49 }
50
51 /*
52  * Generates a parse tree out of the lexees generated by the lexer.  This
53  * is where the tree is built.  This is where valid check is performed.
54  */
55 int parse_gen(struct lex_file *file) {  
56         int     token = 0;
57         while ((token = lex_token(file)) != ERROR_LEX      && \
58                     token                    != ERROR_COMPILER && \
59                     token                    != ERROR_INTERNAL && \
60                     token                    != ERROR_PARSE    && \
61                     token                    != ERROR_PREPRO   && file->length >= 0) {
62                 switch (token) {
63                         case TOKEN_TYPEDEF: {
64                                 char *f; /* from */
65                                 char *t; /* to   */
66                                 
67                                 token = lex_token(file); 
68                                 token = lex_token(file); f = util_strdup(file->lastok);
69                                 token = lex_token(file); 
70                                 token = lex_token(file); t = util_strdup(file->lastok);
71                                 
72                                 typedef_add(f, t);
73                                 mem_d(f);
74                                 mem_d(t);
75                                 
76                                 token = lex_token(file);
77                                 if (token == ' ')
78                                         token = lex_token(file);
79                                         
80                                 if (token != ';')
81                                         error(ERROR_PARSE, "%s:%d Expected a `;` at end of typedef statement\n", file->name, file->line);
82                                         
83                                 token = lex_token(file);
84                                 break;
85                         }
86                         
87                         case TOKEN_VOID:   goto fall;
88                         case TOKEN_STRING: goto fall;
89                         case TOKEN_VECTOR: goto fall;
90                         case TOKEN_ENTITY: goto fall;
91                         case TOKEN_FLOAT:  goto fall;
92                         {
93                         fall:;
94                                 char *name = NULL;
95                                 int   type = token; /* story copy */
96                                 
97                                 /* skip over space */
98                                 token = lex_token(file);
99                                 if (token == ' ')
100                                         token = lex_token(file);
101                                 
102                                 /* save name */
103                                 name = util_strdup(file->lastok);
104                                 
105                                 /* skip spaces */
106                                 token = lex_token(file);
107                                 if (token == ' ')
108                                         token = lex_token(file);
109                                         
110                                 if (token == ';') {
111                                         /*
112                                          * Definitions go to the defs table, they don't have
113                                          * any sort of data with them yet.
114                                          */
115                                 } else if (token == '=') {
116                                         token = lex_token(file);
117                                         if (token == ' ')
118                                                 token = lex_token(file);
119                                         
120                                         /* strings are in file->lastok */
121                                         switch (type) {
122                                                 case TOKEN_VOID:
123                                                         return error(ERROR_PARSE, "%s:%d Cannot assign value to type void\n", file->name, file->line);
124                                                         
125                                                 /* TODO: Validate (end quote), strip quotes for constant add, name constant */
126                                                 case TOKEN_STRING:
127                                                         if (*file->lastok != '"')
128                                                                 error(ERROR_PARSE, "%s:%d Expected a '\"' (quote) for string constant\n", file->name, file->line);
129                                                         /* add the compile-time constant */
130                                                         compile_constants_add((constant){
131                                                                 .name   = util_strdup(name),
132                                                                 .type   = TYPE_STRING,
133                                                                 .value  = {0,0,0},
134                                                                 .string = util_strdup(file->lastok)
135                                                         });
136                                                         break;
137                                                 /* TODO: name constant, old qc vec literals, whitespace fixes, name constant */
138                                                 case TOKEN_VECTOR: {
139                                                         float compile_calc_x = 0;
140                                                         float compile_calc_y = 0;
141                                                         float compile_calc_z = 0;
142                                                         int   compile_calc_d = 0; /* dot?        */
143                                                         int   compile_calc_s = 0; /* sign (-, +) */
144                                                         
145                                                         char  compile_data[1024];
146                                                         char *compile_eval = compile_data;
147                                                         
148                                                         if (token != '{')
149                                                                 error(ERROR_PARSE, "%s:%d Expected initializer list `{`,`}` for vector constant\n", file->name, file->line);    
150                                                         
151                                                         /*
152                                                          * This parses a single vector element: x,y & z.  This will handle all the
153                                                          * complicated mechanics of a vector, and can be extended as well.  This
154                                                          * is a rather large macro, and is #undef'd after it's use below.
155                                                          */
156                                                         #define PARSE_VEC_ELEMENT(NAME, BIT)                                                                                                                                   \
157                                                             token = lex_token(file);                                                                                                                                           \
158                                                             if (token == ' ')                                                                                                                                                  \
159                                                                 token = lex_token(file);                                                                                                                                       \
160                                                             if (token == '.')                                                                                                                                                  \
161                                                                 compile_calc_d = 1;                                                                                                                                            \
162                                                             if (!isdigit(token) && !compile_calc_d && token != '+' && token != '-')                                                                                            \
163                                                                 error(ERROR_PARSE,"%s:%d Invalid constant initializer element %c for vector, must be numeric\n", file->name, file->line, NAME);                                \
164                                                             if (token == '+')                                                                                                                                                  \
165                                                                 compile_calc_s = '+';                                                                                                                                          \
166                                                             if (token == '-' && !compile_calc_s)                                                                                                                               \
167                                                                 compile_calc_s = '-';                                                                                                                                          \
168                                                             while (isdigit(token) || token == '.' || token == '+' || token == '-') {                                                                                           \
169                                                                 *compile_eval++ = token;                                                                                                                                       \
170                                                                 token           = lex_token(file);                                                                                                                             \
171                                                                 if (token == '.' && compile_calc_d) {                                                                                                                          \
172                                                                     error(ERROR_PARSE, "%s:%d Invalid constant initializer element %c for vector, must be numeric.\n", file->name, file->line, NAME);                          \
173                                                                     token = lex_token(file);                                                                                                                                   \
174                                                                 }                                                                                                                                                              \
175                                                                 if ((token == '-' || token == '+') && compile_calc_s) {                                                                                                        \
176                                                                     error(ERROR_PARSE, "%s:%d Invalid constant initializer sign for vector element %c\n", file->name, file->line, NAME);                                       \
177                                                                     token = lex_token(file);                                                                                                                                   \
178                                                                 }                                                                                                                                                              \
179                                                                 else if (token == '.' && !compile_calc_d)                                                                                                                      \
180                                                                     compile_calc_d = 1;                                                                                                                                        \
181                                                                 else if (token == '-' && !compile_calc_s)                                                                                                                      \
182                                                                     compile_calc_s = '-';                                                                                                                                      \
183                                                                 else if (token == '+' && !compile_calc_s)                                                                                                                      \
184                                                                     compile_calc_s = '+';                                                                                                                                      \
185                                                             }                                                                                                                                                                  \
186                                                             if (token == ' ')                                                                                                                                                  \
187                                                                 token = lex_token(file);                                                                                                                                       \
188                                                             if (NAME != 'z') {                                                                                                                                                 \
189                                                                 if (token != ',' && token != ' ')                                                                                                                              \
190                                                                     error(ERROR_PARSE, "%s:%d invalid constant initializer element %c for vector (missing spaces, or comma delimited list?)\n", file->name, file->line, NAME); \
191                                                             } else if (token != '}') {                                                                                                                                         \
192                                                                 error(ERROR_PARSE, "%s:%d Expected `}` on end of constant initialization for vector\n", file->name, file->line);                                               \
193                                                             }                                                                                                                                                                  \
194                                                             compile_calc_##BIT = atof(compile_data);                                                                                                                           \
195                                                             compile_calc_d = 0;                                                                                                                                                \
196                                                             compile_calc_s = 0;                                                                                                                                                \
197                                                             compile_eval   = &compile_data[0];                                                                                                                                 \
198                                                             memset(compile_data, 0, sizeof(compile_data))
199                                                         
200                                                         /*
201                                                          * Parse all elements using the macro above.
202                                                          * We must undef the macro afterwards.
203                                                          */
204                                                         PARSE_VEC_ELEMENT('x', x);
205                                                         PARSE_VEC_ELEMENT('y', y);
206                                                         PARSE_VEC_ELEMENT('z', z);
207                                                         #undef PARSE_VEC_ELEMENT
208                                                         
209                                                         /* Check for the semi-colon... */
210                                                         token = lex_token(file);
211                                                         if (token == ' ')
212                                                                 token = lex_token(file);
213                                                         if (token != ';')
214                                                                 error(ERROR_PARSE, "%s:%d Expected `;` on end of constant initialization for vector\n", file->name, file->line);
215                                                                 
216                                                         /* add the compile-time constant */
217                                                         compile_constants_add((constant){
218                                                                 .name   = util_strdup(name),
219                                                                 .type   = TYPE_VECTOR,
220                                                                 .value  = {
221                                                                         [0] = compile_calc_x,
222                                                                         [1] = compile_calc_y,
223                                                                         [2] = compile_calc_z
224                                                                 },
225                                                                 .string = NULL
226                                                         });
227                                                         break;
228                                                 }
229                                                         
230                                                 case TOKEN_ENTITY:
231                                                 case TOKEN_FLOAT: /*TODO: validate, constant generation, name constant */
232                                                         if (!isdigit(token))
233                                                                 error(ERROR_PARSE, "%s:%d Expected numeric constant for float constant\n");
234                                                         compile_constants_add((constant){
235                                                                 .name   = util_strdup(name),
236                                                                 .type   = TOKEN_FLOAT,
237                                                                 .value  = {0,0,0},
238                                                                 .string = NULL
239                                                         });
240                                                         break;
241                                         }
242                                 } else if (token == '(') {
243                                         printf("FUNCTION ??\n");
244                                 }
245                                 mem_d(name);
246                         }
247                                 
248                         /*
249                          * From here down is all language punctuation:  There is no
250                          * need to actual create tokens from these because they're already
251                          * tokenized as these individual tokens (which are in a special area
252                          * of the ascii table which doesn't conflict with our other tokens
253                          * which are higer than the ascii table.)
254                          */
255                         case '#':
256                                 token = lex_token(file); /* skip '#' */
257                                 if (token == ' ')
258                                         token = lex_token(file);
259                                 /*
260                                  * If we make it here we found a directive, the supported
261                                  * directives so far are #include.
262                                  */
263                                 if (strncmp(file->lastok, "include", sizeof("include")) == 0) {
264                                         /*
265                                          * We only suport include " ", not <> like in C (why?)
266                                          * because the latter is silly.
267                                          */
268                                         while (*file->lastok != '"' && token != '\n')
269                                                 token = lex_token(file);
270                                         if (token == '\n')
271                                                 return error(ERROR_PARSE, "%d: Invalid use of include preprocessor directive: wanted #include \"file.h\"\n", file->line-1);
272                                                 
273                                         char            *copy = util_strdup(file->lastok);
274                                         struct lex_file *next = lex_include(file,   copy);
275                                         
276                                         if (!next) {
277                                                 error(ERROR_INTERNAL, "Include subsystem failure\n");
278                                                 exit (-1);
279                                         }
280                                         compile_constants_add((constant) {
281                                                         .name   = "#include",
282                                                         .type   = TYPE_VOID,
283                                                         .value  = {0,0,0},
284                                                         .string = copy
285                                         });
286                                         parse_gen(next);
287                                         mem_d    (copy);
288                                         lex_close(next);
289                                 }
290                                 /* skip all tokens to end of directive */
291                                 while (token != '\n')
292                                         token = lex_token(file);
293                                 break;
294                                 
295                         case LEX_IDENT:
296                                 token = lex_token(file);
297                                 break;
298                 }
299         }
300         compile_constant_debug();
301         lex_reset(file);
302         return 1;
303 }