windstorm 0.1.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.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +236 -0
- data/Rakefile +9 -0
- data/lib/windstorm.rb +12 -0
- data/lib/windstorm/executor.rb +88 -0
- data/lib/windstorm/machine.rb +254 -0
- data/lib/windstorm/parser.rb +64 -0
- data/lib/windstorm/version.rb +3 -0
- data/spec/fixtures/bf.yml +31 -0
- data/spec/fixtures/invalid.yml +6 -0
- data/spec/fixtures/source.txt +17 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/windstorm/executor_spec.rb +203 -0
- data/spec/windstorm/machine_execute_spec.rb +911 -0
- data/spec/windstorm/machine_spec.rb +571 -0
- data/spec/windstorm/parser_spec.rb +133 -0
- data/windstorm.gemspec +24 -0
- metadata +83 -0
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 parrot-studio
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
BF風言語解析器 - Windstorm
|
2
|
+
===============
|
3
|
+
|
4
|
+
Introduction
|
5
|
+
---------------
|
6
|
+
BrainF**k(BF)風のソースコード解析と実行をおこなうRubyのGemです。
|
7
|
+
定義を作成することで、自由にBF風の言語を構築することができます。
|
8
|
+
いわゆる「ネタ言語用解析器」です。
|
9
|
+
|
10
|
+
Ruby Version
|
11
|
+
---------------
|
12
|
+
- Ruby1.9.x以上
|
13
|
+
- CRuby1.9.2/1.9.3でspecが通ることを確認しています
|
14
|
+
- Ruby1.9.xに対応した処理系であれば動くと思われます(JRuby等)
|
15
|
+
- Ruby1.8.xに対応する予定はありません(動くかもしれませんが、一切保証しません)
|
16
|
+
|
17
|
+
Source
|
18
|
+
---------------
|
19
|
+
https://github.com/parrot-studio/windstorm
|
20
|
+
|
21
|
+
Installation
|
22
|
+
---------------
|
23
|
+
gem install windstorm
|
24
|
+
|
25
|
+
Usage
|
26
|
+
---------------
|
27
|
+
### 概要
|
28
|
+
基本的な実行原理はBFと同じです。
|
29
|
+
外部から自由に定義を与えることで、自由な解析器を作ることができます。
|
30
|
+
|
31
|
+
WindstormはParser/Machine/Executorの3クラスで構成されています。
|
32
|
+
|
33
|
+
- Parser : 与えられた定義を元に、ソースコードを命令の配列に変換する
|
34
|
+
- Machine : 与えられた命令の配列を元に、命令を実行して結果を返す
|
35
|
+
- Executor : ParserとMachineを連携させて一つの処理系を構成する
|
36
|
+
|
37
|
+
### 命令
|
38
|
+
WindstormではBFに存在する8命令を抽象化し、シンボルで定義しています。
|
39
|
+
(括弧内は対応するBFの命令語)
|
40
|
+
|
41
|
+
- :pinc(>) : ポインタをインクリメントする
|
42
|
+
- :pdec(<) : ポインタをデクリメントする
|
43
|
+
- :inc (+) : ポインタが指す値をインクリメントする
|
44
|
+
- :dec (-) : ポインタが指す値をデクリメントする
|
45
|
+
- :out (.) : ポインタが指す値を出力する
|
46
|
+
- :inc (,) : 入力から読み込んで、ポインタが指す値を更新する
|
47
|
+
- :jmp ([) : ポインタが指す値が0ならば、対応する :ret にジャンプする
|
48
|
+
- :ret (]) : ポインタが指す値が0でなければ、対応する :jmp にジャンプする
|
49
|
+
|
50
|
+
また、以下の2命令を追加しています。
|
51
|
+
|
52
|
+
- :clip : ポインタが指す値を専用のclipバッファに保存する
|
53
|
+
- 再度呼ばれた場合は、新しい値でclipバッファを上書きする
|
54
|
+
- :paste : ポインタが指す値をclipバッファの値で上書きする
|
55
|
+
- 一度も :clip を呼ばれていない場合、0で上書きする
|
56
|
+
|
57
|
+
### 定義
|
58
|
+
|
59
|
+
10の命令について、対応する文字列をHash形式で指定します
|
60
|
+
|
61
|
+
table = {
|
62
|
+
:pinc => '>',
|
63
|
+
:pdec => '<',
|
64
|
+
:inc => ['+', 'a'], # 複数指定も可能
|
65
|
+
:dec => ['-', 'あ'], # マルチバイトでもOK
|
66
|
+
:out => ['.', '出力'] # 一文字でなくてもOK
|
67
|
+
# 不要な命令は定義しなくても良い
|
68
|
+
}
|
69
|
+
exec = Executor.create_from_table(table)
|
70
|
+
|
71
|
+
Executorは外部ファイルからの読み込みに対応しています。
|
72
|
+
その場合は定義をYAML形式(UTF-8)で記述します。
|
73
|
+
|
74
|
+
# table.yml
|
75
|
+
:pinc:
|
76
|
+
- '>'
|
77
|
+
:pdec:
|
78
|
+
- '<'
|
79
|
+
:inc:
|
80
|
+
- '+'
|
81
|
+
- 'a'
|
82
|
+
:dec:
|
83
|
+
- '-'
|
84
|
+
- あ
|
85
|
+
:out:
|
86
|
+
- '.'
|
87
|
+
- 出力
|
88
|
+
########
|
89
|
+
|
90
|
+
exec = Executor.create_from_file('table.yml')
|
91
|
+
|
92
|
+
### ソースコード仕様
|
93
|
+
|
94
|
+
- 文字コードはUTF-8で記述してください
|
95
|
+
|
96
|
+
- 定義されていない文字列は全て無視します
|
97
|
+
- 定義に重複する文字列があった場合、どちらの命令に変換されるかは不定です
|
98
|
+
- 解析には正規表現を用いていますので、それに依存した動作になります
|
99
|
+
|
100
|
+
- '#'か'//'で始まる行はコメントとして扱われます
|
101
|
+
- 空白を含め、一文字でも'#'等の前にあるとコメントになりません
|
102
|
+
- よって、文の途中に'#'等があっても、コメントとして解釈しません
|
103
|
+
- なので、'#'等を定義に含めても、命令として解釈させることが可能です
|
104
|
+
|
105
|
+
### Executorによる解析と実行
|
106
|
+
|
107
|
+
# 定義をYAMLファイルから読み込み
|
108
|
+
exec = Executor.create_from_file('table.yml')
|
109
|
+
|
110
|
+
# 命令に対応する文字列だけを抽出
|
111
|
+
exec.filter('+!+? 出力します >') #=> ['+', '+', '出力', '>']
|
112
|
+
|
113
|
+
# 命令に変換した配列を取得
|
114
|
+
exec.build('+!+? 出力します >') #=> [:inc, :inc, :out, :pinc]
|
115
|
+
|
116
|
+
# 命令を実行
|
117
|
+
source = (['+'] * 33 + ['.']).join
|
118
|
+
exec.execute(source) #=> '!'
|
119
|
+
|
120
|
+
# デバッグモード(後述)で実行
|
121
|
+
exec.debug_execute(source)
|
122
|
+
|
123
|
+
# ファイルから読み込んで実行
|
124
|
+
exec.filter_from_file('hello.bf')
|
125
|
+
exec.build_from_file('hello.bf')
|
126
|
+
exec.execute_from_file('hello.bf')
|
127
|
+
exec.debug_execute_from_file('hello.bf')
|
128
|
+
|
129
|
+
Executorが中で使っているParser/Machineの詳細は、
|
130
|
+
それぞれのspecを参照してください。
|
131
|
+
|
132
|
+
### 実行オプション
|
133
|
+
|
134
|
+
Executor#executeにはオプションを与えることができます
|
135
|
+
|
136
|
+
exec.execute(source, :debug => true, :size => 10)
|
137
|
+
|
138
|
+
- :debug, :loose, :flash : trueを与えると、各モードが有効になります
|
139
|
+
- :size : 値を格納するバッファのサイズを指定します
|
140
|
+
|
141
|
+
格納バッファサイズのデフォルトは100となっていますが、
|
142
|
+
実行時オプションで :size を指定することにより、
|
143
|
+
任意のサイズに変更することが可能です。(通常は変更不要)
|
144
|
+
|
145
|
+
格納バッファサイズはWindstorm処理系の仮想的な概念で、
|
146
|
+
本質的にはRuby処理系のArrayクラス仕様に依存します。
|
147
|
+
後述のlooseモードの説明も参照してください。
|
148
|
+
|
149
|
+
#### デバッグモード(:debug)
|
150
|
+
|
151
|
+
命令を一つ実行するたびに、デバッグ文を出力します。
|
152
|
+
|
153
|
+
# 出力例
|
154
|
+
"step:197 com:out index:43 point:0 buffer:[115, 0] clip:100 result:s"
|
155
|
+
|
156
|
+
- step : 実行ステップ数
|
157
|
+
- com : 実行したコマンド
|
158
|
+
- index : comに対応する命令配列のindex
|
159
|
+
- point : ポインタの値(bufferのindexに対応)
|
160
|
+
- buffer : 値の格納バッファ(配列)
|
161
|
+
- clip : clipバッファに保存されている値
|
162
|
+
- result : :out で出力した結果
|
163
|
+
|
164
|
+
出力先はRuby処理系の$stdoutが指すIOオブジェクトです。
|
165
|
+
通常は実行しているコンソールになります。
|
166
|
+
|
167
|
+
#### strictモードとlooseモード(:loose)
|
168
|
+
|
169
|
+
Windstormでソースを実行する目的は、
|
170
|
+
最終的に「何らかの文字列の出力すること」です。
|
171
|
+
|
172
|
+
そのため、デフォルトでは以下の状況になるとエラーになります。
|
173
|
+
|
174
|
+
- ポインタが指す値が負になる
|
175
|
+
- ポインタが負になる
|
176
|
+
- ポインタがsizeの範囲を超える
|
177
|
+
- clipバッファに負の値が格納される
|
178
|
+
|
179
|
+
これを「strictモード」と呼びます。
|
180
|
+
|
181
|
+
ただ、Ruby処理系としてみた場合、
|
182
|
+
Arrayに格納される値は負でもかまわないし、
|
183
|
+
ポインタ(Machine#pointが指す整数)が範囲を超えても問題ありません。
|
184
|
+
|
185
|
+
そこで、Ruby処理系として都合が悪い状況になるまでエラーにしないという指定が可能です。
|
186
|
+
これを「looseモード」と呼びます
|
187
|
+
|
188
|
+
looseモードでは以下の場合でもエラーとみなしません。
|
189
|
+
|
190
|
+
- ポインタが範囲を超えたが、値を操作する前に正常範囲に戻った場合
|
191
|
+
- ポインタが指す値が負になったが、:out が呼ばれる前に0以上に戻った場合
|
192
|
+
- clipバッファに負の値が格納された場合
|
193
|
+
|
194
|
+
その場でエラーにしないというだけで、想定した処理がおこなわれるかは状況次第です。
|
195
|
+
特に理由がない限り、strictモードで実行してください。
|
196
|
+
|
197
|
+
#### flashモード(:flash)
|
198
|
+
|
199
|
+
Executor#executeは出力結果の文字列を返しますが、
|
200
|
+
本来のBFでは、:out を呼んだ時点で文字を出力します。
|
201
|
+
BFと同じく、即時出力をおこなうのが「flashモード」です。
|
202
|
+
|
203
|
+
通常の実行時にはさほど差がありませんが、
|
204
|
+
debugモードを有効にしていると、出力が混じるのでお勧めできません。
|
205
|
+
|
206
|
+
### WindstormのI/Oについて
|
207
|
+
|
208
|
+
このドキュメントで使っている「出力先」という言葉は、
|
209
|
+
厳密に言うとRuby処理系の_$stdout_が指すIOオブジェクトです。
|
210
|
+
通常はRubyを実行しているコンソールになります。
|
211
|
+
|
212
|
+
同じく、「入力元」はRuby処理系の_$stdin_が指すIOオブジェクトです。
|
213
|
+
通常はRubyを実行しているコンソールからの入力になります。
|
214
|
+
|
215
|
+
これらの入出力を変更する必要はありませんが、
|
216
|
+
必要であれば、StringIOなどのオブジェクトに差し替えることが可能です。
|
217
|
+
Machineに対するspecの記述も参考にしてください。
|
218
|
+
|
219
|
+
Note
|
220
|
+
---------------
|
221
|
+
- windstorm : 暴風 = 上州名物・空っ風
|
222
|
+
|
223
|
+
- Windstormを使ってネタプログラム言語を作成するツール、「Youma」もあります
|
224
|
+
- https://github.com/parrot-studio/youma
|
225
|
+
- ネタ言語のサンプルも含まれています
|
226
|
+
- WindstormをWebアプリ等に組み込むのでなければ、Youmaを使ってください
|
227
|
+
|
228
|
+
License
|
229
|
+
---------------
|
230
|
+
The MIT License
|
231
|
+
|
232
|
+
see LICENSE file for detail
|
233
|
+
|
234
|
+
Author
|
235
|
+
---------------
|
236
|
+
ぱろっと(@parrot_studio / parrot.studio.dev at gmail.com)
|
data/Rakefile
ADDED
data/lib/windstorm.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Windstorm
|
3
|
+
|
4
|
+
BUFFER_DEFAULT_SIZE = 100
|
5
|
+
COMMANDS = [:pinc, :pdec, :inc, :dec, :out, :inp, :jmp, :ret, :clip, :paste]
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
path = File.join(File.dirname(__FILE__), 'windstorm')
|
10
|
+
require File.join(path, 'parser')
|
11
|
+
require File.join(path, 'machine')
|
12
|
+
require File.join(path, 'executor')
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Windstorm
|
5
|
+
module Reader
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def read(file)
|
10
|
+
raise 'file not found' unless (File.exist?(file) && File.file?(file))
|
11
|
+
File.read(file)
|
12
|
+
end
|
13
|
+
|
14
|
+
def yaml_read(file)
|
15
|
+
YAML.load(read(file))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Executor
|
20
|
+
include Reader
|
21
|
+
|
22
|
+
attr_writer :parser
|
23
|
+
|
24
|
+
class << self
|
25
|
+
include Reader
|
26
|
+
|
27
|
+
def create_from_file(file)
|
28
|
+
create_from_table(yaml_read(file))
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_from_table(t)
|
32
|
+
create(Parser.create(t))
|
33
|
+
end
|
34
|
+
|
35
|
+
def create(pa)
|
36
|
+
return unless pa
|
37
|
+
ex = self.new
|
38
|
+
ex.parser = pa
|
39
|
+
ex
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def parser
|
45
|
+
raise 'parser not found' unless @parser
|
46
|
+
@parser
|
47
|
+
end
|
48
|
+
|
49
|
+
def machine
|
50
|
+
raise 'not executed yet' unless @machine
|
51
|
+
@machine
|
52
|
+
end
|
53
|
+
|
54
|
+
def execute(source, params = nil)
|
55
|
+
@machine = Machine.create(parser.build(source), params)
|
56
|
+
@machine.execute
|
57
|
+
end
|
58
|
+
|
59
|
+
def execute_from_file(file, params = nil)
|
60
|
+
execute(read(file), params)
|
61
|
+
end
|
62
|
+
|
63
|
+
def filter(source)
|
64
|
+
parser.filter(source)
|
65
|
+
end
|
66
|
+
|
67
|
+
def filter_from_file(file)
|
68
|
+
filter(read(file))
|
69
|
+
end
|
70
|
+
|
71
|
+
def build(source)
|
72
|
+
parser.build(source)
|
73
|
+
end
|
74
|
+
|
75
|
+
def build_from_file(file)
|
76
|
+
build(read(file))
|
77
|
+
end
|
78
|
+
|
79
|
+
def debug_execute(source, params = nil)
|
80
|
+
execute(source, {:debug => true}.merge(params || {}))
|
81
|
+
end
|
82
|
+
|
83
|
+
def debug_execute_from_file(file, params = nil)
|
84
|
+
debug_execute(read(file), params)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Windstorm
|
3
|
+
class Machine
|
4
|
+
|
5
|
+
attr_writer :commands, :size, :debug, :flash, :loose
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def create(coms, params = nil)
|
10
|
+
params ||= {}
|
11
|
+
s = params[:size].to_i
|
12
|
+
|
13
|
+
m = self.new
|
14
|
+
m.commands = coms
|
15
|
+
m.size = s if s > 0
|
16
|
+
m.debug = true if params[:debug]
|
17
|
+
m.flash = true if params[:flash]
|
18
|
+
m.loose = true if params[:loose]
|
19
|
+
m
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute(coms, params = nil)
|
23
|
+
m = create(coms, params)
|
24
|
+
m.execute
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def execute
|
30
|
+
return result if finish?
|
31
|
+
parse_jump
|
32
|
+
loop do
|
33
|
+
break if finish?
|
34
|
+
step_execute
|
35
|
+
end
|
36
|
+
result
|
37
|
+
end
|
38
|
+
|
39
|
+
def step_execute
|
40
|
+
case com
|
41
|
+
when :pinc
|
42
|
+
move_point_inc
|
43
|
+
when :pdec
|
44
|
+
move_point_dec
|
45
|
+
when :inc
|
46
|
+
increment
|
47
|
+
when :dec
|
48
|
+
decrement
|
49
|
+
when :out
|
50
|
+
output
|
51
|
+
when :inp
|
52
|
+
input
|
53
|
+
when :jmp
|
54
|
+
jump if value == 0
|
55
|
+
when :ret
|
56
|
+
jump unless value == 0
|
57
|
+
when :clip
|
58
|
+
clip
|
59
|
+
when :paste
|
60
|
+
paste
|
61
|
+
end
|
62
|
+
debug_out if debug?
|
63
|
+
forward
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def debug?
|
68
|
+
@debug ? true : false
|
69
|
+
end
|
70
|
+
|
71
|
+
def loose?
|
72
|
+
@loose ? true : false
|
73
|
+
end
|
74
|
+
|
75
|
+
def strict?
|
76
|
+
! loose?
|
77
|
+
end
|
78
|
+
|
79
|
+
def step
|
80
|
+
@step ||= 0
|
81
|
+
@step
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_step
|
85
|
+
@step = step + 1
|
86
|
+
step
|
87
|
+
end
|
88
|
+
|
89
|
+
def commands
|
90
|
+
@commands ||= []
|
91
|
+
@commands
|
92
|
+
end
|
93
|
+
|
94
|
+
def index
|
95
|
+
@index ||= 0
|
96
|
+
@index
|
97
|
+
end
|
98
|
+
|
99
|
+
def add_index
|
100
|
+
@index = index + 1
|
101
|
+
@index
|
102
|
+
end
|
103
|
+
|
104
|
+
def com(ind = nil)
|
105
|
+
ind ||= index
|
106
|
+
commands[ind]
|
107
|
+
end
|
108
|
+
|
109
|
+
def forward
|
110
|
+
add_step
|
111
|
+
add_index
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
115
|
+
def finish?
|
116
|
+
index >= commands.size ? true : false
|
117
|
+
end
|
118
|
+
|
119
|
+
def jump_table
|
120
|
+
@jump_table ||= {}
|
121
|
+
@jump_table
|
122
|
+
end
|
123
|
+
|
124
|
+
def parse_jump
|
125
|
+
return if commands.empty?
|
126
|
+
inds = []
|
127
|
+
commands.each.with_index do |com, i|
|
128
|
+
case com
|
129
|
+
when :jmp
|
130
|
+
inds << i
|
131
|
+
when :ret
|
132
|
+
hind = inds.delete_at(-1)
|
133
|
+
raise 'jump couples invalid' unless hind
|
134
|
+
jump_table[hind] = i
|
135
|
+
jump_table[i] = hind
|
136
|
+
end
|
137
|
+
end
|
138
|
+
raise 'jump couples invalid' unless inds.empty?
|
139
|
+
if debug? && !jump_table.empty?
|
140
|
+
puts 'jump table:'
|
141
|
+
puts jump_table
|
142
|
+
end
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
def jump_index(ind)
|
147
|
+
error 'jump index is nil' unless ind
|
148
|
+
strict_error 'out of command index' if ind < 0 || ind >= commands.size
|
149
|
+
@index = ind
|
150
|
+
end
|
151
|
+
|
152
|
+
def jump
|
153
|
+
jump_index(jump_table[index])
|
154
|
+
end
|
155
|
+
|
156
|
+
def size
|
157
|
+
@size ||= BUFFER_DEFAULT_SIZE
|
158
|
+
@size = BUFFER_DEFAULT_SIZE if @size <= 0
|
159
|
+
@size
|
160
|
+
end
|
161
|
+
|
162
|
+
def point
|
163
|
+
@point ||= 0
|
164
|
+
strict_error 'point over' if (@point < 0 || @point >= size)
|
165
|
+
@point
|
166
|
+
end
|
167
|
+
|
168
|
+
def move_point_inc
|
169
|
+
@point = point + 1
|
170
|
+
point
|
171
|
+
end
|
172
|
+
|
173
|
+
def move_point_dec
|
174
|
+
@point = point - 1
|
175
|
+
point
|
176
|
+
end
|
177
|
+
|
178
|
+
def increment
|
179
|
+
replace_value(value+1)
|
180
|
+
end
|
181
|
+
|
182
|
+
def decrement
|
183
|
+
replace_value(value-1)
|
184
|
+
end
|
185
|
+
|
186
|
+
def buffer
|
187
|
+
@buffer ||= []
|
188
|
+
@buffer
|
189
|
+
end
|
190
|
+
|
191
|
+
def value(po = nil)
|
192
|
+
po ||= point
|
193
|
+
strict_error 'point over' if (po < 0 || po >= size)
|
194
|
+
buffer[po] || 0
|
195
|
+
end
|
196
|
+
|
197
|
+
def replace_value(val, po = nil)
|
198
|
+
po ||= point
|
199
|
+
strict_error 'value under 0' if val < 0
|
200
|
+
strict_error 'point over' if (po < 0 || po >= size)
|
201
|
+
buffer[po] = val
|
202
|
+
end
|
203
|
+
|
204
|
+
def cache
|
205
|
+
@cache ||= ""
|
206
|
+
@cache
|
207
|
+
end
|
208
|
+
alias :result :cache
|
209
|
+
|
210
|
+
def output
|
211
|
+
putc value if @flash
|
212
|
+
cache << value.chr
|
213
|
+
end
|
214
|
+
|
215
|
+
def input
|
216
|
+
c = $stdin.getc
|
217
|
+
replace_value(c.bytes.first)
|
218
|
+
end
|
219
|
+
|
220
|
+
def clip_value
|
221
|
+
@clip_value ||= 0
|
222
|
+
@clip_value
|
223
|
+
end
|
224
|
+
|
225
|
+
def replace_clip_value(val)
|
226
|
+
strict_error 'value under 0' if val < 0
|
227
|
+
@clip_value = val
|
228
|
+
end
|
229
|
+
|
230
|
+
def clip
|
231
|
+
replace_clip_value(value)
|
232
|
+
end
|
233
|
+
|
234
|
+
def paste
|
235
|
+
replace_value(clip_value)
|
236
|
+
end
|
237
|
+
|
238
|
+
def debug_out
|
239
|
+
puts "step:#{step} com:#{com} index:#{index} point:#{point} buffer:#{buffer.inspect} clip:#{clip_value} result:#{result}"
|
240
|
+
end
|
241
|
+
|
242
|
+
private
|
243
|
+
|
244
|
+
def error(msg)
|
245
|
+
raise msg
|
246
|
+
end
|
247
|
+
|
248
|
+
def strict_error(msg)
|
249
|
+
return unless strict?
|
250
|
+
error(msg)
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
end
|