Interpret /// (pronounced 'slashes')

2018-03-09 08:42:58

Because we can't get enough of esoteric language golfs, can we?

///—pronounced slashes—is a fun little language based on the s/// regex-replacement function of Perl fame. It contains only two special characters, slash / and backslash \. You can find a full article on it at the esolangs wiki, but I will reproduce a description of the language below, as well as some examples.

In short, it works by identifying /pattern/repl/rest in the program and making the substitution as many times as possible. No characters are special except / and \: / demarcates patterns and replacements in the program, while \ allows you to insert literal / or \ characters into your code. Notably, these are not regular expressions, just plain string substitutions.

Your challenge is to produce an interpreter for the /// language, as either a program reading STDIN or a function taking a string argument, in as few characters as possible.

You may use any language except for /// itself. You may not use

  • 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:⍬

    ⍝ otherwis

    2018-03-09 08:52:37
  • 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

    i (short for iterate) is an adverb. It takes a verb argument on the left and returns a verb (f)i, which when applied to an argument, applies f repeatedly to the argument until one of two things happens: it finds a fixed point (y = f y), or it throws an error. The fixed-point behaviour is inherent to ^:_, and ::] does the error handli

    2018-03-09 09:08:56
  • 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.

    2018-03-09 09:23:29
  • 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).

    2018-03-09 09:24:46
  • C++: Visual C++ 2013 = 427, g++ 4.9.0 = 446

    This will never win but since I have decided that all my future software projects will be be written in this awesome language I needed an intepreter for it and I figured I might as well share the one I made...

    The difference in score is that Visual C++ doesn't need the first include but g++ does. The score assumes that line endings count as 1.

    #include

    #include

    #define M(x)memset(x,0,99);

    #define P o[i])

    #define N(x)else if(n

    #define O (o[++i]):(P==47?n++:

    #define S std::string

    int main(int n,char**m){S o=m[1];char p[99],*q=p,r[99],*s=r;M(p)M(r)for(int i=0,t;i<=o.size();++i){if(!P;N(3)putchar O putchar(P;N(4)*q++=O(*q++=P;N(5)*s++=O(*s++=P;if(n>4){for(;;){if((t=o.find(p,i+1))==S::npos)break;o=o.substr(0,t)+r+o.substr(t+strlen(p));}M(p)M(r)n=2;q=p;s=r;}}}

    2018-03-09 09:32:21
  • 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

    2018-03-09 09:42:26
  • 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''

    while p in l,l=l[:l.indexOf(p)+1]+s+l[p.length:]

    .f(l)

    2018-03-09 09:44:10
  • 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

    2018-03-09 09:47:11