Unminify a Pythlike String

Pyth is perhaps the most successful general-purpose golfing language. Though it is somewhat in decline as a result of newer languages, from 2014 to 2016 Pyth's concise syntax, constant updates, overloading, and (for its era) many builtins made it a favorite for the majority of questions.

Pyth code is often difficult to read. Even the output of the debug mode (transpiled Python) often consists of a long line, sometimes with parentheses nested ten deep. However, correctly formatted Pyth is very readable.

Here's a piece of Pyth code, written by @isaacg in Play the Word Chain.

.MlZfqhMtTeMPT+Lzs.pMyQ


It's much more readable like this.

.M                     Filter by gives-maximal-value of
l Z                   lambda Z:length(Z) over
f                     filter by (lambda T:
q                     equal
hM t T                head-map tail T
eM P T                end-map Pop T)
+L                    Append z to each element in
z
s .pM y Q            flattened permutations of each subset of Q


For this challenge, we eliminate the aspect of categorizing Pyth characters and focus on formatting. Instead of being Pyth code, the input will consist of characters in 0123456789M. The digit n represents a function of arity n, and M represents an operator. For example, the above code is represented as 210221M101M102M011M10. Here are the steps to unminify:

Separate the string into tokens.

A token matches [0-9]M*. 0M will not occur in input.

When there are not enough arguments, Pyth appends as many implicit variables (lambda variables or Qs) to the code as are necessary to fill the arguments of the program; these should be represented by 0s.

Group tokens into lines.

The arity of a token is the value of its digit.

• An arity-0 token (i.e. a 0) ends a line.

• For an arity-1 token the next token should go on the same line, separated by a space.

• For an arity >=2 token, its arguments go on separate lines, in the order they appear in the code, each followed by their own subarguments and so on. Arguments to a token are indented to the end of that token plus one space.

Input

A nonempty string (or char array, array of length-1 strings, etc. as allowed by Standard I/O Methods) consisting of 0123456789M, which will not contain the substring 0M.

Output

The string formatted according to the above rules.

Test cases

210221M101M102M011M10

2
1 0
2
2
1M 1 0
1M 1 0
2M
0
1 1M 1 0

123M4M

1 2
3M
4M
0
0
0
0
0
0
0

2MM

2MM
0
0

11011100

1 1 0
1 1 1 0
0

9000000

9
0
0
0
0
0
0
0
0
0

• Related question: codegolf.stackexchange.com/questions/47798/… – lirtosiast Nov 16 '18 at 17:54
• Can I take the input as an array of digits/string? Example 210221M101M102M011M10 would be [2,1,0,2,2,1,'M',1,0,1,'M',1,0,2,'M',0,1,1,'M',1,0] – Luis felipe De jesus Munoz Nov 16 '18 at 18:29
• @LuisfelipeDejesusMunoz No, unless standard I/O rules require that you be allowed to (which I don't think they do.) IMO it would slightly change the challenge if the Ms are allowed to be a different datatype from the integers. – lirtosiast Nov 16 '18 at 18:33
• @lirtosiast So an array of characters/single-character strings is fine, just not using different datatypes between digits and M? – Kamil Drakari Nov 16 '18 at 19:54
• @LeakyNun Empty string is now undefined behavior. – lirtosiast Nov 17 '18 at 19:29

JavaScript (ES8), 160 159 bytes

f=(s,a=[d=0,p=[1]])=>s.replace(/(.)M*/g,(s,c)=>(g=_=>a[d]?s+(P=p[d]-=c--&&~s.length,c?


Try it online!

Commented

f = (                          // f = recursive function taking:
s,                           //   s   = input string
a = [                        //   a[] = array holding the number of expected arguments
d = 0,                     //   d   = current depth, initialized to 0
p = [1]                    //   p[] = array holding the padding values
]                            //
) =>                           //
s.replace(                   // search in s all substrings
RegExp('(.)M*', 'g'),      // consisting of a digit followed by 0 to N 'M' characters
(s, c) =>                  // for each substring s beginning with the digit c:
( g = _ =>               //   g = recursive function
a[d] ?               //     if we're still expecting at least one argument at
//     this depth:
s + (              //       append s
P = p[d] -=      //       update the padding value P = p[d] for this depth:
c-- &&         //         decrement c; unless c was equal to 0,
~s.length,     //         add the length of s + 1 to p[d]
c ?              //       if c is not equal to 0 (i.e. was not equal to 1):
\n.padEnd(P) //         append a linefeed followed by P - 1 spaces
:                //       else:
' '            //         append a single space
)                  //
:                    //     else (all arguments have been processed):
g(d--)             //       decrement the depth and call g again
)(                       //   before the initial call to g:
a[d]--,                //     decrement the number of arguments at depth d
a[++d] = +c,           //     set the number of arguments for the next depth
p[d] = p[d - 1]        //     set the padding value for the next depth,
)                        //     using a copy of the previous depth
) + (                        // end of replace()
d ?                        // if we're not back at depth 0:
f('0', a)                //   do a recursive call to f with an extra '0'
:                          // else:
''                       //   stop recursion
)                            //


unlines.snd.f
f(n:r)|(m,t)<-span(>'9')r,(s,l)<-n#t=(s,n?((n:m):map((' '<$(n:n:m))++)l)) f e=(e,["0"]) '1'?(a:b:r)=(a++drop(length a)b):r _?s=s '0'#s=(s,[]) n#s|(r,l)<-f s=(l++)<$>pred n#r


Try it online!

There has to be a better way to handle the arity-1 case, it currently takes up 45 bytes.

Edits:

• -2 bytes by switching to a different method of handling 1, though the previous method has probably more optimization potential.
• -3 bytes by not converting the character digits to numbers and using pred instead of n-1.
unlines.snd.f
f(n:r)|(m,t)<-span(>'9')r,(s,l)<-read[n]#t,w<-map((' '<$(n:n:m))++)=(s,last$((n:m):w l):[(n:m++' ':h):w t|n<'2',h:t<-[l]])
f e=(e,["0"])
0#s=(s,[])
n#s|(r,l)<-f s=(l++)<\$>(n-1)#r


Try it online!

Charcoal, 75 bytes

ＦＳ⊞υ⎇⁼ιM⁺⊟υιι≔⮌υυ≔⟦⟧θ≔⟦⟧ηＷ∨υη«≔⎇υ⊟υ0ιι¿⊖Σι↘→⊞θι⊞ηΣιＷ∧η¬§η±¹«⊟ηＭ⊕Ｌ⊟θ←¿η⊞η⊖⊟η


Try it online! Link is to verbose version of code. Explanation:

ＦＳ⊞υ⎇⁼ιM⁺⊟υιι


Loop over the input characters and turn them into a list of digits with optional M suffixes.

≔⮌υυ


Reverse this list so that we can use Pop to consume it.

≔⟦⟧θ


This variable is a stack of tokens whose arity has not yet been fulfilled.

≔⟦⟧η


This variable is a stack of the remaining arity of the unfulfilled tokens.

Ｗ∨υη«


Repeat until we have consumed all of the tokens and emptied the stack.

     ≔⎇υ⊟υ0ι


Get the next token or 0 if none.

     ι¿⊖Σι↘→


Print the token and then move the cursor horizontally if it begins with 1 otherwise diagonally.

     ⊞θι⊞ηΣι


Add the token and its arity to the appropriate variables.

     Ｗ∧η¬§η±¹«


Repeat while the arity stack is nonempty but the top arity is zero.

              ⊟η


              Ｍ⊕Ｌ⊟θ←

              ¿η⊞η⊖⊟η