import sys

# Type constants:
ty_ptr = "ty_ptr"
ty_int32 = "ty_int32"
ty_int128 = "ty_int128"

def define_opcodes():
    return [
        Opcode("QUEUE",
               ("OP_FLAG_PROCEDURE",),
               None,
               []),
        Opcode("TAIL",
               ("OP_FLAG_PROCEDURE",),
               None,
               []),
        Opcode("LOADV",
               (),
               [("ptr", ty_ptr)],
               [("res",ty_int128)]),
        Opcode("STOREV",
               (),
               [("ptr", ty_ptr), ("val", ty_int128)],
               []),
        Opcode("CAST",
               (),
               [("val", None)],
               [('res', None)]),
        Opcode("COPY",
               (),
               [("val", None)],
               [('res', None)]),
        Opcode("IFTRUE",
               ("OP_FLAG_BRANCH",),
               [("val", None)],
               []),
        Opcode("ADD",
               (),
               [("left", ty_int32), ("right", ty_int32)],
               [('res', ty_int32)]),
        Opcode("SUB",
               (),
               [("left", ty_int32), ("right", ty_int32)],
               [('res', ty_int32)]),
        Opcode("MUL",
               (),
               [("left", ty_int32), ("right", ty_int32)],
               [('res', ty_int32)]),
        Opcode("DIV",
               (),
               [("left", ty_int32), ("right", ty_int32)],
               [('res', ty_int32)])
        ]

class Opcode(object):
    
    def __init__(self, name, flags, input_spec, output_spec):
        self.string = name
        self.name = "OP_%s" % name

        self.flags = flags
        
        if input_spec is None:
            self.input_count = -1
        else:
            self.input_count = len(input_spec)
        self.input_spec = input_spec

        if output_spec is None:
            self.output_count = -1
        else:
            self.output_count = len(output_spec)
        self.output_spec = output_spec

        return

    pass

def dump_enum(out, opcodes):
    out.write('\n')
    out.write('// Opcode enumeration:\n')
    out.write('enum t_opcode {\n')
    for opcode in opcodes:
        out.write('  %s,\n' % opcode.name)
    out.write('  OP_MAX\n')
    out.write('};')
    return

def dump_input_constants(out, opcodes):
    out.write('\n')
    out.write('// Named indexes into the inputs array for each opcode:\n')
    for opcode in opcodes:
        if opcode.input_spec:
            ctr = 0
            for name, ty in opcode.input_spec:
                out.write('#define %s_%s %d\n' % (
                    opcode.name, name.upper(), ctr))
                ctr += 1
    return

def dump_flags_header(out, opcodes):
    out.write('\n')
    out.write('// Opcode flags:\n')
    flags = {}
    num = 1
    for opcode in opcodes:
        for flag in opcode.flags:
            if flag not in flags:
                out.write('#define %s %d\n' % (flag, num))
                out.write('#define %s(opc) ((opcode_flags[opc]&%s) != 0)\n' % (
                    flag.lower(), flag))
                num <<= 1
                flags[flag] = True
    out.write('extern int opcode_flags[OP_MAX+1];\n')
    return

def dump_flags_body(out,opcodes):
    out.write('int opcode_flags[OP_MAX+1] = {\n')
    for opcode in opcodes:
        if not opcode.flags:
            out.write('    0,')
        else:
            out.write('    %s,' % ("|".join(opcode.flags)))
        out.write(' /* %s */\n' % opcode.name)
    out.write('    0\n')
    out.write('};\n')
    return

def dump_counts(out, opcodes):
    out.write('int input_operand_counts[OP_MAX+1] = {\n')
    for opcode in opcodes:
        out.write('  %s, /* %s */\n' % (opcode.input_count, opcode.name))
    out.write('  -1 /* OP_MAX */\n')
    out.write('};\n')

    out.write('int output_operands_counts[OP_MAX+1] = {\n')
    for opcode in opcodes:
        out.write('  %s, /* %s */\n' % (opcode.output_count, opcode.name))
    out.write('  -1 /* OP_MAX */\n')
    out.write('};\n')
    return

def dump_strings(out, opcodes, max):
    out.write('const char *opcode_strings[OP_MAX+1] = {\n')
    for opcode in opcodes:
        out.write('  "%s",\n' % opcode.string)
    out.write('  NULL')
    out.write('};\n')

    out.write('const char *input_operand_names[OP_MAX+1][%d] = {\n' % max)
    for opcode in opcodes:
        if opcode.input_spec:
            out.write('  {')
            for name, ty in opcode.input_spec:
                out.write('"%s", ' % name)
            out.write('NULL},\n')
        else:
            out.write('  {NULL},\n')
    out.write('  {NULL}\n')
    out.write('};\n')
    return

def main():
    opcodes = define_opcodes()

    # Determine maximum number of named inputs and outputs, and add 1.
    # This will be the size of the name array, and the extra 1 is for
    # NULL termination.
    max = 0
    for op in opcodes:
        if op.input_count > max: max = op.input_count
        if op.output_count > max: max = op.output_count
    max += 1

    sys.stdout.write('#ifdef FLOWIR_OPCODES_HEADER\n')
    dump_enum(sys.stdout, opcodes)
    sys.stdout.write('\n')
    dump_input_constants(sys.stdout, opcodes)
    dump_flags_header(sys.stdout, opcodes)
    sys.stdout.write('\n')
    sys.stdout.write('// Various other information:\n')
    sys.stdout.write('extern int input_operand_counts[OP_MAX+1];\n')
    sys.stdout.write('extern const char *input_operand_names[OP_MAX+1][%d];\n'%
                     max)
    sys.stdout.write('extern int output_operands_counts[OP_MAX+1];\n')
    sys.stdout.write('extern const char *opcode_strings[OP_MAX+1];\n')
    sys.stdout.write('#endif\n\n')

    sys.stdout.write('#ifdef FLOWIR_OPCODES_BODY\n')
    opcodes = define_opcodes()
    dump_flags_body(sys.stdout, opcodes)
    dump_counts(sys.stdout, opcodes)
    dump_strings(sys.stdout, opcodes, max)
    sys.stdout.write('#endif\n')

main()
        
    
