16
\$\begingroup\$

Given an input of a string representing a function definition, output the string with newlines and spaces inserted so that the function's arguments are newline-separated and aligned.

The input string will follow the following pattern:

  • First, it will start with a prefix, which is always at least one character long and does not contain any of the characters ,().

  • An open parenthesis (() will then mark the beginning of the argument list.

  • A list of zero or more arguments will then follow. These are separated by the string ", " (a comma and then a space). None of the arguments will contain any of the characters ,().

  • A close parenthesis ()) will mark the end of the argument list.

  • Lastly, a postfix may be found, which is zero or more characters long and may contain the characters ,().

The input string will consist solely of printable ASCII (which means it will never contain a newline).

The output must be:

  • The prefix, copied down verbatim, and the open parenthesis.

  • The argument list, this time separated not by ", " but by a comma, newline, and as many spaces as is needed to vertically align the first character of each argument.

  • The close paren and postfix (if it exists) verbatim.

Since this is , the shortest code in bytes will win.

Test cases (format: single-line input followed by output followed by double newline):

def foo(bar, baz, quux):
def foo(bar,
        baz,
        quux):

int main() {
int main() {

fn f(a: i32, b: f64, c: String) -> (String, Vec<i32>) {
fn f(a: i32,
     b: f64,
     c: String) -> (String, Vec<i32>) {

function g(h) {
function g(h) {

def abc(def, ghi, jkl, mno)
def abc(def,
        ghi,
        jkl,
        mno)

x y z(x, y, z) x, y, z)
x y z(x,
      y,
      z) x, y, z)
\$\endgroup\$
0

10 Answers 10

7
\$\begingroup\$

Haskell, 115 bytes

import Data.Lists
f x|(a,b:c)<-span(/='(')x,(d,e)<-span(/=')')c=a++b:intercalate(",\n "++(a>>" "))(splitOn", "d)++e

Usage example:

*Main> putStrLn $ f "fn f(a: i32, b: f64, c: String) -> (String, Vec<i32>) {"
fn f(a: i32,
     b: f64,
     c: String) -> (String, Vec<i32>) {

How it works:

bind
  a: everything before the first (
  b: the first (
  c: everything after the first (
  d: everything of c before the first )
  e: everything of c from the first ) to the end

construct the output string by concatenating
  a
  b
  splitting d at the argument separator ", " and rejoining it with ",\n " followed by (length a) spaces    
  e
\$\endgroup\$
1
  • \$\begingroup\$ a>>" " is really clever... \$\endgroup\$
    – fmt
    Commented Feb 8, 2016 at 23:10
4
\$\begingroup\$

Japt, 23 bytes

¡Y?X:Xr',",
"+SpUb'(}')

Test it online!

How it works

               // Implicit: U = input string
¡        }')   // Map each item X and index Y in U.split(")") to:
Y?X            //  If Y is non-zero, X. This keeps e.g. "(String, Vec<i32>)" from being parsed.
:Xr',",\n"+    //  Otherwise, X with each comma replaced with ",\n" concatenated with
SpUb'(         //  U.indexOf("(") spaces.
               // Implicit: re-join with ")", output
\$\endgroup\$
3
\$\begingroup\$

Perl, 62 52 + 2 = 54 bytes

s/\(.*?\)/$a=$"x length$`;$&=~s|(?<=,)[^,]+|\n$a$&|gr/e

Requires the -p flag:

$ echo "x y z(x, y, z) x, y, z)
fn f(a: i32, b: f64, c: String) -> (String, Vec<i32>) {" | \
perl -pe's/\(.*?\)/$a=$"x length$`;$&=~s|(?<=,)[^,]+|\n$a$&|gr/e'
x y z(x,
      y,
      z) x, y, z)
fn f(a: i32,
     b: f64,
     c: String) -> (String, Vec<i32>) {

How it works:

# '-p' reads first line into $_ and will also auto print at the end
s/\(.*?\)/             # Match (...) and replace with the below
  $a=$"x length$`;     # $` contains all the content before the matched string
                       # And $" contains a literal space 
  $&=~s|               # Replace in previous match
    (?<=,)[^,]+        # Check for a , before the the string to match
                       # This will match ' b: f64', ' c: String'
  |\n$a$&|gr/e         # Replace with \n, [:spaces:] and all the matched text
\$\endgroup\$
3
\$\begingroup\$

Retina, 31 bytes

(?<=^([^(])*\([^)]*,) 
¶ $#1$* 

Note the spaces at the end of both lines.

We replace every space which has the regex ^([^(])*\([^)]*, before it. The replacing string will be a newline, and the number of the captures with ([^(])* plus one spaces.

A more coherent explanation comes later.

Try it online here.

\$\endgroup\$
3
\$\begingroup\$

ES6, 68 67 bytes

s=>s.replace(/\(.*?\)/,(s,n)=>s.replace/, /g, `,
 `+` `.repeat(n)))

This works by extracting the argument list from the original string, and replacing each argument separator with indentation calculated from the position of the argument list within the original string.

Edit: Saved 1 byte thanks to @ETHproductions.

\$\endgroup\$
1
  • \$\begingroup\$ I was wondering why you did .split`, `.join(...) instead of .replace(...). Turns out the other is a byte shorter: s=>s.replace(/\(.*?\)/,(s,n)=>s.replace(/, /g,`,\n `+` `.repeat(n))) \$\endgroup\$ Commented Feb 9, 2016 at 16:37
2
\$\begingroup\$

Pyth, 35 30 bytes

+j++\,b*dhxz\(c<zKhxz\)", ">zK

Try it here!

Explanation:

+j++\,b*dhxz\(c<zKhxz\)", ">zK    # z = input()

                 Khxz\)           # Get index of the first ")"
               <z                 # Take the string until there...
              c        ", "       # ...and split it on the arguments
 j                                # Join the splitted string on...
  ++                              # ...the concatenation of...
    \,b                           # ...a comma followed by a newline...
       *dhxz\(                    # ...followed by the right amount of spaces = index of the first "(" + 1
+                         >zK     # Concat the resulting string with the postfix
\$\endgroup\$
2
\$\begingroup\$

Groovy, 137 89 95 bytes

Groovy is not the "Right Tool for the Job"™. Edit: It works just fine when you have someone with a brain using it...

f={s=(it+' ').split(/\0/)
s[0].replace(',',',\n'+(' '*it.indexOf('(')))+')'+s[1..-1].join(')')}

Tests:

println f("def foo(bar, baz, quux):")
println f("int main() {")
println f("fn f(a: i32, b: f64, c: String) -> (String, Vec<i32>) {")
println f("function g(h) {")
println f("def abc(def, ghi, jkl, mno)")
println f("x y z(x, y, z) x, y, z)")

Somewhat ungolfed:

f = {String it ->
    def str = (it + ' ').split(/\)/)
    return (str[0].replace (',', ',\n'+(' ' * it.indexOf('('))) + ')' + str[1])
}
\$\endgroup\$
1
\$\begingroup\$

Retina, 47 bytes

Byte count assumes ISO 8859-1 encoding.

m+`^(([^(]+.)[^,)]+,) (.+)
$1¶$2$3
T`p` `¶.+?\(

Try it online!

\$\endgroup\$
1
\$\begingroup\$

JavaScript (ES6), 85

s=>s.replace(/^.*?\(|[^),]+, |.+/g,(x,p)=>[a+x,a=a||(p?`
`+' '.repeat(p):a)][0],a='')

Test

f=s=>s.replace(/^.*?\(|[^),]+, |.+/g,(x,p)=>[a+x,a=a||(p?`
`+' '.repeat(p):a)][0],a='')

console.log=x=>O.textContent+=x+'\n'

;['def foo(bar, baz, quux):',
  'int main() {',
  'fn f(a: i32, b: f64, c: String) -> (String, Vec<i32>) {',
  'function g(h) {',
  'def abc(def, ghi, jkl, mno)',
  'x y z(x, y, z) x, y, z)']
.forEach(t=>console.log(t+'\n'+f(t)+'\n'))
<pre id=O></pre>

\$\endgroup\$
2
  • \$\begingroup\$ I'm sorry, I was mistaking, was running the code in my console and the output was something like this: "x y z(x as you can see the " thats why I was thinking it was one space off. Hence the deletion \$\endgroup\$ Commented Feb 8, 2016 at 22:44
  • \$\begingroup\$ @dev-null that happens to me all the time. \$\endgroup\$
    – edc65
    Commented Feb 8, 2016 at 22:48
0
\$\begingroup\$

Jelly, 39 bytes

ṣ”)Ḣṣ”(Ṫ©œṣ⁾, ji”(⁶ẋƊ⁾,¶;ƊḢ,j®${jʋ@œṣ®$

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.