# Remove All Smalltalk Comments for Code Golf

• Take a line of input (function arguments or stdin, etc.).
• Remove comments in it, where a comment starts and ends with ".
• Do not remove comments in string literals, which start and end with '. String literals do not contain escapes, because smalltalk has nice, easily lexable syntax ;).
• Output this string.
• The number of "s not inside strings and the number of 's not in comments in the input are both guaranteed to be even.
• Because removing comments makes programs more golfy, this is : shortest code per language wins.

## Examples

input => output

123 => 123
1"2"3 => 13
1""23 => 123
'12' => '12'
'1"2'3"" => '1"2'3
'1"2"3' => '1"2"3'
a"1'2'3"a => aa
"a"b"c" => b
'a'b'c' => 'a'b'c'
a'"'b'"'c => a'"'b'"'c
a"'"b"'"c => abc

Regex is boring as usual. Try something new, or answer in smalltalk!
• sandboxed ;) Jun 10 at 13:10
• This would be a good use-case for graphical programming languages for statecharts! Unfortunately, graphical programming languages are not good for golfing. Jun 10 at 13:28
• @MartinRosenau Remember that this isn't a competition between languages. I'd still be interested in a solution using one of those languages, even if it isn't competitive with a dedicated golfing language. Jun 10 at 13:36

# Smalltalk, 325 bytes

[:k|k inject:OrderedCollection new into:[:a :b|(a isEmpty not and:[a last=0])ifTrue:[(b=$')ifTrue:[a removeLast. a add:b. a]ifFalse:[a removeLast. a add:b. a add:0. a]]ifFalse:[(a isEmpty not and:[a last=$"])ifTrue:[(b=$")ifTrue:[a removeLast. a]ifFalse:[a]]ifFalse:[(b=$')ifTrue:[a add:b. a add:0. a]ifFalse:[a add:b. a]]]]]


Some hot garbage smalltalk code.

It essentially uses a stack, pushing a 0 for a placeholder for when it sees '. When it sees ", it keeps in on the top of the stack until it sees another ".

I couldn't get an Eval function (smalltalk docs are terrible), nor could I get regex to work (smalltalk docs are terrible).

You can use GNU Smalltalk to run this, in theory, although I only tested it on the online compiler (linked above).

Here's a (more) readable version that takes from stdin and writes to stdout. As you can tell smalltalk is a beautiful language...

Transcript show:(stdin nextLine inject:
OrderedCollection new into:
[:a :b|

Transcript show: a printString; cr.

(a isEmpty not and:[a last = 0])ifTrue: [
(b = $') ifTrue: [ a removeLast. a add: b. a ] ifFalse:[ a removeLast. a add: b. a add: 0. a ] ] ifFalse: [ (a isEmpty not and:[a last =$"]) ifTrue: [
(b = $") ifTrue: [ a removeLast. a ] ifFalse: [a] ] ifFalse: [ (b =$') ifTrue: [
a
] ifFalse: [
a
]
]
]
]).


Note: the regex solution almost works, using the same approach as Jakque, but it fails since smalltalk gives nil instead of an empty string:

Transcript show:(stdin nextLine replacingAllRegex: '".*?"|(''.*?''|.)' with: '%1')

• By "the online compiler" you mean codingground? Jun 10 at 16:12
• yes I do mean that Jun 10 at 16:17
• I didn't know about character literals in smalltalk. Because of those, this challenge won't work on real smalltalk code :/ Jun 10 at 16:35
• Transcript show: 'a"''"b"''"c' "works", but single quotes get escaped by placing... another single quote. Edit: nvm it doesn't work since it's not parsed as code, duh. Jun 10 at 16:45

# APL (Dyalog Extended), 30 26 bytes (SBCS)

Anonymous prefix lambda.

{⍵/⍨≠\⍛⍱⍨<⌿≠\@1⊢'''"'∘.=⍵}


Try it online!

{}dfn; argument is ⍵:
a"1'2'3"a'1"2"3'a"'"b"'"c

'''"'∘.=⍵ equality table for ' and " versus the argument
[[0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0],
 [0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0]]

≠\@1⊢ running parity (lit. XOR scan) at the 1st row (lit XOR scan) indicating characters inside strings
[[0,0,0,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,0,0,0],
 [0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0]]

<⌿ second row, i.e. comment delimiters, and not first row, i.e. inside string, i.e. active comment delimiters (lit. vertical less-than reduce)
[0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0]

≠\⍛⍱⍨ neither that (active comment delimiters) NOR its running parity (inside comments)
[1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1]

⍵/⍨ use that to filter the characters of the argument
aa'1"2"3'ac

• it's nice to see that the regex solution and the cool and refreshing solution are the same length in APL Jun 10 at 14:36
• @Wzl Shorter, even.
Jun 10 at 15:57

# Jelly, 48 bytes

iⱮ⁾'"o1:0¤>/
ṣ”"2œPj€”"µẹ”'2ị‘œṖµÇ?;ß}¥/$¹f⁾'"$?


Try it online!

This is definitely not the best approach, even considering Jelly is typically not great with strings.

iⱮ⁾'"o1:0¤>/  Helper Link
o        Logical OR with
1:0¤    1 / 0 (infinity) - that way, "not found" is at the end, not the start
>/  Is the first greater than the second?

ṣ”"2œPj€”"µẹ”'2ị‘œṖµÇ?;ß}¥/$¹f⁾'"$?  Main Link
?  If
f⁾'"$There are any ' or " ?$         - If
Ç                - " was found first
ṣ”"                                    - Split on "
2œP                                 - Split over and discard the second item
j€”"                             - Join each of those with " (basically, this slices out
the part between and including the first and second ",
and leaves the part before and after as two sublists)
- Otherwise
ẹ”'                         - Find all indices of '
2ị                       - Get the second of these
‘                      - And increment it
œṖ                    - And partition the list at that point (basically, this
divides the list at the second ' without removing anything)
¥/          - Then, reduce (apply to first and second as left and right) by
;ß}            - Concatenating the left side with this function applied to the right link
¹        Otherwise, if ' and " aren't found, just return the string itself
$$$$

• can we get an explanation of how it works? Jun 11 at 18:19
• @NooneAtAll I usually don't do much work on answers anymore after they've been beaten by so much, but here you go. Jun 11 at 18:49

# Jelly, 17 bytes

⁾'"iⱮnaoɗ\ŻṖoƊỊx@


Try it online!

A link taking a Jelly string argument and returning a Jelly string without the comments.

## Explanation

⁾'"iⱮ              | Positions of each character in '" (so single quote -> 1, double quote -> 2, anything else -> 0)
ɗ\        | Cumulative reduce (x,y)
nao           | - (x != y) and (x or y)
Ɗ    | Following as a monad:
Ż       | - Prepend zero
Ṗ      | - Remove last item from list
o     | - Or
Ị   | Less than or equal to 1
x@ | Original input with 0 or 1 copies of each character as appropriate

• sadly you missed the fgitw :/ Jun 10 at 19:56
• @Wzl what do you mean? Jun 10 at 19:56
• fastest gun in the west Jun 11 at 3:42

# QuadRg, 18 12 bytes

‒6 thanks to Neil.

'.*'
".*"
&



Try it online!

This is equivalent to the Dyalog APL function '''.*''' '".*"'⎕R'&' ''⍠'Greedy'0

The g flag turns off greedy patterns, essentially making * mean *?. Then the two patterns simply replace '.*' strings and ".*" comments with & themselves and  nothing, respectively.

• okay now we've gotten regex out of the way :P Jun 10 at 13:22
• No non-greedy quantifiers?
– Neil
Jun 10 at 19:29
• @Neil Wouldn't make a difference, as '[^']*' matches exactly all the chars between ' and the next '. The important thing is that the two patterns run in parallel, with, for each starting position, preference for the first one.
Jun 10 at 19:57
• Sure, but Retina lets me use '.*?' which is shorter.
– Neil
Jun 10 at 20:52
• @Neil D'uh, of course. Thank you!
Jun 10 at 20:56

# QBasic, 118113 112 bytes

LINE INPUT s$FOR i=1TO LEN(s$)
a=ASC(MID$(s$,i))
k=c
c=c XOR(a=34)<q
q=q XOR(a=39)<c
IF c+k=0THEN?CHR$(a); NEXT  ### Explanation Looping over the ASCII code a of each character in a line of input s$, we track three boolean values:

• c is true inside a comment, false otherwise
• q is true inside quotes, false otherwise
• k is the previous iteration's value of c (we need to track this so we can suppress both double quotes instead of just the first one)

The values are updated as follows:

• If the current character is a double quote (a=34) and q is false, toggle c
• If the current character is a single quote (a=39) and c is false, toggle q

If both c and k are false, we're not in a comment, so output the character.

(There's a fun trick in the update statements: In QBasic, truthy is -1, so instead of a=39AND c=0, we can get the same result from (a=39)<c. The inequality will be true only when a=39 is -1 and c is 0.)

# C, 92 91 bytes

c;s;main(){while((c=getchar())+1)s=s?s-2?putchar(c):0,s*(c!=44-5*s):c-34?putchar(c)==39:2;}


All my submissions so far have been in esolangs, so even though C might not be anything groundbreaking, it's at least new to me. :)

Try It Online!

### Explanation

c;s;                          // for current char and state
main() {
while ((c=getchar())+1)     // while input is not EOF:
s=s?            // if state is not 0 (0=default, 1=string literal, 2=comment)
s-2?putchar(c):0,       // output the current char if state is not 2
s*(c!=44-5*s) // set s to 0 if the relevant character is found
// (39 ['] if state = 1, 34 ["] if state = 2)
:              // else (state = 0)
c-34?         // if the current char is not ["]
putchar(c)==39 // output current char, and if it is ['], set s to 1
:      // else (current char is ["])
2     // set s to 2
;}        // end expression, statement, and main function

• Looks great! Would you mind adding a "readable" version as well? Jun 11 at 14:06
• No problem, although I'm not sure how 'readable' I can make it. Jun 12 at 16:21

# Python 3.8 (pre-release), no regex, 75 65 bytes

a=b=1
for i in input():c=b*i;a^="'"==c;b^='"'==a*i;print(end=c*b)


Try it online!

Much cleaner solution than my previous one

### how it works :

a and b store the state of the string:

• a is equal to 0 if the char is inside a literal and 1 otherwise
• b is equal to 0 if the char is inside a comment and 1 otherwise
• c store the char multiplied by b. If the char is inside a comment (b is equal to 0), c is equal to the empty string
• a^= use of the bitwise operator xor to swich the state of a if the current char is ' and the char is not inside a comment
• same goes for b but with "
• print(end=c*b) print the char if b is not set to 0. end= is for avoiding the trailing new line

# Python 3.8 (pre-release), regex, 52 bytes

lambda s:re.sub("\".*?\"|('.*?')","\\1",s)
import re


Try it online!

Pretty basic: I replace comments by nothing and literals by themselves. Regex substitution are not overriding. Lazy operators do the rest

• any particular reason for putting the import after the lambda? I guess it makes no difference; I've just never seen it before Jun 12 at 0:49
• It's for the TIO link. I can add f=\  in the header to name my lambda function. But yeah, it doesn't make any difference Jun 12 at 8:42

# JavaScript (Node.js), 41 bytes

s=>s.replace(/('|").*?\1/g,_=>_<'%'?'':_)


Try it online!

Trivial, thank EliteDaMyth for -3 bytes

# JavaScript (Node.js), 88 86 bytes, No RegEx

f=([c,...s],y=c=='"')=>c?[[c+s.splice(0,s.indexOf(c=="'"|y?c:s)+1).join][+y]]+f(s):s


Try it online!

If s.indexOf(s) happens to be non-negative, then s==c and f([c,c])==c+c

# Retina 0.8.2, 16 bytes

('.*?')|".*?"
\$1


Try it online! Link includes test cases. Explanation: Regex, obviously. Since by default matches can't overlap, the regex can match both strings and comments and won't get confused. It then remains to delete the comments without changing the strings.

# Charcoal, 28 bytes

≔ωζＦＳ«Ｆ¬⁼∨ζι"ι¿№"'ι≔⁻∨ζι∧ζιζ


Try it online! Link is to verbose version of code. Explanation:

≔ωζ


Keep track of the current quote character, initially , but can be " or '.

ＦＳ«


Loop over the input.

Ｆ¬⁼∨ζι"ι


If the current quote, or the current character if there is no current quote, is not ", then print the current character.

¿№"'ι


If the current character is a quote, then...

≔⁻∨ζι∧ζιζ


... turn the current quote on or off as appropriate.

• If the current quote is empty, then we subtract nothing from the current character, so the current quote becomes the current character.
• Otherwise, we subtract the current character from the current quote. This ends the current quote only once we find the matching character.

f('"':s)=g s
`