rbf 0.0.4 → 0.0.5
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/lib/rbf/interpreter/extensions.rb +31 -0
- data/lib/rbf/interpreter/storage.rb +73 -0
- data/lib/rbf/interpreter.rb +10 -78
- data/lib/rbf/jit/code.rb +34 -0
- data/lib/rbf/jit.rb +47 -0
- data/lib/rbf/optimizer.rb +52 -19
- data/lib/rbf/parser.rb +1 -0
- data/lib/rbf.rb +10 -7
- metadata +16 -1
@@ -0,0 +1,31 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
|
6
|
+
#
|
7
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
8
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
9
|
+
#
|
10
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
11
|
+
#++
|
12
|
+
|
13
|
+
require 'stringio'
|
14
|
+
|
15
|
+
class IO
|
16
|
+
begin
|
17
|
+
require 'Win32API'
|
18
|
+
|
19
|
+
def read_char
|
20
|
+
Win32API.new('crtdll', '_getch', [], 'L').Call
|
21
|
+
end
|
22
|
+
rescue LoadError
|
23
|
+
def read_char
|
24
|
+
system 'stty raw -echo'
|
25
|
+
|
26
|
+
STDIN.getc
|
27
|
+
ensure
|
28
|
+
system 'stty -raw echo'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
|
6
|
+
#
|
7
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
8
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
9
|
+
#
|
10
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
11
|
+
#++
|
12
|
+
|
13
|
+
module RBF; class Interpreter
|
14
|
+
|
15
|
+
class Storage < Hash
|
16
|
+
attr_accessor :position
|
17
|
+
|
18
|
+
def initialize (data)
|
19
|
+
if data.is_a?(Array)
|
20
|
+
parent = {}
|
21
|
+
|
22
|
+
data.each_with_index {|value, index|
|
23
|
+
parent[index] = value
|
24
|
+
}
|
25
|
+
|
26
|
+
super(parent)
|
27
|
+
else
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
@position = 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def check!
|
35
|
+
unless self[@position].is_a?(Integer)
|
36
|
+
self[@position] = 0
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def forward!
|
41
|
+
@position += 1
|
42
|
+
end
|
43
|
+
|
44
|
+
def backward!
|
45
|
+
@position -= 1
|
46
|
+
end
|
47
|
+
|
48
|
+
def increase!
|
49
|
+
check!
|
50
|
+
|
51
|
+
self[@position] += 1
|
52
|
+
end
|
53
|
+
|
54
|
+
def decrease!
|
55
|
+
check!
|
56
|
+
|
57
|
+
self[@position] -= 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def set (value, position=nil)
|
61
|
+
self[position ? position.to_i : @position.to_i] = value.ord
|
62
|
+
end
|
63
|
+
|
64
|
+
def get (position=nil)
|
65
|
+
self[position ? position.to_i : @position.to_i].to_i rescue 0
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
"#<Storage(#{position}): #{super}>"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end; end
|
data/lib/rbf/interpreter.rb
CHANGED
@@ -10,87 +10,12 @@
|
|
10
10
|
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
11
11
|
#++
|
12
12
|
|
13
|
-
require '
|
14
|
-
|
15
|
-
class IO
|
16
|
-
begin
|
17
|
-
require 'Win32API'
|
18
|
-
|
19
|
-
def read_char
|
20
|
-
Win32API.new('crtdll', '_getch', [], 'L').Call
|
21
|
-
end
|
22
|
-
rescue LoadError
|
23
|
-
def read_char
|
24
|
-
system 'stty raw -echo'
|
25
|
-
|
26
|
-
STDIN.getc
|
27
|
-
ensure
|
28
|
-
system 'stty -raw echo'
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
13
|
+
require 'rbf/interpreter/extensions'
|
14
|
+
require 'rbf/interpreter/storage'
|
32
15
|
|
33
16
|
module RBF
|
34
17
|
|
35
18
|
class Interpreter
|
36
|
-
class Storage < Hash
|
37
|
-
attr_reader :position
|
38
|
-
|
39
|
-
def initialize (data)
|
40
|
-
if data.is_a?(Array)
|
41
|
-
parent = {}
|
42
|
-
|
43
|
-
data.each_with_index {|value, index|
|
44
|
-
parent[index] = value
|
45
|
-
}
|
46
|
-
|
47
|
-
super(parent)
|
48
|
-
else
|
49
|
-
super
|
50
|
-
end
|
51
|
-
|
52
|
-
@position = 0
|
53
|
-
end
|
54
|
-
|
55
|
-
def check!
|
56
|
-
unless self[@position].is_a?(Integer)
|
57
|
-
self[@position] = 0
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def forward!
|
62
|
-
@position += 1
|
63
|
-
end
|
64
|
-
|
65
|
-
def backward!
|
66
|
-
@position -= 1
|
67
|
-
end
|
68
|
-
|
69
|
-
def increase!
|
70
|
-
check!
|
71
|
-
|
72
|
-
self[@position] += 1
|
73
|
-
end
|
74
|
-
|
75
|
-
def decrease!
|
76
|
-
check!
|
77
|
-
|
78
|
-
self[@position] -= 1
|
79
|
-
end
|
80
|
-
|
81
|
-
def set (value, position=nil)
|
82
|
-
self[position || @position] = value.ord
|
83
|
-
end
|
84
|
-
|
85
|
-
def get (position=nil)
|
86
|
-
self[position || @position].to_i rescue 0
|
87
|
-
end
|
88
|
-
|
89
|
-
def inspect
|
90
|
-
"#<Storage(#{position}): #{super}>"
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
19
|
attr_reader :options, :storage
|
95
20
|
|
96
21
|
def initialize (options={})
|
@@ -103,6 +28,7 @@ class Interpreter
|
|
103
28
|
@parser = RBF::Parser.syntax(RBF.syntax(options[:syntax])).new
|
104
29
|
@transform = RBF::Transform.new
|
105
30
|
@optimizer = RBF::Optimizer.new(options)
|
31
|
+
@jit = RBF::JIT.new(options)
|
106
32
|
end
|
107
33
|
|
108
34
|
def parse (text)
|
@@ -116,7 +42,7 @@ class Interpreter
|
|
116
42
|
end
|
117
43
|
|
118
44
|
def evaluate (tree, options=nil)
|
119
|
-
options
|
45
|
+
options = @options.merge(options || {})
|
120
46
|
|
121
47
|
if options[:catch]
|
122
48
|
@output = StringIO.new
|
@@ -124,6 +50,12 @@ class Interpreter
|
|
124
50
|
@output = STDOUT
|
125
51
|
end
|
126
52
|
|
53
|
+
tree = parse(tree)
|
54
|
+
|
55
|
+
if JIT.supported? && !options[:catch]
|
56
|
+
return @jit.compile(tree).execute
|
57
|
+
end
|
58
|
+
|
127
59
|
cycle(parse(tree))
|
128
60
|
|
129
61
|
if options[:catch]
|
data/lib/rbf/jit/code.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
|
6
|
+
#
|
7
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
8
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
9
|
+
#
|
10
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
11
|
+
#++
|
12
|
+
|
13
|
+
module RBF; class JIT
|
14
|
+
|
15
|
+
class Code
|
16
|
+
attr_reader :options
|
17
|
+
|
18
|
+
def initialize (tree, options=nil)
|
19
|
+
@options = options
|
20
|
+
|
21
|
+
@module = LLVM::Module.create('brainfuck')
|
22
|
+
@module.functions.add('self', [], LLVM::Void) do |func|
|
23
|
+
builder = LLVM::Builder.create
|
24
|
+
end
|
25
|
+
|
26
|
+
@module.verify
|
27
|
+
end
|
28
|
+
|
29
|
+
def execute
|
30
|
+
LLVM::ExecutionEngine.create_jit_compiler(@module).run_function(@module.functions['self'])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end; end
|
data/lib/rbf/jit.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
|
6
|
+
#
|
7
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
8
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
9
|
+
#
|
10
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
11
|
+
#++
|
12
|
+
|
13
|
+
require 'memoized'
|
14
|
+
|
15
|
+
module RBF
|
16
|
+
|
17
|
+
class JIT
|
18
|
+
class << self
|
19
|
+
memoize
|
20
|
+
def supported?
|
21
|
+
raise
|
22
|
+
|
23
|
+
require 'llvm'
|
24
|
+
require 'llvm/core'
|
25
|
+
require 'llvm/execution_engine'
|
26
|
+
require 'llvm/transforms/scalar'
|
27
|
+
|
28
|
+
LLVM.init_x86
|
29
|
+
|
30
|
+
true
|
31
|
+
rescue
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :options
|
37
|
+
|
38
|
+
def initialize (options)
|
39
|
+
@options = options
|
40
|
+
end
|
41
|
+
|
42
|
+
def compile (tree)
|
43
|
+
Code.new(tree, options)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/lib/rbf/optimizer.rb
CHANGED
@@ -16,15 +16,20 @@ class Optimizer
|
|
16
16
|
class Algorithm
|
17
17
|
attr_reader :optimizer
|
18
18
|
|
19
|
-
def initialize (optimizer)
|
19
|
+
def initialize (optimizer, &block)
|
20
20
|
@optimizer = optimizer
|
21
|
+
@block = block
|
21
22
|
end
|
22
23
|
|
23
24
|
def optimize (tree)
|
24
|
-
|
25
|
+
self.instance_exec(tree, &@block)
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
29
|
+
def self.optimization (name, &block)
|
30
|
+
(@@optimizations ||= []) << [name, block]
|
31
|
+
end
|
32
|
+
|
28
33
|
attr_reader :options
|
29
34
|
|
30
35
|
def initialize (options={})
|
@@ -35,38 +40,66 @@ class Optimizer
|
|
35
40
|
result = tree.clone
|
36
41
|
|
37
42
|
algorithms.each {|alg|
|
38
|
-
alg.
|
43
|
+
alg.optimize(result)
|
39
44
|
}
|
40
45
|
|
41
46
|
result
|
42
47
|
end
|
43
48
|
|
44
49
|
def algorithms
|
45
|
-
|
46
|
-
|
50
|
+
@@optimizations.map {|(name, block)|
|
51
|
+
Algorithm.new(self, &block) unless options[name] == false
|
47
52
|
}.compact
|
48
53
|
end
|
49
54
|
|
50
|
-
|
51
|
-
|
52
|
-
|
55
|
+
optimization :useless_operations do |tree|
|
56
|
+
i = 0
|
57
|
+
changed = false
|
53
58
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
59
|
+
until i >= tree.length
|
60
|
+
if tree[i].is_a?(Array) && tree[i + 1].is_a?(Array)
|
61
|
+
optimize(tree[i])
|
62
|
+
optimize(tree[i + 1])
|
58
63
|
|
59
|
-
|
60
|
-
|
61
|
-
|
64
|
+
i += 2
|
65
|
+
elsif tree[i].is_a?(Array)
|
66
|
+
optimize(tree[i])
|
62
67
|
|
63
|
-
|
64
|
-
|
65
|
-
|
68
|
+
i += 1
|
69
|
+
elsif [[?+, ?-], [?-, ?+], [?>, ?<], [?<, ?>]].member?(tree[i ... i + 2])
|
70
|
+
tree.slice!(i ... i + 2)
|
71
|
+
|
72
|
+
changed = true
|
73
|
+
else
|
74
|
+
i += 1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
optimize(tree) if changed
|
79
|
+
end
|
80
|
+
|
81
|
+
optimization :clear_empty_loops do |tree|
|
82
|
+
i = 0
|
83
|
+
|
84
|
+
until i >= tree.length
|
85
|
+
if !tree[i].is_a?(Array)
|
86
|
+
i += 1
|
87
|
+
|
88
|
+
next
|
89
|
+
else
|
90
|
+
optimize(tree[i])
|
91
|
+
|
92
|
+
if tree[i].empty?
|
93
|
+
unless @warned
|
94
|
+
@warned = true
|
95
|
+
warn 'Optimizing out one or more potentially dangerous empty loops'
|
96
|
+
end
|
97
|
+
|
98
|
+
tree.delete_at(i)
|
66
99
|
else
|
67
100
|
i += 1
|
68
101
|
end
|
69
|
-
end
|
102
|
+
end
|
70
103
|
end
|
71
104
|
end
|
72
105
|
end
|
data/lib/rbf/parser.rb
CHANGED
data/lib/rbf.rb
CHANGED
@@ -13,10 +13,9 @@
|
|
13
13
|
require 'rbf/syntax'
|
14
14
|
require 'rbf/parser'
|
15
15
|
require 'rbf/optimizer'
|
16
|
+
require 'rbf/jit'
|
16
17
|
require 'rbf/interpreter'
|
17
18
|
|
18
|
-
require 'parslet/convenience'
|
19
|
-
|
20
19
|
module RBF
|
21
20
|
def self.syntax (name)
|
22
21
|
name.is_a?(Hash) ? name :
|
@@ -74,17 +73,21 @@ module RBF
|
|
74
73
|
when 'storage'
|
75
74
|
STDOUT.puts interpreter.storage.inspect
|
76
75
|
|
77
|
-
when
|
78
|
-
|
76
|
+
when /^position(?:\s+(\d+))?$/
|
77
|
+
if $1
|
78
|
+
interpreter.storage.position = $1
|
79
|
+
else
|
80
|
+
STDOUT.puts interpreter.storage.position.to_s.bold
|
81
|
+
end
|
79
82
|
|
80
|
-
when /^get(?:\s
|
81
|
-
STDOUT.puts "#{($1 || interpreter.storage.position).to_s.bold}: #{interpreter.storage.get($1
|
83
|
+
when /^get(?:\s+(\d+))?$/
|
84
|
+
STDOUT.puts "#{($1 || interpreter.storage.position).to_s.bold}: #{interpreter.storage.get($1)}"
|
82
85
|
|
83
86
|
when /^set(?:\s+(\d+)\s+(\d+))/
|
84
87
|
interpreter.storage.set($2.to_i, $1.to_i)
|
85
88
|
|
86
89
|
else
|
87
|
-
STDOUT.puts 'Command not found'.red
|
90
|
+
STDOUT.puts 'Command not found or used improperly'.red
|
88
91
|
end
|
89
92
|
else
|
90
93
|
output = interpreter.evaluate(line, interpreter.options.merge(:catch => true))
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: rbf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.5
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- meh.
|
@@ -34,6 +34,17 @@ dependencies:
|
|
34
34
|
version: "0"
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: memoized
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id003
|
37
48
|
description:
|
38
49
|
email: meh@paranoici.org
|
39
50
|
executables:
|
@@ -43,10 +54,14 @@ extensions: []
|
|
43
54
|
extra_rdoc_files: []
|
44
55
|
|
45
56
|
files:
|
57
|
+
- lib/rbf/jit.rb
|
46
58
|
- lib/rbf/interpreter.rb
|
59
|
+
- lib/rbf/interpreter/extensions.rb
|
60
|
+
- lib/rbf/interpreter/storage.rb
|
47
61
|
- lib/rbf/syntax.rb
|
48
62
|
- lib/rbf/parser.rb
|
49
63
|
- lib/rbf/optimizer.rb
|
64
|
+
- lib/rbf/jit/code.rb
|
50
65
|
- lib/rbf.rb
|
51
66
|
- bin/rbf
|
52
67
|
homepage: http://github.com/meh/rbf
|