diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mds-kbdc/make-tree.c | 562 |
1 files changed, 327 insertions, 235 deletions
diff --git a/src/mds-kbdc/make-tree.c b/src/mds-kbdc/make-tree.c index 8f8a7ce..af3fdca 100644 --- a/src/mds-kbdc/make-tree.c +++ b/src/mds-kbdc/make-tree.c @@ -994,296 +994,388 @@ static int sequence_fully_popped(size_t stack_orig) /** - * Parse a line + * Parse an else- or else if-statement * - * @return Zero on success, -1 on error + * @return Zero on success, -1 on error, 1 if the caller should go to `redo` */ -static int parse_line(void) +static int parse_else(void) { - /* TODO make this function less complex */ - - redo: - if (in_array) + size_t i; + if (stack_ptr == 0) { - for (;;) - { - SKIP_SPACES(line); - if (*line == '\0') - break; - else if (*line == '}') - { - line++; - end = line + strlen(line); - END; - line = end, prev_end_char = '\0'; - in_array = 0; - stack_ptr -= 2; - NEXT; - break; - } - else - { - NEW_NODE(string, STRING); - NO_JUMP; - CHARS(string); - LEAF; - node->loc_end = (size_t)(end - LINE); - *end = prev_end_char; - line = end; - } - } + NEW_ERROR(1, ERROR, "runaway ‘else’ statement"); return 0; } - else if (!strcmp(line, "have_chars")) - MAKE_LEAF(assumption_have_chars, ASSUMPTION_HAVE_CHARS, QUOTES_1(chars)); - else if (!strcmp(line, "have_range")) - MAKE_LEAF(assumption_have_range, ASSUMPTION_HAVE_RANGE, CHARS(first); CHARS(last); END); - else if (!strcmp(line, "have")) MAKE_LEAF(assumption_have, ASSUMPTION_HAVE, KEYS(data); END); - else if (!strcmp(line, "information")) MAKE_BRANCH(information, INFORMATION, NO_PARAMETERS("information")); - else if (!strcmp(line, "assumption")) MAKE_BRANCH(assumption, ASSUMPTION, NO_PARAMETERS("assumption")); - else if (!strcmp(line, "return")) MAKE_LEAF(return, RETURN, NO_PARAMETERS("return")); - else if (!strcmp(line, "continue")) MAKE_LEAF(continue, CONTINUE, NO_PARAMETERS("continue")); - else if (!strcmp(line, "break")) MAKE_LEAF(break, BREAK, NO_PARAMETERS("break")); - else if (!strcmp(line, "language")) MAKE_LEAF(information_language, INFORMATION_LANGUAGE, QUOTES_1(data)); - else if (!strcmp(line, "country")) MAKE_LEAF(information_country, INFORMATION_COUNTRY, QUOTES_1(data)); - else if (!strcmp(line, "variant")) MAKE_LEAF(information_variant, INFORMATION_VARIANT, QUOTES_1(data)); - else if (!strcmp(line, "include")) MAKE_LEAF(include, INCLUDE, QUOTES_1(filename)); - else if (!strcmp(line, "function")) MAKE_BRANCH(function, FUNCTION, NAMES_1(name)); - else if (!strcmp(line, "macro")) MAKE_BRANCH(macro, MACRO, NAMES_1(name)); - else if (!strcmp(line, "if")) MAKE_BRANCH(if, IF, CHARS(condition); END); - else if (!strcmp(line, "else")) + line += strlen(line); + *end = prev_end_char, prev_end_char = '\0'; + end = line + strlen(line); + SKIP_SPACES(line); + i = stack_ptr - 1; + while (keyword_stack[i] == NULL) + i--; + if (strcmp(keyword_stack[i], "if")) { - size_t i; - if (stack_ptr == 0) - { - NEW_ERROR(1, ERROR, "runaway ‘else’ statement"); - goto next; - } - line += strlen(line); - *end = prev_end_char, prev_end_char = '\0'; - end = line + strlen(line); - SKIP_SPACES(line); - i = stack_ptr - 1; - while (keyword_stack[i] == NULL) - i--; - if (strcmp(keyword_stack[i], "if")) - { - stack_ptr--; - line = original, end = line + strlen(line); - NEW_ERROR(1, ERROR, "runaway ‘else’ statement"); - } - else if (*line == '\0') - { - /* else */ - mds_kbdc_tree_if_t* supernode = &(tree_stack[stack_ptr - 1][0]->if_); - if (supernode->otherwise) - { - line = strstr(LINE, "else"); - end = line + 4, prev_end_char = *end; - NEW_ERROR(1, ERROR, "multiple ‘else’ statements"); - mds_kbdc_tree_free(supernode->otherwise); - supernode->otherwise = NULL; - } - tree_stack[stack_ptr] = &(supernode->otherwise); - } - else if ((strstr(line, "if") == line) && ((line[2] == ' ') || (line[2] == '\0'))) - { - /* else if */ - mds_kbdc_tree_if_t* supernode = &(tree_stack[stack_ptr - 1][0]->if_); - NEW_NODE(if, IF); - node->loc_end = node->loc_start + 2; - end = line += 2, prev_end_char = *end, *end = '\0'; - CHARS(condition); - END; - tree_stack[stack_ptr] = &(supernode->otherwise); - BRANCH(NULL); - } - else + stack_ptr--; + line = original, end = line + strlen(line); + NEW_ERROR(1, ERROR, "runaway ‘else’ statement"); + } + else if (*line == '\0') + { + /* else */ + mds_kbdc_tree_if_t* supernode = &(tree_stack[stack_ptr - 1][0]->if_); + if (supernode->otherwise) { - NEW_ERROR(1, ERROR, "expecting nothing or ‘if’"); - stack_ptr--; + line = strstr(LINE, "else"); + end = line + 4, prev_end_char = *end; + NEW_ERROR(1, ERROR, "multiple ‘else’ statements"); + mds_kbdc_tree_free(supernode->otherwise); + supernode->otherwise = NULL; } + tree_stack[stack_ptr] = &(supernode->otherwise); } - else if (!strcmp(line, "for")) + else if ((strstr(line, "if") == line) && ((line[2] == ' ') || (line[2] == '\0'))) { - NEW_NODE(for, FOR); - CHARS(first); - TEST_FOR_KEYWORD("to"); - CHARS(last); - TEST_FOR_KEYWORD("as"); - CHARS(variable); + /* else if */ + mds_kbdc_tree_if_t* supernode = &(tree_stack[stack_ptr - 1][0]->if_); + NEW_NODE(if, IF); + node->loc_end = node->loc_start + 2; + end = line += 2, prev_end_char = *end, *end = '\0'; + CHARS(condition); END; - BRANCH("for"); + tree_stack[stack_ptr] = &(supernode->otherwise); + BRANCH(NULL); } - else if (!strcmp(line, "let")) + else { - NEW_NODE(let, LET); - CHARS(variable); - TEST_FOR_KEYWORD(":"); - *end = prev_end_char; - SKIP_SPACES(line); - if (*line == '{') + NEW_ERROR(1, ERROR, "expecting nothing or ‘if’"); + stack_ptr--; + } + + return 0; + pfail: + return -1; +} + + +/** + * Parse a for-statement + * + * @return Zero on success, -1 on error, 1 if the caller should go to `redo` + */ +static int parse_for(void) +{ + NEW_NODE(for, FOR); + CHARS(first); + TEST_FOR_KEYWORD("to"); + CHARS(last); + TEST_FOR_KEYWORD("as"); + CHARS(variable); + END; + BRANCH("for"); + + return 0; + pfail: + return -1; +} + + +/** + * Parse a let-statement + * + * @return Zero on success, -1 on error, 1 if the caller should go to `redo` + */ +static int parse_let(void) +{ + NEW_NODE(let, LET); + CHARS(variable); + TEST_FOR_KEYWORD(":"); + *end = prev_end_char; + SKIP_SPACES(line); + if (*line == '{') #define inner value - BRANCH(NULL); + BRANCH(NULL); #undef inner - else - LEAF; - if (*line == '\0') - { - line = original, end = line + strlen(line), prev_end_char = '\0'; - NEW_ERROR(1, ERROR, "too few parameters"); - } - else if (*line != '{') - { + else + LEAF; + if (*line == '\0') + { + line = original, end = line + strlen(line), prev_end_char = '\0'; + NEW_ERROR(1, ERROR, "too few parameters"); + } + else if (*line != '{') + { #define node subnode - NEW_NODE(string, STRING); - NO_JUMP; - CHARS(string); - node->loc_end = (size_t)(end - LINE); + NEW_NODE(string, STRING); + NO_JUMP; + CHARS(string); + node->loc_end = (size_t)(end - LINE); #undef node - node->value = (mds_kbdc_tree_t*)subnode; - END; - } - else - { + node->value = (mds_kbdc_tree_t*)subnode; + END; + } + else + { #define node subnode #define inner elements - NEW_NODE(array, ARRAY); - BRANCH("}"); - node->loc_end = node->loc_start + 1; + NEW_NODE(array, ARRAY); + BRANCH("}"); + node->loc_end = node->loc_start + 1; #undef inner #undef node - in_array = 1; - line++; - goto redo; - } + in_array = 1; + line++; + return 1; } - else if (!strcmp(line, "end")) + + return 0; + pfail: + return -1; +} + + +/** + * Parse an end-statement + * + * @return Zero on success, -1 on error, 1 if the caller should go to `redo` + */ +static int parse_end(void) +{ + if (stack_ptr == 0) { - if (stack_ptr == 0) - { - NEW_ERROR(1, ERROR, "runaway ‘end’ statement"); - goto next; - } - line += strlen(line); - *end = prev_end_char, prev_end_char = '\0'; - SKIP_SPACES(line); - while (keyword_stack[--stack_ptr] == NULL); - if (*line == '\0') - { - line = original, end = line + strlen(line); - NEW_ERROR(1, ERROR, "expecting a keyword after ‘end’"); - } - else if (strcmp(line, keyword_stack[stack_ptr])) - NEW_ERROR(1, ERROR, "expected ‘%s’ but got ‘%s’", keyword_stack[stack_ptr], line); - NEXT; + NEW_ERROR(1, ERROR, "runaway ‘end’ statement"); + return 0; } - else if (strchr("\\\"<([0123456789", *line)) + line += strlen(line); + *end = prev_end_char, prev_end_char = '\0'; + SKIP_SPACES(line); + while (keyword_stack[--stack_ptr] == NULL); + if (*line == '\0') { - size_t stack_orig = stack_ptr + 1; - char* colon; + line = original, end = line + strlen(line); + NEW_ERROR(1, ERROR, "expecting a keyword after ‘end’"); + } + else if (strcmp(line, keyword_stack[stack_ptr])) + NEW_ERROR(1, ERROR, "expected ‘%s’ but got ‘%s’", keyword_stack[stack_ptr], line); + NEXT; + + return 0; + pfail: + return -1; +} + + +/** + * Parse a mapping- or value-statement + * + * @return Zero on success, -1 on error, 1 if the caller should go to `redo` + */ +static int parse_map(void) +{ + size_t stack_orig = stack_ptr + 1; + char* colon; #define node supernode #define inner sequence - NEW_NODE(map, MAP); - node->loc_end = node->loc_start; - BRANCH(":"); + NEW_NODE(map, MAP); + node->loc_end = node->loc_start; + BRANCH(":"); #undef inner #undef node - SEQUENCE(1); - SEQUENCE_FULLY_POPPED; + SEQUENCE(1); + SEQUENCE_FULLY_POPPED; #define node supernode #define inner result - stack_ptr--; - *end = prev_end_char; - SKIP_SPACES(line); - if (colon = line, *line++ != ':') - { - LEAF; - return 0; /* Not an error in functions, or if \set is access, even indirectly. */ - } - BRANCH(":"); + stack_ptr--; + *end = prev_end_char; + SKIP_SPACES(line); + if (colon = line, *line++ != ':') + { + LEAF; + prev_end_char = *end; + return 0; /* Not an error in functions, or if \set is access, even indirectly. */ + } + BRANCH(":"); #undef inner #undef node - SEQUENCE(1); - SEQUENCE_FULLY_POPPED; - stack_ptr--; - *end = prev_end_char; - SKIP_SPACES(line); + SEQUENCE(1); + SEQUENCE_FULLY_POPPED; + stack_ptr--; + *end = prev_end_char; + SKIP_SPACES(line); #define node supernode - LEAF; + LEAF; #undef node - if (supernode->result == NULL) - { - NEW_ERROR(1, ERROR, "output missing"); - error->start = (size_t)(colon - LINE); - error->end = error->start + 1; - } - if (*line == '\0') - return 0; - end = line + strlen(line), prev_end_char = *end; - NEW_ERROR(1, ERROR, "too many parameters"); + if (supernode->result == NULL) + { + NEW_ERROR(1, ERROR, "output missing"); + error->start = (size_t)(colon - LINE); + error->end = error->start + 1; } - else + if (*line == '\0') + return prev_end_char = *end, 0; + end = line + strlen(line), prev_end_char = *end; + NEW_ERROR(1, ERROR, "too many parameters"); + + return 0; + pfail: + return -1; +} + + +/** + * Parse a macro call + * + * @return Zero on success, -1 on error, 1 if the caller should go to `redo` + */ +static int parse_macro_call(void) +{ + char* old_end = end; + char old_prev_end_char = prev_end_char; + size_t stack_orig = stack_ptr + 1; + *end = prev_end_char; + end = strchrnul(line, '('); + prev_end_char = *end, *end = '\0'; + if (prev_end_char) { - char* old_end = end; - char old_prev_end_char = prev_end_char; - size_t stack_orig = stack_ptr + 1; - *end = prev_end_char; - end = strchrnul(line, '('); - prev_end_char = *end, *end = '\0'; - if (prev_end_char) - { #define node supernode #define inner arguments - NEW_NODE(macro_call, MACRO_CALL); - old_end = end, old_prev_end_char = prev_end_char; - NO_JUMP; - *old_end = '\0'; - CHARS(name); - BRANCH(NULL); - end = old_end, prev_end_char = old_prev_end_char; - line++; + NEW_NODE(macro_call, MACRO_CALL); + old_end = end, old_prev_end_char = prev_end_char; + NO_JUMP; + *old_end = '\0'; + CHARS(name); + BRANCH(NULL); + end = old_end, prev_end_char = old_prev_end_char; + line++; #undef inner #undef node - SEQUENCE(0); - SEQUENCE_FULLY_POPPED; + SEQUENCE(0); + SEQUENCE_FULLY_POPPED; #define node supernode - if (*line == ')') - { - line++; - SKIP_SPACES(line); - if (*line) - { - NEW_ERROR(1, ERROR, "extra token after macro call"); - error->end = strlen(LINE); - } - } - else + if (*line == ')') + { + line++; + SKIP_SPACES(line); + if (*line) { - NEW_ERROR(1, ERROR, "missing ‘)’"); - error->start = (size_t)(strchr(LINE, '(') - LINE); - error->end = error->start + 1; + NEW_ERROR(1, ERROR, "extra token after macro call"); + error->end = strlen(LINE); } - stack_ptr--; - NEXT; - goto next; + } + else + { + NEW_ERROR(1, ERROR, "missing ‘)’"); + error->start = (size_t)(strchr(LINE, '(') - LINE); + error->end = error->start + 1; + } + stack_ptr--; + NEXT; + return 0; #undef node + } + *old_end = '\0'; + end = old_end; + prev_end_char = old_prev_end_char; + if (strchr("}", *line)) + NEW_ERROR(1, ERROR, "runaway ‘%c’", *line); + else + NEW_ERROR(1, ERROR, "invalid syntax ‘%s’", line); + + return 0; + pfail: + return -1; +} + + +/** + * Parse a line of an array + * + * @return Zero on success, -1 on error, 1 if the caller should go to `redo` + */ +static int parse_array_elements(void) +{ + for (;;) + { + SKIP_SPACES(line); + if (*line == '\0') + return 0; + else if (*line == '}') + { + line++; + end = line + strlen(line); + END; + line = end, prev_end_char = '\0'; + in_array = 0; + stack_ptr -= 2; + NEXT; + return 0; } - *old_end = '\0'; - end = old_end; - prev_end_char = old_prev_end_char; - if (strchr("}", *line)) - NEW_ERROR(1, ERROR, "runaway ‘%c’", *line); else - NEW_ERROR(1, ERROR, "invalid syntax ‘%s’", line); + { + NEW_NODE(string, STRING); + NO_JUMP; + CHARS(string); + LEAF; + node->loc_end = (size_t)(end - LINE); + *end = prev_end_char; + line = end; + } } - next: + pfail: + return -1; +} + + +/** + * Parse a line + * + * @return Zero on success, -1 on error + */ +static int parse_line(void) +{ +#define p(function) \ + do \ + { \ + fail_if ((r = function(), r < 0)); \ + if (r > 0) \ + goto redo; \ + } \ + while (0) + + int r; + + redo: + if (in_array) p (parse_array_elements); + else if (!strcmp(line, "have_chars")) + MAKE_LEAF(assumption_have_chars, ASSUMPTION_HAVE_CHARS, QUOTES_1(chars)); + else if (!strcmp(line, "have_range")) + MAKE_LEAF(assumption_have_range, ASSUMPTION_HAVE_RANGE, CHARS(first); CHARS(last); END); + else if (!strcmp(line, "have")) MAKE_LEAF(assumption_have, ASSUMPTION_HAVE, KEYS(data); END); + else if (!strcmp(line, "information")) MAKE_BRANCH(information, INFORMATION, NO_PARAMETERS("information")); + else if (!strcmp(line, "assumption")) MAKE_BRANCH(assumption, ASSUMPTION, NO_PARAMETERS("assumption")); + else if (!strcmp(line, "return")) MAKE_LEAF(return, RETURN, NO_PARAMETERS("return")); + else if (!strcmp(line, "continue")) MAKE_LEAF(continue, CONTINUE, NO_PARAMETERS("continue")); + else if (!strcmp(line, "break")) MAKE_LEAF(break, BREAK, NO_PARAMETERS("break")); + else if (!strcmp(line, "language")) MAKE_LEAF(information_language, INFORMATION_LANGUAGE, QUOTES_1(data)); + else if (!strcmp(line, "country")) MAKE_LEAF(information_country, INFORMATION_COUNTRY, QUOTES_1(data)); + else if (!strcmp(line, "variant")) MAKE_LEAF(information_variant, INFORMATION_VARIANT, QUOTES_1(data)); + else if (!strcmp(line, "include")) MAKE_LEAF(include, INCLUDE, QUOTES_1(filename)); + else if (!strcmp(line, "function")) MAKE_BRANCH(function, FUNCTION, NAMES_1(name)); + else if (!strcmp(line, "macro")) MAKE_BRANCH(macro, MACRO, NAMES_1(name)); + else if (!strcmp(line, "if")) MAKE_BRANCH(if, IF, CHARS(condition); END); + else if (!strcmp(line, "else")) p (parse_else); + else if (!strcmp(line, "for")) p (parse_for); + else if (!strcmp(line, "let")) p (parse_let); + else if (!strcmp(line, "end")) p (parse_end); + else if (strchr("\\\"<([0123456789", *line)) p (parse_map); + else p (parse_macro_call); + *end = prev_end_char; return 0; pfail: return -1; +#undef p } |