Created
September 30, 2009 19:33
-
-
Save dnadlinger/198377 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module redimple; | |
import tools.mersenne, tools.compat : logf = log; | |
import Float = tango.text.convert.Float; | |
import Integer = tango.text.convert.Integer; | |
import tango.io.Console; | |
alias char[] string; | |
bool equal( T )( T a, T b ) { | |
static if ( is( T : Object ) ) | |
return a is b; | |
else | |
return a == b; | |
} | |
bool has( T )( T[] array, T elem ) { | |
foreach ( e; array ) | |
if ( equal( e, elem ) ) | |
return true; | |
return false; | |
} | |
int search( T )( T[] array, T[] match ) { | |
if ( array.length < match.length ) | |
return -1; | |
foreach ( id, elem; array[ 0 .. $ - match.length + 1 ] ) | |
if ( array[ id .. id + match.length ] == match ) | |
return id; | |
return -1; | |
} | |
void remove( T )( ref T[] array, T match ) { | |
if ( array.length == 1 ) { | |
array.length = 0; | |
return; | |
} | |
size_t pos = size_t.max; | |
foreach ( i, elem; array ) | |
if ( elem == match ) | |
pos = i; | |
if ( pos == size_t.max ) { | |
Cerr( "Can't remove - not in array!" ).newline; | |
asm { int 3; } | |
} | |
array[ pos ] = array[ $ - 1 ]; | |
array.length = array.length - 1; | |
} | |
T pop( T )( ref T[] array ) { | |
T res = array[ $ - 1 ]; | |
array = array[ 0 .. $ - 1 ]; | |
return res; | |
} | |
typeof ( T + U ) max( T, U )( T a, U b ) { | |
return a > b ? a : b; | |
} | |
import tools.base : stuple, Stuple, startsWith; | |
class Graph ( T ) { | |
T[] nodes; | |
T[][ T ] edges; | |
size_t _incoming[ T ]; | |
void addNode( T node ) { | |
nodes ~= node; | |
} | |
void addEdge( T src, T dest ) { | |
if ( !( dest in _incoming ) ) | |
_incoming[ dest ] = 1; | |
_incoming[ dest ]++; | |
if ( !nodes.has( src ) ) | |
addNode( src ); | |
if ( !nodes.has( dest ) ) | |
addNode( dest ); | |
if ( !( src in edges ) ) | |
edges[ src ] = [ ]; | |
if ( !edges[ src ].has( dest ) ) | |
edges[ src ] ~= dest; | |
} | |
string[ Stuple!( T, T ) ] annotate; | |
void addEdge( T src, T dest, string anno ) { | |
addEdge( src, dest ); | |
annotate[ stuple( src, dest ) ] = anno; | |
} | |
Graph dup() { | |
auto res = new Graph; | |
res.nodes = nodes.dup; | |
foreach ( id, entry; edges ) { | |
res.edges[ id ] = entry.dup; | |
} | |
return res; | |
} | |
void rmEdge( T src, T dest ) { | |
_incoming[ dest ]--; | |
if ( !edges[ src ].length ) | |
return; | |
edges[ src ][ edges[ src ].search( [ dest ] ) ] = edges[ src ][ $ - 1 ]; | |
edges[ src ] = edges[ src ][ 0 .. $ - 1 ]; | |
} | |
size_t incoming( T node ) { | |
if ( !( node in _incoming ) ) | |
return 0; | |
return _incoming[ node ]; | |
} | |
size_t outgoing( T node ) { | |
return edges[ node ].length; | |
} | |
} | |
alias Graph!( string ) G; | |
/// Tarjan algorithm copied from Wikipedia. Thanks guys :) | |
T[][] tarjan( T )( Graph!( T ) G, T start ) { | |
auto max_dfs = 0; // counter for dfs | |
T[] stack; | |
T[][] res; | |
size_t[ T ] dfs, lowlink; | |
void _tarjan( T v ) { | |
dfs[ v ] = lowlink[ v ] = max_dfs++; | |
stack ~= v; | |
T[] dests = [ ]; | |
if ( auto p = v in G.edges ) | |
dests = *p; | |
foreach ( dest; dests ) { | |
if ( dest / notin / dfs ) { | |
_tarjan( dest ); | |
if ( lowlink[ dest ] < lowlink[ v ] ) | |
lowlink[ v ] = lowlink[ dest ]; | |
} else if ( stack.has( dest ) ) | |
//if (dfs[dest]<lowlink[v]) lowlink[v]=dfs[dest]; | |
if ( lowlink[ dest ] < lowlink[ v ] ) | |
lowlink[ v ] = lowlink[ dest ]; | |
} | |
if ( lowlink[ v ] == dfs[ v ] ) { // the root of a strongly connected component | |
res.length = res.length + 1; | |
T _v; | |
do { | |
_v = stack.pop(); | |
res[ $ - 1 ] ~= _v; | |
} while ( v != _v ); | |
} | |
} | |
_tarjan( start ); | |
return res; | |
} | |
import tools.functional; | |
interface excluder { | |
bool opCall( string from, string to ); | |
} | |
struct Import { | |
string mod, what; | |
} | |
Import[] imports( string mod, excluder xc ) { | |
auto filename = modToFile( mod ); | |
if ( !filename.exists() ) | |
throw new Exception( "File " ~ filename ~ " cannot be found" ); | |
char[] rid = Integer.toString( rand() ); | |
Import[][] list; | |
char[] contents = cast( char[] ) filename.read(); | |
char[][] lines = contents.split( ";" ); | |
foreach ( line; lines ) { | |
line = line.strip(); | |
if ( !( line.startsWith( "import" ) || line.find( " import " ) != -1 ) ) { | |
continue; | |
} | |
bool pub = line.startsWith( "public " ) !is null; | |
line = line[ line.find( "import" ) + 6 .. $ ]; | |
Import[] currentImports; | |
auto sel = line.find( ":" ); | |
string what; | |
if ( sel != -1 ) { | |
what = line[ sel + 1 .. $ ]; | |
line = line[ 0 .. sel ]; | |
} | |
foreach ( part; line.split( "," ) ) | |
currentImports ~= Import( part.strip(), pub ? "#"[] : ""[] ); | |
currentImports[ $ - 1 ].what ~= what.strip(); | |
list ~= currentImports; | |
} | |
Import[] foo; | |
foreach ( sub; list ) | |
foreach ( i; sub ) { | |
if ( !i.mod.length ) | |
continue; | |
if ( !xc( mod, i.mod ) ) | |
continue; | |
foo ~= i; | |
} | |
return foo; | |
} | |
string modToFile( string mod ) { | |
return mod.replace( ".", "/" ) ~ ".d"; | |
} | |
void log( char[] l ) { | |
} | |
G genImportsGraph( string mod, excluder xc, bool incExtNodes = false ) { | |
auto res = new G; | |
string[] scanned; | |
int depth = 0; | |
void recurse( string mod ) { | |
depth++; | |
scope ( exit ) | |
depth--; | |
log( "recurse into " ~ mod ~ " at level " ~ Integer.toString( depth ) ); | |
scope ( exit ) | |
log( "done" ); | |
if ( scanned.has( mod ) ) | |
return; | |
scanned ~= mod; | |
res.addNode( mod ); | |
foreach ( entry; mod.imports( xc ) ) { | |
if ( mod == entry.mod ) | |
continue; | |
if ( modToFile( entry.mod ).exists() ) { | |
log( " " ~ mod ~ " -> " ~ entry.mod ); | |
res.addEdge( mod, entry.mod, entry.what ); | |
recurse( entry.mod ); | |
} else { | |
log( modToFile( entry.mod ) ~ " doesn't exist" ); | |
if ( incExtNodes ) | |
res.addEdge( mod, entry.mod, entry.what ); | |
} | |
} | |
} | |
mod.recurse(); | |
log( "Graph generated" ); | |
return res; | |
} | |
class Package { | |
string name; | |
Package[ string ] pkgs; | |
string[] modules; | |
this( string name, G graph ) { | |
log( "Creating package " ~ name ); | |
this.name = name; | |
foreach ( entry; graph.nodes ) { | |
if ( entry.length !> name.length ) | |
continue; | |
if ( entry[ 0 .. name.length ] != name ) | |
continue; | |
char[] subname = entry[ name.length .. $ ]; | |
if ( subname[ 0 ] == '.' ) | |
subname = subname[ 1 .. $ ]; | |
auto dotpos = subname.find( "." ); | |
if ( ( dotpos != -1 ) && !( subname[ 0 .. dotpos ] in pkgs ) ) | |
pkgs[ subname[ 0 .. dotpos ] ] = new Package( | |
( name.length ? name ~ "." : "" ) ~ subname[ 0 .. dotpos ], | |
graph ); | |
else if ( name.length || ( dotpos == -1 ) ) | |
modules ~= entry; | |
} | |
} | |
} | |
char[] quote( char[] e ) { | |
return '"' ~ e ~ '"'; | |
} | |
char[] rmPkg( char[] inp ) { | |
if ( inp.find( "." ) == -1 ) | |
return inp; | |
return inp[ inp.rfind( "." ) + 1 .. $ ]; | |
} | |
bool leftright = false; | |
string getsettings( string mod ) { | |
string rmvStrings( string inp ) { | |
bool inString = false; | |
string res; | |
char mode; | |
int skip = 0; | |
foreach ( ch; inp ) { | |
if ( skip ) { | |
skip--; | |
continue; | |
} | |
if ( !inString ) { | |
res ~= ch; | |
if ( ( ch == '"' ) || ( ch == '\'' ) ) { | |
inString = true; | |
mode = ch; | |
continue; | |
} | |
} | |
if ( ch == '\\' ) { | |
skip = 1; | |
continue; | |
} | |
if ( ch == mode ) | |
inString = false; | |
} | |
return res; | |
} | |
auto | |
preparts = ( ( cast( string ) mod.modToFile().read() ) ).rmvStrings().split( | |
"\n" ) / map / ( string s ) { | |
if ( s.find( "//" ) != -1 ) | |
return s[ 0 .. s.find( "//" ) ]; | |
return s; | |
} / reduce( ""[] ) / ex!( "a, b -> a~b" ); | |
string s; | |
bool inComment = false; | |
foreach ( pos, ch; preparts[ 0 .. $ - 1 ] ) { | |
if ( !inComment ) | |
s ~= ch; | |
if ( preparts[ pos .. pos + 2 ] == "/*" ) { | |
s ~= "*"; | |
inComment = true; | |
} | |
if ( preparts[ pos .. pos + 2 ] == "*/" ) { | |
s ~= "/"; | |
inComment = false; | |
} | |
} | |
auto parts = s.replace( "\n", "" ).replace( "\r", "" ).split( "{" ); | |
auto | |
classes = parts / select / ( string s ) { | |
auto pos = s.find( "class " ); | |
if ( pos == -1 ) | |
return false; | |
return ( "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890_" ).find( | |
s[ pos - 1 ] ) == -1; | |
} / map / ( string s ) { | |
return s[ s.find( "class " ) + 6 .. $ ].strip(); | |
} / uniq; | |
char[] res; | |
bool skipName; | |
foreach ( entry; classes ) | |
if ( entry.find( mod.rmPkg() ) != -1 ) | |
skipName = true; | |
/+if (classes.length) res="shape=record, style="~quote(/*"rounded, filled"*/"filled")~", label="~'"'~ | |
(leftright?"{":"")~"{ "~(skipName?"":("{ "~mod.rmPkg()~" } ")); | |
// else res="shape=record, style="~quote(/*"rounded, filled"*/"filled")~", label=\""~mod.rmPkg(); | |
else +/res = "label=\"" ~ mod.rmPkg(); | |
/+string[] splitSkipBrackets(string inp, char sep) { | |
size_t bracketlevel=0; | |
string[] res; string buf; | |
foreach (ch; inp) { | |
buf~=ch; | |
if (!bracketlevel) { | |
if (ch=='(') { ++bracketlevel; continue; } | |
if (ch==sep) { res~=buf[0..$-1]; buf=""; } | |
} else { if (ch=='(') ++bracketlevel; else if (ch==')') --bracketlevel; } | |
} | |
if (buf.length) res~=buf; return res; | |
} | |
foreach (entry; classes) { | |
string[] supers; | |
auto ddp = entry.find(":"); | |
if (ddp!=-1) { | |
supers=entry[ddp+1..$].replace("\"", "_").splitSkipBrackets(','); | |
entry=entry[0..ddp]; | |
} | |
auto ep = entry.find(" extends "); | |
if (ep != -1) { | |
supers = entry[ep+9 .. %2 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment