# Copyright (c) 2013 Austin T. Clements. All rights reserved.
# Use of this source code is governed by an MIT license
# that can be found in the LICENSE file.

import sys, re
from optparse import OptionParser

def read_toks():
    data = sys.stdin.read()
    while data:
        data = data.lstrip()
        if data.startswith("//") or data.startswith("#"):
            data = data.split("\n",1)[1]
        elif data.startswith("/*"):
            data = data.split("*/",1)[1]
        elif data.startswith("\"") or data.startswith("'"):
            c = data[0]
            m = re.match(r'%s([^\\%s]|\\.)*%s' % (c,c,c), data)
            yield m.group(0)
            data = data[m.end():]
        else:
            m = re.match(r"[_a-zA-Z0-9]+|[{}();]|[^_a-zA-Z0-9 \n\t\f]+", data)
            yield m.group(0)
            data = data[m.end():]

enums = {}

def do_top_level(toks, ns=[]):
    while toks:
        tok = toks.pop(0)
        if tok == "enum" and toks[0] == "class":
            toks.pop(0)
            name = toks.pop(0)
            # Get to the first token in the body
            while toks.pop(0) != "{":
                pass
            # Consume body and close brace
            do_enum_body("::".join(ns + [name]), toks)
        elif tok == "class":
            name = do_qname(toks)
            # Find the class body, if there is one
            while toks[0] != "{" and toks[0] != ";":
                toks.pop(0)
            # Enter the class's namespace
            if toks[0] == "{":
                toks.pop(0)
                do_top_level(toks, ns + [name])
        elif tok == "{":
            # Enter an unknown namespace
            do_top_level(toks, ns + [None])
        elif tok == "}":
            # Exit the namespace
            assert len(ns)
            return
        elif not ns and tok == "string" and toks[:2] == ["to_string", "("]:
            # Get the argument type and name
            toks.pop(0)
            toks.pop(0)
            typ = do_qname(toks)
            if typ not in enums:
                continue
            arg = toks.pop(0)
            assert toks[0] == ")"

            if typ in options.mask:
                make_to_string_mask(typ, arg)
            else:
                make_to_string(typ, arg)

def fmt_value(typ, key):
    if options.no_type:
        val = key
    else:
        val = "%s%s%s" % (typ, options.separator, key)
    if options.strip_underscore:
        val = val.strip("_")
    return val

def expr_remainder(typ, arg):
    if options.hex:
        return "\"(%s)0x\" + to_hex((int)%s)" % (typ, arg)
    else:
        return "\"(%s)\" + std::to_string((int)%s)" % (typ, arg)

def make_to_string(typ, arg):
    print("std::string")
    print("to_string(%s %s)" % (typ, arg))
    print("{")
    print("        switch (%s) {" % arg)
    for key in enums[typ]:
        if key in options.exclude:
            print("        case %s::%s: break;" % (typ, key))
            continue
        print("        case %s::%s: return \"%s\";" % \
            (typ, key, fmt_value(typ, key)))
    print("        }")
    print("        return %s;" % expr_remainder(typ, arg))
    print("}")
    print()

def make_to_string_mask(typ, arg):
    print("std::string")
    print("to_string(%s %s)" % (typ, arg))
    print("{")
    print("        std::string res;")
    for key in enums[typ]:
        if key in options.exclude:
            continue
        print("        if ((%s & %s::%s) == %s::%s) { res += \"%s|\"; %s &= ~%s::%s; }" % \
            (arg, typ, key, typ, key, fmt_value(typ, key), arg, typ, key))
    print("        if (res.empty() || %s != (%s)0) res += %s;" % \
        (arg, typ, expr_remainder(typ, arg)))
    print("        else res.pop_back();")
    print("        return res;")
    print("}")
    print()

def do_enum_body(name, toks):
    keys = []
    while True:
        key = toks.pop(0)
        if key == "}":
            assert toks.pop(0) == ";"
            enums[name] = keys
            return
        keys.append(key)
        if toks[0] == "=":
            toks.pop(0)
            toks.pop(0)
        if toks[0] == ",":
            toks.pop(0)
        else:
            assert toks[0] == "}"

def do_qname(toks):
    # Get a nested-name-specifier followed by an identifier
    res = []
    while True:
        res.append(toks.pop(0))
        if toks[0] != "::":
            return "::".join(res)
        toks.pop(0)

parser = OptionParser()
parser.add_option("-x", "--exclude", dest="exclude", action="append",
                  help="exclude FIELD", metavar="FIELD", default=[])
parser.add_option("-u", "--strip-underscore", dest="strip_underscore",
                  action="store_true",
                  help="strip leading and trailing underscores")
parser.add_option("-s", "--separator", dest="separator",
                  help="use SEP between type and field", metavar="SEP",
                  default="::")
parser.add_option("--hex", dest="hex", action="store_true",
                  help="return unknown values in hex", default=False)
parser.add_option("--no-type", dest="no_type", action="store_true",
                  help="omit type")
parser.add_option("--mask", dest="mask", action="append",
                  help="treat TYPE as a bit-mask", metavar="TYPE", default=[])
(options, args) = parser.parse_args()
if args:
    parser.error("expected 0 arguments")

do_top_level(list(read_toks()))
