996
|
1 /*
|
|
2 Interpreter for the pbrain programming language (procedural Brainf**k)
|
|
3 Copyright(C) Paul M. Parks
|
|
4 All Rights Reserved.
|
|
5
|
|
6 v1.4.3
|
|
7 2004/07/15 12:10
|
|
8
|
|
9 paul@parkscomputing.com
|
|
10 http://www.parkscomputing.com/
|
|
11
|
|
12 The syntax is the same as traditional Brainf**k, with the following
|
|
13 symbols added:
|
|
14
|
|
15 (
|
|
16 Begin procedure
|
|
17
|
|
18 )
|
|
19 End procecure
|
|
20
|
|
21 :
|
|
22 Call procedure identified by the value at the current location
|
|
23
|
|
24
|
|
25 Procedures are identified by numeric ID:
|
|
26
|
|
27 +([-])
|
|
28
|
|
29 Assuming the current location is zero, defines a procedure number 1 that
|
|
30 sets the current location to zero when called.
|
|
31
|
|
32 ++(<<[>>+<<-]>[>+<-]>)
|
|
33
|
|
34 Assuming the current location is zero, defines a procedure number 2 that
|
|
35 accepts two parameters. It adds parameter 1 and parameter 2 and places
|
|
36 the result in the location that was current when the procedure was
|
|
37 called, zeroing out parameters 1 and 2 in the process.
|
|
38
|
|
39 +++([-]>++++++++++[<++++>-]<++++++++>[-]++:.)
|
|
40
|
|
41 Assuming the current location is zero, defines a procedure 3 that prints
|
|
42 the ASCII equivalent of the numeral at the current location, between 0
|
|
43 and 9.
|
|
44
|
|
45 +++>+++++>++:
|
|
46
|
|
47 Calls procedure 2, passing in parameters 3 and 5.
|
|
48
|
|
49 All of the above examples may be combined into the program below. Note that
|
|
50 the procedures are numbered 1, 2, and 3 because the current location is
|
|
51 incremented prior to each procedure definition.
|
|
52
|
|
53 +([-])
|
|
54 +(-:<<[>>+<<-]>[>+<-]>)
|
|
55 +([-]>++++++++++[<++++>-]<++++++++>[-]++:.)
|
|
56 >+++>+++++>++:
|
|
57 >+++:
|
|
58
|
|
59 An error condition is reported with a short diagnostic to stderr and an
|
|
60 error number returned from the executable. Errors reported by the
|
|
61 interpreter are as follows:
|
|
62
|
|
63 1 - Out of memory
|
|
64 2 - Unknown procedure
|
|
65 3 - Memory address out of range
|
|
66 4 - Cannot find matching ] for beginning [
|
|
67 999 - Unknown exception
|
|
68
|
|
69 */
|
|
70
|
|
71
|
|
72 #include <vector>
|
|
73 #include <iostream>
|
|
74 #include <fstream>
|
|
75 #include <iterator>
|
|
76 #include <map>
|
|
77 #include <cstdlib>
|
|
78
|
|
79
|
|
80 #if defined(_MSC_VER)
|
|
81 #pragma warning(disable: 4571)
|
|
82 #endif
|
|
83
|
|
84 // Define the type contained in the memory array
|
|
85 #ifndef PBRAIN_MEM_TYPE
|
|
86 #define PBRAIN_MEM_TYPE int
|
|
87 #endif
|
|
88
|
|
89 // Define the character input/output type.
|
|
90 #ifndef PBRAIN_CHARACTER_TYPE
|
|
91 #define PBRAIN_CHARACTER_TYPE wchar_t
|
|
92 #endif
|
|
93
|
|
94 // Set the initial size of the memory array, if not defined externally.
|
|
95 #ifndef PBRAIN_INIT_MEM_SIZE
|
|
96 #define PBRAIN_INIT_MEM_SIZE 30000
|
|
97 #endif
|
|
98
|
|
99 // By default, use a dynamic array to store memory locations.
|
|
100 #ifndef PBRAIN_STATIC_MEMORY
|
|
101 typedef std::vector<PBRAIN_MEM_TYPE> Mem;
|
|
102 Mem mem(PBRAIN_INIT_MEM_SIZE);
|
|
103 Mem::size_type mp = 0;
|
|
104 #else
|
|
105 PBRAIN_MEM_TYPE mem[PBRAIN_INIT_MEM_SIZE];
|
|
106 size_t mp = 0;
|
|
107 #endif
|
|
108
|
|
109
|
|
110 // Placeholder template class to be specialized below.
|
|
111 template<typename Ch> struct io_types{};
|
|
112
|
|
113
|
|
114 // Define appropriate I/O and stream iterator types for working with byte
|
|
115 // characters.
|
|
116 template<> struct io_types<char>
|
|
117 {
|
|
118 static std::istream& cin;
|
|
119 static std::ostream& cout;
|
|
120 typedef std::basic_ifstream<char, std::char_traits<char> > ifstream;
|
|
121 typedef std::istream_iterator<char,char> istream_iterator;
|
|
122 };
|
|
123
|
|
124 std::istream& io_types<char>::cin = std::cin;
|
|
125 std::ostream& io_types<char>::cout = std::cout;
|
|
126
|
|
127
|
|
128 // Define appropriate I/O and stream iterator types for working with wide
|
|
129 // characters.
|
|
130 template<> struct io_types<wchar_t>
|
|
131 {
|
|
132 static std::wistream& cin;
|
|
133 static std::wostream& cout;
|
|
134 typedef std::basic_ifstream<wchar_t, std::char_traits<wchar_t> > ifstream;
|
|
135 typedef std::istream_iterator<wchar_t,wchar_t> istream_iterator;
|
|
136 };
|
|
137
|
|
138 std::wistream& io_types<wchar_t>::cin = std::wcin;
|
|
139 std::wostream& io_types<wchar_t>::cout = std::wcout;
|
|
140
|
|
141
|
|
142 // Useful type that chooses the appropriate typedefs for the character width
|
|
143 typedef io_types<PBRAIN_CHARACTER_TYPE> io;
|
|
144
|
|
145 // Type for storing a string of instructions; used for procedures and loops
|
|
146 typedef std::vector<PBRAIN_CHARACTER_TYPE> SourceBlock;
|
|
147
|
|
148 // Type for storing procedures indexed by number
|
|
149 typedef std::map<PBRAIN_MEM_TYPE, std::vector<PBRAIN_CHARACTER_TYPE> > Procedures;
|
|
150
|
|
151
|
|
152 // Map of procedure IDs to procedures
|
|
153 Procedures procedures;
|
|
154
|
|
155
|
|
156 // Interpret a container of instructions
|
|
157 template<typename It> void interpret(It ii, It eos)
|
|
158 {
|
|
159 while (ii != eos)
|
|
160 {
|
|
161 switch (*ii)
|
|
162 {
|
|
163 case '+':
|
|
164 ++mem[mp];
|
|
165 break;
|
|
166
|
|
167 case '-':
|
|
168 --mem[mp];
|
|
169 break;
|
|
170
|
|
171 case '>':
|
|
172 ++mp;
|
|
173
|
|
174 #ifndef PBRAIN_STATIC_MEMORY
|
|
175 // If memory is kept in a dynamic array, the array will grow as
|
|
176 // needed.
|
|
177 try
|
|
178 {
|
|
179 if (mp == mem.size())
|
|
180 {
|
|
181 mem.resize(mem.size() * 2);
|
|
182 }
|
|
183 }
|
|
184 catch (...)
|
|
185 {
|
|
186 // Ostensibly an out-of-memory condition.
|
|
187 throw 1;
|
|
188 }
|
|
189 #else
|
|
190 // Static memory cannot grow, so throw when limit reached
|
|
191 if (mp == sizeof(mem) / sizeof(PBRAIN_MEM_TYPE))
|
|
192 {
|
|
193 throw 1;
|
|
194 }
|
|
195 #endif
|
|
196
|
|
197 break;
|
|
198
|
|
199 case '<':
|
|
200 --mp;
|
|
201
|
|
202 // Throw out-of-range error if cell location is decremented below 0
|
|
203 if (static_cast<int>(mp) < 0)
|
|
204 {
|
|
205 throw 3;
|
|
206 }
|
|
207
|
|
208 break;
|
|
209
|
|
210 case '.':
|
|
211 io::cout.put(static_cast<PBRAIN_CHARACTER_TYPE>(mem[mp]));
|
|
212 break;
|
|
213
|
|
214 case ',':
|
|
215 mem[mp] = static_cast<PBRAIN_MEM_TYPE>(io::cin.get());
|
|
216 break;
|
|
217
|
|
218 case '[':
|
|
219 // Move to first instruction in the loop
|
|
220 ++ii;
|
|
221
|
|
222 {
|
|
223 int nest = 0;
|
|
224 It begin = ii;
|
|
225
|
|
226 // Find the matching ]
|
|
227 while (ii != eos)
|
|
228 {
|
|
229 if (*ii == '[')
|
|
230 {
|
|
231 ++nest;
|
|
232 }
|
|
233 else if (*ii == ']')
|
|
234 {
|
|
235 if (nest)
|
|
236 {
|
|
237 --nest;
|
|
238 }
|
|
239 else
|
|
240 {
|
|
241 break;
|
|
242 }
|
|
243 }
|
|
244
|
|
245 ++ii;
|
|
246 }
|
|
247
|
|
248 // If no matching ] is found in source block, report error.
|
|
249 if (ii == eos)
|
|
250 {
|
|
251 throw 4;
|
|
252 }
|
|
253
|
|
254 // At this point the iterator will point at the matching ]
|
|
255 // character, which is one instruction past the end of the range
|
|
256 // of instructions to be processed in a loop.
|
|
257 loop(begin, ii);
|
|
258 }
|
|
259
|
|
260 break;
|
|
261
|
|
262 case '(':
|
|
263 ++ii;
|
|
264
|
|
265 {
|
|
266 SourceBlock sourceBlock;
|
|
267
|
|
268 while (ii != eos && *ii != ')')
|
|
269 {
|
|
270 sourceBlock.push_back(*ii);
|
|
271 ++ii;
|
|
272 }
|
|
273
|
|
274 procedures.insert(std::make_pair(mem[mp], sourceBlock));
|
|
275 }
|
|
276
|
|
277 break;
|
|
278
|
|
279 case ':':
|
|
280 {
|
|
281 // Look up the source block that matches the value at the current
|
|
282 // location. If found, execute it.
|
|
283 Procedures::iterator i = procedures.find(mem[mp]);
|
|
284
|
|
285 if (i != procedures.end())
|
|
286 {
|
|
287 interpret(i->second.begin(), i->second.end());
|
|
288 }
|
|
289 else
|
|
290 {
|
|
291 throw 2;
|
|
292 }
|
|
293 }
|
|
294 break;
|
|
295
|
|
296 default:
|
|
297 break;
|
|
298 }
|
|
299
|
|
300 ++ii;
|
|
301 }
|
|
302 }
|
|
303
|
|
304
|
|
305 template<typename It> void loop(It ii, It eos)
|
|
306 {
|
|
307 // Interpret instructions until the value in the current memory location
|
|
308 // is zero
|
|
309 while (mem[mp])
|
|
310 {
|
|
311 interpret(ii, eos);
|
|
312 }
|
|
313 }
|
|
314
|
|
315
|
|
316 template<typename C> void parse(C& c)
|
|
317 {
|
|
318 io::istream_iterator ii(c);
|
|
319 io::istream_iterator eos;
|
|
320
|
|
321 SourceBlock sourceBlock;
|
|
322
|
|
323 // Copy instructions from the input stream to a source block.
|
|
324 while (ii != eos)
|
|
325 {
|
|
326 sourceBlock.push_back(*ii);
|
|
327 ++ii;
|
|
328 }
|
|
329
|
|
330 // Execute the instructions in the source block
|
|
331 interpret(sourceBlock.begin(), sourceBlock.end());
|
|
332 }
|
|
333
|
|
334
|
|
335 int main(int argc, char** argv)
|
|
336 {
|
|
337 try
|
|
338 {
|
|
339 // Read from a file if a filename is provided as an argument.
|
|
340 if (argc > 1)
|
|
341 {
|
|
342 io::ifstream source(argv[1]);
|
|
343
|
|
344 if (source.is_open())
|
|
345 {
|
|
346 parse(source);
|
|
347 }
|
|
348 }
|
|
349 // Otherwise interpret code from stdin
|
|
350 else
|
|
351 {
|
|
352 parse(io::cin);
|
|
353 }
|
|
354 }
|
|
355 catch(int e)
|
|
356 {
|
|
357 std::cerr << "Error " << e << ", cell " << (unsigned int)(mp) << "\n";
|
|
358 exit(e);
|
|
359 }
|
|
360 catch(...)
|
|
361 {
|
|
362 std::cerr << "Error " << 999 << ", cell " << (unsigned int)(mp) << "\n";
|
|
363 exit(999);
|
|
364 }
|
|
365 }
|