]> git.xonotic.org Git - xonotic/gmqcc.git/blob - diag.c
Added diagnostic for expected tokens, a test, and a testsuite underflow bug.
[xonotic/gmqcc.git] / diag.c
1 /*
2  * Copyright (C) 2012, 2013
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 #include "lexer.h"
25
26 typedef struct {
27     const char *line;    /* contents of the current line */
28     size_t     *tokens;  /* stream of tokens             */
29     char      **values;  /* stream of values for tokens  */
30 } diagnostic_data_t;
31
32 /* map<const char *, vector<diagnostic_data_t>> */
33 static ht diagnostic_table = NULL;
34
35 static void diagnostic_line(const char *file, size_t line, diagnostic_data_t ***read, size_t beg, size_t end) {
36     diagnostic_data_t  **datas = NULL;
37     size_t               feed  = 0;
38
39     if (!diagnostic_table)
40          diagnostic_table = util_htnew(1024);
41
42     /*
43      * Build the data one line at a time if it doesn't already exists.
44      * We also lex one line at a time for consistency here.
45      */
46     if (!(datas = (diagnostic_data_t**)util_htget(diagnostic_table, file))) {
47         lex_file          *lexer  = NULL;
48         char              *next   = NULL;
49         FILE              *handle = fs_file_open(file, "r");
50         size_t             size   = 0;
51         size_t             tok    = 0;
52
53         /*
54          * Now process all data line per line from the file, while inserting
55          * the contents of each line into data.line, the token stream for
56          * each line into data.tokens, and the values that are associated
57          * with that given token into data.values. Then after one line is
58          * complete, push the data associated with it into the datas vector
59          * which will be stored alongside the hashtable.
60          */
61         while (fs_file_getline(&next, &size, handle) != EOF) {
62             diagnostic_data_t *data    = (diagnostic_data_t*)mem_a(sizeof(diagnostic_data_t));
63             
64             data->tokens               = NULL;
65             data->values               = NULL;
66             data->line                 = util_strdup(next);
67             lexer                      = lex_open_string(next, strlen(next), file);
68             lexer->flags.preprocessing = true; /* enable whitespace */
69             lexer->flags.mergelines    = true;
70             
71             /* build token stream */
72             while ((tok = lex_do(lexer)) != TOKEN_EOF) {
73                 char *string = NULL;
74                 char *claim  = lexer->tok.value;
75                 
76                 for (; claim && *claim; claim ++)
77                     vec_push(string, (*claim == '\t') ? ' ' : *claim);
78                     
79                 vec_push(string, '\0');
80                 vec_push(data->tokens, tok);
81                 vec_push(data->values, string);
82                 
83                 /* prevent duplicated entries */
84                 memset(&lexer->tok, 0, sizeof(lexer->tok));
85             }
86             
87             lex_close(lexer);
88             vec_push(datas, data);
89         }
90         
91         /*mem_d(data);*/
92         util_htset(diagnostic_table, file, datas);
93         fs_file_close(handle);
94     }
95
96     /* store the lines request back to the vector */
97     if (line - beg + end > vec_size(datas)) {
98         beg = 1;
99         end = 1;
100     }
101
102     for(feed = line - beg; feed < line - beg + end; ++feed)
103         vec_push((*read), datas[feed]);
104 }
105
106 static void diagnostic_feed(const char *file, size_t line, size_t column, size_t beg, size_t end, bool marker, size_t diagnostic) {
107     diagnostic_data_t  **read  = NULL;
108     size_t               feed  = 0;
109     size_t               space = 6;
110     size_t               len   = 0;
111     size_t               itr   = 0;
112     size_t               tok   = 0;
113
114     /* get line */
115     diagnostic_line(file, line, &read, beg, end);
116
117     /* use old token stream to pretty the output */
118     for (; feed < vec_size(read); feed++) {
119         con_err("%4d: ", line);
120         while ((tok = read[feed]->tokens[itr]) != TOKEN_EOL) {
121             switch (tok) {
122                 case TOKEN_TYPENAME:
123                 case TOKEN_KEYWORD:
124                     con_err(con_color_err() ? "\033[1;33m%s\033[0m" : "%s", read[feed]->values[itr]);
125                     break;
126
127                 case TOKEN_INTCONST:
128                 case TOKEN_FLOATCONST:
129                     con_err(con_color_err() ? "\033[1;32m%s\033[0m" : "%s", read[feed]->values[itr]);
130                     break;
131
132                 case TOKEN_CHARCONST:
133                 case TOKEN_STRINGCONST:
134                     con_err(con_color_err() ? "\033[1;31m%s\033[0m" : "%s", read[feed]->values[itr]);
135                     break;
136
137                 case TOKEN_EOF:
138                 case TOKEN_ERROR:
139                 case TOKEN_EOL:
140                     /* ignore */
141                     break;
142                     
143                 default:
144                     con_err("%s", read[feed]->values[itr]);
145                     break;
146             };
147             itr++;
148         }
149         itr = 0;
150         con_err("\n");
151     }
152     
153     switch (diagnostic) {
154         case DIAGNOSTIC_EXPRESSION_CASE:
155         case DIAGNOSTIC_SEMICOLON:
156             for (; len < vec_size(vec_last(read)->values); len++)
157                 space += strlen(vec_last(read)->values[len]);
158                 
159             len    = 1;
160             space -= beg - end;
161             break;
162             
163         case DIAGNOSTIC_EXPECTED:
164             /*
165              * use the column number here, it's the EASIEST method yet
166              * because it points to the exact space.
167              */
168             space += column;
169             len    = 0;
170             break;
171             
172         case DIAGNOSTIC_UNEXPECTED_IDENT:
173             for (itr = 0; len < vec_size(vec_last(read)->tokens); len++) {
174                 if (vec_last(read)->tokens[len] == TOKEN_IDENT)
175                     break;
176                 space += strlen(vec_last(read)->values[len]);
177             }
178             len = 0;
179             break;
180             
181         default:
182             break;
183     }
184
185     while (space --) con_err(" ");
186     while (len   --) con_err("~");
187
188     con_err((marker) ? "^\n" : "\n"); 
189
190     vec_free(read);
191 }
192
193
194 static void diagnostic_destory_data(void *data) {
195     diagnostic_data_t **datas = (diagnostic_data_t **)data;
196     size_t              i,j;
197     
198     for (i = 0; i < vec_size(datas); i++) {
199         vec_free(datas[i]->line);
200         
201         /*
202          * There is always the same number of tokens as
203          * values, one loop suffices.
204          */
205         for (j = 0; i < vec_size(datas[i]->tokens); i++) {
206             mem_d(datas[i]->tokens[j]);
207             mem_d(datas[i]->values[j]);
208         }
209         
210         vec_free(datas[i]->tokens);
211         vec_free(datas[i]->values);
212         
213         mem_d(datas[i]);
214     }
215 }
216
217 void diagnostic_destroy() {
218     if (!diagnostic_table)
219         return;
220
221     util_htrem(diagnostic_table, diagnostic_destory_data);
222 }
223
224 void diagnostic_calculate(const char *file, size_t line, size_t column, size_t diagnostic) {
225     size_t linebeg = 1;
226     size_t linecnt = 1;
227     bool   marker  = false;
228
229
230     switch (diagnostic) {
231         /*
232          * Semicolon reports error on nextline, which is why we need
233          * to increment the beginning line for diagnostics, and also
234          * enable the marker (to show where it's missing).
235          */
236         case DIAGNOSTIC_SEMICOLON:
237             linebeg++;
238             marker = true;
239             break;
240             
241         case DIAGNOSTIC_EXPRESSION_CASE:
242             linebeg++;
243             marker = true;
244             break;
245
246         case DIAGNOSTIC_EXPECTED:
247             marker = true;
248             break;
249             
250         case DIAGNOSTIC_UNEXPECTED_IDENT:
251             marker = true;
252             break;
253
254         /* Catches the DIAGNOSTIC_NULL and out of range case */
255         default:
256             return;
257     }
258
259     diagnostic_feed(file, line, column, linebeg, linecnt, marker, diagnostic);
260 }