/** * 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 . */ #include "make-tree.h" #include "raw-data.h" #include #include #include #include #include #include #include #include /** * 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; }