solang-parser 0.2.1

Solang Solidity Parser
Documentation
#!/usr/bin/env python3
# pylint: disable=consider-using-enumerate, import-error

import re
import os
import sys
import getopt
import tempfile
from getkey import getkey


def parse_call(call):
    function = ''
    arguments = ""
    results = ""
    search = re.search(r'// (.*):(.*)\s->\s(.*)', call, re.MULTILINE | re.DOTALL)
    if search:
        function = search.group(1)
        arguments = search.group(2)
        results = search.group(3)
        if results.find("#") != -1:
            results = results[:results.find("#")]
    else:
        search = re.search(r'// (.*)(.*)\s->\s(.*)', call, re.MULTILINE | re.DOTALL)
        if search:
            function = search.group(1)
            arguments = search.group(2)
            results = search.group(3)
            if results.find("#") != -1:
                results = results[:results.find("#")]
    if function.find("wei") >= 0:
        function = function[:function.find(",")]
    return function.strip(), arguments.strip(), results.strip()


def colorize(left, right, index):
    red = "\x1b[31m"
    yellow = "\x1b[33m"
    reset = "\x1b[0m"
    colors = [red, yellow]
    color = colors[index % len(colors)]
    function, _arguments, _results = parse_call(right)
    left = left.replace("compileAndRun", color + "compileAndRun" + reset)
    right = right.replace("constructor", color + "constructor" + reset)
    if function:
        left = left.replace(function, color + function + reset)
        right = right.replace(function, color + function + reset)
    if left.find(function):
        bottom = " " * (left.find(function) - 4) + right
    else:
        bottom = " " + right
    return "    " + left + "\n" + bottom  # " {:<90} {:<90}\n{}".format(left, right, bottom)


def get_checks(content, sol_file_path):
    constructors = []
    checks = []
    for line in content.split("\n"):
        line = line.strip()
        if line.startswith("compileAndRun"):
            constructors.append(line)
        if line.startswith("ABI_CHECK") or line.startswith("BOOST_REQUIRE"):
            checks.append(line)
    with open(sol_file_path, "r", encoding='utf8') as sol_file:
        sol_constructors = []
        sol_checks = []
        inside_expectations = False
        for line in sol_file.readlines():
            if line.startswith("// constructor()"):
                sol_constructors.append(line)
            elif inside_expectations and line.startswith("// "):
                sol_checks.append(line)
            if line.startswith("// ----"):
                inside_expectations = True
        sol_file.close()
        if len(constructors) == len(sol_constructors) == 1:
            checks.insert(0, constructors[0])
            sol_checks.insert(0, sol_constructors[0])
        return checks, sol_checks


def show_test(name, content, sol_file_path, current_test, test_count):
    with tempfile.NamedTemporaryFile(delete=False) as cpp_file:
        cpp_file.write(content.encode())
        cpp_file.close()

        os.system("clear")
        print(str(current_test) + " / " + str(test_count) + " - " + name + "\n")
        diff_env = os.getenv('DIFF', "/usr/local/bin/colordiff -a -d -w -y -W 200 ")
        os.system(diff_env + " " + cpp_file.name + " " + sol_file_path)
        os.unlink(cpp_file.name)
        print("\n")

        checks, sol_checks = get_checks(content, sol_file_path)

        if len(checks) == len(sol_checks):
            for i in range(0, len(checks)):
                print(colorize(checks[i].strip(), sol_checks[i].strip(), i))
        else:
            print("warning: check count not matching. this should not happen!")

        what = ""
        print("\nContinue? (ENTER) Abort? (ANY OTHER KEY)")
        while what != '\n':
            what = getkey()
            if what != '\n':
                sys.exit(0)
        print()


def get_tests(e2e_path):
    tests = []
    for f in os.listdir(e2e_path):
        if f.endswith(".sol"):
            tests.append(f.replace(".sol", ""))
    return tests


def process_input_file(e2e_path, input_file, interactive):
    tests = get_tests(e2e_path)
    with open(input_file, "r", encoding='utf8') as cpp_file:
        inside_test = False
        test_name = ""
        inside_extracted_test = False
        new_lines = 0
        count = 0
        test_content = ""
        for line in cpp_file.readlines():
            test = re.search(r'BOOST_AUTO_TEST_CASE\((.*)\)', line, re.M | re.I)
            if test:
                test_name = test.group(1)
                inside_test = True
                inside_extracted_test = inside_test & (test_name in tests)
                if inside_extracted_test:
                    count = count + 1

            if interactive and inside_extracted_test:
                test_content = test_content + line

            if not inside_extracted_test:
                if line == "\n":
                    new_lines = new_lines + 1
                else:
                    new_lines = 0
                if not interactive and new_lines <= 1:
                    sys.stdout.write(line)

            if line == "}\n":
                if interactive and inside_extracted_test:
                    show_test(test_name, test_content.strip(), e2e_path + "/" + test_name + ".sol", count, len(tests))
                    test_content = ""
                inside_test = False
        cpp_file.close()
        sys.stdout.flush()


def main(argv):
    interactive = False
    input_file = None
    try:
        opts, _args = getopt.getopt(argv, "if:")
    except getopt.GetoptError:
        print("./remove-testcases.py [-i] [-f <full path to SolidityEndToEndTest.cpp>]")
        sys.exit(1)

    for opt, arg in opts:
        if opt == '-i':
            interactive = True
        elif opt in '-f':
            input_file = arg

    base_path = os.path.dirname(__file__)

    if not input_file:
        input_file = base_path + "/../../test/libsolidity/SolidityEndToEndTest.cpp"

    e2e_path = base_path + "/../../test/libsolidity/semanticTests/extracted"

    process_input_file(e2e_path, input_file, interactive)


if __name__ == "__main__":
    main(sys.argv[1:])