160 lines
5.1 KiB
Python
160 lines
5.1 KiB
Python
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()))
|