13
\$\begingroup\$

Definitions:

  • A line is a possibly empty sequence of characters terminated by a “newline character.” A “newline character” is a character that cannot be a component of the aforementioned character sequence.
  • A line number comment is a character sequence that does not alter the meaning of source code in the programming language of your submission. It contains a decimal representation of the (integral) unique line number associated with the line of input. (A conventional notation is sufficient, e. g. 4.2E1 for 42.)
  • A content line is a line that conveys meaning in the programming language of your submission.

Task:

  • Write a program (or function) that numbers all lines, but at least all content lines. The program shall print a line number comment followed by the respective processed (otherwise unaltered) input line. The function shall return a value accordingly.

Conditions:

  • Numbered lines must be numbered in ascending order in the order of input (i. e. not in the order a processor for the respective programming language might possibly process the input).
  • Line numbers must be consecutive. That means you may not omit numbers.
  • The first line is identified either by the line number zero (0) or one (1). The behavior must be consistent for all possible input.
  • Line numbers must reproduce: Feeding the output back as input again produces the same line numbers.

Accommodations:

  • You may assume that input is valid source code in your submission’s programming language.
  • You may assume that no token is spread across multiple lines (e. g. multiline string literals or backslash-continuation), thus insertion of a line number comment is guaranteed not to alter the source code’s meaning.
  • You may assume input consists of lines. Numbering of non-lines (that is empty input or input not terminated by a newline-character) is implementation-dependent.

Test cases:

C (note that tabulator characters are preserved):

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

Pascal (note that it is not specified that input is indeed source code):

Esolanging fruit is hanging low.
{           1 }Esolanging fruit is hanging low.

If you input that output back in again, it must reproduce the same line number comments:

{           1 }{           1 }Esolanging fruit is hanging low.

SQL (note that only content lines were numbered):

-- populate foo with some data
insert into foo values (1, 'bar');
insert into foo values (2, 'foobar');

select * from foo;
-- populate foo with some data
/*0*/insert into foo values (1, 'bar');
/*1*/insert into foo values (2, 'foobar');

/*2*/select * from foo;
\$\endgroup\$
22
  • 10
    \$\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, 2016 at 22:26
  • 6
    \$\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, 2016 at 3:09
  • 2
    \$\begingroup\$ "The comment can only contain newlines at the end of it" is inconsistent with the C "test case". \$\endgroup\$ Nov 21, 2016 at 6:41
  • 3
    \$\begingroup\$ What about langs without comments? \$\endgroup\$ Nov 21, 2016 at 7:58
  • 5
    \$\begingroup\$ The edit doesn't fix the inconsistency. \$\endgroup\$ Nov 21, 2016 at 13:38

22 Answers 22

16
\$\begingroup\$

Perl, 8+1 = 9 bytes

say"#$."

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.

\$\endgroup\$
6
  • \$\begingroup\$ Can you explain how this works? \$\endgroup\$
    – Adám
    Nov 24, 2016 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, 2016 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, 2016 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, 2016 at 16:05
  • 1
    \$\begingroup\$ "You do not have to" ≠ "You can't". I don't think any of the other answers omit line numbers on such lines either, because it would require considerably more code to do so. \$\endgroup\$
    – user62131
    Nov 28, 2016 at 16:25
9
\$\begingroup\$

Javascript, 43 39 bytes

a=>a.replace(/^/gm,_=>`/*${++b}*/`,b=0)

Thanks to ETH and Conor for saving 4 bytes.

\$\endgroup\$
2
  • \$\begingroup\$ 41 bytes: a=>(b=0,a.replace(/^/gm,_=>/*${++b}*/)) (use a formatting string) \$\endgroup\$ Nov 21, 2016 at 0:06
  • 1
    \$\begingroup\$ 39 bytes: a=>a.replace(/^/gm,_=>`/*${++b}*/`,b=0) \$\endgroup\$ Nov 21, 2016 at 0:13
5
\$\begingroup\$

Pyke, 7 bytes

o\Kz++r

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.

\$\endgroup\$
5
\$\begingroup\$

V, 10 bytes

O:1òYjjP

Try it online!

\$\endgroup\$
4
\$\begingroup\$

Lua, 80 75 Bytes

Saved some bytes by abusing the langauge.

x=1print("--[[1]]"..(...):gsub("\n",load('x=x+1return"\\n--[["..x.."]]"')))

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. \
                    ')
                 )
      )
\$\endgroup\$
3
  • \$\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\$ Nov 20, 2016 at 22:51
  • 1
    \$\begingroup\$ Oh, I didn't notice that rule. That seems... A bit redundant... \$\endgroup\$
    – ATaco
    Nov 20, 2016 at 22:59
  • 1
    \$\begingroup\$ Yes. Maybe I'll remove it... [EDIT]: removed. \$\endgroup\$ Nov 20, 2016 at 23:00
3
\$\begingroup\$

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.

\$\endgroup\$
3
\$\begingroup\$

awk (19 13 bytes)

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

{print"#"NR"\n"$0}

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

As 1 action defaults to print $0:

{print"#"NR}1

Or by replacing $0 content

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

Gema, 16 15 characters

*\n=\!@line\n$0

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

Sample run:

bash-4.3$ cat l33t.gema 
e=3
g=9
i=1
o=0
t=7

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
!1
e=3
!2
g=9
!3
i=1
!4
o=0
!5
t=7

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

*\n=c:\=@line\;:$0

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

*\n=@line\=\$0\;$0

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.

\$\endgroup\$
2
  • \$\begingroup\$ But couldn't you insert a string and have some code around it which makes it ignored? \$\endgroup\$
    – Adám
    Nov 24, 2016 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, 2016 at 14:42
3
\$\begingroup\$

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>) {
  //language=kotlin
  val code = """fun main(args: Array<String>) {
  println("Hello world!")
}"""
  code.a()
}

Output:

/*0*/fun main(args: Array<String>) {
/*1*/  println("Hello world!")
/*2*/}
\$\endgroup\$
2
\$\begingroup\$

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 #.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ 19 bytes: sed =|sed s/^/#/\;N, with POSIX sed and shell \$\endgroup\$ Dec 18, 2022 at 16:03
2
\$\begingroup\$

APL (Dyalog Unicode), 53 bytes

N←(⍴P)[0]⋄P←((2×N)⍴0 1)⍀P⋄P[2×⍳N;]←⊂[1](N⍴'⍝'),[.5]⍳N

Try it on TryAPL.org!

The environmental variable ⎕IO←0. The program to comment is contained in an (n 1) nested array of string statements. The result is returned in the same (expanded) array.

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

CJam, 21 Bytes

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

qN%ee{"e#"o(oNo(oNo}/

Explanation coming soon.

\$\endgroup\$
1
  • \$\begingroup\$ oNo can be replaced with n on TIO. \$\endgroup\$ Apr 27, 2017 at 19:00
1
\$\begingroup\$

Mathematica, 58 bytes

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

jq, 31 characters

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

"#\(input_line_number)\n"+.

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

Sample run:

bash-4.3$ cat l33t.jq 
gsub("e";"3")|
gsub("g";"9")|
gsub("i";"1")|
gsub("o";"0")|
gsub("t";"7")

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 
#1
gsub("e";"3")|
#2
gsub("g";"9")|
#3
gsub("i";"1")|
#4
gsub("o";"0")|
#5
gsub("t";"7")

bash-4.3$ jq -Rr -f l33t-nr.jq <<< 'Hello World!'
H3ll0 W0rld!
\$\endgroup\$
1
  • \$\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, 2016 at 13:29
1
\$\begingroup\$

GolfScript, 23 Bytes

n/0:i;{"#"i):i+n+\+}%n*

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.

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

C (gcc), 54 bytes

a;main(i){for(;gets(&a);)printf("/*%d*/%s\n",i++,&a);}

Try it online!

Why not

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

JavaScript (Node.js), 35 bytes

Revisited this question and found a shorter solution. It saves a few bytes to append // comments instead prepending /**/ ones.

a=>a.replace(/$/gm,_=>"//"+v++,v=1)

Try it online!

JavaScript, 47 bytes

Old answer:

a=>a.split`
`.map((l,i)=>"/*"+i+"*/"+l).join`
`
\$\endgroup\$
3
  • \$\begingroup\$ If in Python the comment take whole line why not js? That saves byte \$\endgroup\$
    – l4m2
    Dec 18, 2022 at 6:16
  • \$\begingroup\$ Good idea @l4m2 I just added it \$\endgroup\$
    – noodle man
    Dec 18, 2022 at 17:19
  • \$\begingroup\$ Also removed the Japt solution since I realized it added JS comments instead of Japt ones (which AFAIK don't exist) \$\endgroup\$
    – noodle man
    Dec 18, 2022 at 17:28
1
\$\begingroup\$

C# 6, 66 63 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.

s=>string.Join("\n",s.Split('\n').Select((l,i)=>$"/*{i}*/"+l));

old version:

s=>string.Join("\n",s.Split('\n').Select((l,i)=>$"/*{i,3}*/{l}"));
\$\endgroup\$
2
  • 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, 2016 at 21:51
  • \$\begingroup\$ You're right ^^ but that destroys my "evil score" hehe \$\endgroup\$
    – Stefan
    Nov 24, 2016 at 22:55
0
\$\begingroup\$

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

Nim, 52 bytes

var n=0
for l in stdin.lines:n+=1;echo "#[",n,"]#",l

Attempt This Online!

Nim is indentation-sensitive, but it doesn't seem to have a problem with comments before indentation.

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

Haskell, 84 bytes

Assumes each indentation is done by a horizontal tab(\t).

unlines.zipWith(\n l->let(x,y)=span('\t'==)l in x++"{-"++show n++"-}"++y)[0..].lines
\$\endgroup\$
0
\$\begingroup\$

Pascal, 198 bytes

  • This is a full Pascal program as per ISO standard 10206 “Extended Pascal.” Pascal does not permit multiline literals. String and character literals spanning multiple lines are explicitly banned. Therefore there is no problem with that.
  • The program only works correctly for up to maxInt + 1 input lines (zero-based indexing). MaxInt is an implementation-defined value. There is no safeguard installed when exceeding this limit.
  • A line is a possibly empty sequence of characters terminated by a special “end of line character.” This program might possibly number non-lines, too. At any rate empty input is not numbered.
program p(input,output);var n:integer;c:char;begin n:=0;while not EOF do begin write('{',n,'}');repeat read(c);write(c)until EOF or_else EOLn;if not EOF then begin writeLn;read(c)end;n:=n+1 end end.

Expanded and annotated:

program prependLineNumberComment(input, output);
    var
        { Contains the currently processed `input` line number. }
        lineNumber: integer value 0;
        { Identifiers of buffer variables (`input↑`, `output↑`) are too long. }
        c: char;
    begin
        while not EOF do
        begin
            write('{ ', lineNumber, ' }');
            lineNumber ≔ succ(lineNumber);
                        
            { Copy `input` to `output`. }
            repeat
            begin
                { This is equivalent to: `c ≔ input↑; get(input);`: }
                read(c);
                { This is equivalent to: `output↑ ≔ c; put(output);`: }
                write(c);
            end
            { Invoking `EOLn(input)` if `EOF(input)` is `true` is fatal. }
            until EOF or_else EOLn;
            
            { `get(input)` requires that `EOF(input)` is `false`. }
            if not EOF then
            begin
                { The only valid method for producing a newline character is: }
                writeLn;
                { Advance reading cursor for the embracing `while`‑loop. }
                read(c);                    { Shorter than `get(input);` }
            end;
        end;
    end.
\$\endgroup\$

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