all repos

scratch @ 753ee96db5676dd9ecc5bf980cac917885c01de2

⭐ me doing recreational ~~drugs~~ programming

scratch/brainfuck/src/gbf/internal/eval.gleam (view raw)

1
import gbf/internal/lexer
2
import gbf/internal/parser.{type AST, type Block, type Command}
3
import gbf/internal/token
4
import gbf/internal/vm.{type VirtualMachine}
5
import gleam/int
6
import gleam/list
7
import gleam/result
8
9
pub type Error {
10
  /// An unexpected command was encountered at the given position.
11
  UnexpectedCommand(pos: lexer.Position)
12
13
  /// An error occurred in the virtual machine
14
  VmError(reason: vm.Error, pos: lexer.Position)
15
}
16
17
/// Evaluates an AST node against the virtual machine.
18
///
19
pub fn eval(vm: VirtualMachine, node: AST) -> Result(VirtualMachine, Error) {
20
  case node {
21
    parser.Leaf(command) -> eval_command(vm, command)
22
    parser.Node(block) -> eval_block(vm, block)
23
  }
24
}
25
26
fn eval_command(
27
  vm: VirtualMachine,
28
  command: Command,
29
) -> Result(VirtualMachine, Error) {
30
  case command {
31
    #(token.Comment(_), _) -> Ok(vm)
32
33
    #(token.IncrementPointer, p) ->
34
      vm.set_pointer(vm, vm.pointer + 1) |> wrap_vm_error(p)
35
    #(token.DecrementPointer, p) ->
36
      vm.set_pointer(vm, vm.pointer - 1) |> wrap_vm_error(p)
37
38
    #(token.IncrementByte, p) -> mut_byte(vm, int.add) |> wrap_vm_error(p)
39
    #(token.DecrementByte, p) -> mut_byte(vm, int.subtract) |> wrap_vm_error(p)
40
41
    #(token.InputByte, p) -> vm.input_byte(vm) |> wrap_vm_error(p)
42
    #(token.OutputByte, p) -> vm.output_byte(vm) |> wrap_vm_error(p)
43
44
    #(token.StartBlock, p) -> Error(UnexpectedCommand(p))
45
    #(token.EndBlock, p) -> Error(UnexpectedCommand(p))
46
    #(_, p) -> Error(UnexpectedCommand(p))
47
  }
48
}
49
50
fn eval_block(vm: VirtualMachine, block: Block) -> Result(VirtualMachine, Error) {
51
  use acc_vm, child <- list.fold(block.children, Ok(vm))
52
  case child {
53
    parser.Leaf(command) -> result.try(acc_vm, eval_command(_, command))
54
    parser.Node(child_block) ->
55
      result.try(acc_vm, eval_child_block(_, child_block))
56
  }
57
}
58
59
fn eval_child_block(vm: VirtualMachine, child_block: Block) {
60
  use cell_value <- result.try(
61
    vm.get_cell(vm, vm.pointer)
62
    |> result.map_error(VmError(_, pos: child_block.position)),
63
  )
64
65
  case cell_value > 0 {
66
    False -> Ok(vm)
67
    True -> {
68
      let acc = eval_block(vm, child_block)
69
      result.try(acc, eval_child_block(_, child_block))
70
    }
71
  }
72
}
73
74
fn mut_byte(vm: VirtualMachine, op: fn(Int, Int) -> Int) {
75
  use cell <- result.try(vm.get_cell(vm, vm.pointer))
76
  let cell = op(cell, 1)
77
  vm.set_cell(vm, vm.pointer, cell)
78
}
79
80
fn wrap_vm_error(
81
  r: Result(VirtualMachine, vm.Error),
82
  pos: lexer.Position,
83
) -> Result(VirtualMachine, Error) {
84
  result.map_error(r, VmError(_, pos))
85
}