diff options
Diffstat (limited to 'src/mds-kbdc/make-tree.c')
-rw-r--r-- | src/mds-kbdc/make-tree.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/src/mds-kbdc/make-tree.c b/src/mds-kbdc/make-tree.c new file mode 100644 index 0000000..80faf5f --- /dev/null +++ b/src/mds-kbdc/make-tree.c @@ -0,0 +1,164 @@ +/** + * mds — A micro-display server + * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include "make-tree.h" + +#include "raw-data.h" + +#include <libmdsserver/macros.h> + +#include <limits.h> +#include <stdlib.h> +#include <libgen.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> + + + +/** + * Parse a file into a syntex tree + * + * @param filename The filename of the file to parse + * @param result Output parameter for the root of the tree, `NULL` if -1 is returned + * @param errors `NULL`-terminated list of found error, `NULL` if no errors were found or if -1 is returned + * @return -1 if an error occursed that cannot be stored in `*errors`, zero otherwise + */ +int parse_to_tree(const char* restrict filename, mds_kbdc_tree_t** restrict result, + mds_kbdc_parse_error_t*** restrict errors) +{ + mds_kbdc_parse_error_t** old_errors = NULL; + char* pathname; + source_code_t source_code; + size_t errors_size = 0; + size_t errors_ptr = 0; + size_t line_i, line_n; + int saved_errno; + + *errors = NULL; + source_code_initialise(&source_code); + + /* Get a non-relative pathname for the file, relative filenames + * can be misleading as the program can have changed working + * directroy to be able to resolve filenames. */ + pathname = realpath(filename, NULL); + fail_if (pathname == NULL); + + /* Check that the file exists and can be read. */ + if (access(pathname, R_OK) < 0) + { + saved_errno = errno; + fail_if (xmalloc(*errors, 2, mds_kbdc_parse_error_t*)); + fail_if (xmalloc(**errors, 1, mds_kbdc_parse_error_t)); + (*errors)[1] = NULL; + + (**errors)->severity = MDS_KBDC_PARSE_ERROR_ERROR; + (**errors)->error_is_in_file = 0; + (**errors)->pathname = pathname, pathname = NULL; + (**errors)->line = 0; + (**errors)->start = 0; + (**errors)->end = 0; + (**errors)->code = NULL; + (**errors)->description = strdup(strerror(saved_errno)); + fail_if ((**errors)->description == NULL); + return 0; + } + + /* Read the file and simplify it a bit. */ + fail_if (read_source_lines(pathname, &source_code) < 0); + /* TODO '\t':s should be expanded into ' ':s. */ + + for (line_i = 0, line_n = source_code.line_count; line_i < line_n; line_i++) + { + char* line = source_code.lines[line_i]; + char* end; + char prev_end_char; + + while (*line && (*line == ' ')) + line++; + end = strchrnul(line, ' '); + if (end == line) + continue; + prev_end_char = *end; + *end = '\0'; + + if (!strcmp(line, "information")) ; + else if (!strcmp(line, "language")) ; + else if (!strcmp(line, "country")) ; + else if (!strcmp(line, "variant")) ; + else if (!strcmp(line, "include")) ; + else if (!strcmp(line, "function")) ; + else if (!strcmp(line, "macro")) ; + else if (!strcmp(line, "assumption")) ; + else if (!strcmp(line, "if")) ; + else if (!strcmp(line, "else")) ; + else if (!strcmp(line, "for")) ; + else if (!strcmp(line, "end")) ; + else if (!strcmp(line, "return")) ; + else if (!strcmp(line, "continue")) ; + else if (!strcmp(line, "break")) ; + else if (!strcmp(line, "let")) ; + else if (!strcmp(line, "have")) ; + else if (!strcmp(line, "have_chars")) ; + else if (!strcmp(line, "have_range")) ; + else if (strchr("\"<([", *line) || strchr(line, '(')) ; + else + { + mds_kbdc_parse_error_t* error; + + if (errors_ptr + 1 >= errors_size) + { + errors_size = errors_size ? (errors_size << 1) : 2; + fail_if (xxrealloc(old_errors, *errors, errors_size, mds_kbdc_parse_error_t*)); + } + + fail_if (xcalloc(error, 1, mds_kbdc_parse_error_t)); + (*errors)[errors_ptr + 0] = error; + (*errors)[errors_ptr + 1] = NULL; + errors_ptr++; + + error->severity = MDS_KBDC_PARSE_ERROR_ERROR; + error->error_is_in_file = 1; + fail_if ((error->pathname = strdup(pathname)) == NULL); + error->line = line_i; + error->start = (size_t)(line - source_code.lines[line_i]); + error->end = (size_t)(end - source_code.lines[line_i]); + fail_if ((error->code = strdup(source_code.real_lines[line_i])) == NULL); + if (asprintf(&(error->description), "invalid keyword ‘%s’", line) < 0) + { + error->description = NULL; + goto pfail; + } + } + + *end = prev_end_char; + } + + free(pathname); + source_code_destroy(&source_code); + return 0; + + pfail: + saved_errno = errno; + free(pathname); + source_code_destroy(&source_code); + mds_kbdc_parse_error_free_all(old_errors); + mds_kbdc_parse_error_free_all(*errors), *errors = NULL; + return errno = saved_errno, -1; +} + |