49
\$\begingroup\$

Lots of people on this site use esoteric languages, and since these languages are unusual and hard to understand, they will frequently write an explanation in a certain format. For example, if the code was

abcdefghijklmnop

And this languages uses # for comments, they would write an explanation like this:

a                #Explanation of what 'a' does
 bc              #Bc
   d             #d
    e            #Explanation of e
     fgh         #foobar
        ij       #hello world
          k      #etc.
           l     #so on
            mn   #and
              op #so forth

I frequently do this too, but every time I do this, I feel like creating the layout of text is really obnoxious and time-consuming. So I want you to create a "Esolang-Comment-Template-Generator" for me. For example, if we ignore the comments, the previous code has this template:

a                #
 bc              #
   d             #
    e            #
     fgh         #
        ij       #
          k      #
           l     #
            mn   #
              op #

The Challenge:

You must write a program or function that takes two strings as input, and outputs this "Esolang-Comment-Template". The first input will be the code, but with bars (|) inserted where the newlines go. The second input is what we will use for comments. So our last example would have this for input:

"a|bc|d|e|fgh|ij|k|l|mn|op", "#"

Unfortunately this excludes bars from being part of the code input, but that's OK. You can assume that the comment input will be a single character. For simplicity's sake, the comment char will not be a bar. The code input will only contain printable ASCII, and it will not contain any newlines.

Hopefully you can infer what to do from the testcases, but I'll try to clarify some things.

You must split the code input up into "code-sections" on every bar. Then, each section of code is output on its own line and left-padded with the length of all the previous code (not including the bars). Then, each line is right-padded with enough spaces so that the last two characters on every line are "One additional space" + "The comment character".

One trailing newline is allowed.

Here is another example. For the input

"Hello|World", "/"

The first section of code is "Hello" and the second is "World". So it should give the output:

Hello      /
     World /

Here are some more samples:

Input:
"a|b|c|d|e|f|g", ","

Output:
a       ,
 b      ,
  c     ,
   d    ,
    e   ,
     f  ,
      g ,

Input:
"abcdefg", ":"

Output:
abcdefg :

Input:
"4|8|15|16|23|42", "%"

Output:
4          %
 8         %
  15       %
    16     %
      23   %
        42 %

Input:
"E|ac|h s|ecti|on is| one c|haracte|r longer| than the| last!", "!"

Output:
E                                                   !
 ac                                                 !
   h s                                              !
      ecti                                          !
          on is                                     !
                one c                               !
                     haracte                        !
                            r longer                !
                                     than the       !
                                              last! !

Input:
"This|Code|has||empty||sections", "@"

Output:
This                     @
    Code                 @
        has              @
                         @
           empty         @
                         @
                sections @

Rules:

You may take these inputs and outputs in any reasonable format. For example, reading/writing a file, STDIN/STOUT, function arguments/return value, etc. As usual, this is , so try to make your code as short as possible and you win if you can get the shortest solution in your language! I will also select the shortest solution as the overall winner. Standard loopholes are banned.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Related \$\endgroup\$ Sep 9, 2016 at 4:32
  • \$\begingroup\$ Are trailing blanks allowed? \$\endgroup\$
    – Titus
    Sep 9, 2016 at 9:02
  • 30
    \$\begingroup\$ Next step : a 3D representation for 2D languages \$\endgroup\$
    – Aaron
    Sep 9, 2016 at 9:26
  • 4
    \$\begingroup\$ A bonus if you manage to do it without using the | character would be nice, so you can explain yourself \$\endgroup\$ Sep 9, 2016 at 12:11
  • \$\begingroup\$ Can the comment character be a bar (|) ? \$\endgroup\$
    – Ton Hospel
    Sep 12, 2016 at 8:46

46 Answers 46

1
2
1
\$\begingroup\$

Stax, 16 bytes

ù3l╛CrΣ╝▀↕┘╣óétº

Run and debug it

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

APL (Dyalog Unicode), 28 bytes

Full program. Prompts for code, then for comment character.

⍞,⍤1⍨⎕FMT⎕TC[2]@('|'∘=)⍞,' '

Try it online!

⍞,' ' append a space the to prompted-for code

⎕TC[2]@() place the 2nd Terminal Control character (Line Feed) at the positions indicated by:

'|'∘= the mask indicating where the pipe symbol is equal to the characters

⎕FMT ForMaT as a character matrix, evaluating terminal control characters (Line Feed moves the "cursor" down one line)

⍞,⍤1⍨ prompt for comment character and append it to each line (lit. to each 1D sub-array)

\$\endgroup\$
4
  • \$\begingroup\$ How is dyalog APL non-competing still? Is it still in-dev? \$\endgroup\$
    – DJMcMayhem
    Sep 10, 2016 at 15:59
  • \$\begingroup\$ @DJMcMayhem Yes. I work for Dyalog and have had access to 16.0 even before 15.0 was released. 16.0 is scheduled for release in 2017Q1. \$\endgroup\$
    – Adám
    Sep 10, 2016 at 20:22
  • \$\begingroup\$ How does this work? \$\endgroup\$ Sep 12, 2016 at 0:30
  • 1
    \$\begingroup\$ @ConorO'Brien Rewritten, and explanation added. \$\endgroup\$
    – Adám
    Sep 5, 2022 at 10:01
1
\$\begingroup\$

Go, 209 bytes

import."strings"
func f(C,c string)string{L,Q,S:=Split(C,"|"),[]string{},0
for _,l:=range L{q:=len(l)
if q<1{continue}
Q=append(Q,Repeat(" ",S)+l+Repeat(" ",len(Join(L,""))-q+1-S)+c)
S+=q}
return Join(Q,"\n")}

Attempt This Online!

Explanation

import."strings"
func f(C,c string)string{
// split C into substrings, and init the output slice and space count
L,Q,S:=Split(C,"|"),[]string{},0
// for each substring...
for _,l:=range L{
// if it's empty, skip it
q:=len(l)
if q<1{continue}
// add to the output slice...
Q=append(Q,
    // initial spaces
    Repeat(" ",S)+
    // the substring
    l+
    // the following spaces 
    Repeat(" ",len(Join(L,""))-q+1-S)
    // and the comment    
    +c)
// increment the space count
S+=q}
// join together with newlines
return Join(Q,"\n")}
\$\endgroup\$
1
\$\begingroup\$

Japt -R, 16 bytes

q| åÏiXîÃú m+S+V

Try it

The boilerplate is similar to @Shaggy's 17 byte Japt submission.

Explanation:

q|                 split on |
   åÏ              cumulatively reduce each X, Y to:
     i               prepend to Y
      Xî             spaces to the length of X
        Ã          end cumulative reduce
         ú         right-pad each to the longest line
           m+      to each, append
             S+V     space (S) and comment char (V)

flag -R            join the final result on newlines
\$\endgroup\$
1
\$\begingroup\$

Haskell, 96 bytes

f"".lines.map r
r '|'='\n'
r c=c
w=(' '<$)
f l(a:s)c=(w l++a++(w=<<c:s)++c):f(l++a)s c
f l s c=s

Attempt This Online!

Eplanation

The function r is defined to map pipe characters to newlines (r '|'='\n') and every other char to itself (r c=c). lines.map r thus first replaces all pipes in the input string with new lines (map r), and then splits the string at the newlines with the build-in lines function. The main function f iterates over this list of strings and constructs the output line by line with the expression w l++a++(w=<<c:s)++c where

  • a is the part of the code that appears on the current line
  • c is the comment character (as a string) that gets put at the end of the line
  • w is a function that replaces each character of a string by spaces (definde by w=(' '<$))
  • l is the string of code fragments to the left of a, which is put at the beginnig of the line as spaces with w l
  • s is the list of remaining code strings which are also converted to spaces, concatenated, and put after a (c is added to s to get the extra space required before the comment character)
\$\endgroup\$
1
\$\begingroup\$

Ruby, 80 74 bytes

->i,c{a=0;(i=i.split ?|).map{" "*a+_1+" "*(1+i.join.size-(a+=_1.size))+c}}

Attempt This Online!

I managed to golf a non-regex based solution to beat the other Ruby. The solution previously tied but I found a slightly different method that is 6 bytes shorter.

Explanation:

->i,c{…} Proc (lambda function) taking arguments i (code) and c (comment)

  • a=0; Assign variable a to zero

  • (i=i.split ?|) Assign i to it split on |

  • .map{…} Map each _1 in the list to:

    • " "*a a spaces

    • +_1 Append the list item

    • +" "*(…) Append this many spaces:

      • 1+i.join.size The size of i with | removed, plus one for the extra space
      • -(a+=_1.size) Add the size of the list item to a and subtract a from this.
    • +c Append the comment character to the end of the line.

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

Perl 5 -pl, 50 bytes

$f=<>;s%\||$%$"x$'=~y/|//c." $f
".$"x$`=~y/|//c%ge

Try it online!

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

JavaScript (Node.js), 84 79 bytes

c=>s=>s.split`|`.map(l=>z="".padEnd(a=z.length)+l,z="").map(l=>l.padEnd(a+2)+c)

Attempt This Online!

Saved 5 bytes by using two iterations instead of 1 but keeping track of the previous string so no number is needed to keep track of the length.

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

Uiua, 42 40 36 bytes

≡⊂⬚@ ≡°□\⍚(⊂≡⋅@ )⊕□⍜▽¯⟜\+=,@|:¤⊟@ 

Try it

-2 bytes thanks to chunes :)

This was one of my first Uiua answers, I got so much better in only 2 weeks, and was able to save 10%!

\$\endgroup\$
2
  • \$\begingroup\$ -2 since ⊙(F⊙(G)) is equivalent to ⊙(F|G). \$\endgroup\$
    – chunes
    Mar 7 at 17:05
  • \$\begingroup\$ @chunes Cool! So many neat features I haven’t seen yet \$\endgroup\$
    – noodle man
    Mar 7 at 20:52
0
\$\begingroup\$

Scala, 123 bytes

def?(i:String,c:String)={var b=0
i.split('|').map{s=>println(" "*b+s+" "*(i.replace("|","").size-b-s.size+1)+c)
b+=s.size}}

Test code + Output:

?("a|b|c|d|e|f|g", ",")
a       ,
 b      ,
  c     ,
   d    ,
    e   ,
     f  ,
      g ,

?("abcdefg", ":")
abcdefg :

?("4|8|15|16|23|42", "%")
4          %
 8         %
  15       %
    16     %
      23   %
        42 %

?("E|ac|h s|ecti|on is| one c|haracte|r longer| than the| last!", "!")
E                                                   !
 ac                                                 !
   h s                                              !
      ecti                                          !
          on is                                     !
                one c                               !
                     haracte                        !
                            r longer                !
                                     than the       !
                                              last! !

?("This|Code|has||empty||sections", "@")
This                     @
    Code                 @
        has              @
                         @
           empty         @
                         @
                sections @
\$\endgroup\$
0
\$\begingroup\$

Jelly, 41 bytes

This looks like it has far to many increments, and probably too many links...

ṫø⁹‘‘µFL‘⁶ẋ
‘ị
ḣFL⁶ẋ$;ç@;1ŀ
J’ç@€
ṣ”|Ç;€Y

Test it at TryItOnline

How?

ṫø⁹‘‘µFL‘⁶ẋ  - link 1: get the spaces for after the code, dyadic(split string, index)
 ø           - next chain as a nilad
  ⁹‘‘        - right argument incremented twice (the index we actually want)
ṫ            - tail (get the rest of the split string)
     µ       - next chain as a monad
      FL‘    - flatten, get its length and increment
         ⁶   - a space character " "
          ẋ  - repeat the space character that many times

‘ị           - Link 2: get the code for a line dyadic(index, split string)
‘            - increment the index
 ị           - retrieve the string at that index

ḣFL⁶ẋ$;ç@;1ŀ - Link 3: get the code and join with spaces, dyadic (index, split string)
ḣ            - head: split string[index:]
 FL          - flatten and get its length
     $       - previous two atoms as a monad
   ⁶         - a space character, " "
    ẋ        - repeat the space that many times
      ;      - concatenate with
       ç@    - the result of the previous link (the code) - reverse inputs
         ;   - concatenate with
          1ŀ - the result of Link 1 (the spaces after the code)

J’ç@€        - Link 3: a for loop, monadic(split string)
J’           - [0,...,Len(split string)-1]
  ç@€        - the result of the previous link, with revered inputs, for each

ṣ”|Ç;€Y      - Main Link: dyadic(code string, comment character)
ṣ”|          - split on "|"
   Ç         - the result of the previous link
    ;€       - concatenate each with the comment character
      Y      - join with line feeds
             - implicit print
\$\endgroup\$
0
\$\begingroup\$

CJam, 32 bytes

l'|/_s,)L@{1$,S*\+}%@f{Se]}lN+f+

Explanation

l                                  get code string
 '|/                               split into code parts
    _s,)                           length of all the parts +1
        L@{1$,S*\+}%               left pad spaces to every part for the length of the previous parts
                    @f{Se]}        right pad spaces
                           lN+f+   add comment character and newline

Try it online

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

Thunno 2 N, 25 bytes

'|/DJl⁺X0Yıyṣs+xðĿnlẏ+ƥ¹+

Try it online!

Explanation

(Yes, this is in the exact same format as the outputs for this challenge :P)

'|/DJl⁺X0Yıyṣs+xðĿnlẏ+ƥ¹+  '# Implicit input
'|/                        '# Split the input on "|"
   DJl                      # Duplicate, join, and get length
      ⁺X                    # Increment this and store it in x
        0Y                  # Store 0 in y
          ı                 # Map over the list:
           yṣ               #  Push y spaces
             s+             #  Prepend this to the input
               xðĿ          #  Pad with spaces to length x
                  nlẏ+ƥ     #  Add the length to y
                       ¹+   #  Append the second input
                            # Implicit output, joined on newlines
\$\endgroup\$
0
\$\begingroup\$

GolfScript, 44 43 bytes

~:~;"|"/[""\{\," "*\+.}/,):l;]{l" "*+l<~n}/

Try it online!

This was my first GolfScript submission, and I’ve improved quite a lot since I first tried this task.

Explanation:

~:~;

Eval STDIN as GolfScript. Assign the comment character to the ~ variable, and pop out of the stack, so the code is on top.

Using a symbol instead of a letter as a command name let me remove a whitespace later on.

"|"/

Split the code on |s.

[""\{\," "*\+.}/

This is equivalent to a scan (reduce, keeping in-between values) by repeating spaces to the length of the previous value, and prepending that to the current value.

GolfScript doesn’t have scan built-in, so this is implemented somewhat cumbersomely, using / (each) and duplicating the result each iteration.

,):l;]

The previous step left an extra copy of the last value. We take its length, increment that, and assign it to l.

{l" "*+l<~n}/

For each line ({ … }/): append l spaces (l" "*+), take the first l characters (l<), then push ~ (the comment character) and n (newline).

The entire stack is printed when the program ends.

Original solution:

GolfScript, 64 63 bytes

~n+:c;0:a;."|"/""*,):b;{.124={;b" "*c" "a*}{a):a;b(:b;}if}%" "c

Try it online!

Full program, takes input like "abc|def|g" "#" on STDIN.

I’ve always thought GolfScript seemed interesting but have never actually tried it. After using quite a few more modern golfing languages with hundreds of built-ins, the terse but very readable syntax of GolfScript is really refreshing. I had a lot of fun trying to get this to work, so I’m glad I could beat the 75 byte solution posted in 2017.

I had a 50-or so byte version that used stack manipulation instead of variables, but couldn’t get it to work.

I hope you like the smileys ): (: :b :c ={

Explanation

Eval the input; this leaves two strings on the stack. Append a newline to the top one, store it in c as the comment character, and pop it off the stack. The variable a is initialized to 0. The variable b is initialized to the length of splitting the input on | and joining it back.

The top of the stack is now the |-separated string. Map each character of the input string to:

  • If the character is |, leave b spaces, c, a newline, and a spaces on the stack.

  • Otherwise, increment a, decrement b, and leave the original character in the stack.

Finally, push a space and c to the stack. The whole stack is implicitly output at the end.

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

Perl 5 + -lF\| -M5.10.0, 31 bytes

A different approach to both the existing Perl answers.

$,=v12;say@F," .[A@{[<>]}.."x@F

Try it online!

Explanation

Sets $, (which is the record separator, automatically printed between each item of a list) to v12 (which is chr(12), or form feed - which moves the cursor down a line), then we say @F (which is the input, split on |), followed by a raw string consisting of space, the ANSI escape sequence for "up one line", the comment char from STDIN (<>), which is embedded to the string using @{[...]}, and two backspace characters, which is repeated the same number of times as there are items in the input (using the list of input, @F, in a scalar context yield the list length).

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

Swift 5.9, 153 152 bytes

let f={var x={{String.init}()(" ",$0)},s=($0+"").split{$0=="|"},i=0
return{s.map{x(i)+$0+x(s.joined().count-(i+=$0.count,i).1+1)}.joined}()($1+"\n")+$1}

This is an almost direct port of Kevin Cruijssen's Java answer. Here's an ungolfed version:

let function = { (code: String, comment: String) -> String in
  // convenience closure to repeat the space character
  let spaces = { String(repeating: " ", count: $0) }
  
  // split the code into substrings using the pipe as
  // a separator
  let splitCode = code.split(separator: "|")

  var indentLevel = 0

  let indentedCode = splitCode.map { line in
    // start off with the previous indent level
    let leadingIndent = spaces(indentLevel)

    // update the indent level for the trailing indent
    // and the next leading indent
    indentLevel += line.count

    // gets the total length of the code, subtracts the
    // current indent level, and adds 1 for an extra
    // trailing space
    let trailingIndentAmount = splitCode.joined().count - indentLevel + 1
    let trailingIndent = spaces(trailingIndentAmount)

    return leadingIndent + line + trailingIndent + comment
  }

  // finally, join the code with newlines and return it
  return indentedCode.joined(separator: "\n")
}
\$\endgroup\$
1
2

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.