Your task is to write a program (or function) that will take a string as input and append text to the beginning of every line that satisfy the following rules:

  • The text appended must contain the line number. You may use 0- or 1-based indexing.
  • The text must be a comment in your language. A comment is defined as a syntacticly valid piece of code that makes no changes to the program's state. Removing the comment should make no change to the program.
  • The comment may only contain newlines at the end of it.
  • You do not have to put line numbers for lines that contain just whitespace and comments.


  • You may assume that the input will be a valid program in your language.
  • In indentation-sensitive languages like Python, you may place the line number comment after all indentation. You may choose either tabs or spaces to be your indentation character, but you must specify on your post.
  • You may assume that every statement in the code will take up at most the entirety of 1 line; i.e. no multi-line strings or backslash-continuation.

Test Cases


a = int(input())
while a:

C: (Honestly, I'm surprised this compiles)

/*0 */#include <stdio.h>
/*1 */int main()
/*2 */{
/*3 */    char c;
/*4 */    c = getchar();
/*5 */    do
/*6 */    {
/*7 */        putchar(c);
/*8 */    }
/*9 */    while (c);

This is , so the shortest answer (in bytes) wins.

  • 7
    \$\begingroup\$ Making the comments language-dependent makes things complicated. You already had to create a special rule for Python. What about all the other languages that don't have multi-line comments? What about languages that do not have comments at all. What about multi-line strings where comments cannot be placed without side effects? \$\endgroup\$ – Dennis Nov 20 '16 at 22:26
  • 4
    \$\begingroup\$ Fun fact: the rule "removing the comment should make no change to the program" immediately disqualifies any Python answer, because the code—including comments—can be introspected at runtime. I have seen this in play in a production system: a piece of code would raise an AssertionError unless the stack trace contained the phrase foo.py, which was expected to come from a file name but could also appear as a line comment in the context. \$\endgroup\$ – wchargin Nov 21 '16 at 3:09
  • 2
    \$\begingroup\$ "The comment can only contain newlines at the end of it" is inconsistent with the C "test case". \$\endgroup\$ – Peter Taylor Nov 21 '16 at 6:41
  • 2
    \$\begingroup\$ What about langs without comments? \$\endgroup\$ – NoOneIsHere Nov 21 '16 at 7:58
  • 4
    \$\begingroup\$ The edit doesn't fix the inconsistency. \$\endgroup\$ – Peter Taylor Nov 21 '16 at 13:38

17 Answers 17


Pyke, 7 bytes


Try it here!

o       -  o++
 \K     -  "K"
   z    -  input()
    ++  - sum(^)
      r - while no errors: GOTO start

I'm declaring integer comments to be the integer followed by the character K and then the line. An extra byte is used to stop the newline opcode from kicking in and printing an extra thing.


Perl, 8+1 = 9 bytes


Run with -p (1 byte penalty). (Note to people unfamiliar with PPCG rules; you also need to specify a modern version of Perl syntax using -M5.010, but we decided that options to select language versions are free and don't incur a byte penalty, so I didn't mention it in the original version of this post.)

-p places the program into an implicit loop; it basically causes the program to become a filter that processes each line separately (i.e. the entire program is run on the first line, then the second, then the third, and so on). Perl also keeps track of a line number, called $., that records how many lines of input have been read. So all the program's doing is letting -p read a line of input; output a #, the current line number ($.), and a newline (say adds a newline by default, which is helpful in this case, and is also shorter than the more commonly seen print); and then letting -p output the original line of code that it read (typically a program using -p would do some sort of processing on the input, but because we didn't, it's just output unchanged). Comments in Perl run from # to a newline (the # in the program itself doesn't start a comment because it's inside a string literal), so what we're basically doing is taking the opportunity to write comment lines into the file as we process it, without disturbing the "natural" read and write cycle of -p.

  • \$\begingroup\$ Can you explain how this works? \$\endgroup\$ – Adám Nov 24 '16 at 14:24
  • \$\begingroup\$ Sure. I've explained the operation of relevant Perl language features in a tutorial style, given that it's a very basic Perl program that doesn't really make much use of the language's power. I need to remember that not everyone knows how a -p loop or an automatically updating line number work, though. \$\endgroup\$ – user62131 Nov 24 '16 at 15:04
  • \$\begingroup\$ Note that say"$.;" would work as well, because the question specified that "You may assume that every statement in the code will take up at most the entirety of 1 line". \$\endgroup\$ – msh210 Nov 24 '16 at 15:20
  • \$\begingroup\$ That's not really a comment, though; although it does nothing useful, it ends up in the AST (and because Perl's optimizer is kind-of sucky, I think that actually will end up slowing the program down, something you wouldn't really want a comment to do). \$\endgroup\$ – user62131 Nov 24 '16 at 16:05
  • \$\begingroup\$ Not working for rule You do not have to put line numbers for lines that contain just whitespace and comments. \$\endgroup\$ – Denis Ibaev Nov 28 '16 at 6:58

Javascript, 43 39 bytes


Thanks to ETH and Conor for saving 4 bytes.

  • \$\begingroup\$ 41 bytes: a=>(b=0,a.replace(/^/gm,_=>/*${++b}*/)) (use a formatting string) \$\endgroup\$ – Conor O'Brien Nov 21 '16 at 0:06
  • 1
    \$\begingroup\$ 39 bytes: a=>a.replace(/^/gm,_=>`/*${++b}*/`,b=0) \$\endgroup\$ – ETHproductions Nov 21 '16 at 0:13

V, 10 bytes


Try it online!


Batch, 91 bytes

@set n=
@for /f "delims= tokens=*" %%a in (%1) do @set/an+=1&call echo @rem %%n%%&echo %%a

Batch doesn't have a way of reading STDIN until EOF, so instead the file name has to be passed as a command-line parameter.


Lua, 80 75 Bytes

Saved some bytes by abusing the langauge.


Simple enough starter answer.

Ungolfed, +

x=1                                                     -- Assign x to 1...
print(                                                  -- Print...
      "--[[1]]"                                         -- The first line number comment...
      ..                                                -- With...
      (...):gsub(                                       -- The input, replacing all...
                 "\n",                                  -- Newlines...
                    load                                -- with a string compiled function...
                    (' \
                    x=x+1                               --Increment x by one... \
                    return"\\n--[["..x.."]]"            -- And return the new linecomment. \
  • \$\begingroup\$ I don't know Lua, but are you sure that it will always generate comments that are the same length? For example if a program is 10 lines long than the last comment will be --[[10]] which is 1 character longer than --[[9]] unless you correctly pad it with spaces. \$\endgroup\$ – Esolanging Fruit Nov 20 '16 at 22:51
  • 1
    \$\begingroup\$ Oh, I didn't notice that rule. That seems... A bit redundant... \$\endgroup\$ – ATaco Nov 20 '16 at 22:59
  • 1
    \$\begingroup\$ Yes. Maybe I'll remove it... [EDIT]: removed. \$\endgroup\$ – Esolanging Fruit Nov 20 '16 at 23:00

Gema, 16 15 characters


In Gema there are only line comments, starting with !.

Sample run:

bash-4.3$ cat l33t.gema 

bash-4.3$ gema -f l33t.gema <<< 'Hello World!'
H3ll0 W0rld!

bash-4.3$ gema '*\n=\!@line\n$0' < l33t.gema > l33t-nr.gema

bash-4.3$ cat l33t-nr.gema

bash-4.3$ gema -f l33t-nr.gema <<< 'Hello World!'
H3ll0 W0rld!

The following are to answer Adám's question, whether is possible to add the line number in kind of ignored code.

Gema code is essentially a collection of from=to transformation rules, or template=action in Gema terms. I see no way to define a template that will never ever match anything, this alone not seems to be the way.

Gema, 18 characters


Transforms e=3 into c:=1;:e=3.

Luckily in Gema there are domains, kind of namespaces. The above code defines the dummy rules in namespace c, that we never use. Unfortunately a mentioned domain remains in effect until the end of line, so we have to explicitly to switch back to default domain.

Gema, 18 characters


Transforms e=3 into 1=$0;e=3.

A less complicated alternative is to use effectless code instead of ignored one. I mean to put back exactly what was matched.

  • \$\begingroup\$ But couldn't you insert a string and have some code around it which makes it ignored? \$\endgroup\$ – Adám Nov 24 '16 at 14:26
  • \$\begingroup\$ You mean kind of e=3 transformed in if (false) { 1 }; e=3 instead of the current !1␤e=3? Yeah, there would be a 19 character long workaround. \$\endgroup\$ – manatwork Nov 24 '16 at 14:42

Qbasic, 91 89 bytes



PRINT "Hello!"
INPUT "Type your name: ", b$
PRINT a$; ", "; b$


 2'PRINT "Hello!"
 4'INPUT "Type your name: ", b$
 5'PRINT a$; ", "; b$
  • 1
    \$\begingroup\$ Been a while since I've used QBasic, but doesn't that make the program text all a comment, rather than the line numbers? Or am I misremembering something? \$\endgroup\$ – user44863 Nov 21 '16 at 15:37
  • \$\begingroup\$ Thanks for the input! Actually, the ' just comments that which follows on the line. \$\endgroup\$ – anonymous2 Nov 21 '16 at 16:50
  • 4
    \$\begingroup\$ Yes, which is why I think it's turning the program into comments. Rather than just adding line numbers, it changes the meaning into a program that does nothing. (Honestly, it'd be cool for an entry in a BASIC dialect to add real line numbers and renumber the program if it already has numbers, but that's probably not really needed.) \$\endgroup\$ – user44863 Nov 21 '16 at 19:22

BASH (+ GNU sed) 27 bytes

sed 'i#
='|sed 'N;s/\n//;N'

The first part (i# \n =) almost works in GNU sed (for 4 bytes), but it puts a newline after the #.


awk (19 13 bytes)

19 bytes : This insert "#"+line number above each line of code


13 bytes : Credit & thanks to @manatwork for two 13 bytes solution

As 1 action defaults to print $0:


Or by replacing $0 content

$0="#"NR RS$0
  • \$\begingroup\$ {print"#"NR}1 or $0="#"NR RS$0? \$\endgroup\$ – manatwork Nov 21 '16 at 21:01
  • \$\begingroup\$ @manatwork i'm quite ashamed and grateful to discover those subtilities \$\endgroup\$ – Adam Nov 21 '16 at 21:35

Kotlin extension function, 69 60 bytes

fun String.a()=lines().mapIndexed{i,s->"/*$i*/$s"}.joinToString("\n")

fun String.a(){lines().mapIndexed{i,s->println("/*$i*/$s")}}

Example usage:

fun main(args: Array<String>) {
  val code = """fun main(args: Array<String>) {
  println("Hello world!")


/*0*/fun main(args: Array<String>) {
/*1*/  println("Hello world!")

CJam, 21 Bytes

I am not well-versed in CJam at all, but I knew for a fact it has comments :)


Explanation coming soon.

  • \$\begingroup\$ oNo can be replaced with n on TIO. \$\endgroup\$ – Esolanging Fruit Apr 27 '17 at 19:00

Mathematica, 58 bytes

i = 1; StringReplace[#, StartOfLine :> "(*" <> ToString@i++ <> "*)"] &

jq, 31 characters

(27 characters code + 4 characters command line options.)


In jq there are only line comments, starting with #.

Sample run:

bash-4.3$ cat l33t.jq 

bash-4.3$ jq -Rr -f l33t.jq <<< 'Hello World!'
H3ll0 W0rld!

bash-4.3$ jq -Rr '"#\(input_line_number)\n"+.' l33t.jq > l33t-nr.jq

bash-4.3$ cat l33t-nr.jq 

bash-4.3$ jq -Rr -f l33t-nr.jq <<< 'Hello World!'
H3ll0 W0rld!
  • \$\begingroup\$ Dear Perl and Ruby coders, please observe jq's input_line_number. Being Thanksgiving, any special feeling to express for $.? \$\endgroup\$ – manatwork Nov 24 '16 at 13:29

GolfScript, 23 Bytes


There are only line comments starting with "#".

Ungolfed & explained:

           # the input is pushed on the stack automatically
n          # n is a predefined variable for "\n"
/          # splits the input string with the previously pushed "\n" as delimiter
0:i;       # i = 0
{          # A code block: It is used for map here (going through the input)
    "#"    # push a "#" onto the stack
    i):i  # save i + 1 in i, the value is again not popped from the stack
    +      # implicitly converts the number i to a string and concatenates "#" with it
    n      # newline
    +      # add a newline (# introduces a *line* comment)
    \      # switch the top to elements (-> yields the comment as the bottom, and the line as the top element on the stack)
    +      # concatenate them
}          # end of the code block
%          # map
n          # newline
*          # join the list with newlines between them
           # outputs are done implicitly

I am pretty sure that this can be further simplified, especially the i can probably be left out.

You can test it here: https://golfscript.apphb.com/ Because this site doesn't support adding inputs, you'll have to put a string surrounded with double quotes in front of the code. '\n' will be a newline. Be aware of the fact, that there are other escape sequences as well. User '\\' if you are not sure.


C# 6, 66 61 bytes

Thanks to CSharpie

(666, devils code ^^) not any more ...

This works for all languages using "C style comments" (C, C++, C#, Java, ....)

It simply splits the string into lines, prepends every line with its index and joins the edited lines again with new line characters.


old version:

  • 1
    \$\begingroup\$ Technically 64 since op didnt mention any padding with zeros. Also you can save 1 more byte by wirting: $"/*{i}*/"+l. (Moving the L-parameter out of the interpolation.) \$\endgroup\$ – CSharpie Nov 24 '16 at 21:51
  • \$\begingroup\$ You're right ^^ but that destroys my "evil score" hehe \$\endgroup\$ – Stefan Nov 24 '16 at 22:55

Python 2, 82 bytes

Works for space-only indentation

for i,l in enumerate(input().split('\n')):print l.split(l.lstrip())[0]+'#%d\n'%i+l

Indentation-less version for 56 bytes

for x in enumerate(input().split('\n')):print'#%d\n%s'%x

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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