aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/mds-kbdc/make-tree.c822
1 files changed, 481 insertions, 341 deletions
diff --git a/src/mds-kbdc/make-tree.c b/src/mds-kbdc/make-tree.c
index 5e87269..5d33b54 100644
--- a/src/mds-kbdc/make-tree.c
+++ b/src/mds-kbdc/make-tree.c
@@ -174,21 +174,10 @@
/**
* Check that there are no tokens after a keyword
*
- * @param KEYWORD:const char* The keyword,
+ * @param KEYWORD:const char* The keyword
*/
-#define NO_PARAMETERS(KEYWORD) \
- do \
- { \
- line += strlen(line); \
- *end = prev_end_char, prev_end_char = '\0'; \
- SKIP_SPACES(line); \
- if (*line) \
- { \
- end = line + strlen(line); \
- NEW_ERROR(1, ERROR, "extra token after ‘%s’", KEYWORD); \
- } \
- } \
- while (0)
+#define NO_PARAMETERS(KEYWORD) \
+ fail_if (no_parameters(KEYWORD))
/**
@@ -198,51 +187,8 @@
* @param var:identifier The name of the member variable, for the current
* node, where the parameter should be stored
*/
-#define NAMES_1(var) \
- do \
- { \
- line += strlen(line); \
- *end = prev_end_char, prev_end_char = '\0'; \
- SKIP_SPACES(line); \
- if (*line == '\0') \
- { \
- line = original, end = line + strlen(line); \
- NEW_ERROR(1, ERROR, "a name is expected"); \
- } \
- else \
- { \
- char* name_end = line; \
- char* test; \
- int stray_char = 0; \
- while (*name_end && is_name_char(*name_end)) \
- name_end++; \
- if (*name_end && (*name_end != ' ')) \
- { \
- char* end_end = name_end + 1; \
- while ((*end_end & 0xC0) == 0x80) \
- end_end++; \
- prev_end_char = *end_end, *end_end = '\0'; \
- NEW_ERROR(1, ERROR, "stray ‘%s’ character", name_end); \
- error->start = (size_t)(name_end - LINE); \
- error->end = (size_t)(end_end - LINE); \
- *end_end = prev_end_char; \
- stray_char = 1; \
- } \
- test = name_end; \
- SKIP_SPACES(test); \
- if (*test && !stray_char) \
- { \
- NEW_ERROR(1, ERROR, "too many parameters"); \
- error->start = (size_t)(test - LINE); \
- error->end = strlen(LINE); \
- } \
- end = name_end; \
- prev_end_char = *end; \
- *end = '\0'; \
- fail_if ((node->var = strdup(line)) == NULL); \
- } \
- } \
- while (0)
+#define NAMES_1(var) \
+ fail_if (names_1(&(node->var)))
/**
@@ -271,45 +217,8 @@
* @param var:identifier The name of the member variable, for the current
* node, where the parameter should be stored
*/
-#define CHARS(var) \
- do \
- { \
- if (too_few) \
- break; \
- line += strlen(line); \
- *end = prev_end_char, prev_end_char = '\0'; \
- SKIP_SPACES(line); \
- if (*line == '\0') \
- { \
- line = original, end = line + strlen(line); \
- NEW_ERROR(1, ERROR, "too few parameters"); \
- line = end, too_few = 1; \
- } \
- else \
- { \
- char* arg_end = line; \
- char* call_end = arg_end; \
- int escape = 0, quote = 0; \
- while (*arg_end) \
- { \
- char c = *arg_end++; \
- if (escape) escape = 0; \
- else if (arg_end <= call_end) ; \
- else if (c == '\\') \
- { \
- escape = 1; \
- call_end = arg_end + get_end_of_call(arg_end, 0, strlen(arg_end)); \
- } \
- else if (quote) quote = (c != '"'); \
- else if (IS_END(c)) { arg_end--; break; } \
- else quote = (c == '"'); \
- } \
- prev_end_char = *arg_end, *arg_end = '\0', end = arg_end; \
- fail_if ((node->var = strdup(line)) == NULL); \
- line = end; \
- } \
- } \
- while (0)
+#define CHARS(var) \
+ fail_if (chars(&(node->var)))
/**
@@ -331,24 +240,8 @@
/**
* Test that the next parameter is in quotes
*/
-#define QUOTES \
- do \
- { \
- char* line_ = line; \
- line += strlen(line); \
- *end = prev_end_char; \
- SKIP_SPACES(line); \
- if (*line && (*line != '"')) \
- { \
- char* arg_end = line; \
- SKIP_SPACES(arg_end); \
- NEW_ERROR(1, ERROR, "parameter must be in quotes"); \
- error->end = (size_t)(arg_end - LINE); \
- } \
- *end = '\0'; \
- line = line_; \
- } \
- while (0)
+#define QUOTES \
+ fail_if (quotes())
/**
@@ -373,39 +266,8 @@
*
* @param KEYWORD:const char* The keyword
*/
-#define TEST_FOR_KEYWORD(KEYWORD) \
- do \
- { \
- if (too_few) \
- break; \
- line += strlen(line); \
- *end = prev_end_char, prev_end_char = '\0'; \
- SKIP_SPACES(line); \
- if (*line == '\0') \
- { \
- line = original, end = line + strlen(line); \
- NEW_ERROR(1, ERROR, "too few parameters"); \
- line = end, too_few = 1; \
- } \
- else \
- { \
- int ok = (strstr(line, KEYWORD) == line); \
- line += strlen(KEYWORD); \
- ok = ok && ((*line == '\0') || (*line == ' ')); \
- if (ok) \
- { \
- end = line; \
- prev_end_char = *end, *end = '\0'; \
- break; \
- } \
- line -= strlen(KEYWORD); \
- end = line; \
- SKIP_SPACES(end); \
- prev_end_char = *end, *end = '\0'; \
- NEW_ERROR(1, ERROR, "expecting keyword ‘%s’", KEYWORD); \
- } \
- } \
- while (0)
+#define TEST_FOR_KEYWORD(KEYWORD) \
+ fail_if (test_for_keyword(KEYWORD))
/**
@@ -415,57 +277,8 @@
* @param var:identifier The name of the member variable, for the current
* node, where the parameter should be stored
*/
-#define KEYS(var) \
- do \
- { \
- if (too_few) \
- break; \
- line += strlen(line); \
- *end = prev_end_char, prev_end_char = '\0'; \
- SKIP_SPACES(line); \
- if (*line == '\0') \
- { \
- line = original, end = line + strlen(line); \
- NEW_ERROR(1, ERROR, "too few parameters"); \
- line = end, too_few = 1; \
- } \
- else \
- { \
- char* arg_end = line; \
- char* call_end = arg_end; \
- int escape = 0, quote = 0, triangle = (*arg_end == '<'); \
- while (*arg_end) \
- { \
- char c = *arg_end++ ; \
- if (escape) escape = 0; \
- else if (arg_end <= call_end) ; \
- else if (c == '\\') \
- { \
- escape = 1; \
- call_end = arg_end + get_end_of_call(arg_end, 0, strlen(arg_end)); \
- } \
- else if (quote) quote = (c != '"'); \
- else if (c == '\"') quote = 1; \
- else if (c == '>') triangle = 0; \
- else if (IS_END(c) && !triangle) { arg_end--; break; } \
- } \
- prev_end_char = *arg_end, *arg_end = '\0', end = arg_end; \
- if (*line == '<') \
- { \
- NEW_SUBNODE(keys, KEYS); \
- node->var = (mds_kbdc_tree_t*)subnode; \
- fail_if ((subnode->keys = strdup(line)) == NULL); \
- } \
- else \
- { \
- NEW_SUBNODE(string, STRING); \
- node->var = (mds_kbdc_tree_t*)subnode; \
- fail_if ((subnode->string = strdup(line)) == NULL); \
- } \
- line = end; \
- } \
- } \
- while (0)
+#define KEYS(var) \
+ fail_if (keys(&(node->var)))
/**
@@ -475,46 +288,9 @@
* @param var:identifier The name of the member variable, for the current
* node, where the parameter should be stored
*/
-#define PURE_KEYS(var) \
- do \
- { \
- if (too_few) \
- break; \
- line += strlen(line); \
- *end = prev_end_char, prev_end_char = '\0'; \
- SKIP_SPACES(line); \
- if (*line == '\0') \
- { \
- line = original, end = line + strlen(line); \
- NEW_ERROR(1, ERROR, "too few parameters"); \
- line = end, too_few = 1; \
- } \
- else \
- { \
- char* arg_end = line; \
- char* call_end = arg_end; \
- int escape = 0, quote = 0, triangle = (*arg_end == '<'); \
- while (*arg_end) \
- { \
- char c = *arg_end++ ; \
- if (escape) escape = 0; \
- else if (arg_end <= call_end) ; \
- else if (c == '\\') \
- { \
- escape = 1; \
- call_end = arg_end + get_end_of_call(arg_end, 0, strlen(arg_end)); \
- } \
- else if (quote) quote = (c != '"'); \
- else if (c == '\"') quote = 1; \
- else if (c == '>') triangle = 0; \
- else if (IS_END(c) && !triangle) { arg_end--; break; } \
- } \
- prev_end_char = *arg_end, *arg_end = '\0'; \
- fail_if ((node->var = strdup(line)) == NULL); \
- end = arg_end, line = end; \
- } \
- } \
- while (0)
+#define PURE_KEYS(var) \
+ fail_if (pure_keys(&(node->var)))
+
/**
@@ -523,95 +299,23 @@
* @param mapseq:int Whether this is a mapping sequence, otherwise
* it is treated as macro call arguments
*/
-#define SEQUENCE(mapseq) \
- do /* for(;;) */ \
- { \
- *end = prev_end_char; \
- SKIP_SPACES(line); \
- if ((*line == '\0') || (*line == (mapseq ? ':' : ')'))) \
- break; \
- if (mapseq && (*line == '(')) \
- { \
- NEW_NODE(unordered, UNORDERED); \
- node->loc_end = node->loc_start + 1; \
- BRANCH(")"); \
- line++; \
- } \
- else if (*line == '[') \
- { \
- NEW_NODE(alternation, ALTERNATION); \
- node->loc_end = node->loc_start + 1; \
- BRANCH("]"); \
- line++; \
- } \
- else if (*line == '.') \
- { \
- NEW_NODE(nothing, NOTHING); \
- node->loc_end = node->loc_start + 1; \
- LEAF; \
- line++; \
- } \
- else if (strchr("])", *line)) \
- { \
- end = line + 1; \
- prev_end_char = *end, *end = '\0'; \
- if (stack_ptr == stack_orig) \
- { \
- NEW_ERROR(1, ERROR, "runaway ‘%s’", line); \
- } \
- else \
- { \
- stack_ptr--; \
- if (strcmp(line, keyword_stack[stack_ptr])) \
- { \
- NEW_ERROR(1, ERROR, "expected ‘%s’ but got ‘%s’", keyword_stack[stack_ptr], line); \
- } \
- NEXT; \
- } \
- *end = prev_end_char; \
- line++; \
- } \
- else if (*line == '<') \
- { \
- NEW_NODE(keys, KEYS); \
- NO_JUMP; \
- PURE_KEYS(keys); \
- LEAF; \
- node->loc_end = (size_t)(line - LINE); \
- } \
- else \
- { \
- NEW_NODE(string, STRING); \
- NO_JUMP; \
- CHARS(string); \
- LEAF; \
- node->loc_end = (size_t)(line - LINE); \
- } \
- } \
+#define SEQUENCE(mapseq) \
+ do /* for(;;) */ \
+ { \
+ *end = prev_end_char; \
+ SKIP_SPACES(line); \
+ if ((*line == '\0') || (*line == (mapseq ? ':' : ')'))) \
+ break; \
+ fail_if (sequence(mapseq, stack_orig)); \
+ } \
while (1)
/**
- * Change the scopes created in `SEQUENCE` has all been popped
- *
- * @param stack_orig:size_t The size of the stack when `SEQUENCE` was called
+ * Check that the scopes created in `SEQUENCE` has all been popped
*/
-#define SEQUENCE_FULLY_POPPED(stack_orig) \
- do \
- { \
- if (stack_ptr == stack_orig) \
- break; \
- end = line + 1; \
- NEW_ERROR(1, ERROR, "premature end of sequence"); \
- while (stack_ptr > stack_orig) \
- { \
- stack_ptr--; \
- NEW_ERROR(1, NOTE, "missing associated ‘%s’", keyword_stack[stack_ptr]); \
- error->start = tree_stack[stack_ptr][0]->loc_start; \
- error->end = tree_stack[stack_ptr][0]->loc_end; \
- } \
- } \
- while (0)
+#define SEQUENCE_FULLY_POPPED \
+ fail_if (sequence_fully_popped(stack_orig))
/**
@@ -677,13 +381,41 @@ static mds_kbdc_tree_t*** restrict tree_stack;
/**
* The index of the currently parsed line
*/
-static size_t line_i;
+static size_t line_i = 0;
/**
* Whether an array is currently being parsed
*/
static int in_array;
+/**
+ * The beginning of what has not get been parsed
+ * on the current line
+ */
+static char* line = NULL;
+
+/**
+ * The end of what has been parsed on the current line
+ */
+static char* end = NULL;
+
+/**
+ * The previous value of `*end`
+ */
+static char prev_end_char;
+
+/**
+ * Pointer to the first non-whitespace character
+ * on the current line
+ */
+static char* original;
+
+/**
+ * Whether it has been identified that the
+ * current line has too few parameters
+ */
+static int too_few;
+
/*** Pre-parsing procedures. ***/
@@ -789,9 +521,6 @@ static int read_source_code(void)
*/
static int check_for_premature_end_of_file(void)
{
- char* line = NULL;
- char* end = NULL;
-
/* Check that all scopes have been popped. */
if (stack_ptr)
{
@@ -830,9 +559,6 @@ static int check_for_premature_end_of_file(void)
*/
static int check_whether_file_is_empty(void)
{
- char* line = NULL;
- char* end = NULL;
-
/* Warn about empty files. */
if (result->tree == NULL)
if (result->errors_ptr == 0)
@@ -845,6 +571,425 @@ static int check_whether_file_is_empty(void)
+/*** Parsing subprocedures. ***/
+
+
+/**
+ * Check that there are no tokens after a keyword
+ *
+ * @param keyword The keyword
+ * @return Zero on success, -1 on error
+ */
+static int no_parameters(const char* restrict keyword)
+{
+ line += strlen(line);
+ *end = prev_end_char, prev_end_char = '\0';
+ SKIP_SPACES(line);
+ if (*line)
+ {
+ end = line + strlen(line);
+ NEW_ERROR(1, ERROR, "extra token after ‘%s’", keyword);
+ }
+
+ return 0;
+ pfail:
+ return -1;
+}
+
+
+
+/**
+ * Take next parameter, which should be a name of a callable,
+ * and store it in the current node
+ *
+ * @param var Address of the member variable, for the current
+ * node, where the parameter should be stored
+ * @return Zero on success, -1 on error
+ */
+static int names_1(char** restrict var)
+{
+ char* name_end;
+ char* test;
+ int stray_char = 0;
+ char* end_end;
+
+ line += strlen(line);
+ *end = prev_end_char, prev_end_char = '\0';
+ SKIP_SPACES(line);
+ if (*line == '\0')
+ {
+ line = original, end = line + strlen(line);
+ NEW_ERROR(1, ERROR, "a name is expected");
+ }
+ else
+ {
+ name_end = line;
+ while (*name_end && is_name_char(*name_end))
+ name_end++;
+ if (*name_end && (*name_end != ' '))
+ {
+ end_end = name_end + 1;
+ while ((*end_end & 0xC0) == 0x80)
+ end_end++;
+ prev_end_char = *end_end, *end_end = '\0';
+ NEW_ERROR(1, ERROR, "stray ‘%s’ character", name_end);
+ error->start = (size_t)(name_end - LINE);
+ error->end = (size_t)(end_end - LINE);
+ *end_end = prev_end_char;
+ stray_char = 1;
+ }
+ test = name_end;
+ SKIP_SPACES(test);
+ if (*test && !stray_char)
+ {
+ NEW_ERROR(1, ERROR, "too many parameters");
+ error->start = (size_t)(test - LINE);
+ error->end = strlen(LINE);
+ }
+ end = name_end;
+ prev_end_char = *end;
+ *end = '\0';
+ fail_if ((*var = strdup(line)) == NULL);
+ }
+
+ return 0;
+ pfail:
+ return -1;
+}
+
+
+/**
+ * Take next parameter, which should be a string or numeral,
+ * and store it in the current node
+ *
+ * @param var Address of the member variable, for the current
+ * node, where the parameter should be stored
+ * @return Zero on success, -1 on error
+ */
+static int chars(char** restrict var)
+{
+ if (too_few)
+ return 0;
+ line += strlen(line);
+ *end = prev_end_char, prev_end_char = '\0';
+ SKIP_SPACES(line);
+ if (*line == '\0')
+ {
+ line = original, end = line + strlen(line);
+ NEW_ERROR(1, ERROR, "too few parameters");
+ line = end, too_few = 1;
+ }
+ else
+ {
+ char* arg_end = line;
+ char* call_end = arg_end;
+ int escape = 0, quote = 0;
+ while (*arg_end)
+ {
+ char c = *arg_end++;
+ if (escape) escape = 0;
+ else if (arg_end <= call_end) ;
+ else if (c == '\\')
+ {
+ escape = 1;
+ call_end = arg_end + get_end_of_call(arg_end, 0, strlen(arg_end));
+ }
+ else if (quote) quote = (c != '"');
+ else if (IS_END(c)) { arg_end--; break; }
+ else quote = (c == '"');
+ }
+ prev_end_char = *arg_end, *arg_end = '\0', end = arg_end;
+ fail_if ((*var = strdup(line)) == NULL);
+ line = end;
+ }
+
+ return 0;
+ pfail:
+ return -1;
+}
+
+
+/**
+ * Test that the next parameter is in quotes
+ *
+ * @return Zero on success, -1 on error
+ */
+static int quotes(void)
+{
+ char* line_ = line;
+ line += strlen(line);
+ *end = prev_end_char;
+ SKIP_SPACES(line);
+ if (*line && (*line != '"'))
+ {
+ char* arg_end = line;
+ SKIP_SPACES(arg_end);
+ NEW_ERROR(1, ERROR, "parameter must be in quotes");
+ error->end = (size_t)(arg_end - LINE);
+ }
+ *end = '\0';
+ line = line_;
+
+ return 0;
+ pfail:
+ return -1;
+}
+
+
+/**
+ * Check whether the currently line has unparsed parameters
+ *
+ * @return Whether the currently line has unparsed parameters, -1 on error
+ */
+static int have_more_parameters(void)
+{
+ if (too_few)
+ return 0;
+ line += strlen(line);
+ *end = prev_end_char, prev_end_char = '\0';
+ SKIP_SPACES(line);
+ if (*line == '\0')
+ {
+ line = original, end = line + strlen(line);
+ NEW_ERROR(1, ERROR, "too few parameters");
+ line = end, too_few = 1;
+ return 0;
+ }
+ return 1;
+ pfail:
+ return -1;
+}
+
+
+/**
+ * Check that the next word is a specific keyword
+ *
+ * @param keyword The keyword
+ * @return Zero on success, -1 on error
+ */
+static int test_for_keyword(const char* restrict keyword)
+{
+ int r, ok;
+ if (r = have_more_parameters(), r <= 0)
+ return r;
+
+ ok = (strstr(line, keyword) == line);
+ line += strlen(keyword);
+ ok = ok && ((*line == '\0') || (*line == ' '));
+ if (ok)
+ {
+ end = line;
+ prev_end_char = *end, *end = '\0';
+ return 0;
+ }
+ line -= strlen(keyword);
+ end = line;
+ SKIP_SPACES(end);
+ prev_end_char = *end, *end = '\0';
+ NEW_ERROR(1, ERROR, "expecting keyword ‘%s’", keyword);
+
+ return 0;
+ pfail:
+ return -1;
+}
+
+
+/**
+ * Take next parameter, which should be a key combination or strings,
+ * and store it in the current node
+ *
+ * @param var Address of the member variable, for the current
+ * node, where the parameter should be stored
+ * @return Zero on success, -1 on error
+ */
+static int keys(mds_kbdc_tree_t** restrict var)
+{
+ char* arg_end;
+ char* call_end;
+ int r, escape = 0, quote = 0, triangle;
+ if (r = have_more_parameters(), r <= 0)
+ return r;
+
+ arg_end = line;
+ call_end = arg_end;
+ triangle = (*arg_end == '<');
+ while (*arg_end)
+ {
+ char c = *arg_end++ ;
+ if (escape) escape = 0;
+ else if (arg_end <= call_end) ;
+ else if (c == '\\')
+ {
+ escape = 1;
+ call_end = arg_end + get_end_of_call(arg_end, 0, strlen(arg_end));
+ }
+ else if (quote) quote = (c != '"');
+ else if (c == '\"') quote = 1;
+ else if (c == '>') triangle = 0;
+ else if (IS_END(c) && !triangle) { arg_end--; break; }
+ }
+ prev_end_char = *arg_end, *arg_end = '\0', end = arg_end;
+ if (*line == '<')
+ {
+ NEW_SUBNODE(keys, KEYS);
+ *var = (mds_kbdc_tree_t*)subnode;
+ fail_if ((subnode->keys = strdup(line)) == NULL);
+ }
+ else
+ {
+ NEW_SUBNODE(string, STRING);
+ *var = (mds_kbdc_tree_t*)subnode;
+ fail_if ((subnode->string = strdup(line)) == NULL);
+ }
+ line = end;
+
+ return 0;
+ pfail:
+ return -1;
+}
+
+
+
+/**
+ * Take next parameter, which should be a key combination,
+ * and store it in the current node
+ *
+ * @param var Address of the member variable, for the current
+ * node, where the parameter should be stored
+ * @return Zero on success, -1 on error
+ */
+static int pure_keys(char** restrict var)
+{
+ char* arg_end;
+ char* call_end;
+ int r, escape = 0, quote = 0, triangle;
+ if (r = have_more_parameters(), r <= 0)
+ return r;
+
+ arg_end = line;
+ call_end = arg_end;
+ triangle = (*arg_end == '<');
+ while (*arg_end)
+ {
+ char c = *arg_end++ ;
+ if (escape) escape = 0;
+ else if (arg_end <= call_end) ;
+ else if (c == '\\')
+ {
+ escape = 1;
+ call_end = arg_end + get_end_of_call(arg_end, 0, strlen(arg_end));
+ }
+ else if (quote) quote = (c != '"');
+ else if (c == '\"') quote = 1;
+ else if (c == '>') triangle = 0;
+ else if (IS_END(c) && !triangle) { arg_end--; break; }
+ }
+ prev_end_char = *arg_end, *arg_end = '\0';
+ fail_if ((*var = strdup(line)) == NULL);
+ end = arg_end, line = end;
+
+ return 0;
+ pfail:
+ return -1;
+}
+
+
+/**
+ * Parse an element of a sequence in a mapping
+ *
+ * @param mapseq Whether this is a mapping sequence, otherwise
+ * it is treated as macro call arguments
+ * @param stack_orig The size of the stack when `SEQUENCE` was called
+ * @return Zero on success, -1 on error
+ */
+static int sequence(int mapseq, size_t stack_orig)
+{
+ if (mapseq && (*line == '('))
+ {
+ NEW_NODE(unordered, UNORDERED);
+ node->loc_end = node->loc_start + 1;
+ BRANCH(")");
+ line++;
+ }
+ else if (*line == '[')
+ {
+ NEW_NODE(alternation, ALTERNATION);
+ node->loc_end = node->loc_start + 1;
+ BRANCH("]");
+ line++;
+ }
+ else if (*line == '.')
+ {
+ NEW_NODE(nothing, NOTHING);
+ node->loc_end = node->loc_start + 1;
+ LEAF;
+ line++;
+ }
+ else if (strchr("])", *line))
+ {
+ end = line + 1;
+ prev_end_char = *end, *end = '\0';
+ if (stack_ptr == stack_orig)
+ NEW_ERROR(1, ERROR, "runaway ‘%s’", line);
+ else
+ {
+ stack_ptr--;
+ if (strcmp(line, keyword_stack[stack_ptr]))
+ NEW_ERROR(1, ERROR, "expected ‘%s’ but got ‘%s’", keyword_stack[stack_ptr], line);
+ NEXT;
+ }
+ *end = prev_end_char;
+ line++;
+ }
+ else if (*line == '<')
+ {
+ NEW_NODE(keys, KEYS);
+ NO_JUMP;
+ PURE_KEYS(keys);
+ LEAF;
+ node->loc_end = (size_t)(line - LINE);
+ }
+ else
+ {
+ NEW_NODE(string, STRING);
+ NO_JUMP;
+ CHARS(string);
+ LEAF;
+ node->loc_end = (size_t)(line - LINE);
+ }
+
+ return 0;
+ pfail:
+ return -1;
+}
+
+
+/**
+ * Check that the scopes created in `SEQUENCE` has all been popped
+ *
+ * @param stack_orig The size of the stack when `SEQUENCE` was called
+ */
+static int sequence_fully_popped(size_t stack_orig)
+{
+ if (stack_ptr == stack_orig)
+ return 0;
+ end = line + 1;
+ NEW_ERROR(1, ERROR, "premature end of sequence");
+ while (stack_ptr > stack_orig)
+ {
+ stack_ptr--;
+ NEW_ERROR(1, NOTE, "missing associated ‘%s’", keyword_stack[stack_ptr]);
+ error->start = tree_stack[stack_ptr][0]->loc_start;
+ error->end = tree_stack[stack_ptr][0]->loc_end;
+ }
+
+ return 0;
+ pfail:
+ return -1;
+}
+
+
+
/*** Parsing procedures. ***/
@@ -857,17 +1002,13 @@ static int parse_line(void)
{
/* TODO make this function less complex */
- char* line = LINE;
- char* end;
- char prev_end_char;
- char* original;
- int too_few = 0;
-
+ line = LINE;
SKIP_SPACES(line);
if (end = strchrnul(line, ' '), end == line)
return 0;
prev_end_char = *end, *end = '\0';
original = line;
+ too_few = 0;
redo:
if (in_array)
@@ -1059,7 +1200,7 @@ static int parse_line(void)
#undef inner
#undef node
SEQUENCE(1);
- SEQUENCE_FULLY_POPPED(stack_orig);
+ SEQUENCE_FULLY_POPPED;
#define node supernode
#define inner result
stack_ptr--;
@@ -1074,7 +1215,7 @@ static int parse_line(void)
#undef inner
#undef node
SEQUENCE(1);
- SEQUENCE_FULLY_POPPED(stack_orig);
+ SEQUENCE_FULLY_POPPED;
stack_ptr--;
*end = prev_end_char;
SKIP_SPACES(line);
@@ -1115,7 +1256,7 @@ static int parse_line(void)
#undef inner
#undef node
SEQUENCE(0);
- SEQUENCE_FULLY_POPPED(stack_orig);
+ SEQUENCE_FULLY_POPPED;
#define node supernode
if (*line == ')')
{
@@ -1178,7 +1319,6 @@ int parse_to_tree(const char* restrict filename, mds_kbdc_parsed_t* restrict res
stack_ptr = 0;
keyword_stack = NULL;
tree_stack = NULL;
- line_i = 0;
in_array = 0;
fail_if (xmalloc(result->source_code, 1, mds_kbdc_source_code_t));