pyper 1.0.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,228 @@
1
+ #coding: utf-8
2
+
3
+ module Pyper::ControlCharacters
4
+ # Current pipe is assigned with the argument from the argument source.
5
+ #
6
+ def g
7
+ set grab_arg
8
+ end
9
+
10
+ # Other pipe is assigned with the argument from the argument source.
11
+ #
12
+ def h
13
+ exe "#@r, #{successor_register(@r)} = #{successor_register(@r)}, #@r"
14
+ set grab_arg
15
+ exe "#@r, #{successor_register(@r)} = #{successor_register(@r)}, #@r"
16
+ end
17
+
18
+ # Compiler directive that decrements the @arg_count index. With +:args_counted+
19
+ # argument source, when used once, causes the same argument to be taken twice.
20
+ # When used twice or more, causes the same number of argument array elements to
21
+ # be taken twice.
22
+ #
23
+ def j
24
+ @arg_count -= 1
25
+ end
26
+
27
+ # k:
28
+
29
+ # l:
30
+
31
+ # def m; nullary_method_with_block "map" end # All-important #map method
32
+
33
+ def m
34
+ pipe_2_variable
35
+ start "if #@r.is_a? String then #@r = #@r.each_char end\n"
36
+ start
37
+ nullary_method_with_block "map"
38
+ end
39
+
40
+ # n:
41
+ # o: prefix character
42
+
43
+ # Adaptive prepend.
44
+ #
45
+ def p
46
+ pipe_2_variable; arg = grab_arg; start "#@r =\n" + # arg 2 self
47
+ "if #@r.respond_to?( :unshift ) then #@r.unshift(#{arg})\n" +
48
+ "elsif #@r.respond_to?( :prepend ) then #@r.prepend(#{arg})\n" +
49
+ "elsif #@r.respond_to?( :merge ) and #@r.is_a?( Array ) " +
50
+ "&& #@r.size == 2\nHash[*#@r].merge(#{arg})\n" +
51
+ "elsif #@r.respond_to? :merge then #@r.merge(#{arg})\n" +
52
+ "else raise 'impossible to unshift/prepend' end"
53
+ start
54
+ end
55
+
56
+ # Adaptive append.
57
+ #
58
+ def q
59
+ pipe_2_variable
60
+ arg = grab_arg
61
+ start "#@r =\n" + # arg 2 self
62
+ "if #@r.respond_to?( :<< ) then #@r << #{arg}\n" +
63
+ "elsif #@r.respond_to?( :merge ) and #@r.is_a?(Array) " +
64
+ "&& #@r.size == 2\n#{arg}.merge(Hash[*#@r])\n" +
65
+ "elsif #@r.respond_to?( :merge ) then #{arg}.merge(#@r)\n" +
66
+ "else raise 'impossible to <</append' end"
67
+ start
68
+ end
69
+
70
+ # Pushes the secondary register (beta) onto the argument source stack.
71
+ #
72
+ def r
73
+ @argsrc.beta
74
+ end
75
+
76
+ # Sends the argument from the argument source to the current pipeline.
77
+ #
78
+ def s
79
+ pipe_2_variable
80
+ start "#@r.send( *(#{grab_arg}) )"
81
+ end
82
+
83
+ # t: prefix character
84
+
85
+ # Latin capital letters
86
+ # ********************************************************************
87
+ # Applies +#Array+ method to the register.
88
+ #
89
+ def A
90
+ pipe_2_variable
91
+ start "Array( #@r )"
92
+ end
93
+ alias Α A # Greek Α, looks the same, different char
94
+
95
+ # Makes the next block-enabled method receive the block that was supplied to
96
+ # the Pyper method.
97
+ #
98
+ def B
99
+ @take_block = true unless @take_block == :taken
100
+ end
101
+
102
+ # Explicit parenthesizing of the current pipeline.
103
+ #
104
+ def C
105
+ parenthesize_current_pipeline
106
+ end
107
+
108
+ # Explicit dup-ping of the current pipeline (Giving up the original object
109
+ # and working with its +#dup+ instead.)
110
+ #
111
+ def D
112
+ exe "#@r = #@r.dup"
113
+ end
114
+
115
+ # Equals operator (+:==+).
116
+ #
117
+ def E
118
+ binary_operator "=="
119
+ end
120
+
121
+ # F:
122
+
123
+ # Ungrab the argument (unshift from pipeline).
124
+ #
125
+ def G
126
+ exe "args.unshift #@r"
127
+ end
128
+
129
+ # A combo that switches this and other register, ungrabs the argument,
130
+ # and sets the other register as the argument source.
131
+ #
132
+ def H
133
+ X()
134
+ β()
135
+ end
136
+
137
+ # Braces method, +#[]+.
138
+ #
139
+ def I
140
+ @pipe[-1] << "[#{grab_arg}]"
141
+ end
142
+
143
+
144
+ # I:
145
+
146
+ # +#join+ expecting argument.
147
+ #
148
+ def J
149
+ unary_method "join"
150
+ end
151
+
152
+ # K:
153
+
154
+ # L:
155
+
156
+ # Zip this and other register and invoke +#map+ with a binary block on it.
157
+ #
158
+ def M
159
+ next_block_will_be_binary
160
+ pipe_2_variable
161
+ start "#@r.zip(#{successor_register(@r)})"
162
+ nullary_method_with_block "map"
163
+ end
164
+
165
+ # N:
166
+
167
+ # Map this register with object from the other register as the starting
168
+ # contents of the delta pipeline, and epsilon as the argument source.
169
+ #
170
+ def O
171
+ r()
172
+ m()
173
+ g()
174
+ ε()
175
+ end
176
+
177
+ # P: recursive piper method, begin
178
+
179
+ # Q: recursive piper method, end
180
+
181
+ def R # Reverse zip: Zip other and this register
182
+ pipe_2_variable
183
+ start "#{successor_register}.zip(#@a)"
184
+ end
185
+
186
+ # S
187
+
188
+ # Ternary operator as binary method with the current pipeline as the receiver.
189
+ #
190
+ def T
191
+ parenthesize_current_pipeline
192
+ @pipe[-1] << " ? ( #{grab_arg} ) : ( #{grab_arg} )"
193
+ end
194
+
195
+ # Unshift / prepend this register to the other register.
196
+ #
197
+ def U; end # unsh/prep self 2 reg (other changed)
198
+
199
+ def V; end # <</app self 2 reg (other changed)
200
+
201
+ # Map zipped other and this register using binary block.
202
+ #
203
+ def W
204
+ next_block_will_be_binary # Mnemonic: W is inverted M
205
+ pipe_2_variable
206
+ start "#{successor_register(@r)}.zip(#@r)"
207
+ nullary_method_with_block "map"
208
+ end
209
+
210
+ # Swaps the contents of the primary (alpha) and the secondary (beta) register.
211
+ #
212
+ def X
213
+ case @w
214
+ when :block then raise "'χ' (swap pipes) may not be used in blocks!"
215
+ else
216
+ exe "#@r, #{successor_register} = #{successor_register}, #@r"
217
+ end
218
+ end
219
+
220
+ # Y:
221
+
222
+ # Zip this and other register.
223
+ #
224
+ def Z
225
+ pipe_2_variable
226
+ start "#@r.zip(#{successor_register})"
227
+ end
228
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ class Object
4
+ # Method π is defined on Object, that enables Pyper method calls.
5
+ #
6
+ def π
7
+ tap do |o|
8
+ begin
9
+ o.singleton_class
10
+ rescue TypeError
11
+ o.class
12
+ end.class_exec { include Pyper }
13
+ end
14
+ end
15
+ end
16
+
17
+ # Pyper is by default included in Enumerable.
18
+ #
19
+ module Enumerable
20
+ include Pyper
21
+ end
22
+
23
+ # Pyper is by default included in Array.
24
+ #
25
+ class Array
26
+ include Pyper
27
+ end
28
+
29
+ # Pyper is by default included in Hash.
30
+ #
31
+ class Hash
32
+ include Pyper
33
+ end
34
+
35
+ # Pyper is by default included in Range.
36
+ #
37
+ class Range
38
+ include Pyper
39
+ end
40
+
41
+ # Pyper is by default included in String.
42
+ #
43
+ class String
44
+ include Pyper
45
+ end
46
+
@@ -0,0 +1,442 @@
1
+ #coding: utf-8
2
+
3
+ require_relative 'postfix_machine/argument_source'
4
+ require_relative 'control_characters'
5
+
6
+ # PostfixMachine is a simple compiler of Pyper method symbols. Each written
7
+ # method has two pipelines: 'alpha' (no. 0) and 'beta' (no. 1). Variables
8
+ # 'alpha' and 'beta' are local to the main scope of a Pyper method.
9
+ #
10
+ # When blocks are used inside a Pyper method, variable 'delta' local to the
11
+ # block is used to hold the pipeline inside the block. For blocks with arity 1,
12
+ # variable named 'epsilon' is used to hold the block argument. For blocks with
13
+ # arity 2, variables named 'epsilon', resp. 'zeta' are used to hold 1st, resp.
14
+ # 2nd block argument. Blocks with arity higher than 2 are not allowed in Pyper
15
+ # methods. (However, Pyper methods may receive external block of arbitrary
16
+ # construction.)
17
+ #
18
+ class Pyper::PostfixMachine
19
+ include Pyper::ControlCharacters
20
+
21
+ SUCC = { alpha: :beta, beta: :alpha, α: :β, β: :α } # successor table
22
+ PRE = { alpha: :beta, beta: :alpha, α: :β, β: :α } # predecessor table
23
+
24
+ # Template for the def line of the method being written:
25
+ DEF_LINE = -> name { "def #{name}( *args, &block )" }
26
+
27
+ # PostfixMachine init. Requires the command string as an argument -- yes,
28
+ # for each command string, a new PostfixMachine is instantiated.
29
+ #
30
+ def initialize command_string
31
+ @cmds = parse_command_string( command_string.to_s )
32
+ end
33
+
34
+ # Algorithmically writes a Ruby method, whose name is given in the first
35
+ # argument. The options hash expects 2 named arguments -- +:op+ and +:ret+:
36
+ #
37
+ # op: when 1 (single pipe), makes no assumption about the receiver
38
+ # When 2 (twin pipe), assumes the receiver is a size 2 array,
39
+ # consisting of pipes alpha, beta
40
+ # When -2 (twin pipe with a swap), assumes the same as above and
41
+ # swaps the pipes immediately (alpha, beta = beta, alpha)
42
+ #
43
+ # ret: when 1 (single return value), returns the current pipe only
44
+ # when 2 (return both pipes), returns size 2 array, consisting
45
+ # of pipes a, b
46
+ # when -2 (return both pipes with a swap), returns size 2 array
47
+ # containing the pipes' results in reverse order [b, a]
48
+ #
49
+ def compile( method_name, op: 1, ret: 1 )
50
+ @opts = { op: op, ret: ret }.tap do |oo|
51
+ oo.define_singleton_method :op do self[:op] end
52
+ oo.define_singleton_method :ret do self[:ret] end
53
+ end
54
+
55
+ # Set up compile-time argument sourcing.
56
+ @argsrc = ArgumentSource.new
57
+
58
+ # Write the method skeleton.
59
+ initialize_writer_state
60
+ write_method_head_skeleton( method_name )
61
+ write_initial_pipeline
62
+ write_method_tail_skeleton
63
+
64
+ # Now that we have the skeleton, let's write the meat.
65
+ write_method_meat
66
+
67
+ puts "head is #@head\npipe is #@pipe\ntail is #@tail" if Pyper::DEBUG > 1
68
+
69
+ # Finally, close any blocks and return
70
+ autoclose_open_blocks_and_return
71
+ end
72
+
73
+ private
74
+
75
+ # Converts a command string into a command array.
76
+ #
77
+ def parse_command_string( arg )
78
+ return arg if arg.kind_of? Array # assume no work needed
79
+ # Otherwise, assume arg is a ς and split it using #each_char
80
+ arg.to_s.each_char.with_object [] do |char, memo|
81
+ # Handle prefix characters:
82
+ ( PREFIX_CHARACTERS.include?(memo[-1]) ? memo[-1] : memo ) << char
83
+ end
84
+ end
85
+
86
+ # Initializes method writing flags / state keepers.
87
+ #
88
+ def initialize_writer_state
89
+ # set current pipeline to :alpha (pipeline 0)
90
+ @r = :alpha
91
+
92
+ # set current pipe stack to [@r]
93
+ @rr = [@r]
94
+ # (Pipeline stack is needed due to tha fact, that blocks are allowed
95
+ # inside a Pyper method. At method write time, every time a block is
96
+ # open, block pipeline symbol is pushed onto this stack.)
97
+
98
+ # where are we? flag (whether in :main or :block) set to :main
99
+ @w = :main
100
+
101
+ # argument counter (for args dispensing to the individual methods)
102
+ @arg_count = 0
103
+
104
+ # signal to pass the supplied block to the next method
105
+ @take_block = false
106
+
107
+ # arity flag for next block to be written, default is 1
108
+ @block_arity = 1
109
+ end
110
+
111
+ # Writes the skeleton of the method header.
112
+ #
113
+ def write_method_head_skeleton( ɴ )
114
+ @head = [ [ DEF_LINE.( ɴ ) ] ] # write first line "def ɴ..."
115
+ write "\n"
116
+ # write validation line (written only when @opts[:op] == 2)
117
+ write "raise 'Receiver must be a size 2 array when double-piping!'" +
118
+ "unless self.kind_of?( Array ) and self.size == 2\n" if
119
+ @opts.op == 2
120
+ # 'main_opener' (global)
121
+ write @main_opener = ""
122
+ # 'opener' (local to block)
123
+ write opener = ""
124
+ @opener = [ opener ]
125
+ end
126
+
127
+ # Initializes the pipeline (@pipe).
128
+ #
129
+ def write_initial_pipeline
130
+ @pipe = case @opts.op
131
+ when 1 then [ "self" ] # use receiver (default)
132
+ when 2 then # use alpha, beta = self[0], self[1]
133
+ @alpha_touched = @beta_touched = true
134
+ write "\n( alpha, beta = self[0], self[1]; alpha)\n"
135
+ [ "alpha" ] # pipe 0 aka. primary pipe
136
+ when -2 then # use alpha, beta = self[1], self[0]
137
+ @alpha_touched = @beta_touched = true
138
+ write "\n( alpha, beta = self[1], self[0]; alpha)\n"
139
+ [ "alpha" ] # pipe 0 aka. primary pipe
140
+ end # self compliance tested in the written method itself
141
+ write "\n"; write @pipe[-1] # make @pipe part of @head
142
+ end
143
+
144
+ # Write the skeleton of the tail part of the method, consisting
145
+ # of the finisher line, returner line, and end statement itself.
146
+ #
147
+ def write_method_tail_skeleton
148
+ finisher = String.new # 'finisher' (local to block)
149
+ @finisher = [ finisher ]
150
+ @returner = case @opts.ret # 'returner' (global finisher)
151
+ when 1 then ""
152
+ when 2 then alpha_touch; beta_touch; "return alpha, beta"
153
+ when -2 then alpha_touch; beta_touch; "return beta, alpha"
154
+ else raise "wrong @opts[:fin] value: #{@opts.fin}" end
155
+ @tail = [ [ finisher, "\n", @returner, "\n", "end" ] ] # end line
156
+ end
157
+
158
+ # This consists of taking the atomic commands from @cmds array one by
159
+ # one and calling the command method to write a small piece of the
160
+ # program implied by the command.
161
+ #
162
+ def write_method_meat
163
+ while not @cmds.empty?
164
+ # First, slice off the next command from @cmds array
165
+ cmd = @cmds.shift
166
+
167
+ # puts "doing command #{cmd}, @r is #@r, @head is #@head" # DEBUG
168
+ # puts "doing command #{cmd}, @argsrc is #@argsrc" # DEBUG
169
+
170
+ # Take the block (if not taken) if this is the last command
171
+
172
+ @take_block = true unless @take_block == :taken if @cmds.size <= 0
173
+ # Now send the command to self. Commands are implemented as
174
+ # methods of Pyper::PostfixMachine with one or two-character
175
+ # names. These methods then take care of writing the program
176
+ # pieces implied by these commands. Side effects of this is, that
177
+ # one- and two-character local variables should be avoided inside
178
+ # whole PostfixMachine class.
179
+ # puts "about to self.send( #@w, #{cmd} )" # DEBUG
180
+ self.send @w, cmd
181
+ pipe_2_variable if @cmds.size <= 0
182
+ end
183
+ end
184
+
185
+ # After we run out of atomic commands, it's time to finalize the
186
+ # program by closing any blocks still left open. Metod #close_block
187
+ # called by this method actually produces the program string out of
188
+ # each block it closes, so this method actually returns the program
189
+ # string of whole newly written Pyper method.
190
+ #
191
+ def autoclose_open_blocks_and_return
192
+ ( rslt = close_block; chain rslt; pipe_2_variable ) while @head.size > 1
193
+ return close_block
194
+ end
195
+
196
+ # Called to close a block, including the main def.
197
+ #
198
+ def close_block
199
+ unless @rr.empty? then @r = @rr.pop end # back with the register
200
+ @pipe.pop; @opener.pop; @finisher.pop # pop the writing stack
201
+ ( @head.pop + @tail.pop ).join # join head and tail
202
+ end
203
+
204
+ # Writer of argument grab strings.
205
+ #
206
+ def grab_arg
207
+ msg = "Invalid argument source!"
208
+ @argsrc.source.size == @argsrc.grab_method.size or fail ArgumentError, msg
209
+ grab = case @argsrc.grab_method.last
210
+ when :shift then ".shift"
211
+ when :ref then ""
212
+ when :dup then ".dup"
213
+ else
214
+ msg = "Unknown arg. grab method #{@argsrc.grab_method.last}!"
215
+ fail ArgumentError, msg
216
+ end
217
+ case @argsrc.source.last
218
+ when :args_counted
219
+ x = ( @arg_count += 1 ) - 1
220
+ "args[#{x}]" + grab
221
+ when :args then # now this is a bit difficult, cause
222
+ case @argsrc.grab_method.last # it's necessary to discard the used
223
+ when :shift then # args (shift #@arg_count):
224
+ if @arg_count == 0 then
225
+ "args.shift"
226
+ else
227
+ "(args.shift(#@arg_count); args.shift)"
228
+ end
229
+ when :ref then "args"
230
+ else fail TypeError, "Unknown grab method #{@argsrc.grab_method.last}!"
231
+ end
232
+ when :alpha then alpha_touch; 'alpha' + grab
233
+ when :beta then beta_touch; 'beta' + grab
234
+ when :delta, :epsilon, :zeta then @argsrc.source.last.to_s + grab
235
+ when :psi then "args[-2]" + grab
236
+ when :omega then "args[-1]" + grab
237
+ else fail TypeError, "Unknown argument source #{@argsrc.src.last}!"
238
+ end.tap do
239
+ if @argsrc.source.size > 1 then
240
+ @argsrc.source.pop
241
+ @argsrc.grab_method.pop
242
+ end
243
+ end
244
+ end
245
+
246
+ # Execution method when in the main def (@w == :main).
247
+ #
248
+ def main( cmd )
249
+ self.send( cmd )
250
+ end
251
+
252
+ # Execution method when in a block (@w == :block).
253
+ #
254
+ def block( cmd )
255
+ self.send( cmd )
256
+ end
257
+
258
+ # *** Method writing subroutines.
259
+
260
+ # Active register reader.
261
+ #
262
+ def _r_
263
+ @r
264
+ end
265
+
266
+ # Append string to head.
267
+ #
268
+ def write( x )
269
+ Array( x ).each {|e| @head[-1] << e }
270
+ end
271
+
272
+ # Chain (nullary) method string to the end of the pipe.
273
+ #
274
+ def chain( s )
275
+ @pipe[-1] << ".#{s}"
276
+ end
277
+
278
+ # Suck the pipe into the "memory" ⌿- assign its contents to the designated
279
+ # register of the current pipelene.
280
+ #
281
+ def pipe_2_variable
282
+ @pipe[-1].prepend "#@r = "
283
+ eval "#{@r}_touched = true"
284
+ end
285
+
286
+ # Start a new pipe, on a new line. Without arguments, @r is used.
287
+ #
288
+ def start( s = "#@r" )
289
+ write "\n"; @pipe[-1] = s
290
+ write @pipe.last
291
+ end
292
+
293
+ # Set the pipe to a value, discarding current contents.
294
+ #
295
+ def set( s )
296
+ @pipe[-1].clear << s
297
+ end
298
+
299
+ # Store in active register, and continue in a new pipeline.
300
+ #
301
+ def belay
302
+ pipe_2_variable
303
+ start
304
+ end
305
+
306
+ # Perform pipe_2_variable, execute something else, and go back to @r.
307
+ #
308
+ def exe( s )
309
+ pipe_2_variable; start s; start
310
+ end
311
+
312
+ # Parethesizes the current pipeline.
313
+ #
314
+ def parenthesize_current_pipeline
315
+ @pipe[-1].prepend("( ") << " )"
316
+ end
317
+
318
+ # Write binary operator.
319
+ #
320
+ def binary_operator( s, x = grab_arg )
321
+ @pipe[-1] << " #{s} " << x
322
+ end
323
+
324
+ # Write unary operator.
325
+ #
326
+ def unary_operator( s )
327
+ parenthesize_current_pipeline
328
+ @pipe[-1].prepend s
329
+ end
330
+
331
+ # Returns nothing, or optional block, if flagged to do so.
332
+ #
333
+ def maybe_block
334
+ case @take_block
335
+ when true then @take_block = :taken; '&block'
336
+ when nil, false, :taken then nil
337
+ else
338
+ fail TypeError, "unexpected @take_block value"
339
+ end
340
+ end
341
+
342
+ # Chain unary method.
343
+ #
344
+ def nullary_method( s )
345
+ chain "#{s}(#{maybe_block})"
346
+ end
347
+
348
+ # Chain unary mathod.
349
+ #
350
+ def unary_method( s, x = grab_arg )
351
+ chain "#{s}( #{[x, maybe_block].compact.join(", ")} )"
352
+ end
353
+
354
+ # Chain binary method.
355
+ #
356
+ def binary_method( s, x = grab_arg, y = grab_arg )
357
+ chain "#{s}( #{[x, y, maybe_block].compact.join(", ")} )"
358
+ end
359
+
360
+ # Initiates writing a block method.
361
+ #
362
+ def nullary_method_with_block( str )
363
+ # puts "in nullary_m_with_block, str = #{str}" # DEBUG
364
+ if @take_block == true then
365
+ nullary_method( str )
366
+ else # code a block
367
+ @w = :block # change writing method
368
+ belay # a must before block opening
369
+ # push a new pipe, head and tail to the writing stack:
370
+ @rr.empty? ? ( @rr = [@r] ) : ( @rr.push @r ) # store the register
371
+ @r = :delta # a block runs in its own unswitchable register delta
372
+ @pipe << String.new # push pipe
373
+ # puts "@pipe is << #@pipe >>" # DEBUG
374
+ @head << case @block_arity # push head
375
+ when 0 then [ "#{str} { " ]
376
+ when 1 then set "delta"; [ "#{str} { |epsilon|" ]
377
+ when 2 then @argsrc.zeta; @argsrc.ref!
378
+ set "delta"; [ "#{str} { |epsilon, zeta|" ]
379
+ when -2 then @argsrc.epsilon; @argsrc.ref!
380
+ set "delta"; [ "#{str} { |epsilon, zeta|" ]
381
+ else raise "Unknown @block_arity: #@block_arity"
382
+ end
383
+ write "\n"
384
+ opener = case @block_arity; when 0 then "";
385
+ when 1, 2 then "delta = epsilon"
386
+ when -2 then "delta = zeta" end
387
+ @opener << opener # push opener
388
+ @block_arity = 1 # after use, set block arity flag back to default
389
+ # puts "@pipe is << #@pipe >>" # DEBUG
390
+ write opener; write "\n"; write @pipe.last
391
+ finisher = String.new
392
+ @finisher << finisher # push finisher
393
+ @tail << [ "\n" ] # push tail
394
+ @tail.last << finisher << "\n" << "}" # done
395
+ end
396
+ end
397
+
398
+ # Next block will be written as binary.
399
+ #
400
+ def next_block_will_be_binary
401
+ @block_arity = 2
402
+ end
403
+
404
+ # Next block will be writen as binary with swapped block arguments
405
+ # (delta = zeta; @argsrc.epsilon).
406
+ #
407
+ def next_block_will_be_binary_with_swapped_arguments
408
+ @block_arity = -2
409
+ end
410
+
411
+ # Register 0 (alpha) was ever required for computation.
412
+ #
413
+ def alpha_touch
414
+ belay unless @alpha_touched or @beta_touched
415
+ end
416
+
417
+ # Register 1 (beta) was ever required for the computation.
418
+ #
419
+ def beta_autoinit
420
+ case @opts.op
421
+ when 1 then s = "beta = self.dup rescue self"
422
+ ( @main_opener.clear << s; @beta_touched = true ) unless @beta_touched
423
+ when 2 then @main_opener.clear << "beta = self[1]" unless @beta_touched
424
+ when -2 then @main_opener.clear << "beta = self[0]" unless @beta_touched
425
+ else raise "wrong @opts[:op] value: #{@opts.op}" end
426
+ end
427
+ alias :beta_touch :beta_autoinit
428
+
429
+ # Touch and return the successor of a register, or @r by default.
430
+ #
431
+ def successor_register reg=@r
432
+ send "#{SUCC[reg]}_touch"
433
+ SUCC[reg]
434
+ end
435
+
436
+ # Touch and return the predecessor of a register, or @r by default.
437
+ #
438
+ def predecessor_register reg=@r
439
+ send "#{PRE[reg]}_touch"
440
+ PRE[reg]
441
+ end
442
+ end # class Pyper::PostfixMachine