g | x | w | all
Bytes Lang Time Link
216Swift 6.1250604T162809ZmacOSist
236C++ gcc250603T163728Zceilingc
328C++140924T042759ZJerry Je
211Python 2/3190919T184338ZCarlos L
110Ruby180309T124412ZAsone Tu
102Pip161008T084452ZDLosc
395BaCon161026T183745ZPeter
226Cobra140916T074911ZΟurous
170J140902T060546Zalgorith
133APL140903T113559Zmarinus
3198Python 2 236140830T160313ZBruno Le
190Perl140830T222053Zfaubi

Swift 6.1, 222 216 bytes

I wanted to challenge myself somewhat with this one. To the best of my knowledge, the closure below is 100% compliant with both specifications (the one on Esolangs and the one in this question). It also avoids any sort of recursion, meaning infinite loops are actually infinite.

{var e="",p=$0+e,m=0,b=[e,e,e],f={"\(p.removeFirst())"}
while e<p{let c=f()
"/"==c ?{if m>1{while e==b[1]||p != (p.replace(b[1],with:b[2]),p).1{}
b=[b[0],e,e]}
m=(m+1)%3}():(b[m]+="\\"==c ?e<p ?f():e:c)}
return b[0]}

Try it on SwiftFiddle! Takes the program as a String and returns the output as a String.

Here it is with whitespace and meaningful identifiers:

{
  var empty = "",
    program = $0 + empty,
    mode = 0,
    buffers = [empty, empty, empty],
    first = { "\(program.removeFirst())" }
  while empty < program {
    let character = first()
    "/" == character ? {
      if mode > 1 {
        while empty == buffers[1] || program != (
          program.replace(buffers[1], with: buffers[2]),
          program
        ).1 { }
        buffers = [buffers[0], empty, empty]
      }
      mode = (mode + 1) % 3
    }() : (
      buffers[mode] += "\\" == character
        ? empty < program
          ? first()
          : empty
        : character
    )
  }
  return buffers[0]
}

Breakdown

{
  var empty = "",
    program = $0 + empty,
    mode = 0,
    buffers = [empty, empty, empty],
    first = { "\(program.removeFirst())" }

Assign our initial variables:

  while empty < program {
    let character = first()

Loop while the program is non-empty, popping the first character from the program each time. (The empty string compares less than all other strings.)

    "/" == character ? {

Check if character is a forward slash, i.e. whether we need to switch modes. (Using the ternary operator with a self-executing closure saves a byte over an if statement.)

      if mode > 1 {
        while empty == buffers[1] || program != (
          program.replace(buffers[1], with: buffers[2]),
          program
        ).1 { }
        buffers = [buffers[0], empty, empty]
      }

If we're in replacement mode (i.e. mode == 2) when we hit a forward slash, perform a substitution:

      mode = (mode + 1) % 3

Increment mode, cycling back to 0 via the modulo operator if needed.

    }() : (
      buffers[mode] += "\\" == character
        ? empty < program
          ? first()
          : empty
        : character
    )

If character is not a forward slash, update the appropriate buffer as follows:

  }
  return buffers[0]
}

If/when the main loop completes, simply return the output buffer.

C++ (gcc), 287 269 262 249 240 236 bytes

#import<regex>
#define F(x)for(x="";(q=u[++p])-47;)x+=u[p+=q==92/!!q];
int p;main(int q,char**g){for(std::string s,t,u=g[1];q=u[p];)if(q-47)putchar(u[p+=q==92]),p++;else{F(s)F(t)for(u=&u[++p];p=~(q=u.find(s));)u.replace(q,s.size(),t);}}

Try it online!

Slightly golfed less.

#import<regex>
main(int,char**g){
  std::string s,t,u=g[1],v;
  for(char*p=&u[0],*q;*p;)
    if(*p-47)                             // print if not replacing
      p+=*p==92,
      putchar(*p++);
    else{
      s=t="";
      for(p++;*p-47;p+=*p==92,s+=*p++)    // search string
        if(!*p)
          exit(0);
      for(p++;*p-47;p+=*p==92,t+=*p++)    // replacement string
        if(!*p)
          exit(0);
      u=++p;
      p=&u[0];
      for(v="";q=strstr(p,&s[0]);p=&u[0]) // apply search and replace
        u=v+=u.substr(0,q-p)+t+&q[s.size()],
        v="";
    }
}

C++ 328

Edits:

Here is the code:

#import<bits/stdc++.h>
#define N(x)o[i]);else if(n<x)o[i]==92?
#define O (o[++i]):o[i]==47?n++:
char p[198],*q=p,*s=p+99;int i,t;main(int n,char**m){for(std::string o=m[1];o[i];++i){if(!N(3)putchar O putchar(N(4)*q++=O(*q++=N(5)*s++=O*s++=o[i];if(n>4){for(n=2;~(t=o.find(p,i+1));)o.replace(t,strlen(p),s=p+99);bzero(q=p,198);}}}

Try it online!

Python 2/3 (211 bytes)

The following code, based on Bruno Le Floch's answer, is Python 2 and Python 3 compatible.

Moreover, being iterative rather than recursive it does not risk hitting the maximum recursion depth of Python.

def S(c):
 while c:
  B=["","",1]
  for m in 0,1,2:
   while c:
    if"/"==c[0]:c=c[1:];break
    if"\\"==c[0]:c=c[1:]
    if m:B[m-1]+=c[0]
    else:yield c[0]
    c=c[1:]
  while c and B[0]in c:c=c.replace(*B)

Ruby, 119 110 bytes

Terminates with exception

r=->s,o=$>{s[k=s[0]]='';k==?/?o==$>?s.gsub!([r[s,''],e=r[s,'']][0]){e}:t=o:o<<(k==?\\?s[0]+s[0]='':k);t||redo}

Try it online!

Terminates cleanly (116 bytes)

r=->s,o=$>{s[k=s[0]||exit]='';k==?/?o==$>?s.gsub!([r[s,''],e=r[s,'']][0]){e}:t=o:o<<(k==?\\?s[0]+s[0]='':k);t||redo}

Try it online!

Pip, 100 102 bytes

I hadn't ever proven Pip to be Turing-complete (though it's pretty obviously so), and instead of going the usual route of BF I thought /// would be interesting. Once I had the solution, I figured I'd golf it and post it here.

101 bytes of code, +1 for -r flag:

i:gJnf:{a:xW#i&'/NE YPOia.:yQ'\?POiya}W#iI'\Q YPOiOPOiEIyQ'/{p:VfY0s:VfIyQ'/WpNi&YviR:Xp{++y?ps}}E Oy

Here's my ungolfed version with copious comments:

; Use the -r flag to read the /// program from stdin
; Stdin is read into g as a list of lines; join them on newline and assign to c for code
c : gJn

; Loop while c is nonempty
W #c {
 ; Pop the first character of c and yank into y
 Y POc
 ; If y equals "\"
 I yQ'\
  ; Pop c again and output
  O POc
 ; Else if y equals "/"
 EI yQ'/ {
  ; Build up pattern p from empty string
  p : ""
  ; Pop c, yank into y, loop while that is not equal to "/" and c is nonempty
  W #c & '/ NE Y POc {
   ; If y equals "\"
   I yQ'\
    ; Pop c again and add that character to p
    p .: POc
   ; Else, add y to p
   E p .: y
  }

  ; Yank 0 so we can reliably tell whether the /// construct was completed or not
  Y0
  ; Build up substitution s from empty string
  s : ""
  ; Pop c, yank into y, loop while that is not equal to "/" and c is nonempty
  W #c & '/ NE Y POc {
   ; If y equals "\"
   I yQ'\
    ; Pop c again and add that character to s
    s .: POc
   ; Else, add y to s
   E s .: y
  }

  ; If the last value yanked was "/", then we have a complete substitution
  ; If not, the code must have run out; skip this branch, and then the outer loop
  ; will terminate
  I yQ'/ {
   ; While pattern is found in code:
   W pNc {
    ; Set flag so only one replacement gets done
    i : 0
    ; Convert p to a regex; replace it using a callback function: if ++i is 1,
    ; replace with s; otherwise, leave unchanged
    c R: Xp {++i=1 ? s p}
   }
  }
 }
 ; Else, output y
 E Oy
}

Try it online! (Note that TIO doesn't give any output when the program is non-terminating, and it also has a time limit. For larger examples and infinite loops, running Pip from the command line is recommended.)

BaCon, 391 387 395 bytes

From the contributions on this page I only got the Python program to work. The others work for some /// samples, or do not work at all. Therefore, I decided to add my version, which is an implementation in BASIC.

To compete in a CodeGolf contest with BASIC is not easy, as BASIC uses long words as statements. The only abbreviation commonly found in BASIC is the '?' sign, which means PRINT.

So the below program may never win, but at least it works with all demonstration code on this Codegolf page and on the Esolangs Wiki. Including all versions of the "99 bottles of beer".

p$=""
r$=""
INPUT i$
WHILE LEN(i$)
t$=LEFT$(i$,1)
i$=MID$(i$,2)
IF NOT(e) THEN
IF t$="\\" THEN
e=1
CONTINUE
ELIF t$="/" THEN
o=IIF(o<2,o+1,0)
IF o>0 THEN CONTINUE
FI
FI
IF o=1 THEN
p$=p$&t$
ELIF o=2 THEN
r$=r$&t$
ELIF o=0 THEN
IF LEN(p$) THEN i$=REPLACE$(i$,p$,r$)
IF NOT(INSTR(t$&i$,"/")) THEN
?t$;
BREAK
ELSE
?LEFT$(i$,INSTR(i$,"/")-1);
i$=MID$(i$,INSTR(i$,"/"))
FI
p$=""
r$=""
FI
e=0
WEND
?i$

Cobra - 226

sig Z as String
def f(l='')
    m=Z(do=[l[:1],l=l[1:]][0])
    n as Z=do
        if'/'<>(a=m())>'',return if(a=='\\',m(),a)+n()
        else,return''
    print n()stop
    p,s=n(),n()
    if''<l
        while p in l,l=l[:l.indexOf(p)+1]+s+l[p.length:]
        .f(l)

J - 181 190 170 char

This was a nightmare. I rewrote it from scratch, twice, because it just kept bugging me. This is a function taking a single string argument, outputting to STDOUT.

(0&$`((2{.{:@>&.>)((j{.]),-i@=`p@.~:~/@[,]}.~#@p+j=.0{p I.@E.])i 5;@}.&,'/';"0;&.>)@.(2<#)@}.[4:1!:2~{:@>@p=.>@{.@[)@((0;(0,:~1 0,.2);'\';&<1 0)<;._1@;:'/'&,)i=. ::](^:_)

To explain, I will break it up into subexpressions.

i =. ::](^:_))
parse =: ((0;(0,:~1 0,.2);'\';&<1 0)<;._1@;:'/'&,)
print =: 4:1!:2~{:@>@p=.>@{.@[
eval  =: 0&$`((2{.{:@>&.>)sub 5;@}.&,'/';"0;&.>)@.(2<#)@}.
sub   =: ((j{.]),-i@=`p@.~:~/@[,]}.~#@p+j=.0{p I.@E.])i

interp =: (eval [ print) @ parse i

Fun facts about this golf:

Example usage:

   f=:(0&$`((2{.{:@>&.>)((j{.]),-i@=`p@.~:~/@[,]}.~#@p+j=.0{p I.@E.])i 5;@}.&,'/';"0;&.>)@.(2<#)@}.[4:1!:2~{:@>@p=.>@{.@[)@((0;(0,:~1 0,.2);'\';&<1 0)<;._1@;:'/'&,)i=. ::](^:_)
   f 'no'
no
   f '/ world! world!/Hello,/ world! world! world!'
Hello, world!
   f '/foo/Hello, world!//B\/\\R/foo/B/\R'
Hello, world!
   f '//'  NB. empty string

   f '/\\/good/\/'
good

APL (133)

{T←''∘{(0=≢⍵)∨'/'=⊃⍵:(⊂⍺),⊂⍵⋄(⍺,N⌷⍵)∇⍵↓⍨N←1+'\'=⊃⍵}⋄⍞N←T⍵⋄p N←T 1↓N⋄r N←T 1↓N⋄''≡N:→⋄∇{⍵≡p:∇r⋄∨/Z←p⍷⍵:∇(r,⍵↓⍨N+≢p),⍨⍵↑⍨N←1-⍨Z⍳1⋄⍵}1↓N}

This is a function that takes the /// code as its right argument.

Ungolfed, with explanation:

slashes←{
   ⍝ a function to split the input string into 'current' and 'next' parts,
   ⍝ and unescape the 'current' bit
   split←''∘{
       ⍝ if the string is empty, or '/' is reached,
       ⍝ return both strings (⍺=accumulator ⍵=unprocessed)
       (0=≢⍵)∨'/'=⊃⍵:(⊂⍺),⊂⍵
       ⍝ otherwise, add current character to accumulator,
       ⍝ skipping over '\'s. (so if '\/' is reached, it skips '\',
       ⍝ adds '/' and then processes the character *after* that.)
       idx←1+'\'=⊃⍵
       (⍺,idx⌷⍵)∇idx↓⍵
   }

   ⍞   next ← split ⍵      ⍝ output stage
   pat next ← split 1↓next ⍝ pattern stage, and eat the '/'
   rpl next ← split 1↓next ⍝ replacement stage, and eat the '/'

   ⍝ if there are no characters left, halt.
   ''≡next:⍬

   ⍝ otherwise, replace and continue.
   ∇{  ⍝ if the input string equals the pattern, return the replacement and loop
       ⍵≡pat:∇rpl

       ⍝ otherwise, find occurences, if there are, replace the first and loop
       ∨/occ←pat⍷⍵:∇(rpl, (idx+≢pat)↓⍵),⍨ (idx←(occ⍳1)-1)↑⍵

       ⍝ if no occurences, return string
       ⍵

   }1↓next
}

Python 2 (236), Python 3 (198?)

from __future__ import print_function
def d(i):
 t=0;p=['']*3+[1]
 while i:
  if'/'==i[0]:t+=1
  else:
   if'\\'==i[0]:i=i[1:]
   p[t]+=i[0]
  i=i[1:]
  print(end=p[0]);p[0]=''
  if t>2:
   while p[1]in i:i=i.replace(*p[1:])
   d(i);i=0

Called as d(r"""/foo/Hello, world!//B\/\\R/foo/B/\R"""). The triple quotes are only needed if the /// program contains newlines: otherwise simple quotes are ok.

EDIT: This interpreter now prints stuff as expected (previously it only printed at the very end, cf. comments). For Python 3, remove the first line (but I don't have Python 3 on my ancient install, so cannot be sure there is no other change).

Perl - 190

$|=1;$/=undef;$_=<>;while($_){($d,$_)=/(.)(.*)/;eval(!$e&&({'/','$a++','\\','$e=1'}->{$d})||('print$d','$b.=$d','$c.=$d')[$a].';$e=0');if($a==3){while($b?s/\Q$b/$c/:s/^/$c/){}$a=0;$b=$c=''}}

Reads /// program from stdin until EOF.