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