Simulate a Minsky Register Machine (I)

Perl, 166

@p=<>;/=/,$_{$`}=$' for split$",pop@p;$o='\w+';(map{($r
,$o,$,,$b)=$'=~/".*?"|\S+/g if/^$o :/}@p),$_=$o=($_{$r}
+=','cmp$o)<0?do{$_{$r}=0;$b}:$,until/"/;say for eval,%_

Run with perl -M5.010 file.

It started wildly different, but I'm afraid it converged with the Ruby solution in many areas towards the end. Seems like Ruby's advantage is "no sigils", and Perl's "better regex integration".

A bit of detail from the innards, if you don't read Perl:

  • @p=<>: read the whole machine description to @p
  • /=/,$_{$`}=$' for split$",pop@p: for each (for) assignment (split$") in the last machine description line (@p), locate equal sign (/=/) then assign value $' to hask %_ key $`
  • $o='\w+': initial state would be first one to match Perl regex "word characters"
  • until/"/: loop until we reach a termination state:
    • map{($r,$o,$,,$b)=$'=~/".*?"|\S+/g if/^$o :/}@p: loop on machine description @p: when we're on the line matching current state (if/^$o :/), tokenize (/".*?"|\S+/g) the rest of the line $' to variables ($r,$o,$,,$b). Trick: the same variable $o if used initially for label name and subsequently for operator. As soon as the label matches, the operator overrides it, and as a label can't (reasonably) be named + or -, it never matches again.
    • $_=$o=($_{$r}+=','cmp$o)<0?do{$_{$r}=0;$b}:$,:
      - adjust target register $_{$r} up or down (ASCII magic: ','cmp'+' is 1 whereas ','cmp'-' is -1);
      - if the result is negative (<0?, can only happens for -)
      - then stay at 0 ($_{$r}=0) and return second label $b;
      - else return first (possibly sole) label $,
    • BTW, it's $, instead of $a so it can be glued to next token until with no whitespace in between.
  • say for eval,%_: dump report (eval) and contents of registers in %_

Ruby 1.9, 214 212 211 198 195 192 181 175 173 175

*s,k=*$<
a,=s
b=Hash.new 0
eval k.gsub /(\w+)=/,';b["\1"]='
loop{x,y,r,o,t,f=a.scan /".*?"|\S+/
l=(b[r]-=o<=>?,)<0?(b[r]=0;f):t
l[?"]&&puts(eval(l),b)&exit
a,=s.grep /^#{l} /}

Haskell, 444 characters

(w%f)(u@(s,v):z)|s==w=(s,f+v):z|t=u:(w%f)z
(w%f)[]=[(w,f)]
p#(a:z)|j==a=w p++[j]&z|t=(p++[a])#z;p#[]=w p
p&(a:z)|j==a=p:""#z|t=(p++[a])&z
c x=q(m!!0)$map((\(s,_:n)->(s,read n)).break(=='=')).w$last x where
 m=map(""#)$init x
 q[_,_,r,"+",s]d=n s$r%1$d
 q[_,_,r,_,s,z]d|maybe t(==0)(lookup r d)=n z d|t=n s$r%(-1)$d
 n('"':s)d=unlines[s,d>>=(\(r,v)->r++'=':shows v" ")]
 n s d=q(filter((==s).head)m!!0)d
main=interact$c.lines
t=1<3;j='"';w=words

Man, that was hard! Proper handling of messages with spaces in them cost over 70 characters. Output formatting to be more "human readable", and match the examples cost another 25.


  • Edit: (498 -> 482) various small in-linings, and some of @FUZxxl's suggestions
  • Edit: (482 -> 453) switch back using actual numbers for the registers; many golf tricks applied
  • Edit: (453 -> 444) inlined output formatting and initial value parsing