-
Notifications
You must be signed in to change notification settings - Fork 0
/
brainfuck.nim
110 lines (87 loc) · 2.96 KB
/
brainfuck.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import macros
# dumpTree:
# while tape[tapePos] != '\0':
# inc tapePos
proc compile(code: string): PNimrodNode {.compiletime.} =
var stmts = @[newStmtList()]
template addStmt(text): stmt =
stmts[stmts.high].add parseStmt(text)
addStmt "var tape: array[1_000_000, char]"
addStmt "var tapePos = 0"
for c in code:
case c
of '+': addStmt "inc tape[tapePos]"
of '-': addStmt "dec tape[tapePos]"
of '>': addStmt "inc tapePos"
of '<': addStmt "dec tapePos"
of '.': addStmt "stdout.write tape[tapePos]"
of ',': addStmt "tape[tapePos] = stdin.readChar"
of '[': stmts.add newStmtList()
of ']':
var loop = newNimNode(nnkWhileStmt)
loop.add parseExpr("tape[tapePos] != '\\0'")
loop.add stmts.pop
stmts[stmts.high].add loop
else: discard
result = stmts[0]
# echo result.repr
# static:
# discard compile "+>+[-]>,."
macro compileString*(code: string): stmt =
## Compiles the brainfuck `code` string into Nim code that reads from stdin
## and writes to stdout.
compile code.strval
macro compileFile*(filename: string): stmt =
## Compiles the brainfuck code read from `filename` at compile time into Nim
## code that reads from stdin and writes to stdout.
compile staticRead(filename.strval)
proc interpret*(code: string) =
## Interprets the brainfuck `code` string, reading from stdin
## and writing to stdout.
var
tape = newSeq[char]()
codePos = 0
tapePos = 0
proc run(skip = false): bool =
while tapePos >= 0 and codePos < code.len:
if tapePos >= tape.len:
tape.add '\0'
if code[codePos] == '[':
inc codePos
let oldPos = codePos
while run(tape[tapePos] == '\0'):
codePos = oldPos
elif code[codePos] == ']':
return tape[tapePos] != '\0'
elif not skip:
case code[codePos]
of '+': inc tape[tapePos]
of '-': dec tape[tapePos]
of '>': inc tapePos
of '<': dec tapePos
of '.': stdout.write tape[tapePos]
of ',': tape[tapePos] = stdin.readChar
else: discard
inc codePos
discard run()
when isMainModule:
import os, docopt, tables, strutils
proc mandelbrot = compileFile("examples/mandelbrot.b")
let doc = """
brainfuck
Usage:
brainfuck mandelbrot
brainfuck interpret [<file.b>]
brainfuck (-h | --help)
brainfuck (-v | --version)
Options:
-h --help Show this screen.
-v --version Show version.
"""
let args = docopt(doc, version = "brainfuck 1.0")
if args["mandelbrot"]:
mandelbrot()
elif args["interpret"]:
let code = if args["<file.b>"]: readFile($args["<file.b>"])
else: readAll stdin
interpret code