-
Notifications
You must be signed in to change notification settings - Fork 0
/
BrainFInterpreter.cpp
123 lines (108 loc) · 3.82 KB
/
BrainFInterpreter.cpp
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
111
112
113
114
115
116
117
118
119
120
121
122
123
//===-- BrainFInterpreter.cpp - BrainF trace compiler interpreter -------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===--------------------------------------------------------------------===//
#include "BrainF.h"
#include "BrainFVM.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdio>
using namespace llvm;
//Command line options
static cl::opt<std::string>
InputFilename(cl::Positional, cl::desc("<input brainf>"));
int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv, " BrainF compiler\n");
if (InputFilename == "") {
errs() << "Error: You must specify the filename of the program to "
"be compiled. Use --help to see the options.\n";
abort();
}
// Read the input file.
MemoryBuffer *Code = MemoryBuffer::getFileOrSTDIN(InputFilename);
const uint8_t *CodeBegin = (const uint8_t*)(Code->getBufferStart());
// Create a new buffer to hold the preprocessed code.
MemoryBuffer *ParsedCode =
MemoryBuffer::getNewMemBuffer(sizeof(opcode_func_t) *
(Code->getBufferSize()+1));
BytecodeArray = (opcode_func_t*)(ParsedCode->getBufferStart());
size_t BytecodeOffset = 0;
// Create JumpMap, a special on-the-side data array used to implement
// efficient jumps in the interpreter.
JumpMap = new size_t[Code->getBufferSize()];
memset(JumpMap, 0, sizeof(size_t) * Code->getBufferSize());
std::vector<size_t> Stack;
// Preprocess the input source code, performing three tasks:
// 1 - Remove non-instruction characters
// 2 - Replace character literals with opcode function pointers
// 3 - Precompute the jump targets for [ and ] instructions in JumpMap
for (size_t i = 0; i < Code->getBufferSize(); ++i) {
uint8_t opcode = CodeBegin[i];
switch (opcode) {
case '>':
BytecodeArray[BytecodeOffset++] = &op_right;
break;
case '<':
BytecodeArray[BytecodeOffset++] = &op_left;
break;
case '+':
BytecodeArray[BytecodeOffset++] = &op_plus;
break;
case '-':
BytecodeArray[BytecodeOffset++] = &op_minus;
break;
case '.':
BytecodeArray[BytecodeOffset++] = &op_put;
break;
case ',':
BytecodeArray[BytecodeOffset++] = &op_get;
break;
case '[':
Stack.push_back(BytecodeOffset);
BytecodeArray[BytecodeOffset++] = &op_if;
break;
case ']':
// Special case: [-] --> 0
if (CodeBegin[i-1] == '-' && CodeBegin[i-2] == '[') {
Stack.pop_back();
BytecodeOffset -= 2;
BytecodeArray[BytecodeOffset++] = &op_set_zero;
} else {
JumpMap[Stack.back()] = BytecodeOffset;
JumpMap[BytecodeOffset] = Stack.back();
Stack.pop_back();
BytecodeArray[BytecodeOffset++] = &op_back;
}
break;
default:
continue;
}
}
// Fill in the suffix of the preprocessed source for op_exit.
// Thus, if we reach the end of the source, the program will terminate.
while (BytecodeOffset < Code->getBufferSize()+1) {
BytecodeArray[BytecodeOffset++] = &op_end;
}
// Setup the array.
uint8_t *BrainFArray = new uint8_t[32768];
memset(BrainFArray, 0, 32768);
// Setup the trace recorder.
Recorder = new BrainFTraceRecorder();
// Main interpreter loop.
// Note the lack of a explicit loop: every opcode is a tail-recursive
// function that calls its own successor by indexing into BytecodeArray.
uint8_t* data = BrainFArray;
BytecodeArray[0](0, data);
//Clean up
delete Recorder;
delete Code;
delete ParsedCode;
delete[] BrainFArray;
delete[] JumpMap;
return 0;
}