[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/wingunder/cppfront/main/source/common.h [Back]  [Original]

//  Copyright (c) Herb Sutter
//  SPDX-License-Identifier: CC-BY-NC-ND-4.0

// 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.


//===========================================================================
//  Common types
//===========================================================================

#ifndef __CPP2_COMMON
#define __CPP2_COMMON

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace cpp2 {

//-----------------------------------------------------------------------
//
//  source_line: represents a source code line
//
//-----------------------------------------------------------------------
//
struct source_line
{
    std::string text;

    enum class category { empty, preprocessor, comment, import, cpp1, cpp2 }
        cat = category::empty;

    auto prefix() const -> std::string
    {
        switch (cat) {
        break;case category::empty:         return "/*   */ ";
        break;case category::preprocessor:  return "/* # */ ";
        break;case category::comment:       return "/* / */ ";
        break;case category::import:        return "/* i */ ";
        break;case category::cpp1:          return "/* 1 */ ";
        break;case category::cpp2:          return "/* 2 */ ";
        break;default: assert(!"illegal category"); abort();
        }
    }
};


using lineno_t = int32_t;
using colno_t  = int32_t;   // not int16_t... encountered >80,000 char line during testing

struct source_position
{
    lineno_t    lineno;     // one-based offset into program source
    colno_t     colno;      // one-based offset into line

    source_position(lineno_t l = 1, colno_t  c = 1 )
        : lineno{ l }, colno{ c }
    {
    }

    auto operator(source_position const&) const = default;

    auto to_string() const -> std::string
    {
        return "(" + std::to_string(lineno) + "," + std::to_string(colno) + ")";
    }
};

struct comment
{
    enum class comment_kind { line_comment = 0, stream_comment };

    comment_kind    kind;
    source_position start;
    source_position end;
    std::string     text;
};

//-----------------------------------------------------------------------
//
//  error: represents a user-readable error message
//
//-----------------------------------------------------------------------
//
struct error
{
    source_position where;
    std::string     msg;
    bool            internal = false;

    error( source_position w, std::string const& m, bool i = false )
        : where{w}, msg{m}, internal{i}
    { }

    auto print(auto& o, std::string const& file) const -> void
    {
        o  0) {
            o  T
{
    return b ? "true" : "false";
}


//  Explicit cast
template
auto as(auto x) -> T {
    return T(x);
}


//  String path prefix from filename
//
auto strip_path(std::string const& file) -> std::string
{
    auto i = std::ssize(file)-1;
    while (i >= 0 && file[i] != '\\' && file[i] != '/') {
        --i;
    }
    return {file, as(i+1)};
}


//-----------------------------------------------------------------------
//
//  Command line handling
//
//-----------------------------------------------------------------------
//

class cmdline_processor
{
    bool help_requested = false;

    struct arg
    {
        int pos;
        std::string text;

        arg(int p, char* t) : pos{p}, text{t} { }
    };
    std::vector args;

    using callback0 = void (*)();
    using callback1 = void (*)(std::string const&);
    struct flag
    {
        int         group = 0;
        std::string name;
        int         unique_prefix = 0;
        std::string description;
        callback0   handler0;
        callback1   handler1;
        std::string synonym;

        flag(int g, std::string_view n, std::string_view d, callback0 h0, callback1 h1, std::string_view s)
            : group{g}, name{n}, description{d}, handler0{h0}, handler1{h1}, synonym{s}
        { }
    };
    std::vector flags;
    int max_flag_length = 0;

    //  Define this in the main .cpp to avoid bringing  into the headers,
    //  so that we can't accidentally start depending on iostreams in the compiler body
    static auto print(std::string_view, int width = 0) -> void;

public:
    auto process_flags() -> void
    {
        constexpr auto processed = -1;

        //  Calculate the unique prefixes
        for (auto flag1 = flags.begin(); flag1 != flags.end(); ++flag1) {
            for (auto flag2 = flag1+1; flag2 != flags.end(); ++flag2) {
                int i = 0;
                while (
                    i < std::ssize(flag1->name) &&
                    i < std::ssize(flag2->name) &&
                    flag1->name[i] == flag2->name[i]
                    )
                {
                    ++i;
                }
                //  Record that we found the unique prefix must be at least this long
                flag1->unique_prefix = std::max( flag1->unique_prefix, i+1 );
                flag2->unique_prefix = std::max( flag2->unique_prefix, i+1 );
            }
        }

        //  Look for matches
        for (auto arg = args.begin(); arg != args.end(); ++arg)
        {
            //  The arg should never be empty, but we're going to do a [0]
            //  subscript next so we should either check or assert
            if (arg->text.empty()) {
                continue;
            }

            //  Provide a way to ignore the rest of the command line
            //  for the purpose of looking for switches
            if (arg->text == "--") {
                arg->pos = processed;
                break;
            }

            for (auto& flag : flags) {
                auto length_to_match = std::max(flag.unique_prefix, as(arg->text.length())-1);
                //  Allow a switch to start with either - or /
                if (arg->text.starts_with("-" + flag.name.substr(0, length_to_match)) ||
                    arg->text.starts_with("/" + flag.name.substr(0, length_to_match)) ||
                    arg->text == "-" + flag.synonym ||
                    arg->text == "/" + flag.synonym
                    )
                {
                    assert(flag.handler0 || flag.handler1);

                    //  If this is a standalone switch, just process it
                    if (flag.handler0) {
                        flag.handler0();
                    }

                    //  Else this is a switch that takes the next arg as its value, so pass that
                    else {
                        if (arg+1 == args.end()) {
                            print("Missing argument to option " + arg->text + " (try -help)\n");
                            help_requested = true;
                        }
                        arg->pos = processed;
                        ++arg;  // move to next argument, which is the argument to this switch
                        flag.handler1(arg->text);
                    }

                    arg->pos = processed;
                    break;
                }
            }

            // For now comment this out to try leaving unmatched switches alone, so that
            // Unix absolute filenames work... and in case an absolute filename collides
            // with a legit switch name, also added "--" above... let's see how this works
            //if (arg->pos != processed && (arg->text.starts_with("-") || arg->text.starts_with("/"))) {
            //    arg->pos = processed;
            //    print("Unknown option: " + arg->text + " (try -help)\n");
            //    help_requested = true;
            //}
        }

        std::erase_if( args, [=](auto& arg){ return arg.pos == processed; } );
    }

    auto print_help() -> void
    {
        help_requested = true;

        std::sort(
            flags.begin(),
            flags.end(),
            [](auto& a, auto& b){ return a.group < b.group || (a.group == b.group && a.name < b.name); }
        );

        print("\nUsage: cppfront [options] file ...\n\nOptions:\n");
        int last_group = -1;
        for (auto& flag : flags) {
            if (last_group != flag.group) {
                print("\n");
                last_group = flag.group;
            }
            print("  -");
            auto n = flag.name.substr(0, flag.unique_prefix);
            if (flag.unique_prefix < flag.name.length()) {
                n += "[";
                n += flag.name.substr(flag.unique_prefix);
                n += "]";
            }
            if (!flag.synonym.empty()) {
                n += ", -" + flag.synonym;
            }
            print(n, max_flag_length + 3);
            print(flag.description);
            print("\n");
        }
    }

    auto add_flag(int group, std::string_view name, std::string_view description, callback0 handler0, callback1 handler1, std::string_view synonym) {
        flags.emplace_back( group, name, description, handler0, handler1, synonym );
        if (max_flag_length < std::ssize(name)) {
            max_flag_length = std::ssize(name);
        }
    }
    struct register_flag {
        register_flag(int group, std::string_view name, std::string_view description, callback0 handler0, callback1 handler1 = nullptr, std::string_view synonym = {});
    };

    auto set_args(int argc, char* argv[]) -> void {
        for (auto i = 1; i < argc; ++i) {
            args.emplace_back( i, argv[i] );
        }
    }

    auto help_was_requested() -> bool {
        return help_requested;
    }

    auto arguments() -> std::vector& {
        return args;
    }

    auto print_version() -> void {
        help_requested = true;
        print("cppfront 0.1.1 compiler\nCopyright (C) Herb Sutter\n");
        print("All rights reserved\n");
        print("\nSPDX-License-Identifier: CC-BY-NC-ND-4.0");
        print("\n  No commercial use");
        print("\n  No forks/derivatives");
        print("\n");
        print("\nNote: This is a hilariously incomplete personal project.");
        print("\nIt's known to be incomplet and incorrekt, and it has lots of b");
        print("\nad formAt ting. Absolutely no warranty - try at your own risk.");
        print("\n");
    }

} cmdline;

cmdline_processor::register_flag::register_flag(int group, std::string_view name, std::string_view description, callback0 handler0, callback1 handler1, std::string_view synonym) {
    cmdline.add_flag( group, name, description, handler0, handler1, synonym );
}

static cmdline_processor::register_flag cmd_help   (
    0,
    "help",
    "Print help",
    []{ cmdline.print_help(); },
    nullptr,
    "?"
);

static cmdline_processor::register_flag cmd_version(
    0,
    "version",
    "Print version information",
    []{ cmdline.print_version(); }
);

}

#endif

Web Proxy Viewer  |  New URL  |  Original Page