Skip to content

Instantly share code, notes, and snippets.

@folkertdev
Created July 19, 2023 12:05
Show Gist options
  • Save folkertdev/ef1970afe9f00048f163f4d71d5f03f7 to your computer and use it in GitHub Desktop.
Save folkertdev/ef1970afe9f00048f163f4d71d5f03f7 to your computer and use it in GitHub Desktop.
brainroc
app "brainroc"
packages {
pf: "https://github.com/roc-lang/basic-cli/releases/download/0.3.2/tE4xS_zLdmmxmHwHih9kHWQ7fsXtJr7W7h3425-eZFk.tar.br",
}
imports [
pf.File,
pf.Path,
pf.Stdout,
pf.Task,
]
provides [main] to pf
Instruction : {
operator: Operator,
operand: Nat
}
Operator : [
IncrementPointer,
DecrementPointer,
IncrementByte,
DecrementByte,
Output,
JumpForward,
JumpBack,
]
Jump : Nat
tokenize : List U8 -> (Nat, List Instruction, List Jump)
tokenize = \bytes ->
List.walk bytes (0, [], []) \(pc, instructions, jumpStack), c ->
when c is
'>' ->
instruction = { operator: IncrementPointer, operand: 0 }
(pc + 1, List.append instructions instruction, jumpStack)
'<' ->
instruction = { operator: DecrementPointer, operand: 0 }
(pc + 1, List.append instructions instruction, jumpStack)
'+' ->
instruction = { operator: IncrementByte, operand: 0 }
(pc + 1, List.append instructions instruction, jumpStack)
'-' ->
instruction = { operator: DecrementByte, operand: 0 }
(pc + 1, List.append instructions instruction, jumpStack)
'.' ->
instruction = { operator: Output, operand: 0 }
(pc + 1, List.append instructions instruction, jumpStack)
'[' ->
instruction = { operator: JumpForward, operand: 0 }
(
pc + 1,
List.append instructions instruction,
List.append jumpStack pc,
)
']' ->
when List.last jumpStack is
Err _ -> crash "malformed program"
Ok jumpPc ->
instruction = { operator: JumpBack, operand: jumpPc }
(
pc + 1,
List.append instructions instruction |> List.update jumpPc \{ operator } -> { operator, operand: pc },
List.dropLast jumpStack,
)
' ' | '\n' | '\t' -> (pc, instructions, jumpStack)
other ->
when Str.fromUtf8 [ other ] is
Ok str ->
crash "unexpected character: '\(str)'"
Err _ ->
crash "unexpected non-utf8 character"
dataSize = 30_000
execute : List Instruction -> Str
execute = \instructions ->
output = executeHelp 0 instructions 0 (List.repeat 0 dataSize) []
when Str.fromUtf8 output is
Err _ -> crash "invalid utf8 in the output"
Ok str -> str
executeHelp : Nat, List Instruction, Nat, List U8, List U8 -> List U8
executeHelp = \pc, instructions, dataPtr, data, output ->
if pc < List.len instructions then
{ operator, operand } = when List.get instructions pc is
Ok v -> v
Err _ -> crash "out-of-bounds"
when operator is
IncrementPointer ->
executeHelp (pc + 1) instructions (Num.addWrap dataPtr 1) data output
DecrementPointer ->
executeHelp (pc + 1) instructions (Num.subWrap dataPtr 1) data output
IncrementByte ->
newData = List.update data dataPtr (\x -> Num.addWrap x 1)
executeHelp (pc + 1) instructions dataPtr newData output
DecrementByte ->
newData = List.update data dataPtr (\x -> Num.subWrap x 1)
executeHelp (pc + 1) instructions dataPtr newData output
Output ->
c = List.get data dataPtr |> Result.withDefault '$'
newOutput = List.append output c
executeHelp (pc + 1) instructions dataPtr data newOutput
JumpForward ->
when List.get data dataPtr is
Ok 0 ->
# question: add one to the operand here?
executeHelp (operand + 1) instructions dataPtr data output
_ ->
executeHelp (pc + 1) instructions dataPtr data output
JumpBack ->
when List.get data dataPtr is
Ok n if n > 0 ->
# question: add one to the operand here?
executeHelp (operand + 1) instructions dataPtr data output
_ ->
executeHelp (pc + 1) instructions dataPtr data output
else
output
main =
inputFilePath = "/home/folkertdev/roc/brainroc/examples/bench/bench-1.bf"
inputResult <- inputFilePath |> Path.fromStr |> File.readBytes |> Task.attempt
when inputResult is
# Run the interpreter
Ok bytes ->
(_pc, tokens, _jumpStack) = tokenize bytes
Stdout.line (execute tokens)
Err (FileReadErr _ _) -> "Failed to read the input file `\(inputFilePath)`." |> Stdout.line
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment