Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

Background

(Based on a true, heart-wrenching story)

In my time, I've played around with Lisp and similar languages often. I've written with them, ran them, interpreted them, designed them, and made machines write with them for me... And if there is one thing that bothers me, it's seeing Lisp that does not comply with my specific formatting style.

Unfortunately, some text editors (cough XCode cough) tend to strip my beautiful tabs and spaces whenever code is copied and pasted... Take this beautifully spaced Lisp-like syntax:

(A
    (B
        (C)
        (D))
    (E))

(Where ABCDE are arbitrary functions)

SOME text editors butcher this lovely code to the following end:

(A
(B
(C)
(D))
(E))

What a mess! That's not readable!

Help me out, here?

The Challenge

Your goal in this challenge is to take a series of functions separated by newlines in a format described below and return a more beautiful arrangement that highlights readability and elegance.

The Input

We define a function F of arity N arguments as a construct similar to the following:

(F (G1 ...) (G2 ...) (G3 ...) ... (GN ...))

where G1, G2, ..., GN are all functions in and of themselves. An arity 0 function A is simply (A), while an arity 2 function B is of the form (B (...) (...))

Your code should take input as a series of functions with a single newline before every function's leading parenthesis (except for the first function). The example above is valid input.

You may assume:

  • The parentheses are balanced.
  • EVERY function is surrounded by parentheses: ()
  • A function's name will only contain printable ASCII characters.
  • A function's name will never contain parentheses or spaces.
  • A function's name is followed by a maximum of two trailing spaces.
  • There is an optional trailing newline on input.

The Output

Your code should output the same set of functions, where the only changes made are the additions of spaces or tabs before the leading parentheses of functions. Output should comply with the following rules:

  • The first function (and later top-level functions) given should have no preceding spaces
  • An argument to a function's horizontal location is exactly one tab to the right of that function's horizontal location.
  • A tab is implementation-defined, but must be at least 3 spaces.

Rules

Examples

Input:

(A
(B
(C)
(D))
(E))

Output:

(A
    (B
        (C)
        (D))
    (E))

Input:

(!@#$%^&*
(asdfghjklm
(this_string_is_particularly_long
(...))
(123456789)))
(THIS_IS_TOP_LEVEL_AGAIN
(HERE'S_AN_ARGUMENT))

Output:

(!@#$%^&*
    (asdfghjklm
        (this_string_is_particularly_long
            (...))
        (123456789)))
(THIS_IS_TOP_LEVEL_AGAIN
    (HERE'S_AN_ARGUMENT))

Input:

(-:0
(*:0
(%:0
(Arg:6)
(Write:0
(Read:0
(Arg:30))
(Write:0
(Const:-6)
(Arg:10))))
(%:0
(Const:9)
(/:0
(Const:-13)
(%:0
(Arg:14)
(Arg:0)))))
(WriteArg:22
(-:0
(Const:45)
(?:0
(Arg:3)
(Arg:22)
(Arg:0)))))

Output:

(-:0
    (*:0
        (%:0
            (Arg:6)
            (Write:0
                (Read:0
                    (Arg:30))
                (Write:0
                    (Const:-6)
                    (Arg:10))))
        (%:0
            (Const:9)
            (/:0
                (Const:-13)
                (%:0
                    (Arg:14)
                    (Arg:0)))))
    (WriteArg:22
        (-:0
            (Const:45)
            (?:0
                (Arg:3)
                (Arg:22)
                (Arg:0)))))
share|improve this question
    
Congrats on making the Hot Network Questions list! :D –  Alex A. 11 hours ago
    
@AlexA. Hooray! My dreams have been realized. :D –  BrainSteel 10 hours ago

5 Answers 5

Pyth, 24 20 19 bytes

FN.z+*Z*4dN~Z-1/N\)

Increments a counter for every line, counts total number of closing parentheses encountered so far, and subtracts it from the counter. Then we indent by 4*counter spaces.

share|improve this answer
    
@Downvoter Care to explain? –  orlp 26 mins ago

C: 95 94 characters

It's not very golfed yet, and from the question I'm unsure whether tabs are acceptable, which is what I use here.

i,j;main(c){for(;putchar(c=getchar()),c+1;i+=c==40,i-=c==41)if(c==10)for(j=i;j--;putchar(9));}

Ungolfed:

i,j;
main(c){
  for(
    ;
    putchar(c=getchar()),
    c+1;
    i+=c==40,
    i-=c==41
  )
    if(c==10)
      for(
        j=i;
        j--;
        putchar(9)
      );
}

Edit: Made it so that it quits on EOF.

share|improve this answer
    
Tabs are perfectly acceptable. –  BrainSteel 12 hours ago
    
Could you use if(c<11) instead of if(c==10)? –  Digital Trauma 4 hours ago

Julia, 103 99 97 bytes

p->(i=j=0;for l in split(p,"\n") i+=1;println("\t"^abs(i-j-1)*l);j+=length(matchall(r"\)",l))end)

This defines an unnamed function that accepts a string and prints the indented version. To call it, give it a name, e.g. f=p->.... Note that the input must be a valid Julia string, so dollar signs ($) must be escaped.

Ungolfed + explanation:

function f(p)
    # Set counters for the line number and the number of close parens
    i = j = 0

    # Loop over each line of the program
    for l in split(p, "\n")
        # Increment the line number
        i += 1

        # Print the program line with |i-j-1| tabs
        println("\t"^abs(i-j-1) * l)

        # Count the number of close parens on this line
        j += length(matchall(r"\)", l))
    end
end

Example, pretending each set of four spaces is a tab:

julia> f("(A
(B
(C)
(D))
(E))")

(A
    (B
        (C)
        (D))
    (E))

This is much longer than I expected it to be, likely because Julia doesn't have a convenient way of counting the occurrences of a character in a string, hence the use of a regex. Any suggestions are more than welcome!

share|improve this answer

Retina, 89 83 bytes

s`.+
$0<tab>$0
s`(?<=<tab>.*).
<tab>
+ms`^((\()|(?<-2>\))|[^)])+^(?=\(.*^((?<-2><tab>)+))
$0$3
<tab>+$
<empty>

Where <tab> stands for an actual tab character (0x09) and <empty> stands for an empty line. After making those replacements, you can run the above code with the -s flag. However, I'm not counting that flag, because you could also just put each line in its own source file, in which case the 7 newlines would be replaced by 7 penalty bytes for the additional source files.

This is a full program, taking input on STDIN and printing the result to STDOUT.

Explanation

Every pair of lines defines a regex substitution. The basic idea is to make use of .NET's balancing groups to count the current depth up to a given (, and then insert that many tabs before that (.

s`.+
$0<tab>$0

First, we prepare the input. We can't really write back a conditional number of tabs, if we can't find them somewhere in the input string to capture them. So we start by duplicating the entire input, separated by a tab. Note that the s` just activates the single-line (or "dot-all") modifier, which ensures that the . also matches newlines.

s`(?<=<tab>.*).
<tab>

Now we turn every character after that tab into a tab as well. This gives us a sufficient amount of tabs at the end of the string, without modifying the original string so far.

+ms`^((\()|(?<-2>\))|[^)])+^(?=\(.*^((?<-2><tab>)+))
$0$3

This is the meat of the solution. The m and s activate multi-line mode (so that ^ matches the beginnings of lines) and single-line mode. The + tells Retina to keep repeating this substitution until the output stops changing (in this case, that means until the pattern no longer matches the string).

The pattern itself matches a prefix of the input up to an unprocessed ( (that is, a ( that doesn't have any tabs before it, but should). At the same time it determines the depth of the prefix with balancing groups, such that the height of stack 2 will correspond to the current depth, and therefore to number of tabs we need to append. That is this part:

((\()|(?<-2>\))|[^)])+

It either matches a (, pushing it onto the 2 stack, or it matches a ), popping the last capturing from the 2 stack, or it matches something else and leaves the stack untouched. Since the parentheses are guaranteed to be balanced we don't need to worry about trying to pop from an empty stack.

After we've gone through the string like this and found an unprocessed ( to stop at, the lookahead then skips ahead to the end of the string, and captures tabs into group 3 while popping from the 2 stack until its empty:

(?=\(.*^((?<-2><tab>)+))

By using a + in there, we ensure that the pattern only matches anything if at least one tab should be inserted into the match - this avoids an infinite loop when there are multiple root-level functions.

<tab>+$
<empty>

Lastly, we just get rid of those helper tabs at the end of the string to clean up the result.

share|improve this answer
    
This is very cool. Well done! It's always a pleasure to see Retina. –  BrainSteel 10 hours ago

Perl, 41

$_="\t"x($i-$j).$_;$i+=y/(/(/;$j+=y/)/)/

40 characters +1 for -p.

Run with:

cat input.txt | perl -pe'$_="\t"x($i-$j).$_;$i+=y/(/(/;$j+=y/)/)/'
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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