async def MAIN(message, args, level, perms, SERVER):

    if isinstance(message.channel, discord.DMChannel
                  ):  # Everyone should be able to see button presses
        await message.channel.send("This command cannot be used in DMs!")
        return

    db = Database()

    if level == 1:  # `tc/button` will check the current button

        try:
            # "public.bigredbutton" always has a single entry unless there has never been a button. If len(button_info)
            # is 0, that's the case, and this will throw an error caught by the try except block.
            button_info = db.get_entries("bigredbutton",
                                         columns=["button", "info"])[0]
        except IndexError:
            button_info = db.get_entries("bigredbutton",
                                         columns=["button", "info"])

        if len(button_info) == 0:  # If there is no button, create one
            button_number = 1  # It'd be the first ever button
            serial_number = key_generator(random.randrange(
                8, 15))  # Generate serial
            exploding_chance = random.randrange(
                15, 51)  # 15 to 50% chance of exploding
            inspector = number_key(3)  # Factory inspector code

            db.add_entry(
                "bigredbutton",
                [1, f"{serial_number} {exploding_chance} {inspector}", "", ""])
            # Insert the new button info. Now there is a button, so skip to [*]

        elif button_info[
                1] == "PRESSED":  # This is the 3 second interval after a button is pressed and before
            return  # the outcome is announced. Ignore button checks for this period

        elif len(button_info[1].split(" ")) == 1:  # [#]
            # Button, when in cooldown, has a single code for its info column, representing when it was pressed and
            # if it blew up or not. This code, unlike the normal `serial exp_chance` information, has no space in
            # it. This section detects that code

            pressing_time = int(
                button_info[1][2:])  # Time at which the button was pressed

            if button_info[1].startswith(
                    "0-"):  # `0-time` means button didn't blow up
                left = int((pressing_time + 15) -
                           time.time())  # Time left for button to come back

                if left < 0:  # If it's negative, the button should've returned by now but didn't
                    # So generate the button on the spot
                    button_number = button_info[
                        0] + 1  # Increment button number
                    serial_number = key_generator(random.randrange(
                        8, 15))  # Generate serial
                    exploding_chance = random.randrange(
                        15, 51)  # 15 to 50% chance of exploding
                    inspector = number_key(3)  # Factory inspector code

                    n_info = f"{serial_number} {exploding_chance} {inspector}"
                    db.edit_entry("bigredbutton",
                                  entry={
                                      "button": button_number,
                                      "info": n_info
                                  })
                    # Update with the new button

                else:  # If it's not negative, and the timer hasn't ended, report the amount of time remaining
                    await message.channel.send(
                        f"The new button is currently being prepared! {left}s remain!"
                    )
                    return

            if button_info[1].startswith(
                    "1-"):  # `1-time` means the button blew up
                left = int((pressing_time + 300) -
                           time.time())  # Time left for button to come back

                if left < 0:  # Read above about the timer being negative
                    button_number = button_info[0] + 1
                    serial_number = key_generator(random.randrange(8, 15))
                    exploding_chance = random.randrange(15, 51)
                    inspector = number_key(3)

                    n_info = f"{serial_number} {exploding_chance} {inspector}"
                    db.edit_entry("bigredbutton",
                                  entry={
                                      "button": button_number,
                                      "info": n_info
                                  })
                    # Update with new button

                else:  # Report the actual timer if it hasn't ended yet
                    mn = int(left / 60)  # Extract minutes
                    sc = left % 60  # Extract seconds
                    await message.channel.send(
                        f"The new button is being reconstructed. {mn}min {sc}s remain!"
                    )
                    return

        else:  # If there's a button and that's it
            button_number = button_info[0]  # Report the button number...
            serial_number = button_info[1].split(" ")[0]  # Its serial...
            exploding_chance = button_info[1].split(" ")[
                1]  # ...Its explosion chance
            inspector = button_info[1].split(" ")[2]  # And its inspector

        # [*] Report the current button
        await message.channel.send(
            f"""<:bigredbutton:654042578617892893> This is **Big Red Button #{button_number}**
			
			It has a **{exploding_chance}%** chance of exploding. The serial number is `{serial_number}`.
			It was inspected and approved by Factory Inspector #{inspector}.
			Use `tc/bigredbutton press` to press this button!""".replace("\t", ""))

        return

    if args[1].lower() == "top":  # Points leaderboard

        unformatted_points = db.get_entries("bigredbutton",
                                            columns=["points"])[0]
        unformatted_points = [
            x.split("-") for x in unformatted_points[0].split(" ")
        ]
        # unformatted_points might include empty strings from how the points value is formatted
        points = []  # This variable will be the clean version

        for x in unformatted_points:
            try:
                x[0] = int(x[0])
                x[1] = int(x[1])
                points.append(
                    x)  # If it passed the int tests, append it to points
            except ValueError:  # If it fails to convert to integers, it's an empty string and is ignored
                continue

        points = sorted(points, reverse=True,
                        key=lambda x: x[1])  # Sort the leaderboard

        player_info = [x for x in points if x[0] == message.author.id][0]
        player_ind = points.index(player_info)

        # args[2] is the page number.
        if level == 2:  # If it's not specified, assume it's the first page
            points = points[:10]
            page = 1

        elif not is_whole(
                args[2]
        ):  # If it's not a valid integer, assume it's first page also
            points = points[:10]
            page = 1

        elif (int(args[2]) -
              1) * 10 >= len(points):  # Detect if the page number is too big
            await message.channel.send(
                f"There is no page {args[2]} on Big Red Button!")
            return

        else:  # This means the user specified a valid page number
            lower = (int(args[2]) - 1) * 10
            upper = int(args[2]) * 10
            points = points[lower:upper]
            page = int(args[2])

        # Top of the message
        beginning = f"```diff\n---⭐ Big Red Button Points Leaderboard Page {page} ⭐---\n"
        beginning += "\n Rank |  Name                  |  Points\n"

        for person in points:
            r = points.index(person) + 1 + (page - 1) * 10
            if r == 1:  # + if the person is first
                line = f"+ {r}{' ' * (4 - len(str(r)))}|  "
            else:  # - otherwise
                line = f"- {r}{' ' * (4 - len(str(r)))}|  "

            try:  # Try to gather a username from the ID
                member = SERVER["MAIN"].get_member(int(person[0])).name
            except:  # If you can't, just display the ID
                member = str(person[0])

            line += f"{member[:20]}{' ' * (22 - len(member[:20]))}|  "  # Trim usernames to 20 characters long
            line += str(person[1]) + "\n"  # Add points value and newline

            beginning += line  # Add this line to the final message

        beginning += f"\nYour rank is {player_ind+1}, with {player_info[1]} points."
        beginning += "```"  # Close off code block

        await message.channel.send(beginning)
        return

    if args[1].lower() == "press":  # Press the button!

        button_info = db.get_entries("bigredbutton")[0]

        # This segment is almost an exact repeat of [#] up above
        if len(button_info[1].split(" ")) == 1 and button_info[1] != "PRESSED":
            pressing_time = int(button_info[1][2:])

            if button_info[1].startswith("0-"):
                left = int((pressing_time + 15) - time.time())

                if left < 0:
                    button_number = button_info[0] + 1
                    serial_number = key_generator(random.randrange(8, 15))
                    exploding_chance = random.randrange(15, 51)
                    inspector = number_key(3)

                    n_info = f"{serial_number} {exploding_chance} {inspector}"
                    db.edit_entry("bigredbutton",
                                  entry={
                                      "button": button_number,
                                      "info": n_info
                                  })

                    await message.channel.send(
                        f"""Big Red Button #{button_number} has arrived from inspection 
					by Factory Inspector #{inspector}, now with a {exploding_chance}% chance to explode and a 
					serial number of `{serial_number}`!""".replace("\n",
                                                    "").replace("\t", ""))
                    return

                else:
                    await message.channel.send(
                        f"The new button is currently being prepared! {left}s remain!"
                    )
                    return

            if button_info[1].startswith("1-"):
                left = int((pressing_time + 300) - time.time())

                if left < 0:
                    button_number = button_info[0] + 1
                    serial_number = key_generator(random.randrange(8, 15))
                    exploding_chance = random.randrange(15, 51)
                    inspector = number_key(3)

                    n_info = f"{serial_number} {exploding_chance} {inspector}"
                    db.edit_entry("bigredbutton",
                                  entry={
                                      "button": button_number,
                                      "info": n_info
                                  })

                    await message.channel.send(
                        f"""Big Red Button #{button_number} has arrived from inspection 
					by Factory Inspector #{inspector}, now with a {exploding_chance}% chance to explode and a 
					serial number of `{serial_number}`!""".replace("\n",
                                                    "").replace("\t", ""))
                    return

                else:
                    mn = int(left / 60)
                    sc = left % 60
                    await message.channel.send(
                        f"The new button is being reconstructed. {mn}min {sc}s remain!"
                    )
                    return

        # We already checked button_info[1], check two others now
        button_number = button_info[0]
        incapacitated = button_info[2]

        if str(message.author.id) in incapacitated:  # If you're incapacitated
            incapacitated = incapacitated.split(" ")

            ind = 0  # Find the author in the incapacitated list and extract their explosion time
            for incap in incapacitated:
                if incap.split("-")[0] == str(message.author.id):
                    ind = incapacitated.index(incap)
                    explosion_t = int(incap.split("-")[1])
                    break

            # Calculate how long it'll be before they can recover
            delta = (explosion_t + 21600) - time.time()

            if delta < 0:  # If it's negative, then they already recovered and can go on
                del incapacitated[ind]  # Delete the entry for this person

                incapacitated = " ".join(incapacitated)  # Join the list

                # Update with the new incapacitated list
                db.edit_entry("bigredbutton",
                              entry={"incapacitated": incapacitated})

            else:  # If it's not negative, they still have to wait a little
                abs_delta = [
                    int(delta),  # Seconds
                    int(delta / 60),  # Minutes
                    int(delta / (60 * 60))
                ]  # Hours

                sc = abs_delta[0] % 60
                mn = abs_delta[1] % 60
                hr = abs_delta[2]

                await message.channel.send(
                    f"You are still incapacitated! Wait {hr}h {mn}min {sc}s to press again."
                )
                return

        if strip_alpha(message.author.name) == "":
            return  # Don't try to cheese it by having no letters

        if button_info[
                1] == "PRESSED":  # If it's currently being pressed, ignore this press
            return
        else:  # Mark this button as being pressed so nobody else presses it during the 3 second interval
            db.edit_entry("bigredbutton", entry={"info": "PRESSED"})

        # Gather serial_number and exploding_chance for calculations
        serial_number = button_info[1].split(" ")[0]
        exploding_chance = int(button_info[1].split(" ")[1])
        inspector = list(button_info[1].split(" ")[2])

        new_chance = exploding_chance  # Clean slate variable
        if str(message.author.id)[-1] in serial_number:
            new_chance *= 0.67  # If last digit of ID is in serial...

        if strip_alpha(message.author.name)[0].upper() in serial_number:
            new_chance *= 2  # If first letter of username is in serial...

        point_retention = 0.5
        share_count = 0
        disc = list(str(message.author.discriminator))
        for x in range(len(list(inspector))):
            if inspector[x] in disc:
                share_count += 1
                disc.remove(inspector[x])
                inspector[x] = "-"

        if share_count == 2:
            point_retention = 0.75
        if share_count == 3:
            point_retention = 0.9

        seed = random.uniform(
            0,
            100)  # Has to be above the explosion chance otherwise it explodes

        await message.channel.send(
            f"**{message.author.name}** presses the button, and...")
        await asyncio.sleep(3)  # Suspense!

        if seed <= new_chance:  # If it's under the explosion chance, it blows up
            n_button_info = f"1-{int(time.time())}"  # Remember, `1-time` is the explosion flag
            new_inc = f" {message.author.id}-{n_button_info[2:]}"  # Add this person to the incapacitated list
            points = button_info[3].split(" ")  # Get the points list

            new_points = 0

            ind = -1  # Find the player in the points list and halve their point count
            for player in points:
                if player.split("-")[0] == str(message.author.id):
                    ind = points.index(player)
                    new_points = int(
                        int(player.split("-")[1]) * point_retention)
                    points[ind] = f"{message.author.id}-{new_points}"

            if ind == -1:  # If ind is still -1, then the player wasn't found in the points list so create a new
                points.append(f"{message.author.id}-{new_points}"
                              )  # entry for them with 0 points

            points = " ".join(points)

            db.edit_entry("bigredbutton",
                          entry={
                              "info": n_button_info,
                              "points": points,
                              "incapacitated": incapacitated + new_inc
                          })
            # Update with the explosion info, the new incapacitated list, and the new points list

            await message.channel.send(
                f"""<:bigredbutton:654042578617892893> ***The #{button_number} Big Red Button blew up!***

			<@{message.author.id}> has been incapacitated. Their point total is now **{new_points}**.
			They cannot press any more buttons for 6 hours.
			The button is broken. It'll take **5 minutes** to rebuild it.""".replace(
                    "\t", ""))

            await asyncio.sleep(300)  # Five minutes until the button is back

        else:  # If seed > new_chance, it doesn't blow up
            points = button_info[3].split(" ")  # Get points list to add points
            n_button_info = f"0-{int(time.time())}"  # `0-time` means no explosion

            ind = -1  # Find player in points list and add the new points
            for player in points:
                if player.split("-")[0] == str(message.author.id):
                    ind = points.index(player)
                    # Note: the points they gain is ALWAYS the nominal value for the exploding chance, not the
                    # modified serial number chance that was used to calculate explosions
                    new_points = int(player.split("-")[1]) + exploding_chance
                    points[ind] = f"{message.author.id}-{new_points}"

            if ind == -1:  # If they're not in the points list, add them with the new point value
                points.append(f"{message.author.id}-{exploding_chance}")

            points = " ".join(points)

            db.edit_entry("bigredbutton",
                          entry={
                              "info": n_button_info,
                              "points": points
                          })
            # Update with the pressing info and the new points list

            await message.channel.send(f"""
			<:bigredbutton:654042578617892893> The #{button_number} Big Red Button did nothing.

			<@{message.author.id}> gained {exploding_chance} points. Another button arrives in **15 seconds**.
			""".replace("\t", ""))

            await asyncio.sleep(15)  # Fifteen seconds until the button is back

        # Generate new serial_number and exploding_chance
        button_number += 1
        serial_number = key_generator(random.randrange(8, 15))
        exploding_chance = random.randrange(15, 51)
        inspector = number_key(3)

        n_info = f"{serial_number} {exploding_chance} {inspector}"
        db.edit_entry("bigredbutton",
                      entry={
                          "button": button_number,
                          "info": n_info
                      })
        # Update table with the new button

        # Announce the new button
        await message.channel.send(
            f"""Big Red Button #{button_number} has arrived from inspection 
		by Factory Inspector #{inspector}, now with a {exploding_chance}% chance to explode and a 
		serial number of `{serial_number}`!""".replace("\n", "").replace("\t", ""))
        return
Exemple #2
0
def operation_check(block):
    matching_ops = []  # List of operations that match

    for possible_op in FUNCTIONS.keys():  # Check through each operation
        letter_expect = False
        op_regex = possible_op  # Prepare a variable to serve as the regex for whether or not the code matches
        # this operation

        for param in range(possible_op.count(
                "?")):  # Each ? is a parameter of the function
            alph = ALPHABET.lower()[param]
            types = FUNCTIONS[possible_op]["TYPES"][
                alph]  # Find out what type is expected for that parameter

            if "STRING" in types or "BOOLEAN" in types or "ARRAY STRING" in types or "ARRAY BOOLEAN" in types:
                letter_expect = True

            dot_exp = r"([^\^!-&\*-\/<-@_]{1,})"
            if possible_op == "out{?}":
                dot_exp = r"([^\^]{1,})"
            elif types == ["NUMBER"] or types == ["INTEGER"] or types == [
                    "ARRAY NUMBER"
            ]:
                dot_exp = r"([^\\A-z]{1,})"
            '''if len(strip_alpha(possible_op)) == 0:
				dot_exp = r"([^\^\\!-\/<-@]{1,})"
			elif len(types) == 0:
				dot_exp = r"([^\\]{1,})"
			elif types == ["NUMBER"] or types == ["INTEGER"] or types == ["ARRAY NUMBER"]:
				# If it's expecting a number of some kind, the parameter shouldn't have any letters in it, and so we 
				# can use a regex that does not count letter characters to detect this parameter
				dot_exp = r"([^A-z]{1,})"'''

            # If any type is allowed (len(types) == 0) or the type is BOOLEAN or STRING (which contain letters)
            # Use the regex that matches all characters except for backslashes

            op_regex = op_regex.replace(
                "?", dot_exp, 1
            )  # Replace the _ placeholder with the matching group for that
            # parameter

        match = re.search(
            op_regex, block
        )  # Try to match this operation (the previously generated regex) with the code

        if match:  # If it's a match, append it to the matching operations array
            if possible_op.count("?") == 2 and "\\" == match.group(1)[-1]:
                continue

            if not possible_op.startswith("?") and match.span(
            )[0] != 0 and block[match.span()[0] - 1] == "\\":
                continue

            matching_ops.append([possible_op, match, letter_expect])

    if "out{?}" in matching_ops or "out{" in block:  # If the operation is out{}, leave a flag for it. It'll be handled
        substring = block[block.find("out{") + 4:block.find("}")]
        return ["out", f"({substring})", False]  # in parenthesis_parser

    if len(matching_ops
           ) == 0:  # If no operations match the code, return it unchanged
        return [False, block, False]

    letter_operations = [x for x in matching_ops if strip_alpha(x[0]) != ""
                         ]  # Operations that don't use letters are

    if len(letter_operations) == 0 and strip_alpha(block) != "":
        if True not in [x[2] for x in matching_ops]:
            return [False, block, False]

    if len(letter_operations) == 0 and len(
            matching_ops
    ) != 0:  # all either math operations or other operations that
        # can be used with eachother. If this statement is composed entirely of them, then we can treat it differently

        operators = [[
            x[0].replace("?", "").replace("\\", ""),
            FUNCTIONS[x[0]]["PRIORITY"], x[0]
        ] for x in matching_ops]

        full_token_list = []
        current_index = 0
        omit_bracket = False

        while True:
            operator_indices = []
            for op in operators:
                found = block.find(op[0], current_index)
                if found != -1:
                    operator_indices.append(found)
                else:
                    operator_indices.append(len(block))

            min_op = min(operator_indices)
            now_op = operators[operator_indices.index(min_op)][0]

            if min_op == len(block):
                full_token_list.append(block[current_index:])
                break

            to_add = block[current_index:min_op]

            full_token_list.append(to_add)
            full_token_list.append(now_op)
            current_index = min_op + len(now_op)

        #full_token_list.append(block[current_index:])

        #operator_order = sorted(operator_order, key=lambda o: (5 - o[1]))

        priority_limit = max([x[1] for x in operators])
        op_list = [x[0] for x in operators]

        for p in range(priority_limit + 1):
            current_priority = priority_limit - p

            for token in full_token_list:
                if token in op_list:
                    ind = op_list.index(token)
                    if operators[ind][1] != current_priority:
                        continue

                    token_ind = full_token_list.index(token)
                    operation_name = operators[ind][2]
                    param_info = FUNCTIONS[operation_name]["TYPES"]

                    params = []
                    params_ind = []
                    if len(param_info.keys()) != 1:
                        for r in range(token_ind):
                            search_ind = token_ind - r - 1
                            if full_token_list[search_ind] != []:
                                c_param = full_token_list[search_ind]

                                param_name = "a"
                                expected_types = param_info[
                                    param_name]  # The allowed types for param
                                fit_type = ""  # The type this parameter will fit under

                                for expect in expected_types:
                                    if expect == "NUMBER":
                                        if is_whole(c_param):
                                            c_param = int(c_param)
                                            fit_type = expect
                                            break
                                        if is_float(c_param):
                                            c_param = float(c_param)
                                            fit_type = expect
                                            break
                                    if expect == "INTEGER":
                                        if is_whole(c_param):
                                            c_param = int(c_param)
                                            fit_type = expect
                                            break
                                    if expect == "BOOLEAN":
                                        if c_param in ["True", "False"]:
                                            c_param = bool(c_param)
                                            fit_type = expect
                                            break
                                    if expect.startswith("ARRAY"):
                                        if c_param.startswith(
                                                "[") and c_param.endswith("]"):
                                            c_param = array_to_list(c_param)
                                            fit_type = expect
                                            break
                                    if expect == "STRING":
                                        if (not is_float(c_param)
                                                and not is_whole(c_param)
                                                and c_param
                                                not in ["True", "False"]):
                                            fit_type = expect
                                            break

                                if fit_type == "" and len(expected_types) > 0:
                                    #return [True, [operation_name, param_name, c_param], True]
                                    return [True, block, True]

                                params_ind.append(search_ind)
                                params.append(c_param)
                                break

                    for search_ind in range(token_ind + 1,
                                            len(full_token_list)):
                        if full_token_list[search_ind] != []:
                            c_param = full_token_list[search_ind]

                            param_name = "a" if len(
                                param_info.keys()) == 1 else "b"
                            expected_types = param_info[param_name]
                            fit_type = ""  # The type this parameter will fit under

                            for expect in expected_types:
                                if expect == "NUMBER":
                                    if is_whole(c_param):
                                        c_param = int(c_param)
                                        fit_type = expect
                                        break
                                    if is_float(c_param):
                                        c_param = float(c_param)
                                        fit_type = expect
                                        break
                                if expect == "INTEGER":
                                    if is_whole(c_param):
                                        c_param = int(c_param)
                                        fit_type = expect
                                        break
                                if expect == "BOOLEAN":
                                    if c_param in ["True", "False"]:
                                        c_param = bool(c_param)
                                        fit_type = expect
                                        break
                                if expect.startswith("ARRAY"):
                                    if c_param.startswith(
                                            "[") and c_param.endswith("]"):
                                        c_param = array_to_list(c_param)
                                        fit_type = expect
                                        break
                                if expect == "STRING":
                                    if (not is_float(c_param)
                                            and not is_whole(c_param) and
                                            c_param not in ["True", "False"]):
                                        fit_type = expect
                                        break

                            if fit_type == "" and len(expected_types) > 0:
                                #return [True, [operation_name, param_name, c_param], True]
                                return [True, block, True]

                            params_ind.append(search_ind)
                            params.append(c_param)
                            break

                    result = FUNCTIONS[operation_name]["MAIN"](*params)

                    if type(result) is list:
                        result = list_to_array(result)

                    full_token_list[token_ind] = str(result)
                    for x in params_ind:
                        full_token_list[x] = []

            full_token_list = [x for x in full_token_list if x != []]

        full_token_list = [x for x in full_token_list if x.strip() != ""]

        return [True, full_token_list[0],
                False]  # There was an operation and no error. Pass the result

    # Just in case there's more than 1 matching operation, run this sorted command. It takes the span of the operation
    # match (the start and end indices for when the operation applies), and subtracts the start index from end index
    # to determine how long the span is. Put the one with the longest span first
    matching_ops = sorted(matching_ops,
                          reverse=True,
                          key=(lambda m: len(m[0])))
    matching_ops = sorted(matching_ops,
                          reverse=True,
                          key=(lambda m: m[1].span()[1] - m[1].span()[0]))

    # Assume it's the one with the longest span. This prevents subsets of operations being counted instead of the
    operation, match, lt = matching_ops[
        0]  # actual operation specified. Define the operation and the match object

    parameters = [
    ]  # List of parameters that will be used in the operation function

    for g in range(operation.count("?")):  # For each parameter necessary...
        param = match.group(g + 1).strip(
        )  # Match the group of the parameter (group 0 is the entire block; start at 1)
        param_name = ALPHABET.lower(
        )[g]  # Get the parameter name (which is just the corresponding letter in alphabet)

        expected_types = FUNCTIONS[operation]["TYPES"][
            param_name]  # The allowed types for this parameter
        fit_type = ""  # The type this parameter will fit under

        for expect in expected_types:
            if expect == "NUMBER":  # NUMBER can be both int and float
                if is_whole(
                        param):  # Detect if it can be interpreted as an int
                    param = int(param)  # If so, make it an int
                    fit_type = expect
                    break
                if is_float(
                        param):  # Detect if it can be interpreted as a float
                    param = float(param)  # If so, make it a float
                    fit_type = expect
                    break
            if expect == "INTEGER":  # INTEGER is exclusively int
                if is_whole(param):  # Detect if it can be an int
                    param = int(param)  # If so, make it an int
                    fit_type = expect
                    break
            if expect == "BOOLEAN":  # BOOLEAN can only be True or False
                if param in ["True", "False"]:  # Detect if it's one of those
                    param = bool(param)  # If so, turn it into a bool
                    fit_type = expect
                    break
            if expect.startswith("ARRAY"):  # ARRAYs are defined with brackets
                if param.startswith("[") and param.endswith("]"):
                    param = array_to_list(param)
                    fit_type = expect
                    break
            if expect == "STRING":  # STRING can be anything that isn't instantly defined as one of the others
                if not is_float(param) and not is_whole(
                        param) and param not in ["True", "False"
                                                 ]:  # Detect that it's
                    # not any of the other types. param is already a str, so it doesn't need to be made into one
                    fit_type = expect
                    break

        if fit_type == "" and len(
                expected_types
        ) > 0:  # If there ARE expected types but the parameter can't fit them
            #return [True, [operation_name, param_name, c_param], True]
            return [True, block,
                    True]  # There IS an operation but there is an error.
            # Return the operation and param name for error specification

        parameters.append(
            param
        )  # If everything went alright, add param to the parameter list

    result = FUNCTIONS[operation]["MAIN"](
        *parameters)  # Extract a result from the operation function

    return [True, result,
            False]  # There was an operation and no error. Pass the result