git @ Cat's Eye Technologies Beturing / a4e303c
Initial import of Beturing 1.0 revision 2005.0606 sources. Cat's Eye Technologies 10 years ago
3 changed file(s) with 568 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 <html>
1 <head><title>Beturing - a Befunge-flavoured Turing(-esque) machine</title></head>
2 <body>
3 <h1>Beturing</h1>
4 <h2>a Befunge-flavoured Turing(-esque) machine</h2>
5
6 <p><i>Chris Pressey, June 2005</i></p>
7 <!--
8 $Id: beturing.html 8 2005-06-06 22:46:20Z catseye $
9 -->
10
11 <h3>Introduction</h3>
12
13 <p>Beturing is a programming language based on a "universal" Turing machine
14 with an unbounded, 2-dimensional "tape". (The Turing machine on which it is
15 based is "universal" in the sense that the machine's state transition diagram is
16 stored on the "tape" along with the data.)</p>
17
18 <h3>General Layout</h3>
19
20 <p>This 2-dimensional "tape" is where all the action happens; it is
21 called the <i>playfield</i> and is divided into discrete units called <i>cells</i>.
22 Each cell may store exactly one of a number of <i>symbols</i> drawn from
23 a finite alphabet.</p>
24
25 <p>There are two "heads" that access the playfield, one of which
26 (the <i>data head</i>) reads and alters the data (like in a common Turing machine,)
27 the other of which (the <i>code head</i>) reads the state transition diagram.</p>
28
29 <p>The state transition diagram is made up of <i>codes</i>. Each code is a
30 2x2 block of cells in the following form:</p>
31
32 <center><table border=0 bgcolor=#e0e0e0>
33 <tr><td><tt>a</tt></td><td><tt>b</tt></td></tr>
34 <tr><td><tt>&gt;</tt></td><td><tt>/</tt></td></tr>
35 </table></center>
36
37 <p>The code head is considered to be over a code when it is over the upper-left cell of it.
38 The names and meanings of the four parts of the code are as follows:</p>
39
40 <ul>
41 <li>The upper-left cell of a code contains a symbol to look for, called the <i>seek symbol</i>.
42 All symbols are valid seek symbols.</li>
43 <li>The upper-right cell of the code contains a symbol to replace it with, called the <i>replacement symbol</i>.
44 All symbols are valid replacement symbols.</li>
45 <li>The lower-left cell of the code contains an indication of how to move the data head, called
46 the <i>data head movement operator</i>.
47 The set of valid data head movement operators is
48 {<tt>&gt;</tt>, <tt>&lt;</tt>, <tt>^</tt>, <tt>v</tt>, <tt>.</tt>, <tt>*</tt>}.</li>
49 <li>The lower-right cell of the code contains an indication of what to do with the code head, called
50 the <i>state transition operator</i>.
51 The set of valid state transition operators is
52 {<tt>&gt;</tt>, <tt>&lt;</tt>, <tt>^</tt>, <tt>v</tt>, <tt>/</tt>, <tt>@</tt>}.</li>
53 </ul>
54
55 <h3>Syntax</h3>
56
57 <p>When a Beturing playfield is loaded from source such as a text file, lines are
58 translated to rows in the playfield. The first line is loaded at (0, 0), and subsequent
59 lines are loaded at (0, 1), (0, 2), etc. Lines which begin with a <tt>#</tt> are not
60 loaded into the playfield. Certain lines that begin with <tt>#</tt>, listed below,
61 are directives meaningful to any Beturing interpreter.
62 The rest may have a local interpretation (such as the <tt>#!</tt> convention
63 on Unix systems,) or be ignored. A line which begins with <tt>##</tt> is
64 guaranteed to be ignored.</p>
65
66 <ul>
67 <li>Lines of the form <tt># @(<i>x</i>, <i>y</i>)</tt> where <i>x</i> and <i>y</i>
68 are integers reposition the loading of the text file; subsequent lines will be loaded into the playfield
69 at the given position.</li>
70 <li>Lines of the form <tt># C(<i>x</i>, <i>y</i>)</tt> specify the initial position of
71 the code head. The last such line is the one that takes effect.</li>
72 <li>Lines of the form <tt># D(<i>x</i>, <i>y</i>)</tt> specify the initial position of
73 the data head. The last such line is the one that takes effect.</li>
74 </ul>
75
76 <h3>Semantics</h3>
77
78 <p>When a Beturing machine is set in motion, it <i>interprets</i> the code under the code head,
79 transitions to a new state by moving the code head, then
80 repeats indefinitely until the machine enters the <i>halt</i> state.</p>
81
82 <p>Here is how each code is interpreted:</p>
83
84 <ul>
85 <li>If the data head movement operator is the special symbol <tt>*</tt>, the following things happen:
86 <ul>
87 <li>The code head is moved by the positive interpretation (x2) of the state transition operator.</li>
88 </ul>
89 </li>
90 <li>Otherwise, if the symbol under the data head matches the seek symbol, the following things
91 happen:
92 <ul>
93 <li>The replacement symbol is written to the cell under the data head.</li>
94 <li>The data head is moved by the positive interpretation of the data head movement operator.</li>
95 <li>The code head is moved by the positive interpretation (x2) of the state transition operator.</li>
96 </ul>
97 </li>
98 <li>Otherwise the symbol under the data head does <b>not</b> match the
99 seek symbol, and the following things happen:
100 <ul>
101 <li>The code head is moved by the negative interpretation (x2) of the state transition operator.</li>
102 </ul>
103 </li>
104 </ul>
105
106 <p>The positive and negative interpretations of the data head movement and state transition
107 operators are given below:</p>
108
109 <center><table border=1>
110 <tr><th>Symbol</th><th>Positive interpretation</th><th>Negative interpretation</th></tr>
111 <tr><td><tt>&gt;</tt></td><td>Move right</td><td>Move right</td></tr>
112 <tr><td><tt>&lt;</tt></td><td>Move left</td><td>Move left</td></tr>
113 <tr><td><tt>^</tt></td><td>Move up</td><td>Move up</td></tr>
114 <tr><td><tt>v</tt></td><td>Move down</td><td>Move down</td></tr>
115 <tr><td><tt>.</tt></td><td>Don't move</td><td>Don't move</td></tr>
116 <tr><td><tt>/</tt></td><td>Move right</td><td>Move down</td></tr>
117 <tr><td><tt>@</tt></td><td>Halt</td><td>Halt</td></tr>
118 </table></center>
119
120 <p>Note that "x2" in the rules given above means to advance two cells in the given direction;
121 this is used everywhere for moving the code head because codes are 2x2 cell blocks.</p>
122
123 <h3>Discussion</h3>
124
125 <p>The Beturing language was designed (in part) as a test of the wire-crossing problem,
126 in the following manner.</p>
127
128 <p>Note that the code head does not have "direction" or "delta" state as the instruction
129 pointer does in Befunge; it has only "position" state. Its next position (and thus the machine's
130 next state) is determined entirely by the state transition operator of the current code.</p>
131
132 <p>Note also that there is no "leap over" state transition operator (like <tt>#</tt> in Befunge.)
133 Therefore the next state must always be reachable by a continuous, unbroken
134 path through the playfield.</p>
135
136 <p>This means that a Beturing machine is incapable of having a state transition diagram
137 that, when rendered on a 2-dimensional plane, requires that two edges cross.
138 (A state diagram with, e.g., 5 states, and a transition from each state to every other state, appears
139 to have this property (although it would be nice to have a reference to a watertight proof of this...))</p>
140
141 <p>This <b>might</b> mean that it is impossible to construct a
142 true universal Turing machine in Beturing, <i>if</i> a universal Turing machine
143 requires a state diagram which has edges that cross when rendered in two dimensions.</p>
144
145 <p>If this were the case (and it may well be) then Beturing would not be Turing-complete,
146 and in fact its level of computational power would probably be difficult to determine.</p>
147
148 </body></html>
0 ## Trivial Beturing program that swaps a's and b's along a line.
1 ## $Id: example.bet 2 2005-06-06 22:28:23Z catseye $
2 # D(40, 4)
3 # @(40, 4)
4 .bbab.
5 # C(0, 0)
6 # @(0, 0)
7
8 *v*<*<*<*>*v
9 aa ab aa
10 >/*>./*^*^</*v
11 bb ba bb
12 >/*^./*^*^</*v
13 .. .. ..
14 >/*^</*>*^.@*v
15
16 *@ *^*<*<
0 #!/usr/local/bin/lua
1 --
2 -- beturing.lua
3 -- A Befunge-flavoured Turing(-esque) machine
4 -- Implemented in Lua 5 by Chris Pressey, June 2005
5 --
6
7 --
8 -- Copyright (c)2005 Cat's Eye Technologies. All rights reserved.
9 --
10 -- Redistribution and use in source and binary forms, with or without
11 -- modification, are permitted provided that the following conditions
12 -- are met:
13 --
14 -- Redistributions of source code must retain the above copyright
15 -- notice, this list of conditions and the following disclaimer.
16 --
17 -- Redistributions in binary form must reproduce the above copyright
18 -- notice, this list of conditions and the following disclaimer in
19 -- the documentation and/or other materials provided with the
20 -- distribution.
21 --
22 -- Neither the name of Cat's Eye Technologies nor the names of its
23 -- contributors may be used to endorse or promote products derived
24 -- from this software without specific prior written permission.
25 --
26 -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 -- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 -- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 -- FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 -- COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
31 -- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 -- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 -- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 -- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 -- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 -- OF THE POSSIBILITY OF SUCH DAMAGE.
38 --
39 -- $Id: beturing.lua 2 2005-06-06 22:28:23Z catseye $
40
41 --[[ Common functions ]]--
42
43 local debug_log = print
44 local usage = function()
45 io.stderr:write("Usage: [lua] beturing.lua [-q] [filename.bet]\n")
46 os.exit(1)
47 end
48
49 --[[ Object Classes ]]--
50
51 --[[-----------]]--
52 --[[ Playfield ]]--
53 --[[-----------]]--
54
55 --
56 -- Store an unbounded grid.
57 --
58 Playfield = {}
59 Playfield.new = function(tab)
60 tab = tab or {}
61 local nw, ne, sw, se = {}, {}, {}, {} -- quadrant storage
62 local min_x, min_y, max_x, max_y -- limits seen so far
63 local method = {}
64
65 --
66 -- Private function: pick the appropriate quadrant & translate
67 --
68 local pick_quadrant = function(x, y)
69 if x > 0 and y > 0 then return se, x, y end
70 if x > 0 and y <= 0 then return ne, x, 1-y end
71 if x <= 0 and y > 0 then return sw, 1-x, y end
72 if x <= 0 and y <= 0 then return nw, 1-x, 1-y end
73 end
74
75 --
76 -- Read the symbol at a given position in the playfield
77 --
78 method.peek = function(pf, x, y)
79 local contents, nx, ny = pick_quadrant(x, y)
80 contents[ny] = contents[ny] or {} -- make sure row exists
81 local sym = contents[ny][nx] or " "
82 return sym
83 end
84
85 --
86 -- Write a symbol at a given position in the playfield
87 --
88 method.poke = function(pf, x, y, sym)
89 local contents, nx, ny = pick_quadrant(x, y)
90 contents[ny] = contents[ny] or {} -- make sure row exists
91 contents[ny][nx] = sym
92 if not min_x or x < min_x then min_x = x end
93 if not max_x or x > max_x then max_x = x end
94 if not min_y or y < min_y then min_y = y end
95 if not max_y or y > max_y then max_y = y end
96 end
97
98 --
99 -- Store a string starting at (x, y).
100 --
101 method.poke_str = function(pf, x, y, str)
102 local i
103 for i = 1, string.len(str) do
104 pf:poke(x + (i - 1), y, string.sub(str, i, i))
105 end
106 end
107
108 --
109 -- Load the playfield from a file.
110 --
111 method.load = function(pf, filename, callback)
112 local file = io.open(filename)
113 local line = file:read("*l")
114 local x, y = 0, 0
115
116 while line do
117 if string.find(line, "^%s*%#") then
118 -- comment or directive - not included in playfield.
119 local found, len, nx, ny =
120 string.find(line, "^%s*%#%s*%@%(%s*(%-?%d+)%s*%,%s*(%-?%d+)%s*%)")
121 if found then
122 x = tonumber(nx)
123 y = tonumber(ny)
124 debug_log("Now loading at " ..
125 "(" .. tostring(x) .. "," .. tostring(y) .. ")")
126 else
127 callback(line)
128 end
129 else
130 pf:poke_str(x, y, line)
131 y = y + 1
132 end
133 line = file:read("*l")
134 end
135 file:close()
136 end
137
138 --
139 -- Return a string representing the playfield.
140 --
141 method.render = function(pf)
142 local y = min_y
143 local s = "--- (" .. tostring(min_x) .. "," .. tostring(min_y) .. ")-"
144 s = s .. "(" .. tostring(max_x) .. "," .. tostring(max_y) .. ") ---\n"
145 while y <= max_y do
146 local x = min_x
147 while x <= max_x do
148 s = s .. pf:peek(x, y)
149 x = x + 1
150 end
151 s = s .. "\n"
152 y = y + 1
153 end
154
155 return s
156 end
157
158 return method
159 end
160
161 --[[------]]--
162 --[[ Head ]]--
163 --[[------]]--
164
165 --
166 -- Represent a readable(/writeable) location within a playfield.
167 --
168 Head = {}
169 Head.new = function(tab)
170 tab = tab or {}
171
172 local pf = assert(tab.playfield)
173 local x = tab.x or 0
174 local y = tab.y or 0
175
176 local method = {}
177
178 method.read = function(hd, sym)
179 return pf:peek(x, y)
180 end
181
182 method.write = function(hd, sym)
183 pf:poke(x, y, sym)
184 end
185
186 --
187 -- look for this symbol -> 13 <- on match, write this symbol
188 -- on match, move head this way -> 24 <- choose next state on this
189 --
190 method.read_code = function(hd)
191 local seek_sym, repl_sym, move_cmd, state_cmd
192
193 debug_log("rd cd")
194 seek_sym = hd:read()
195 hd:move(">")
196 repl_sym = hd:read()
197 hd:move("v")
198 state_cmd = hd:read()
199 hd:move("<")
200 move_cmd = hd:read()
201 hd:move("^")
202 debug_log("cd rd")
203
204 return seek_sym, repl_sym, move_cmd, state_cmd
205 end
206
207 method.move = function(hd, sym)
208 if sym == "^" then
209 y = y - 1
210 elseif sym == "v" then
211 y = y + 1
212 elseif sym == "<" then
213 x = x - 1
214 elseif sym == ">" then
215 x = x + 1
216 elseif sym ~= "." then
217 error("Illegal movement symbol '" .. sym .. "'")
218 end
219 end
220
221 return method
222 end
223
224 --[[---------]]--
225 --[[ Machine ]]--
226 --[[---------]]--
227
228 --
229 -- Perform the mechanics of the machine.
230 --
231 Machine = {}
232 Machine.new = function(tab)
233 tab = tab or {}
234
235 local pf = tab.playfield or Playfield.new()
236 local data_head = Head.new{
237 playfield = pf,
238 x = tab.data_head_x or 0,
239 y = tab.data_head_y or 0
240 }
241 local code_head = Head.new{
242 playfield = pf,
243 x = tab.code_head_x or 0,
244 y = tab.code_head_y or 0
245 }
246
247 local method = {}
248
249 --
250 -- Private function: provide interpretation of the state-
251 -- transition operator.
252 --
253 local interpret = function(sym, sense)
254 if sense then
255 -- Positive interpretation.
256 if sym == "/" then
257 return ">"
258 else
259 return sym
260 end
261 else
262 -- Negative interpretation.
263 if sym == "/" then
264 return "v"
265 else
266 return state_cmd
267 end
268 end
269 end
270
271 --
272 -- Advance the machine's configuration one step.
273 --
274 method.step = function(m)
275 local this_sym = data_head:read()
276 local seek_sym, repl_sym, move_cmd, state_cmd = code_head:read_code()
277 local code_move
278
279 debug_log("Symbol under data head is '" .. this_sym .. "'")
280 debug_log("Instruction under code head is:")
281 debug_log("(" .. seek_sym .. repl_sym .. ")")
282 debug_log("(" .. move_cmd .. state_cmd .. ")")
283
284 --
285 -- Main processing logic
286 --
287 if move_cmd == "*" then
288 --
289 -- Special - match anything, do no rewriting or data head
290 -- moving, and advance the state using positive intrepretation.
291 --
292 debug_log("-> Wildcard!")
293 code_move = interpret(state_cmd, true)
294 elseif seek_sym == this_sym then
295 --
296 -- The seek symbol matches the symbol under the data head.
297 -- Rewrite it, move the head, and advance the state
298 -- using the positive interpretation.
299 --
300 debug_log("-> Symbol matches, replacing with '" .. repl_sym .. "'")
301 debug_log("-> moving data head '" .. move_cmd .. "'")
302 data_head:write(repl_sym)
303 data_head:move(move_cmd)
304 code_move = interpret(state_cmd, true)
305 else
306 --
307 -- No match - just advance the state, using negative interp.
308 --
309 debug_log("-> No match.")
310 code_move = interpret(state_cmd, false)
311 end
312
313 --
314 -- Do the actual state advancement here.
315 --
316 if code_move == "@" then
317 debug_log("-> Machine halted!")
318 return false
319 else
320 debug_log("-> moving code head '" .. code_move .. "'")
321 code_head:move(code_move)
322 code_head:move(code_move)
323 return true
324 end
325 end
326
327 --
328 -- Run the machine 'til it halts.
329 --
330 method.run = function(m)
331 local done = false
332 while not done do
333 debug_log(pf:render())
334 done = not m:step()
335 end
336 end
337
338 return method
339 end
340
341 --[[ INIT ]]--
342
343 local pf = Playfield.new()
344
345 --[[ command-line arguments ]]--
346
347 local argno = 1
348 while arg[argno] and string.find(arg[argno], "^%-") do
349 if arg[argno] == "-q" then
350 debug_log = function() end
351 else
352 usage()
353 end
354 argno = argno + 1
355 end
356
357 if not arg[argno] then
358 usage()
359 end
360
361 --[[ load playfield ]]--
362
363 local data_head_x, data_head_y, code_head_x, code_head_y = 0, 0, 0, 0
364 local directive_processor = function(directive)
365 local found, len, x, y
366
367 found, len, x, y =
368 string.find(directive, "^%s*%#%s*D%(%s*(%-?%d+)%s*%,%s*(%-?%d+)%s*%)")
369 if found then
370 data_head_x = tonumber(x)
371 data_head_y = tonumber(y)
372 debug_log("Data head initially located at " ..
373 "(" .. tostring(data_head_x) .. "," .. tostring(data_head_y) .. ")")
374 return true
375 end
376 found, len, x, y =
377 string.find(directive, "^%s*%#%s*C%(%s*(%-?%d+)%s*%,%s*(%-?%d+)%s*%)")
378 if found then
379 code_head_x = tonumber(x)
380 code_head_y = tonumber(y)
381 debug_log("Code head initially located at " ..
382 "(" .. tostring(code_head_x) .. "," .. tostring(code_head_y) .. ")")
383 return true
384 end
385
386 return false
387 end
388
389 pf:load(arg[argno], directive_processor)
390
391 --[[ MAIN ]]--
392
393 local m = Machine.new{
394 playfield = pf,
395 data_head_x = data_head_x,
396 data_head_y = data_head_y,
397 code_head_x = code_head_x,
398 code_head_y = code_head_y
399 }
400
401 m:run()