all repos

gbf @ 374a8c7

⭐ gleaming brainfuck
2 files changed, 96 insertions(+), 78 deletions(-)
refactor vm
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2025-10-17 13:56:03 +0300
Change ID: sspwvsqkpuszxqsuyzouoqqkuoxvknvy
Parent: 125cdd1
M src/gbf/eval.gleam

@@ -1,18 +1,13 @@

-import char import gbf/lexer import gbf/parser.{type AST, type Block, type Command} import gbf/token import gbf/vm.{type VirtualMachine} +import gleam/int import gleam/list -import gleam/option import gleam/result pub type Error { - PointerRanOffTape - IntegerOverflow - IntegerUnderflow - EmptyInput - InvalidChar(Int) + VmError(reason: vm.Error) UnexpectedCommand(pos: lexer.Position) }

@@ -28,12 +23,16 @@ command: Command,

vm: VirtualMachine, ) -> Result(VirtualMachine, Error) { case command { - #(token.IncrementPointer, _) -> increment_pointer(vm) - #(token.DecrementPointer, _) -> decrement_pointer(vm) - #(token.IncrementByte, _) -> increment_byte(vm) - #(token.DecrementByte, _) -> decrement_byte(vm) - #(token.OutputByte, _) -> output_byte(vm) - #(token.InputByte, _) -> input_byte(vm) + #(token.IncrementPointer, _) -> + vm.set_pointer(vm, vm.pointer + 1) |> wrap_vm_error() + #(token.DecrementPointer, _) -> + vm.set_pointer(vm, vm.pointer - 1) |> wrap_vm_error() + + #(token.IncrementByte, _) -> mut_byte(vm, int.add) + #(token.DecrementByte, _) -> mut_byte(vm, int.subtract) + + #(token.InputByte, _) -> vm.input_byte(vm) |> wrap_vm_error + #(token.OutputByte, _) -> vm.output_byte(vm) |> wrap_vm_error #(token.StartBlock, pos) -> Error(UnexpectedCommand(pos)) #(token.EndBlock, pos) -> Error(UnexpectedCommand(pos))

@@ -51,9 +50,10 @@ }

} fn eval_child_block(vm: VirtualMachine, child_block: Block) { - let cell_value = + use cell_value <- result.try( vm.get_cell(vm, vm.pointer) - |> option.unwrap(0) + |> result.map_error(VmError), + ) case cell_value > 0 { False -> Ok(vm)

@@ -64,66 +64,19 @@ }

} } -fn increment_pointer(vm: VirtualMachine) { - let pointer = vm.pointer + 1 - case pointer > vm.tape_size { - True -> PointerRanOffTape |> Error - False -> Ok(vm.VirtualMachine(..vm, pointer: pointer)) - } -} - -fn decrement_pointer(vm: VirtualMachine) { - let pointer = vm.pointer - 1 - case pointer < 0 { - True -> PointerRanOffTape |> Error - False -> Ok(vm.VirtualMachine(..vm, pointer: pointer)) - } -} - -fn increment_byte(vm: VirtualMachine) { - let cell_value = +fn mut_byte(vm: VirtualMachine, op: fn(Int, Int) -> Int) { + use cell_value <- result.try( vm.get_cell(vm, vm.pointer) - |> option.unwrap(0) + |> result.map_error(VmError), + ) - let new_cell_value = cell_value + 1 - case new_cell_value > vm.cell_size { - True -> IntegerOverflow |> Error - False -> vm.set_cell(vm, vm.pointer, new_cell_value) |> Ok - } + let cell_value = op(cell_value, 1) + vm.set_cell(vm, vm.pointer, cell_value) + |> result.map_error(VmError) } -fn decrement_byte(vm: VirtualMachine) { - let cell_value = - vm.get_cell(vm, vm.pointer) - |> option.unwrap(0) - - let new_cell_value = cell_value - 1 - case new_cell_value < 0 { - True -> IntegerUnderflow |> Error - False -> vm.set_cell(vm, vm.pointer, new_cell_value) |> Ok - } -} - -fn input_byte(vm: VirtualMachine) { - case vm.input { - [] -> EmptyInput |> Error - [first, ..] -> { - let new_input = list.drop(vm.input, 1) - let vm = vm.set_cell(vm, vm.pointer, first) - Ok(vm.VirtualMachine(..vm, input: new_input)) - } - } -} - -fn output_byte(vm: VirtualMachine) { - let cell_value = - vm.get_cell(vm, vm.pointer) - |> option.unwrap(0) - - case char.from_code(cell_value) { - "" -> Error(InvalidChar(cell_value)) - c -> - vm.VirtualMachine(..vm, output: vm.output <> c) - |> Ok - } +fn wrap_vm_error( + r: Result(VirtualMachine, vm.Error), +) -> Result(VirtualMachine, Error) { + result.map_error(r, VmError) }
M src/gbf/vm.gleam

@@ -1,9 +1,19 @@

+import char import gleam/dict.{type Dict} -import gleam/option.{type Option, None, Some} +import gleam/list +import gleam/result pub const tape_size = 30_000 pub const cell_size = 255 + +pub type Error { + PointerRanOffTape + IntegerOverflow + IntegerUnderflow + EmptyInput + InvalidChar(Int) +} /// The machine model we are going to use for this interpreter is very simple: /// - Our memory consists of 30,000 cells (1000 rows * 30 columns).

@@ -29,10 +39,12 @@ pub fn new(input: List(Int)) -> VirtualMachine {

VirtualMachine(input:, pointer: 0, cells: dict.new(), output: "") } -pub fn get_cell(vm: VirtualMachine, pointer: Index) -> Option(Int) { +pub fn get_cell(vm: VirtualMachine, pointer: Index) -> Result(Index, Error) { + use pointer <- result.try(validate_tape_size(pointer)) + case dict.get(vm.cells, pointer) { - Ok(value) -> Some(value) - Error(_) -> None + Ok(value) -> Ok(value) + Error(_) -> Ok(0) } }

@@ -40,7 +52,60 @@ pub fn set_cell(

vm: VirtualMachine, pointer: Index, value: Int, -) -> VirtualMachine { +) -> Result(VirtualMachine, Error) { + use pointer <- result.try(validate_tape_size(pointer)) + use value <- result.try(validate_cell_size(value)) + let new_cells = dict.insert(vm.cells, pointer, value) VirtualMachine(..vm, cells: new_cells) + |> Ok +} + +pub fn set_pointer( + vm: VirtualMachine, + pointer: Index, +) -> Result(VirtualMachine, Error) { + use pointer <- result.try(validate_tape_size(pointer)) + + VirtualMachine(..vm, pointer:) + |> Ok +} + +pub fn input_byte(vm: VirtualMachine) -> Result(VirtualMachine, Error) { + case vm.input { + [] -> Error(EmptyInput) + [first, ..] -> { + use vm <- result.try(set_cell(vm, vm.pointer, first)) + + VirtualMachine(..vm, input: list.drop(vm.input, 1)) + |> Ok + } + } +} + +pub fn output_byte(vm: VirtualMachine) -> Result(VirtualMachine, Error) { + use cell_value <- result.try(get_cell(vm, vm.pointer)) + + case char.from_code(cell_value) { + "" -> Error(InvalidChar(cell_value)) + c -> + VirtualMachine(..vm, output: vm.output <> c) + |> Ok + } +} + +fn validate_tape_size(pointer: Index) { + case pointer { + p if p > tape_size -> Error(PointerRanOffTape) + p if p < 0 -> Error(PointerRanOffTape) + _ -> Ok(pointer) + } +} + +fn validate_cell_size(value: Int) { + case value { + v if v > cell_size -> Error(IntegerOverflow) + v if v < 0 -> Error(IntegerUnderflow) + _ -> Ok(value) + } }