2 * Copyright (C) 2012, 2013
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:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
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
27 const char *line; /* contents of the current line */
28 size_t *tokens; /* stream of tokens */
29 char **values; /* stream of values for tokens */
32 /* map<const char *, vector<diagnostic_data_t>> */
33 static ht diagnostic_table = NULL;
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;
39 if (!diagnostic_table)
40 diagnostic_table = util_htnew(1024);
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.
46 if (!(datas = (diagnostic_data_t**)util_htget(diagnostic_table, file))) {
47 lex_file *lexer = NULL;
49 FILE *handle = fs_file_open(file, "r");
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.
61 while (fs_file_getline(&next, &size, handle) != EOF) {
62 diagnostic_data_t *data = (diagnostic_data_t*)mem_a(sizeof(diagnostic_data_t));
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;
71 /* build token stream */
72 while ((tok = lex_do(lexer)) != TOKEN_EOF) {
74 char *claim = lexer->tok.value;
76 for (; claim && *claim; claim ++)
77 vec_push(string, (*claim == '\t') ? ' ' : *claim);
79 vec_push(string, '\0');
80 vec_push(data->tokens, tok);
81 vec_push(data->values, string);
83 /* prevent duplicated entries */
84 memset(&lexer->tok, 0, sizeof(lexer->tok));
88 vec_push(datas, data);
92 util_htset(diagnostic_table, file, datas);
93 fs_file_close(handle);
96 /* store the lines request back to the vector */
97 if (line - beg + end > vec_size(datas)) {
102 for(feed = line - beg; feed < line - beg + end; ++feed)
103 vec_push((*read), datas[feed]);
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;
115 diagnostic_line(file, line, &read, beg, end);
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) {
124 con_err(con_color_err() ? "\033[1;33m%s\033[0m" : "%s", read[feed]->values[itr]);
128 case TOKEN_FLOATCONST:
129 con_err(con_color_err() ? "\033[1;32m%s\033[0m" : "%s", read[feed]->values[itr]);
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]);
144 con_err("%s", read[feed]->values[itr]);
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]);
163 case DIAGNOSTIC_EXPECTED:
165 * use the column number here, it's the EASIEST method yet
166 * because it points to the exact space.
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)
176 space += strlen(vec_last(read)->values[len]);
185 while (space --) con_err(" ");
186 while (len --) con_err("~");
188 con_err((marker) ? "^\n" : "\n");
194 static void diagnostic_destory_data(void *data) {
195 diagnostic_data_t **datas = (diagnostic_data_t **)data;
198 for (i = 0; i < vec_size(datas); i++) {
199 vec_free(datas[i]->line);
202 * There is always the same number of tokens as
203 * values, one loop suffices.
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]);
210 vec_free(datas[i]->tokens);
211 vec_free(datas[i]->values);
217 void diagnostic_destroy() {
218 if (!diagnostic_table)
221 util_htrem(diagnostic_table, diagnostic_destory_data);
224 void diagnostic_calculate(const char *file, size_t line, size_t column, size_t diagnostic) {
230 switch (diagnostic) {
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).
236 case DIAGNOSTIC_SEMICOLON:
241 case DIAGNOSTIC_EXPRESSION_CASE:
246 case DIAGNOSTIC_EXPECTED:
250 case DIAGNOSTIC_UNEXPECTED_IDENT:
254 /* Catches the DIAGNOSTIC_NULL and out of range case */
259 diagnostic_feed(file, line, column, linebeg, linecnt, marker, diagnostic);