45
\$\begingroup\$

In this challenge you are going to write an interpreter for a simple language I've made up. The language is based on a single accumulator A, which is exactly one byte in length. At the start of a program, A = 0. These are the languages instructions:

!: Inversion

This instruction simply inverts every bit of the accumulator. Every zero becomes a one and every one becomes a zero. Simple!

>: Shift Right

This instruction shifts every bit in A one place to the right. The leftmost bit becomes a zero and the rightmost bit is discarded.

<: Shift Left

This instruction shifts every bit in A one place to the left. The rightmost bit becomes a zero and the leftmost bit is discarded.

@: Swap Nybbles

This instruction swaps the top four bits of A with the bottom four bits. For example, If A is 01101010 and you execute @, A will be 10100110:

 ____________________
 |                  |
0110 1010    1010 0110
      |_______|

That's all the instructions! Simple, right?

Rules

  • Your program must accept input once at the beginning. This will be a line of code. This is not an interactive interpreter! You can only accept input once and do not have to loop back to the start once that line has been executed.
  • Your program must evaluate said input. Every character that is not mentioned above is ignored.
  • Your program should then print out the final value of the accumulator, in decimal.
  • Usual rules for valid programming languages apply.
  • Standard loopholes are disallowed.
  • This is , smallest byte count wins.

Here are some small programs to test out your submissions. Before the arrow is the code, after it is the expected result:

  • ! -> 255
  • !>> -> 63
  • !<@ -> 239
  • !nop!&6*! -> 255

Enjoy!

\$\endgroup\$
3
  • \$\begingroup\$ I'm presuming from ! -> 255 that we're to use 8 bits per byte here? The question isn't explicit. \$\endgroup\$ Commented Sep 9, 2015 at 12:10
  • 8
    \$\begingroup\$ @TobySpeight A byte, by definition, is 8 bits. \$\endgroup\$ Commented Feb 15, 2016 at 0:30
  • \$\begingroup\$ @hyperneutrino traditionally that wasn't always the case; "byte" was often synonymous with "word", referring to whatever the word size of a given machine was. it doesn't hurt to be explicit about the bit count! \$\endgroup\$ Commented Jul 19 at 13:37

43 Answers 43

1
2
1
\$\begingroup\$

Python 2.7.3, 104 bytes

Having code in strings to be evaluated looks pretty dirty, but works :D

a=0
for c in raw_input():a=eval({'!':'~a','<':'a<<1','>':'a>>1','@':'a<<4|a>>4'}.get(c,'a'))&255
print a

Here's the output (and input actually..)

And yes, it's really running on a RaspberryPi :)

Example output

\$\endgroup\$
1
\$\begingroup\$

brainfuck, 316 bytes

->,[>++++[<-------->-]<-[>>]<[>>>>+[->-<]>[-<+>]<<<]++++[<------->-]<+[>>]<[>>>>[->++<]>[-<+>]<<<]<--[>>]<[>>>>[->+>+<<]>>[->>--[--<->]<-<]>[[-]<<->>]<<[--<+>]<<<]<--[>>]<[>>>>[->+>+<<]>>[->>++++[-<++++>]<<]>[->+>+<<]>>[>++++[<---->-]>+<<]>>[-<<<<<<->>>>>>]<<<<<<[>++++[<---->-]>+<<]>>>[-<+>]<[-<<<+>>>]<<<<<]<,]>>>.

Try it online!

Explanation:

->, input to cell 1
[
>++++[<-------->-]<- sub 33= now will be 0 if invert instruction (cell 1)
[>>] if not move to cell 3
<[    runs if !
>>>>+[->-<]>[-<+>]<<<  invert cell 4 to cell 5 back to 4 end 2
]
now ends on cell 2 either way

++++[<------->-]<+ sub 27
[>>]
<[ runs if shl
>>>>[->++<] shl cell 4 to cell 5
>[-<+>]< back to 4
<< to 2
]
ends on 2

<-- sub 2
[>>]
<[    runs if shr (hard)
>>>>[->+>+<<] copy 4 to cell 5 and 6
>>[->>--[--<->]<-<] get evenness of 6 to 7
>[ if odd
[-] zero cell 7
<<->> sub cell 5
]
ends cell 7
<<[--<+>]<<< shr cell 5 to 4
]
ends cell 2 again

<-- sub 2
[>>]
<[ if @ (also very hard)
>>>>[->+>+<<] copy 4 to 5 and 6 (end on 4)
>>[->>++++[-<++++>]<<] mul cell 6 by 16 to 7
>[->+>+<<] copy 7 to 8 and 9
>>[>++++[<---->-]>+<<] div 9 by 16 to 11= chopping off upper bits
>>[-<<<<<<->>>>>>]<<<<<< sub 5 by 11
[>++++[<---->-]>+<<] div 5 by 16 to 7
>>>[-<+>] add 8 to 7
<[-<<<+>>>]<<< move 7 to 4
<<
]
end on 2 again

<,] end on 1

a ends up at cell 4

>>>. print
\$\endgroup\$
1
\$\begingroup\$

GBZ80 machine code, 37 bytes

As soon as I saw this challenge, I knew I had to do this for the Game Boy where this maps 1:1.

000000a5: af 47 2a a7 c8 fe 21 28 00 fe 3c 28 00 fe 3e 28  .G*...!(..<(..>(
000000b5: 00 fe 40 20 ed cb 30 18 e9 78 2f 18 e4 cb 20 18  ..@ ..0..x/... .
000000c5: e1 cb 38 18 dd                                   ..8..

Commented assembly (rgbds with # comments instead of ;):

        SECTION ".text",ROM0
        # input: null terminated string in HL
        # output: accumulator final value in B
func::
        # a = 0
        xor     a, a
.with_ld:
        # Save A to B so we can load the next byte from the tape.
        ld      b, a
        # Since not all instructions require the A register, we can skip the
        # ld by jumping here instead.
.no_ld:
        # load byte from HL and increment
        # a = *hl++
        ld      a, [hli]
        # and A against itself to check for zero
        and     a, a
        # return if null terminator
        ret     z
        # just a normal compare and branch chain
        cp      a, "!"
        jr      z, .cpl
        cp      a, "<"
        jr      z, .left
        cp      a, ">"
        jr      z, .right
        cp      a, "@"
        # comment char, skip without ld
        jr      nz, .no_ld
        # b = (b >> 4) | (b << 4)
        swap    b
        jr      .no_ld
.cpl:
        # we need to use A for CPL
        ld      a, b
        # a = ~a
        cpl
        jr      .with_ld
.left:
        # b <<= 1
        sla     b
        jr      .no_ld
.right:
        # b >>= 1
        srl     b
        jr      .no_ld

Here is a version which prints directly using printf (SDCC ABI), satisfying the "must print" requirement (which I don't enjoy doing), 53 bytes

00 00 represents unlinked placeholders for the string below and _printf.

000000e4: af 4f 2a a7 28 00 fe 21 28 00 fe 3c 28 00 fe 3e  .O*.(..!(..<(..>
000000f4: 28 00 fe 40 20 ec cb 31 18 e8 79 2f 18 e3 cb 21  (..@ ..1..y/...!
00000104: 18 e0 cb 39 18 dc 47 c5 21 00 00 e5 cd 00 00 e8  ...9..G.!.......
00000114: 04 c9 25 75 00                                   ..%u.

The only differences is that it uses c instead of b and adds a call to printf.

        # .area _BASE
        SECTION ".text", ROM0
        # input: null terminated string in HL
        # output: decimal printed with printf
func::
        # a = 0
        xor     a, a
.with_ld:
        # save A to C
        ld      c, a
        # since not all instructions require the A register, we can skip the
        # ld by jumping here.
.no_ld:
        # a = *hli++
        ld      a, [hli]
        # and a, a is better than cp a, 0
        and     a, a
        # return if null terminator
        jr      z, .print
        # just a normal compare and branch chain
        cp      a, "!"
        jr      z, .cpl
        cp      a, "<"
        jr      z, .left
        cp      a, ">"
        jr      z, .right
        cp      a, "@"
        # comment char, skip without ld
        jr      nz, .no_ld
        # c = (c >> 4) | (c << 4)
        swap    c
        jr      .no_ld
.cpl:
        # we need to use A for CPL
        ld      a, c
        # a = ~a
        cpl
        jr      .with_ld
.left:
        # c <<= 1
        sla     c
        jr      .no_ld
.right:
        # c >>= 1
        srl     c
        jr      .no_ld
.print:
        # zero extend bc
        # a is known to be zero
        ld      b, a
        # SDCC uses cdecl
        push    bc
        # "%u"
        ld      hl, .str
        push    hl
        # printf("%u", (u16)result);
        call    _printf
        # restore stack and return
        add     sp, 4
        ret
.str:
        db "%u"
        db 0

Screenshot: screenshot

\$\endgroup\$
1
\$\begingroup\$

Easyfuck, 106 bytes

Ì¢RGÏCCHçP®óëÊR>hñãåPLD¯[¼ùbë×ZÙ]nSŸ␝«¶òëêl%!}yn␅␏APC>|PU1ó␚jPM/|PU1ãÜÏ␔ç[HOPCçÏ¿fSOSCn␅␏APC}ÌùNu¸␔>÷SGCIóå*ï]CCHùóçÏSTSÀ␛á␋çPAD␀␀ÿ

due to lack of unicode representations for c1 control characters, they have been replaced by their superscripted abbreviations

Decompressed:

f(J$>>)g(+^>^)$>4<<>,.^[^>,.^]5Y.[J<-`;+[<]>S0J!>^-`(>>>>$>1S*</>$<=f<)g-`(>>>~f)g-`(>>}f>)g-`(>{f>>)+^]J>>>>>'@␍x!><␀␀
\$\endgroup\$
1
\$\begingroup\$

Perl 5 -pF, 70 bytes

$\=(/\@/?($\<<4)+($\>>4):/!/?255-$\:/>/?$\>>1:/</?$\<<1:$\)%256for@F}{

Try it online!

\$\endgroup\$
1
\$\begingroup\$

PowerShell Core, 98 bytes

switch($args){!{[byte]$a=-bnot$a-band255}`<{$a=$a-shl1}`>{$a=$a-shr1}`@{$a=($a-shl4)+($a-shr4)}}$a

Try it online!

\$\endgroup\$
1
\$\begingroup\$

AWK -

  • instead of doing both an upshift and downshift for @, this uses the trick about % 255 instead of % 256

    unlike standard nomenclature that calls it left-shift and right-shift that describes what a computer is doing to the bits,

    which also un-intuitively requires flipping the direction of the number line mentally when dealing with 2's-complement negatives - since the "direction" of both positive and negative infinity is left hand side,

    I prefer using the terms up-shift and down-shift since they actually describe what we're doing to the numbers themselves that equally applies to unsigned as well as 2's-complement negatives, since we're upping the magnitude of the value or drawing it down.

    it's the same approach when describing manual gear shifting in sports cars, whether the manufacturer implemented it in the traditional multi-up-down design, clicking mini-tabs at the wheel, a circular knob, or a linear slider on some touch-screen.

    while it's true that neither up-shift nor down-shift provide insight to wrap-around effects of signed integer types, the same is also true for left-shift and right-shift.

  • the main for(..) loop performs all calculations arithmetically instead of using any bitwise ops

  • it uses upfront regex to scrub out self-cancelling actions, such as consecutive double swaps or double inverts

  • p.s. +++ isn't some strange undocumented operator in awk - that's just _ + ++_^_ that awk allows per POSIX specs, and since both left and right hand side of the + refer to the same variable (_), it matters not whether it got parsed as _++ + _^_ or _ + ++_^_ since the effects are absolutely identical

one can write fully POSIX-compliant awk code without using a single semi-colon (;)

 echo '
    ! -> 255
    !>> -> 63
    !<@ -> 239
    !nop!&6*! -> 255' |

gawk 'function _______(__,___,____,_____,_) {
  
  _ = "^[^!]+|@@|!!|[^!"(_____ = "<>@")"]+"
  while(gsub(_,___,__)) { }
  _ += ++_+_
  
  if (__ < "! ")
      return (_+++_^_-_)*!!__

  split(substr(__,--_),____,___)
  __ = ___ = _+_^_^++_-_
  
  for(_ in ____)___ = (_=index(_____,____[_]))<=!!_ ? (_ ?(___+___) % (_+__) \
      : __-___) : _+_==_*_ ? (___-___%_)/_ : ___<__ ? ++_*___*_%__ : ___
  return ___ 

} ($++NF = _______($!_))^_'

! -> 255 255
!>> -> 63 63
!<@ -> 239 239
!nop!&6*! -> 255 255

# gawk profile, created Tue Apr  2 07:15:18 2024

# Rule(s)

 4  ($++NF = _______($!_))^_ { # 4
 4      print
     }


# Functions, listed alphabetically

 4  function _______(__, ___, ____, _____, _)
    {
 4      _ = "^[^!]+|@@|!!|[^!" (_____ = "<>@") "]+"
 2      while (gsub(_, ___, __)) { }

 4      _ += ++_ + _
 4      if (__ < "! ")
 2          return (_+++_^_-_) * !!__

 2      split(substr(__, --_), ____, ___)
 2      __ = ___ = _+_^_^++_-_

 4      for (_ in ____)
 4          ___ = (_ = index(_____,____[_])) <= !!_? (_? (___+___) % (_+__) :\
            __-___) : _+_ == _*_ ? (___-___%_)/_ : ___<__ ? ++_*___*_%__ : ___

 2      return ___
     }
\$\endgroup\$
1
\$\begingroup\$

Swift 6, 99 95 bytes

let b={($0+"").reduce(UInt8()){a,i in"!"==i ? ~a:i==">" ?a/2:i=="<" ?a<<1:i=="@" ?a>>4|a<<4:a}}

Try it on SwiftFiddle!

\$\endgroup\$
1
\$\begingroup\$

Uiua, 34 bytes

°⋯˜∧⨬(¬|⬚0↻1|⬚0⌝↻1|↻4|∘)⊚8˜⊗"!><@"

Try it!

˜⊗"!><@" # get the index of each char within "!><@"
⊚8       # push an array of 8 zeroes
˜∧⨬(     # fold across the string, and switch on the indexes:
  ¬      # 0: not
 |⬚0↻1   # 1: rotate left 1, filling with zeroes
 |⬚0⌝↻1  # 2: rotate right 1, filling with zeroes
 |↻4     # 3: rotate left 4
 |∘)      # 4/out-of-bounds: do nothing
°⋯       # convert from binary

The ↻ rotates are flipped because Uiua's binary conversion is backwards (lsb-first).

\$\endgroup\$
1
\$\begingroup\$

JavaScript (Node.js), 69 bytes

p=>[...p].map(t=>p=255&[~p,p*2,p/2,p*257/16]['!<>@'.indexOf(t)]||p)|p

Try it online!

-3 bytes Shaggy

\$\endgroup\$
2
  • \$\begingroup\$ 69 bytes \$\endgroup\$ Commented Jul 18 at 20:21
  • \$\begingroup\$ Of course, you could save another 5 taking input as an array of characters. \$\endgroup\$ Commented Jul 19 at 15:35
0
\$\begingroup\$

Vyxal 3, 37 bytes

0?ƛ"><!@"⟒∩"2∻2×N‹"²"H2Þ0⇄H"JyᏜ⑦%+]“ᴥ

Vyxal It Online!

-1 from changing how i implemented @

Left fork aware! (also implicit input cost me 2 bytes due to exec shenanigans)

All of the 33 byters fail on !>>>>@ because it doesn't properly reverse 15 hex to F0.

\$\endgroup\$
0
\$\begingroup\$

Japt -mh, 28 bytes

Port of l4m2's JS solution

T=#ÿ&[~TT/2TÑ57/GT]g"!<>@"bU

Try it

\$\endgroup\$
0
\$\begingroup\$

Turing Machine Code, 91 rules

0 * * l SU    ; setup the 8 0s
SU _ _ l SU8
SU8 _ 0 l SU7
SU7 _ 0 l SU6
SU6 _ 0 l SU5
SU5 _ 0 l SU4
SU4 _ 0 l SU3
SU3 _ 0 l SU2
SU2 _ 0 l SU1
SU1 _ 0 r SU0
SU0 * * r SU0
SU0 _ _ r P ; start reading operators when finished

P ! x l gIN ; if the char is not a no-op, execute.
P < x l gSL
P > x l gSR
P @ x l gSW
P * x r P

gIN x x l gIN ; inverse function
gIN _ _ l IN
IN 0 1 l IN
IN 1 0 l IN
* _ _ r RD ; return back to read another operator

gSL x x l gSL ; shift left function
gSL _ _ l SL0
SL0 0 0 l SL0
SL0 1 0 l SL1
SL1 0 1 l SL0
SL1 1 1 l SL1

gSR x x l gSR ; shift right function
gSR _ _ l gSR2
gSR2 * * l gSR2
gSR2 _ _ r SR0
SR0 0 0 r SR0
SR0 1 0 r SR1
SR1 0 1 r SR0
SR1 1 1 r SR1
SR0 _ _ r RD1
SR1 _ _ r RD1

gSW x x l gSW ; swap nybbles function
gSW _ _ l SW
SW 0 * l 40
SW 1 * l 41
40 * * l 30 ; read 4 bits ahead
30 * * l 20
20 * * l 10
10 0 0 r 4b0
10 1 0 r 4b1
41 * * l 31
31 * * l 21
21 * * l 11
11 0 1 r 4b0
11 1 1 r 4b1
4b0 * * r 3b0 ; and swap it
3b0 * * r 2b0
2b0 * * r 1b0
1b0 * 0 l SW
4b1 * * r 3b1
3b1 * * r 2b1
2b1 * * r 1b1
1b1 * 1 l SW

RD * * r RD  ; read next operator
RD _ _ r RD1
RD1 x x r RD1
RD1 * * * P
RD1 _ _ l CL ; if no more ops left, erase filler chars and convert to decimal.

CL x _ l CL ; clear filler chars
CL _ _ l D  ; if all filler chars are cleared, finally start converting
CL2 0 _ l CL2  ; start clearing all the 0s
CL2 _ _ * halt ; and halt when finished

D 0 1 l D   ; decrement the binary bits
D 1 0 l D2
D2 * * l D2 ; then increment the decimal number
D2 _ _ l I

I _ 1 r f ; increment the decimal number
I 0 1 r f
I 1 2 r f
I 2 3 r f
I 3 4 r f
I 4 5 r f
I 5 6 r f
I 6 7 r f
I 7 8 r f
I 8 9 r f
I 9 0 l I

f * * r f    ; then go back to the binary bits
f _ _ r A0   ; if reached one
A0 0 * r A0  ; check that the rest isn't all 0s
A0 1 * r f2  ; if not all 0s, go back to the binary bits and decrement
A0 _ _ l CL2 ; if is all 0s, start clearing for result display
f2 * * r f2  ; go back until LSB is reached
f2 _ _ l D   ; then decrement

Try this online!
It is recommended that you run at full speed, as the binary conversion is very very slow.

\$\endgroup\$
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.