Skip to content

Commit 6810fc1

Browse files
committed
Initial commit
0 parents  commit 6810fc1

File tree

19 files changed

+732
-0
lines changed

19 files changed

+732
-0
lines changed

.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Scala CLI
2+
.scala-build/
3+
.bsp/
4+
5+
# Metals
6+
.metals/
7+
.bloop/
8+
project/metals.sbt
9+
10+
# VS Code
11+
.vscode/
12+
.history/
13+
14+
# IntelliJ
15+
.idea/
16+
*.iml
17+
*.ipr
18+
*.iws
19+
20+
# Build
21+
target/
22+
out/
23+
24+
# MacOS
25+
.DS_Store

.scalafmt.conf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
version = 3.8.4
2+
runner.dialect = scala3
3+
4+
maxColumn = 110
5+
align.preset = most
6+
newlines.beforeTypeBounds = fold
7+
newlines.avoidForSimpleOverflow = [tooLong, punct, slc]
8+
9+
project.git = true
10+
11+
rewrite.rules = [RedundantBraces, RedundantParens, SortModifiers, Imports]
12+
rewrite.scala3.convertToNewSyntax = true
13+
rewrite.scala3.removeOptionalBraces = true
14+
rewrite.imports.sort = scalastyle
15+
rewrite.imports.groups = [["javax?\\..*", "scala\\..*"], [".*"], ["dev.meetree\\..*"]]
16+
rewrite.trailingCommas.style = keep
17+
18+
docstrings.wrapMaxColumn = 120
19+
docstrings.forceBlankLineBefore = false

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Dmitri Polchinski
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Brainfuck
2+
3+
A simple [Brainfuck](https://en.wikipedia.org/wiki/Brainfuck) interpreter implemented in Scala, built primarily for the purpose of trying out [Scala CLI](https://scala-cli.virtuslab.org/).
4+
5+
## Usage
6+
7+
### Running the Project
8+
9+
```bash
10+
# Run the interpreter
11+
scala-cli run . -- "program"
12+
13+
# Run tests
14+
scala-cli test .
15+
```
16+
17+
### Example Programs
18+
19+
You can find example programs in the `examples/` directory and run them using:
20+
```bash
21+
scala-cli run . -- "$(cat path-to-example)"
22+
```
23+
All examples are sourced from [wikipedia.org](https://en.wikipedia.org/wiki/Brainfuck#Examples) and [brainfuck.org](https://brainfuck.org/).
24+
25+
### Commands
26+
27+
The language consists of eight single-character commands:
28+
| Command | Description |
29+
|---------|-------------|
30+
| `>` | Increment the data pointer by one (to point to the next cell to the right).|
31+
| `<` | Decrement the data pointer by one (to point to the next cell to the left). |
32+
| `+` | Increment the byte at the data pointer by one. |
33+
| `-` | Decrement the byte at the data pointer by one. |
34+
| `.` | Output the byte at the data pointer. |
35+
| `,` | Accept one byte of input, storing its value in the byte at the data pointer. |
36+
| `[` | If the byte at the data pointer is zero, then instead of moving the instruction pointer forward to the next command, jump it forward to the command after the matching `]` command |
37+
| `]` | If the byte at the data pointer is nonzero, then instead of moving the instruction pointer forward to the next command, jump it back to the command after the matching `[` command. |
38+
39+
All other characters are ignored and can be used for comments.

examples/addition.b

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
++ Cell c0 = 2
2+
> +++++ Cell c1 = 5
3+
4+
[ Start your loops with your cell pointer on the loop counter (c1 in our case)
5+
< + Add 1 to c0
6+
> - Subtract 1 from c1
7+
] End your loops with the cell pointer on the loop counter
8+
9+
At this point our program has added 5 to 2 leaving 7 in c0 and 0 in c1
10+
but we cannot output this value to the terminal since it is not ASCII encoded
11+
12+
To display the ASCII character "7" we must add 48 to the value 7
13+
We use a loop to compute 48 = 6 * 8
14+
15+
++++ ++++ c1 = 8 and this will be our loop counter again
16+
[
17+
< +++ +++ Add 6 to c0
18+
> - Subtract 1 from c1
19+
]
20+
< . Print out c0 which has the value 55 which translates to "7"!

examples/bubblesort.b

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[bsort.b -- bubble sort
2+
(c) 2016 Daniel B. Cristofani
3+
http://brainfuck.org/]
4+
5+
>>,[>>,]<<[
6+
[<<]>>>>[
7+
<<[>+<<+>-]
8+
>>[>+<<<<[->]>[<]>>-]
9+
<<<[[-]>>[>+<-]>>[<<<+>>>-]]
10+
>>[[<+>-]>>]<
11+
]<<[>>+<<-]<<
12+
]>>>>[.>>]
13+
14+
[This program sorts the bytes of its input by bubble sort.]

examples/helloworld.b

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
[ This program prints "Hello World!" and a newline to the screen; its
2+
length is 106 active command characters. [It is not the shortest.]
3+
4+
This loop is an "initial comment loop", a simple way of adding a comment
5+
to a BF program such that you don't have to worry about any command
6+
characters. Any ".", ",", "+", "-", "<" and ">" characters are simply
7+
ignored, the "[" and "]" characters just have to be balanced. This
8+
loop and the commands it contains are ignored because the current cell
9+
defaults to a value of 0; the 0 value causes this loop to be skipped.
10+
]
11+
++++++++ Set Cell #0 to 8
12+
[
13+
>++++ Add 4 to Cell #1; this will always set Cell #1 to 4
14+
[ as the cell will be cleared by the loop
15+
>++ Add 2 to Cell #2
16+
>+++ Add 3 to Cell #3
17+
>+++ Add 3 to Cell #4
18+
>+ Add 1 to Cell #5
19+
<<<<- Decrement the loop counter in Cell #1
20+
] Loop until Cell #1 is zero; number of iterations is 4
21+
>+ Add 1 to Cell #2
22+
>+ Add 1 to Cell #3
23+
>- Subtract 1 from Cell #4
24+
>>+ Add 1 to Cell #6
25+
[<] Move back to the first zero cell you find; this will
26+
be Cell #1 which was cleared by the previous loop
27+
<- Decrement the loop Counter in Cell #0
28+
] Loop until Cell #0 is zero; number of iterations is 8
29+
30+
The result of this is:
31+
Cell no : 0 1 2 3 4 5 6
32+
Contents: 0 0 72 104 88 32 8
33+
Pointer : ^
34+
35+
>>. Cell #2 has value 72 which is 'H'
36+
>---. Subtract 3 from Cell #3 to get 101 which is 'e'
37+
+++++++..+++. Likewise for 'llo' from Cell #3
38+
>>. Cell #5 is 32 for the space
39+
<-. Subtract 1 from Cell #4 for 87 to give a 'W'
40+
<. Cell #3 was set to 'o' from the end of 'Hello'
41+
+++.------.--------. Cell #3 for 'rl' and 'd'
42+
>>+. Add 1 to Cell #5 gives us an exclamation point
43+
>++. And finally a newline from Cell #6

examples/life.b

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[life.b -- John Horton Conway's Game of Life
2+
(c) 2021 Daniel B. Cristofani
3+
http://brainfuck.org/]
4+
5+
>>>->+>+++++>(++++++++++)[[>>>+<<<-]>+++++>+>>+[<<+>>>>>+<<<-]<-]>>>>[
6+
[>>>+>+<<<<-]+++>>+[<+>>>+>+<<<-]>>[>[[>>>+<<<-]<]<<++>+>>>>>>-]<-
7+
]+++>+>[[-]<+<[>+++++++++++++++++<-]<+]>>[
8+
[+++++++++.-------->>>]+[-<<<]>>>[>>,----------[>]<]<<[
9+
<<<[
10+
>--[<->>+>-<<-]<[[>>>]+>-[+>>+>-]+[<<<]<-]>++>[<+>-]
11+
>[[>>>]+[<<<]>>>-]+[->>>]<-[++>]>[------<]>+++[<<<]>
12+
]<
13+
]>[
14+
-[+>>+>-]+>>+>>>+>[<<<]>->+>[
15+
>[->+>+++>>++[>>>]+++<<<++<<<++[>>>]>>>]<<<[>[>>>]+>>>]
16+
<<<<<<<[<<++<+[-<<<+]->++>>>++>>>++<<<<]<<<+[-<<<+]+>->>->>
17+
]<<+<<+<<<+<<-[+<+<<-]+<+[
18+
->+>[-<-<<[<<<]>[>>[>>>]<<+<[<<<]>-]]
19+
<[<[<[<<<]>+>>[>>>]<<-]<[<<<]]>>>->>>[>>>]+>
20+
]>+[-<<[-]<]-[
21+
[>>>]<[<<[<<<]>>>>>+>[>>>]<-]>>>[>[>>>]<<<<+>[<<<]>>-]>
22+
]<<<<<<[---<-----[-[-[<->>+++<+++++++[-]]]]<+<+]>
23+
]>>
24+
]
25+
26+
[This program simulates the Game of Life cellular automaton.
27+
28+
It duplicates the interface of the classic program at
29+
http://www.linusakesson.net/programming/brainfuck/index.php,
30+
but this program was written from scratch.
31+
32+
Type e.g. "be" to toggle the fifth cell in the second row, "q" to quit,
33+
or a bare linefeed to advance one generation.
34+
35+
Grid wraps toroidally. Board size in parentheses in first line (2-166 work).
36+
37+
This program is licensed under a Creative Commons Attribution-ShareAlike 4.0
38+
International License (http://creativecommons.org/licenses/by-sa/4.0/).]

examples/rot13.b

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-,+[ Read first character and start outer character reading loop
2+
-[ Skip forward if character is 0
3+
>>++++[>++++++++<-] Set up divisor (32) for division loop
4+
(MEMORY LAYOUT: dividend copy remainder divisor quotient zero zero)
5+
<+<-[ Set up dividend (x minus 1) and enter division loop
6+
>+>+>-[>>>] Increase copy and remainder / reduce divisor / Normal case: skip forward
7+
<[[>+<-]>>+>] Special case: move remainder back to divisor and increase quotient
8+
<<<<<- Decrement dividend
9+
] End division loop
10+
]>>>[-]+ End skip loop; zero former divisor and reuse space for a flag
11+
>--[-[<->+++[-]]]<[ Zero that flag unless quotient was 2 or 3; zero quotient; check flag
12+
++++++++++++<[ If flag then set up divisor (13) for second division loop
13+
(MEMORY LAYOUT: zero copy dividend divisor remainder quotient zero zero)
14+
>-[>+>>] Reduce divisor; Normal case: increase remainder
15+
>[+[<+>-]>+>>] Special case: increase remainder / move it back to divisor / increase quotient
16+
<<<<<- Decrease dividend
17+
] End division loop
18+
>>[<+>-] Add remainder back to divisor to get a useful 13
19+
>[ Skip forward if quotient was 0
20+
-[ Decrement quotient and skip forward if quotient was 1
21+
-<<[-]>> Zero quotient and divisor if quotient was 2
22+
]<<[<<->>-]>> Zero divisor and subtract 13 from copy if quotient was 1
23+
]<<[<<+>>-] Zero divisor and add 13 to copy if quotient was 0
24+
] End outer skip loop (jump to here if ((character minus 1)/32) was not 2 or 3)
25+
<[-] Clear remainder from first division if second division was skipped
26+
<.[-] Output ROT13ed character from copy and clear it
27+
<-,+ Read next character
28+
] End character reading loop

project.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//> using scala "3.3.5"
2+
//> using jvm 17
3+
4+
//> using options -Wunused:imports,implicits,privates -Werror
5+
//> using option -Ykind-projector
6+
7+
//> using dep org.typelevel::cats-core:2.13.0
8+
//> using test.dep org.scalatest::scalatest:3.2.19
9+
10+
package dev.meetree.brainfuck
11+
12+
import scala.util.CommandLineParser
13+
import scala.util.chaining.*
14+
15+
import dev.meetree.brainfuck.core.Program
16+
import dev.meetree.brainfuck.interpreter.Interpreter
17+
import dev.meetree.brainfuck.parser.Parser
18+
19+
given CommandLineParser.FromString[Program] = Parser.parseOrThrow(_)
20+
21+
@main def run(program: Program): ExitCode = Interpreter.id
22+
.interpret(program)
23+
.fold(err => err.tap(onError).pipe(_ => ExitCode.Failure(1)), _ => ExitCode.Success)
24+
25+
private def onError(err: Throwable) = System.err.println(err.getMessage)
26+
27+
enum ExitCode(code: Int):
28+
case Success extends ExitCode(0)
29+
case Failure(code: Int) extends ExitCode(code)

0 commit comments

Comments
 (0)