git @ Cat's Eye Technologies Burro / 98ac2d6
Indent all code four spaces to make the Markdown link trick work. Chris Pressey 5 years ago
1 changed file(s) with 185 addition(s) and 185 deletion(s). Raw diff Collapse all Expand all
6464 for presentation.) As such, this document serves as an "executable
6565 semantics", both defining the language and providing a ready tool.
6666
67 > module Language.Burro where
68 > import System.Environment
67 > module Language.Burro where
68 > import System.Environment
6969
7070
7171 Inductive Definition of a Burro Program
8181 If a and b are Burro programs, then ab is a Burro program.
8282 Nothing else is a Burro program.
8383
84 > data Burro = Null
85 > | ToggleHalt
86 > | Inc
87 > | Dec
88 > | GoLeft
89 > | GoRight
90 > | Test Burro Burro
91 > | Seq Burro Burro
92 > deriving (Read, Eq)
84 > data Burro = Null
85 > | ToggleHalt
86 > | Inc
87 > | Dec
88 > | GoLeft
89 > | GoRight
90 > | Test Burro Burro
91 > | Seq Burro Burro
92 > deriving (Read, Eq)
9393
9494
9595 Representation of Burro Programs
101101 a given string of symbols into a Burro program is straightforward; all
102102 symbols which are not Burro symbols are simply ignored.
103103
104 > instance Show Burro where
105 > show Null = "e"
106 > show ToggleHalt = "!"
107 > show Inc = "+"
108 > show Dec = "-"
109 > show GoLeft = "<"
110 > show GoRight = ">"
111 > show (Test a b) = "(" ++ (show a) ++ "/" ++ (show b) ++ ")"
112 > show (Seq a b) = (show a) ++ (show b)
113 >
114 > parse string =
104 > instance Show Burro where
105 > show Null = "e"
106 > show ToggleHalt = "!"
107 > show Inc = "+"
108 > show Dec = "-"
109 > show GoLeft = "<"
110 > show GoRight = ">"
111 > show (Test a b) = "(" ++ (show a) ++ "/" ++ (show b) ++ ")"
112 > show (Seq a b) = (show a) ++ (show b)
113 >
114 > parse string =
115 > let
116 > (rest, acc) = parseProgram string Null
117 > in
118 > trim acc
119 >
120 > parseProgram [] acc =
121 > ([], acc)
122 > parseProgram ('e':rest) acc =
123 > parseProgram rest (Seq acc Null)
124 > parseProgram ('+':rest) acc =
125 > parseProgram rest (Seq acc Inc)
126 > parseProgram ('-':rest) acc =
127 > parseProgram rest (Seq acc Dec)
128 > parseProgram ('<':rest) acc =
129 > parseProgram rest (Seq acc GoLeft)
130 > parseProgram ('>':rest) acc =
131 > parseProgram rest (Seq acc GoRight)
132 > parseProgram ('!':rest) acc =
133 > parseProgram rest (Seq acc ToggleHalt)
134 > parseProgram ('(':rest) acc =
115135 > let
116 > (rest, acc) = parseProgram string Null
136 > (rest', thenprog) = parseProgram rest Null
137 > (rest'', elseprog) = parseProgram rest' Null
138 > test = Test thenprog elseprog
117139 > in
118 > trim acc
119 >
120 > parseProgram [] acc =
121 > ([], acc)
122 > parseProgram ('e':rest) acc =
123 > parseProgram rest (Seq acc Null)
124 > parseProgram ('+':rest) acc =
125 > parseProgram rest (Seq acc Inc)
126 > parseProgram ('-':rest) acc =
127 > parseProgram rest (Seq acc Dec)
128 > parseProgram ('<':rest) acc =
129 > parseProgram rest (Seq acc GoLeft)
130 > parseProgram ('>':rest) acc =
131 > parseProgram rest (Seq acc GoRight)
132 > parseProgram ('!':rest) acc =
133 > parseProgram rest (Seq acc ToggleHalt)
134 > parseProgram ('(':rest) acc =
135 > let
136 > (rest', thenprog) = parseProgram rest Null
137 > (rest'', elseprog) = parseProgram rest' Null
138 > test = Test thenprog elseprog
139 > in
140 > parseProgram rest'' (Seq acc test)
141 > parseProgram ('/':rest) acc =
142 > (rest, acc)
143 > parseProgram (')':rest) acc =
144 > (rest, acc)
145 > parseProgram (_:rest) acc =
146 > parseProgram rest acc
147 >
148 > trim (Seq Null a) = trim a
149 > trim (Seq a Null) = trim a
150 > trim (Seq a b) = Seq (trim a) (trim b)
151 > trim (Test a b) = Test (trim a) (trim b)
152 > trim x = x
140 > parseProgram rest'' (Seq acc test)
141 > parseProgram ('/':rest) acc =
142 > (rest, acc)
143 > parseProgram (')':rest) acc =
144 > (rest, acc)
145 > parseProgram (_:rest) acc =
146 > parseProgram rest acc
147 >
148 > trim (Seq Null a) = trim a
149 > trim (Seq a Null) = trim a
150 > trim (Seq a b) = Seq (trim a) (trim b)
151 > trim (Test a b) = Test (trim a) (trim b)
152 > trim x = x
153153
154154
155155 Group Properties of Burro Programs
167167 If aa' = e and bb' = e, (a/b)(b'/a') = e.
168168 If aa' = e and bb' = e, abb'a' = e.
169169
170 > inverse Null = Null
171 > inverse ToggleHalt = ToggleHalt
172 > inverse Inc = Dec
173 > inverse Dec = Inc
174 > inverse GoLeft = GoRight
175 > inverse GoRight = GoLeft
176 > inverse (Test a b) = Test (inverse b) (inverse a)
177 > inverse (Seq a b) = Seq (inverse b) (inverse a)
170 > inverse Null = Null
171 > inverse ToggleHalt = ToggleHalt
172 > inverse Inc = Dec
173 > inverse Dec = Inc
174 > inverse GoLeft = GoRight
175 > inverse GoRight = GoLeft
176 > inverse (Test a b) = Test (inverse b) (inverse a)
177 > inverse (Seq a b) = Seq (inverse b) (inverse a)
178178
179179 For every Burro program x, annihilationOf x is always equivalent
180180 computationally to e.
181181
182 > annihilationOf x = Seq x (inverse x)
182 > annihilationOf x = Seq x (inverse x)
183183
184184
185185 State Model for Burro Programs
199199 appears on the tape.) The second list contains everything to the right of
200200 the tape head, in the same order as it appears on the tape.
201201
202 > data Tape = Tape [Integer] [Integer]
203 > deriving (Read)
204 >
205 > instance Show Tape where
206 > show t@(Tape l r) =
207 > let
208 > (Tape l' r') = strip t
209 > in
210 > show (reverse l') ++ "<" ++ (show r')
202 > data Tape = Tape [Integer] [Integer]
203 > deriving (Read)
204 >
205 > instance Show Tape where
206 > show t@(Tape l r) =
207 > let
208 > (Tape l' r') = strip t
209 > in
210 > show (reverse l') ++ "<" ++ (show r')
211211
212212 When comparing two tapes for equality, we must disregard any zero cells
213213 farther to the left/right than the outermost non-zero cells. Specifically,
217217 Also, the current cell must be the same for both tapes (that is, tape heads
218218 must be in the same location) for two tapes to be considered equal.
219219
220 > stripzeroes list = (reverse (sz (reverse list)))
221 > where sz [] = []
222 > sz (0:rest) = sz rest
223 > sz x = x
224 >
225 > ensurecell [] = [0]
226 > ensurecell x = x
227 >
228 > strip (Tape l r) = Tape (ensurecell (stripzeroes l)) (stripzeroes r)
229 >
230 > tapeeq :: Tape -> Tape -> Bool
231 > tapeeq t1 t2 =
232 > let
233 > (Tape t1l t1r) = strip t1
234 > (Tape t2l t2r) = strip t2
235 > in
236 > (t1l == t2l) && (t1r == t2r)
237 >
238 > instance Eq Tape where
239 > t1 == t2 = tapeeq t1 t2
220 > stripzeroes list = (reverse (sz (reverse list)))
221 > where sz [] = []
222 > sz (0:rest) = sz rest
223 > sz x = x
224 >
225 > ensurecell [] = [0]
226 > ensurecell x = x
227 >
228 > strip (Tape l r) = Tape (ensurecell (stripzeroes l)) (stripzeroes r)
229 >
230 > tapeeq :: Tape -> Tape -> Bool
231 > tapeeq t1 t2 =
232 > let
233 > (Tape t1l t1r) = strip t1
234 > (Tape t2l t2r) = strip t2
235 > in
236 > (t1l == t2l) && (t1r == t2r)
237 >
238 > instance Eq Tape where
239 > t1 == t2 = tapeeq t1 t2
240240
241241 A convenience function for creating an inital tape is also provided.
242242
243 > tape :: [Integer] -> Tape
244 > tape x = Tape [head x] (tail x)
243 > tape :: [Integer] -> Tape
244 > tape x = Tape [head x] (tail x)
245245
246246 We now define some operations on tapes that we will use in the semantics.
247247 First, operations on tapes that alter or access the cell under the tape head.
248248
249 > inc (Tape (cell:left) right) = Tape (cell + 1 : left) right
250 > dec (Tape (cell:left) right) = Tape (cell - 1 : left) right
251 > get (Tape (cell:left) right) = cell
252 > set (Tape (_:left) right) value = Tape (value : left) right
249 > inc (Tape (cell:left) right) = Tape (cell + 1 : left) right
250 > dec (Tape (cell:left) right) = Tape (cell - 1 : left) right
251 > get (Tape (cell:left) right) = cell
252 > set (Tape (_:left) right) value = Tape (value : left) right
253253
254254 Next, operations on tapes that move the tape head.
255255
256 > left (Tape (cell:[]) right) = Tape [0] (cell:right)
257 > left (Tape (cell:left) right) = Tape left (cell:right)
258 > right (Tape left []) = Tape (0:left) []
259 > right (Tape left (cell:right)) = Tape (cell:left) right
256 > left (Tape (cell:[]) right) = Tape [0] (cell:right)
257 > left (Tape (cell:left) right) = Tape left (cell:right)
258 > right (Tape left []) = Tape (0:left) []
259 > right (Tape left (cell:right)) = Tape (cell:left) right
260260
261261 Finally, an operation on two tapes that swaps the current cell between
262262 them.
263263
264 > swap t1 t2 = (set t1 (get t2), set t2 (get t1))
264 > swap t1 t2 = (set t1 (get t2), set t2 (get t1))
265265
266266 A program state consists of:
267267
268 - A "data tape";
269 - A "stack tape"; and
270 - A flag called the "halt flag", which may be 0 or 1.
268 - A "data tape";
269 - A "stack tape"; and
270 - A flag called the "halt flag", which may be 0 or 1.
271271
272272 The 0 and 1 are represented by False and True boolean values in this
273273 semantics.
274274
275 > data State = State Tape Tape Bool
276 > deriving (Show, Read, Eq)
277 >
278 > newstate = State (tape [0]) (tape [0]) True
275 > data State = State Tape Tape Bool
276 > deriving (Show, Read, Eq)
277 >
278 > newstate = State (tape [0]) (tape [0]) True
279279
280280
281281 Semantics of Burro Programs
288288 If ab is a Burro program, and a maps state S to state S', and b maps
289289 state S' to S'', then ab maps state S to state S''.
290290
291 > exec (Seq a b) t = exec b (exec a t)
291 > exec (Seq a b) t = exec b (exec a t)
292292
293293 The e instruction is the identity function on states.
294294
295 > exec Null s = s
295 > exec Null s = s
296296
297297 The ! instruction toggles the halt flag. If it is 0 in the input state, it
298298 is 1 in the output state, and vice versa.
299299
300 > exec ToggleHalt (State dat stack halt) = (State dat stack (not halt))
300 > exec ToggleHalt (State dat stack halt) = (State dat stack (not halt))
301301
302302 The + instruction increments the current data cell, while - decrements the
303303 current data cell.
304304
305 > exec Inc (State dat stack halt) = (State (inc dat) stack halt)
306 > exec Dec (State dat stack halt) = (State (dec dat) stack halt)
305 > exec Inc (State dat stack halt) = (State (inc dat) stack halt)
306 > exec Dec (State dat stack halt) = (State (dec dat) stack halt)
307307
308308 The instruction < makes the cell to the left of the current data cell, the
309309 new current data cell. The instruction > makes the cell to the right of the
310310 current data cell, the new current data cell.
311311
312 > exec GoLeft (State dat stack halt) = (State (left dat) stack halt)
313 > exec GoRight (State dat stack halt) = (State (right dat) stack halt)
312 > exec GoLeft (State dat stack halt) = (State (left dat) stack halt)
313 > exec GoRight (State dat stack halt) = (State (right dat) stack halt)
314314
315315 (a/b) is the conditional construct, which is quite special.
316316
333333
334334 Seventh, the current data cell and the current stack cell are swapped again.
335335
336 > exec (Test thn els) (State dat stack halt) =
337 > let
338 > x = get dat
339 > (dat', stack') = swap dat stack
340 > stack'' = right (set stack' (0 - (get stack')))
341 > f = if x > 0 then thn else if x < 0 then els else Null
342 > (State dat''' stack''' halt') = exec f (State dat' stack'' halt)
343 > (dat'''', stack'''') = swap dat''' (left stack''')
344 > in
345 > (State dat'''' stack'''' halt')
336 > exec (Test thn els) (State dat stack halt) =
337 > let
338 > x = get dat
339 > (dat', stack') = swap dat stack
340 > stack'' = right (set stack' (0 - (get stack')))
341 > f = if x > 0 then thn else if x < 0 then els else Null
342 > (State dat''' stack''' halt') = exec f (State dat' stack'' halt)
343 > (dat'''', stack'''') = swap dat''' (left stack''')
344 > in
345 > (State dat'''' stack'''' halt')
346346
347347 We observe an invariant here: because only the (a/b) construct affects the
348348 stack tape, and because it does so in a monotonic way — that is, both a
364364
365365 Additionally, each time the program repeats, the stack tape is cleared.
366366
367 > run program state =
368 > let
369 > state'@(State dat' stack' halt') = exec program state
370 > in
371 > if
372 > not halt'
373 > then
374 > run program (State dat' (tape [0]) True)
375 > else
376 > state'
367 > run program state =
368 > let
369 > state'@(State dat' stack' halt') = exec program state
370 > in
371 > if
372 > not halt'
373 > then
374 > run program (State dat' (tape [0]) True)
375 > else
376 > state'
377377
378378
379379 Central theorem of Burro
494494 We define a few more convenience functions to cater for the execution
495495 of Burro programs on an initial state.
496496
497 > interpret text = run (parse text) newstate
498
499 > main = do
500 > [fileName] <- getArgs
501 > burroText <- readFile fileName
502 > putStrLn $ show $ interpret burroText
497 > interpret text = run (parse text) newstate
498
499 > main = do
500 > [fileName] <- getArgs
501 > burroText <- readFile fileName
502 > putStrLn $ show $ interpret burroText
503503
504504 Although we have proved that Burro programs form a group, it is not a
505505 mechanized proof, and only goes so far in helping us tell if the
516516 each one by applying the annihilationOf function to it and checking that
517517 the result of executing it on a blank tape is equivalent to e.
518518
519 > testCases = [
520 > ("+++", "-++-++-++"),
521 > ("+(>+++</---)", "->+++<"),
522 > ("-(+++/>---<)", "+>---<"),
523 > ("(!/!)", "e"),
524 > ("+(--------!/e)", "+(/)+"),
525 > ("+++(/)", "---"),
526 > ("---(/)", "+++"),
527 > ("+> +++ --(--(--(/>>>>>+)+/>>>+)+/>+)+",
528 > "+> >>> +(---(/+)/)+")
529 > ]
530
531 > annihilationTests = [
532 > "e", "+", "-", "<", ">", "!",
533 > "++", "--", "<+<-", "-->>--",
534 > "(+/-)", "+(+/-)", "-(+/-)",
535 > "+(--------!/e)"
536 > ]
537
538 > allTestCases = testCases ++ map nihil annihilationTests
539 > where
540 > nihil x = ((show (annihilationOf (parse x))), "e")
519 > testCases = [
520 > ("+++", "-++-++-++"),
521 > ("+(>+++</---)", "->+++<"),
522 > ("-(+++/>---<)", "+>---<"),
523 > ("(!/!)", "e"),
524 > ("+(--------!/e)", "+(/)+"),
525 > ("+++(/)", "---"),
526 > ("---(/)", "+++"),
527 > ("+> +++ --(--(--(/>>>>>+)+/>>>+)+/>+)+",
528 > "+> >>> +(---(/+)/)+")
529 > ]
530
531 > annihilationTests = [
532 > "e", "+", "-", "<", ">", "!",
533 > "++", "--", "<+<-", "-->>--",
534 > "(+/-)", "+(+/-)", "-(+/-)",
535 > "+(--------!/e)"
536 > ]
537
538 > allTestCases = testCases ++ map nihil annihilationTests
539 > where
540 > nihil x = ((show (annihilationOf (parse x))), "e")
541541
542542 Our unit test harness evaluates to a list of tests which did
543543 not pass. If all went well, it will evaluate to the empty list.
544544
545 > test [] =
546 > []
547 > test ((a, b):cases) =
548 > let
549 > resultA = interpret a
550 > resultB = interpret b
551 > in
552 > if
553 > resultA == resultB
554 > then
555 > test cases
556 > else
557 > ((a, b):(test cases))
545 > test [] =
546 > []
547 > test ((a, b):cases) =
548 > let
549 > resultA = interpret a
550 > resultB = interpret b
551 > in
552 > if
553 > resultA == resultB
554 > then
555 > test cases
556 > else
557 > ((a, b):(test cases))
558558
559559 Finally, some miscellaneous functions for helping analyze why the
560560 Burro tests you've written aren't working :)
561561
562 > debug (a, b) = ((a, interpret a), (b, interpret b))
563
564 > debugTests = map debug (test allTestCases)
562 > debug (a, b) = ((a, interpret a), (b, interpret b))
563
564 > debugTests = map debug (test allTestCases)
565565
566566
567567 Implementing a Turing Machine in Burro