### Mindbend

Mindbend is BF with extra steps.

What if BF had named registers? More operations? What if we fused that with assembly?

This is Mindbend, a silly little interpreter I made in an hour or two killing time on a Friday at school.

## Basic rundown

There are 26 registers, a - z.
Each register is a 32-bit integer.

### Commands

Keep in mind this notation:

• R is a register
• N is a number
• M is either
• C is a condition
• `*` represents an unfinished instruction

Here's the arithmetic:

• `!RM` moves M into R
• `+RM` is R += M (addition)
• `-RM` is R -= M (subtraction)
• `*RM` is R *= M (multiplication)
• `/RM` is R /= M (integer division)
• `%RM` is R %= M (modulo)

Handling I/O:

• `.R` print out the value of R as a number
• `;R` print out the value of R as a character (unicode codepoint)
• `,R` accept one input character into R
• `#R` read the input as a number, consuming the most amount of characters that creates a proper number
• `_` take and discard an input character
• `'M` seek to M in the input
• `|R` store the length of the input in R

Conditions:

• `M=M` if M == M
• `M!M` if M != M
• `M>M` if M > M
• `M<M` if M < M

Control structures:

• `iC[` ... `]` if C is true, then do what's inside, otherwise skip past `]`
• `wC[` ... `]` while C is true, do what's inside, otherwise skip past `]`

## Examples

`#a*a2.a` Read a number from input, double, and output

`#a_#b+ab.a` Read two numbers delimited by a non-number character, add, and output

`#bwa<b[.a+a1]` Read a number from the input and output every number from 0 to 1 less than it, e.g. if you pass in 10, the output will be `0123456789`

### An example with a breakdown

Here's the last example with a breakdown, using comments (`:`):

``````#b    : Take a number from the input
wa<b[ : While a < b
.a    : Add a to output
+a1   : Add 1 to a
]     : End of while block
``````
``````module Mindbend
class Parser
getter registers : Hash(Char, Int32)
getter program : String = ""
getter position : Int32 = 0
getter input : String = ""
getter output : String = ""
getter input_position = 0
getter loop_pairs = [] of Tuple(Int32, Int32)

def reset
@program = ""
@position = 0
@input = ""
@input_position = 0
@output = ""
@loop_pairs = [] of Tuple(Int32, Int32)
('a'..'z').each { |char| @registers[char] = 0 }
end

def initialize()
@registers = {} of Char => Int32
end

reset
@program = program
end

def quick_run(program, input)
reset
@program = program
run(input)
@output
end

def run(input)
@input = input
while @position < @program.size
end
end

char = @program[@position]?
@position += 1
char
end

end

raise Exception.new("#{char} is not a register") unless ('a'..'z').includes? char
char
end

number_string = ""
loop do
if number_string == "" && char == '-'
number_string += '-'
elsif ('0'..'9').includes? char
number_string += char
else
@position -= 1
break
end
end
raise Exception.new("Invalid number") if number_string == ""
number_string.to_i32
end

number_string = ""
loop do
char = @input[@input_position]?
@input_position += 1
if number_string == "" && char == '-'
number_string += '-'
elsif char.nil?
break
elsif ('0'..'9').includes? char
number_string += char
else
@input_position -= 1
break
end
end
raise Exception.new("Invalid number from input") if number_string == ""
number_string.to_i32
end

begin
rescue
@position -= 1
end
raise Exception.new("Invalid register/number")
end

return target if target.is_a? Int32
return @registers[target] if target.is_a? Char
raise Exception.new("Invalid register/number")
end

raise Exception.new("Invalid conditional operator") unless ['=', '!', '>', '<'].includes? operator
case operator
when '='
return a == b
when '!'
return a != b
when '>'
return a > b
when '<'
return a < b
end
false
end

raise Exception.new("Invalid block opening") unless read_char! == '['
end

def seek_close_block
level = 0
start = @position
end_position = -1
loop do
case char
when '['
level += 1
when ']'
if level > 0
level -= 1
else
end_position = @position
break
end
end
raise Exception.new("Block never closed") if @position > @program.size
end
@position = start
end_position
end

when '!'
# move instruction
@registers[register] = from
when '+'
@registers[register] += operand
when '-'
# subtract instruction
@registers[register] -= operand
when '*'
@registers[register] *= operand
when '/'
@registers[register] //= operand
when '%'
@registers[register] = @registers[register] % operand
when ','
value = @input[@input_position]? || '0'
@registers[register] = value.to_i32? || value.ord
@input_position += 1
when '#'
@registers[register] = value
when '.'
@output += @registers[register].to_s
when ';'
@output += @registers[register].chr
when '\''
when 'i'
unless condition
@position = seek_close_block
end
when 'w'
start = @position - 1
end_pos = seek_close_block
if condition
loop_pairs << {end_pos, start}
else
@position = end_pos
end
when ']'
if lp = loop_pairs.find { |lp| lp[0] == @position }
@position = lp[1]
loop_pairs.delete lp
end
when '|'