comparison interps/pbrain/pbrain.cc @ 996:859f9b4339e6

<Gregor> tar xf egobot.tar.xz
author HackBot
date Sun, 09 Dec 2012 19:30:08 +0000
parents
children
comparison
equal deleted inserted replaced
995:6883f5911eb7 996:859f9b4339e6
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 }