GNU sed, 178 158 132 + 1 = 133 bytes
+1 byte for -r
flag.
s/(.)(.*)(.)/ \1\n\2;\3/
:
s/( *)(.\n.)(.*)(...);(.*)(.)/\1\2\1 \6\n\3;\4\5/m
t
:A
s/(.*\n)( *)(.*);/ \2;\1\2\3/m
tA
s/. (.)$/\1/gm
Try it online!
Explanation
In previous revisions I used a lot of bytes dealing with math, special cases, and cleanup, even though intuitively I was sure they could be avoided. I've since managed to do so, mostly.
Suppose we have the input abcdEFGHIJKLMnop
. The letters EFGHIJKLM
will be the bottom of the triangle, so I've capitalized them as a visual aid.
First we prepare the input by putting the first character on its own line (preceded by a space) and inserting a cursor (;
) before the last character:
s/(.)(.*)(.)/ \1\n\2;\3/
Now we have:
a
bcdEFGHIJKLMno;p
Now, in a loop, we're going to do a few things to the last line: 1. Copy the spaces from the previous line and insert them after the first character, plus two; 2. Move the last character to right after the spaces, followed by a newline; and 3. Move the cursor three characters to the left.
:
s/( *)(.\n.)(.*)(...);(.*)(.)/\1\2\1 \6\n\3;\4\5/m
t
Here's the result of each iteration:
a
b p
cdEFGHIJKL;Mno
a
b p
c o
dEFGHI;JKLMn
a
b p
c o
d n
EF;GHIJKLM
You can see the pyramid begin to take shape. You can also see what the cursor was for: In each iteration it moved left three characters, and when there are no longer three characters to its left, it breaks the loop, which happens to be just when we've reached the "bottom" of the pyramid.
Now we're going to do a similar operation but in reverse. In a loop, we'll copy the spaces from the beginning of the line with the cursor to the beginning of the preceding line, plus one, in the process moving the cursor up to that line.
:A
s/(.*\n)( *)(.*);/ \2;\1\2\3/m
tA
Here are a couple iterations and the end result:
a
b p
c o
;d n
EFGHIJKLM
a
b p
;c o
d n
EFGHIJKLM
...
; a
b p
c o
d n
EFGHIJKLM
We're all done now, except for some extra characters: A ;
and extra space on the first line, and two spaces in the "middle" of the pyramid on the next three lines. A simple substitution gets rid of them:
s/. (.)$/\1/gm
All done!
a
b p
c o
d n
EFGHIJKLM