From 190bcaf982869388b508a0a4c97cff62fbb73038 Mon Sep 17 00:00:00 2001 From: werner Date: Mon, 26 Apr 2010 15:18:01 +0000 Subject: [PATCH] Added a topological sort algorithm, for use when dumping. - tsort.h, tsort.c, Makefile: stable topological sort with priorities - fpd.l, fpd.y: added directive %tsort to test-drive the sort algorithm - README: documented %tsort git-svn-id: http://svn.openmoko.org/trunk/eda/fped@5942 99fdad57-331a-0410-800a-d7fa5415bdb3 --- Makefile | 2 +- README | 10 +++- TODO | 1 - fpd.l | 2 + fpd.y | 38 ++++++++++++- tsort.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tsort.h | 25 +++++++++ 7 files changed, 233 insertions(+), 5 deletions(-) create mode 100644 tsort.c create mode 100644 tsort.h diff --git a/Makefile b/Makefile index 964248b..30ff3ca 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ UPLOAD = werner@sita.openmoko.org:public_html/fped/ OBJS = fped.o expr.o coord.o obj.o delete.o inst.o util.o error.o \ unparse.o file.o dump.o kicad.o postscript.o meas.o \ - layer.o overlap.o hole.o \ + layer.o overlap.o hole.o tsort.o \ cpp.o lex.yy.o y.tab.o \ gui.o gui_util.o gui_style.o gui_inst.o gui_status.o gui_canvas.o \ gui_tool.o gui_over.o gui_meas.o gui_frame.o gui_frame_drag.o diff --git a/README b/README index b5234a8..294d712 100644 --- a/README +++ b/README @@ -582,14 +582,15 @@ would print "width = 1mm" Experimental: debugging directives ---------------------------------- -For debugging and regression tests, fped supports the following commands -that mimick the effect of GUI operations: +For debugging and regression tests, fped supports the following commands, +most of which mimick the effect of GUI operations: %del %move [] %print %dump %exit +%tsort { - | + | [] ... } %del and %move take as their first argument the name of the vector or object to manipulate. For this purpose, also objects can be labeled. @@ -608,3 +609,8 @@ anchor index vec/frame line/rect/pad arc measurement %dump writes the footprint definition in the fped language to standard output. %exit immediately exits fped, without invoking the GUI. + +%tsort is used to test-drive the topological sort algorithm. The items +in the curly braces are declarations of nodes with (-) or without +(+) decay or edges in the partial order. The optional number is +the edge's priority. See tsort.c for details. diff --git a/TODO b/TODO index 896b1da..f0528ea 100644 --- a/TODO +++ b/TODO @@ -69,7 +69,6 @@ Future directions: - live update of value when entering strings and expressions ? - advanced: non-standard solder mask - advanced: solder paste exceptions (subtractive, additive) -- advanced: holes - advanced: silk line width - future: consider editing non-canvas items (e.g., variable names/values) in place diff --git a/fpd.l b/fpd.l index 1b1a1aa..9d5624d 100644 --- a/fpd.l +++ b/fpd.l @@ -133,6 +133,8 @@ SP [\t ]* return TOK_DBG_DUMP; } "%exit" { BEGIN(NOKEYWORD); return TOK_DBG_EXIT; } +"%tsort" { BEGIN(NOKEYWORD); + return TOK_DBG_TSORT; } [a-zA-Z_][a-zA-Z_0-9]*: { *strchr(yytext, ':') = 0; yylval.id = unique(yytext); diff --git a/fpd.y b/fpd.y index a834490..742a23d 100644 --- a/fpd.y +++ b/fpd.y @@ -22,6 +22,7 @@ #include "meas.h" #include "gui_status.h" #include "dump.h" +#include "tsort.h" #include "fpd.h" @@ -46,6 +47,8 @@ static int n_vars, n_values; static const char *id_sin, *id_cos, *id_sqrt; +static struct tsort *tsort; + static struct frame *find_frame(const char *name) { @@ -289,7 +292,7 @@ static void append_root_frame(void) %token TOK_MEAS TOK_MEASX TOK_MEASY TOK_UNIT %token TOK_NEXT TOK_NEXT_INVERTED TOK_MAX TOK_MAX_INVERTED %token TOK_DBG_DEL TOK_DBG_MOVE TOK_DBG_PRINT TOK_DBG_DUMP -%token TOK_DBG_EXIT +%token TOK_DBG_EXIT TOK_DBG_TSORT %token NUMBER %token STRING @@ -481,6 +484,39 @@ debug_item: { exit(0); } + | TOK_DBG_TSORT '{' + { + tsort = begin_tsort(); + } + sort_items '}' + { + void **sort, **walk; + + sort = end_tsort(tsort); + for (walk = sort; *walk; walk++) + printf("%s\n", (char *) *walk); + free(sort); + } + ; + +sort_items: + | sort_items '+' ID + { + add_node(tsort, (void *) $3, 0); + } + | sort_items '-' ID + { + add_node(tsort, (void *) $3, 1); + } + | sort_items ID ID opt_num + { + struct node *a, *b; + + /* order is important here ! */ + a = add_node(tsort, (void *) $2, 0); + b = add_node(tsort, (void *) $3, 0); + add_edge(a, b, $4.n); + } ; table: diff --git a/tsort.c b/tsort.c new file mode 100644 index 0000000..d54fe6e --- /dev/null +++ b/tsort.c @@ -0,0 +1,160 @@ +/* + * tsort.c - Topological sort + * + * Written 2010 by Werner Almesberger + * Copyright 2010 by Werner Almesberger + * + * 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 2 of the License, or + * (at your option) any later version. + */ + +/* + * We use a slight variation of Kahn's algorithm. The difference is that we add + * a priority. Edges with the highest priority get selected before edges with + * lower priority. + * + * We maintain the initial list of nodes in the order in which they were added. + * Therefore, the first node with inbound edges will always be sorted first. + * E.g., the root frame. + * + * add_node and add_edge can be invoked multiple times with the same + * parameters. In the case of add_node, simply the existing node is returned. + * In the case of add_edge, the new edge's priority is added to the priority of + * the previous edges. + * + * Priority is accumulated in a node until the node is output. If a node has + * the "decay" flag set, it resets the priorities of all other nodes when + * output. E.g., when outputting a vector, all priorities accumulated from + * previous vectors (towards referencing them with ".") lose their effect. + * + * Last but not least, the algorithm is stable: a pre-existing order that + * conflicts neither with the partial order nor the priorities is preserved. + * + * Thus, we have the following sorting criteria, in decreasing importance: + * - the destination if an edge never precedes its origin + * - higher priority comes before lower priority + * - earlier add_node comes before later + */ + + +#include +#include +#include + +#include "util.h" +#include "tsort.h" + + +struct edge { + struct node *to; + int priority; /* edge priority */ + struct edge *next; +}; + +struct node { + void *user; + struct edge *edges; /* outbound edges */ + int incoming; /* number of incoming edges */ + int priority; /* cumulative node priority */ + int decay; /* all node prio. decay after issuing this */ + struct node *next; +}; + +struct tsort { + struct node *nodes; + struct node **next_node; + int n_nodes; +}; + + +void add_edge(struct node *from, struct node *to, int priority) +{ + struct edge **edge; + + for (edge = &from->edges; *edge; edge = &(*edge)->next) + if ((*edge)->to == to) { + (*edge)->priority += priority; + return; + } + *edge = alloc_type(struct edge); + (*edge)->to = to; + (*edge)->priority = priority; + (*edge)->next = NULL; + to->incoming++; +} + + +struct node *add_node(struct tsort *tsort, void *user, int decay) +{ + struct node *node; + + for (node = tsort->nodes; node; node = node->next) + if (node->user == user) + return node; + node = alloc_type(struct node); + node->user = user; + node->edges = NULL; + node->incoming = 0; + node->priority = 0; + node->decay = decay; + node->next = NULL; + *tsort->next_node = node; + tsort->next_node = &node->next; + tsort->n_nodes++; + return node; +} + + +struct tsort *begin_tsort(void) +{ + struct tsort *tsort; + + tsort = alloc_type(struct tsort); + tsort->nodes = NULL; + tsort->next_node = &tsort->nodes; + tsort->n_nodes = 0; + return tsort; +} + + +void **end_tsort(struct tsort *tsort) +{ + struct node **walk, **first, *node; + struct edge *edge; + void **res; + int n = 0; + + res = alloc_size(sizeof(void *)*(tsort->n_nodes+1)); + while (1) { + first = NULL; + for (walk = &tsort->nodes; *walk; walk = &(*walk)->next) { + if ((*walk)->incoming) + continue; + if (!first || (*first)->priority < (*walk)->priority) + first = walk; + } + if (!first) + break; + if ((*first)->decay) + for (node = tsort->nodes; node; node = node->next) + node->priority = 0; + node = *first; + *first = node->next; + res[n++] = node->user; + while (node->edges) { + edge = node->edges; + edge->to->incoming--; + edge->to->priority += edge->priority; + node->edges = edge->next; + free(edge); + } + free(node); + } + if (tsort->nodes) /* we have at least one cycle */ + abort(); + free(tsort); + res[n] = NULL; + return res; +} diff --git a/tsort.h b/tsort.h new file mode 100644 index 0000000..7e10731 --- /dev/null +++ b/tsort.h @@ -0,0 +1,25 @@ +/* + * tsort.h - Topological sort + * + * Written 2010 by Werner Almesberger + * Copyright 2010 by Werner Almesberger + * + * 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 2 of the License, or + * (at your option) any later version. + */ + +#ifndef TSORT_H +#define TSORT_H + +struct node; +struct tsort; + +struct node *add_node(struct tsort *tsort, void *user, int decay); +void add_edge(struct node *from, struct node *to, int priority); + +struct tsort *begin_tsort(void); +void **end_tsort(struct tsort *tsort); + +#endif /* !TSORT_H */