#!/usr/bin/python
# convert LLVM GenAsmWriter.inc for Capstone disassembler.
# by Nguyen Anh Quynh, 2019

import sys

if len(sys.argv) == 1:
    print("Syntax: %s <GenAsmWriter.inc> <Output-GenAsmWriter.inc> <Output-GenRegisterName.inc> <arch>" %sys.argv[0])
    sys.exit(1)

arch = sys.argv[4] 
f = open(sys.argv[1])
lines = f.readlines()
f.close()

f1 = open(sys.argv[2], 'w+')

f2 = open(sys.argv[3], 'w+')

f1.write("/* Capstone Disassembly Engine, http://www.capstone-engine.org */\n")
f1.write("/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */\n")
f1.write("\n")

f2.write("/* Capstone Disassembly Engine, http://www.capstone-engine.org */\n")
f2.write("/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */\n")
f2.write("\n")

need_endif = False
in_getRegisterName = False
in_printAliasInstr = False
fragment_no = None
skip_printing = False

skip_line = 0
skip_count = 0

def replace_getOp(line):
    line2 = line
    if 'MI->getOperand(0)' in line:
        line2 = line.replace('MI->getOperand(0)', 'MCInst_getOperand(MI, 0)')
    elif 'MI->getOperand(1)' in line:
        line2 = line.replace('MI->getOperand(1)', 'MCInst_getOperand(MI, 1)')
    elif 'MI->getOperand(2)' in line:
        line2 = line.replace('MI->getOperand(2)', 'MCInst_getOperand(MI, 2)')
    elif 'MI->getOperand(3)' in line:
        line2 = line.replace('MI->getOperand(3)', 'MCInst_getOperand(MI, 3)')
    elif 'MI->getOperand(4)' in line:
        line2 = line.replace('MI->getOperand(4)', 'MCInst_getOperand(MI, 4)')
    elif 'MI->getOperand(5)' in line:
        line2 = line.replace('MI->getOperand(5)', 'MCInst_getOperand(MI, 5)')
    elif 'MI->getOperand(6)' in line:
        line2 = line.replace('MI->getOperand(6)', 'MCInst_getOperand(MI, 6)')
    elif 'MI->getOperand(7)' in line:
        line2 = line.replace('MI->getOperand(7)', 'MCInst_getOperand(MI, 7)')
    elif 'MI->getOperand(8)' in line:
        line2 = line.replace('MI->getOperand(8)', 'MCInst_getOperand(MI, 8)')
    return line2

def replace_getReg(line):
    line2 = line
    if 'MI->getOperand(0).getReg()' in line:
        line2 = line.replace('MI->getOperand(0).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 0))')
    elif 'MI->getOperand(1).getReg()' in line:
        line2 = line.replace('MI->getOperand(1).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 1))')
    elif 'MI->getOperand(2).getReg()' in line:
        line2 = line.replace('MI->getOperand(2).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 2))')
    elif 'MI->getOperand(3).getReg()' in line:
        line2 = line.replace('MI->getOperand(3).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 3))')
    elif 'MI->getOperand(4).getReg()' in line:
        line2 = line.replace('MI->getOperand(4).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 4))')
    elif 'MI->getOperand(5).getReg()' in line:
        line2 = line.replace('MI->getOperand(5).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 5))')
    elif 'MI->getOperand(6).getReg()' in line:
        line2 = line.replace('MI->getOperand(6).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 6))')
    elif 'MI->getOperand(7).getReg()' in line:
        line2 = line.replace('MI->getOperand(7).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 7))')
    elif 'MI->getOperand(8).getReg()' in line:
        line2 = line.replace('MI->getOperand(8).getReg()', 'MCOperand_getReg(MCInst_getOperand(MI, 8))')
    return line2

# extract param between text()
# MRI.getRegClass(AArch64::GPR32spRegClassID).contains(MI->getOperand(1).getReg()))
def extract_paren(line, text):
    i = line.index(text)
    return line[line.index('(', i)+1 : line.index(')', i)]


# extract text between <>
# printSVERegOp<'q'>
def extract_brackets(line):
    if '<' in line:
        return line[line.index('<')+1 : line.index('>')]
    else:
        return ''

# delete text between <>, including <>
# printSVERegOp<'q'>
def del_brackets(line):
    if '<' in line:
        return line[:line.index('<')] + line[line.index('>') + 1:]
    else:
        return line


def print_line(line):
    line = line.replace('::', '_')
    line = line.replace('nullptr', 'NULL')
    if not skip_printing:
        if in_getRegisterName:
            f2.write(line + "\n")
        else:
            f1.write(line + "\n")


for line in lines:
    line = line.rstrip()
    #print("@", line)

    # skip Alias
    if arch.upper() == 'X86':
        if 'PRINT_ALIAS_INSTR' in line:
            # done
            break

    if skip_line:
        skip_count += 1
        if skip_count <= skip_line:
            # skip this line
            continue
        else:
            # skip enough number of lines, reset counters
            skip_line = 0
            skip_count = 0

    if "::printInstruction" in line:
        if arch.upper() in ('AARCH64', 'ARM64'):
            #print_line("static void printInstruction(MCInst *MI, SStream *O, MCRegisterInfo *MRI)\n{")
            print_line("static void printInstruction(MCInst *MI, SStream *O)\n{")
        else:
            print_line("static void printInstruction(MCInst *MI, SStream *O)\n{")
    elif 'const char *AArch64InstPrinter::' in line:
        continue
    elif 'getRegisterName(' in line:
        if 'unsigned AltIdx' in line:
            print_line("static const char *getRegisterName(unsigned RegNo, unsigned AltIdx)\n{")
        else:
            print_line("static const char *getRegisterName(unsigned RegNo)\n{")
    elif 'getRegisterName' in line:
        in_getRegisterName = True
        print_line(line)
    elif '::printAliasInstr' in line:
        if arch.upper() in ('AARCH64', 'PPC'):
            print_line("static char *printAliasInstr(MCInst *MI, SStream *OS, MCRegisterInfo *MRI)\n{")
            print_line('  #define GETREGCLASS_CONTAIN(_class, _reg) MCRegisterClass_contains(MCRegisterInfo_getRegClass(MRI, _class), MCOperand_getReg(MCInst_getOperand(MI, _reg)))')
        else:
            print_line("static bool printAliasInstr(MCInst *MI, SStream *OS)\n{")
        print_line("  unsigned int I = 0, OpIdx, PrintMethodIdx;")
        print_line("  char *tmpString;")
        in_printAliasInstr = True
    elif 'STI.getFeatureBits()[' in line:
        if arch.upper() == 'ARM':
            line2 = line.replace('STI.getFeatureBits()[', 'ARM_getFeatureBits(MI->csh->mode, ')
        elif arch.upper() == 'AARCH64':
            line2 = line.replace('STI.getFeatureBits()[', 'AArch64_getFeatureBits(')
        line2 = line2.replace(']', ')')
        print_line(line2)
    elif ', STI, ' in line:
        line2 = line.replace(', STI, ', ', ')

        if 'printSVELogicalImm<' in line:
            if 'int16' in line:
                line2 = line2.replace('printSVELogicalImm', 'printSVELogicalImm16')
                line2 = line2.replace('<int16_t>', '')
            elif 'int32' in line: 
                line2 = line2.replace('printSVELogicalImm', 'printSVELogicalImm32')
                line2 = line2.replace('<int32_t>', '')
            else:
                line2 = line2.replace('printSVELogicalImm', 'printSVELogicalImm64')
                line2 = line2.replace('<int64_t>', '')

        if 'MI->getOperand(' in line:
            line2 = replace_getOp(line2)

        # C++ template
        if 'printPrefetchOp' in line2:
            param = extract_brackets(line2)
            if param == '':
                param = 'false'
            line2 = del_brackets(line2)
            line2 = line2.replace(', O);', ', O, %s);' %param)
            line2 = line2.replace(', OS);', ', OS, %s);' %param)
        elif '<false>' in line2:
            line2 = line2.replace('<false>', '')
            line2 = line2.replace(', O);', ', O, false);')
            line2 = line2.replace('STI, ', '')
        elif '<true>' in line:
            line2 = line2.replace('<true>', '')
            line2 = line2.replace(', O);', ', O, true);')
            line2 = line2.replace('STI, ', '')
        elif 'printAdrLabelOperand' in line:
            # C++ template
            if '<0>' in line:
                line2 = line2.replace('<0>', '')
                line2 = line2.replace(', O);', ', O, 0);')
            elif '<1>' in line:
                line2 = line2.replace('<1>', '')
                line2 = line2.replace(', O);', ', O, 1);')
            elif '<2>' in line:
                line2 = line2.replace('<2>', '')
                line2 = line2.replace(', O);', ', O, 2);')
        elif 'printImm8OptLsl' in line2:
            param = extract_brackets(line2)
            line2 = del_brackets(line2)
            if '8' in param or '16' in param or '32' in param:
                line2 = line2.replace('printImm8OptLsl', 'printImm8OptLsl32')
            elif '64' in param:
                line2 = line2.replace('printImm8OptLsl', 'printImm8OptLsl64')
        elif 'printLogicalImm' in line2:
            param = extract_brackets(line2)
            line2 = del_brackets(line2)
            if '8' in param or '16' in param or '32' in param:
                line2 = line2.replace('printLogicalImm', 'printLogicalImm32')
            elif '64' in param:
                line2 = line2.replace('printLogicalImm', 'printLogicalImm64')
        elif 'printSVERegOp' in line2 or 'printGPRSeqPairsClassOperand' in line2 or 'printTypedVectorList' in line2 or 'printPostIncOperand' in line2 or 'printImmScale' in line2 or 'printRegWithShiftExtend' in line2 or 'printUImm12Offset' in line2 or 'printExactFPImm' in line2 or 'printMemExtend' in line2 or 'printZPRasFPR' in line2:
            param = extract_brackets(line2)
            if param == '':
                param = '0'
            line2 = del_brackets(line2)
            line2 = line2.replace(', O);', ', O, %s);' %param)
            line2 = line2.replace(', OS);', ', OS, %s);' %param)
        elif 'printComplexRotationOp' in line:
            # printComplexRotationOp<90, 0>(MI, 5, STI, O);
            bracket_content = line2[line2.index('<') + 1 : line2.index('>')]
            line2 = line2.replace('<' + bracket_content + '>', '')
            line2 = line2.replace(' O);', ' O, %s);' %bracket_content)

        print_line(line2)
    elif "static const char AsmStrs[]" in line:
        print_line("#ifndef CAPSTONE_DIET")
        print_line("  static const char AsmStrs[] = {")
        need_endif = True
    elif "static const char AsmStrsNoRegAltName[]" in line:
        print_line("#ifndef CAPSTONE_DIET")
        print_line("  static const char AsmStrsNoRegAltName[] = {")
        need_endif = True
    elif line == '  O << "\\t";':
        print_line("  unsigned int opcode = MCInst_getOpcode(MI);")
        print_line('  // printf("opcode = %u\\n", opcode);');
    elif 'MI->getOpcode()' in line:
        if 'switch' in line:
            line2 = line.replace('MI->getOpcode()', 'MCInst_getOpcode(MI)')
        else:
            line2 = line.replace('MI->getOpcode()', 'opcode')
        print_line(line2)

    elif 'O << ' in line:
        if '"' in line:
            line2 = line.lower()
            line2 = line2.replace('o << ', 'SStream_concat0(O, ');
        else:
            line2 = line.replace('O << ', 'SStream_concat0(O, ');
        line2 = line2.replace("'", '"')
        line2 = line2.replace(';', ');')
        if '" : "' in line2:    # "segment : offset" in X86
            line2 = line2.replace('" : "', '":"')

        # ARM
        print_line(line2)

        if '", #0"' in line2:
            print_line('    op_addImm(MI, 0);')

        if '", #1"' in line2: