
private var argc = 0;
private var argv = Pointer[Pointer[Int8]]();
private var commandLineInitialized? = false;
private var commandLineArgs = Vector[String]();

commandLine() {
    if (not commandLineInitialized?) {
        clear(commandLineArgs);
        for (i in range(argc)) {
            var cstr = (argv + i)^;
            push(commandLineArgs, String(cstr));
        }
        commandLineInitialized? = true;
    }
    return ref commandLineArgs;
}

setArgcArgv(argc_, argv_) {
    argc = argc_;
    argv = argv_;
    commandLineInitialized? = false;
}

getArgcArgv() = argc, argv;

define callMain;

[Main]
overload callMain(static Main) {
    try {
        return getExitCode(..Main());
    }
    catch (e) {
        printlnTo(stderr, "unhandled exception: ", e);
        return -1;
    }
}

[Main | CallDefined?(Main, Type(commandLine()))]
overload callMain(static Main) {
    try {
        return getExitCode(..Main(commandLine()));
    }
    catch (e) {
        printlnTo(stderr, "unhandled exception: ", e);
        return -1;
    }
}

[Main | CallDefined?(Main, ..Type(argc, argv))]
overload callMain(static Main) {
    try {
        return getExitCode(..Main(argc, argv));
    }
    catch (e) {
        printlnTo(stderr, "unhandled exception: ", e);
        return -1;
    }
}

private define getExitCode;
overload getExitCode() = 0;
overload getExitCode(x) = Int(x);
