all repos

gbf @ 25951614d051ff4ed79ea94def175b1b3f80ff96

⭐ gleaming brainfuck
4 files changed, 60 insertions(+), 1 deletions(-)
add docs
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2025-10-17 13:56:03 +0300
Change ID: nqyzqwsnkqplwzrwqxtnlmvtqxvssuzp
Parent: 35a5e15
M src/gbf/eval.gleam

@@ -7,10 +7,15 @@ import gleam/list

import gleam/result pub type Error { + /// An unexpected command was encountered at the given position. + UnexpectedCommand(pos: lexer.Position) + + /// An error occurred in the virtual machine VmError(reason: vm.Error) - UnexpectedCommand(pos: lexer.Position) } +/// Evaluates an AST node against the virtual machine. +/// pub fn eval(vm: VirtualMachine, node: AST) -> Result(VirtualMachine, Error) { case node { parser.Leaf(command) -> eval_command(command, vm)
M src/gbf/lexer.gleam

@@ -8,6 +8,7 @@ Lexer(source: String, offset: Int, newlines: splitter.Splitter)

} pub type Position { + /// A token position in a wile, represented as offset of bytes Position(offset: Int) }
M src/gbf/parser.gleam

@@ -5,7 +5,12 @@ import gleam/pair

import gleam/result pub type AST { + /// A single command + /// Leaf(Command) + + /// A block with nested children (used for loops) + /// Node(Block) }

@@ -22,6 +27,12 @@ UnexpectedCommand

UnexpectedBlock } +/// Parses a list of tokens into an Abstract Syntax Tree. +/// +/// Takes a flat list of tokens with their positions and constructs +/// a hierarchical tree structure where loop blocks become nested nodes. +/// All tokens must be consumed for successful parsing. +/// pub fn parse(tokens: List(#(Token, Position))) -> Result(AST, Error) { let root = Node(Block(children: [], position: Position(0)))
M src/gbf/vm.gleam

@@ -25,6 +25,7 @@ /// - A data cell is 8 bits, and an error will be reported if the program tries

/// to perform under- or overflow, i.e. decrement 0 or increment 255. /// - Two streams of bytes for input and output using the ASCII character /// encoding. +/// pub type VirtualMachine { VirtualMachine(pointer: Index, cells: Cells, output: String, input: List(Int)) }

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

VirtualMachine(input:, pointer: 0, cells: dict.new(), output: "") } +/// Returns the accumulated output string from the virtual machine. +/// pub fn output(vm: VirtualMachine) -> String { vm.output } +/// Gets the value of the cell at the specified pointer. +/// Returns an error if the pointer is out of bounds. +/// +/// ```gleam +/// get_cell(vm, 0) // Ok(0) +/// get_cell(vm, -1) // Error(PointerRanOffTape) +/// ``` pub fn get_cell(vm: VirtualMachine, pointer: Index) -> Result(Index, Error) { use pointer <- result.try(validate_tape_size(pointer))

@@ -52,6 +62,16 @@ Error(_) -> Ok(0)

} } +/// Sets the value of the cell at the specified pointer. +/// +/// Returns an updated virtual machine if successful, or an error if: +/// - The pointer is out of bounds (< 0 or > 30,000) +/// - The value is out of bounds (< 0 or > 255) +/// +/// ```gleam +/// set_cell(vm, 0, 65) // Ok(...) +/// set_cell(vm, 0, 256) // Error(IntegerOverflow) +/// ``` pub fn set_cell( vm: VirtualMachine, pointer: Index,

@@ -65,6 +85,13 @@ VirtualMachine(..vm, cells: new_cells)

|> Ok } +/// Moves the data pointer to the specified position. +/// Returns error if pointer is out of bounds. +/// +/// ```gleam +/// set_pointer(vm, 100) // Ok(...) +/// set_pointer(vm, -1) // Error(PointerRanOffTape) +/// ``` pub fn set_pointer( vm: VirtualMachine, pointer: Index,

@@ -75,6 +102,16 @@ VirtualMachine(..vm, pointer:)

|> Ok } +/// Reads a byte from the input stream and stores it in the current cell. +/// +/// Consumes the first byte from the input list and writes it to the cell +/// at the current pointer position. +/// Returns an error if the input is empty. +/// +/// ```gleam +/// input_byte(vm) // Ok(...) +/// input_byte(empty_vm) // Error(EmptyInput) +/// ``` pub fn input_byte(vm: VirtualMachine) -> Result(VirtualMachine, Error) { case vm.input { [] -> Error(EmptyInput)

@@ -87,6 +124,11 @@ }

} } +/// Reads the value from the current cell and appends it to the output as a character. +/// +/// Converts the cell value to an ASCII character and adds it to the output string. +/// Returns an error if the cell value is not a valid ASCII code point. +/// pub fn output_byte(vm: VirtualMachine) -> Result(VirtualMachine, Error) { use cell_value <- result.try(get_cell(vm, vm.pointer))