From: Dale Weiler Date: Fri, 26 Jul 2013 16:28:57 +0000 (+0000) Subject: Merge branch 'master' into diagnostics X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=a6d1d4bd18323430ffc00ef6b9b579a7401c152a;p=xonotic%2Fgmqcc.git Merge branch 'master' into diagnostics Conflicts: gmqcc.h parser.c --- a6d1d4bd18323430ffc00ef6b9b579a7401c152a diff --cc diag.c index 78ad7a7,0000000..e7f9c46 mode 100644,000000..100644 --- a/diag.c +++ b/diag.c @@@ -1,280 -1,0 +1,282 @@@ +/* + * Copyright (C) 2012, 2013 + * Dale Weiler + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ ++#include ++ +#include "gmqcc.h" +#include "lexer.h" + +typedef struct { + const char *line; /* contents of the current line */ + size_t *tokens; /* stream of tokens */ + char **values; /* stream of values for tokens */ +} diagnostic_data_t; + +/* map> */ +static ht diagnostic_table = NULL; + +static void diagnostic_line(const char *file, size_t line, diagnostic_data_t ***read, size_t beg, size_t end) { + diagnostic_data_t **datas = NULL; + size_t feed = 0; + + if (!diagnostic_table) + diagnostic_table = util_htnew(1024); + + /* + * Build the data one line at a time if it doesn't already exists. + * We also lex one line at a time for consistency here. + */ + if (!(datas = (diagnostic_data_t**)util_htget(diagnostic_table, file))) { + lex_file *lexer = NULL; + char *next = NULL; + FILE *handle = fs_file_open(file, "r"); + size_t size = 0; + size_t tok = 0; + + /* + * Now process all data line per line from the file, while inserting + * the contents of each line into data.line, the token stream for + * each line into data.tokens, and the values that are associated + * with that given token into data.values. Then after one line is + * complete, push the data associated with it into the datas vector + * which will be stored alongside the hashtable. + */ + while (fs_file_getline(&next, &size, handle) != EOF) { + diagnostic_data_t *data = (diagnostic_data_t*)mem_a(sizeof(diagnostic_data_t)); + + data->tokens = NULL; + data->values = NULL; + data->line = util_strdup(next); + lexer = lex_open_string(next, strlen(next), file); + lexer->flags.preprocessing = true; /* enable whitespace */ + lexer->flags.mergelines = true; + + /* build token stream */ + while ((tok = lex_do(lexer)) != TOKEN_EOF) { + char *string = NULL; + char *claim = lexer->tok.value; + + for (; claim && *claim; claim ++) + vec_push(string, (*claim == '\t') ? ' ' : *claim); + + vec_push(string, '\0'); + vec_push(data->tokens, tok); + vec_push(data->values, string); + + /* prevent duplicated entries */ + memset(&lexer->tok, 0, sizeof(lexer->tok)); + } + + lex_close(lexer); + vec_push(datas, data); + } + + /*mem_d(data);*/ + util_htset(diagnostic_table, file, datas); + fs_file_close(handle); + } + + /* store the lines request back to the vector */ + if (line - beg + end > vec_size(datas)) { + beg = 1; + end = 1; + } + + for(feed = line - beg; feed < line - beg + end; ++feed) + vec_push((*read), datas[feed]); +} + +static void diagnostic_feed(const char *file, size_t line, size_t column, size_t beg, size_t end, bool marker, size_t diagnostic) { + diagnostic_data_t **read = NULL; + size_t feed = 0; + size_t space = 6; + size_t len = 0; + size_t itr = 0; + size_t tok = 0; + + /* get line */ + diagnostic_line(file, line, &read, beg, end); + + /* use old token stream to pretty the output */ + for (; feed < vec_size(read); feed++) { + con_err("%4d: ", line); + while ((tok = read[feed]->tokens[itr]) != TOKEN_EOL) { + switch (tok) { + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + con_err(con_color_err() ? "\033[1;33m%s\033[0m" : "%s", read[feed]->values[itr]); + break; + + case TOKEN_INTCONST: + case TOKEN_FLOATCONST: + con_err(con_color_err() ? "\033[1;32m%s\033[0m" : "%s", read[feed]->values[itr]); + break; + + case TOKEN_CHARCONST: + case TOKEN_STRINGCONST: + con_err(con_color_err() ? "\033[1;31m%s\033[0m" : "%s", read[feed]->values[itr]); + break; + + case TOKEN_EOF: + case TOKEN_ERROR: + case TOKEN_EOL: + /* ignore */ + break; + + default: + con_err("%s", read[feed]->values[itr]); + break; + }; + itr++; + } + itr = 0; + con_err("\n"); + } + + switch (diagnostic) { + case DIAGNOSTIC_EXPECTED_END: + for (; len < vec_size(vec_last(read)->values); len++) + space += strlen(vec_last(read)->values[len]); + + /* +/- 1 because of the ^ */ + len = space - column - 1; + space = column + 1; + break; + + case DIAGNOSTIC_EXPRESSION_CASE: + case DIAGNOSTIC_SEMICOLON: + for (; len < vec_size(vec_last(read)->values); len++) + space += strlen(vec_last(read)->values[len]); + + len = 1; + space -= beg - end; + break; + + case DIAGNOSTIC_EXPECTED: + /* + * use the column number here, it's the EASIEST method yet + * because it points to the exact space. + */ + space += column; + len = 0; + break; + + case DIAGNOSTIC_UNEXPECTED_TOKEN: + len = column; + break; + + case DIAGNOSTIC_UNEXPECTED_IDENT: + /* + * TODO: better way to determine the identifier. So far we + * "assume" it's the first one in the token stream, the issue + * with this, is it'ss possible that it isn't the first ident + * in the stream. In all likely hood, the parser itself should + * carry a "current token in line" count so we can simply use + * it as a index into vec_last(read)->tokens. This will also + * allow for other diagnostics involving unexpected tokens. + */ + for (itr = 0; len < vec_size(vec_last(read)->tokens); len++) { + if (vec_last(read)->tokens[len] == TOKEN_IDENT) + break; + space += strlen(vec_last(read)->values[len]); + } + len = 0; + break; + + default: + break; + } + + while (space --) con_err(" "); + while (len --) con_err("~"); + + con_err((marker) ? "^\n" : "\n"); + + vec_free(read); +} + + +static void diagnostic_destory_data(void *data) { + diagnostic_data_t **datas = (diagnostic_data_t **)data; + size_t i,j; + + for (i = 0; i < vec_size(datas); i++) { + vec_free(datas[i]->line); + + /* + * There is always the same number of tokens as + * values, one loop suffices. + */ + for (j = 0; i < vec_size(datas[i]->tokens); i++) { + mem_d(datas[i]->tokens[j]); + mem_d(datas[i]->values[j]); + } + + vec_free(datas[i]->tokens); + vec_free(datas[i]->values); + + mem_d(datas[i]); + } +} + +void diagnostic_destroy() { + if (!diagnostic_table) + return; + + util_htrem(diagnostic_table, diagnostic_destory_data); +} + +void diagnostic_calculate(const char *file, size_t line, size_t column, size_t diagnostic) { + size_t linebeg = 1; + size_t linecnt = 1; + bool marker = false; + + + switch (diagnostic) { + /* + * Semicolon reports error on nextline, which is why we need + * to increment the beginning line for diagnostics, and also + * enable the marker (to show where it's missing). + */ + case DIAGNOSTIC_SEMICOLON: + case DIAGNOSTIC_EXPRESSION_CASE: + case DIAGNOSTIC_EXPECTED_END: + linebeg++; + marker = true; + break; + + case DIAGNOSTIC_UNEXPECTED_TOKEN: + marker = false; + break; + + case DIAGNOSTIC_UNEXPECTED_IDENT: + case DIAGNOSTIC_EXPECTED: + marker = true; + break; + + /* Catches the DIAGNOSTIC_NULL and out of range case */ + default: + return; + } + + diagnostic_feed(file, line, column, linebeg, linecnt, marker, diagnostic); +} diff --cc gmqcc.h index f7fc39b,d6055db..9a073e2 --- a/gmqcc.h +++ b/gmqcc.h @@@ -785,9 -781,6 +781,9 @@@ int con_vout (const char *, va_list) int con_err (const char *, ...); int con_out (const char *, ...); - bool con_color_err(); - bool con_color_out(); ++bool con_color_err(void); ++bool con_color_out(void); + /* error/warning interface */ extern size_t compile_errors; extern size_t compile_Werrors; @@@ -1021,23 -1014,6 +1017,23 @@@ void ftepp_flush void ftepp_add_define (struct ftepp_s *ftepp, const char *source, const char *name); void ftepp_add_macro (struct ftepp_s *ftepp, const char *name, const char *value); +/*===================================================================*/ +/*============================= exec.c ==============================*/ +/*===================================================================*/ +enum { + DIAGNOSTIC_NULL, + DIAGNOSTIC_SEMICOLON, + DIAGNOSTIC_EXPRESSION_CASE, + DIAGNOSTIC_UNEXPECTED_IDENT, + DIAGNOSTIC_UNEXPECTED_TOKEN, + DIAGNOSTIC_EXPECTED, + DIAGNOSTIC_EXPECTED_END +}; + - void diagnostic_destroy(); ++void diagnostic_destroy(void); +void diagnostic_calculate(const char *file, size_t line, size_t column, size_t diagnostic); + + /*===================================================================*/ /*======================= main.c commandline ========================*/ /*===================================================================*/ diff --cc parser.c index bb30043,8a7cf87..2a233a1 --- a/parser.c +++ b/parser.c @@@ -6483,9 -6467,6 +6493,7 @@@ bool parser_finish(parser_t *parser, co return false; } } - + diagnostic_destroy(); - ir_builder_delete(ir); return retval; }