/**
* 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 "compile-layout.h"
/* TODO add call stack */
#include "include-stack.h"
#include "builtin-functions.h"
#include "string.h"
#include
#include
#include
/**
* This process's value for `mds_kbdc_tree_t.processed`
*/
#define PROCESS_LEVEL 6
/**
* Tree type constant shortener
*/
#define C(TYPE) MDS_KBDC_TREE_TYPE_##TYPE
/**
* Add an error with “included from here”-notes to the error list
*
* @param NODE:const mds_kbdc_tree_t* The node the triggered the error
* @param SEVERITY:identifier * in `MDS_KBDC_PARSE_ERROR_*` to indicate severity
* @param ...:const char*, ... Error description format string and arguments
* @scope error:mds_kbdc_parse_error_t* Variable where the new error will be stored
*/
#define NEW_ERROR(NODE, SEVERITY, ...) \
NEW_ERROR_WITH_INCLUDES(NODE, includes_ptr, SEVERITY, __VA_ARGS__)
/**
* Beginning of failure clause
*/
#define FAIL_BEGIN pfail: saved_errno = errno
/**
* End of failure clause
*/
#define FAIL_END return errno = saved_errno, -1
/**
* Variable whether the latest created error is stored
*/
static mds_kbdc_parse_error_t* error;
/**
* The parameter of `compile_layout`
*/
static mds_kbdc_parsed_t* restrict result;
/**
* 3: `return` is being processed
* 2: `break` is being processed
* 1: `continue` is being processed
* 0: Neither is being processed
*/
static int break_level = 0;
/**
* Whether a second variant has already been encountered
*/
static int multiple_variants = 0;
/**
* Compile a subtree
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_subtree(mds_kbdc_tree_t* restrict tree);
static int check_function_calls_in_literal(const mds_kbdc_tree_t* restrict tree,
const char* restrict raw, size_t lineoff)
{
(void) tree;
(void) raw;
(void) lineoff;
return 0; /* TODO */
}
static char32_t* parse_string(mds_kbdc_tree_t* restrict tree, const char* restrict raw, size_t lineoff)
{
(void) tree;
(void) raw;
(void) lineoff;
return NULL; /* TODO */
}
static char32_t* parse_keys(mds_kbdc_tree_t* restrict tree, const char* restrict raw, size_t lineoff)
{
(void) tree;
(void) raw;
(void) lineoff;
return NULL; /* TODO */
}
static size_t parse_variable(mds_kbdc_tree_t* restrict tree, const char* restrict raw, size_t lineoff)
{
(void) tree;
(void) raw;
(void) lineoff;
return 0; /* TODO */
}
static int let(size_t variable, const char32_t* restrict string, const mds_kbdc_tree_t* restrict value,
mds_kbdc_tree_t* restrict statement, size_t lineoff, int possibile_shadow_attempt)
{
(void) variable;
(void) string;
(void) value;
(void) statement;
(void) lineoff;
(void) possibile_shadow_attempt;
return 0; /* TODO */
}
static int push_stack(void)
{
return 0; /* TODO */
}
static int pop_stack(void)
{
return 0; /* TODO */
}
static int set_macro(const mds_kbdc_tree_macro_t* restrict macro,
mds_kbdc_include_stack_t* macro_include_stack)
{
(void) macro;
(void) macro_include_stack;
return 0; /* TODO */
}
static int get_macro_lax(const char* restrict macro_name, const mds_kbdc_tree_macro_t** restrict macro,
mds_kbdc_include_stack_t** restrict macro_include_stack)
{
(void) macro_name;
(void) macro;
(void) macro_include_stack;
return 0; /* TODO */
}
static int get_macro(const mds_kbdc_tree_macro_call_t* restrict macro_call,
const mds_kbdc_tree_macro_t** restrict macro,
mds_kbdc_include_stack_t** restrict macro_include_stack)
{
NEW_ERROR(macro_call, ERROR, "macro ‘%s’ as not been defined yet", macro_call->name);
/* return set `*macro = NULL` if `(*macro)->processed == PROCESS_LEVEL` */
(void) macro;
(void) macro_include_stack;
return 0; /* TODO */
pfail:
return -1;
}
/**
* Compile an include-statement
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_include(mds_kbdc_tree_include_t* restrict tree)
{
void* data;
int r;
if (mds_kbdc_include_stack_push(tree, &data))
return -1;
r = compile_subtree(tree->inner);
mds_kbdc_include_stack_pop(data);
return r;
}
/**
* Compile a language-statement
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_language(mds_kbdc_tree_information_language_t* restrict tree)
{
size_t lineoff;
char* restrict code = result->source_code->real_lines[tree->loc_line];
char32_t* restrict data = NULL;
char** old = NULL;
int saved_errno;
for (lineoff = tree->loc_end; code[lineoff] == ' '; lineoff++);
if (result->languages_ptr == result->languages_size)
{
result->languages_size = result->languages_size ? (result->languages_size << 1) : 1;
fail_if (xxrealloc(old, result->languages, result->languages_size, char*));
}
fail_if ((data = parse_string((mds_kbdc_tree_t*)tree, tree->data, lineoff), data == NULL));
fail_if ((code = string_encode(data), code == NULL));
result->languages[result->languages_ptr++] = code;
return 0;
FAIL_BEGIN;
free(old);
free(data);
FAIL_END;
}
/**
* Compile a country-statement
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_country(mds_kbdc_tree_information_country_t* restrict tree)
{
size_t lineoff;
char* restrict code = result->source_code->real_lines[tree->loc_line];
char32_t* restrict data = NULL;
char** old = NULL;
int saved_errno;
for (lineoff = tree->loc_end; code[lineoff] == ' '; lineoff++);
if (result->countries_ptr == result->countries_size)
{
result->countries_size = result->countries_size ? (result->countries_size << 1) : 1;
fail_if (xxrealloc(old, result->countries, result->countries_size, char*));
}
fail_if ((data = parse_string((mds_kbdc_tree_t*)tree, tree->data, lineoff), data == NULL));
fail_if ((code = string_encode(data), code == NULL));
free(data);
result->countries[result->countries_ptr++] = code;
return 0;
FAIL_BEGIN;
free(old);
free(data);
FAIL_END;
}
/**
* Compile a variant-statement
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_variant(mds_kbdc_tree_information_variant_t* restrict tree)
{
size_t lineoff;
char* restrict code = result->source_code->real_lines[tree->loc_line];
char32_t* restrict data = NULL;
int saved_errno;
if (result->variant)
{
if (multiple_variants == 0)
NEW_ERROR(tree, ERROR, "only one ‘variant’ is allowed");
multiple_variants = 1;
return 0;
}
for (lineoff = tree->loc_end; code[lineoff] == ' '; lineoff++);
fail_if ((data = parse_string((mds_kbdc_tree_t*)tree, tree->data, lineoff), data == NULL));
fail_if ((code = string_encode(data), code == NULL));
free(data);
result->variant = code;
return 0;
FAIL_BEGIN;
free(data);
FAIL_END;
}
/**
* Compile a have-statement
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_have(mds_kbdc_tree_assumption_have_t* restrict tree)
{
mds_kbdc_tree_t* node = tree->data;
char32_t* data = NULL;
char32_t** old = NULL;
size_t new_size = (node->type == C(STRING)) ? result->assumed_strings_size : result->assumed_keys_size;
int saved_errno;
new_size = new_size ? (new_size << 1) : 1;
if (node->type == C(STRING))
{
fail_if ((data = parse_string(node, node->string.string, node->loc_start), data == NULL));
if (node->processed == PROCESS_LEVEL)
return free(data), 0;
if (result->assumed_strings_ptr == result->assumed_strings_size)
fail_if (xxrealloc(old, result->assumed_strings, new_size, char*));
result->assumed_strings_size = new_size;
result->assumed_strings[result->assumed_strings_ptr++] = data;
}
else
{
fail_if ((data = parse_keys(node, node->keys.keys, node->loc_start), data == NULL));
if (node->processed == PROCESS_LEVEL)
return free(data), 0;
if (result->assumed_keys_ptr == result->assumed_keys_size)
fail_if (xxrealloc(old, result->assumed_keys, new_size, char*));
result->assumed_keys_size = new_size;
result->assumed_keys[result->assumed_keys_ptr++] = data;
}
return 0;
FAIL_BEGIN;
free(old);
free(data);
FAIL_END;
}
/**
* Compile a have_chars-statement
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_have_chars(mds_kbdc_tree_assumption_have_chars_t* restrict tree)
{
size_t lineoff;
char* restrict code = result->source_code->real_lines[tree->loc_line];
char32_t* restrict data = NULL;
char32_t** old = NULL;
char32_t* restrict character;
size_t n;
int saved_errno;
for (lineoff = tree->loc_end; code[lineoff] == ' '; lineoff++);
fail_if ((data = parse_string((mds_kbdc_tree_t*)tree, tree->chars, lineoff), data == NULL));
for (n = 0; data[n] >= 0; n++);
if (result->assumed_strings_ptr + n > result->assumed_strings_size)
{
result->assumed_strings_size += n;
fail_if (xxrealloc(old, result->assumed_strings, result->assumed_strings_size, char*));
}
while (n--)
{
fail_if (xmalloc(character, 2, char32_t));
character[0] = data[n];
character[1] = -1;
result->assumed_strings[result->assumed_strings_ptr++] = character;
}
free(data);
return 0;
FAIL_BEGIN;
free(data);
free(old);
FAIL_END;
}
/**
* Compile a have_range-statement
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_have_range(mds_kbdc_tree_assumption_have_range_t* restrict tree)
{
size_t lineoff_first;
size_t lineoff_last;
char* restrict code = result->source_code->real_lines[tree->loc_line];
char32_t* restrict first = NULL;
char32_t* restrict last = NULL;
char32_t** old = NULL;
char32_t* restrict character;
size_t n;
int saved_errno;
for (lineoff_first = tree->loc_end; code[lineoff_first] == ' '; lineoff_first++);
for (lineoff_last = lineoff_first + strlen(tree->first); code[lineoff_last] == ' '; lineoff_last++);
fail_if ((first = parse_string((mds_kbdc_tree_t*)tree, tree->first, lineoff_first), first == NULL));
fail_if ((last = parse_string((mds_kbdc_tree_t*)tree, tree->last, lineoff_last), last == NULL));
if (tree->processed == PROCESS_LEVEL)
goto done;
if ((first[0] < 0) || (first[1] >= 0))
{
NEW_ERROR(tree, ERROR, "iteration boundary must be a single character string");
error->start = lineoff_first, lineoff_first = 0;
error->end = error->start + strlen(tree->first);
}
if ((last[0] < 0) || (last[1] >= 0))
{
NEW_ERROR(tree, ERROR, "iteration boundary must be a single character string");
error->start = lineoff_last, lineoff_last = 0;
error->end = error->start + strlen(tree->last);
}
if ((lineoff_first == 0) || (lineoff_last == 0))
goto done;
if (*first > *last)
*first ^= *last, *last ^= *first, *first ^= *last;
n = (size_t)(*last - *first) + 1;
if (result->assumed_strings_ptr + n > result->assumed_strings_size)
{
result->assumed_strings_size += n;
fail_if (xxrealloc(old, result->assumed_strings, result->assumed_strings_size, char*));
}
while (*first != *last)
{
fail_if (xmalloc(character, 2, char32_t));
character[0] = (*first)++;
character[1] = -1;
result->assumed_strings[result->assumed_strings_ptr++] = character;
}
done:
free(first);
free(last);
return 0;
FAIL_BEGIN;
free(first);
free(last);
free(old);
FAIL_END;
}
/**
* Check that all called macros are already defined
*
* @param tree The tree to evaluate
* @return Zero on success, -1 on error, 1 if an undefined macro is used
*/
static int check_marco_calls(const mds_kbdc_tree_t* restrict tree)
{
#define t(...) if (rc |= r = (__VA_ARGS__), r < 0) return r
const mds_kbdc_tree_macro_t* _macro;
mds_kbdc_include_stack_t* _macro_include_stack;
void* data;
int r, rc = 0;
again:
if (tree == NULL)
return rc;
switch (tree->type)
{
case C(INCLUDE):
t (mds_kbdc_include_stack_push(&(tree->include), &data));
t (r = check_marco_calls(tree->include.inner), mds_kbdc_include_stack_pop(data), r);
break;
case C(FOR):
t (check_marco_calls(tree->for_.inner));
break;
case C(IF):
t (check_marco_calls(tree->if_.inner));
t (check_marco_calls(tree->if_.otherwise));
break;
case C(MACRO_CALL):
t (get_macro(&(tree->macro_call), &_macro, &_macro_include_stack));
break;
default:
break;
}
tree = tree->next;
goto again;
(void) _macro;
(void) _macro_include_stack;
#undef t
}
/**
* Check that all called functions in a for-statement are already defined
*
* @param tree The tree to evaluate
* @return Zero on success, -1 on error, 1 if an undefined function is used
*/
static int check_function_calls_in_for(const mds_kbdc_tree_for_t* restrict tree)
{
#define t(...) if (rc |= r = check_function_calls_in_literal(__VA_ARGS__), r < 0) return r
size_t lineoff_first;
size_t lineoff_last;
char* restrict code = result->source_code->real_lines[tree->loc_line];
int r, rc = 0;
for (lineoff_first = tree->loc_end; code[lineoff_first] == ' '; lineoff_first++);
for (lineoff_last = lineoff_first + strlen(tree->first); code[lineoff_last] == ' '; lineoff_last++);
t ((const mds_kbdc_tree_t*)tree, tree->first, lineoff_first);
t ((const mds_kbdc_tree_t*)tree, tree->last, lineoff_last);
return rc;
#undef t
}
/**
* Check that all called functions in an if-statement are already defined
*
* @param tree The tree to evaluate
* @return Zero on success, -1 on error, 1 if an undefined function is used
*/
static int check_function_calls_in_if(const mds_kbdc_tree_if_t* restrict tree)
{
size_t lineoff;
char* restrict code = result->source_code->real_lines[tree->loc_line];
for (lineoff = tree->loc_end; code[lineoff] == ' '; lineoff++);
return check_function_calls_in_literal((const mds_kbdc_tree_t*)tree, tree->condition, lineoff);
}
/**
* Check that all called functions in a key-combination are already defined
*
* @param tree The tree to evaluate
* @return Zero on success, -1 on error, 1 if an undefined function is used
*/
static int check_function_calls_in_keys(const mds_kbdc_tree_keys_t* restrict tree)
{
return check_function_calls_in_literal((const mds_kbdc_tree_t*)tree, tree->keys, tree->loc_end);
}
/**
* Check that all called functions in a string are already defined
*
* @param tree The tree to evaluate
* @return Zero on success, -1 on error, 1 if an undefined function is used
*/
static int check_function_calls_in_string(const mds_kbdc_tree_string_t* restrict tree)
{
return check_function_calls_in_literal((const mds_kbdc_tree_t*)tree, tree->string, tree->loc_end);
}
/**
* Check that all called functions are already defined
*
* @param tree The tree to evaluate
* @return Zero on success, -1 on error, 1 if an undefined function is used
*/
static int check_function_calls(const mds_kbdc_tree_t* restrict tree)
{
#define t(...) if (rc |= r = (__VA_ARGS__), r < 0) return r
void* data;
int r, rc = 0;
again:
if (tree == NULL)
return rc;
switch (tree->type)
{
case C(INCLUDE):
t (mds_kbdc_include_stack_push(&(tree->include), &data));
t (r = check_function_calls(tree->include.inner), mds_kbdc_include_stack_pop(data), r);
break;
case C(FOR):
t (check_function_calls_in_for(&(tree->for_)));
t (check_function_calls(tree->for_.inner));
break;
case C(IF):
t (check_function_calls_in_if(&(tree->if_)));
t (check_function_calls(tree->if_.inner));
t (check_function_calls(tree->if_.otherwise));
break;
case C(LET): t (check_function_calls(tree->let.value)); break;
case C(ARRAY): t (check_function_calls(tree->array.elements)); break;
case C(KEYS): t (check_function_calls_in_keys(&(tree->keys))); break;
case C(STRING): t (check_function_calls_in_string(&(tree->string))); break;
case C(MAP): t (check_function_calls(tree->map.sequence)); break;
default:
break;
}
tree = tree->next;
goto again;
#undef t
}
/**
* Check that a callable's name-suffix is correct
*
* @param tree The tree to inspect
* @return Zero on sucess, -1 on error, 1 if the name-suffix in invalid
*/
static int check_name_suffix(struct mds_kbdc_tree_callable* restrict tree)
{
const char* restrict name = strchr(tree->name, '/');
const char* restrict code = result->source_code->real_lines[tree->loc_line];
if (name == NULL)
{
NEW_ERROR(tree, ERROR, "name-suffix is missing");
goto name_error;
}
if (*++name == '\0')
{
NEW_ERROR(tree, ERROR, "empty name-suffix");
goto name_error;
}
if (!strcmp(name, "0"))
return 0;
if (*name == '\0')
{
NEW_ERROR(tree, ERROR, "leading zero in name-suffix");
goto name_error;
}
for (; *name; name++)
if ((*name < '0') || ('0' < *name))
{
NEW_ERROR(tree, ERROR, "name-suffix may only contain digits");
goto name_error;
}
return 0;
pfail:
return -1;
name_error:
error->start = tree->loc_end;
while (code[error->start] == ' ')
error->start++;
error->end = error->start + strlen(tree->name);
tree->processed = PROCESS_LEVEL;
return 1;
}
/**
* Compile a function
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_function(mds_kbdc_tree_function_t* restrict tree)
{
#define t(expr) fail_if ((r = (expr), r < 0)); if (r) tree->processed = PROCESS_LEVEL
const mds_kbdc_tree_function_t* function;
mds_kbdc_include_stack_t* function_include_stack;
mds_kbdc_include_stack_t* our_include_stack = NULL;
char* suffixless;
char* suffix_start;
size_t arg_count;
int r, saved_errno;
t (check_name_suffix((struct mds_kbdc_tree_callable*)tree));
suffixless = strdup(tree->name);
fail_if (suffixless == NULL);
suffix_start = strchr(suffixless, '/');
*suffix_start++ = '\0';
arg_count = (size_t)atoll(suffix_start);
if (builtin_function_defined(suffixless, arg_count))
{
NEW_ERROR(tree, ERROR, "function ‘%s’ is already defined as a builtin function", tree->name);
return 0;
}
/* TODO check for redefinition */ function = NULL, function_include_stack = NULL;
if (function)
{
NEW_ERROR(tree, ERROR, "function ‘%s’ is already defined", tree->name);
fail_if (mds_kbdc_include_stack_restore(function_include_stack));
NEW_ERROR(function, NOTE, "previously defined here");
fail_if (mds_kbdc_include_stack_restore(our_include_stack));
mds_kbdc_include_stack_free(our_include_stack);
return 0;
}
t (check_marco_calls(tree->inner));
t (check_function_calls(tree->inner));
/* TODO add definition */
return 0;
FAIL_BEGIN;
mds_kbdc_include_stack_free(our_include_stack);
FAIL_END;
#undef t
}
/**
* Compile a macro
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_macro(mds_kbdc_tree_macro_t* restrict tree)
{
#define t(expr) fail_if ((r = (expr), r < 0)); if (r) tree->processed = PROCESS_LEVEL
const mds_kbdc_tree_macro_t* macro;
mds_kbdc_include_stack_t* macro_include_stack;
mds_kbdc_include_stack_t* our_include_stack = NULL;
int r, saved_errno;
fail_if ((our_include_stack = mds_kbdc_include_stack_save(), our_include_stack == NULL));
t (check_name_suffix((struct mds_kbdc_tree_callable*)tree));
t (get_macro_lax(tree->name, ¯o, ¯o_include_stack));
if (macro)
{
NEW_ERROR(tree, ERROR, "macro ‘%s’ is already defined", tree->name);
fail_if (mds_kbdc_include_stack_restore(macro_include_stack));
NEW_ERROR(macro, NOTE, "previously defined here");
fail_if (mds_kbdc_include_stack_restore(our_include_stack));
mds_kbdc_include_stack_free(our_include_stack);
return 0;
}
t (check_marco_calls(tree->inner));
t (check_function_calls(tree->inner));
t (set_macro(tree, our_include_stack));
return 0;
FAIL_BEGIN;
mds_kbdc_include_stack_free(our_include_stack);
FAIL_END;
#undef t
}
/**
* Compile a for-loop
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_for(mds_kbdc_tree_for_t* restrict tree)
{
size_t lineoff_first;
size_t lineoff_last;
size_t lineoff_var;
char* restrict code = result->source_code->real_lines[tree->loc_line];
char32_t* restrict first = NULL;
char32_t* restrict last = NULL;
char32_t diff;
char32_t character[2];
size_t variable;
int saved_errno;
for (lineoff_first = tree->loc_end; code[lineoff_first] == ' '; lineoff_first++);
for (lineoff_last = lineoff_first + strlen(tree->first); code[lineoff_last] == ' '; lineoff_last++);
for (lineoff_last += strlen("to"); code[lineoff_last] == ' '; lineoff_last++);
for (lineoff_var = lineoff_last + strlen(tree->variable); code[lineoff_var] == ' '; lineoff_var++);
for (lineoff_var += strlen("as"); code[lineoff_var] == ' '; lineoff_var++);
fail_if ((first = parse_string((mds_kbdc_tree_t*)tree, tree->first, lineoff_first), first == NULL));
fail_if ((last = parse_string((mds_kbdc_tree_t*)tree, tree->last, lineoff_last), last == NULL));
fail_if ((variable = parse_variable((mds_kbdc_tree_t*)tree, tree->variable, lineoff_var), variable == 0));
if (tree->processed == PROCESS_LEVEL)
goto done;
if ((first[0] < 0) || (first[1] >= 0))
{
NEW_ERROR(tree, ERROR, "iteration boundary must be a single character string");
error->start = lineoff_first, lineoff_first = 0;
error->end = error->start + strlen(tree->first);
}
if ((last[0] < 0) || (last[1] >= 0))
{
NEW_ERROR(tree, ERROR, "iteration boundary must be a single character string");
error->start = lineoff_last, lineoff_last = 0;
error->end = error->start + strlen(tree->last);
}
if ((lineoff_first == 0) || (lineoff_last == 0))
goto done;
character[1] = -1;
for (diff = (*first > *last) ? -1 : +1; (*first != *last) && (break_level < 2); *first += diff)
{
break_level = 0;
character[0] = *first;
fail_if (let(variable, character, NULL, (mds_kbdc_tree_t*)tree, lineoff_var, 1));
fail_if (compile_subtree(tree->inner));
}
if (break_level < 3)
break_level = 0;
done:
free(first);
free(last);
return 0;
FAIL_BEGIN;
free(first);
free(last);
FAIL_END;
}
/**
* Compile an if-statement
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_if(mds_kbdc_tree_if_t* restrict tree)
{
size_t lineoff;
char* restrict code = result->source_code->real_lines[tree->loc_line];
char32_t* restrict data = NULL;
int ok, saved_errno;
size_t i;
for (lineoff = tree->loc_end; code[lineoff] == ' '; lineoff++);
fail_if ((data = parse_string((mds_kbdc_tree_t*)tree, tree->condition, lineoff), data == NULL));
fail_if (tree->processed == PROCESS_LEVEL);
for (ok = 1, i = 0; data[i] >= 0; i++)
ok &= !!(data[i]);
free(data);
return compile_subtree(ok ? tree->inner : tree->otherwise);
FAIL_BEGIN;
free(data);
FAIL_END;
}
/**
* Compile a let-statement
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_let(mds_kbdc_tree_let_t* restrict tree)
{
size_t lineoff;
char* restrict code = result->source_code->real_lines[tree->loc_line];
mds_kbdc_tree_t* value = NULL;
size_t variable;
int saved_errno;
for (lineoff = tree->loc_end; code[lineoff] == ' '; lineoff++);
fail_if ((variable = parse_variable((mds_kbdc_tree_t*)tree, tree->variable, lineoff), variable == 0));
if (tree->processed == PROCESS_LEVEL)
return 0;
fail_if ((value = mds_kbdc_tree_dup(tree->value), value == NULL));
fail_if (compile_subtree(value));
if ((tree->processed = value->processed) == PROCESS_LEVEL)
return 0;
fail_if (let(variable, NULL, value, NULL, 0, 0));
free(value);
return 0;
FAIL_BEGIN;
free(value);
FAIL_END;
}
/*
* `compile_keys`, `compile_string`, `compile_array` and `evaluate_element`
* are do only compilation subprocedures that may alter the compiled nodes.
* This is because (1) `compile_let`, `compile_map` and `compile_macro_call`
* needs the compiled values, and (2) only duplicates of nodes of types
* `C(KEYS)`, `C(STRING)` and `C(ARRAY)` are compiled, as they can only be
* found with `C(LET)`-, `C(MAP)`- and `C(MACRO_CALL)`-nodes.
*/
/**
* Evaluate an element or argument in a mapping-, value-, let-statement or macro call
*
* @param node The element to evaluate
* @return Zero on success, -1 on error, 1 if the element is invalid
*/
static int evaluate_element(mds_kbdc_tree_t* restrict node)
{
char32_t* restrict data = NULL;
int bad = 0;
for (; node; node = node->next)
{
if (node->type == C(STRING))
fail_if ((data = parse_string(node, node->string.string, node->loc_start), data == NULL));
if (node->type == C(KEYS))
fail_if ((data = parse_keys(node, node->keys.keys, node->loc_start), data == NULL));
free(node->string.string);
node->string.string = string_encode(data);
free(data);
fail_if (node->string.string == NULL);
bad |= (node->processed == PROCESS_LEVEL);
}
return bad;
pfail:
return -1;
}
/**
* Compile a key-combination
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_keys(mds_kbdc_tree_keys_t* restrict tree)
{
return evaluate_element((mds_kbdc_tree_t*)tree) < 0 ? -1 : 0;
}
/**
* Compile a string
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_string(mds_kbdc_tree_string_t* restrict tree)
{
return evaluate_element((mds_kbdc_tree_t*)tree) < 0 ? -1 : 0;
}
/**
* Compile an array
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_array(mds_kbdc_tree_array_t* restrict tree)
{
int r = evaluate_element(tree->elements);
if (r < 0)
return -1;
if (r)
tree->processed = PROCESS_LEVEL;
return 0;
}
/**
* Check that a chain of strings and key-combinations
* does not contain NULL characters
*
* @param tree The tree to check
* @return Zero on success, -1 on error, 1 if any of
* the elements contain a NULL character
*/
static int check_nonnul(mds_kbdc_tree_t* restrict tree)
{
const char* restrict string;
int rc = 0;
again:
if (tree == NULL)
return rc;
for (string = tree->string.string; *string; string++)
if ((string[0] == (char)0xC0) && (string[1] == (char)0x80))
{
NEW_ERROR(tree, ERROR, "NULL characters are not allowed in mappings");
tree->processed = PROCESS_LEVEL;
rc = 1;
break;
}
tree = tree->next;
goto again;
pfail:
return -1;
}
/**
* Compile a mapping- or value-statement
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_map(mds_kbdc_tree_map_t* restrict tree)
{
int bad = 0;
mds_kbdc_tree_t* seq = NULL;
mds_kbdc_tree_t* res = NULL;
int saved_errno;
fail_if ((seq = mds_kbdc_tree_dup(tree->sequence), seq = NULL));
fail_if ((bad |= evaluate_element(seq), bad < 0));
if (tree->result)
{
fail_if ((res = mds_kbdc_tree_dup(tree->result), res = NULL));
fail_if ((bad |= evaluate_element(res), bad < 0));
}
if (bad)
goto done;
if (tree->result)
{
fail_if ((bad |= check_nonnul(seq), bad < 0));
fail_if ((bad |= check_nonnul(res), bad < 0));
if (bad)
goto done;
/* TODO */
}
else
{
/* TODO */
}
done:
mds_kbdc_tree_free(seq);
mds_kbdc_tree_free(res);
return 0;
FAIL_BEGIN;
mds_kbdc_tree_free(seq);
mds_kbdc_tree_free(res);
FAIL_END;
}
/**
* Compile a macro call
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_macro_call(mds_kbdc_tree_macro_call_t* restrict tree)
{
mds_kbdc_tree_t* arg = NULL;
mds_kbdc_tree_t* arg_;
const mds_kbdc_tree_macro_t* macro;
mds_kbdc_include_stack_t* macro_include_stack;
mds_kbdc_include_stack_t* our_include_stack = NULL;
size_t variable;
int bad, saved_errno;
fail_if ((arg = mds_kbdc_tree_dup(tree->arguments), arg = NULL));
fail_if ((bad = evaluate_element(arg), bad < 0));
if (bad)
return 0;
fail_if (get_macro(tree, ¯o, ¯o_include_stack));
if (macro == NULL)
goto done;
fail_if (push_stack());
for (arg_ = arg; arg_; arg_ = arg_->next)
fail_if (let(variable, NULL, arg_, NULL, 0, 0));
fail_if ((our_include_stack = mds_kbdc_include_stack_save(), our_include_stack == NULL));
fail_if (mds_kbdc_include_stack_restore(macro_include_stack));
fail_if (compile_subtree(macro->inner));
fail_if (mds_kbdc_include_stack_restore(our_include_stack));
mds_kbdc_include_stack_free(our_include_stack), our_include_stack = NULL;
fail_if (pop_stack());
done:
break_level = 0;
mds_kbdc_tree_free(arg);
return 0;
FAIL_BEGIN;
mds_kbdc_tree_free(arg);
mds_kbdc_include_stack_free(our_include_stack);
FAIL_END;
}
/**
* Compile a subtree
*
* @param tree The tree to compile
* @return Zero on success, -1 on error
*/
static int compile_subtree(mds_kbdc_tree_t* restrict tree)
{
#define t(expr) if (r = (expr), r < 0) return r
#define c(type) t (compile_##type(&(tree->type)))
#define c_(type) t (compile_##type(&(tree->type##_)))
int r;
again:
if (tree == NULL)
return 0;
if (tree->processed == PROCESS_LEVEL)
goto next;
switch (tree->type)
{
case C(INFORMATION):
t (compile_subtree(tree->information.inner));
break;
case C(INFORMATION_LANGUAGE): c (language); break;
case C(INFORMATION_COUNTRY): c (country); break;
case C(INFORMATION_VARIANT): c (variant); break;
case C(INCLUDE): c (include); break;
case C(FUNCTION): c (function); break;
case C(MACRO): c (macro); break;
case C(ASSUMPTION):
t ((includes_ptr == 0) && compile_subtree(tree->assumption.inner));
break;
case C(ASSUMPTION_HAVE): c (have); break;
case C(ASSUMPTION_HAVE_CHARS): c (have_chars); break;
case C(ASSUMPTION_HAVE_RANGE): c (have_range); break;
case C(FOR): c_ (for); break;
case C(IF): c_ (if); break;
case C(LET): c (let); break;
case C(KEYS): c (keys); break;
case C(STRING): c (string); break;
case C(ARRAY): c (array); break;
case C(MAP): c (map); break;
case C(MACRO_CALL): c (macro_call); break;
case C(RETURN): break_level = 3; break;
case C(BREAK): break_level = 2; break;
case C(CONTINUE): break_level = 1; break;
default:
break;
}
next:
if (break_level)
return 0;
tree = tree->next;
goto again;
#undef c_
#undef c
#undef t
}
/**
* Compile the layout code
*
* @param result_ `result` from `eliminate_dead_code`, will be updated
* @return -1 if an error occursed that cannot be stored in `result`, zero otherwise
*/
int compile_layout(mds_kbdc_parsed_t* restrict result_)
{
int r;
mds_kbdc_include_stack_begin(result = result_);
r = compile_subtree(result_->tree);
return mds_kbdc_include_stack_end(), r;
}
#undef FAIL_END
#undef FAIL_BEGIN
#undef NEW_ERROR
#undef C
#undef PROCESS_LEVEL