rbf 0.0.3 → 0.0.4
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/bin/rbf +19 -1
- data/lib/rbf/interpreter.rb +63 -24
- data/lib/rbf/optimizer.rb +74 -0
- data/lib/rbf/parser.rb +14 -1
- data/lib/rbf.rb +69 -8
- metadata +13 -2
- data/lib/rbf/transform.rb +0 -30
data/bin/rbf
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
2
|
require 'rubygems'
|
3
|
+
require 'optparse'
|
3
4
|
require 'rbf'
|
4
5
|
|
5
|
-
|
6
|
+
options = {}
|
7
|
+
|
8
|
+
OptionParser.new do |opts|
|
9
|
+
opts.on '-h', '--help', 'Display this screen' do
|
10
|
+
puts opts
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
|
14
|
+
opts.on '-s', '--syntax [NAME]', ['default', 'nintendo'], 'Choose a brainfuck syntax' do |value|
|
15
|
+
options[:syntax] = value
|
16
|
+
end
|
17
|
+
end.parse!
|
18
|
+
|
19
|
+
if ARGV.first
|
20
|
+
RBF.execute(ARGV.first, options[:syntax])
|
21
|
+
else
|
22
|
+
RBF.repl(options)
|
23
|
+
end
|
data/lib/rbf/interpreter.rb
CHANGED
@@ -33,63 +33,98 @@ end
|
|
33
33
|
module RBF
|
34
34
|
|
35
35
|
class Interpreter
|
36
|
-
class Storage <
|
37
|
-
|
38
|
-
super
|
36
|
+
class Storage < Hash
|
37
|
+
attr_reader :position
|
39
38
|
|
40
|
-
|
39
|
+
def initialize (data)
|
40
|
+
if data.is_a?(Array)
|
41
|
+
parent = {}
|
41
42
|
|
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
|
43
53
|
end
|
44
54
|
|
45
55
|
def check!
|
46
|
-
|
47
|
-
self[@
|
56
|
+
unless self[@position].is_a?(Integer)
|
57
|
+
self[@position] = 0
|
48
58
|
end
|
49
59
|
end
|
50
60
|
|
51
61
|
def forward!
|
52
|
-
@
|
53
|
-
|
54
|
-
check!
|
62
|
+
@position += 1
|
55
63
|
end
|
56
64
|
|
57
65
|
def backward!
|
58
|
-
@
|
59
|
-
|
60
|
-
check!
|
66
|
+
@position -= 1
|
61
67
|
end
|
62
68
|
|
63
69
|
def increase!
|
64
|
-
|
70
|
+
check!
|
71
|
+
|
72
|
+
self[@position] += 1
|
65
73
|
end
|
66
74
|
|
67
75
|
def decrease!
|
68
|
-
|
76
|
+
check!
|
77
|
+
|
78
|
+
self[@position] -= 1
|
69
79
|
end
|
70
80
|
|
71
|
-
def set (value)
|
72
|
-
self[@
|
81
|
+
def set (value, position=nil)
|
82
|
+
self[position || @position] = value.ord
|
73
83
|
end
|
74
84
|
|
75
|
-
def get
|
76
|
-
self[@
|
85
|
+
def get (position=nil)
|
86
|
+
self[position || @position].to_i rescue 0
|
87
|
+
end
|
88
|
+
|
89
|
+
def inspect
|
90
|
+
"#<Storage(#{position}): #{super}>"
|
77
91
|
end
|
78
92
|
end
|
79
93
|
|
80
|
-
|
81
|
-
|
94
|
+
attr_reader :options, :storage
|
95
|
+
|
96
|
+
def initialize (options={})
|
97
|
+
@options = options
|
98
|
+
|
99
|
+
@storage = Storage.new(options[:env] || [])
|
82
100
|
@input = STDIN
|
101
|
+
@output = STDOUT
|
102
|
+
|
103
|
+
@parser = RBF::Parser.syntax(RBF.syntax(options[:syntax])).new
|
104
|
+
@transform = RBF::Transform.new
|
105
|
+
@optimizer = RBF::Optimizer.new(options)
|
106
|
+
end
|
107
|
+
|
108
|
+
def parse (text)
|
109
|
+
return text if text.is_a?(Array)
|
110
|
+
|
111
|
+
parsed = @parser.parse_with_debug(text)
|
112
|
+
|
113
|
+
raise SyntaxError, 'There is a syntax error' unless parsed
|
114
|
+
|
115
|
+
@optimizer.optimize(@transform.apply(parsed))
|
83
116
|
end
|
84
117
|
|
85
|
-
def evaluate (tree, options=
|
118
|
+
def evaluate (tree, options=nil)
|
119
|
+
options ||= {}
|
120
|
+
|
86
121
|
if options[:catch]
|
87
122
|
@output = StringIO.new
|
88
123
|
else
|
89
124
|
@output = STDOUT
|
90
125
|
end
|
91
126
|
|
92
|
-
cycle(tree)
|
127
|
+
cycle(parse(tree))
|
93
128
|
|
94
129
|
if options[:catch]
|
95
130
|
@output.rewind
|
@@ -97,6 +132,10 @@ class Interpreter
|
|
97
132
|
end
|
98
133
|
end
|
99
134
|
|
135
|
+
def execute (path, options=nil)
|
136
|
+
evaluate(File.read(path), options)
|
137
|
+
end
|
138
|
+
|
100
139
|
def cycle (tree)
|
101
140
|
tree.each {|token|
|
102
141
|
if token.is_a?(Array)
|
@@ -130,7 +169,7 @@ class Interpreter
|
|
130
169
|
end
|
131
170
|
|
132
171
|
define_method ?. do
|
133
|
-
@output.print @storage.get.chr
|
172
|
+
@output.print @storage.get.chr rescue nil
|
134
173
|
@output.flush
|
135
174
|
end
|
136
175
|
|
@@ -0,0 +1,74 @@
|
|
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
|
14
|
+
|
15
|
+
class Optimizer
|
16
|
+
class Algorithm
|
17
|
+
attr_reader :optimizer
|
18
|
+
|
19
|
+
def initialize (optimizer)
|
20
|
+
@optimizer = optimizer
|
21
|
+
end
|
22
|
+
|
23
|
+
def optimize (tree)
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :options
|
29
|
+
|
30
|
+
def initialize (options={})
|
31
|
+
@options = options
|
32
|
+
end
|
33
|
+
|
34
|
+
def optimize (tree)
|
35
|
+
result = tree.clone
|
36
|
+
|
37
|
+
algorithms.each {|alg|
|
38
|
+
alg.new(self).optimize(result)
|
39
|
+
}
|
40
|
+
|
41
|
+
result
|
42
|
+
end
|
43
|
+
|
44
|
+
def algorithms
|
45
|
+
Optimizer.constants.map {|const|
|
46
|
+
Optimizer.const_get(const) unless options[const.to_s.downcase.to_sym] == false
|
47
|
+
}.compact
|
48
|
+
end
|
49
|
+
|
50
|
+
class UselessOperations < Algorithm
|
51
|
+
def optimize (tree)
|
52
|
+
i = 0
|
53
|
+
|
54
|
+
until i >= tree.length
|
55
|
+
if tree[i].is_a?(Array) && tree[i + 1].is_a?(Array)
|
56
|
+
optimize(tree[i])
|
57
|
+
optimize(tree[i + 1])
|
58
|
+
|
59
|
+
i += 2
|
60
|
+
elsif tree[i].is_a?(Array)
|
61
|
+
optimize(tree[i])
|
62
|
+
|
63
|
+
i += 1
|
64
|
+
elsif [[?+, ?-], [?-, ?+], [?>, ?<], [?<, ?>]].member?(tree[i ... i + 2])
|
65
|
+
tree.slice!(i ... i + 2)
|
66
|
+
else
|
67
|
+
i += 1
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
data/lib/rbf/parser.rb
CHANGED
@@ -15,7 +15,7 @@ require 'parslet'
|
|
15
15
|
module RBF
|
16
16
|
|
17
17
|
class Parser < Parslet::Parser
|
18
|
-
def self.
|
18
|
+
def self.syntax (keys)
|
19
19
|
klass = self.clone
|
20
20
|
|
21
21
|
klass.class_eval {
|
@@ -69,4 +69,17 @@ class Parser < Parslet::Parser
|
|
69
69
|
root :code
|
70
70
|
end
|
71
71
|
|
72
|
+
class Transform < Parslet::Transform
|
73
|
+
rule(?> => simple(:x)) { ?> }
|
74
|
+
rule(?< => simple(:x)) { ?< }
|
75
|
+
|
76
|
+
rule(?+ => simple(:x)) { ?+ }
|
77
|
+
rule(?- => simple(:x)) { ?- }
|
78
|
+
|
79
|
+
rule(?. => simple(:x)) { ?. }
|
80
|
+
rule(?, => simple(:x)) { ?, }
|
81
|
+
|
82
|
+
rule(:loop => subtree(:x)) { x }
|
83
|
+
end
|
84
|
+
|
72
85
|
end
|
data/lib/rbf.rb
CHANGED
@@ -12,28 +12,89 @@
|
|
12
12
|
|
13
13
|
require 'rbf/syntax'
|
14
14
|
require 'rbf/parser'
|
15
|
-
require 'rbf/
|
15
|
+
require 'rbf/optimizer'
|
16
16
|
require 'rbf/interpreter'
|
17
17
|
|
18
18
|
require 'parslet/convenience'
|
19
19
|
|
20
20
|
module RBF
|
21
|
-
def self.
|
22
|
-
|
21
|
+
def self.syntax (name)
|
22
|
+
name.is_a?(Hash) ? name :
|
23
|
+
(RBF::Syntax.const_get(name.to_s.capitalize) rescue nil) || Syntax::Default
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.parse (text, syn=nil)
|
27
|
+
Transform.new.apply(Parser.syntax(syntax(syn)).new.parse_with_debug(text)) or
|
23
28
|
raise SyntaxError, 'There is a syntax error'
|
24
29
|
end
|
25
30
|
|
26
|
-
def self.
|
27
|
-
|
31
|
+
def self.optimize (text, options=nil)
|
32
|
+
options ||= {}
|
33
|
+
|
34
|
+
Optimizer.new(options).optimize(text.is_a?(Array) ? text : parse(text.to_s, options[:syntax]))
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.evaluate (text, options=nil)
|
38
|
+
options ||= {}
|
28
39
|
|
29
|
-
Interpreter.new.evaluate(
|
40
|
+
Interpreter.new(options).evaluate(text)
|
30
41
|
end
|
31
42
|
|
32
43
|
def self.execute (file, options={})
|
33
44
|
evaluate(File.read(file), options)
|
34
45
|
end
|
35
46
|
|
36
|
-
def self.[] (text,
|
37
|
-
evaluate(text, :catch => true, :syntax =>
|
47
|
+
def self.[] (text, syn=nil)
|
48
|
+
evaluate(text, :catch => true, :syntax => syn)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.repl (options=nil)
|
52
|
+
require 'colorb'
|
53
|
+
|
54
|
+
interpreter = Interpreter.new(options || {})
|
55
|
+
|
56
|
+
loop do
|
57
|
+
STDOUT.print '>> '.bold
|
58
|
+
STDOUT.flush
|
59
|
+
|
60
|
+
begin
|
61
|
+
line = STDIN.gets.chomp rescue nil or raise SystemExit
|
62
|
+
rescue Interrupt, SystemExit
|
63
|
+
puts "\nExiting REPL."
|
64
|
+
|
65
|
+
return false
|
66
|
+
end
|
67
|
+
|
68
|
+
begin
|
69
|
+
if line.start_with?('!')
|
70
|
+
case line[1 .. -1]
|
71
|
+
when 'exit', 'quit'
|
72
|
+
return true
|
73
|
+
|
74
|
+
when 'storage'
|
75
|
+
STDOUT.puts interpreter.storage.inspect
|
76
|
+
|
77
|
+
when 'position'
|
78
|
+
STDOUT.puts interpreter.storage.position.to_s.bold
|
79
|
+
|
80
|
+
when /^get(?:\s*(\d+))?$/
|
81
|
+
STDOUT.puts "#{($1 || interpreter.storage.position).to_s.bold}: #{interpreter.storage.get($1.to_i)}"
|
82
|
+
|
83
|
+
when /^set(?:\s+(\d+)\s+(\d+))/
|
84
|
+
interpreter.storage.set($2.to_i, $1.to_i)
|
85
|
+
|
86
|
+
else
|
87
|
+
STDOUT.puts 'Command not found'.red
|
88
|
+
end
|
89
|
+
else
|
90
|
+
output = interpreter.evaluate(line, interpreter.options.merge(:catch => true))
|
91
|
+
|
92
|
+
print output
|
93
|
+
print "\n" unless output.empty? || output.end_with?("\n")
|
94
|
+
end
|
95
|
+
rescue
|
96
|
+
STDOUT.puts $!.inspect.red, $@.join("\n")
|
97
|
+
end
|
98
|
+
end
|
38
99
|
end
|
39
100
|
end
|
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.4
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- meh.
|
@@ -23,6 +23,17 @@ dependencies:
|
|
23
23
|
version: "0"
|
24
24
|
type: :runtime
|
25
25
|
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: colorb
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id002
|
26
37
|
description:
|
27
38
|
email: meh@paranoici.org
|
28
39
|
executables:
|
@@ -32,10 +43,10 @@ extensions: []
|
|
32
43
|
extra_rdoc_files: []
|
33
44
|
|
34
45
|
files:
|
35
|
-
- lib/rbf/transform.rb
|
36
46
|
- lib/rbf/interpreter.rb
|
37
47
|
- lib/rbf/syntax.rb
|
38
48
|
- lib/rbf/parser.rb
|
49
|
+
- lib/rbf/optimizer.rb
|
39
50
|
- lib/rbf.rb
|
40
51
|
- bin/rbf
|
41
52
|
homepage: http://github.com/meh/rbf
|
data/lib/rbf/transform.rb
DELETED
@@ -1,30 +0,0 @@
|
|
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 'parslet'
|
14
|
-
|
15
|
-
module RBF
|
16
|
-
|
17
|
-
class Transform < Parslet::Transform
|
18
|
-
rule(?> => simple(:x)) { ?> }
|
19
|
-
rule(?< => simple(:x)) { ?< }
|
20
|
-
|
21
|
-
rule(?+ => simple(:x)) { ?+ }
|
22
|
-
rule(?- => simple(:x)) { ?- }
|
23
|
-
|
24
|
-
rule(?. => simple(:x)) { ?. }
|
25
|
-
rule(?, => simple(:x)) { ?, }
|
26
|
-
|
27
|
-
rule(:loop => subtree(:x)) { x }
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|