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.

Given the following input to the program:

  1. List of block start characters
  2. List of block end characters
  3. A string to format

format the string with the blocks delimited by the two character sets indented.

Formatting is done with two spaces per level and the parentheses are placed as shown in the example below. You may assume the sets of opening and closing characters to be disjoint.

E.g. for {[(< and }])> as the opening and closing character sets and the following string:

abc{xyz{text[note{comment(t{ex}t)abc}]}}

the following output would be expected:

abc
{
  xyz
  {
    text
    [
      note
      {
        comment
        (
          t
          {
            ex
          }
          t
        )
        abc
      }
    ]
  }
}

You may not hard-code the list of “parentheses” characters. How input is given is not specified, though; this could be either command-line arguments or via standard input, as you wish.

share|improve this question
1  
Your indented output is inconsistent. The first two braces are aligned with abc and note, but the third one is not aligned with t –  atnnn Jul 4 '11 at 22:39
    
my bad , change it now –  Prashant Bhate Jul 4 '11 at 23:14
5  
Can we assume that for each parenthesis there's a closing one, and in the same order? –  Juan Jul 5 '11 at 1:22
    
Does the program have to support any parenthesis characters given as arguments? e.g. ./program 'p' 'q' <<< '1p23p45q67q8' Or does it need only support {[(< and }])> ? –  Joey Adams Jul 5 '11 at 3:27
1  
@Phrasant Bhate: And in the output? –  Lowjacker Jul 7 '11 at 11:37

21 Answers 21

Ruby, 106 101 96 95

s,e,i=$*
i.scan(/[#{z=Regexp.quote s+e}]|[^#{z}]*/){|l|puts'  '*(s[l]?~-$.+=1:e[l]?$.-=1:$.)+l}

Input is provided via the command line.

share|improve this answer
1  
You can save 4 characters by using ~-j+=1 instead of (j+=1;j-1). Additionally, using $. everywhere instead of j allows you to remove the j=0, which saves another character. –  Ventero Jul 7 '11 at 11:37
    
@Ventero: Thanks! –  Lowjacker Jul 7 '11 at 12:36

Perl - 131 96 94 chars

$i="";for$_(split/([\Q$ARGV[0]$ARGV[1]\E])/,$ARGV[2]){$i=~s/..// if/[\Q$ARGV[1]\E]/;print "$i$_\n"if$_;$i.='  'if/[\Q$ARGV[0]\E]/;}

Seems like there should be room for eliminating common expressions, at least, but it's a quick take that handles the example, as well as Joey Adams's hypothetical about arbitrary brackets.


There was, indeed, plenty of room for improvement:

$_=pop;($s,$e)=map"[\Q$_\E]",@ARGV;for(split/($s|$e)/){print"  "x($i-=/$e/),"$_\n"if$_;$i+=/$s/}

...and still a little more:

$_=pop;($s,$e)=map"[\Q$_\E]",@ARGV;map{print"  "x($i-=/$e/),"$_\n"if$_;$i+=/$s/}split/($s|$e)/
share|improve this answer

Mathematica (non code golf)

indent[str_String]:=Module[{ind,indent,f},
ind=0;
indent[i_]:="\n"<>Nest["    "<>ToString[#]&,"",i];
f[c_] :=  (indent[ind] <> c <> indent[++ind]) /; StringMatchQ["[({",___~~c~~___];
f[c_] := ( indent[--ind] <> c <>indent[ind])  /; StringMatchQ["])}",___~~c~~___];
f[c_] := (c <>indent[ind])       /; StringMatchQ[";,",___~~c~~___];
f[c_] := c  ;
f /@ Characters@ str//StringJoin
]

Test

indent["abc{xyz{text[note{comment(t{ex}t)abc}]}}"]
abc
{
    xyz
    {
        text
        [
            note
            {
                comment
                (
                    t
                    {
                        ex
                    }
                    t
                )
                abc
            }

        ]

    }

}

As a bonus, following function can be used to format mathematica expression

format[expr_] := indent[expr // FullForm // ToString]

EDIT(non code golf) Updated with fine granular control over the way newlines are rendered

indent[str_String, ob_String, cb_String, delim_String] := 
  Module[{ind, indent, f, tab}, ind = 0; tab = "    ";
   indent[i_, tab_, nl_] := nl <> Nest[tab <> ToString[#] &, "", i];
   f[c_] := (indent[ind, "", " "] <> c <> indent[++ind, tab, "\n"]) /;StringMatchQ[ob, ___ ~~ c ~~ ___];
   f[c_] := (indent[--ind, "", " "] <> c <> indent[ind, tab, "\n"]) /;StringMatchQ[cb, ___ ~~ c ~~ ___];
   f[c_] := (c <> indent[ind, tab, "\n"]) /;StringMatchQ[delim, ___ ~~ c ~~ ___];
   f[c_] := c;
   f /@ Characters@str // StringJoin];
format[expr_] := indent[expr // InputForm // ToString, "[({", "])}", ";"];

format[Hold@Module[{ind, indent, f, tab}, ind = 0; tab = "    ";
 indent[i_, tab_, nl_] := nl <> Nest[tab <> ToString[#] &, "", i];
 f[c_] := (indent[ind, "", " "] <> c <> indent[++ind, tab, "\n"]) /;StringMatchQ[ob, ___ ~~ c ~~ ___];
 f[c_] := (indent[--ind, "", " "] <> c <> indent[ind, tab, "\n"]) /;StringMatchQ[cb, ___ ~~ c ~~ ___];
 f[c_] := (c <> indent[ind, tab, "\n"]) /;StringMatchQ[delim, ___ ~~ c ~~ ___];
 f[c_] := c;
 f /@ Characters@str // StringJoin]]

Output

Hold [
    Module [
         {
            ind, indent, f, tab }
        , ind = 0;
         tab = "    ";
         indent [
            i_, tab_, nl_ ]
         := StringJoin [
            nl, Nest [
                StringJoin [
                    tab, ToString [
                        #1 ]
                     ]
                 & , "", i ]
             ]
        ;
         f [
            c_ ]
         := StringJoin [
            indent [
                ind, "", " " ]
            , c, indent [
                ++ind, tab, "\n" ]
             ]
         /;
         StringMatchQ [
            ob, ___~~c~~___ ]
        ;
         f [
            c_ ]
         := StringJoin [
            indent [
                --ind, "", " " ]
            , c, indent [
                ind, tab, "\n" ]
             ]
         /;
         StringMatchQ [
            cb, ___~~c~~___ ]
        ;
         f [
            c_ ]
         := StringJoin [
            c, indent [
                ind, tab, "\n" ]
             ]
         /;
         StringMatchQ [
            delim, ___~~c~~___ ]
        ;
         f [
            c_ ]
         := c;
         StringJoin [
            f / @
                 Characters [
                    str ]
                 ]
             ]
         ]
share|improve this answer
    
That is hardly code golf, with multi-character names like indent. Is your goal maximally terse code, or readability? There are a number of ways to make that code shorter, if that is indeed your goal. Also: "You may not hard-code the list of “parentheses” characters." yet isn't that exactly what you did here? Anyway, sorry to sound so negative; it just strikes me as a strange answer to your own challenge. –  Mr.Wizard Nov 28 '11 at 17:13
1  
@Mr.Wizard its not code golf, I have added it for my own reference [updated to make it clear]. I frequently use it to understand unformatted mathematica code that span larger than a page –  Prashant Bhate Dec 27 '11 at 13:40

JavaScript, 255 227 205 characters

Hey, its length fits perfectly in a byte! :D

function(s,e,t){R=eval.bind(0,"Array(n).join(' ')");for(i=n=0,b=r='';c=t[i++];)~s.indexOf(c)?(r+=b,b='\n'+R(++n)+c+'\n '+R(++n)):~e.indexOf(c)?b+='\n'+((n-=2)?R()+' ':'')+c+'\n'+(n?R()+' ':''):b+=c;return r+b}

It's a function, pass it the start characters, the end characters, then the text.

share|improve this answer
    
Your own edit comment has been used against you. :D –  Doorknob Sep 30 '13 at 23:38
    
@Doorknob: I… I thought I had never done that. D: I am so sorry. (Were you hunting?) –  minitech Sep 30 '13 at 23:52
    
@Doorknob: And thanks for reminding me about this; shortened :) –  minitech Oct 1 '13 at 0:02
    
No I wasn't hunting, just stumbled upon this question, but I decided to, and I found this :O :P –  Doorknob Oct 1 '13 at 2:03

Python – 162 chars

i=f=0
s=""
l,r,z=[raw_input()for c in'   ']
o=lambda:s+("\n"+"  "*i)*f+c
for c in z:
 if c in l:f=1;s=o();i+=1
 elif c in r:i-=1;f=1;s=o()
 else:s=o();f=0
print s
share|improve this answer
    
Note that the task calls for the two sets of parentheses to be part of the input, not hardcoded. –  Joey Jul 5 '11 at 12:51
    
@Joey noted, I'll get around to fixing that in a while. Thanks –  Juan Jul 5 '11 at 13:40

Python 2.7.X - 136 chars

import sys
a,c=sys.argv,0
for i in a[3]:
 if not(i in a[2]):print ' '*c+i
 else:print ' '*(c-4)+i
 if i in a[1]:c+=4
 if i in a[2]:c-=4

Usage : $ ./foo.py '(' ')' '(ab(cd(ef)gh)ij)'

Resulting Output:

(
    a
    b
    (
        c
        d
        (
            e
            f
        )
        g
        h
    )
    i
    j
)
share|improve this answer

C (159 225 chars)

#define q(s,c)strchr(s,c)
#define p(i,j,k)printf("\n%*s%c%c%*s",i,"",*s,k,j,"")
g(char*b,char*e,char*s){int i;for(i=0;*s;s++)q(b,*s)?p(i-2,i+=2,'\n'):q(e,*s)?q(b,*(s+1))||q(e,*(s+1))?p(i-=2,i-2,0):p(i-=2,i-2,'\n'):putchar(*s);}

It cost me 66 extra characters just to fix the bug with the empty lines :( Frankly, I need a fresh approach, but I'll call it a day for now.

#define p(i,j)printf("\n%*s%c\n%*s",i,"",*s,j,"")
f(char*b,char*e,char*s){int i;for(i=0;*s;s++){strchr(b,*s)?p(i-2,i+=2):strchr(e,*s)?p(i-=2,i-2):putchar(*s);}}

A rather quick & dirty approach. It has a bug of producing empty lines between consecutive closing parenthesis, but otherwise it does the job (or so I think). I will revisit it for a better & cleaner solution, sometime this week.

char *b is the opening parenthesis set, char *e is the closing parenthesis set and char *s is the input string.

share|improve this answer

Perl - 69 bytes

TMTOWTDI makes code simple

#!perl -p
s/([[{(<])|([]})>])|\w+/"  "x($1?$t++:$2?--$t:$t)."$&
"/ge
share|improve this answer
3  
You're supposed to take the parentheses as input, not hardcode them. –  Gareth Jul 31 '11 at 8:26

Scala(2.9), 211 characters

object P extends App{def x(j:Int)={"\n"+"  "*j}
var(i,n)=(0,"")
for(c<-args(2)){if(args(0).exists(_==c)){print(x(i)+c)
i+=1
n=x(i)}else{if(args(1).exists(_==c)){i-=1
print(x(i)+c)
n=x(i)}else{print(n+c)
n=""}}}}
share|improve this answer

Perl - 89 85 bytes

A version of Hojung Youn's answer which accepts the block characters via two arguments.

#!perl -p
BEGIN{$b=pop;$a=pop}s/([$a])|([$b])|\w+/"  "x($1?$t++:$2?--$t:$t)."$&
"/ge

Called like:

perl golf.pl<<<'abc{xyz{text[note{comment(t{ex}t)abc}]}}' '[{(<' ']})>'
share|improve this answer
    
Very nice concept, @Hojung and Sorpigal. It is a bit fragile, though. For example, swap the ] and } in the close-paren argument, and the ] closes the character class, leading to an unmatched paren error. Similarly, suppose the open set starts with ^, perhaps to match v in the close set; you'll get the complement of the intended [$a] class. That's why I used \Q...\E in my answer. The \w+ for non-paren characters works for the example, but what about input like 'x(foo-bar)y' '(' ')'? Of course, it isn't clear the code needs to handle something like that. –  DCharness Aug 2 '11 at 15:23

Python3, 184 182 chars

import sys
_,p,q,t=sys.argv
i,f,x=0,1,print
for e in t:
 if e in p:f or x();x(' '*i+e);i+=2;f=1
 elif e in q:f or x();i-=2;f=1;x(' '*i+e)
 else:not f or x(' '*i,end='');f=x(e,end='')

Example:

$ python3 ./a.py '{[(<' '}])>' 'abc{xyz{text[note{comment(t{ex}t)abc}]}}'
abc
{
  xyz
  {
    text
    [
      note
      {
        comment
        (
          t
          {
            ex
          }
          t
        )
        abc
      }
    ]
  }
}
share|improve this answer
    
heinrich5991 suggested saving two characters by changing the second line to _,p,q,t=sys.argv –  Peter Taylor Apr 3 '13 at 8:53

Groovy, 125

p=args;i=0;s={a,b->"\n"+"\t"*(b?i++:--i)+a+"\n"+"\t"*i};p[0].each{c->print p[1].contains(c)?s(c,1):p[2].contains(c)?s(c,0):c}

You can save the script in a file indent.groovy and try it with:
groovy indent.groovy "abc{xyz{text[note{comment(t{ex}t)abc}]}}" "{[(" ")]}"

share|improve this answer
    
I tried around in groovy for an hour before seeing your answer, I used a similar aproach, but mine is much longer than yours so I won't even bother to post.. Good job! :) –  Fels Oct 11 '13 at 17:33

C - 213 209

I hate stupid mistakes... >.<

#include<stdio.h>
#include<string.h>
int main(int i,char**s){for(char q,r,c,t,a=0;~(c=getchar());t=q|r){q=!!strchr(s[1],c);a-=r=!!strchr(s[2],c);for(i=0;t|q|r&&i<2*a+1;putchar(i++?' ':'\n'));a+=q;putchar(c);}}

Reads left-parens from first command-line argument, right-parens from second argument, and input to indent on stdin.

Pretty-printed & commented:

int main(int i, char **s) {
  for (char q, r, /* is left-paren? is right-paren? */
            c,    /* character read from input */
            t,    /* last char was a paren-char */
            a=0;  /* indentation */
       ~(c = getchar());
       t = q|r) {
         q = !!strchr(s[1],c);
    a -= r = !!strchr(s[2],c);
    for (i=0; t|q|r && i<2*a+1; putchar(i++? ' ' : '\n'));
    a += q;
    putchar(c);
  }
}
share|improve this answer

Python - 407

from sys import*;o=argv[1];c=argv[2];t=argv[3];p=0;n=False;a=lambda:h not in e;b=lambda s:print(s+(" "*p)+h);r="";e=o+c
for h in t:
 for k in o:
  if h==k:
   if(r in e)and(r!=""):b("")
   else:b("\n")
   p+=2;n=True;break
 for k in c:
  if h==k:
   p-=2
   if(r in e)and(r!=""):b("")
   else:b("\n")
   n=True;break
 if a()and n:print((" "*p)+h,end="");n=False
 elif a():print(h,end="")
 r=h

An ungolfed version of the program:

import sys

open_set = sys.argv[1]
close_set = sys.argv[2]
text = sys.argv[3]
spaces = 0
newline = False
a = lambda : char not in b_set
b = lambda s: print(s + (" " * spaces) + char)
prev = ""
b_set = open_set + close_set

for char in text:
    for bracket in open_set:
        if char == bracket:
            if (prev in b_set) and (prev != ""):
                b("")
            else:
            b("\n")
        spaces += 2
        newline = True
        break
    for bracket in close_set:
        if char == bracket:
            spaces -= 2
            if (prev in b_set) and (prev != ""):
                b("")
            else:
                b("\n")
            newline = True
            break
    if a() and newline:
        print((" " * spaces) + char, end="")
        newline = False
    elif a():
        print(char, end="")
    prev = char

The arguments to the program are (in order): the opening parentheses, the closing parentheses, and the text to indent.

Example ($ is command line prompt):

$ python indent.py "{[(<" "}])>" "abc{xyz{text[note{comment(t{ex}t)abc}]}}"
abc
{
  xyz
  {
    text
    [
      note
      {
        comment
        (
          t
          {
            ex
          }
          t
        )
        abc
      }
    ]
  }
}
share|improve this answer

D (300)

C[] i(C,S)(ref S s,C p){if(!*s)return[];static C[] w;w~=" ";C[] r;C c=s[0];while(c!=p){s=s[1..$];r~=(c=='{'||c=='['||c=='<'?"\n"~w~c~"\n"~i(s,cast(char)(c+2)):c=='('?"\n"~w~c~"\n"~i(s,')'):[c]);c=*s;}w=w[1..$];if(*s)s=s[1..$];c=*s;return" "~w~r~"\n"~w~(c=='}'||c==']'||c=='>'||c==')'?[p]:p~"\n"~w);}

needs a null terminated string for the bounds check (otherwise the if(*s) needs to be changed to if(s.length))

share|improve this answer
    
Note that the task calls for the two sets of parentheses to be part of the input, not hardcoded. –  Joey Jul 5 '11 at 12:51

Java

Non codegolf version! Assuming we have this version of split() that includes delims,

public static String indent(String input, String openPars,
        String closingPars) {
    String re = "["
            + (openPars + closingPars).replace("[", "\\[").replace("]",
                    "\\]") + "]";
    String[] split = inclusiveSplit(input, re, 0);
    int indent = 0;
    StringBuilder sb = new StringBuilder();
    for (String string : split) {
        if (StringUtils.isEmpty(string))
            continue;
        if (closingPars.indexOf(string) != -1) {
            indent--;
        }
        sb.append(StringUtils.repeat(" ", indent * 2));
                    sb.append(string);
                    sb.append("\n");
        if (openPars.indexOf(string) != -1) {
            indent++;
        }
    }
    String string = sb.toString();
    return string;
}
share|improve this answer
2  
StringUtils is not part of the Standard JDK. –  st0le Jul 7 '11 at 4:58

C 284 Non White space Characters

I'm no fan of obfuscation but well...

#include<cstdio>
#include<cstring>
#define g printf
#define j char
int main(int a,j**b){int c=0;for(j*f=b[3];*f!='\0';++f){if(strchr(b[1],*f)!=0){g("\n%*c\n%*c",c,*f,c+2,'\0');c+=2;}else if(strchr(b[2],*(f))!=0){c-=2;g("\n%*c",c,*f);if(strchr(b[2],*(f+1))==0)g("\n%*c",c,'\0');}else putchar(*f);}}

Usage: ./program start_brackets end_brackets string_to_parse

share|improve this answer

php (187) (153)

function a($s,$o,$e){while(''!=$c=$s[$i++]){$a=strpbrk($c,$o)?2:0;$b=strpbrk($c,$e)?2:0;echo ($a+$b||$r)?"\n".str_pad('',$t-=$b):'',$c;$t+=$a;$r=$a+$b;}}

Function takes string, opening delimiters, ending delimiters as arguments.

share|improve this answer

C, 256

Parameters:

  • e is the ending char,
  • n is the indentation,
  • b the opening brackets,
  • d the closing brackets.

I broke the code up to avoid the horizontal scrollbar.

#define r char
#define P(c) putchar(c);
#define N P(x)
#define W printf("%*s",n,"");
r*s,x='\n';i(r e,int n,r*b,r*d){r*t=s,*p;int l=0;W while(*s!=e)    
{if(p=strchr(b,*s)){if(s!=t){N W}P(*s++)N i(d[p-b],n+2,b,d); N W 
P(*s++);l=1;}else{if(l){N W l=0;}P(*s++)}}}

Complete program is 363 characters.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define r char
#define P(c) putchar(c);
#define N P(x)
#define W printf("%*s",n,"");
r*s,x='\n';i(r e,int n,r*b,r*d)
{r*t=s,*p;int l=0;W while(*s!=e)
{if(p=strchr(b,*s)){if(s!=t){N W}
P(*s++)N i(d[p-b],n+2,b,d); N W
P(*s++);l=1;}else{if(l){N W l=0;}
P(*s++)}}}main(int c,r*v[]){s =
v[3];i('\0',0,v[1],v[2]);}
share|improve this answer

VB.net (?c)

Language isn't suited to code golfing, so I used an uncommon approach. Using a trace listener to output to the console.

Imports System.Diagnostics.Debug
Module Module1
  Sub Main(args() As String)
    IndentText(args(0), args(1), args(2)) 'openings, closings, text)
  End Sub
  Sub IndentText(o As String, e As String, t As String)
    Dim x = 0
    Listeners.Add(New Diagnostics.ConsoleTraceListener)
    IndentSize = 2
    For Each c In t
      If o.Contains(c) Then
        WriteLine("")
        WriteLine(c)
        Indent()
        x = 1
      ElseIf e.Contains(c) Then
        If x = 0 Then WriteLine("")
        Unindent()
        WriteLine(c)
        x = 1
      Else
        Write(c)
        x = 0
      End If
    Next
  End Sub
End Module

Uses commandline args for the input

args(0) is the indenting chars
args(1) is the undenting chars
args(2) is the text to be indented.
share|improve this answer

C, 114 121

main(i,x,s,c){while(~(c=getchar()))(s=x)|(x=2*!!strchr("(){}[]<>",c))?s=c-1&x,i-=x-2*s,printf("\n%*c",i-s,c):putchar(c);}

Not very nice, but a solution.. an empty line may appear before/after depending on whether input starts/finishes with a parentheses.

With the new restriction, this approach is almost useless for golfing.

share|improve this answer
    
Doesn't indent the opening parentheses far enough and outputs empty lines between consecutive closing ones. –  Joey Jul 5 '11 at 7:57
    
@joey fixed, thank you for the feedback! –  esneider Jul 5 '11 at 12:31
    
It still hard-codes the parentheses while they should be part of the input. Currently all answers do not conform to the specification. –  Joey Jul 5 '11 at 12:42

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.