git @ Cat's Eye Technologies Flobnar / a22073b
Initial import of Flobnar version 0.1 revision 2011.1214. catseye 10 years ago
3 changed file(s) with 977 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 -> encoding: UTF-8
1
2 Flobnar
3 =======
4
5 _Version 0.1, Chris Pressey, Oct 28 2011_
6
7 One day in September of 2011 -- though I'm not sure precisely which
8 one -- marked Befunge-93's 18th birthday. That means that Befunge is
9 now old enough to drink in its native land of Canada.
10
11 To celebrate this, I thought I'd get Befunge-93 drunk to see what
12 would happen.
13
14 What happened was _Flobnar_, an esolang which is in many respects a
15 functional dual of Befunge-93; most of the symbols have analogous
16 meanings, but execution proceeds in a much more dataflow-like fashion.
17
18 This document describes Flobnar with a series of examples, presented
19 in the format of Falderal 0.4 tests.
20
21 Concepts
22 --------
23
24 A familiarity with Befunge-93 is assumed in this document. Also,
25 some concepts need to be explained before the description of the
26 examples will make much sense.
27
28 Like Befunge-93, Flobnar programs are held in a playfield -- a
29 two-dimensional Cartesian grid of cells, each of which contains a
30 symbol.
31
32 Any cell in a Flobnar playfield may be evaluated. The meaning of
33 the evaluation of a cell depends on the symbol it contains. In the
34 context of execution, this symbol is called a term.
35
36 Except for the first term to be evaluated, all terms are "evaluated
37 from" one of the four cardinal directions: north, south, east, and
38 west. The direction provides context: a term may evaluate to a
39 different value depending on what direction it is evaluated from.
40
41 When we say something about "what the cell to the /d/ evaluates to",
42 where /d/ is a direction, it is implied that that cell is evaluated
43 from the direction opposite to /d/. So, "what the cell to the north
44 evaluates to" means "what the cell to the north evaluates to, when
45 evaluated from the south."
46
47 In addition, when we say "what the cell on the other side evaluates
48 to", we mean this to be relative to the direction the current term
49 was evaluated from. So, if the term was evaluated from the east,
50 the "cell on the other side" refers to the cell to the west, as
51 evaluated from the east.
52
53 Flobnar is not a purely functional language; it permits input and
54 output, as well as self-modification, just like Befunge-93 does.
55 For this reason, order of evaluation should be completely defined.
56
57 Flobnar Tests
58 -------------
59
60 -> Tests for functionality "Interpret Flobnar program"
61
62 -> Functionality "Interpret Flobnar program" is implemented by
63 -> Haskell function Flobnar:showRun
64
65 Basics of Execution
66 -------------------
67
68 Whereas in Befunge-93 `@` indicates a stopping point of the program,
69 in Flobnar, `@` indicates the starting point. The program evaluates
70 to whatever the `@` it contains evaluates to. The `@` evaluates to
71 whatever is west of it evaluates to.
72
73 | 4@
74 = Result: 4
75
76 The program must contain one and only one @.
77
78 | 4
79 ? Program does not contain exactly one @
80
81 | 4@@
82 ? Program does not contain exactly one @
83
84 Simple Constant Data
85 --------------------
86
87 As in Befunge-93, single digits evaluate to the common decimal
88 interpretation of themselves as numbers. You've already seen this
89 for 4, but it's true for all of them.
90
91 | 0@
92 = Result: 0
93
94 | 1@
95 = Result: 1
96
97 | 2@
98 = Result: 2
99
100 | 3@
101 = Result: 3
102
103 | 5@
104 = Result: 5
105
106 | 6@
107 = Result: 6
108
109 | 7@
110 = Result: 7
111
112 | 8@
113 = Result: 8
114
115 | 9@
116 = Result: 9
117
118 Playfield Traversal
119 -------------------
120
121 Whereas in Befunge-93 `><^v` change the direction of the motion
122 of the IP, in Flobnar these characters evaluate to what the
123 appropriate adjacent cell evaluates to:
124
125 < evaluates to whatever is west of it evaluates to
126 > evaluates to whatever is east of it evaluates to
127 v evaluates to whatever is south of it evaluates to
128 ^ evaluates to whatever is north of it evaluates to
129
130 | 4<<<<<@
131 = Result: 4
132
133 | >>>>>v
134 | ^ v
135 | ^ 4
136 | ^<<<<@
137 = Result: 4
138
139 Also, ' ' (blank space) evaluates to whatever the cell on the other
140 side of it evaluates to. So, for example, if evaluated from the
141 south, it evaluates to what the north of it evaluates to.
142
143 | 4 @
144 = Result: 4
145
146 | > v
147 |
148 | 4
149 | ^ @
150 = Result: 4
151
152 Cells which are not specified are considered to contain blank space.
153 (In the example below, the two middle lines have nothing in them, not
154 even blank space.)
155
156 | v@
157 |
158 |
159 | 4 <
160 = Result: 4
161
162 Like Befunge-93, there is toroidal wrapping of evaluation: if we try
163 to evaluate something outside the bounds of the playfield, we end up
164 evaluating whatever is directly on the other side of the playfield.
165 Unlike Befunge-93, however, the bounds of the playfield are determined
166 solely by the minimal bounding box that encompasses all the non-' '
167 terms in the playfield.
168
169 | @4
170 = Result: 4
171
172 | v@
173 | < v
174 | ^<
175 | 4
176 = Result: 4
177
178 There's a "Bridge" term, similar to Befunge's `#` instruction. It
179 evaluates to whatever is one cell past the other side of it.
180
181 | 5 6#@
182 = Result: 5
183
184 | 7v @
185 | v8#<
186 | >#9 v
187 | >^
188 | ^ <
189 = Result: 7
190
191 And `#` is compatible with wrapping.
192
193 | #@ 56
194 = Result: 5
195
196 And we were serious when we said that thing about how the bounds of
197 the playfield are computed.
198
199 |
200 | v @
201 | #< 17
202 |
203 = Result: 1
204
205 Arithmetic
206 ----------
207
208 The `+` term evaluates whatever is to the north of it, then evaluates
209 whatever is to the south of it, and evaluates to the sum of those
210 two resulting values.
211
212 | 5
213 | +@
214 | 7
215 = Result: 12
216
217 | 5<<
218 | +<<
219 | 7<< +<@
220 | 6<
221 = Result: 18
222
223 The `*` term evaluates whatever is to the north of it, then evaluates
224 whatever is to the south of it, and evaluates to the product of those
225 two resulting values.
226
227 | 5
228 | *@
229 | 7
230 = Result: 35
231
232 The `-` term evaluates whatever is to the north of it (and we call that
233 /a/), then evaluates whatever is to the south of it (and we call that /b/).
234 It evaluates to the difference, /a/ - /b/.
235
236 | 7
237 | -@
238 | 5
239 = Result: 2
240
241 Subtraction resulting in a negative value.
242
243 | 1
244 | -@
245 | 9
246 = Result: -8
247
248 The `/` term evaluates whatever is to the north of it (and we call that
249 /a/), then evaluates whatever is to the south of it (and we call that /b/).
250 It evaluates to the quotient of dividing /a/ by /b/.
251
252 | 8
253 | /@
254 | 2
255 = Result: 4
256
257 Integer division rounds down.
258
259 | 9
260 | /@
261 | 2
262 = Result: 4
263
264 Division by zero evaluates to whatever the cell on the other side
265 of the `/` term evaluates to.
266
267 | 9
268 | 7/@
269 | 0
270 = Result: 7
271
272 | v9#@
273 | >/7
274 | 0
275 = Result: 7
276
277 The `%` term evaluates whatever is to the north of it (and we call that
278 /a/), then evaluates whatever is to the south of it (and we call that /b/).
279 It evaluates to the remainder of dividing /a/ by /b/. This operation is
280 called "modulo".
281
282 | 8
283 | %@
284 | 3
285 = Result: 2
286
287 Modulo of a negative value has the sign of the dividend.
288
289 | 7
290 | 0%@
291 | +<
292 | 3
293 = Result: 1
294
295 | 7
296 | 0%@
297 | -<
298 | 3
299 = Result: 1
300
301 Modulo by zero evaluates to whatever the cell on the other side
302 evaluates to.
303
304 | 9
305 | 7%@
306 | 0
307 = Result: 7
308
309 | v9#@
310 | >%7
311 | 0
312 = Result: 7
313
314 Decision Making
315 ---------------
316
317 'Horizontal if', denoted `_`, checks what the cell on the other side
318 of it evaluates to. If that value is nonzero, it evaluates to what
319 the cell west of it evaluates to; otherwise, it evaluates to what the
320 cell east of it evaluates to. In either case, at most two evaluations
321 are made.
322
323 | 0
324 | 5_9
325 | ^@
326 = Result: 9
327
328 | 7
329 |
330 | 5 _ 9
331 |
332 | ^@
333 = Result: 5
334
335 | v<
336 |
337 | 5 _ 9
338 |
339 | 7^@
340 = Result: 5
341
342 'Vertical if', denoted `|`, checks what the other side of it evaluates to.
343 If that value is nonzero, it evaluates to what the cell north of it
344 evaluates to; otherwise, it evaluates to what the cell south of it
345 evaluates to. In either case, at most two evaluations are made.
346
347 | 3
348 | 0|@
349 | 4
350 = Result: 4
351
352 | 3
353 |
354 | 9 | @
355 |
356 | 4
357 = Result: 3
358
359 | 3
360 | v @
361 | > | 9
362 |
363 | 4
364 = Result: 3
365
366 These "if"s can be used to evaluate a cell for its side-effects only.
367 In the following, the sum is evaluated, but the result is effectively
368 thrown out, in preference to the zero.
369
370 | 90 <
371 | +|@
372 | 9> ^
373 = Result: 0
374
375 Like Befunge-93, `!` is logical negation: it evaluates to zero if the
376 cell on the other side evaluates to non-zero, and to one if the cell on
377 the other side evaluates to zero.
378
379 | 0!@
380 = Result: 1
381
382 | > v
383 | ^@ !
384 | 9
385 = Result: 0
386
387 We don't need greater than, because we can subtract one value
388 from other, divide the result by itself (specifying a result of 0
389 if the division is by zero), then add one, and check if that is
390 non-zero or not with a horizontal or vertical if.
391
392 But because Befunge-93 has it, we have it too. The <code>`</code> term
393 evaluates whatever is to the north of it (and we call that /a/), then
394 evaluates whatever is to the south of it (and we call that /b/). It
395 evaluates to 1 if /a/ is greater than /b/, 0 otherwise.
396
397 | 8
398 | `@
399 | 7
400 = Result: 1
401
402 | 8
403 | `@
404 | 8
405 = Result: 0
406
407 | 8
408 | `@
409 | 9
410 = Result: 0
411
412 `?` picks one of the cardinal directions at random and evaluates
413 to whatever the cell in that direction evaluates to. `?` should
414 use a fair distribution of the four possible choices, and should
415 be difficult to predict. We will not present this as a testable
416 example program, because the Falderal test framework doesn't
417 provide a way to test that, currently. (And it's not implemented
418 yet, but never mind that.) Instead, here is a plain example.
419
420 1
421 2?3#@
422 4
423
424 The above program should evaluate to 1 25% of the time, 2 25% of
425 the time, 3 25% of the time, and 4 the rest of the time.
426
427 Introspection and Self-Modification
428 -----------------------------------
429
430 Just like Befunge-93, program introspection and self-modification
431 are fully supported.
432
433 The `g` term evaluates to the north to get an x coordinate, then
434 to the south to get a y coordinate, and evaluates to the ASCII value
435 of the symbol that's found in that cell in the playfield. The origin
436 (coordinates (0,0)) of the playfield is the upper-left corner of that
437 bounding box I mentioned above, and x values increase to the right,
438 and y values to the south.
439
440 | A0
441 | g@
442 | 0
443 = Result: 65
444
445 The `p` term evaluates to the north to get an x coordinate, then
446 to the south to get a y coordinate. It then evaluates what is on
447 the other side of it to get a value. It then alters the playfield
448 in effect, by placing that value at that (x,y) coordinate. The
449 coordinate system is the same as that used by `g`. The `p` term
450 always itself evaluates to zero.
451
452 | 0
453 | 5p @
454 | 0
455 = Result: 0
456
457 | 0
458 | 5 p <
459 | 0 +@
460 | g <
461 | 0
462 = Result: 5
463
464 | 0
465 | > p 5
466 | +@
467 | 0
468 | > g
469 | 0
470 = Result: 5
471
472 Writing a space over an existing cell deletes that cell, and affects
473 the calculation of the bounds of the playfield.
474
475 | 85 5
476 | *p<
477 | 40+@
478 | > +
479 | 9
480 | 9
481 = Result: 18
482
483 | 5
484 | 85 #
485 | *p<
486 | 40+@
487 | > ^
488 | 6
489 | 9
490 = Result: 6
491
492 Writing outside the bounds of the playfield expands those bounds.
493 Since only cardinal directions are allowed in evaluation, the space
494 is still topologically a torus; no Lahey-space-like construction
495 is necessary.
496
497 | 99> v
498 | 7p*^@ >>#
499 | 16 >+
500 | <^
501 = Result: 7
502
503 Every cell in the playfield can hold a signed, unbounded integer.
504
505 | c 00
506 | -p <
507 | 90 +@
508 | g <
509 | 0
510 = Result: -9
511
512 | 9
513 | *< 0
514 | 9* p <
515 | *< 0 +@
516 | 9 g <
517 | 0
518 = Result: 6561
519
520 (One consequence of the above two facts is that there are at least
521 two tactics available for demonstrating that Flobnar is Turing-
522 complete; the playfield could be used as a tape in the simulation
523 of a Turing machine, or two cells could be used as registers in
524 the simulation of a Minsky machine.)
525
526 Evaluating a cell whose value is not the ASCII value of any of the
527 characters which denote terms defined in this document is a
528 runtime error, which results in the immediate termination of the
529 program, without producing a result value.
530
531 9
532 *<5
533 9*p<
534 *<0+@7
535 9 > v
536
537 The above program will result in a runtime error.
538
539 Functions
540 ---------
541
542 There's no real equivalent to Befunge-93's `:`, because there's no
543 need. Common subexpressions can be shared geometrically.
544
545 | v<
546 | 5+@
547 | ^<
548 = Result: 10
549
550 Likewise, there are no equivalents for `\` and `$`. Therefore, these
551 symbols have different meanings in Flobnar.
552
553 Originally, my idea for Flobnar included function values (lambda
554 functions.) But eventually these struck me as un-Befunge-like.
555 A function is just some code you want to be able to evaluate more
556 than once without repeating verbatim. And in the context of Befunge,
557 a function is just a part of the playfield. It's already possible
558 to execute the same part of the playfield from different points in
559 your program, using arrows; and in Flobnar this is even easier,
560 since evaluation of a part of th playfield "remembers" where it was
561 "evaluated from".
562
563 What's really useful in a function is that it can take an argument.
564 So I retained the idea of having arguments available -- a call stack.
565 Surprisingly, it turned out to be similar to Befunge-93's stack, so I
566 consider that a bonus.
567
568 The `\` term takes what to the south of it evaluates to, and uses
569 that as the argument as it "applies" the "one-argument" "function" on
570 the other side of it. The `:` term evaluates to the current argument.
571
572 | 5\@
573 | 0
574 = Result: 5
575
576 | :
577 | +\@
578 | 54
579 = Result: 9
580
581 | v 1# \ @
582 | > +
583 |
584 | : 7
585 = Result: 8
586
587 | > v :
588 | ^@>\*
589 | 7:
590 = Result: 49
591
592 If no function is being applied, `:` evaluates to zero.
593
594 | :@
595 = Result: 0
596
597 A function can call another function. The outer function retains its
598 argument after the inner function returns.
599
600 | 1
601 | +\<
602 | :4+\@
603 | :7
604 = Result: 12
605
606 Hellooooo, factorial!
607
608 | > v
609 | ^\ <
610 |
611 | :v v \<@
612 | -< : 6
613 | 1 : > *
614 | -| <
615 | 11
616 = Result: 720
617
618 The `$` term removes the top value from the call stack and "calls" the
619 "function" on the other side with this reduced call stack. This, in
620 effect, lets you write functions which take multiple arguments.
621
622 | :
623 | +\<<\@
624 | :7 9
625 = Result: 14
626
627 | :
628 | $
629 | +\<<\@
630 | :7 9
631 = Result: 16
632
633 Input and Output
634 ----------------
635
636 Flobnar supports input and output of ASCII characters, although
637 because the Falderal test framework doesn't handle tests with
638 input very well (and because I would have to refactor my beautiful
639 implementation in a major way, either threading an IO monad
640 through all the evaluation functions, or converting those
641 functions to continuation-passing style), they are only briefly
642 covered here, with only plain examples. My apologies if they are
643 not very well defined; a future version of the language and the
644 test suite may attempt to rectify that.
645
646 The `,` term evaluates what is on the other side of it and
647 outputs the character with that ASCII value to standard output.
648 The `,` term itself evaluates to zero. So, the following
649 example should output the two-character string 'Hi', and evaluate
650 to a result of zero.
651
652 8
653 *,< 5
654 9 +@>*
655 >,*7
656 3
657
658 Note that the convention of the result of each program being
659 printed after "Result: ", in the tests here, is merely a convention.
660 What the implementation does with the result of the main Flobnar
661 expression is outside the domain of Flobnar proper. (Of course,
662 it is extremely useful if it can make this value available to the
663 outside world somehow, for example by outputting it after the
664 string "Result: ".)
665
666 In similar vein, attempting to output and integer outside the range
667 of ASCII is, as of this writing, undefined.
668
669 The `~` term reads a character from standard input and evaluates
670 to the ASCII value of that character. So, the following program
671 reads two characters, and evaluates to 1 if they are the same
672 character, and 0 if they are not.
673
674 ~
675 -!@
676 ~
677
678 Putting these two together, the following program should be the
679 virtual equivalent of the Unix `cat` utility:
680
681 ~,<
682 +<@
683 >^
684
685 Other Things
686 ------------
687
688 The terms denoted by all characters not mentioned in the above
689 sections are undefined. For maximum compatibility with future
690 versions of Flobnar, they should not appear in a Flobnar program.
691
692 Specifically, I have a vague idea that extensions to Flobnar
693 may be indicated by the presence of a certain characters or
694 combination of characters immediately and non-wrappingly to
695 the east of the `@` term. So, best to leave that cell blank or
696 make it an arrow.
697
698 As you've probably noticed, I've referred to the character set
699 as ASCII in this entire document. I actually mean "printable
700 ASCII" -- control characters and whitespace (aside from space
701 and linefeed) are not defined, and (except for specific cases
702 addressed in this document) an implementation is not expected
703 to load them into the playfield. If at some point Flobnar is
704 ever extended into the realm of Unicode, source files will be
705 expected to be encoded in UTF-8.
706
707 To be really true to Befunge-93, Flobnar should support `.` for
708 outputting integers formatted in conventional decimal notation,
709 and `&` for inputting integers in that format too. They may
710 appear in a future version of the language. On the other hand,
711 they may not. I can't see the future.
712
713 After all that, the only thing from Befunge-93 that's missing a
714 counterpart in Flobnar is stringmode. I originally added it to
715 Flobnar, having each string evaluate to a string value, but that
716 complicated evaluation rules by adding a new type that would have
717 to be handled everywhere. I afterwards considered making it more
718 like ':', pushing the ASCII value of each character onto the call
719 stack, but decided that was a little awkward too. So, for
720 simplicity, I just left it out of this version.
721
722 That's All
723 ----------
724
725 Happy bar-hopping!
726 Chris Pressey
727 Evanston, Illinois
728 October 28, 2011
0 -- encoding: UTF-8
1
2 --
3 -- Copyright (c)2011 Chris Pressey, Cat's Eye Technologies.
4 -- All rights reserved.
5 --
6 -- Redistribution and use in source and binary forms, with or without
7 -- modification, are permitted provided that the following conditions
8 -- are met:
9 --
10 -- 1. Redistributions of source code must retain the above copyright
11 -- notices, this list of conditions and the following disclaimer.
12 -- 2. Redistributions in binary form must reproduce the above copyright
13 -- notices, this list of conditions, and the following disclaimer in
14 -- the documentation and/or other materials provided with the
15 -- distribution.
16 -- 3. Neither the names of the copyright holders nor the names of their
17 -- contributors may be used to endorse or promote products derived
18 -- from this software without specific prior written permission.
19 --
20 -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 -- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES INCLUDING, BUT NOT
22 -- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 -- FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 -- COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 -- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 -- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 -- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 -- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 -- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 -- POSSIBILITY OF SUCH DAMAGE.
32 --
33
34 module Flobnar where
35
36 import qualified Data.Map as Map
37 import qualified Char as Char
38
39 data Value = IntVal Integer
40 deriving (Show, Ord, Eq)
41
42 -- ======================================= --
43 -- Playfield data definition and functions --
44 -- ======================================= --
45
46 type Playfield = Map.Map (Integer,Integer) Integer
47
48 emptyPlayfield :: Playfield
49 emptyPlayfield = Map.empty
50
51 get :: Playfield -> Integer -> Integer -> Integer
52 get pf x y = Map.findWithDefault 32 (x, y) pf
53
54 put :: Playfield -> Integer -> Integer -> Integer -> Playfield
55 put pf x y value =
56 case value of
57 32 -> Map.delete (x, y) pf
58 _ -> Map.insert (x, y) value pf
59
60 putc :: Playfield -> Integer -> Integer -> Char -> Playfield
61 putc pf x y char = put pf x y (toInteger $ Char.ord char)
62
63 loadLine pf x y [] = pf
64 loadLine pf x y (char:chars) =
65 loadLine (putc pf x y char) (x+1) y chars
66
67 loadLines pf x y [] = pf
68 loadLines pf x y (line:lines) =
69 loadLines (loadLine pf x y line) x (y+1) lines
70
71 load lines = loadLines emptyPlayfield 0 0 lines
72
73 locate pf value =
74 let
75 f accum key val =
76 if val == value then
77 ((key:accum), val)
78 else
79 (accum, val)
80 in
81 fst $ Map.mapAccumWithKey f [] pf
82
83 extents pf =
84 let
85 f (lowX, lowY, highX, highY) (x, y) val =
86 let
87 lowX' = if x < lowX then x else lowX
88 lowY' = if y < lowY then y else lowY
89 highX' = if x > highX then x else highX
90 highY' = if y > highY then y else highY
91 in
92 ((lowX', lowY', highX', highY'), val)
93 in
94 fst $ Map.mapAccumWithKey f (1000, 1000, (-1000), (-1000)) pf
95
96 -- ================== --
97 -- Flobnar evaluation --
98 -- ================== --
99
100 --
101 -- Evaluation is implemented as a set of mutually recursive functions.
102 -- Evaluation functions return a pair of (result value, new playfield).
103 -- All terms except p leave the playfield unchanged.
104 --
105 -- env is a list of values; each value is the argument that was passed
106 -- to a function that was called to get here.
107 --
108 -- dx and dy are the delta that the expression is being evaluated from:
109 --
110 -- dx=0, dy=1: being evaluated from the north (toward the south)
111 -- dx=0, dy=-1: being evaluated from the south (toward the north)
112 -- dx=1, dy=0: being evaluated from the west (toward the east)
113 -- dx=-1, dy=0: being evaluated from the east (toward the west)
114 --
115 -- Terms should call one of these 6 functions to evaluate another
116 -- location in the playfield, as these functions handle wrapping.
117 -- Don't call eval directly unless you know (x, y) is in the playfield.
118 --
119
120 evalEast env pf x y = evalDelta env pf 1 0 x y
121 evalWest env pf x y = evalDelta env pf (-1) 0 x y
122 evalNorth env pf x y = evalDelta env pf 0 (-1) x y
123 evalSouth env pf x y = evalDelta env pf 0 1 x y
124 evalDelta env pf dx dy x y = evalLeap env pf dx dy dx dy x y
125 evalLeap env pf dx dy leapDx leapDy x y =
126 let
127 (nx, ny) = wrap pf (x+leapDx) (y+leapDy)
128 in
129 eval env pf dx dy nx ny
130
131 wrap pf x y =
132 let
133 (lowX, lowY, highX, highY) = extents pf
134 x' = if (x < lowX) then highX-(lowX-x)+1 else
135 if (x > highX) then lowX+(x-highX)-1 else x
136 y' = if (y < lowY) then highY-(lowY-y)+1 else
137 if (y > highY) then lowY+(y-highY)-1 else y
138 in
139 (x', y')
140
141
142 eval env pf dx dy x y =
143 let
144 term = Char.chr $ fromInteger $ get pf x y
145 in
146 evalThe term env pf dx dy x y
147
148 --
149 -- Evaluation of individual terms. The meaning of each of these is
150 -- explained in the documentation.
151 --
152
153 evalThe :: Char -> [Value] -> Playfield -> Integer -> Integer -> Integer -> Integer -> (Value, Playfield)
154
155 evalThe ':' (arg:env) pf dx dy x y = (arg, pf)
156 evalThe ':' [] pf dx dy x y = (IntVal 0, pf)
157
158 evalThe '$' (arg:env) pf dx dy x y = evalDelta env pf dx dy x y
159 evalThe '$' [] pf dx dy x y = evalDelta [] pf dx dy x y
160
161 evalThe '\\' env pf dx dy x y =
162 let
163 (arg, pf') = evalSouth env pf x y
164 in
165 evalDelta (arg:env) pf' dx dy x y
166
167 evalThe '>' env pf dx dy x y = evalEast env pf x y
168 evalThe '<' env pf dx dy x y = evalWest env pf x y
169 evalThe 'v' env pf dx dy x y = evalSouth env pf x y
170 evalThe '^' env pf dx dy x y = evalNorth env pf x y
171
172 evalThe '_' env pf dx dy x y =
173 case evalDelta env pf dx dy x y of
174 (IntVal 0, pf') -> evalEast env pf' x y
175 (_, pf') -> evalWest env pf' x y
176
177 evalThe '|' env pf dx dy x y =
178 case evalDelta env pf dx dy x y of
179 (IntVal 0, pf') -> evalSouth env pf' x y
180 (_, pf') -> evalNorth env pf' x y
181
182 evalThe '@' env pf dx dy x y = evalWest env pf x y
183
184 evalThe '!' env pf dx dy x y =
185 case evalDelta env pf dx dy x y of
186 (IntVal 0, pf') -> (IntVal 1, pf')
187 (_, pf') -> (IntVal 0, pf')
188
189 evalThe ' ' env pf dx dy x y =
190 evalDelta env pf dx dy x y
191
192 evalThe '#' env pf dx dy x y =
193 evalLeap env pf dx dy (dx*2) (dy*2) x y
194
195 evalThe digit env pf dx dy x y
196 | Char.isDigit digit = (IntVal $ toInteger $ Char.digitToInt digit, pf)
197
198 evalThe oper env pf dx dy x y =
199 let
200 (IntVal north, pf') = evalNorth env pf x y
201 (IntVal south, pf'') = evalSouth env pf' x y
202 in
203 case oper of
204 '+' -> (IntVal (north + south), pf'')
205 '*' -> (IntVal (north * south), pf'')
206 '-' -> (IntVal (north - south), pf'')
207 '/' -> case south of
208 0 -> evalDelta env pf'' dx dy x y
209 _ -> (IntVal (north `div` south), pf'')
210 '%' -> case south of
211 0 -> evalDelta env pf'' dx dy x y
212 _ -> (IntVal (north `rem` south), pf'')
213 '`' -> case north > south of
214 True -> (IntVal 1, pf'')
215 False -> (IntVal 0, pf'')
216 'g' -> (IntVal $ get pf'' north south, pf'')
217 'p' -> let
218 (IntVal value, pf''') = evalDelta env pf'' dx dy x y
219 pf'''' = put pf''' north south value
220 in
221 (IntVal 0, pf'''')
222 _ ->
223 error "undefined term"
224
225 --
226 -- Main entry points for executing Flobnar programs.
227 --
228
229 run program =
230 let
231 pf = load (lines program)
232 in
233 case locate pf $ toInteger $ Char.ord '@' of
234 [(x, y)] ->
235 let
236 (result, pf') = eval [] pf 0 0 x y
237 in
238 result
239 _ ->
240 error "Program does not contain exactly one @"
241
242 showRun program =
243 case run program of
244 IntVal x -> "Result: " ++ (show x)
0 #!/bin/sh
1 cd src
2 falderal test ../doc/Flobnar.falderal