befunge 0.0.1
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.
- checksums.yaml +7 -0
- data/bin/befunge +25 -0
- data/lib/befunge_interpreter.rb +90 -0
- data/lib/code_map.rb +61 -0
- data/lib/operations.rb +147 -0
- data/lib/pointer.rb +26 -0
- data/lib/stack.rb +6 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 755f4a89eb72d0411019a7893e06ac36af17adbe
|
4
|
+
data.tar.gz: bcd23559bd918099356cbff55b3420074aae86c7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8e303c70c8d2c4a49043bf310fbb121ae1a85a61d199afbb8c694f53c1f4cc666da5a6bf0bf047863268d122e1ee835819a71fdc063f2e48b072e3c285c1a05b
|
7
|
+
data.tar.gz: ee1c81455fcadaa5bf06628fdc944d65d816b69c5fafd58666be07dc2258d3d2964ad5f98afa3657f4505874d195bdde130726df368557ec022c42dc14e46dd9
|
data/bin/befunge
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require './lib/operations.rb'
|
4
|
+
require './lib/code_map.rb'
|
5
|
+
require './lib/pointer.rb'
|
6
|
+
require './lib/stack.rb'
|
7
|
+
require './lib/befunge_interpreter.rb'
|
8
|
+
require 'colorize'
|
9
|
+
|
10
|
+
AVAILABLE_FLAGS = %w(-d -a)
|
11
|
+
|
12
|
+
filename = ARGV[0]
|
13
|
+
flags = ARGV[1..-1]
|
14
|
+
flags ||= []
|
15
|
+
flags.each { |flag| raise 'Fuck you' unless AVAILABLE_FLAGS.include?(flag) }
|
16
|
+
|
17
|
+
if flags.include?('-a')
|
18
|
+
klass = BefungeInterpreterAnimation
|
19
|
+
elsif flags.include?('-d')
|
20
|
+
klass = BefungeInterpreterDebugger
|
21
|
+
else
|
22
|
+
klass = BefungeInterpreter
|
23
|
+
end
|
24
|
+
|
25
|
+
klass.new(filename, flags).interpret
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# This is the top-level class containing everything needed to interpret
|
2
|
+
# befunge code. Intialize with a string location of a filename and an array
|
3
|
+
# of flags and then call BefungeInterpreter#interpret. This will run the
|
4
|
+
# befunge code.
|
5
|
+
# TODO: Implement flags - debugging mode, animation mode.
|
6
|
+
class BefungeInterpreter
|
7
|
+
include Operations
|
8
|
+
|
9
|
+
def initialize(file, flags)
|
10
|
+
@code_map = CodeMap.new(file)
|
11
|
+
@flags = flags
|
12
|
+
@stack = Stack.new
|
13
|
+
@string_mode = false
|
14
|
+
@computing = true
|
15
|
+
@return_string = ''
|
16
|
+
end
|
17
|
+
|
18
|
+
def interpret
|
19
|
+
operate_and_step while @computing
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def operate_and_step
|
25
|
+
operate!(@code_map.get_operation)
|
26
|
+
@code_map.pointer.step
|
27
|
+
end
|
28
|
+
|
29
|
+
def print_status
|
30
|
+
system 'clear'
|
31
|
+
@code_map.print_map
|
32
|
+
print_stack
|
33
|
+
print_return_string
|
34
|
+
end
|
35
|
+
|
36
|
+
def print_stack
|
37
|
+
print_whole_stack if @stack.length <= 10
|
38
|
+
print_top_ten_stack if @stack.length > 10
|
39
|
+
end
|
40
|
+
|
41
|
+
def print_whole_stack
|
42
|
+
p @stack
|
43
|
+
end
|
44
|
+
|
45
|
+
def print_top_ten_stack
|
46
|
+
puts "Showing top ten items on stack..."
|
47
|
+
@stack[-11..-1].each { |stack_item| print ", #{stack_item}" }
|
48
|
+
print "]\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
def print_return_string
|
52
|
+
p @return_string
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Use this class for debugger mode
|
57
|
+
class BefungeInterpreterDebugger < BefungeInterpreter
|
58
|
+
def interpret
|
59
|
+
print_status
|
60
|
+
while @computing
|
61
|
+
puts "\nDebugger Mode!~"
|
62
|
+
puts 'Commands: n = next([, step_amt]), p = put_operation(x, y, op)'
|
63
|
+
user_input = $stdin.gets.chomp
|
64
|
+
case user_input
|
65
|
+
when /\An\z/ then operate_and_step
|
66
|
+
when /\An \d+\z/ then operate_and_step(Integer(user_input[2..-1]))
|
67
|
+
when /\A(\d+)n/ then operate_and_step(Integer(user_input.match(/(\d+)/)[1]))
|
68
|
+
when /\Aquit\z/ then @computing = false
|
69
|
+
end
|
70
|
+
print_status
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def operate_and_step(n = 1)
|
75
|
+
n.times { super() }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Use this class for animation mode
|
80
|
+
class BefungeInterpreterAnimation < BefungeInterpreter
|
81
|
+
def interpret
|
82
|
+
print_status
|
83
|
+
while @computing
|
84
|
+
sleep 0.1
|
85
|
+
operate_and_step
|
86
|
+
print_status
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
data/lib/code_map.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# This class is reponsible for keeping the befunge code. It will output
|
2
|
+
# the current operation the pointer is at, and also set and get operations
|
3
|
+
# based on coordinates. CodeMap#print_map will be used for animation
|
4
|
+
# and debuggin modes.
|
5
|
+
class CodeMap
|
6
|
+
attr_reader :width, :height, :pointer, :map
|
7
|
+
def initialize(file)
|
8
|
+
@pointer = Pointer.new
|
9
|
+
@map = File.open(file, 'r').readlines.map { |line| line.chomp.split '' }
|
10
|
+
format_code_map
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_operation
|
14
|
+
@map[@pointer.y][@pointer.x]
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_operation_at(x, y, op)
|
18
|
+
@map[y][x] = op
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_operation_at(x, y)
|
22
|
+
@map[y][x]
|
23
|
+
end
|
24
|
+
|
25
|
+
def print_map
|
26
|
+
system 'clear'
|
27
|
+
@map.each_with_index do |line, i|
|
28
|
+
line.each_with_index do |char, j|
|
29
|
+
if @pointer.x == j && @pointer.y == i
|
30
|
+
print char.colorize(background: :red)
|
31
|
+
else
|
32
|
+
print char
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
print "\n"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def format_code_map
|
43
|
+
check_if_valid
|
44
|
+
until @map.length == 20
|
45
|
+
@map << []
|
46
|
+
80.times { @map.last << ' ' }
|
47
|
+
end
|
48
|
+
@map.each do |line|
|
49
|
+
until line.length == 80
|
50
|
+
line << ' '
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def check_if_valid
|
56
|
+
raise 'Invalid map' unless @map.length <= 80
|
57
|
+
@map.each do |line|
|
58
|
+
raise 'Invalid map' unless line.length <= 80
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/operations.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
# This module is responsible for performing operations. It keeps all logic
|
2
|
+
# based on opertaions encapsulated. It is meant to be included with the
|
3
|
+
# top-level BefungeInterpreter.
|
4
|
+
module Operations
|
5
|
+
# Bunch of Constants here.
|
6
|
+
# Important thing: %i() defines array of symbols while %w() defines array
|
7
|
+
# of strings. The Hash[Array#zip(Array)] pattern I've chosen to employ here
|
8
|
+
# simply maps the string/symbol single character representation of the
|
9
|
+
# operation to it's actual method name here. For instance:
|
10
|
+
# OPERATORS_MAP['!'] => :logical_not
|
11
|
+
# We can just #send that symbol in our #operate! method.
|
12
|
+
|
13
|
+
MATH_OPS = %i(+ - * / %)
|
14
|
+
DIRECTION_OPS = %i(^ < > v)
|
15
|
+
DIRECTIONS = %i(n w e s)
|
16
|
+
DIRECTIONS_MAP = Hash[DIRECTION_OPS.zip(DIRECTIONS)]
|
17
|
+
OPERATORS = %w(! ` ? _ | : \\ $ . , # p g & ~ @)
|
18
|
+
OPERATORS_MAP = Hash[OPERATORS.zip([
|
19
|
+
:logical_not, :greater_than, :rand_direction, :left_right,
|
20
|
+
:up_down, :duplicate_stack_top, :swap_stack_top,
|
21
|
+
:pop_and_discard, :pop_and_display_int, :pop_and_display_char,
|
22
|
+
:trampoline, :put_call, :get_call, :get_user_input_int,
|
23
|
+
:get_user_input_char, :end_program
|
24
|
+
])]
|
25
|
+
|
26
|
+
def operate!(op)
|
27
|
+
string_mode and return if op == '"'
|
28
|
+
push_ascii_value(op) and return if @string_mode
|
29
|
+
return if op == ' '
|
30
|
+
send(op) and return if MATH_OPS.include?(op.to_sym)
|
31
|
+
push_num(op) and return if Integer(op) rescue false
|
32
|
+
change_dir(op.to_sym) and return if DIRECTION_OPS.include?(op.to_sym)
|
33
|
+
send(OPERATORS_MAP[op])
|
34
|
+
end
|
35
|
+
|
36
|
+
# Meta. This defines the + - * / % methods in one loop since they're
|
37
|
+
# really all the same rpn calculation.
|
38
|
+
MATH_OPS.each do |math_op|
|
39
|
+
define_method(math_op) do
|
40
|
+
a = @stack.pop
|
41
|
+
b = @stack.pop
|
42
|
+
@stack.push(b.send(math_op, a))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def push_num(num)
|
47
|
+
@stack.push(num.to_i)
|
48
|
+
end
|
49
|
+
|
50
|
+
# I call this method two different ways - one with directional operational
|
51
|
+
# arguments < > ^ v and one with actual directions :n :e :s :w. This is
|
52
|
+
# the reason for the unless statement here.
|
53
|
+
def change_dir(op)
|
54
|
+
op = DIRECTIONS_MAP[op] unless DIRECTIONS.include?(op)
|
55
|
+
@code_map.pointer.direction = op
|
56
|
+
end
|
57
|
+
|
58
|
+
def logical_not
|
59
|
+
@stack.pop == 0 ? @stack.push(1) : @stack.push(0)
|
60
|
+
end
|
61
|
+
|
62
|
+
def greater_than
|
63
|
+
a = @stack.pop
|
64
|
+
b = @stack.pop
|
65
|
+
b > a ? @stack.push(1) : @stack.push(0)
|
66
|
+
end
|
67
|
+
|
68
|
+
def rand_direction
|
69
|
+
@code_map.pointer.direction = DIRECTIONS.sample
|
70
|
+
end
|
71
|
+
|
72
|
+
def left_right
|
73
|
+
@stack.pop == 0 ? change_dir(:e) : change_dir(:w)
|
74
|
+
end
|
75
|
+
|
76
|
+
def up_down
|
77
|
+
@stack.pop == 0 ? change_dir(:s) : change_dir(:n)
|
78
|
+
end
|
79
|
+
|
80
|
+
def string_mode
|
81
|
+
@string_mode = !@string_mode
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
def duplicate_stack_top
|
86
|
+
@stack.push(0) and return if @stack.empty?
|
87
|
+
@stack.push(@stack.last)
|
88
|
+
end
|
89
|
+
|
90
|
+
def swap_stack_top
|
91
|
+
return if @stack.length < 2
|
92
|
+
@stack[-1], @stack[-2] = @stack[-2], @stack[-1]
|
93
|
+
end
|
94
|
+
|
95
|
+
def pop_and_discard
|
96
|
+
@stack.pop
|
97
|
+
end
|
98
|
+
|
99
|
+
def pop_and_display_int
|
100
|
+
display_int = @stack.pop
|
101
|
+
@return_string << display_int.to_s
|
102
|
+
print display_int
|
103
|
+
end
|
104
|
+
|
105
|
+
def pop_and_display_char
|
106
|
+
display_char = @stack.pop.chr
|
107
|
+
@return_string << display_char
|
108
|
+
print display_char
|
109
|
+
end
|
110
|
+
|
111
|
+
def trampoline
|
112
|
+
@code_map.pointer.trampoline = true
|
113
|
+
end
|
114
|
+
|
115
|
+
def put_call
|
116
|
+
y = @stack.pop
|
117
|
+
x = @stack.pop
|
118
|
+
v = @stack.pop
|
119
|
+
@code_map.set_operation(y, x, v.chr)
|
120
|
+
end
|
121
|
+
|
122
|
+
def get_call
|
123
|
+
y = @stack.pop
|
124
|
+
x = @stack.pop
|
125
|
+
@stack.push(@code_map.get_operation_at(y, x).ord)
|
126
|
+
end
|
127
|
+
|
128
|
+
def get_user_input_int
|
129
|
+
puts 'Befunge needs your input for int!'
|
130
|
+
@stack.push(Integer($stdin.gets.chomp))
|
131
|
+
end
|
132
|
+
|
133
|
+
def get_user_input_char
|
134
|
+
puts 'Befunge needs your input for char!'
|
135
|
+
input = $stdin.gets.chomp
|
136
|
+
raise 'Must be a single character' unless input.length == 1
|
137
|
+
@stack.push(input.ord)
|
138
|
+
end
|
139
|
+
|
140
|
+
def end_program
|
141
|
+
@computing = false
|
142
|
+
end
|
143
|
+
|
144
|
+
def push_ascii_value(op)
|
145
|
+
@stack.push(op.ord)
|
146
|
+
end
|
147
|
+
end
|
data/lib/pointer.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# This class is responsible for keeping track of it's location on the
|
2
|
+
# code map and moving in a direction every iteration.
|
3
|
+
class Pointer
|
4
|
+
attr_accessor :direction, :trampoline, :x, :y
|
5
|
+
DIRECTIONS = [:w, :e, :n, :s]
|
6
|
+
DIRECTIONS_MAP = Hash[DIRECTIONS.zip(
|
7
|
+
[
|
8
|
+
[-1, 0], [1, 0], [0, -1], [0, 1]
|
9
|
+
])]
|
10
|
+
|
11
|
+
def initialize(direction = :e)
|
12
|
+
@direction = direction
|
13
|
+
@x, @y = 0, 0
|
14
|
+
@trampoline = false
|
15
|
+
end
|
16
|
+
|
17
|
+
def step
|
18
|
+
diff = DIRECTIONS_MAP[@direction]
|
19
|
+
multiplier = @trampoline ? 2 : 1
|
20
|
+
self.x = x + diff.first * multiplier
|
21
|
+
self.y = y + diff.last * multiplier
|
22
|
+
self.x = x % 80
|
23
|
+
self.y = y % 20
|
24
|
+
@trampoline = false
|
25
|
+
end
|
26
|
+
end
|
data/lib/stack.rb
ADDED
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: befunge
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Obnoxious_Slime
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: colorize
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Basic Befunge Interpreter
|
28
|
+
email: Obnoxious_slime@mail.com
|
29
|
+
executables:
|
30
|
+
- befunge
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- bin/befunge
|
35
|
+
- lib/befunge_interpreter.rb
|
36
|
+
- lib/code_map.rb
|
37
|
+
- lib/operations.rb
|
38
|
+
- lib/pointer.rb
|
39
|
+
- lib/stack.rb
|
40
|
+
homepage:
|
41
|
+
licenses:
|
42
|
+
- WTFPL
|
43
|
+
metadata: {}
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 2.2.2
|
61
|
+
signing_key:
|
62
|
+
specification_version: 4
|
63
|
+
summary: Befunge
|
64
|
+
test_files: []
|