aboutsummaryrefslogtreecommitdiffstats
path: root/src/mds-kbdc/make-tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mds-kbdc/make-tree.c')
-rw-r--r--src/mds-kbdc/make-tree.c581
1 files changed, 307 insertions, 274 deletions
diff --git a/src/mds-kbdc/make-tree.c b/src/mds-kbdc/make-tree.c
index 9c9c2ef..2cdb4c0 100644
--- a/src/mds-kbdc/make-tree.c
+++ b/src/mds-kbdc/make-tree.c
@@ -672,6 +672,16 @@ typedef struct state
*/
mds_kbdc_tree_t*** restrict tree_stack_;
+ /**
+ * The index of the currently parsed line
+ */
+ size_t line_i_;
+
+ /**
+ * Whether an array is currently being parsed
+ */
+ int in_array_;
+
} state_t;
@@ -680,6 +690,8 @@ typedef struct state
#define stack_ptr (state->stack_ptr_)
#define keyword_stack (state->keyword_stack_)
#define tree_stack (state->tree_stack_)
+#define line_i (state->line_i_)
+#define in_array (state->in_array_)
@@ -759,7 +771,7 @@ static int get_pathname(const char* restrict filename, state_t* restrict state)
*/
static int allocate_stacks(state_t* restrict state)
{
- size_t max_line_length = 0, cur_line_length, line_i, line_n;
+ size_t max_line_length = 0, cur_line_length, line_n;
/* The maximum line-length is needed because lines can have there own stacking,
* like sequence mapping lines, additionally, let statements can have one array. */
@@ -802,14 +814,13 @@ static int read_source_code(state_t* restrict state)
* Check that a the file did not end prematurely by checking
* that the stack has been fully popped
*
- * @param state Parsing state
+ * @param state The parsing state
* @return Zero on success, -1 on error
*/
static int check_for_premature_end_of_file(state_t* restrict state)
{
char* line = NULL;
char* end = NULL;
- size_t line_i = 0;
/* Check that all scopes have been popped. */
if (stack_ptr)
@@ -845,14 +856,13 @@ static int check_for_premature_end_of_file(state_t* restrict state)
* and generate a warning if that is the case, comments
* and whitespace is ignored
*
- * @param state Parsing state
+ * @param state The parsing state
* @return Zero on success, -1 on error
*/
static int check_whether_file_is_empty(state_t* restrict state)
{
char* line = NULL;
char* end = NULL;
- size_t line_i = 0;
/* Warn about empty files. */
if (parsing_result->tree == NULL)
@@ -865,336 +875,357 @@ static int check_whether_file_is_empty(state_t* restrict state)
/**
- * Parse a file into a syntax tree
+ * Parse a line
*
- * @param filename The filename of the file to parse
- * @param result Output parameter for the parsing result
- * @return -1 if an error occursed that cannot be stored in `result`, zero otherwise
+ * @param state The parsing state
+ * @return Zero on success, -1 on error
*/
-int parse_to_tree(const char* restrict filename, mds_kbdc_parsed_t* restrict result)
+static int parse_line(state_t* restrict state)
{
/* TODO make this function less complex */
- size_t line_i, line_n;
- int r, saved_errno, in_array = 0;
- state_t state_;
- state_t* restrict state = &state_;
-
- memset(state, 0, sizeof(state_t));
- parsing_result = result;
-
- fail_if (xmalloc(result->source_code, 1, mds_kbdc_source_code_t));
- mds_kbdc_source_code_initialise(result->source_code);
-
- if (r = get_pathname(filename, state), r <= 0)
- return r;
-
- fail_if (read_source_code(state));
- fail_if (allocate_stacks(state));
+ char* line = LINE;
+ char* end;
+ char prev_end_char;
+ char* original;
+ int too_few = 0;
- /* Create a node-slot for the tree root. */
- *tree_stack = &(result->tree);
+ SKIP_SPACES(line);
+ if (end = strchrnul(line, ' '), end == line)
+ return 0;
+ prev_end_char = *end, *end = '\0';
+ original = line;
- /* Parse the file. */
- for (line_i = 0, line_n = result->source_code->line_count; line_i < line_n; line_i++)
+ redo:
+ if (in_array)
{
- char* line = LINE;
- char* end;
- char prev_end_char;
- char* original;
- int too_few = 0;
-
- SKIP_SPACES(line);
- if (end = strchrnul(line, ' '), end == line)
- continue;
- prev_end_char = *end, *end = '\0';
- original = line;
-
- redo:
- if (in_array)
+ for (;;)
{
- 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
{
- 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
- {
#define node subnode
- NEW_NODE(string, STRING);
- NO_JUMP;
- CHARS(string);
- LEAF;
- node->loc_end = (size_t)(end - LINE);
- *end = prev_end_char;
- line = end;
+ NEW_NODE(string, STRING);
+ NO_JUMP;
+ CHARS(string);
+ LEAF;
+ node->loc_end = (size_t)(end - LINE);
+ *end = prev_end_char;
+ line = end;
#undef node
- }
}
- continue;
}
- 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"))
+ 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"))
+ {
+ size_t i;
+ if (stack_ptr == 0)
{
- 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
+ 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)
{
- 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--;
+ }
+ }
+ else if (!strcmp(line, "for"))
+ {
+ NEW_NODE(for, FOR);
+ CHARS(first);
+ TEST_FOR_KEYWORD("to");
+ CHARS(last);
+ TEST_FOR_KEYWORD("as");
+ CHARS(variable);
+ END;
+ BRANCH("for");
+ }
+ else if (!strcmp(line, "let"))
+ {
+ 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++;
+ goto redo;
}
- else if (!strcmp(line, "end"))
+ }
+ else if (!strcmp(line, "end"))
+ {
+ 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");
+ goto next;
}
- 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;
+ }
+ else if (strchr("\\\"<([0123456789", *line))
+ {
+ 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(stack_orig);
+ SEQUENCE(1);
+ SEQUENCE_FULLY_POPPED(stack_orig);
#define node supernode
#define inner result
- stack_ptr--;
- *end = prev_end_char;
- SKIP_SPACES(line);
- if (colon = line, *line++ != ':')
- {
- LEAF;
- continue; /* Not an error in functions. */
- }
- BRANCH(":");
+ stack_ptr--;
+ *end = prev_end_char;
+ SKIP_SPACES(line);
+ if (colon = line, *line++ != ':')
+ {
+ LEAF;
+ return 0; /* Not an error in functions. */
+ }
+ BRANCH(":");
#undef inner
#undef node
- SEQUENCE(1);
- SEQUENCE_FULLY_POPPED(stack_orig);
- stack_ptr--;
- *end = prev_end_char;
- SKIP_SPACES(line);
+ SEQUENCE(1);
+ SEQUENCE_FULLY_POPPED(stack_orig);
+ 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')
- continue;
- 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 0;
+ end = line + strlen(line), prev_end_char = *end;
+ NEW_ERROR(1, ERROR, "too many parameters");
+ }
+ else
+ {
+ 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(stack_orig);
+ SEQUENCE(0);
+ SEQUENCE_FULLY_POPPED(stack_orig);
#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;
-#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);
+ {
+ NEW_ERROR(1, ERROR, "missing ‘)’");
+ error->start = (size_t)(strchr(LINE, '(') - LINE);
+ error->end = error->start + 1;
+ }
+ stack_ptr--;
+ NEXT;
+ goto next;
+#undef node
}
-
- next:
- *end = prev_end_char;
+ *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);
}
+ next:
+ *end = prev_end_char;
+
+ return 0;
+ pfail:
+ return -1;
+}
+
+
+/**
+ * Parse a file into a syntax tree
+ *
+ * @param filename The filename of the file to parse
+ * @param result Output parameter for the parsing result
+ * @return -1 if an error occursed that cannot be stored in `result`, zero otherwise
+ */
+int parse_to_tree(const char* restrict filename, mds_kbdc_parsed_t* restrict result)
+{
+ size_t line_n;
+ int r, saved_errno;
+ state_t state_;
+ state_t* restrict state = &state_;
+
+
+ /* Prepare parsing. */
+
+ memset(state, 0, sizeof(state_t));
+ parsing_result = result;
+
+ fail_if (xmalloc(result->source_code, 1, mds_kbdc_source_code_t));
+ mds_kbdc_source_code_initialise(result->source_code);
+
+ if (r = get_pathname(filename, state), r <= 0)
+ return r;
+
+ fail_if (read_source_code(state));
+ fail_if (allocate_stacks(state));
+
+
+ /* Create a node-slot for the tree root. */
+ *tree_stack = &(result->tree);
+
+ /* Parse the file. */
+ for (line_i = 0, line_n = result->source_code->line_count; line_i < line_n; line_i++)
+ parse_line(state);
+
+
+ /* Check parsing state. */
fail_if (check_for_premature_end_of_file(state));
fail_if (check_whether_file_is_empty(state));
+ /* Clean up. */
free(keyword_stack);
free(tree_stack);
return 0;
@@ -1208,6 +1239,8 @@ int parse_to_tree(const char* restrict filename, mds_kbdc_parsed_t* restrict res
+#undef in_array
+#undef line_i
#undef parsing_result
#undef stack_ptr
#undef keyword_stack