Loose Circular Brainfuck (LCBF)

From Esolang
Jump to navigation Jump to search

Loose Circular Brainfuck (LCBF) is created by Maurice Ling and is a derivative of Brainfuck. It is originally meant as a generic mechanism to test out different varieties of assembly or op-code typed languages.

LCBF uses all 8 operations of Brainfuck with major differences in the tape and the loop operations.


Differences from Brainfuck

  1. The tape or array is circular (a ring list) instead of linear. When the pointer is at the "end" of the tape, an increment (">") will move the tape to the start. Similarly, when the pointer is decremented at the "beginning" of the tape, the pointer goes to the end.
  2. Operations after a start loop operator ("[") will only be executed provided the loop(s) are properly closed. If the loops are open, the program will terminate.
  3. However, it is possible to have an end loop operator ("]") without a preceding start loop operator ("["). In this case, the end loop operator ("]") will be ignored and execution continues.
  4. Unclosed or unopened loops may result in non-deterministic behaviour.
  5. All inputs are pre-defined at the start of the program.

Python Implementation

   def increment(array, apointer, inputdata, output, source, spointer):
       array[apointer] = array[apointer] + 1
       return (array, apointer, inputdata, output, source, spointer)
   
   def decrement(array, apointer, inputdata, output, source, spointer):
       array[apointer] = array[apointer] - 1
       return (array, apointer, inputdata, output, source, spointer)
   
   def forward(array, apointer, inputdata, output, source, spointer):
       return (array, apointer + 1, inputdata, output, source, spointer)
   
   def backward(array, apointer, inputdata, output, source, spointer):
       return (array, apointer - 1, inputdata, output, source, spointer)
   
   def call_out(array, apointer, inputdata, output, source, spointer):
       output.append(array[apointer])
       return (array, apointer, inputdata, output, source, spointer)
   
   def accept_predefined(array, apointer, inputdata, output, source, spointer):
       if len(inputdata) > 0: array[apointer] = inputdata.pop(0)
       else: array[apointer] = 0
       return (array, apointer, inputdata, output, source, spointer)
   
   def cbf_start_loop(array, apointer, inputdata, output, source, spointer):
       if array[apointer] > 0:
           return (array, apointer, inputdata, output, source, spointer)
       else:
           count = 1
           try:
               while count > 0:
                   spointer = spointer + 1
                   if source[spointer] == ']':
                       count = count - 1
                   if source[spointer] == '[':
                       count = count + 1
           except IndexError:
               spointer = len(source) - 1
       return (array, apointer, inputdata, output, source, spointer)
   
   def cbf_end_loop(array, apointer, inputdata, output, source, spointer):
       temp = spointer
       if array[apointer] < 1:
           return (array, apointer, inputdata, output, source, spointer + 1)
       else:
           count = 1
           try:
               while count > 0:
                   spointer = spointer - 1
                   if source[spointer] == ']':
                       count = count + 1
                   if source[spointer] == '[':
                       count = count - 1
           except IndexError:
               spointer = temp
       return (array, apointer, inputdata, output, source, spointer)
   
   LCBF = {'+': increment,
           '-': decrement,
           '>': forward,
           '<': backward,
           '.': call_out,
           ',': accept_predefined,
           '[': cbf_start_loop,
           ']': cbf_end_loop,
           }
   
   def interpret(source, functions,
                function_size=1, inputdata=[],
                array=None, size=30):
       spointer = 0
       apointer = 0
       output = list()
       if array == None:
           array = [0] * size
       if len(array) > size:
           array = array[0:size]
       if len(source) % function_size != 0:
           source = source + '!'*(function_size - \
                                  len(source) % function_size)
       tokens = functions.keys()
       source = "".join([x for x in source if x in tokens])
       while spointer < len(source):
           try:
               token = source[spointer:spointer+function_size]
               (array, apointer, inputdata, output,
                    source, spointer) = functions[token](array, apointer,
                                                         inputdata, output,
                                                         source, spointer)
           except KeyError:
               print ' '.join(['Unknown function: ',
                               source[i:i+function_size],
                               'at source position',
                               str(i)])
           if apointer > size:
               apointer = apointer - size
           if apointer < 0:
               apointer = size + apointer
           spointer = spointer + function_size
       return (array, apointer, inputdata, output, source, spointer)
   
   if __name__ == '__main__':
       print interpret('++++++++++[>+++++<.-]', LCBF)
       print interpret('++[>+++++<.-]>>>+++.', LCBF)
       print interpret('++>+++++<.-]>>>+++.', LCBF)
       print interpret('++>[+++++<.->>>+++.', LCBF)
       print interpret('+++++[>++++[>+++.<-].<-]', LCBF)