aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <maandree@operamail.com>2014-11-28 18:01:28 +0100
committerMattias Andrée <maandree@operamail.com>2014-11-28 18:01:28 +0100
commit4100c42bc6a64597abf81cd9dddc2773f0932f6e (patch)
tree8dcd891f22dc865667658288e6cde2ec88e3b101
parentm comment (diff)
downloadmds-4100c42bc6a64597abf81cd9dddc2773f0932f6e.tar.gz
mds-4100c42bc6a64597abf81cd9dddc2773f0932f6e.tar.bz2
mds-4100c42bc6a64597abf81cd9dddc2773f0932f6e.tar.xz
mds-kbdc: simplification of unordered subseq:s
Signed-off-by: Mattias Andrée <maandree@operamail.com>
-rw-r--r--doc/info/mds.texinfo19
-rw-r--r--src/mds-kbdc/simplify-tree.c170
-rw-r--r--src/mds-kbdc/tree.c3
3 files changed, 168 insertions, 24 deletions
diff --git a/doc/info/mds.texinfo b/doc/info/mds.texinfo
index 6f5e5f5..a4bb672 100644
--- a/doc/info/mds.texinfo
+++ b/doc/info/mds.texinfo
@@ -5461,6 +5461,25 @@ may however choose to discourage unordered
subsequence inside unordered subsequence
because of readability issues.
+Unordered subsequences longer than 5 elements
+cannot compile under normal circumstances. This
+is eliminiation of unordered subsequences grows
+superexponentially, and thus is probably an error
+than can cause memory exhaustion and unrealistic
+compilation-time. Therefore, if an unordered
+subsequences longer than 5 elements is used the
+compiler required that the @option{--force} flag
+is used and that the unordered subsequences uses
+double brackets:
+
+@example
+<dead compose> ("1" "2" "3" "4" "5") : "120"
+<dead compose> (("1" "2" "3" "4" "5" "6")) : "720"
+<dead compose> (("1" "2" "3" "4" "5" "6" "7")) : "5040"
+<dead compose> (("1" "2" "3" "4" "5" "6" "7" "8")) : "40320"
+<dead compose> (("1" "2" "3" "4" "5" "6" "7" "8" "9")) : "362880"
+@end example
+
@node Keyboard Layout Identification
diff --git a/src/mds-kbdc/simplify-tree.c b/src/mds-kbdc/simplify-tree.c
index 3cd97b8..851c219 100644
--- a/src/mds-kbdc/simplify-tree.c
+++ b/src/mds-kbdc/simplify-tree.c
@@ -16,12 +16,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "simplify-tree.h"
+#include "globals.h"
#include <stdlib.h>
#include <string.h>
#include <alloca.h>
+
/**
* This processes value for `mds_kbdc_tree_t.processed`
*/
@@ -29,6 +31,12 @@
/**
+ * Tree type constant shortener
+ */
+#define C(TYPE) MDS_KBDC_TREE_TYPE_##TYPE
+
+
+/**
* Add an error the to error list
*
* @param NODE:const mds_kbdc_tree_t* The node the triggered the error
@@ -100,10 +108,10 @@ static int simplify_macro_call(mds_kbdc_tree_macro_call_t* restrict tree)
/* Remove ‘.’:s. */
processed = tree->processed, tree->processed = PROCESS_LEVEL;
for (here = &(tree->arguments); *here;)
- if ((*here)->type != MDS_KBDC_TREE_TYPE_NOTHING)
+ if ((*here)->type != C(NOTHING))
here = &((*here)->next);
else
- while (*here && (*here)->type == MDS_KBDC_TREE_TYPE_NOTHING)
+ while (*here && (*here)->type == C(NOTHING))
{
argument = (*here)->next, (*here)->next = NULL;
if ((processed != PROCESS_LEVEL) && ((*here)->processed != PROCESS_LEVEL))
@@ -121,7 +129,7 @@ static int simplify_macro_call(mds_kbdc_tree_macro_call_t* restrict tree)
/* Eliminate alterations. */
for (argument = dup_argument; argument; argument = argument->next, argument_index++)
{
- if (argument->type != MDS_KBDC_TREE_TYPE_ALTERNATION)
+ if (argument->type != C(ALTERNATION))
continue;
/* Detach next statement, we do not want to duplicate all following statements. */
@@ -264,7 +272,7 @@ static int simplify_alternation(mds_kbdc_tree_alternation_t* restrict tree)
if (tree->inner == NULL)
{
NEW_ERROR(tree, ERROR, "empty alternation");
- tree->type = MDS_KBDC_TREE_TYPE_NOTHING;
+ tree->type = C(NOTHING);
tree->processed = PROCESS_LEVEL;
return 0;
}
@@ -281,7 +289,7 @@ static int simplify_alternation(mds_kbdc_tree_alternation_t* restrict tree)
/* Simplify. */
for (here = &(tree->inner); (argument = *here); redo ? (redo = 0) : (here = &(argument->next), 0))
- if ((argument->type == MDS_KBDC_TREE_TYPE_NOTHING) && (argument->processed != PROCESS_LEVEL))
+ if ((argument->type == C(NOTHING)) && (argument->processed != PROCESS_LEVEL))
{
/* Test multiple nothings. */
if (first_nothing == NULL)
@@ -292,14 +300,14 @@ static int simplify_alternation(mds_kbdc_tree_alternation_t* restrict tree)
NEW_ERROR(first_nothing, NOTE, "first ‘.’ was here");
}
}
- else if (argument->type == MDS_KBDC_TREE_TYPE_ALTERNATION)
+ else if (argument->type == C(ALTERNATION))
{
/* Alternation nesting. */
if (argument->processed != PROCESS_LEVEL)
NEW_ERROR(argument, WARNING, "alternation inside alternation is unnessary");
if (simplify_alternation(&(argument->alternation)))
return -1;
- if (argument->type == MDS_KBDC_TREE_TYPE_ALTERNATION)
+ if (argument->type == C(ALTERNATION))
{
/* Remember the alternation and the argument that follows it. */
eliminated_argument = argument;
@@ -319,14 +327,13 @@ static int simplify_alternation(mds_kbdc_tree_alternation_t* restrict tree)
}
redo = 1;
}
- else if (argument->type == MDS_KBDC_TREE_TYPE_UNORDERED)
+ else if (argument->type == C(UNORDERED))
{
/* Nesting unordered subsequence,
simplifies to alternation of ordered subsequence, or simpler. */
- NEW_ERROR(argument, WARNING, "unorderd subsequence inside alternation is discouraged");
+ NEW_ERROR(argument, WARNING, "unordered subsequence inside alternation is discouraged");
if (simplify_unordered(&(argument->unordered)))
return -1;
- argument->processed = PROCESS_LEVEL;
redo = 1;
}
@@ -337,6 +344,73 @@ static int simplify_alternation(mds_kbdc_tree_alternation_t* restrict tree)
/**
+ * Create a chain of ordered subsequence covering all
+ * permutations of a set of subtrees
+ *
+ * @param elements The subtrees, chained
+ * @return Chain of ordered subsequence, `NULL` on error
+ */
+static mds_kbdc_tree_t* create_permutations(mds_kbdc_tree_t* elements)
+{
+ mds_kbdc_tree_t* first = NULL;
+ mds_kbdc_tree_t** here = &first;
+ mds_kbdc_tree_t** previous_next = &elements;
+ mds_kbdc_tree_t* argument;
+ mds_kbdc_tree_t* temp;
+ mds_kbdc_tree_t* subperms = NULL;
+ mds_kbdc_tree_t* perm;
+ mds_kbdc_tree_t ordered;
+ int saved_errno, no_perms;
+
+ for (previous_next = &elements; (argument = *previous_next); previous_next = &((*previous_next)->next))
+ {
+ /* Created ordered alternative for a permutation prototype. */
+ mds_kbdc_tree_initialise(&ordered, C(ORDERED));
+ /* Select the first element. */
+ temp = argument->next, argument->next = NULL;
+ ordered.ordered.inner = mds_kbdc_tree_dup(argument);
+ argument->next = temp;
+ fail_if (ordered.ordered.inner == NULL);
+ /* Create subpermutations. */
+ *previous_next = argument->next;
+ argument->next = NULL;
+ no_perms = (elements == NULL);
+ subperms = create_permutations(elements);
+ argument->next = *previous_next;
+ *previous_next = argument;
+ fail_if (no_perms ? 0 : subperms == NULL);
+ /* Join first element with subpermutations. */
+ while (subperms)
+ {
+ /* Join. */
+ fail_if ((perm = mds_kbdc_tree_dup(&ordered), perm == NULL));
+ perm->ordered.inner = subperms->ordered.inner;
+ /* Add the permutation to the chain. */
+ *here = perm;
+ here = &(perm->next);
+ /* Select next permutation. */
+ temp = subperms;
+ subperms = subperms->next;
+ temp->next = NULL;
+ mds_kbdc_tree_free(temp);
+ }
+ /* Destroy prototype. */
+ mds_kbdc_tree_destroy(&ordered);
+ }
+
+ return first;
+
+ pfail:
+ saved_errno = errno;
+ mds_kbdc_tree_free(first);
+ mds_kbdc_tree_free(subperms);
+ mds_kbdc_tree_destroy(&ordered);
+ errno = saved_errno;
+ return NULL;
+}
+
+
+/**
* Simplify an unordered subsequence-subtree
*
* @param tree The unordered subsequence-subtree
@@ -344,15 +418,29 @@ static int simplify_alternation(mds_kbdc_tree_alternation_t* restrict tree)
*/
static int simplify_unordered(mds_kbdc_tree_unordered_t* restrict tree)
{
+ mds_kbdc_tree_t* arguments;
mds_kbdc_tree_t* argument;
mds_kbdc_tree_t* temp;
mds_kbdc_tree_t** here;
+ int allow_long = 0;
+ size_t argument_count;
+ int argv_force = 0; /* TODO globals.h */
+
+ /* Test for ‘(( ))’. */
+ if (tree->inner && (tree->inner->next == NULL) && (tree->inner->type == C(UNORDERED)))
+ {
+ temp = tree->inner;
+ tree->inner = tree->inner->unordered.inner;
+ temp->unordered.inner = NULL;
+ mds_kbdc_tree_free(temp);
+ allow_long = 1;
+ }
/* Test emptyness. */
if (tree->inner == NULL)
{
NEW_ERROR(tree, ERROR, "empty unordered subsequence");
- tree->type = MDS_KBDC_TREE_TYPE_NOTHING;
+ tree->type = C(NOTHING);
tree->processed = PROCESS_LEVEL;
return 0;
}
@@ -369,10 +457,10 @@ static int simplify_unordered(mds_kbdc_tree_unordered_t* restrict tree)
/* Remove ‘.’:s. */
for (here = &(tree->inner); *here;)
- if ((*here)->type != MDS_KBDC_TREE_TYPE_NOTHING)
+ if ((*here)->type != C(NOTHING))
here = &((*here)->next);
else
- while (*here && (*here)->type == MDS_KBDC_TREE_TYPE_NOTHING)
+ while (*here && (*here)->type == C(NOTHING))
{
argument = (*here)->next, (*here)->next = NULL;
NEW_ERROR(*here, WARNING, "‘.’ inside unordered subsequences has no effect");
@@ -380,7 +468,40 @@ static int simplify_unordered(mds_kbdc_tree_unordered_t* restrict tree)
*here = argument;
}
- /* TODO alternation, unordered (warn: unreadable) */
+ /* Simplify. */
+ for (argument = tree->inner, argument_count = 0; argument; argument = argument->next, argument_count++)
+ if (argument->type == C(ALTERNATION))
+ {
+ if (simplify_alternation(&(argument->alternation)))
+ return -1;
+ argument->processed = PROCESS_LEVEL;
+ }
+ else if (argument->type == C(UNORDERED))
+ {
+ NEW_ERROR(argument, WARNING, "unordered subsequence inside unordered subsequence is discouraged");
+ if (simplify_unordered(&(argument->unordered)))
+ return -1;
+ argument->processed = PROCESS_LEVEL;
+ }
+
+ /* Check the size of the subsequence. */
+ if ((argument_count > 5) && (allow_long * argv_force == 0))
+ {
+ if (allow_long == 0)
+ NEW_ERROR(argument, ERROR, "unordered subsequence longer than 5 elements need double brackets");
+ else if (argv_force == 0)
+ NEW_ERROR(argument, ERROR, "unordered subsequence of size %zu found, requires ‘--force’ to compile",
+ argument_count);
+ return 0;
+ }
+
+ /* Generate permutations. */
+ tree->type = C(ALTERNATION);
+ tree->processed = PROCESS_LEVEL;
+ arguments = tree->inner;
+ if (tree->inner = create_permutations(arguments), tree->inner == NULL)
+ return tree->inner = arguments, -1;
+ mds_kbdc_tree_free(arguments);
return 0;
pfail:
@@ -405,16 +526,16 @@ static int simplify(mds_kbdc_tree_t* restrict tree)
switch (tree->type)
{
- case MDS_KBDC_TREE_TYPE_INFORMATION: s (information.inner); break;
- case MDS_KBDC_TREE_TYPE_FUNCTION: s (function.inner); break;
- case MDS_KBDC_TREE_TYPE_MACRO: s (macro.inner); break;
- case MDS_KBDC_TREE_TYPE_ASSUMPTION: s (assumption.inner); break;
- case MDS_KBDC_TREE_TYPE_FOR: s (for_.inner); break;
- case MDS_KBDC_TREE_TYPE_IF: s (if_.inner); s (if_.otherwise); break;
- case MDS_KBDC_TREE_TYPE_MAP: /* TODO */ break;
- case MDS_KBDC_TREE_TYPE_ALTERNATION: S (alternation); break;
- case MDS_KBDC_TREE_TYPE_UNORDERED: S (unordered); break;
- case MDS_KBDC_TREE_TYPE_MACRO_CALL: S (macro_call); break;
+ case C(INFORMATION): s (information.inner); break;
+ case C(FUNCTION): s (function.inner); break;
+ case C(MACRO): s (macro.inner); break;
+ case C(ASSUMPTION): s (assumption.inner); break;
+ case C(FOR): s (for_.inner); break;
+ case C(IF): s (if_.inner); s (if_.otherwise); break;
+ case C(MAP): /* TODO */ break;
+ case C(ALTERNATION): S (alternation); break;
+ case C(UNORDERED): S (unordered); break;
+ case C(MACRO_CALL): S (macro_call); break;
default:
break;
}
@@ -441,5 +562,6 @@ int simplify_tree(mds_kbdc_parsed_t* restrict result_)
#undef NEW_ERROR
+#undef C
#undef PROCESS_LEVEL
diff --git a/src/mds-kbdc/tree.c b/src/mds-kbdc/tree.c
index 1e7dc07..a107408 100644
--- a/src/mds-kbdc/tree.c
+++ b/src/mds-kbdc/tree.c
@@ -23,6 +23,9 @@
+/**
+ * Tree type constant shortener
+ */
#define C(t) MDS_KBDC_TREE_TYPE_##t