B1nary

From Esolang
Jump to navigation Jump to search

B1NARY

B1nary is a language developed while UtilityHotbar was attempting to create a tally/prediction machine for use as an example introduction to programming. A combination with a desire to see what only two data values could make, a love of complex looking strings of 0s and 1s and a rudimentary attempt at "entanglement" (the state of the first program affects the state of every other program until the interpreter is restarted) led to several other features and the creation of B1nary.

Introduction

Full name: B1nary (stylised as B1NARY)

Type: Imperative programming

Paradigm: Procedural paradigm

Features: Simple flow control, loops, simple arithmetic

Inspiration (Esolang): Brainfuck, Malbolge and Binaryfuck

Inspiration (Concept): Encryption (Enigma), prediction, entanglement, binary sh*t (Phenomenon in movies where all code is treated as strings of 0s and 1s)

Creator: UtilityHotbar

Concepts

Counter manipulation and the trash function

B1nary has 2 counters, named CTR-0 and CTR-1, which hold a maximum value of 255 and wraps around. Each time 0 is inputted, counter 0 goes up. as such, Counter0 = 6 is represented as

 000000

Two "hidden" counters, PREF-0 and PREF-1, track every time 0 or 1 is directly inputted. These counters have no memory limit. Thus, after our previous program, this is the state of the interpreter:

 CTR-0: 6
 CTR-1: 0
 PREF-0: 6
 PREF-1: 0
 PREF: 0 (We will get to this later)

All other non-command inputs are treated as "trash". Trash is dealt with as thus: the interpreter looks in the command string for the last number (1/0) and assigns the trash to that counter. However, the preference counters are not updated since it is not a 0 or 1. For example, the program "0trash" generates this state:

 CTR-0: 6
 CTR-1: 0
 PREF-0: 1 <-One 0 in the program
 PREF-1: 0
 PREF: 0

These five variables, known as the "state", are stored between command blocks (/). The state can never be reset save an interpreter shutdown. This, the "o" in "Hello" and the "o" in "World" would be programmed differently depending on their place in the program. The "o" in "World" would also be different to the "o" in "world" or the second "o" in "Hello world" or the second "o" in "hello world".

Preference generation

A preference is generated with this sequence:

 if CTR-0 is larger than CTR-1:
   PREF is 1
 else if CTR-1 is larger than CTR-0:
   PREF is 1
 else:
   if PREF-0 is larger than PREF-1:
     PREF is 0
   else if PREF-1 is larger than PREF-0:
     PREF is 1
   else:
       PREF is LAST_INPUTTED_NUMBER of CURRENT_CODE_BLOCK
       if CURRENT_CODE_BLOCK has no 0 or 1:
           PREF is 0      

Output

Output is created with this formula:

 CTR-A - CTR-B + PREF-C - PREF-D + PREF (Where CTR-A >= CTR- B and CTR-C >= CTR-D)

The Unicode symbol with this code point is then outputted.

Syntax and Commands

Programs in B1nary are read left-to-right as a pointer travels along the program, interpreting each character in order.

There are 16 valid commands/characters in B1nary. All others are treated as "?" (trash) including WHITESPACE.

All B1nary commands are as follows (written in pseudo assembly style):

Basic commands:

 1 INC CTR-1 & PREF-1
 0 INC CTR-0 & PREF-0
 ? INC CTR-P [WHERE P IS PREV NUM]
 ! REV INC->DEC & JMP R->JMP L
 . OUT CHR ( (ROW-A - ROW-B) + (PREF-C- PREF-D) + PREF ) WHERE A >= B AND C >= D
  • Note: (!) Reverses all operations (0 becomes CTR-0 - 1, * Jumps backward(see below)) until the program encounters a second (!)

Loops:

 [ STR LOOP
 ] END LOOP
  • Note: All loops function like this: [ defines start point, when the pointer reaches ] it moves to start point. The start point, if undefined, is set to 0. Therefore 0] is a valid program, if not very useful.

Conditions

 ( STR COND
 ) END COND
 - DIV COND
 < LWR COND TYPE
 > LGR COND TYPE
 = EQL COND TYPE
 | NOT COND TYPE
 # RTN PREF-P IN COND
 : RTN CTR-P IN COND
 * TST COND IF FAIL JMP 1 R
  • Note: A condition is created like this: (TYPE-VAL.A-VAL.B) VAL.A and VAL.B can be: :0 (CTR-0), :1 (CTR-1), #0 (PREF-0), #1 (PREF-1), and any binary number (1010)

Conditions have three types: >, <, =, and |. (| means not equal) A new condition immediately overrides the old condition. The condition is tested with * (* can be used at any point in the program after the creation of the condition), which executes the next character if true and skips the next character if false. Except when ! appears, which causes the * to skip backwards in case of failure. A simple multiplication method can be created like this:

4 X 6:

 (<-:1-110)[00001*] 

Explanation: The program adds 4 to CTR-0 until CTR-1 is no longer smaller than 6 i.e. CTR-1 = 6, at which point the * skips the next character ] , the command to send the loop back to the beginning.

Examples:

H: (RUN WHEN STATE IS 0/0/0/0/0)

 (<-:0-100100)[0*]/.

Interpreters:

Original Programmable Output Machine project. Python 3.5.2 (With simple Tkinter interface):

   from tkinter import *
   import sys
   root = Tk()
   class b1nary:
       def __init__(self,master):
           self.rec = {"0":0,"1":0,"p0":0,"p1":0}
           self.pref = 0
           self.inp = 0 # All inputs
           self.cyc = 0 # All valid inputs
           self.jump = 0
           self.loop = [0,0]
           self.cond = []
           self.flag = False
           self.cgrab = False
           self.reverse = False
           self.testing = False
           self.history = []
           self.master = master
           master.title("Programmable Output Machine")
           mainframe = Frame(master)
           mainframe.grid(row=0)
           in_field = Entry(master)
           def get_in_field():
               data = in_field.get()
               in_field.delete(0,"end")
               self.process(data)
           def reset():
               self.rec = {"0":0,"1":0,"p0":0,"p1":0}
               self.pref = 0
               self.inp = 0 # All inputs
               self.cyc = 0 # All valid inputs
               self.jump = 0
               self.loop = [0,0]
               self.cond = []
               self.flag = False
               self.cgrab = False
               self.reverse = False
           def toggle_test():
               if self.testing == False:
                   self.testing = True
                   print ('Testing mode is now TRUE')
               else:
                   self.testing = False
                   print ('Testing mode is now FALSE')
   # UI Element definitions
           description = Label(master, text="Programmable Output Machine v.1")
           get_button = Button(master, text="Submit", command=get_in_field)
           tester = Button(master, text="Toggle testing mode", command=toggle_test)
           self.status = Label(master, text="Inputs: {} Cycles: {}".format(self.inp,self.cyc))
           self.output = Button(master, text="State", command=self.readout)
           self.clear = Button(master, text="Reset", command=reset)
   # UI Element placement
           description.grid(row=0,column=0,columnspan=3,sticky="w")
           in_field.grid(row=1,column=0,columnspan=2,sticky="w")
           get_button.grid(row=1,column=2,sticky="w")
           self.status.grid(row=3,column=2)
           self.output.grid(row=2,column=0,sticky="w")
           self.clear.grid(row=2,column=1,sticky="w")
           tester.grid(row=3,columnspan=2,sticky="w")
           self.cycle_update()
       def readout(self):
           print ('CTR-0: {}'.format(self.rec["0"]))
           print ('CTR-1: {}'.format(self.rec["1"]))
           print ('PREF-0: {}'.format(self.rec["p0"]))
           print ('PREF-1: {}'.format(self.rec["p1"]))
           print ('Pref: {}'.format(self.pref))
           print ('Cond: {}'.format(self.cond))
       def process(self,data):
           self.jump = 0
           self.loop = [0,0]
           self.flag = False
           self.cgrab = False
           if data == "":
               try:
                   data = self.history[len(self.history)-1]
               except:
                   data = ' '
           self.history.append(data)
           if self.testing == True:
               print ('Data: {}'.format(data))
           datalist = data.split('/')
           for chunk in datalist:
               chunk = list(chunk)
               i = 0
               while True:
                   chunk[i] = self.crunch(chunk,chunk[i])
                   if self.jump > 0:
                       if self.testing == True:
                           print ('jumped')
                       if self.reverse == False:
                           i += self.jump
                       else:
                           i -= self.jump + 2
                       self.jump = 0
                   if self.loop[1] > self.loop[0] and self.flag == True:
                       if self.testing == True:
                           print ('looping')
                           self.readout()
                       i = self.loop[0]
                       self.flag = False
                       continue
                   i += 1
                   if i > len(chunk)-1:
                       break
               self.update(chunk)
       def findnum(self,thing):
           n = None
           for _ in thing:
               if _ in ["1","0"]:
                   n = _
           return n
       def prtout(self):
           out = 0
           if self.rec["0"] > self.rec["1"]:
               out += self.rec["0"] - self.rec["1"]
           elif self.rec["0"] < self.rec["1"]:
               out += self.rec["1"] - self.rec["0"]
           if self.rec["p0"] > self.rec["p1"]:
               out += self.rec["p0"] - self.rec["p1"]
           elif self.rec["p0"] < self.rec["p1"]:
               out += self.rec["p1"] - self.rec["p0"]
           out += self.pref
           return chr(out)
       def verify(self):
           if self.rec["0"] > 255:
               self.rec["0"] -= 255
           if self.rec["1"] > 255:
               self.rec["1"] -= 255       
       def crunch(self,chunk,t):
           self.inp += 1
           self.cyc += 1
           if self.testing == True:
               print ("Unit: {}".format(t))
           if self.cgrab == True and t != ')':
               self.cond.append(t)
               return t
           #0
           if t == "0":
               if self.reverse == False:
                   self.rec["0"] += 1
                   self.rec["p0"] += 1
               else:
                   self.rec["0"] -= 1
                   self.rec["p0"] += 1
           #1
           elif t == "1":
               if self.reverse == False:
                   self.rec["1"] += 1
                   self.rec["p1"] += 1
               else:
                   self.rec["1"] -= 1
                   self.rec["p1"] += 1
           #Output
           elif t == '.':
               sys.stdout.writelines(self.prtout())
           #Loop commands
           elif t == '[':
               self.loop[0] = chunk.index(t)
           elif t == ']':
               self.loop[1] = chunk.index(t)
               self.flag = True
           elif t == "(":
               self.cond = []
               self.cgrab = True
           elif t == ')':
               self.cgrab = False
           elif t == '*':
               if len(self.cond) >= 5:
                   if self.check(self.cond) == False:
                       self.jump += 1
           elif t == '!':
               if self.reverse == False:
                   self.reverse = True
               else:
                   self.reverse = False
           else:
               if self.testing == True:
                   print ('Referring back...')
               self.cyc -= 1
               getnm = self.findnum(chunk[:chunk.index(t)])
               if getnm != None:
                   if self.reverse == False:
                       self.rec[getnm] += 1
                   else:
                       self.rec[getnm] -= 1
               else:
                   pass
           self.verify()
           return t
       def ref(self,thing):
           if thing == ':0':
               return self.rec["0"]
           elif thing == ':1':
               return self.rec["1"]
           elif thing == '#0':
               return self.rec["p0"]
           elif thing == '#1':
               return self.rec["p1"]
           else:
               return int(thing, 2)
       def check(self,cnd):
           cnd = .join(cnd)
           cnd = cnd.split('-')
           if self.testing == True:
               print (cnd)
           a = self.ref(cnd[1])
           b = self.ref(cnd[2])
           if cnd[0] == '>':
               if a > b:
                   return True
           elif cnd[0] == '<':
               if a < b:
                   return True
           elif cnd[0] == '=':
               if a == b:
                   return True
           elif cnd[0] == '|':
               if a != b:
                   return True
           return False
       def update(self,chunk):
           if self.rec["0"] > self.rec["1"]:
               larger = '0'
           elif self.rec["1"] > self.rec["0"]:
               larger = '1'
           else:
               if self.rec["p0"] > self.rec["p1"]:
                   larger = '0'
               elif self.rec["p1"] > self.rec["p0"]:
                   larger = '1'
               else:
                   if self.testing == True:
                       print ('Defaulting to last num...')
                   if self.findnum(chunk) != None:
                       larger = self.findnum(chunk)
                   else:
                       if self.testing == True:
                           print ('Defaulting to 0...')
                       larger = "0"  
           if larger == '0':
               self.pref = 0
           else:
               self.pref = 1
       def cycle_update(self):
           self.status.configure(text="Inputs: {} Cycles: {}".format(self.inp,self.cyc))
           self.master.after(1000, self.cycle_update)
   print ('Programmable Output Machine (Tk-based B1nary Interpreter) 1.0.0')
   print ('Made by UtilityHotbar in 2017')
   ai = b1nary(root)