changeset 211:9e9f3e7e342b default tip

Added dang folder and Module in ast.
author Anders Johnsen <skabet@gmail.com>
date Tue, 12 Aug 2008 20:07:35 +0200
parents f4149d4f6896
children
files dang/OptParse.d dang/compiler.d docs/candydoc/modules.ddoc dsss.conf src/ast/Module.d src/basic/Message.d
diffstat 6 files changed, 1093 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dang/OptParse.d	Tue Aug 12 20:07:35 2008 +0200
@@ -0,0 +1,810 @@
+/*
+Copyright (c) 2007 Kirk McDonald
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+/**
+ * Command-line option parsing, in the style of Python's optparse.
+ *
+ * Refer to the complete docs for more information.
+ */
+module dang.OptParse;
+
+import tango.io.Stdout;
+import tango.text.Util : locate, locatePrior;
+import tango.text.Ascii : toUpper;
+import tango.stdc.stdlib : exit, EXIT_FAILURE, EXIT_SUCCESS;
+import tango.text.convert.Integer : parse, toInt, toString = toString;
+import tango.text.convert.Utf : toString, toString32;
+
+/*
+Options may be in two forms: long and short. Short options start with a single
+dash and are one letter long. Long options start with two dashes and may
+consist of any number of characters (so long as they don't start with a dash,
+though they may contain dashes). Options are case-sensitive.
+
+Short options may be combined. The following are equivalent:
+
+$ myapp -a -b -c
+$ myapp -abc
+$ myapp -ab -c
+
+If -f and --file are aliases of the same option, which accepts an argument,
+the following are equivalent:
+
+$ myapp -f somefile.txt
+$ myapp -fsomefile.txt
+$ myapp --file somefile.txt
+$ myapp --file=somefile.txt
+
+The following are also valid:
+
+$ myapp -abcf somefile.txt
+$ myapp -abcfsomefile.txt
+$ myapp -abc --file somefile.txt
+
+If an option occurs multiple times, the last one is the one recorded:
+
+$ myapp -f somefile.txt --file otherfile.txt
+
+Matches 'otherfile.txt'.
+*/
+
+bool startswith(char[] s, char[] start) {
+    if (s.length < start.length) return false;
+    return s[0 .. start.length] == start;
+}
+bool endswith(char[] s, char[] end) {
+    if (s.length < end.length) return false;
+    return s[$ - end.length .. $] == end;
+}
+
+/// Thrown if client code tries to set up an improper option.
+class OptionError : Exception {
+    this(char[] msg) { super(msg); }
+}
+// Thrown if client code tries to extract the wrong type from an option.
+class OptionTypeError : Exception {
+    this(char[] msg) { super(msg); }
+}
+
+/++
+This class represents the results after parsing the command-line.
++/
+class Options {
+    char[][][char[]] opts;
+    int[char[]] counted_opts;
+    /// By default, leftover arguments are placed in this array.
+    char[][] args;
+
+    /// Retrieves the results of the Store and StoreConst actions.
+    char[] opIndex(char[] opt) {
+        char[][]* o = opt in opts;
+        if (o) {
+            return (*o)[0];
+        } else {
+            return "";
+        }
+    }
+    /// Retrieves the results of the Store action, when the type is Integer.
+    int value(char[] opt) {
+        char[][]* o = opt in opts;
+        if (o) {
+            return toInt((*o)[0]);
+        } else {
+            return 0;
+        }
+    }
+    /// Retrieves the results of the Append and AppendConst actions.
+    char[][] list(char[] opt) {
+        char[][]* o = opt in opts;
+        if (o) {
+            return *o;
+        } else {
+            return null;
+        }
+    }
+    /// Retrieves the results of the Append action, when the type is Integer.
+    int[] valueList(char[] opt) {
+        char[][]* o = opt in opts;
+        int[] l;
+        if (o) {
+            l.length = (*o).length;
+            foreach (i, s; *o) {
+                l[i] = toInt(s);
+            }
+        }
+        return l;
+    }
+    /// Retrieves the results of the Count action.
+    int count(char[] opt) {
+        int* c = opt in counted_opts;
+        if (c) {
+            return *c;
+        } else {
+            return 0;
+        }
+    }
+    /// Retrieves the results of the SetTrue and SetFalse actions.
+    bool flag(char[] opt) {
+        char[][]* o = opt in opts;
+        if (o) {
+            return (*o)[0] == "1";
+        } else {
+            return false;
+        }
+    }
+}
+
+// Options, args, this opt's index in args, name[, arg]
+///
+alias void delegate(Options, inout char[][], inout int, char[], char[]) OptionCallbackFancyArg;
+///
+alias void delegate(Options, inout char[][], inout int, char[], int)    OptionCallbackFancyInt;
+///
+alias void delegate(Options, inout char[][], inout int, char[])         OptionCallbackFancy;
+
+///
+alias void delegate(char[]) OptionCallbackArg;
+///
+alias void delegate(int)    OptionCallbackInt;
+///
+alias void delegate()       OptionCallback;
+
+/*
+Actions:
+ * Store:        name
+ * StoreConst:   name, const_value
+ * Append:       name
+ * AppendConst:  name, const_value
+ * Count:        name
+ * CallbackArg:  dga
+ * CallbackVoid: dg
+*/
+///
+enum Action { /+++/Store, /+++/StoreConst, /+++/Append, /+++/AppendConst, /+++/Count, /+++/SetTrue, /+++/SetFalse, /+++/Callback, /+++/CallbackFancy, /+++/Help /+++/}
+///
+enum ArgType { /+++/None, /+++/String, /+++/Integer /+++/}
+
+ArgType defaultType(Action action) {
+    switch (action) {
+        case Action.Store, Action.Append, Action.Callback, Action.CallbackFancy:
+            return ArgType.String;
+            break;
+        default:
+            return ArgType.None;
+            break;
+    }
+}
+
+/++
+This class represents a single command-line option.
++/
+class Option {
+    char[][] shortopts, longopts;
+    Action action;
+    ArgType type;
+    char[] name, argname;
+    char[] const_value;
+
+    char[] default_string;
+    int default_value;
+    bool default_flag, has_default;
+
+    OptionCallbackArg callback;
+    OptionCallbackInt int_callback;
+    OptionCallback void_callback;
+
+    OptionCallbackFancyArg fancy_callback;
+    OptionCallbackFancyInt fancy_int_callback;
+    OptionCallbackFancy fancy_void_callback;
+    char[] helptext;
+    this(
+        char[][] shorts, char[][] longs, ArgType type,
+        Action act, char[] name, char[] const_value,
+        OptionCallbackArg dga, OptionCallback dg,
+        OptionCallbackInt dgi,
+        OptionCallbackFancyArg fdga,
+        OptionCallbackFancyInt fdgi,
+        OptionCallbackFancy fdg
+    ) {
+        this.shortopts = shorts;
+        this.longopts = longs;
+        this.action = act;
+        this.type = type;
+        this.name = name;
+        this.argname = toUpper(name.dup);
+        this.default_string = "";
+        this.default_value = 0;
+        this.default_flag = false;
+
+        // Perform sanity checks.
+        assert (name !is null);
+        switch (act) {
+            case Action.Store, Action.Append:
+                assert(type != ArgType.None);
+                break;
+            case Action.StoreConst, Action.AppendConst:
+                assert(type == ArgType.None);
+                assert(const_value !is null);
+                break;
+            case Action.Callback:
+                //assert(type != ArgType.None);
+                switch (type) {
+                    case ArgType.String:
+                        assert(dga !is null);
+                        break;
+                    case ArgType.Integer:
+                        assert(dgi !is null);
+                        break;
+                    case ArgType.None:
+                        assert(dg !is null);
+                        break;
+                }
+                break;
+            case Action.CallbackFancy:
+                switch (type) {
+                    case ArgType.String:
+                        assert(fdga !is null);
+                        break;
+                    case ArgType.Integer:
+                        assert(fdgi !is null);
+                        break;
+                    case ArgType.None:
+                        assert(fdg !is null);
+                        break;
+                }
+            default:
+                break;
+        }
+        this.const_value = const_value;
+        this.callback = dga;
+        this.int_callback = dgi;
+        this.void_callback = dg;
+        this.fancy_callback = fdga;
+        this.fancy_int_callback = fdgi;
+        this.fancy_void_callback = fdg;
+    }
+    char[] toString() {
+        int optCount = this.shortopts.length + this.longopts.length;
+        char[] result;
+        bool printed_arg = false;
+        foreach(i, opt; this.shortopts ~ this.longopts) {
+            result ~= opt;
+            if (i < optCount-1) {
+                result ~= ", ";
+            } else if (this.hasArg()) {
+                result ~= "=" ~ toUpper(this.argname.dup);
+            }
+        }
+        return result;
+    }
+    //enum Action { Store, StoreConst, Append, AppendConst, Count, SetTrue, SetFalse, Callback, CallbackFancy, Help }
+    void issue_default(Options results) {
+        // Only set the default if the option doesn't already have a value.
+        char[][]* val = this.name in results.opts;
+        switch (this.action) {
+            case Action.Store, Action.Append:
+                if (val !is null) return;
+                if (this.type == ArgType.String) {
+                    results.opts[name] = [default_string];
+                } else {
+                    results.opts[name] = [.toString(default_value)];
+                }
+                break;
+            case Action.StoreConst, Action.AppendConst:
+                if (val !is null) return;
+                results.opts[name] = [default_string];
+                break;
+            case Action.SetTrue, Action.SetFalse:
+                if (val !is null) return;
+                if (default_flag) {
+                    results.opts[name] = ["1"];
+                } else {
+                    results.opts[name] = ["0"];
+                }
+                break;
+            default:
+                return;
+        }
+    }
+    // Does whatever this option is supposed to do.
+    void performAction(OptionParser parser, Options results, inout char[][] args, inout int idx, char[] arg) {
+        int i;
+        if (this.type == ArgType.Integer) {
+            // Verify that it's an int.
+            i = parser.toOptInt(arg);
+        }
+        switch (this.action) {
+            case Action.Store:
+                results.opts[name] = [arg];
+                break;
+            case Action.Append:
+                results.opts[name] ~= arg;
+                break;
+            case Action.StoreConst:
+                assert(arg is null, "Got unexpected argument for '"~name~"' option.");
+                results.opts[name] = [const_value];
+                break;
+            case Action.AppendConst:
+                assert(arg is null, "Got unexpected argument for '"~name~"' option.");
+                results.opts[name] ~= const_value;
+                break;
+            case Action.Count:
+                assert(arg is null, "Got unexpected argument for '"~name~"' option.");
+                ++results.counted_opts[name];
+                break;
+            case Action.SetTrue:
+                results.opts[name] = ["1"];
+                break;
+            case Action.SetFalse:
+                results.opts[name] = ["0"];
+                break;
+            case Action.Callback:
+                switch (type) {
+                    case ArgType.String:
+                        callback(arg);
+                        break;
+                    case ArgType.Integer:
+                        int_callback(i);
+                        break;
+                    case ArgType.None:
+                        void_callback();
+                        break;
+                }
+                break;
+            case Action.CallbackFancy:
+                switch (type) {
+                    case ArgType.String:
+                        fancy_callback(results, args, idx, name, arg);
+                        break;
+                    case ArgType.Integer:
+                        fancy_int_callback(results, args, idx, name, i);
+                        break;
+                    case ArgType.None:
+                        fancy_void_callback(results, args, idx, name);
+                        break;
+                }
+                break;
+            case Action.Help:
+                parser.helpText();
+                exit(EXIT_SUCCESS);
+                break;
+        }
+    }
+    /// Returns whether this option accepts an argument.
+    bool hasArg() {
+        return this.type != ArgType.None;
+    }
+    /// Sets the help text for this option.
+    Option help(char[] help) {
+        this.helptext = help;
+        return this;
+    }
+    /// Sets the name of this option's argument, if it has one.
+    Option argName(char[] argname) {
+        this.argname = argname;
+        return this;
+    }
+    Option def(char[] val) {
+        if (
+            (this.type != ArgType.String || (this.action != Action.Store && this.action != Action.Append)) &&
+            this.action != Action.StoreConst && this.action != Action.AppendConst
+        )
+            throw new OptionError("Cannot specify string default for non-string option '"~this.name~"'");
+        this.has_default = true;
+        this.default_string = val;
+        return this;
+    }
+    Option def(int val) {
+        if (this.type != ArgType.Integer || (this.action != Action.Store && this.action != Action.Append))
+            throw new OptionError("Cannot specify integer default for non-integer option '"~this.name~"'");
+        this.has_default = true;
+        this.default_value = val;
+        return this;
+    }
+    Option def(bool val) {
+        if (this.action != Action.SetTrue && this.action != Action.SetFalse)
+            throw new OptionError("Cannot specify boolean default for non-flag option '"~this.name~"'");
+        this.has_default = true;
+        this.default_flag = val;
+        return this;
+    }
+    // Returns true if the passed option string matches this option.
+    bool matches(char[] _arg) {
+        dchar[] arg = toString32(_arg);
+        if (
+            arg.length < 2 ||
+            arg.length == 2 && (arg[0] != '-' || arg[1] == '-') ||
+            arg.length > 2 && (arg[0 .. 2] != "--" || arg[2] == '-')
+        ) {
+            return false;
+        }
+        if (arg.length == 2) {
+            foreach (opt; shortopts) {
+                if (_arg == opt) {
+                    return true;
+                }
+            }
+        } else {
+            foreach (opt; longopts) {
+                if (_arg == opt) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
+
+/++
+This class is used to define a set of options, and parse the command-line
+arguments.
++/
+class OptionParser {
+    OptionCallbackArg leftover_cb;
+    /// An array of all of the options known by this parser.
+    Option[] options;
+    char[] name, desc;
+    /// The description of the programs arguments, as used in the Help action.
+    char[] argdesc;
+    private void delegate(char[]) error_callback;
+
+    this(char[] desc="") {
+        this.name = "";
+        this.desc = desc;
+        this.argdesc = "[options] args...";
+    }
+
+    /// Sets a callback, to override the default error behavior.
+    void setErrorCallback(void delegate(char[]) dg) {
+        error_callback = dg;
+    }
+    void unknownOptError(char[] opt) {
+        error("Unknown argument '"~opt~"'");
+    }
+    void expectedArgError(char[] opt) {
+        error("'"~opt~"' option expects an argument.");
+    }
+    /// Displays an error message and terminates the program.
+    void error(char[] err) {
+        if (error_callback !is null) {
+            error_callback(err);
+        } else {
+            this.helpText();
+            Stdout.formatln(err);
+        }
+        exit(EXIT_FAILURE);
+    }
+    int toOptInt(char[] s) {
+        int i;
+        uint ate;
+        i = .parse(s, 10u, &ate);
+        if (ate != s.length)
+            error("Could not convert '"~s~"' to an integer.");
+        return i;
+    }
+
+    /// Displays useful "help" information about the program's options.
+    void helpText() {
+        int optWidth;
+        char[][] optStrs;
+        typedef char spacechar = ' ';
+        spacechar[] padding;
+        // Calculate the maximum width of the option lists.
+        foreach(i, opt; options) {
+            optStrs ~= opt.toString();
+            if (optStrs[i].length > optWidth) {
+                optWidth = optStrs[i].length;
+            }
+        }
+        Stdout.formatln("Usage: {0} {1}", this.name, this.argdesc);
+        if (this.desc !is null && this.desc != "") Stdout.formatln(this.desc);
+        Stdout.formatln("\nOptions:");
+        foreach(i, opt; options) {
+            padding.length = optWidth - optStrs[i].length;
+            Stdout.formatln("  {0}{1} {2}", optStrs[i], cast(char[])padding, opt.helptext);
+        }
+    }
+    
+    // Checks the passed arg against all the options in the parser.
+    // Returns null if no match is found.
+    Option matches(char[] arg) {
+        foreach(o; options) {
+            if (o.matches(arg)) {
+                return o;
+            }
+        }
+        return null;
+    }
+    char[] getBaseName(char[] path) {
+        version(Windows) {
+            char delimiter = '\\';
+        } else {
+            char delimiter = '/';
+        }
+        uint idx = locatePrior(path, delimiter);
+        if (idx == path.length) return path;
+        return path[idx+1 .. $];
+    }
+    char[] getProgramName(char[] path) {
+        version(Windows) {
+            // (Unicode note: ".exe" only contains 4 code units, so this slice
+            // should Just Work.) (Although it remains to be seen how robust
+            // this code actually is.)
+            //Stdout.formatln(path);
+            //assert(path[$-4 .. $] == ".exe");
+            //path = path[0 .. $-4];
+        }
+        return getBaseName(path);
+    }
+    /// Parses the passed command-line arguments and returns the results.
+    Options parse(char[][] args) {
+        this.name = getProgramName(args[0]);
+        args = args[1 .. $];
+        Options options = new Options;
+        /*
+        The issue is this:
+
+        $ myapp -abc
+
+        This might be three short opts, or one or two opts, the last of which
+        accepts an argument. In the three-opt case, we want to get:
+
+        $ myapp -a -b -c
+
+        In the one-opt case, we want:
+
+        $ myapp -a bc
+
+        In the two-opt case, we want:
+
+        $ myapp -a -b c
+
+        We also want to parse apart "--file=somefile" into "--file somefile"
+        */
+        char[] opt, newopt, arg;
+        dchar[] opt32;
+        int idx;
+        Option match;
+
+        for (int i=0; i<args.length; ++i) {
+            opt = args[i];
+            // -- ends the option list, the remainder is dumped into args
+            if (opt == "--") {
+                if (this.leftover_cb !is null) {
+                    foreach(a; args[i+1 .. $]) {
+                        this.leftover_cb(a);
+                    }
+                } else {
+                    options.args ~= args[i+1 .. $];
+                }
+                i = args.length;
+            } else if (opt.startswith("--")) {
+                idx = locate(opt, '=');
+                if (idx != opt.length) {
+                    newopt = opt[0 .. idx];
+                    // Stitch out the old arg, stitch in the newopt, arg pair.
+                    // (Unicode note: idx+1 works, since we know '=' is a
+                    // single code unit.)
+                    args = args[0 .. i] ~ [newopt, opt[idx+1 .. $]] ~ args[i+1 .. $];
+                } else {
+                    newopt = opt;
+                }
+                match = matches(newopt);
+                if (match is null) {
+                    unknownOptError(newopt);
+                }
+                if (match.hasArg) {
+                    if (i == args.length-1) expectedArgError(match.name);
+                    arg = args[i+1];
+                    ++i;
+                } else {
+                    arg = null;
+                }
+                match.performAction(this, options, args, i, arg);
+            } else if (opt.startswith("-")) {
+                if (opt.length >= 2) {
+                    opt32 = toString32(opt[1 .. $]);
+                    foreach (j, c; opt32) {
+                        newopt = .toString("-" ~ [c]);
+                        match = matches(newopt);
+                        if (match is null) {
+                            unknownOptError(newopt);
+                        }
+                        if (match.hasArg) {
+                            // This is the last char in the group, look to the
+                            // next element of args for the arg.
+                            if (j == opt32.length-1) {
+                                if (i == args.length-1) expectedArgError(match.name);
+                                arg = args[i+1];
+                                ++i;
+                            // Otherwise, consume the rest of this group for
+                            // the arg.
+                            } else {
+                                arg = .toString(opt32[j+1 .. $]);
+                                match.performAction(this, options, args, i, arg);
+                                break;
+                            }
+                        } else {
+                            arg = null;
+                        }
+                        match.performAction(this, options, args, i, arg);
+                    }
+                } else {
+                    unknownOptError(opt);
+                }
+            } else {
+                if (this.leftover_cb is null) {
+                    options.args ~= opt;
+                } else {
+                    this.leftover_cb(opt);
+                }
+            }
+        }
+        foreach (o; this.options) {
+            o.issue_default(options);
+        }
+        return options;
+    }
+
+    /++
+    Overrides the default behavior of leftover arguments, calling this callback
+    with them instead of adding them an array.
+    +/
+    void leftoverCallback(OptionCallbackArg dg) {
+        this.leftover_cb = dg;
+    }
+    ///
+    Option addOption(Option option) {
+        this.options ~= option;
+        return option;
+    }
+    //                    options  action             name  type                       const_value  dga   dgv   dgi   fdga  fdgi  fdg
+    ///
+    Option addOption(char[][] options ...) {
+        return addOption(options, Action.Store,      null, defaultType(Action.Store), null,        null, null, null, null, null, null);
+    }
+    ///
+    Option addOption(char[][] options, char[] name) {
+        return addOption(options, Action.Store,      name, defaultType(Action.Store), null,        null, null, null, null, null, null);
+    }
+    ///
+    Option addOption(char[][] options, Action action) {
+        return addOption(options, action,            null, defaultType(action),       null,        null, null, null, null, null, null);
+    }
+    ///
+    Option addOption(char[][] options, ArgType type) {
+        return addOption(options, Action.Store,      null, type,                      null,        null, null, null, null, null, null);
+    }
+    ///
+    Option addOption(char[][] options, Action action, ArgType type) {
+        return addOption(options, action,            null, type,                      null,        null, null, null, null, null, null);
+    }
+    ///
+    Option addOption(char[][] options, char[] name, Action action) {
+        return addOption(options, action,            name, defaultType(action),       null,        null, null, null, null, null, null);
+    }
+    ///
+    Option addOption(char[][] options, char[] name, Action action, ArgType type) {
+        return addOption(options, action,            name, type,                      null,        null, null, null, null, null, null);
+    }
+    ///
+    Option addOption(char[][] options, Action action, char[] const_value) {
+        return addOption(options, action,            null, defaultType(action),       const_value, null, null, null, null, null, null);
+    }
+    ///
+    Option addOption(char[][] options, char[] name, char[] const_value) {
+        return addOption(options, Action.StoreConst, name, defaultType(Action.Store), const_value, null, null, null, null, null, null);
+    }
+    ///
+    Option addOption(char[][] options, char[] name, Action action, char[] const_value) {
+        return addOption(options, action,            name, defaultType(action),       const_value, null, null, null, null, null, null);
+    }
+    ///
+    Option addOption(char[][] options, OptionCallbackArg dg) {
+        return addOption(options, Action.Callback,   null, ArgType.String,            null,        dg,   null, null, null, null, null);
+    }
+    ///
+    Option addOption(char[][] options, OptionCallback dg) {
+        return addOption(options, Action.Callback,   null, ArgType.None,              null,        null, dg,   null, null, null, null);
+    }
+    ///
+    Option addOption(char[][] options, OptionCallbackInt dg) {
+        return addOption(options, Action.Callback,   null, ArgType.Integer,           null,        null, null, dg,   null, null, null);
+    }
+    ///
+    Option addOption(char[][] options, OptionCallbackFancyArg dg) {
+        return addOption(options, Action.CallbackFancy, null, ArgType.String,         null,        null, null, null, dg,   null, null);
+    }
+    ///
+    Option addOption(char[][] options, OptionCallbackFancy dg) {
+        return addOption(options, Action.CallbackFancy, null, ArgType.None,           null,        null, null, null, null, null, dg);
+    }
+    ///
+    Option addOption(char[][] options, OptionCallbackFancyInt dg) {
+        return addOption(options, Action.CallbackFancy, null, ArgType.Integer,        null,        null, null, null, null, dg,   null);
+    }
+    ///
+    Option addOption(char[][] options, char[] name, OptionCallbackFancyArg dg) {
+        return addOption(options, Action.CallbackFancy, name, ArgType.String,         null,        null, null, null, dg,   null, null);
+    }
+    ///
+    Option addOption(char[][] options, char[] name, OptionCallbackFancy dg) {
+        return addOption(options, Action.CallbackFancy, name, ArgType.None,           null,        null, null, null, null, null, dg);
+    }
+    ///
+    Option addOption(char[][] options, char[] name, OptionCallbackFancyInt dg) {
+        return addOption(options, Action.CallbackFancy, name, ArgType.Integer,        null,        null, null, null, null, dg,   null);
+    }
+    // Although users certainly /can/ call this, all those overloads are there
+    // for a reason.
+    Option addOption(
+        char[][] options, 
+        Action action, char[] name, ArgType type, char[] const_value,
+        OptionCallbackArg callback, OptionCallback vcall,
+        OptionCallbackInt icall,
+        OptionCallbackFancyArg fdga,
+        OptionCallbackFancyInt fdgi,
+        OptionCallbackFancy fdg
+    ) {
+        char[][] shortopts;
+        char[][] longopts;
+        dchar[] opt;
+        Option option;
+        foreach (_opt; options) {
+            // (Unicode note: We convert to dchar[] so the length checks work
+            // out in the event of a short opt with a >127 character.)
+            opt = toString32(_opt);
+            if (opt.length < 2) {
+                throw new OptionError(
+                    "invalid option string '" ~ _opt ~ "': must be at least two characters long"
+                );
+            } else if (opt.length > 2) {
+                if (opt[0 .. 2] != "--" || opt[2] == '-')
+                    throw new OptionError(
+                        "invalid long option string '" ~ _opt ~ "': must start with --, followed by non-dash"
+                    );
+                longopts ~= _opt;
+            } else {
+                if (opt[0] != '-' || opt[1] == '-')
+                    throw new OptionError(
+                        "invalid short option string '" ~ _opt ~ "': must be of the form -x, where x is non-dash"
+                    );
+                shortopts ~= _opt;
+            }
+        }
+        if (name is null) {
+            // (Unicode note: We know '-' is a single code unit, so these
+            // slices are okay.)
+            if (longopts.length > 0)
+                name = longopts[0][2 .. $];
+            else if (shortopts.length > 0)
+                name = shortopts[0][1 .. 2];
+            else
+                throw new OptionError(
+                    "No options provided to addOption!"
+                );
+        }
+        option = new Option(shortopts, longopts, type, action, name, const_value, callback, vcall, icall, fdga, fdgi, fdg);
+        this.options ~= option;
+        return option;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dang/compiler.d	Tue Aug 12 20:07:35 2008 +0200
@@ -0,0 +1,240 @@
+module dang.compiler;
+
+import tango.io.Stdout,
+       tango.core.Signal,
+       tango.core.Memory,
+       tango.sys.Process,
+       tango.time.StopWatch,
+       tango.text.Util,
+       tango.io.FileConduit,
+       tango.io.FilePath;
+
+import lexer.Lexer,
+       parser.Action,
+       parser.Parser;
+
+import basic.SourceManager;
+
+import basic.Message;
+
+import ast.Module;
+
+import tango.stdc.posix.unistd;
+import tango.stdc.stdlib;
+
+import Opt = dang.OptParse;
+            
+class NullAction : Action 
+{
+}
+
+void checkFiles(char[][] *files)
+{
+//    GC.disable();
+    bool non_existant_files = false;
+    bool duplicate_files = false;
+
+    char[][] validFiles;
+
+    foreach (file; *files)
+    {
+        scope path = new FilePath(file);
+
+        if (!path.exists)
+        {
+            Stderr.formatln("'{}' does not exist", file).newline;
+            non_existant_files = true;
+
+            continue;
+        }
+        
+        bool fileInStack = false;
+        foreach (vFile; validFiles)
+            if (vFile == file)
+            {
+                fileInStack = true;
+                duplicate_files = true;
+            }
+
+        if (fileInStack)
+            continue;
+
+        validFiles ~= path.toString();
+    }
+
+    *files = validFiles;
+
+    if (non_existant_files)
+        throw new Exception("All files given must exist");
+    if (duplicate_files)
+        Stderr("warning: duplicate files ignored").newline;
+}
+
+void main(char[][] args)
+{
+    char[][] filesToHandle;
+
+    Signal!(char[][]*) preStart;
+
+    Signal!(char[]) preLex;
+    Signal!(Lexer) postLex;
+
+    Signal!(Lexer) preParse;
+    Signal!(Module[], SourceManager) postParse;
+    Signal!(Module[], SourceManager) postSema;
+
+    preStart.attach(&checkFiles);
+
+    auto argParse = new Opt.OptionParser(`Dang "D" compiler v0.1`);
+
+    bool optimize = false;
+    bool inline = false;
+
+
+    SourceManager src_mgr = new SourceManager;
+    MessageHandler messages = new MessageHandler(src_mgr);
+
+    argParse.addOption(["-h", "--help"], Opt.Action.Help)
+        .help("Show this help message");
+
+    auto options = argParse.parse(args);
+
+    filesToHandle ~= options.args;
+    
+    // Will throw exception if some files don't exist
+    preStart(&filesToHandle);
+
+    struct Measurement { char[] label; double time; }
+    Measurement[] timings;
+    StopWatch total;
+    total.start;
+
+    Module[] modules;
+
+    StopWatch watch;
+    watch.start;
+    foreach (file; filesToHandle)
+    {
+        preLex(file);
+
+        auto start = src_mgr.addFile(file);
+        auto lexer = new Lexer(start, src_mgr, messages);
+        postLex(lexer);
+
+        preParse(lexer);
+
+        auto parser = new Parser(messages);
+        auto action = new NullAction;
+        modules ~= cast(Module)parser.parse(src_mgr, lexer, action);
+        timings ~= Measurement("Lex + Parse of '"~file~"'", watch.stop);
+        messages.checkErrors(ExitLevel.Parser);
+/*
+        StopWatch watch2;
+        watch.start;
+        watch2.start;
+        Module[] mods = (new LoadModule).visit(m, src_mgr, messages);
+        (new ScopeBuilder).visit(m);
+        auto scope_builder = watch2.stop;
+        watch2.start;
+        (new ScopeCheck).visit(m);
+        auto scope_check = watch2.stop;
+        watch2.start;
+        (new TypeCheck).visit(m);
+        auto type_check = watch2.stop;
+        watch2.start;
+
+        foreach (decl; m.decls)
+            decl.simplify();
+        auto simplify = watch2.stop;
+        auto extra_stuff = watch.stop;
+        timings ~= Measurement("Extra stuff", watch.stop);
+        timings ~= Measurement("  - Building scopes", scope_builder);
+        timings ~= Measurement("  - Checking scopes", scope_check);
+        timings ~= Measurement("  - Checking types", type_check);
+
+        postParse(m, src_mgr);*/
+    }
+/*
+    (new LiteralInterpreter(messages)).visit(modules);
+    messages.checkErrors;
+    postParse(modules, src_mgr);
+
+    class ModuleLoader : Visitor!(void)
+    {
+        Module[] visit(Module[] modules, MessageHandler messages, SourceManager src_mgr)
+        {
+            this.modules = modules;
+            this.messages = messages;
+            this.src_mgr = src_mgr;
+            super.visit(modules);
+            return this.modules;
+        }
+
+        override void visitImportDecl(ImportDecl decl)
+        {
+            char[] path = replace!(char)(decl.get,'.','/')~".d";
+
+            auto start = src_mgr.addFile(path);
+            auto lexer = new Lexer(start, src_mgr, messages);
+
+            auto parser = new Parser(messages);
+            auto action = new AstAction(src_mgr);
+
+            Module m = cast(Module)parser.parse(src_mgr, lexer, action);
+            modules ~= m;
+            m.outputModule = false;
+    //        decl.env.mHandle.add(m);
+            messages.checkErrors(ExitLevel.Parser);
+        }
+
+        Module[] modules;
+        SourceManager src_mgr;
+        MessageHandler messages;
+    }
+
+    modules = (new ModuleLoader()).visit(modules, messages, src_mgr);
+    messages.checkErrors;
+
+    (new BuildScopes).visit(modules);
+    (new BuildSymbols).visit(modules);
+    StopWatch watch2;
+    watch.start;
+    watch2.start;
+
+    (new BuildTypes(messages)).visit(modules);
+
+    (new CheckScopes(messages)).visit(modules);
+    messages.checkErrors;
+    auto scope_check = watch2.stop;
+
+    watch2.start;
+    (new CheckTypes(messages)).visit(modules);
+    messages.checkErrors;
+    auto type_check = watch2.stop;
+
+    watch2.start;
+    (new ObjectOriented(messages)).visit(modules);
+    messages.checkErrors;
+    auto object_check = watch2.stop;
+
+    watch2.start;
+    auto vc = new VC;
+    vc.msg = messages;
+    foreach (m; modules)
+        m.verify(vc);
+    messages.checkErrors;
+    auto ast_verify = watch2.stop;
+
+    foreach (m; modules)
+        foreach (decl; m.decls)
+            decl.simplify();
+
+    timings ~= Measurement("Total", total.stop);
+    postSema(modules, src_mgr);
+
+    if (options.flag("time"))
+        foreach (m; timings)
+            Stderr.formatln("{,-45} {}ms", m.label, m.time*1e3);
+    */
+}
+
--- a/docs/candydoc/modules.ddoc	Tue Aug 12 19:09:01 2008 +0200
+++ b/docs/candydoc/modules.ddoc	Tue Aug 12 20:07:35 2008 +0200
@@ -1,1 +1,14 @@
 MODULES =
+	$(MODULE_FULL parser.Action)
+	$(MODULE_FULL parser.Parser)
+	$(MODULE_FULL lexer.Keyword)
+	$(MODULE_FULL lexer.Lexer)
+	$(MODULE_FULL lexer.Token)
+	$(MODULE_FULL basic.SmallArray)
+	$(MODULE_FULL basic.conv)
+	$(MODULE_FULL basic.Message)
+	$(MODULE_FULL basic.SourceLocation)
+	$(MODULE_FULL basic.LiteralParsing)
+	$(MODULE_FULL basic.Messages)
+	$(MODULE_FULL basic.Attribute)
+	$(MODULE_FULL basic.SourceManager)
--- a/dsss.conf	Tue Aug 12 19:09:01 2008 +0200
+++ b/dsss.conf	Tue Aug 12 20:07:35 2008 +0200
@@ -1,4 +1,11 @@
+[src/ast]
+Target= ast
+buildflags = -version=Tango -I./src
+
+[dang/compiler.d]
+Target = Dang
+buildflags = -version=Tango -I./src
 
 [src]
-buildflags = -version=Tango -candydoc -Dqdocs -I./src
+buildflags = -version=Tango -candydoc -Dqdocs -I./src -full
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ast/Module.d	Tue Aug 12 20:07:35 2008 +0200
@@ -0,0 +1,22 @@
+module ast.Module;
+
+import ast.Decl;
+
+class Module
+{
+    this(char[] moduleName)
+    {
+        this.moduleName = moduleName;
+    }
+
+    void addDecl(Decl decl)
+    {
+        decls ~= decl;
+    }
+
+private:
+    Decl[] decls;
+    char[] moduleName;
+    bool outputModule = true;
+}
+
--- a/src/basic/Message.d	Tue Aug 12 19:09:01 2008 +0200
+++ b/src/basic/Message.d	Tue Aug 12 20:07:35 2008 +0200
@@ -7,8 +7,6 @@
 
 import tango.stdc.stdlib;
 
-import llvm.type;
-
 import lexer.Token,
        lexer.Lexer;