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.
@@ -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
@@ -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
+
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,6 @@
1
+ class Stack < Array
2
+ def pop
3
+ return 0 if empty?
4
+ super
5
+ end
6
+ end
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: []