/* strip.d From the dig library by Burton Radons, http://www.opend.org/dig/index.html Author: Burton Radons (and J C Calvarese, ) License: Public Domain Purpose: Strip all whitespace from D source files. Slightly modified by: J C Calvarese, http://jcc_7.tripod.com/d/ Suggested compilation options: dmd -unittest -version=dstripcopy -O -inline strip.d Usage Example: strip ..\..\win32\*.d dest (Note: the "dest" directory must already exist.) */ import std.ctype, std.file; import std.string; /* for memmove */ import std.c.windows.windows; /* for WIN32_FIND_DATA */ char spaceCharacter = '\n'; /* Strip comments from a block of code. */ char [] dstripComments (char [] data) { char* s = data, e = s + data.length; char* b = data, o = s; bit start(char[] string) { if (s > e - string.length) return false; if (s [0..string.length] != string) return false; s += string.length; return true; } bit match(char[] string) { if (s > e - string.length) return false; if (s [0..string.length] != string) return false; s += string.length; return true; } bit skipString() { if (match ("\"")) { *o ++ = '\"'; while (s < e) { *o ++ = *s ++; if (s [-1] == '\\') *o ++ = *s ++; else if (s [-1] == '\"') break; } } else if (match ("\'")) { *o ++ = '\''; while (s < e) { *o ++ = *s ++; if (s [-1] == '\'') break; } } else return false; return true; } while (s < e) { if (skipString ()) continue; if (start ("/*")) { while (s < e - 1) { if (*s == '*' && s [1] == '/') { s += 2; break; } else s ++; } } else if (start ("//")) { while (s < e) { if (*s == '\n') { s ++; break; } else s ++; } } else if (start ("/+")) { int depth = 1; while (s < e) { if (match ("/+")) depth ++; else if (match ("+/")) { depth --; if (depth == 0) break; } else s ++; } } else *o ++ = *s ++; } return data [0..cast (int) (o - b)]; } /* Strip lines with no content from the code. */ char[] dstripEmptyLines(char[] data) { char* s = data, e = s + data.length; char* b = data, o = s; void remove () { memmove (o, s, cast (int) (e - s)); data = data [0 .. data.length - cast (int) (s - o)]; e -= cast (int) (s - o); s = o; } bit iswhite = true; while (s < e) { s ++; if (s [-1] == '\n') { if (iswhite) remove (); iswhite = true; o = s; } else if (!isspace (s [-1])) { if (iswhite) { s --; remove (); s ++; } iswhite = false; } } return data; } /* Strip function bodies. */ char[] dstripBodies (char [] data) { char* s = data, e = s + data.length; char* b = data, o = s; void copy () { *o ++ = *s ++; } void skipSpaces () { while (s < e && isspace (*s)) s ++; } void cskipSpaces () { while (s < e && isspace (*s)) copy (); } bit isIdStart (char ch) { return isalpha (ch) || ch == '_'; } bit isId (char ch) { return isalnum (ch) || ch == '_'; } bit start (char [] string) { if (s > e - string.length) return false; if (s [0 .. string.length] != string) return false; if (isIdStart (s [0]) && s != e - string.length && isId (s [string.length])) return false; //o = s; s += string.length; return true; } bit match (char [] string) { if (s + string.length > e) return false; if (s [0 .. string.length] != string) return false; if (isIdStart (s [0]) && s != e - string.length && isId (s [string.length])) return false; s += string.length; return true; } bit cmatch (char [] string) { if (!match (string)) return false; o [0 .. string.length] = string; o += string.length; return true; } void remove() { /*memmove (o, s, cast (int) (e - s)); data = data [0 .. data.length - cast (int) (s - o)]; e -= cast (int) (s - o); s = o;*/ } bit skipString() { if (match ("\"")) { while (s < e) { s ++; if (s [-1] == '\\') s ++; else if (s [-1] == '\"') break; } } else if (match ("\'")) { while (s < e) { s ++; if (s [-1] == '\'') break; } } else return false; return true; } bit cskipString() { char* p = s; if (!skipString ()) return false; o [0 .. (int) (s - p)] = p [0 .. (int) (s - p)]; o += (int) (s - p); return true; } void removeBody(int depth) { if (o [-1] == '\n') { o [-1] = ';'; *o ++ = '\r'; } else *o ++ = ';'; while (s < e) { if (skipString()) continue; if (match ("{")) depth ++; else if (match ("}")) { depth --; if (depth == 0) break; } else s ++; } remove (); } void skipInitializer(char closea, char closeb) { while (s < e) { copy (); if (s [-1] == '[') skipInitializer (']', 0); else if (s [-1] == '{') skipInitializer ('}', 0); else if (s [-1] == '(') skipInitializer (')', 0); else if (s [-1] == closea || s [-1] == closeb) break; } } void scan(bit inner) { outer: while (s < e) { char *p = s; if (skipString ()) continue; else if (start ("in") || start ("body")) { skipSpaces (); if (!match ("{")) continue; removeBody (1); } else if (start ("out")) { skipSpaces (); if (!match ("(")) continue; while (s < e && *s != ')') s ++; if (!match (")")) continue; skipSpaces (); if (!match ("{")) continue; removeBody (1); } else if (cmatch ("=")) skipInitializer (';', ','); else if (cmatch ("template")) { int depth = 0; while (s < e) { if (cmatch (";") && depth == 0) break; else if (cskipString ()) continue; else if (cmatch ("{")) depth ++; else if (cmatch ("}")) { depth --; if (depth == 0) break; } else copy (); } } else if (start ("static")) { skipSpaces (); if (match ("this") || (match ("~") && match ("this"))) removeBody(0); else { for (int c; c < (int) (s - p); c ++) *o ++ = p [c]; if (cmatch ("{")) scan (true); } } else if (cmatch ("public") || cmatch ("private") || cmatch ("protected") || cmatch ("final") || cmatch ("override") || cmatch ("deprecated")) { cskipSpaces (); if (cmatch ("{")) scan (true); } else if (start ("unittest")) { skipSpaces (); match ("{"); removeBody (1); } else if (cmatch ("version")) { skipSpaces (); if (!cmatch ("(")) continue; while (s < e && *s != ')') copy (); if (!cmatch (")")) continue; skipSpaces (); if (!cmatch ("{")) continue; scan (true); cskipSpaces (); if (cmatch ("else")) { skipSpaces (); if (cmatch ("{")) scan (true); } } else if (cmatch ("extern") || cmatch ("export")) { skipSpaces (); if (!cmatch ("(")) continue; while (s < e && *s != ')') copy (); if (!cmatch (")")) continue; skipSpaces (); if (!cmatch ("{")) continue; } else if (cmatch ("class") || cmatch ("struct") || cmatch ("union") || cmatch ("enum") || cmatch ("interface")) { while (s < e) { if (cmatch ("{")) { scan (true); break; } if (cmatch (";")) break; copy (); } } else if (start ("{")) removeBody (1); else if (inner && cmatch ("}")) return; else if (isIdStart (*s)) { copy (); while (s < e && isId (*s)) copy (); } else if (cmatch ("(")) { int depth = 1; while (s < e) { if (*s == '(') depth ++; else if (*s == ')') depth --; copy (); if (depth == 0) break; } } else copy (); } } scan (false); return data [0 .. (int) (o - b)]; } /* Strip all possible whitespace from the text. */ char[] dstripWhitespace(char[] data) { char* s = data, e = s + data.length; char* o = s; bit skipString () { if (*s == '\"') { *o ++ = *s ++; while (s < e) { *o ++ = *s ++; if (s [-1] == '\\') *o ++ = *s ++; else if (s [-1] == '\"') break; } } else if (*s == '\'') { *o ++ = *s ++; while (s < e) { *o ++ = *s ++; if (s [-1] == '\'') break; } } else return false; return true; } while (s < e) { if (skipString ()) continue; if (isspace (*s)) { if (s === data) { s ++; while (s < e && isspace (*s)) s ++; } else if (isalnum (s [-1]) || s [-1] == '_') { *o ++ = spaceCharacter; while (s < e && isspace (*s)) s ++; if (!isalnum (*s) && *s != '_') o --; } else { while (s < e && isspace (*s)) s ++; } } else *o ++ = *s ++; } return data[0..cast (int) (o - (char *) data)]; } /* Strip comments and function bodies from a block of code. */ char[] dstrip (char [] data) { data = dstripComments (data); data = dstripBodies (data); data = dstripWhitespace (data); return data; } private void dstripTest(char[] input, char[] output) { char [1] space; char [] initial = input.dup; space [0] = spaceCharacter; output = replace (output, " ", space); if (dstrip (input) != output) throw new Error ("dstrip (\"" ~ initial ~ "\") should return \"" ~ replace (output, space, " ") ~ "\", but returned \"" ~ replace (dstrip (input), space, " ") ~ "\"."); } unittest { dstripTest ("version (xyz) { struct { foo } } else { ... }", "version(xyz){struct{foo}}else{...}"); dstripTest ("void foo () body { }", "void foo();"); dstripTest ("void foo () { }", "void foo();"); dstripTest ("template Bar(T) { void foo (int z, int y) { carp () ; } }", "template Bar(T){void foo(int z,int y){carp();}}"); dstripTest ("extern (C) { foo }", "extern(C){foo}"); dstripTest ("public static { }", "public static{}"); dstripTest ("version = foobar;", "version=foobar;"); dstripTest ("version (Fopo) { ... }", "version(Fopo){...}"); dstripTest ("version (Fopo) { ... }else{}", "version(Fopo){...}else{}"); dstripTest ("static this() { }", ";"); dstripTest ("interface Foo { }", "interface Foo{}"); dstripTest ("struct x { enum { } void foo () { } }", "struct x{enum{}void foo();}"); dstripTest ("enum MB { OK = 1 << 0, } char [] messageBox (char [] title, char [] message, MB flags) { }", "enum MB{OK=1<<0,}char[]messageBox(char[]title,char[]message,MB flags);"); } bit readWildcard (inout char [] [] files, char [] arg) { WIN32_FIND_DATA data; HANDLE handle; arg = replace (arg, "/", "\\"); if (std.string.find (arg, '?') < 0 && std.string.find (arg, '*') < 0) { files ~= arg; return true; } handle = FindFirstFileA (arg, &data); if (handle == (HANDLE) 0) { printf ("couldn't find files matching '%.*s'\n", arg); return false; } while (1) { char [] path; for (int d = arg.length - 1; d >= 0; d --) if (arg [d] == '\\') { path = arg [0 .. d + 1]; break; } char [] filename = path ~ data.cFileName [0 .. strlen (data.cFileName)]; files ~= filename.dup; if (!FindNextFileA (handle, &data)) break; } FindClose (handle); return true; } version (dstripcopy) { import std.path, std.string; int main (char [] [] args) { bit showHelp = false; char[][] files; char[] dest; if (args.length < 3) showHelp = true; dest = args [args.length - 1]; for (int c = 1; c < args.length - 1; c ++) if (!readWildcard(files, args[c])) showHelp = true; if (showHelp) { int p; char[] program; p = rfind(args[0], '\\'); if(p > -1) program = args[0][p+1..args[0].length]; else program = args[0]; printf ("\n%.*s FILES... DESTINATION\n" "\n" "Copy the D files to the destination, stripping out all whitespace from them.\n", program); //args [0]); return 1; } printf ("%.*s\n", dest); for (int c; c < files.length; c ++) { char [] file = files [c]; char [] fileResult = std.path.join (dest, getBaseName (file)); write (fileResult, (byte []) dstrip ((char []) read (file))); printf (" %.*s\n", file); } return 0; } }