17
\$\begingroup\$

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.

\$\endgroup\$
10
  • 5
    \$\begingroup\$ Can we assume that for each parenthesis there's a closing one, and in the same order? \$\endgroup\$
    – Champo
    Jul 5, 2011 at 1:22
  • \$\begingroup\$ 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 }])> ? \$\endgroup\$
    – Joey Adams
    Jul 5, 2011 at 3:27
  • \$\begingroup\$ @Joey, I'd assume not, though that would be all the more impressive. \$\endgroup\$
    – Neil
    Jul 5, 2011 at 7:39
  • \$\begingroup\$ joey : input are 1. open parenthesis characters 2.close parenthesis chars 3. string to indent. Juan : we can assume that, though code need not rely on that, what I mean is if delim is part of opening parenthesis chars, increase indent, else if part of closing parenthesis chars decrease indent. \$\endgroup\$ Jul 5, 2011 at 11:25
  • 1
    \$\begingroup\$ @Phrasant Bhate: And in the output? \$\endgroup\$
    – Lowjacker
    Jul 7, 2011 at 11:37

17 Answers 17

7
\$\begingroup\$

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.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ 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. \$\endgroup\$
    – Ventero
    Jul 7, 2011 at 11:37
7
\$\begingroup\$

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)/
\$\endgroup\$
3
\$\begingroup\$

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.

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

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
\$\endgroup\$
2
  • \$\begingroup\$ Note that the task calls for the two sets of parentheses to be part of the input, not hardcoded. \$\endgroup\$
    – Joey
    Jul 5, 2011 at 12:51
  • \$\begingroup\$ @Joey noted, I'll get around to fixing that in a while. Thanks \$\endgroup\$
    – Champo
    Jul 5, 2011 at 13:40
2
\$\begingroup\$

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
)
\$\endgroup\$
1
  • \$\begingroup\$ Do you need the spaces after the print statements? \$\endgroup\$
    – Adalynn
    Dec 4, 2016 at 0:25
2
\$\begingroup\$

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);
  }
}
\$\endgroup\$
1
\$\begingroup\$

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.

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

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=""}}}}
\$\endgroup\$
1
\$\begingroup\$

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}]}}' '[{(<' ']})>'
\$\endgroup\$
1
  • \$\begingroup\$ 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. \$\endgroup\$
    – DCharness
    Aug 2, 2011 at 15:23
1
\$\begingroup\$

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
      }
    ]
  }
}
\$\endgroup\$
1
  • \$\begingroup\$ heinrich5991 suggested saving two characters by changing the second line to _,p,q,t=sys.argv \$\endgroup\$ Apr 3, 2013 at 8:53
1
\$\begingroup\$

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}]}}" "{[(" ")]}"

\$\endgroup\$
1
  • \$\begingroup\$ 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! :) \$\endgroup\$
    – Fels
    Oct 11, 2013 at 17:33
1
\$\begingroup\$

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
      }
    ]
  }
}
\$\endgroup\$
0
\$\begingroup\$

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

\$\endgroup\$
0
\$\begingroup\$

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.

\$\endgroup\$
0
\$\begingroup\$

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]);}
\$\endgroup\$
0
\$\begingroup\$

Powershell, 146 Bytes

param([char[]]$s,[char[]]$e,[char[]]$f)$f|%{}{if($_-in$s){$o;'  '*$i+$_;$o='  '*++$i;}elseif($_-in$e){$o;'  '*--$i+$_;$o='  '*$i}else{$o+=$_}}{$o}

Ungolfed Explanation

param([char[]]$start,             # Cast as array of Chars
      [char[]]$end,
      [char[]]$string)
$string | foreach-object { } {    # For every char in string. Empty Begin block
    if ( $_ -in $start ) {        # If char is in start
        $o                        # Print stack ($o)
        '  ' * $i + $_            # Newline, indent, insert start char
        $o = '  ' * ++$i          # Set stack to ident (incremented)
    } elseif ( $_ -in $end ) {    # If char is in end
        $o                        # Print stack
        '  ' * --$i + $_          # Newline, decrement indent, insert end char
        $o = '  ' * $i            # Set stack to indent
    } else {
        $o+ = $_                  # Otherwise add character to stack
    }
} { $o }                          # Print remaining stack (if any)
\$\endgroup\$
0
\$\begingroup\$

C, 181 characters

#define d(m,f)if(strchr(v[m],*s)){puts("");for(j=f;j--;)printf("  ");}
i;main(j,v,s)char**v,*s;{for(s=v[3];*s;s++){d(1,i++)d(2,--i)putchar(*s);d(1,i)if(!strchr(v[2],*(s+1)))d(2,i)}}

Pretty much the most straightforward approach imaginable. Iterate through the string (v[3]), if it's a left brace (as defined in v[1]), increase the indent level, if it's a right brace (as defined in v[2]), decrease the indent level.

\$\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.