/**
* mds — A micro-display server
* Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (maandree@kth.se)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "tree.h"
#include <libmdsserver/macros.h>
#undef xfree
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/* Helper `typedef`:s */
typedef struct mds_kbdc_tree_nesting mds_kbdc_tree_nesting_t;
typedef struct mds_kbdc_tree_callable mds_kbdc_tree_callable_t;
typedef struct mds_kbdc_tree_information_data mds_kbdc_tree_information_data_t;
/**
* Tree type constant shortener
*/
#define C(t) MDS_KBDC_TREE_TYPE_##t
/**
* Initialise a tree node
*
* @param this The memory slot for the tree node
* @param type The type of the node
*/
void
mds_kbdc_tree_initialise(mds_kbdc_tree_t *restrict this, int type)
{
memset(this, 0, sizeof(mds_kbdc_tree_t));
this->type = type;
}
/**
* Create a tree node
*
* @param type The type of the node
* @return The tree node, `NULL` on error
*/
mds_kbdc_tree_t *
mds_kbdc_tree_create(int type)
{
mds_kbdc_tree_t *this;
fail_if (xmalloc(this, 1, mds_kbdc_tree_t));
mds_kbdc_tree_initialise(this, type);
return this;
fail:
return NULL;
}
/**
* Common procedure for `mds_kbdc_tree_destroy` and `mds_kbdc_tree_destroy_nonrecursive`
*
* @param this The tree node
* @param recursive Whether subtree should be destroyed and freed
*/
static void mds_kbdc_tree_destroy_(mds_kbdc_tree_t *restrict this, int recursive)
{
#define V(type, var) (((type)this)->var)
#define xfree(t, v) (free(V(t, v)), V(t, v) = NULL)
#define xdestroy(t, v) (recursive ? (mds_kbdc_tree_destroy_(V(t, v), 1), xfree(t, v)) : (V(t, v) = NULL))
mds_kbdc_tree_t *prev = NULL;
mds_kbdc_tree_t *first = this;
again:
if (!this)
return;
switch (this->type) {
case C(INFORMATION):
case C(ASSUMPTION):
case C(ALTERNATION):
case C(UNORDERED):
case C(ORDERED):
xdestroy(mds_kbdc_tree_nesting_t *, inner);
break;
case C(INFORMATION_LANGUAGE):
case C(INFORMATION_COUNTRY):
case C(INFORMATION_VARIANT):
xfree(mds_kbdc_tree_information_data_t *, data);
break;
case C(FUNCTION):
case C(MACRO):
xfree(mds_kbdc_tree_callable_t *, name);
xdestroy(mds_kbdc_tree_callable_t *, inner);
break;
case C(INCLUDE):
xfree(mds_kbdc_tree_include_t *, filename);
xdestroy(mds_kbdc_tree_include_t *, inner);
mds_kbdc_source_code_free(this->include.source_code);
break;
case C(ASSUMPTION_HAVE):
xdestroy(mds_kbdc_tree_assumption_have_t *, data);
break;
case C(ASSUMPTION_HAVE_CHARS):
xfree(mds_kbdc_tree_assumption_have_chars_t *, chars);
break;
case C(ASSUMPTION_HAVE_RANGE):
xfree(mds_kbdc_tree_assumption_have_range_t *, first);
xfree(mds_kbdc_tree_assumption_have_range_t *, last);
break;
case C(FOR):
xfree(mds_kbdc_tree_for_t *, first);
xfree(mds_kbdc_tree_for_t *, last);
xfree(mds_kbdc_tree_for_t *, variable);
xdestroy(mds_kbdc_tree_for_t *, inner);
break;
case C(IF):
xfree(mds_kbdc_tree_if_t *, condition);
xdestroy(mds_kbdc_tree_if_t *, inner);
xdestroy(mds_kbdc_tree_if_t *, otherwise);
break;
case C(LET):
xfree(mds_kbdc_tree_let_t *, variable);
xdestroy(mds_kbdc_tree_let_t *, value);
break;
case C(MAP):
xdestroy(mds_kbdc_tree_map_t *, sequence);
xdestroy(mds_kbdc_tree_map_t *, result);
break;
case C(ARRAY):
xdestroy(mds_kbdc_tree_array_t *, elements);
break;
case C(KEYS):
case C(STRING):
case C(COMPILED_KEYS):
case C(COMPILED_STRING):
xfree(mds_kbdc_tree_keys_t *, keys);
/* We are abusing the similaries of the structures. */
break;
case C(MACRO_CALL):
xfree(mds_kbdc_tree_macro_call_t *, name);
xdestroy(mds_kbdc_tree_macro_call_t *, arguments);
break;
default:
break;
}
prev = this;
this = this->next;
if (prev != first)
free(prev);
goto again;
#undef xdestroy
#undef xfree
#undef V
}
/**
* Release all resources stored in a tree node,
* without freeing the node itself or freeing
* or destroying inner `mds_kbdc_tree_t*`:s
*
* @param this The tree node
*/
void
mds_kbdc_tree_destroy_nonrecursive(mds_kbdc_tree_t *restrict this)
{
mds_kbdc_tree_destroy_(this, 0);
}
/**
* Release all resources stored in a tree node,
* without freeing or destroying inner
* `mds_kbdc_tree_t*`:s, but free this node's
* allocation
*
* @param this The tree node
*/
void
mds_kbdc_tree_free_nonrecursive(mds_kbdc_tree_t *restrict this)
{
mds_kbdc_tree_destroy_nonrecursive(this);
free(this);
}
/**
* Release all resources stored in a tree node
* recursively, but do not free the allocation
* of the tree node
*
* @param this The tree node
*/
void
mds_kbdc_tree_destroy(mds_kbdc_tree_t *restrict this)
{
mds_kbdc_tree_destroy_(this, 1);
}
/**
* Release all resources stored in a tree node
* recursively, and free the allocation
* of the tree node
*
* @param this The tree node
*/
void
mds_kbdc_tree_free(mds_kbdc_tree_t *restrict this)
{
mds_kbdc_tree_destroy(this);
free(this);
}
/**
* Duplicate a subtree and goto `pfail` on failure
*
* @param member:identifer The member in the tree to duplicate
*/
#define T(member) fail_if (t->member && !(n->member = mds_kbdc_tree_dup(t->member)))
/**
* Duplicate a string and goto `pfail` on failure
*
* @param member:identifer The member in the tree to duplicate
*/
#define S(member) fail_if (t->member && xstrdup(n->member, t->member))
/**
* Duplicate an UTF-32-string and goto `pfail` on failure
*
* @param member:identifer The member in the tree to duplicate
*/
#define Z(member) fail_if (t->member && !(n->member = string_dup(t->member)))
/**
* Duplicate a source code structure and goto `pfail` on failure
*
* @param member:identifer The member in the tree to copied
*/
#define R(member) fail_if (t->member && !(n->member = mds_kbdc_source_code_dup(t->member)))
/**
* Create a duplicate of a tree node and its children
*
* @param this The tree node
* @return A duplicate of `this`, `NULL` on error
*/
mds_kbdc_tree_t *
mds_kbdc_tree_dup(const mds_kbdc_tree_t *restrict this)
{
#define t this
#define n (*node)
mds_kbdc_tree_t *rc = NULL;
mds_kbdc_tree_t **node = &rc;
int saved_errno;
again:
if (!t)
return rc;
fail_if (xcalloc(n, 1, mds_kbdc_tree_t));
n->type = t->type;
n->loc_line = t->loc_line;
n->loc_start = t->loc_start;
n->loc_end = t->loc_end;
n->processed = t->processed;
switch (this->type) {
case C(INFORMATION):
case C(ASSUMPTION):
case C(ALTERNATION):
case C(UNORDERED):
case C(ORDERED):
T(ordered.inner);
break;
case C(FUNCTION):
case C(MACRO):
S(macro.name);
T(macro.inner);
break;
case C(ASSUMPTION_HAVE):
T(have.data);
break;
case C(ARRAY):
T(array.elements);
break;
case C(LET):
S(let.variable);
T(let.value);
break;
case C(MACRO_CALL):
S(macro_call.name);
T(macro_call.arguments);
break;
case C(INFORMATION_LANGUAGE):
case C(INFORMATION_COUNTRY):
case C(INFORMATION_VARIANT):
S(variant.data);
break;
case C(INCLUDE):
S(include.filename);
T(include.inner);
R(include.source_code);
break;
case C(ASSUMPTION_HAVE_CHARS):
S(have_chars.chars);
break;
case C(KEYS):
S(keys.keys);
break;
case C(STRING):
S(string.string);
break;
case C(COMPILED_KEYS):
Z(compiled_keys.keys);
break;
case C(COMPILED_STRING):
Z(compiled_string.string);
break;
case C(ASSUMPTION_HAVE_RANGE):
S(have_range.first);
S(have_range.last);
break;
case C(FOR):
S(for_.first);
S(for_.last);
S(for_.variable);
T(for_.inner);
break;
case C(IF):
S(if_.condition);
T(if_.inner);
T(if_.otherwise);
break;
case C(MAP):
T(map.sequence);
T(map.result);
break;
default:
break;
}
t = t->next;
node = &(n->next);
goto again;
fail:
saved_errno = errno;
mds_kbdc_tree_free(rc);
return errno = saved_errno, NULL;
#undef n
#undef t
}
#undef R
#undef Z
#undef S
#undef T
/**
* Convert the tree to a specialised subtype and
* prints its type and code location
*
* @param LOWERCASE:identifer The name of subtype
* @param NOTATION:const char* The notation for the subtype
*/
#define NODE(LOWERCASE, NOTATION)\
const mds_kbdc_tree_##LOWERCASE##_t *node;\
node = (const mds_kbdc_tree_##LOWERCASE##_t *)this;\
fprintf(output, "%*.s(\033[01m%s\033[00m", indent, "", NOTATION);\
fprintf(output, " \033[36m(@ %zu %zu-%zu)\033[00m",\
node->loc_line + 1, node->loc_start, node->loc_end)
/**
* Print a member for `node` which is a subtree
*
* @param MEMBER:identifier The tree structure's member
*/
#define BRANCH(MEMBER)\
do {\
if (node->MEMBER) {\
fprintf(output, "\n%*.s(.%s\n", indent + 2, "", #MEMBER);\
mds_kbdc_tree_print_indented(node->MEMBER, output, indent + 4);\
fprintf(output, "%*.s)", indent + 2, "");\
} else {\
fprintf(output, "\n%*.s(.%s \033[35mnil\033[00m)", indent + 2, "", #MEMBER);\
}\
} while (0)
/**
* End a tree which has at least one member that is a subtree
*/
#define COMPLEX\
fprintf(output, "\n%*.s)\n", indent, "")
/**
* Print a member for `node` which is a string
*
* @param MEMBER:identifier The tree structure's member
*/
#define STRING(MEMBER)\
do {\
if (node->MEMBER)\
fprintf(output, " ‘\033[32m%s\033[00m’", node->MEMBER);\
else\
fprintf(output, " \033[35mnil\033[00m");\
} while (0)
/**
* Print a member for `node` which is a string,
* and end the tree
*
* @param MEMBER:identifier The tree structure's member
*/
#define SIMPLE(MEMBER)\
do {\
STRING(MEMBER);\
fprintf(output, ")\n");\
} while (0)
/**
* Print a tree which has only one member,
* and whose member is a string
*
* @param LOWERCASE:identifier See `NODE`
* @param NOTATION:const char* See `NODE`
* @param MEMBER:identifier See `STRING`
*/
#define SIMPLEX(LOWERCASE, NOTATION, MEMBER)\
{\
NODE(LOWERCASE, NOTATION);\
SIMPLE(MEMBER);\
}\
break
/**
* Print a tree which has exactly two members,
* and whose members is are strings
*
* @param LOWERCASE:identifier See `NODE`
* @param NOTATION:const char* See `NODE`
* @param FIRST:identifier See `STRING`, the first member
* @param LAST:identifier See `STRING`, the second member
*/
#define DUPLEX(LOWERCASE, NOTATION, FIRST, LAST)\
{\
NODE(LOWERCASE, NOTATION);\
STRING(FIRST);\
SIMPLE(LAST);\
}\
break
/**
* Print a tree which has exactly one member,
* and whose members is a subtree
*
* @param LOWERCASE:identifier See `NODE`
* @param NOTATION:const char* See `NODE`
* @param MEMBER:identifier See `BRANCH`
*/
#define NESTING(LOWERCASE, NOTATION, MEMBER)\
{\
NODE(LOWERCASE, NOTATION);\
BRANCH(MEMBER);\
COMPLEX;\
}\
break
/**
* Print a tree which has exactly two members,
* and whose first member is a string and second
* member is a subtree
*
* @param LOWERCASE:identifier See `NODE`
* @param NOTATION:const char* See `NODE`
* @param NAMER:identifier See `STRING`
* @param MEMBER:identifier See `BRANCH`
*/
#define NAMED_NESTING(LOWERCASE, NOTATION, NAMER, MEMBER)\
{\
NODE(LOWERCASE, NOTATION);\
STRING(NAMER);\
BRANCH(MEMBER);\
COMPLEX;\
}\
break
/**
* Print a tree which has no members
*
* @param NOTATION:const char* See `NODE`
*/
#define NOTHING(NOTATION)\
fprintf(output, "%*.s(\033[01m%s\033[00m", indent, "", NOTATION);\
fprintf(output, " \033[36m(@ %zu %zu-%zu)\033[00m",\
this->loc_line + 1, this->loc_start, this->loc_end);\
fprintf(output, ")\n");\
break
/**
* Print a tree into a file
*
* @param this The tree node
* @param output The output file
* @param indent The indent
*/
static void
mds_kbdc_tree_print_indented(const mds_kbdc_tree_t *restrict this, FILE *output, int indent)
{
again:
if (!this)
return;
switch (this->type) {
/* These have their break built into their macro. */
case C(INFORMATION): NESTING(information, "information", inner);
case C(INFORMATION_LANGUAGE): SIMPLEX(information_language, "language", data);
case C(INFORMATION_COUNTRY): SIMPLEX(information_country, "country", data);
case C(INFORMATION_VARIANT): SIMPLEX(information_variant, "variant", data);
case C(INCLUDE): NAMED_NESTING(include, "include", filename, inner);
case C(FUNCTION): NAMED_NESTING(function, "function", name, inner);
case C(MACRO): NAMED_NESTING(macro, "macro", name, inner);
case C(ASSUMPTION): NESTING(assumption, "assumption", inner);
case C(ASSUMPTION_HAVE): NESTING(assumption_have, "have", data);
case C(ASSUMPTION_HAVE_CHARS): SIMPLEX(assumption_have_chars, "have_chars", chars);
case C(ASSUMPTION_HAVE_RANGE): DUPLEX(assumption_have_range, "have_range", first, last);
case C(LET): NAMED_NESTING(let, "let", variable, value);
case C(ARRAY): NESTING(array, "array", elements);
case C(KEYS): SIMPLEX(keys, "keys", keys);
case C(STRING): SIMPLEX(string, "string", string);
case C(NOTHING): NOTHING("nothing");
case C(ALTERNATION): NESTING(alternation, "alternation", inner);
case C(UNORDERED): NESTING(unordered, "unordered", inner);
case C(ORDERED): NESTING(ordered, "ordered", inner);
case C(MACRO_CALL): NAMED_NESTING(macro_call, "macro_call", name, arguments);
case C(RETURN): NOTHING("return");
case C(BREAK): NOTHING("break");
case C(CONTINUE): NOTHING("continue");
case C(COMPILED_KEYS):
{
NODE(compiled_keys, "compiled_keys");
if (node->keys)
fprintf(output, " ‘\033[32m%s\033[00m’", string_encode(node->keys));
else
fprintf(output, " \033[35mnil\033[00m");
fprintf(output, ")\n");
}
break;
case C(COMPILED_STRING):
{
NODE(compiled_string, "compiled_string");
if (node->string)
fprintf(output, " ‘\033[32m%s\033[00m’", string_encode(node->string));
else
fprintf(output, " \033[35mnil\033[00m");
fprintf(output, ")\n");
}
break;
case C(FOR):
{
NODE(for, "for");
STRING(first);
STRING(last);
fprintf(output, " (.variable");
STRING(variable);
fprintf(output, ")");
BRANCH(inner);
COMPLEX;
}
break;
case C(IF):
{
NODE(if, "if");
STRING(condition);
BRANCH(inner);
BRANCH(otherwise);
COMPLEX;
}
break;
case C(MAP):
{
NODE(map, "map");
BRANCH(sequence);
BRANCH(result);
COMPLEX;
}
break;
default:
abort();
break;
}
this = this->next;
goto again;
}
/**
* Print a tree into a file
*
* @param this The tree node
* @param output The output file
*/
void
mds_kbdc_tree_print(const mds_kbdc_tree_t *restrict this, FILE *output)
{
mds_kbdc_tree_print_indented(this, output, 0);
}
#undef NOTHING
#undef NAMED_NESTING
#undef NESTING
#undef DUPLEX
#undef SIMPLEX
#undef SIMPLE
#undef STRING
#undef COMPLEX
#undef BRANCH
#undef NODE
#undef C