Quantcast
Viewing latest article 6
Browse Latest Browse All 17

Answer by tjjfvi for Write a Stack Exchange compliant brainfuck explainer

brainfuck, 811775709 701 bytes

>>-[-[--->]<<-]>--...<++++++++++.>>>>>>>>>>+[->>>>>>+<<,[[-<+<+>>]<----------[[-]->]-[+>-]<<<+[-<[-]>]<[-<+>>+<]++++++[->-------<]>>++++++[-<<++++++>>]>+<<-[<->>]>[<]<-[<------->>]>[<]<-[<-->>]>[<]<-[<-------->>]>[<]<--------------[<--->>]>[<]<--[<---->>]>[<]+++++[-<------>]<+[<----->>]>[<]<--[<------>>]>[<]<[-]<<+>[>>>-]<[>>>]<<<->>++++[->++++++++<]+>>>+>]<]+[<<<<<<]>>>>[.>>>>>>]>>>>-<<<<<<<<<<[<<<<<<]<<<<<.>>>+[->>>>>>>>>>>>>[>>>>>>]<<<<<[<[<<.<<<<]>>>>>>[>>>>>>]<<<<<.>>>>[>+>>>>>[<<<<.>>>>>+>>>>>]<<<<[>>>.>>>]>>>.>>>[.>>>>>>]>>[<<<<<<]<]<<<[>->>>+>[>>>.>>>]>>-<<<<<.<[<<<<<<]<[->>>>>>>[>>>>>>]+>>>>[>>>>>>]>>-<<<<<<[<<<<<<]<]>+[>>>>>>]+>>>>[.>>>>>>]>>[<<<<<<]<<<<]+<<<.>>]>]>[<<<<<<]<<<<<<...

Try it online!

This could probably be golfed more, as this is, like, the second brainfuck program I've written.

Explanation

I should really write a program to do this for me...

Fullscreen Explanation

###### Execution will be divided into two phases: input and output.# The input phase will input all characters and lay them out into memory with metadata.# The output phase will then loop over the memory and print various segments of it, forming the explanation.################ Input Phase ###################### First, let's take a look at the memory layout.# We'll divide the memory into 6-byte chunks.# The chunks will be laid out in this format:#   - first header chunk, to denote the start#   - second header chunk, to denote the start#   - first source chunk#   - second source chunk#   - ...#   - last source chunk#   - primary separator chunk (in place of newline)#   - first chunk of first explanation#   - second chunk of first explanation#   - ...#   - last chunk of first explanation#   - separator chunk (in place of newline)#   - first chunk of second explanation#   - ......#   - last chunk of last explanation#   - eof chunk# The first header chunk will have the following bytes:#   0 10 96 0 0 0# (10 is newline, and 96 is backtick)# The second header chunk will consist entirely of zeroes.                     #####                     # Initialize the header chunks and output the initial code fence:>>                     # Move to cell #2 (0-indexed).-[-[--->]<<-]>--       # Set cell #2 to backtick (96) (pulled from https://esolangs.org/wiki/Brainfuck_constants)....                    # Print it thrice (fine, there is a tiny bit of output in this phase).<++++++++++.           # Set cell #1 to newline (10) and print it.>>>>>>>>>>+          # Move to cell #5 of the second header chunk and temporarily set it to 1.                     #####                     # Next, read the input into chunks and initialize them.                     # Every non-header non-eof chunk will be initialized to the following format:                     #   #0: [char]  (0 if separator)                     #   #1: [kind]  (which operator the char is)                     #   #2: [mark]  (currently 1; this will occasionally be temporarily set to 0 as a "bookmark")                     #   #3: 32      (the code for space, used for output)                     #   #4: [noop]  (1 if no-op source chunk or non-primary separator, 0 otherwise)                     #   #5: [done]  (currently 0, undone)                     # During the initialization, some cells are used for temporary values.[                    # While the current cell (#5 of the preceding chunk) is non-zero (and thus known to be 1), loop over the input:  -                    # Subtract 1 from the cell, [done], returning it to 0 (undone)>                    # Move to the active chunk.                       # Cell #5 will be used for positioning the pointer after a branch ('alignment'):>>>>>+               # Set cell #5 to 1                       #####                       # Next, we'll read a character.<<,                  # Move to cell #3 and input a character into it (temp).  [                    # If this character is non-zero (not eof):    [-<+<+>>]            # Duplicate this character into cells #1 and #2 [temp], erasing this cell, #3.<----------          # Move to cell #2 and subtract 10 (code for newline) from it.                         # The chunk is now:                         #   #0: 0                         #   #1: char (temp)                         # > #2: char - 10 (temp)                         #   #3: 0                         #   #4: 0                         #   #5: 1 (alignment)                         #####                         # Next, if char is newline (10), we'll set cell #1 to 0.    [[-]->]              # If cell #2 (char - 10) is non-zero (the char is not a newline), set this cell to 255 and move to cell #3.    -[+>-]               # Move to the first cell to the right set to 1 (cell #5), and set it to 0.                         # (The above is done without any net change to other cells.) <<<+                 # Move to cell #2 and add one to it; this cell is now 1 if char is a newline, or 0 otherwise.    [-<[-]>]             # If cell #2 is 1, set it back to 0, move to cell #1, clear it, and move back to cell #2.                         # From now on, char will be 0 if the character was a newline.                         # The chunk is now:                         #   #0: 0                         #   #1: char (temp)                         # > #2: 0                         #   #3: 0                         #   #4: 0                         #   #5: 0                         #####                         # Next we'll inialize cell #0 [char] and put a copy of char into cell #2.<                    # Move to cell #1.    [-<+>>+<]            # Duplicate this value to cells #0 and #2, clearing this cell.                         # The chunk is now:                         #   #0: [char]                         # > #1: 0                         #   #2: char (temp)                         #   #3: 0                         #   #4: 0                         #   #5: 0                         #####                         # Next, we'll initialize cell #1 [kind] to a number denoting the 'kind' of the char is.                         # The kind is an index from 0 to 8 matching the order "N+-<>[],." (where N stands for any no-op).                         # The kind could be determined by directly checking if the character matches an operation (char == op).                         # In BF, however, it's easier to check if something is not equal to X than if it is equal to X.                         # We'll start cell #1 [kind] at the sum of the kind, 36 (1+2+3+4+5+6+7+8).                         # For each operation, we'll check if the character does not match that operation, (char != op).                         # To do so, we will use cell #2 for checking whether (char - op) is non-zero.                         # If the character does not match that operation, we will subtract its kind from the sum.                         # After doing so for all of the operations, the result will be the kind of the operation that the character matches.                         # We'll do this not in kind order, but in op order: "+" (43), then "," (44), then "-" (45), etc.                         #####>>++++++[-<<++++++>>]    # Set cell #1 to 36 and move to cell #3.>+                       # Set cell #4 to 1 to be used for alignment.                         # The chunk is now:                         #   #0: [char]                         #   #1: 36 (sum of all kinds)                         #   #2: char (char - 0)                         #   #3: 0                         # > #4: 1 (alignment)                         #   #5: 0                         #####                         # The next set of lines are based on using the following pattern for each of the operations:                           # Subtract from cell #2 the difference between the char codes for this operation and the previous.                           # If cell #2 is not zero (if char != op):                             # Remove this kind from the sum by subtracting from cell #1 (kind_sum) the kind of this operation.                           # Return to cell #2 (using cell #4 for alignment).                           # The resulting chunk will be:                           #   #0: [char]                           #   #1: kind_sum                           # > #2: char (char - op)                           #   #3: 0                           #   #4: 1 (alignment)                           #   #5: 0                         # For '+', check 43:<++++++[-<------->]<-  # Subtract 43 (6 * 7 + 1) from cell #2, the difference between '+' and 0.                           # The value in cell #2 is now char - 43.    [                      # If the cell #2 is non-zero (char != 43; the character is not '+'):<->>                   # Subtract from cell #1 the kind of '+', 1.    ]                      # End if.>[<]<                  # Return to cell #2 (using cell #4 for alignment).                         # (For the remaining operations, the pattern will be shown in a condensed form.)                         # For ',', check 44:     -                      # Subtract 1 (the difference between ',' and '+').    [<------->>]>[<]<      # If not zero, remove 7 from kind_sum. Return to cell #2.                         # For '-', check 45:    -                      # Subtract 1.    [<-->>]>[<]<           # If not zero, remove 2 from kind_sum. Return to cell #2.                         # For '.', check 46:     -                      # Subtract 1.    [<-------->>]>[<]<     # If not zero, remove 8 from kind_sum. Return to cell #2.                         # For '<', check 60:    --------------         # Subtract 14.    [<--->>]>[<]<          # If not zero, remove 3 from kind_sum. Return to cell #2.                         # For '>', check 62:    --                     # Subtract 2.    [<---->>]>[<]<         # If not zero, remove 4 from kind_sum. Return to cell #2.                         # For '[', check 91>+++++[-<------>]<+    # Subtract 29 (5 * 6 - 1).    [<----->>]>[<]<        # If not zero, remove 5 from kind_sum. Return to cell #2.                         # For ']', check 93    --                     # Subtract 2.    [<------>>]>[<]<       # If not zero, remove 6 from kind_sum. Return to cell #2.                         #    [-]                  # Set cell #2 to zero.                         # The chunk is now:                         #   #0: [char]                         #   #1: [kind]                         # > #2: 0                         #   #3: 0                         #   #4: 1 (temp)                         #   #5: 0                         #####                         # Next, cell #4 [noop] will be initialized to 0 if char is not a no-op.<<+                  # Move to cell #0, adding 1 (temporarily, for alignment)>                    # Move to cell #1.    [>>>-]               # If cell #1 [kind] is non-zero (the char is not a no-op),                         #  move to cell #4 [noop] and subtract 1 to set it to 0.<[>>>]               # Move to cell #3, using cell #0 for realignment.<<<-                 # Move to cell #0 and subtract 1 from it, returning it to char.                         # The chunk is now:                         # > #0: [char]                         #   #1: [kind]                         #   #2: 0                         #   #3: 0                         #   #4: [noop] (1 if no-op, 0 otherwise)                         #   #5: 0                         #####                         # Next, cell #2 [mark] will be initialized to 1 (unmarked) and cell #3 [space] will be initialized to 32 (code for space).>>++++[->++++++++<]  # Move to cell #2 and set cell #3 to 32 (4 * 8)+                    # Set #2 [mark] to 1 (unmarked)>>>+                 # Move to cell #5 and set it to 1 (temporarily, so the input loop continues; this will be unset in the next iteration).>                    # Move to cell #0 of the next chunk.  ]                    # End "if non-eof".<                  # If at cell #0 of the next chunk (from the preceding branch for processing a source chunk), move to cell #5 of the current chunk;                     #   otherwise (at cell #3 after the source chunks), move to cell #2.]                    # End input loop.                     # Every non-header non-eof chunk is now:                     #   #0: [char]  (0 if separator)                     #   #1: [kind]  (which operator the char is)                     #   #2: [mark]  (currently 1; this will occasionally be temporarily set to 0 as a "bookmark")                     #   #3: 32      (the code for space, used for output)                     #   #4: [noop]  (1 if no-op source chunk or non-primary separator, 0 otherwise)                     #   #5: [done]  (currently 0, undone)                     # Since the loop ended, it reached eof, and the head is at cell #2 of the eof chunk.################# Output Phase #################                     #####                     # First, we will return to the header chunks.+                    # Set cell #2 to 1 (used later to save a byte).[<<<<<<]             # Move left one chunk at a time until cell #2 is zero, reaching the second header chunk.>>>>                 # Move to cell #0 of the first source chunk.[.>>>>>>]            # While cell #0 is non-zero, print it and move to the next chunk                      #  (this writes the first line of the explanation, the code verbatim).                     # Now, the head is at cell #0 of the primary separator chunk.>>>>-<<<<            # Set its cell #4 to 0 (used later to break a loop).<<<<<<[<<<<<<]       # Move to the next 0-char chunk to the left (this is the second header chunk).<<<<<.               # Move to cell #1 of the first header chunk (newline) and output it (ending the first line).                     #####                     # As we go through each line of the explanation, we'll follow this psuedo-code:                     #   - Output a space for every source character marked as 'done'                     #   - Output the characters for the active operations (multiple if it's a chain of no-ops, only one otherwise)                     #   - Mark all active operations 'done'                     #   - Output a space for every source character after the active ones                     #   - Output a space followed by the associated explanation                     # These steps will be expanded on later.>>>                  # Move to cell #4 of the first header chunk.+                    # Set it to one to start the loop.[                    # Begin a loop to print subsequent lines:                       #####                       # First, we will find the first not done chunk.  -                    # Set this cell, #4 of the first header chunk, back to 0.>>>>>>>>>>>>>        # Move to cell #5 [done] of the first source chunk.  [>>>>>>]             # Go to the first not done cell to the right (it might be the current chunk).<<<<<                # Go to cell #0 [char].  [                    # If it's non-zero (if it's a source chunk):                         #####                         # Next, we will output the leading spaces and the first active character.<[<<.<<<<]           # Go to cell #5 of the first not done chunk to the left                         #  (this is the second header chunk), outputting spaces along the way.>>>>>>               # Go to cell #5 [done] of the next chunk.    [>>>>>>]             # Move to cell #5 of the first non-done cell to the right.<<<<<.               # Move to cell #0 [char] of this chunk (the first active source chunk) and output it.>>>>                 # Move to cell #4 [noop] of this chunk.                         #####                         # Next, the following branch will process a sequence of no-op chunks.                          # This is handled differently from non-no-op chunks because sequential no-op characters are printed in one line.    [                    # If it's non-zero (if this character is a no-op):>+                   # Set cell #5 [done].                           #####                           # First, we will output each subsequent active character and mark their chunks as done.>>>>>                # Move to cell #4 [noop] of the next chunk.      [                    # While the current chunk is a no-op:<<<<.                # Move to cell #0 [char] and output it.>>>>>+               # Move to cell #5 [done] and set it to 1.>>>>>                # Move to cell #4 [noop] of the next chunk.                             # (Note that the primary separator has cell #4 [noop] set to 0 in order to break this loop)      ]                    # End loop. Now the head is at the cell #4 [noop] of the first non-no-op chunk.                           #####                           # Next, we will output the trailing spaces and the no-op explanation.<<<<[>>>.>>>]        # Move to the primary separator chunk, outputting a space for each skipped chunk.>>>.>>>              # Move to cell #0 of the first chunk of the first explanation, outputting a space.      [.>>>>>>]            # Move to the next non-explanation chunk, outputting each character.>>                   # Move to cell #2 [mark].      [<<<<<<]             # Move to cell #2 of the second header chunk.<                    # Move to cell #1.    ]                    # End if.<<<                  # If at cell #1 of the second header chunk (from the preceding branch for processing a no-op), move to cell #4 of the first header chunk;                         #  otherwise, (still at the cell $4 [noop] of the current source chunk), move to cell #1 [kind].                         #####                         # Next, the following branch will process a non-no-op chunk.                         # This is handled separately from no-op chunks because operators are printed on separate lines.    [                    # If cell #1 [kind] is non-zero (if in a non-no-op source chunk):                           #####                           # First, we will output the trailing spaces and find the primary separator chunk.>-                   # Move to cell #2 [mark] and set it to 0 (this marks the cell to return to later).>>>+                 # Move to cell #5 [done] and set it to 1.>[>>>.>>>]           # Move to the primary separator chunk, outputting a space for each skipped chunk.>>-                  # Mark this chunk (the primary separator chunk).<<<<<.<              # Move to cell #2 [mark] of the previous chunk, outputting a space.      [<<<<<<]             # Move to the first marked (mark == 0) cell to the left (the active source chunk).<                    # Move to cell #1 [kind].                           #####                           # Next, we'll locate the correct explanation.                           # We're at cell #2 [kind], which acts as an index to the explanation.                           # The primary separator (which precedes the 0th (no-op) explanation) is marked.      [-                   # Continually decrement the kind:                             # Each iteration will unmark the currently marked separator and mark the next separator.>>>>>>>[>>>>>>]      # Move to the first marked cell to the right.+                    # Unmark this chunk.>>>>                 # Move to cell #0 of the next chunk.        [>>>>>>]             # Move through the explanation chunks to the next separator chunk.>>-                  # Mark this separator chunk.<<<<<<[<<<<<<]<      # Return cell #1 [kind] of the active source chunk (as it's marked).      ]                    # End loop.                           #####                           # Next, we will output the explanation we have located.>+                   # Unmark the current source chunk.      [>>>>>>]             # Move to the marked separator chunk.+                    # Unmark this chunk.>>>>                 # Move to cell #0 [char] of the next chunk.      [.>>>>>>]            # While cell #0 [char] is non-zero, output it and move to the next chunk.>>[<<<<<<]           # Return to the second header chunk.<<<<                 # Move to cell #4 of the first header chunk.    ]                    # End if.                         # From either branch, we are now at cell #4 of the first header chunk.+                    # Set cell #4 to 1 (to continue the loop).<<<.                 # Move to cell #1 (newline) and output it.>>                   # Move to cell #3 (which is 0).  ]                    # End if.>                    # If at the first header chunk (from the preceding branch for processing a source chunk), move to cell #4 (which is 1, continuing the loop);                       #  otherwise (still at the primary separator), move to cell #1 (which is 0, ending the loop).]                    # End loop.###### Lastly, we will print the final code fence.>[<<<<<<]            # Move to cell #2 of the second header chunk.<<<<<<...            # Move to the first header chunk and output the backtick thrice.###### Finally, we can enjoy the fruits of our efforts: https://pastebin.com/raw/C9yYHx3F.

Viewing latest article 6
Browse Latest Browse All 17

Trending Articles