Initial import of Etcha version 1.0 revision 2009.1004 distribution.
Cat's Eye Technologies
12 years ago
0 | JAVAC?="/cygdrive/c/Program Files/Java/jdk1.6.0_02/bin/javac" | |
1 | JAVA?="/cygdrive/c/Program Files/Java/jre1.6.0_07/bin/java" | |
2 | ||
3 | all: bin/tc/catseye/etcha/Executor.class | |
4 | ||
5 | bin/tc/catseye/etcha/Executor.class: src/Etcha.java | |
6 | $(JAVAC) -cp bin -d bin src/Etcha.java | |
7 | ||
8 | clean: | |
9 | rm bin/tc/catseye/etcha/*.class | |
10 | ||
11 | test: bin/tc/catseye/etcha/Executor.class | |
12 | $(JAVA) -cp bin tc.catseye.etcha.Main eg/test.etcha |
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/2002/REC-xhtml1-20020801/DTD/xhtml1-strict.dtd"> | |
1 | <html xmlns="http://www.w3.org/1999/xhtml" lang="en"> | |
2 | <head> | |
3 | <title>The Etcha Programming Language</title> | |
4 | <!-- begin html doc dynamic markup --> | |
5 | <script type="text/javascript" src="/contrib/jquery-1.6.4.min.js"></script> | |
6 | <script type="text/javascript" src="/scripts/documentation.js"></script> | |
7 | <!-- end html doc dynamic markup --> | |
8 | </head> | |
9 | <body> | |
10 | ||
11 | <h1>The Etcha Programming Language</h1> | |
12 | ||
13 | <h2>Introduction</h2> | |
14 | ||
15 | <p><dfn>Etcha</dfn> is an esoteric programming language based on Jeffry Johnston's <a class="external" href="http://www.esolangs.org/wiki/BitChanger">BitChanger</a>. | |
16 | Like BitChanger, Etcha has four instructions, two of which are used to form Brainfuck-like while-loops. | |
17 | Unlike BitChanger, Etcha has a 2-dimensional storage model based on <dfn>turtle graphics</dfn>, which permits it | |
18 | to be immediately used for an alternative purpose: graphical composition. Unlike a classical turtle in a language such | |
19 | as LOGO however, the turtle in Etcha is an integral part of the computation, playing a role similar to the | |
20 | tape head of a Turing machine.</p> | |
21 | ||
22 | <h2>Instructions</h2> | |
23 | ||
24 | <ul> | |
25 | <li><code>+</code> -- equivalent to FD 1</li> | |
26 | <li><code>></code> -- equivalent to RT 90; toggles PU/PD every 4 executions</li> | |
27 | <li><code>[</code> -- equivalent to While</li> | |
28 | <li><code>]</code> -- equivalent to Wend</li> | |
29 | </ul> | |
30 | ||
31 | <p>In Etcha, instructions control a <dfn>turtle</dfn>. The turtle exists vis-a-vis | |
32 | an unbounded Cartesian grid called the <dfn>playfield</dfn>. | |
33 | The turtle has a <dfn>position</dfn> in that it occupies exactly one of the points on the playfield | |
34 | (which are referred to as <dfn>pixels</dfn>). Each pixel has a | |
35 | <dfn>state</dfn>, which is either black or white; all pixels are initially black.</p> | |
36 | ||
37 | <p>The turtle also has an | |
38 | <dfn>orientation</dfn> which describes the direction it would move in should it travel forward. Unlike a conventional | |
39 | turtle, because of its Cartesian context, there are only four possible orientations which the Etcha turtle can possess: north, east, south, | |
40 | and west, corresponding to headings of 0, 90, 180, and 270 degrees. | |
41 | When an Etcha program starts, the turtle is initially oriented north. Because position is relative, it doesn't matter where the turtle is initially located, | |
42 | but solely for psychological satisfaction we can say that it is initially situated in the very center of this unbounded Cartesian grid.</p> | |
43 | ||
44 | <p>The turtle is also equipped with a <dfn>pen</dfn>, which has a <dfn>pen mode</dfn> and a <dfn>pen position</dfn>. | |
45 | The pen mode is always XOR, meaning that when moving forward by execution of <code>+</code>, the | |
46 | state of the pixel that was previously occupied by the turtle gets inverted (from black to white, or vice versa.) The pen position may be up or down. | |
47 | It is initially down. Every fourth time an <code>></code> instruction is executed, the pen's position is toggled from up to down | |
48 | or vice versa. These executions need not be consecutive; there may be any number of intervening instructions executed.</p> | |
49 | ||
50 | <h2>Computational Class</h2> | |
51 | ||
52 | <p>Etcha is Turing-complete if BitChanger is, because we can easily translate any BitChanger program into an equivalent Etcha | |
53 | program. Let the Etcha program begin with <code>>>>></code>, to initially lift the pen. The BitChanger | |
54 | instruction <code><</code> is translated to the Etcha instructions <code>>>>+>>>>></code>, | |
55 | and the BitChanger instruction <code>}</code> is translated to <code>>>>>>+>>></code>. | |
56 | The instructions <code>[</code> and <code>]</code> remain the same. The relation between the BitChanger tape state | |
57 | and the Etcha playfield is quite literal; the <var>y</var> dimension of the grid is simply ignored.</p> | |
58 | ||
59 | <h2>Implementation</h2> | |
60 | ||
61 | <p>In a particular implementation of Etcha on a microcomputer with a finite storage, the playfield cannot be truly unbounded, | |
62 | and there will come a point in a long enough program execution, entirely dependent on the capabilities of the hardware and the | |
63 | implementation, where a pixel change cannot be correctly stored. The behaviour after this point is undefined. <i>Such | |
64 | <b>m</b>icro<b>c</b>omputer implementations of Etcha may be marketed under the name "MC Etcha".</i></p> | |
65 | ||
66 | <p>Cat's Eye Technologies provides an implementation of Etcha written in the Java<sup>TM</sup> programming language. | |
67 | This implementation attempts to demonstrate that the Model-View-Controller design pattern can be applied | |
68 | not only to user interfaces, but also to programming language interpreters. The Model is the state of the | |
69 | program (which is also the state of the Turtle graphics engine.) The View is the interpreter's interpretation of that | |
70 | state, and the Controller is the interpreter's behaviour with respect to the View. For example, the Model exposes | |
71 | the pen up/down semantics, but it is the Controller that implements the rule that <code>>>>></code> | |
72 | toggles the pen position.</p> | |
73 | ||
74 | <p>Happy Etchin'! | |
75 | <br/>Chris Pressey | |
76 | <br/>October 4<sup>th</sup>, 2009 | |
77 | <br/>Mold City, USA</p> | |
78 | ||
79 | </body> | |
80 | </html> |
0 | >+++>+++>+++>+++>[+]>>>>+ |
0 | package tc.catseye.etcha; | |
1 | ||
2 | // Etcha.java | |
3 | // Ridiculous over-implementation of the Etcha programming language. | |
4 | // Chris Pressey, Cat's Eye Technologies. | |
5 | // $Id: Etcha.java 257 2009-10-04 03:21:43Z cpressey $ | |
6 | ||
7 | import java.util.List; | |
8 | import java.util.ArrayList; | |
9 | import java.util.Map; | |
10 | import java.util.HashMap; | |
11 | import java.util.Set; | |
12 | import java.util.HashSet; | |
13 | import java.io.BufferedReader; | |
14 | import java.io.FileReader; | |
15 | import java.io.IOException; | |
16 | ||
17 | interface Grid | |
18 | { | |
19 | void togglePixel(int x, int y); | |
20 | boolean isPixelBlack(int x, int y); | |
21 | } | |
22 | ||
23 | class DefaultGrid implements Grid | |
24 | { | |
25 | private HashMap<Integer, HashMap<Integer, Boolean>> grid; | |
26 | private int minX, minY, maxX, maxY; | |
27 | ||
28 | DefaultGrid() | |
29 | { | |
30 | grid = new HashMap<Integer, HashMap<Integer, Boolean>>(); | |
31 | minX = minY = maxX = maxY = 0; | |
32 | } | |
33 | ||
34 | private static boolean getBoolean(Boolean b) | |
35 | { | |
36 | return b == null ? false : b; | |
37 | } | |
38 | ||
39 | public void togglePixel(int x, int y) | |
40 | { | |
41 | HashMap<Integer, Boolean> column = grid.get(x); | |
42 | if (column == null) { | |
43 | column = new HashMap<Integer, Boolean>(); | |
44 | column.put(y, true); | |
45 | grid.put(x, column); | |
46 | } else { | |
47 | column.put(y, !getBoolean(column.get(y))); | |
48 | } | |
49 | if (x < minX) minX = x; | |
50 | if (y < minY) minY = y; | |
51 | if (x > maxX) maxX = x; | |
52 | if (y > maxY) maxY = y; | |
53 | } | |
54 | ||
55 | public boolean isPixelBlack(int x, int y) | |
56 | { | |
57 | HashMap<Integer, Boolean> column = grid.get(x); | |
58 | if (column == null) { | |
59 | return false; | |
60 | } else { | |
61 | return getBoolean(column.get(y)); | |
62 | } | |
63 | } | |
64 | ||
65 | public void dump() | |
66 | { | |
67 | int width = maxY - minY; | |
68 | for (int i = 0; i <= width; i++) | |
69 | System.out.print("-"); | |
70 | System.out.println(); | |
71 | for (int y = minY; y <= maxY; y++) { | |
72 | for (int x = minX; x <= maxX; x++) { | |
73 | System.out.print(isPixelBlack(x, y) ? "#" : " "); | |
74 | } | |
75 | System.out.println(); | |
76 | } | |
77 | for (int i = 0; i <= width; i++) | |
78 | System.out.print("-"); | |
79 | System.out.println(); | |
80 | } | |
81 | } | |
82 | ||
83 | interface Turtle | |
84 | { | |
85 | void forward(int distance); | |
86 | void rotate(int degrees); | |
87 | void setPenDown(boolean penDown); | |
88 | public boolean isPenDown(); | |
89 | boolean detectPixel(); | |
90 | } | |
91 | ||
92 | class DefaultTurtle implements Turtle | |
93 | { | |
94 | private Grid grid; | |
95 | private int x, y; | |
96 | private int dx, dy; | |
97 | private boolean penDown; | |
98 | private int heading; | |
99 | ||
100 | DefaultTurtle() | |
101 | { | |
102 | grid = new DefaultGrid(); | |
103 | x = 0; | |
104 | y = 0; | |
105 | penDown = true; | |
106 | heading = 0; | |
107 | } | |
108 | ||
109 | DefaultTurtle(Grid g) | |
110 | { | |
111 | grid = g; | |
112 | x = 0; | |
113 | y = 0; | |
114 | penDown = true; | |
115 | heading = 0; | |
116 | } | |
117 | ||
118 | public void forward(int distance) | |
119 | { | |
120 | for (int i = 0; i < distance; i++) { | |
121 | if (penDown) | |
122 | grid.togglePixel(x, y); | |
123 | x += dx(); | |
124 | y += dy(); | |
125 | } | |
126 | } | |
127 | ||
128 | private int dx() | |
129 | { | |
130 | if (heading == 90) | |
131 | return 1; | |
132 | if (heading == 270) | |
133 | return -1; | |
134 | return 0; | |
135 | } | |
136 | ||
137 | private int dy() | |
138 | { | |
139 | if (heading == 0) | |
140 | return -1; | |
141 | if (heading == 180) | |
142 | return 1; | |
143 | return 0; | |
144 | } | |
145 | ||
146 | public void rotate(int degrees) | |
147 | { | |
148 | heading += degrees; | |
149 | heading %= 360; | |
150 | } | |
151 | ||
152 | public void setPenDown(boolean penDown) | |
153 | { | |
154 | this.penDown = penDown; | |
155 | } | |
156 | ||
157 | public boolean isPenDown() | |
158 | { | |
159 | return penDown; | |
160 | } | |
161 | ||
162 | public boolean detectPixel() | |
163 | { | |
164 | return grid.isPixelBlack(x, y); | |
165 | } | |
166 | } | |
167 | ||
168 | interface Counter | |
169 | { | |
170 | public boolean count(); | |
171 | } | |
172 | ||
173 | class DefaultCounter implements Counter | |
174 | { | |
175 | private int modulus; | |
176 | private int counter; | |
177 | ||
178 | DefaultCounter() | |
179 | { | |
180 | modulus = 4; | |
181 | counter = 0; | |
182 | } | |
183 | ||
184 | DefaultCounter(int m) | |
185 | { | |
186 | modulus = m; | |
187 | counter = 0; | |
188 | } | |
189 | ||
190 | DefaultCounter(int m, int c) | |
191 | { | |
192 | modulus = m; | |
193 | counter = c; | |
194 | } | |
195 | ||
196 | public boolean count() | |
197 | { | |
198 | counter++; | |
199 | counter %= modulus; | |
200 | return (counter == 0); | |
201 | } | |
202 | } | |
203 | ||
204 | // An execution context knows what command it is currently executing. | |
205 | interface ExecutionContext | |
206 | { | |
207 | Turtle getTurtle(); | |
208 | Counter getCounter(); | |
209 | ExecutionContext step(); | |
210 | } | |
211 | ||
212 | class BaseExecutionContext implements ExecutionContext | |
213 | { | |
214 | protected Turtle turtle; | |
215 | protected Counter counter; | |
216 | protected ExecutionContext parent; | |
217 | ||
218 | BaseExecutionContext(Turtle t, Counter c) | |
219 | { | |
220 | turtle = t; | |
221 | counter = c; | |
222 | parent = null; | |
223 | } | |
224 | ||
225 | BaseExecutionContext(ExecutionContext parent) | |
226 | { | |
227 | turtle = parent.getTurtle(); | |
228 | counter = parent.getCounter(); | |
229 | this.parent = parent; | |
230 | } | |
231 | ||
232 | public Turtle getTurtle() | |
233 | { | |
234 | return turtle; | |
235 | } | |
236 | ||
237 | public Counter getCounter() | |
238 | { | |
239 | return counter; | |
240 | } | |
241 | ||
242 | public ExecutionContext step() | |
243 | { | |
244 | return parent; | |
245 | } | |
246 | } | |
247 | ||
248 | class PrimitiveExecutionContext extends BaseExecutionContext | |
249 | { | |
250 | protected Command cmd; | |
251 | ||
252 | PrimitiveExecutionContext(Command cmd, ExecutionContext parent) | |
253 | { | |
254 | super(parent); | |
255 | this.cmd = cmd; | |
256 | } | |
257 | ||
258 | public ExecutionContext step() | |
259 | { | |
260 | cmd.execute(turtle, counter); | |
261 | return parent; | |
262 | } | |
263 | } | |
264 | ||
265 | class BlockExecutionContext extends PrimitiveExecutionContext | |
266 | { | |
267 | protected int pos; | |
268 | protected Block cmd; | |
269 | ||
270 | BlockExecutionContext(Block cmd, ExecutionContext parent) | |
271 | { | |
272 | super(cmd, parent); | |
273 | this.cmd = cmd; // because our cmd is not our parent's! | |
274 | pos = 0; | |
275 | } | |
276 | ||
277 | public ExecutionContext step() | |
278 | { | |
279 | Command sub = cmd.get(pos); | |
280 | if (sub == null) { | |
281 | return parent; | |
282 | } | |
283 | pos++; | |
284 | ExecutionContext e = sub.createExecutionContext(this); | |
285 | // Could just return e here. But then we'd have to do twice as many | |
286 | // steps, stepping into each contained command | |
287 | return e.step(); | |
288 | } | |
289 | } | |
290 | ||
291 | class LoopExecutionContext extends BlockExecutionContext | |
292 | { | |
293 | protected Loop cmd; | |
294 | ||
295 | LoopExecutionContext(Loop cmd, ExecutionContext parent) | |
296 | { | |
297 | super(cmd, parent); | |
298 | this.cmd = cmd; // because our cmd is not our parent's! | |
299 | } | |
300 | ||
301 | public ExecutionContext step() | |
302 | { | |
303 | if (pos >= cmd.size()) { | |
304 | pos = 0; | |
305 | } | |
306 | if (pos == 0 && !turtle.detectPixel()) { | |
307 | return parent; | |
308 | } | |
309 | return super.step(); | |
310 | } | |
311 | } | |
312 | ||
313 | interface Command | |
314 | { | |
315 | void execute(Turtle t, Counter c); | |
316 | ExecutionContext createExecutionContext(ExecutionContext parent); | |
317 | } | |
318 | ||
319 | abstract class AbstractCommand implements Command | |
320 | { | |
321 | abstract public void execute(Turtle t, Counter c); | |
322 | public ExecutionContext createExecutionContext(ExecutionContext parent) | |
323 | { | |
324 | return new PrimitiveExecutionContext(this, parent); | |
325 | } | |
326 | } | |
327 | ||
328 | class Right extends AbstractCommand | |
329 | { | |
330 | public void execute(Turtle t, Counter c) | |
331 | { | |
332 | t.rotate(90); | |
333 | if (c.count()) | |
334 | t.setPenDown(!t.isPenDown()); | |
335 | } | |
336 | } | |
337 | ||
338 | class Forward extends AbstractCommand | |
339 | { | |
340 | public void execute(Turtle t, Counter c) | |
341 | { | |
342 | t.forward(1); | |
343 | } | |
344 | } | |
345 | ||
346 | class Block extends AbstractCommand | |
347 | { | |
348 | private List<Command> cmds; | |
349 | ||
350 | Block(List<Command> cmds) | |
351 | { | |
352 | this.cmds = cmds; | |
353 | } | |
354 | ||
355 | public Command get(int index) | |
356 | { | |
357 | try { | |
358 | return cmds.get(index); | |
359 | } catch (IndexOutOfBoundsException e) { | |
360 | return null; | |
361 | } | |
362 | } | |
363 | ||
364 | public int size() | |
365 | { | |
366 | return cmds.size(); | |
367 | } | |
368 | ||
369 | public void execute(Turtle t, Counter c) | |
370 | { | |
371 | for (Command cmd : cmds) { | |
372 | cmd.execute(t, c); | |
373 | } | |
374 | } | |
375 | ||
376 | public ExecutionContext createExecutionContext(ExecutionContext parent) | |
377 | { | |
378 | return new BlockExecutionContext(this, parent); | |
379 | } | |
380 | } | |
381 | ||
382 | class Loop extends Block | |
383 | { | |
384 | Loop(List<Command> cmds) | |
385 | { | |
386 | super(cmds); | |
387 | } | |
388 | ||
389 | public void execute(Turtle t, Counter c) | |
390 | { | |
391 | while (t.detectPixel()) { | |
392 | super.execute(t, c); | |
393 | } | |
394 | } | |
395 | ||
396 | public ExecutionContext createExecutionContext(ExecutionContext parent) | |
397 | { | |
398 | return new LoopExecutionContext(this, parent); | |
399 | } | |
400 | } | |
401 | ||
402 | class Executor | |
403 | { | |
404 | Turtle turtle; | |
405 | Counter counter; | |
406 | ||
407 | Executor() | |
408 | { | |
409 | turtle = new DefaultTurtle(); | |
410 | counter = new DefaultCounter(); | |
411 | } | |
412 | ||
413 | Executor(Turtle t, Counter c) | |
414 | { | |
415 | turtle = t; | |
416 | counter = c; | |
417 | } | |
418 | ||
419 | Executor(Grid g) | |
420 | { | |
421 | turtle = new DefaultTurtle(g); | |
422 | counter = new DefaultCounter(); | |
423 | } | |
424 | ||
425 | public void run(Command program) | |
426 | { | |
427 | program.execute(turtle, counter); | |
428 | } | |
429 | ||
430 | // same as run, but uses the step methods | |
431 | public void stepAll(Command program) | |
432 | { | |
433 | ExecutionContext base = new BaseExecutionContext(turtle, counter); | |
434 | ExecutionContext e = program.createExecutionContext(base); | |
435 | while (e != base) { | |
436 | e = e.step(); | |
437 | } | |
438 | } | |
439 | } | |
440 | ||
441 | interface Warner | |
442 | { | |
443 | void warn(String warning); | |
444 | } | |
445 | ||
446 | interface ParseFactory<X> | |
447 | { | |
448 | char getRepresentation(); | |
449 | X generate(Parser p); | |
450 | } | |
451 | ||
452 | abstract class AbstractParseFactory<X> implements ParseFactory<X> | |
453 | { | |
454 | public abstract char getRepresentation(); | |
455 | public abstract X generate(Parser p); | |
456 | } | |
457 | ||
458 | class ForwardParseFactory extends AbstractParseFactory<Command> | |
459 | { | |
460 | public char getRepresentation() | |
461 | { | |
462 | return '+'; | |
463 | } | |
464 | ||
465 | public Command generate(Parser p) | |
466 | { | |
467 | return new Forward(); | |
468 | } | |
469 | } | |
470 | ||
471 | class RightParseFactory extends AbstractParseFactory<Command> | |
472 | { | |
473 | public char getRepresentation() | |
474 | { | |
475 | return '>'; | |
476 | } | |
477 | ||
478 | public Command generate(Parser p) | |
479 | { | |
480 | return new Right(); | |
481 | } | |
482 | } | |
483 | ||
484 | class LoopParseFactory extends AbstractParseFactory<Command> | |
485 | { | |
486 | public char getRepresentation() | |
487 | { | |
488 | return '['; | |
489 | } | |
490 | ||
491 | public Command generate(Parser p) | |
492 | { | |
493 | p.advance(); | |
494 | Set<Character> until = new HashSet<Character>(); | |
495 | until.add(new Character(']')); | |
496 | List<Command> cmds = p.parseMany(until); | |
497 | return new Loop(cmds); | |
498 | } | |
499 | } | |
500 | ||
501 | class Parser | |
502 | { | |
503 | private String str; | |
504 | private int pos; | |
505 | private Warner warner; | |
506 | private Map<Character,ParseFactory<Command>> factoryMap; | |
507 | ||
508 | Parser(String s) | |
509 | { | |
510 | str = s; | |
511 | pos = 0; | |
512 | warner = null; | |
513 | factoryMap = new HashMap<Character,ParseFactory<Command>>(); | |
514 | ArrayList<ParseFactory<Command>> factories = new ArrayList<ParseFactory<Command>>(); | |
515 | factories.add(new ForwardParseFactory()); | |
516 | factories.add(new RightParseFactory()); | |
517 | factories.add(new LoopParseFactory()); | |
518 | for (ParseFactory<Command> factory : factories) { | |
519 | factoryMap.put(factory.getRepresentation(), factory); | |
520 | } | |
521 | } | |
522 | ||
523 | public void setWarner(Warner w) | |
524 | { | |
525 | warner = w; | |
526 | } | |
527 | ||
528 | public void advance() | |
529 | { | |
530 | pos++; | |
531 | } | |
532 | ||
533 | public Command parseOne() | |
534 | { | |
535 | char c = str.charAt(pos); | |
536 | Command cmd = null; | |
537 | ||
538 | ParseFactory<Command> factory = factoryMap.get(c); | |
539 | if (factory != null) { | |
540 | cmd = factory.generate(this); | |
541 | } else if (warner != null) { | |
542 | warner.warn("unrecognized command '" + c + | |
543 | "' at position " + pos); | |
544 | } | |
545 | advance(); | |
546 | ||
547 | return cmd; | |
548 | } | |
549 | ||
550 | public List<Command> parseMany(Set<Character> until) | |
551 | { | |
552 | List<Command> cmds = new ArrayList<Command>(); | |
553 | ||
554 | while (pos < str.length()) { | |
555 | char c = str.charAt(pos); | |
556 | if (until != null && until.contains(c)) | |
557 | break; | |
558 | Command cmd = parseOne(); | |
559 | if (cmd != null) | |
560 | cmds.add(cmd); | |
561 | } | |
562 | ||
563 | return cmds; | |
564 | } | |
565 | } | |
566 | ||
567 | // A command-line interface to our Etcha interpreter. | |
568 | class Main | |
569 | { | |
570 | static public void main(String argv[]) | |
571 | { | |
572 | // parse command line arguments | |
573 | for (String arg : argv) | |
574 | loadAndInterpret(arg); | |
575 | } | |
576 | ||
577 | static void loadAndInterpret(String filename) | |
578 | { | |
579 | // open and read file | |
580 | StringBuffer fileContents = new StringBuffer(); | |
581 | try { | |
582 | BufferedReader input = new BufferedReader(new FileReader(filename)); | |
583 | String str; | |
584 | str = input.readLine(); | |
585 | while (str != null) { | |
586 | fileContents.append(str); | |
587 | str = input.readLine(); | |
588 | } | |
589 | input.close(); | |
590 | } catch (IOException e) { | |
591 | System.err.println("Couldn't read '" + filename + "'"); | |
592 | return; | |
593 | } | |
594 | Parser parser = new Parser(fileContents.toString()); | |
595 | Set<Character> empty = new HashSet<Character>(); | |
596 | Block program = new Block(parser.parseMany(empty)); | |
597 | DefaultGrid grid = new DefaultGrid(); | |
598 | Executor executor = new Executor(grid); | |
599 | executor.stepAll(program); | |
600 | grid.dump(); | |
601 | } | |
602 | } |