Are the brackets fully matched?

Brain-Flak, 1101, 1085, 981 bytes


Try it online!

This is 980 bytes of source code, and +1 for the -a flag allowing ASCII input (but decimal output)

This is an answer I've been wanting to write for a very very long time. At least 6 months. I waited to post this because I knew that answering this challenge would be extra hard in brain-flak. But it's worth it for one very important reason: The source code itself is a truthy input, which is the entire point of this language itself.

And as I wrote about here, this question was what inspired me to write brain-flak.

Shortly after I wrote Are the brackets fully matched?, it made me wonder how much information you can store with only matched brackets. One thing that stood out to me, was that even though you only have 4 "atoms" of sorts:


you really have 8 units of information to convey, since each of these bracket types can be empty, or have other brackets in between, which are fundamentally different pieces of information. So, I decided to write a language that only allowed for matched brackets, and where empty brackets convey something different than brackets with other brackets inside of them.

This answer took roughly two hours to write. I'll admit it's pretty poorly golfed, mostly because a lot of the code is repeated for each bracket type. But I'm mostly amazed that I was able to write an answer at all, especially given that Brain-Flak is

A minimalist esolang designed to be painful to use

I'm going to attempt to golf it down later, but I wanted to get this out there anyway.

I have a detailed explanation, but it's about 6 thousand characters long, so I think it would not be wise to paste the entire thing into this answer. You can read through it here if you want. I'll add a shorter explanation here.

The basic idea, is that we repeat the following steps for every character on the stack:

  • We check each character to see if it matches any bracket. If it is an opening bracket, we push a number onto the other stack according to the following mapping:

    ( = 1
    < = 2
    [ = 3
    { = 4
  • Then we check to see if it matches any closing bracket. If it does, we push the equivalent number onto the alternate stack, just like for opening brackets. Then, we check if the top two numbers are equal. If they are, the both get popped and the program continues as normal. If they are not, we clear both stacks (to stop the looping) and push a one onto the alternate stack. This is essentially a "break" statement.

  • After checking the 8 bracket types, we push the value of this run through the loop. Since we zero out most of it, the only snippets that have any value are the conditionals when we compare against brackets. So if any bracket is matched, the whole loop has a value of 1. If none of them did, the whole loop has a value of 0. In this case, we will clear both stacks and push a 0 onto the alternate stack. Again, this is like a "break" statement.

After this main loop is running, the rest is fairly simple. We are on the (empty) main stack, and the alternate stack is empty (if the brackets were matched) or non-empty if they were not. So we run this:

#Toggle to the alternate stack

#Push this stack-height onto main-stack

#Logical not

This will push a 0 or a 1 onto the main-stack, and when the program ends it is implicitly printed.

  • Thanks to @WheatWizard for coming up with a good stack-clean "equals" and "logical not" snippet, and for regularly updating the github wiki with useful examples.

  • Thanks to @ASCII-only for writing an online integer metagolfer which helped immensely in writing this program


  • Removed some push pop redundancy

  • Changed my zero counter logic

Brain-Flak, 204 196 190 bytes


Try it online!

-8 bytes thanks to Wheat Wizard. -6 bytes thanks to Jo King.


This program stores the character codes of all current unclosed brackets on the second stack. The bracket pairs <>, [], and {} each have character codes that differ by exactly 2, so there is no need to check for them specifically. The pair () only differs by 1, so we check for ( specifically, and effectively decrement that byte (actually increment every other byte) before continuing.

# While there are bytes left to process

 # Move byte to second stack

 # Push 40, 0, 40, 60, 91, 123: (, then null, then all four opening brackets


   # For each opening bracket type:

    # Evaluate as zero

     # Compute difference between bracket type and input byte


    # Evaluate loop iteration as -1 if equal, 0 otherwise


   # Remove the 0 that was inserted to terminate that loop

   # Add 1 to result

   # Evaluate rest of this expression as zero

    # Determine whether the byte is open parenthesis

    # If not:

     # Add 1 to byte and break if


    # Return to main stack


 # Push result twice (0 if matched an opening bracket, 1 otherwise)

 # If byte was not an opening bracket:

  # Push zero to break out of if

    # Push (open bracket + 2 - byte) below that zero



 # If byte was neither an opening bracket nor the appropriate closing bracket:

  # Clear alternate stack and stay there to break out of main loop early


# End of main loop

# If a prefix was invalid, the top of the other stack is the same nonzero value
# that made us break out in the first place. If the string was a valid prefix,
# the other stack contains every unclosed bracket.  If the string is balanced,
# there are none of these. Thus, the other stack is empty if the
# brackets are balanced, and has a nonzero value on top otherwise.

# Push 1 on other stack if empty, and 0 on current stack otherwise

05AB1E, 19 bytes

Input is given in quotes. Code:


Well crap, a lot of bugs and unimplemented features were found. Explanation:

"[](){}<>"           # Push this string
          2÷         # Split into pieces of two
            )        # Wrap it into an array (which should not be needed)
             ""      # Push an empty string
               :     # Infinite replacement

This is actually a tricky part. What this looks like in pseudocode is:

input().replace(['[]', '()', '{}', '<>'], "")

This is covered by this part from the 05AB1E code:

if type(b) is list:
    temp_string = temp_string_2 = str(a)
    while True:
        for R in b:
            temp_string = temp_string.replace(R, c)
        if temp_string == temp_string_2:
            temp_string_2 = temp_string

As you can see, this is infinite replacement (done until the string doesn't change anymore). So, I don't have to worry about setting the replacement into a loop, since this is already builtin. After that:

                g    # Take the length of the final string
                 2Q  # Check if equal with 2 (which are the quotes at the end)

Uses CP-1252 encoding. Try it online! (slightly modified because the above version is deprecated).