3var is a variant of deadfish which uses three variables called A, B and R. A and B are accumulators, while R is used as a result variable.

In this challenge, you need to make an interpreter for a stripped-down version of this language.

Required Commands:

i   Increments A
d   Decrements A
s   Squares A
p   Prints A
P   Prints the ASCII character of A
>   Sets A to R
a   Increments B
k   Decrements B
m   Squares B
o   Prints B
O   Prints the ASCII character of B
<   Sets B to R
+   Adds A and B, stores in R
-   Subtracts B from A, stores in R
*   Multiplies A and B, stores in R
/   Divides A by B, stores in R
w   Prints R
@   Resets A to 0
#   Resets B to 0
e   Resets R to 0

Everything else (including whitespaces) is ignored.

Clarifications

  • o and p should output without anything after it.
  • Division is integer division.
  • Numbers above 255 and numbers below 0 are supported.
  • 'w' should output a space or a newline after R
  • Division by 0 halts without errors. (No output to STDERR)
  • A, B and R are initially 0

Rules

  • This is so the shortest answer wins.
  • In the event of a tie, the oldest answer wins.
  • The file will be provided through command line arguments or STDIN.
  • Any language is allowed.
  • Eval is allowed.

Test Cases

Hello world! (taken from Esolangs)

iisssaa/>e
maa->e#
aamam->e#
dddddddddddddddddddddddddPiiiiiiiiiiiiiiiiiiiiiiiii
iiiiPiiiiiiiPPiiiP
@#e
iis
aamaaaa
*>P
@#e
iisssaa/>e
maa->e#
aamam->e#
iiiiiiiiiiiiiiiiiiiiiiP
ddddddddP
iiiP
ddddddP
ddddddddP
@#e
iiss
aa*>
iP

Outputs 20spooky22me:

iipois+iis<-<aaaO<OkOOkkkkOP@#iippisa+<m+>PaO

Outputs 9P-1420 100 3Q-1 162 0:

iiispsdPkokmo/w<+w#aaaommO-w@ii*wew

Leaderboards

Here is a Stack Snippet to generate both a regular leaderboard and an overview of winners by language.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

# Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

# Ruby, <s>104</s> <s>101</s> 96 bytes

If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

# Perl, 43 + 2 (-p flag) = 45 bytes

You can also make the language name a link which will then show up in the leaderboard snippet:

# [><>](http://esolangs.org/wiki/Fish), 121 bytes

var QUESTION_ID=63008,OVERRIDE_USER=45220;function answersUrl(e){return"http://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"http://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

  • 1
    @Sp3000 Speaking of it, I recently helped fix a nested loop bug in the interpreter. – LegionMammal978 Nov 5 '15 at 13:33
  • 2
    Additional clarifications for people familiar with Deadfish: dddddp results in -5 and iiiissp results in 256 as you'd expect, rather than 0. – Sp3000 Nov 5 '15 at 13:35
  • 1
    In the test Case What dose r do? its not included on your list of valid commands – JimmyJazzx Nov 5 '15 at 14:17
  • 1
    @JimmyJazzx The testcase is now edited, but in the full 3var language r resets everything. It's the same as @#e. – DJgamer98 Nov 5 '15 at 15:36
  • 1
    Are A, B, and R initialized to 0 when the interpreter starts? – Tony Ennis Nov 6 '15 at 8:33

18 Answers 18

up vote 7 down vote accepted

CJam, 112

q{"+-*/dspP>@ikmoO<#awe"_@#\4<"X2 U|{TU :V;}P?"S/f*'T"( _*T cT;V;0)"2/f+":T;"f+_'Tf/'Uf*+"VS 0:V; "S/++=~}%Pa/0=

Try it online

Explanation:

The program builds an array of pieces of code to be executed, and executes the corresponding piece for each character. Dealing with the division by zero is more difficult, because CJam doesn't yet have a "break" operator. Instead, the program pushes π as a marker (since no floating point number can appear otherwise), and at the end it keeps only the output before the first π.
Also, the program uses variables T, U and V instead of A, B and R, because they are preinitialized with 0 in CJam.

q            read the input
{…}%         transform each character
  "…"        push a string with all the 3var commands, in a convenient order
  _@         duplicate the string and bring the current character to the top
  #          find the index of the character in the string (or -1 if not found)
  \          swap with the other string copy
  4<         keep only the first 4 characters ("+-*/")
  "…"        push a string that will be used for implementing those operators
  S/         split by space into ["X2" "U|{TU" ":V;}P?"]
  f*         join with each of '+', '-', '*', '/' -> 4 strings
  'T         push the 'T' character
  "…"        push a string with implementations of commands involving A
  2/         split into pieces of length 2
  f+         prepend 'T' to each piece
  ":T;"f+    append ":T;"to each piece
  _          duplicate the array (containing full A-command implementations)
  'Tf/       split each piece by the 'T' character
  'Uf*       join each split piece using the 'U' character
              practically replacing 'T' with 'U'
              to obtain B-command implementations
  +          concatenate the 2 arrays
  "…"        push a string containing implementations for "w" and "e", 
              separated by space; the string also ends with a space
  S/         split by space, obtaining the implementations for "w" and "e"
              and an empty string, for handling unrecognized characters
  ++         concatenate with the A and B command and +-*/ implementations
  =          get the corresponding string implementing the current command
              if the character was not found, -1 will get the empty string (no-op)
  ~          execute it
Pa/          split the array of results by the π separator
0=           get the first piece (before the first π, if any)

The actual command implementations (constructed by the program):

+: X2+U|{TU+:V;}P?    if (1+2)|U != 0, set V=T+U, else push π
-: X2-U|{TU-:V;}P?    if (1-2)|U != 0, set V=T-U, else push π
*: X2*U|{TU*:V;}P?    if (1*2)|U != 0, set V=T*U, else push π
/: X2/U|{TU/:V;}P?    if (1/2)|U != 0, set V=T/U, else push π
d: T( :T;             set T=T-1
s: T_*:T;             set T=T*T
p: TT :T;             push T and set T=T (no-op)
P: TcT:T;             push T converted to character and set T=T (no-op)
>: T;V:T;             push and pop T (no-op) and set T=V
@: T;0:T;             push and pop T (no-op) and set T=0
i: T):T;              set T=T+1
k: U( :U;             set U=U-1
m: U_*:U;             set U=U*U
o: UU :U;             push U and set U=U (no-op)
O: UcU:U;             push U converted to character and set U=U (no-op)
<: U;V:U;             push and pop U (no-op) and set U=V
#: U;0:U;             push and pop U (no-op) and set U=0
a: U):U;              set U=U+1
w: VS                 push V and a space
e: 0:V;               set V=0
(default): (empty)    no-op

JavaScript (ES7) 208 213 223 237 241 311

Edit3 Copying each other, I and Dendrobium are collapsing together.

Edit2 Using EcmaScript 7 to save just 2 bytes, joined together handling of A and B

Edit Following the changes of the rules.

Note, I added the r command that is not required in the question, just to run the old example Hello world

V=p=>(o='',[for(c of'@#e'+p)eval('++x,--x,x*=x,x=R,x=0,o+=x,o+=String.fromCharCode(x),R=A+B,R=A-B,R=A*B,R=A/B|0,R=0,o+=R+` `'.replace(/x/g,(k='ids>@pP+-*/ewakm<#oO'.indexOf(c))<13?'A':'B').split`,`[k%13])],o)

// More readable
U=p=>(
  o='',
  [for(c of'@#e'+p)
   eval(
   '++x,--x,x*=x,x=R,x=0,o+=x,o+=String.fromCharCode(x),R=A+B,R=A-B,R=A*B,R=A/B|0,R=0,o+=R+` `'
   .replace(/x/g,(k='ids>@pP+-*/ewakm<#oO'.indexOf(c))<13?'A':'B')
   .split`,`[k%13])]
  ,o
)

// ES6, no array comprehension, 2 bytes more
W=p=>
  [...'@#e'+p].map(c=>
    eval(
    '++x,--x,x*=x,x=R,x=0,o+=x,o+=String.fromCharCode(x),R=A+B,R=A-B,R=A*B,R=A/B|0,R=0,o+=R+` `'
    .replace(/x/g,(k='ids>@pP+-*/ewakm<#oO'.indexOf(c))<13?'A':'B')
    .split`,`[k%13]),
    o=''
  )&&o

// Test
function test() 
{ 
  OUT.innerHTML = V(I.value) 
}

test()
textarea { width: 75%; height: 8em }
Test program <button onclick="test()">Run</button><br><textarea id=I>
iisssaa/>e
maa->e#
aamam->e#
dddddddddddddddddddddddddPiiiiiiiiiiiiiiiiiiiiiiiii
iiiiPiiiiiiiPPiiiP
@#e
iis
aamaaaa
*>P
@#e
iisssaa/>e
maa->e#
aamam->e#
iiiiiiiiiiiiiiiiiiiiiiP
ddddddddP
iiiP
ddddddP
ddddddddP
@#e
iiss
aa*>
iP
</textarea><br>Output<pre id=OUT></pre>

  • You could save 2 bytes by removing A=B=R=0, and setting those variables using the actual 3var functions before processing the string, like: [...'@#e'+p]. – insertusernamehere Nov 5 '15 at 21:13
  • 2
    @insertusernamehere thx, I love this one – edc65 Nov 5 '15 at 21:20

GNU Sed (with eval option to evaluate dc expression), 254

Deadfish maps fairly nicely to a subset of dc. So we use sed to do that mapping:

s/^/@#e/
s/\+/AB+r/g
s/-/AB-r/g
s/\*/AB*r/g
s|/|AB/r|g
s/a/B1+b/g
s/i/A1+a/g
s/d/A1-a/g
s/s/Ad*a/g
s/p/An/g
s/P/AP/g
s/>/Ra/g
s/k/B1-b/g
s/m/Bd*b/g
s/o/Bn/g
s/O/BP/g
s/</Rb/g
s/w/Rn/g
s/@/0a/g
s/#/0b/g
s/e/0r/g
s/[ABR]/l&/g
s/[abr]/s\u&/g
s/.*/dc -e'&'/e

Javascript ES6 ES7, 217 215 213 208 bytes

f=s=>(O='',[for(c of'@#e'+s)eval('v=0,++v,--v,v*=v,O+=v,O+=String.fromCharCode(v),v=R,R=0,R=A+B,R=A-B,R=A*B,R=A/B|0,O+=R+" "'.replace(/v/g,'AB'[(x='@idspP>e+-*/w#akmoO<'.indexOf(c))/13|0]).split`,`[x%13])],O)

Ungolfed

f=s=>(
    O='',                                                                   // initialize output to empty string
    [for(c of'@#e'+s)eval(                                                  // initialize A, B, and R, loop over all chars in input, eval returned function string
        'v=0,++v,--v,v*=v,O+=v,O+=String.fromCharCode(v),v=R,R=0,R=A+B,R=A-B,R=A*B,R=A/B|0,O+=R+" "' // operation list
            .replace(/v/g,'AB'[(x='@idspP>e+-*/w#akmoO<'.indexOf(c))/13|0]) // replace all instances of v with either A or B
            .split`,`[x%13])],                                              // split ops list, select corresponding function string
    O)                                                                      // return output string
  • 1
    +1 Not far from mine, but better. Now I'll have to try something different – edc65 Nov 5 '15 at 19:10
  • 1
    You could save 2 bytes by removing A=B=R=0, and setting those variables using the actual 3var functions before processing the string, like: for(c of '@#e'+s). – insertusernamehere Nov 5 '15 at 21:14
  • 1
    @insertusernamehere Aha, thats pretty clever, thanks! – Dendrobium Nov 5 '15 at 21:20
  • Revising your answer, I see that you don't output a newline for command w – edc65 Nov 6 '15 at 15:40
  • @edc65 One of the clarification bullets states "'w' should output a space or a newline after R" – Dendrobium Nov 6 '15 at 16:41

APL, 191

{0::→⋄A B R←0⋄{⍵∊G←'aikdms<>':⍎'AB'[1+2|G⍳⍵],'+-*∘'[M],'←','112R'[M←⌈2÷⍨G⍳⍵]⋄⍵∊G←'PpOo':⍞←⎕UCS⍣(2|G⍳⍵)⊢A B[⌈2÷⍨G⍳⍵]⋄⍵∊G←'+-*/':R∘←⌊A(⍎'+-×÷'[G⍳⍵])B⋄⍵∊G←'@#e':⍎'ABR'[G⍳⍵],'∘←0'⋄⍵='w':⍞←R}¨⍵}

This is a function that takes the program as an argument, e.g:

      ∆3var←{0::→⋄A B R←0⋄{⍵∊G←'aikdms<>':⍎'AB'[1+2|G⍳⍵],'+-*∘'[M],'←','112R'[M←⌈2÷⍨G⍳⍵]⋄⍵∊G←'PpOo':⍞←⎕UCS⍣(2|G⍳⍵)⊢A B[⌈2÷⍨G⍳⍵]⋄⍵∊G←'+-*/':R∘←⌊A(⍎'+-×÷'[G⍳⍵])B⋄⍵∊G←'@#e':⍎'ABR'[G⍳⍵],'∘←0'⋄⍵='w':⍞←R}¨⍵}
      ∆3var 'iipois+iis<-<aaaO<OkOOkkkkOP@#iippisa+<m+>PaO'
20spooky22me

It can be used as an anonymous function, I just gave it a name for clarity.

Explanation:

  • 0::→: if an error occurs (say, division by zero), halt without printing the error message
  • A B R←0: initialize variables
  • {...}¨⍵: for each command:

    • ⍵∊G←'aikdms<>': If the command is one of the type var ← fn(var, x), find the proper fn and x, substitute them in and then evaluate it:
      • : evaluate
      • 'AB'[1+2|G⍳⍵: A if the position of in 'aikdms<>' is even, B otherwise.
      • '+-*∘'[M]: add, subtract, power, or nothing, depending on M (defined later)
      • '←': assign
      • '112R'[M←⌈2÷⍨G⍳⍵]: 1 (for adding and subtracting), 2 (for power), and R (for nothing, i.e. it just sets the variable to R), depending on M, which is whether the command belongs to the first, second, third or fourth pair.
    • ⍵∊G←'PpOo': output:
      • ⍞←: output
      • ⎕UCS⍣(2|G⍳⍵): ASCII (well, Unicode) or number depending on whether the command was on an odd or even position in PpOo,
      • ⊢A B[⌈2÷⍨G⍳⍵]: A or B, depending on whether the command was in the first or second half.
    • ⍵∊G←'+-*/: math:
      • R∘←⌊A(⍎'+-×÷'[G⍳⍵])B: set R to the result of applying the given operator to A and B.
    • ⍵∊G←'@#e': reset:

      • : evaluate
      • 'ABR'[G⍳⍵]: select the right variable
      • '∘←0': set to zero
    • ⍵='w':⍞←R: if the command is w, output R.

C, 253 241 bytes

#define _ c--?c--?c--
k="id@s>ak#m<e+-*/wpoPO",*p;main(c,a,b,r){for(a=b=r=0;~c;c=getchar())c=strchr(k,c),c&&(c-=k,p=&a+c/5,*p=_?_?_?_?_?printf(c?c<3?"%d":"%c":"%d ",c?c%2?a:b:r):a/(b?b:exit()):a*b:a-b:a+b:0:r:b*b:0:b-1:b+1:r:a*a:0:a-1:a+1);}

This code uses the string id@s>ak#m<e+-*/wpoPO as a table of commands. The string is arranged according to the destination of the calculated expression. It just so happens that there are 5 commands that update each of the variables:

  • id@s> - update a
  • ak#m< - update b
  • e+-*/ - update r
  • wpoPO - update... the memory location after a, b and r. I hope it's not too important :)

So after locating the input character in the string of commands, its index is repeatedly decreased, and depending on when it reaches 0, an expression is chosen.

If it doesn't reach 0 after 15 subtractions, it's a printf with some properly chosen arguments.

Also, when dividing, it avoids division by 0 by calling exit().

This code should be compiled without optimizations, because it assumes that a, b and r are located at adjacent addresses on stack (not in CPU registers).

Also, it should be compiled in 32-bit mode, because it converts pointers to integers and vice-versa.

VBA, 484, 453 380 Bytes

To long to win but A super Simple way of doings things, Nothing Fancy just good old Select Case

Adding in Integer Division and Div 0 Error Handling Ate a lot of Bytes
Removed Error Handling as it seems the normal error handling results in the same functionality. Fixed Int Division to work as expected. Was also shorter.

Function Y(x)
For Z=1 To Len(x)
w=Mid(x,Z,1)
Select Case w
Case"i":A=A+1
Case"d":A=A-1
Case"s":A=A^2
Case"p":Y=Y &A
Case"P":Y=Y &Chr(A)
Case">":A=R
Case"a":B=B+1
Case"k":B=B-1
Case"m":B=B^2
Case"o":Y=Y &B
Case"O":Y=Y &Chr(B)
Case"<":B=R
Case"+":R=A+B
Case"-":R=A-B
Case"*":R=A*B
Case"/":R=A\B
Case"w":Y=Y &R &vbCrLf
Case"@":A=0
Case"#":B=0
Case"e":R=0
End Select
Next
End Function

Thanks to Henrik Ilgen for saving 31 104 bytes

  • 2
    Not entirely sure if this works in VBA (it does in VB6), but you could save a bunch of bytes by using DefInt A-Z, thereby omitting the need to explicitly declaring A, B and R as Integer: DefInt A-Z:Dim A, B, R. It might as well work to just assign them, without declaration: A=0:B=0:R=0. The calculations should work on variants, too. – Henrik Ilgen Nov 6 '15 at 11:37
  • @HenrikIlgen thanks alot, Forgot about DefInt Not Something I use everyday but going to be super useful golfing VBA in the future. Sadly A=0 will just make it act like a double from my tests. There for making iiiaa/w produce a decimal result rather then a whole. – JimmyJazzx Nov 6 '15 at 15:31
  • 1
    Try \ for integer division ;) – Henrik Ilgen Nov 9 '15 at 8:34
  • 1
    You can condense it down to 405 bytes if you make it a function and use the return value to "build" the result, assuming you don't have to actually print the result :) – Henrik Ilgen Nov 9 '15 at 8:58

PHP, 310 bytes

First time in my life using eval:

for($A=$B=$R=0;$c=$argv[1][$i++];)eval([i=>'++$A',d=>'--$A',s=>'$A*=$A',p=>'echo$A',P=>'echo chr($A)','>'=>'$A=$R',a=>'++$B',k=>'--$B',m=>'$B*=$B',o=>'echo$B',O=>'echo chr($B)','<'=>'$B=$R','+'=>'$R=$A+$B','-'=>'$R=$A-$B','*'=>'$R=$A*$B','/'=>'$R=$A/$B',w=>'echo$R','@'=>'$A=0','#'=>'$B=0',e=>'$R=0'][$c].';');

Takes the first command line input:

php 3var.php "iipois+iis<-<aaaO<OkOOkkkkOP@#iippisa+<m+>PaO"

Output from the examples:

Hello world!
20spooky22me

C, 357

Macros FTW!

(Who am I kidding - c will never win this one)

#define X(p,e) case p:e;break;
#define Y(p,a,b,c,d,e,f) X(a,p++)X(b,p--)X(c,p*=p)X(d,printf("%d",p))X(e,printf("%c",p%256))X(f,p=R)
A,B,R;main(int C,char**V){for(;*V[1];)switch(*V[1]++){Y(A,'i','d','s','p','P','>')Y(B,'a','k','m','o','O','<')X('+',R=A+B)X('-',R=A-B)X('*',R=A*B)X('/',R=A/(B?B:exit(0),1))X('w',printf("%d",R))X('@',A=0)X('#',B=0)X('e',R=0)}}

JavaScript (ES6), 293 262 bytes

f=x=>(o="",a=b=r=0,p=String.fromCharCode,[...x].map(c=>{if(eval(`m={i_a++,d_a--,s_a*=a,p_o+=a,P_o+=p(a),">"_a=r,a_b++,k_b--,m_b*=b,o_o+=b,O_o+=p(b),"<"_b=r,"+"_r=a+b,"-"_r=a-b,"*"_r=a*b,"/"_r=a/b|0,w_o+=r,"@"_a=0,"#"_b=0,e_r=0}[c]`.replace(/_/g,":_=>")))m()}),o)

Usage

f(`iisssaa/>e
maa->e#
aamam->e#
dddddddddddddddddddddddddPiiiiiiiiiiiiiiiiiiiiiiiii
iiiiPiiiiiiiPPiiiP
@#e
iis
aamaaaa
*>P
@#e
iisssaa/>e
maa->e#
aamam->e#
iiiiiiiiiiiiiiiiiiiiiiP
ddddddddP
iiiP
ddddddP
ddddddddP
@#e
iiss
aa*>
iP`)

=> "Hello world!"

Explanation

There are a few details of the language that I'm unsure about (integer sizes, handling unrecognised characters, etc) but this solution seems to work sufficiently and ignores whitespace characters like the new-lines in the test case.

f=x=>(
  o="",                           // o = output string
  a=b=r=0,
  p=String.fromCharCode,
  [...x].map(c=>{                 // iterate through each character
    if(                           // if used for NOP characters (whitespace)

      // Functions:
      eval(`m={
        i_a++,
        d_a--,
        s_a*=a,
        p_o+=a,
        P_o+=p(a),
        ">"_a=r,
        a_b++,
        k_b--,
        m_b*=b,
        o_o+=b,
        O_o+=p(b),
        "<"_b=r,
        "+"_r=a+b,
        "-"_r=a-b,
        "*"_r=a*b,
        "/"_r=a/b|0,
        w_o+=r,
        "@"_a=0,
        "#"_b=0,
        e_r=0
      }[c]`.replace(/_/g,":_=>")) // dynamically add common characters

    )m()                          // execute the function
  }),
  o                               // return the output string
)
  • 1
    I think you could save a few bytes by enclosing the dictionary within an eval whilst replacing all :$=> with $, then adding a replace for the string. – Conor O'Brien Nov 5 '15 at 14:44

Simplex v.0.8, 211 bytes

(UTF-8 encoded.)

h@u]u2ƒ§I]ƒ§M]ƒ§^O]ƒ§o]ƒ§s]ƒ§@]ƒ§Ah]ƒ§Sh]ƒ§Mh]ƒ§Vh]ƒ§&oh]ƒðI]ƒðM]ƒð^O]ƒðo]ƒðs]ƒð@]ƒðAh]ƒðSh]ƒðMh]ƒðVh]ƒð&oh]ƒ§z]ƒðz]ƒuz@]{"idspP>akmoO<+-*/w@#e"RlyG^u·u†vøÏ}

Explanation

Since this is the longest simplex program I have thus far written, I will generally explain how this works in bullet points.

  • h@u] - defines macro 0. This macro simply writes to the register and makes a lambda return nothing.
  • u2 - goes to the above strip and sets the current byte to 2; this defines the arity of the lambdas to be defined.
  • ƒ - begin lambda expression; once completed by ], will push lambda function to the lambda stack. It acts by taking (arity) cells from the pointer into its local strip, and, after completion, will set its local strip to the taken cells, unless the current byte is unwritten. The pointer is not affected. Macro 0 allows for a function to return without modifying anything in the strip.
  • § - moves to the first written cell in the current strip i.e. A.
  • ð - moves to the last written cell in the current strip i.e. B.
  • {"idspP>akmoO<+-*/w@#e"RlyG^u·u†vøÏ}
    • {...Ï} repeat until the input stack is empty
    • "idspP>akmoO<+-*/w@#e" - the commands
    • Rly - put the strip into a tuple
    • G^u - the index of the input in the tuple
    • · - load the current byteth into the lambda evaluator
    • uRL - goes to the strip holding A and B (writes A and B if they don't exist)
    • - executes lambda (this is the lambda evaluator)
    • - resets below strip

phew I'm impressed. It's long for Simplex, but short for everything else. ;)

Minkolang 0.11, 222 bytes

I'm sure this can be golfed further, but it was fun. Also, first Minkolang interpreter of another language!

>I3&000V$Vod?.45*[di1q=5&]x00wx1i2+kw
idspP>akmoO<+-*/w@#e
vr1+r
vr1-r
vr2;r
v0c1$((dl%"0"+$rl:d)$Ok
v0cO
vrx0cr
v1g1+1G
v1g1-1G
v1g2;1G
v1c1$((dl%"0"+$rl:d)$Ok
v1cO
v1gxd1G
vx$d+
vx$d-
vx$d*
vx$dd?.:
vdN
vrx0r
v1gx01G
vx0

Try it here.

Explanation

>I3&000                    Initializes A, B, and R if they don't already exist.
V$V                        An idiom that ensures no time-travel.
od?.                       Reads in a character and halts if input is empty.
45*[di1q=5&]               Looks through the second row for a matching character.
            x00w           Jumps back to beginning if no match.
                x1i2+kw    Jumps to corresponding line upon match.

The rest of the lines are pretty simple, perhaps with the exception of those with 1$((dl%"0"+$rl:d)$Ok, which is an idiom that prints a number without a trailing space. (I haven't yet implemented the convert-this-number-to-a-string functionality, which will be 1Z.) Oh, yes, they all have a v at the beginning, which takes it back to the beginning.

GNU Sed (with eval option to evaluate dc expression), 289

Inspired by Digital Trauma who sadly failed to realize, that a) illegal characters need to be ignored, b) dc needs the entire converted program in one argument and c) division by 0 must terminate the program without error.

If all these rules wouldn't apply, my solution would only be 235 bytes long ;)

H
$!d
x
s/^/@#e/
s:[^-a><de/+i*k@w#opsmOP]::g
s:[e@#]:0&:g
s:[-*+/pPid]:Q&:g
s:[-kaoO*+/]:H&:g
s:[><w]:Z&:g
s:[diak]:1&:g
s:s:Q2^>:g
s:m:H2^<:g
s:[-+*/]:&e:g
s:[ia]:+&:g
s:[dk]:-&:g
y:Oop:Pnn:
s:w:n32P:g
s:[id>@]:sq:g
s:e:sz:g
s:[#<ka]:sh:g
s:[QHZ]:l\l&:g
s:/:d0=t/:g
s/.*/dc -e'[q]st&'/e

AWK, 311 309

func p(z,f,x){if(c~z)printf"%"f,x}BEGIN{FS=z}{for(i=0;++i<=NF;){c=$i;A=c~/i/?A+1:c~/d/?A-1:c~/s/?A*A:c~/@/?0:c~/>/?R:A;p("p","d",A);p("P","c",A);B=c~/a/?B+1:c~/k/?B-1:c~/m/?B*B:c~/#/?0:c~/</?R:B;p("o","d",B);p("O","c",B);R=c~/+/?A+B:c~/-/?A-B:c~/*/?A*B:c~/e/?0:R;if(c~"/")if(B)R=A/B;else exit;p("w","d ",R)}}

TIL parenthesis were not needed around printf. 2 bytes saved!

Ungolfed version for easier reading:

func p(z,f,x) {
    if(c~z) printf "%"f,x
}

BEGIN {
    FS=z
}

{
    for(i=0;++i<=NF;){
        c=$i;
        A=c~/i/?A+1:c~/d/?A-1:c~/s/?A*A:c~/@/?0:c~/>/?R:A;
        p("p","d",A);
        p("P","c",A);
        B=c~/a/?B+1:c~/k/?B-1:c~/m/?B*B:c~/#/?0:c~/</?R:B;
        p("o","d",B);
        p("O","c",B);
        R=c~/+/?A+B:c~/-/?A-B:c~/*/?A*B:c~/e/?0:R;
        if(c~"/")if(B)R=A/B;else exit;
        p("w","d ",R)
    }
}

Python 2, 272

import sys;a,b,r=0,0,0;exec';'.join(filter(None,map(dict(x.split(':')for x in"a:b+=1|@:a=0|#:b=0|/:r=a/b|d:a-=1|PXchr(a)|i:a+=1|-:r=a-b|k:b-=1|*:r=a*b|m:b*=b|oXb|pXa|s:a*=a|OXchr(b)|wXr|+:r=a+b|e:r=0|<:b=r|>:a=r".replace('X',':print ').split('|')).get,sys.stdin.read())))
  • This doesn't work. Your print statements include a trailing newline. – Griffin Nov 10 '15 at 19:39

Ruby, 199 bytes

a=b=r=0
$<.read.chars{|c|eval %w{a+=1
a-=1
a*=a
$><<a
$><<a.chr
a=r
b+=1
b-=1
b*=b
$><<b
$><<b.chr
b=r
r=a+b
r=a-b
r=a*b
b==0&&exit||r=a/b
puts\ r
a=0
b=0
r=0
p}["idspP>akmoO<+-*/w@#e".index(c)||-1]}

5 bytes can be saved by removing .read from the second line if you can tolerate a warning being printed to stderr on newer version of Ruby.

Python, 244

import sys;P=sys.stdout.write;A=B=R=0;exec';'.join(filter(None,map(dict(zip('idspP>akmoO<+-*/w@#e','A+=1 A-=1 A**=2 P(`A`) P(chr(A)) A=R B+=1 B-=1 B**=2 P(`B`) P(chr(B)) B=R R=A+B R=A-B R=A*B R=A/B print(R) A=0 B=0 R=0'.split())).get,input())))

Bit late but I wanted to have a go.

Prolog, 759 bytes

Not the shortest program, but at least it's structured and readable.

q(A,B,R,i,X,B,R):-X is A+1.
q(A,B,R,d,X,B,R):-X is A-1.
q(A,B,R,s,X,B,R):-X is A*A.
q(_,B,R,>,R,B,R).
q(A,B,R,a,A,X,R):-X is B+1.
q(A,B,R,k,A,X,R):-X is B-1.
q(A,B,R,m,A,X,R):-X is B*B.
q(A,_,R,<,A,R,R).
q(A,B,_,+,A,B,X):-X is A+B.
q(A,B,_,-,A,B,X):-X is A-B.
q(A,B,_,*,A,B,X):-X is A*B.
q(_,0,_,/,_,_,_):-!,1=2.
q(A,B,_,/,A,B,X):-B>0,X is A/B.
q(_,B,R,@,0,B,R).
q(A,_,R,#,A,0,R).
q(A,B,_,e,A,B,0).
q(A,B,R,p,A,B,R):-write(A).
q(A,B,R,'P',A,B,R):-A>0,format('~c',A).
q(A,B,R,'P',A,B,R):-write(A).
q(A,B,R,o,A,B,R):-write(B).
q(A,B,R,'O',A,B,R):-format('~c',B).
q(A,B,R,'O',A,B,R):-write(B).
q(A,B,R,w,A,B,R):-write(R),nl.
q(A,B,R,_,A,B,R).
f([],_,_,_).
f([H|T],A,B,R):-q(A,B,R,H,X,Y,Z),f(T,X,Y,Z),!.
p(X):-atom_chars(X,Y),f(Y,0,0,0).

Example input

p('iipois+iis<-<aaaO<OkOOkkkkOP@#iippisa+<m+>PaO').

Try it out online here

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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