braingasm 0.3.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dd80d14547d49b0c815eed05dccd712772b3bb58
4
- data.tar.gz: 1664bf7fc54b68e9e830d497eccae10522663fce
3
+ metadata.gz: 41e3533b688fd89095305a59c011c395b74fd8df
4
+ data.tar.gz: 17354beb196e6fb94998c021f9f52532c53723ec
5
5
  SHA512:
6
- metadata.gz: d74db6070885068e3c7c844189e2fd72ba309018357495851cac96f53729350259e93b3017acf84cfd0020ff4c32aabb0aca543d6eb6aa90282e154f7aedcc93
7
- data.tar.gz: 0228237b8fa8682463eb3ad40f83c9d31b544a1eb71da0b07f6ac7d88359df2d0ddfea83c9acc8c06b0d02482849cc637a48bdbb10d393418ed94cfdd675635f
6
+ metadata.gz: 6cea12d603342077e4d1290602fc808845b03f073109e26189c18e62a309629f63876e4215220acaf9c689dd335ceb804f21f1b71c3e5deb0db29013edaf52c6
7
+ data.tar.gz: f77a348b9d2c49349053aff9fa1e22092b95cc02f0b8b9532e5eb92f65dcfd20749f49a0451e44ba0d4ac1d7dd5f62e836d92fd7042d4b797b54d6fb4123723e
data/braingasm.gemspec CHANGED
@@ -14,6 +14,8 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = "https://github.com/daniero/braingasm"
15
15
  spec.license = "MIT"
16
16
 
17
+ spec.required_ruby_version = '>= 2.3'
18
+
17
19
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
20
  spec.bindir = "exe"
19
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -21,7 +23,7 @@ Gem::Specification.new do |spec|
21
23
 
22
24
  spec.add_runtime_dependency "trollop", "~> 2.1.2"
23
25
 
24
- spec.add_development_dependency "bundler", "~> 1.13"
25
- spec.add_development_dependency "rake", "~> 11.2.2"
26
- spec.add_development_dependency "rspec", "~> 3.5"
26
+ spec.add_development_dependency "bundler", "~> 1.14"
27
+ spec.add_development_dependency "rake", "~> 12"
28
+ spec.add_development_dependency "rspec", "~> 3.6"
27
29
  end
data/examples/quine.bg ADDED
@@ -0,0 +1 @@
1
+ 2898478,:.
data/exe/braingasm CHANGED
@@ -19,11 +19,12 @@ opts = Trollop::options do
19
19
  opt :as_is, "EOF leaves cell unchanged"
20
20
  conflicts :zero, :negative, :as_is
21
21
 
22
- opt :unbound, "Allow arbitrarily large cells"
22
+ opt :unbound, "Allow arbitrarily large cells (on by default)"
23
+ opt :bound, "Don't Allow arbitrarily large cells"
23
24
  opt :cell_size, "Cell size in bits",
24
25
  :short => '-s',
25
26
  :default => 8
26
- conflicts :unbound, :cell_size
27
+ conflicts :unbound, :bound, :cell_size
27
28
  end
28
29
 
29
30
  Braingasm.handle_options(opts)
@@ -35,6 +36,8 @@ err "Error: No such file: #{filename}" unless File.exists?(filename)
35
36
  begin
36
37
  code = IO.read(filename)
37
38
  Braingasm.run(code)
39
+ rescue Braingasm::ExitSignal => e
40
+ exit e.code
38
41
  rescue Braingasm::BraingasmError => e
39
42
  err "#{e.type}: #{e.message}"
40
43
  end
data/lib/braingasm.rb CHANGED
@@ -12,14 +12,19 @@ module Braingasm
12
12
  end
13
13
 
14
14
  def self.initialize_machine(code)
15
- tokenizer = Tokenizer.new(code)
16
- compiler = Compiler.new
17
- program = Parser.new(tokenizer, compiler).parse_program
18
15
  machine = Machine.new
19
16
 
17
+ machine.program = compile(code)
20
18
  machine.input = InputBuffer.new($<)
21
19
  machine.output = $>
22
- machine.program = program
23
20
  machine
24
21
  end
22
+
23
+ def self.compile(code)
24
+ tokenizer = Tokenizer.new(code)
25
+ compiler = Compiler.new
26
+ parser = Parser.new(tokenizer, compiler)
27
+
28
+ parser.parse_program
29
+ end
25
30
  end
@@ -14,6 +14,11 @@ module Braingasm
14
14
  prefix
15
15
  end
16
16
 
17
+ READ_CELL = ->(n, m) { m.cell }
18
+ def read_cell
19
+ push_prefix @prefixes.fix_params(READ_CELL)
20
+ end
21
+
17
22
  def pos
18
23
  push_prefix ->(m) { m.pos }
19
24
  end
@@ -25,30 +30,52 @@ module Braingasm
25
30
  end
26
31
 
27
32
  def zero
28
- push_prefix ->(m) { m.last_write == 0 ? 1 : 0 }
33
+ read_cell if @prefixes.empty?
34
+
35
+ push_prefix @prefixes.fix_params(->(n, m) { n.zero? ? 1 : 0 })
36
+ end
37
+
38
+ def signed
39
+ push_prefix ->(m) { m.last_write >= 0 ? 0 : 1 }
29
40
  end
30
41
 
31
42
  def parity
32
- push_prefix ->(m) { (m.last_write &.% 2) || 0 }
43
+ read_cell if @prefixes.empty?
44
+
45
+ push_prefix @prefixes.fix_params(->(n, m) { (n % 2) ^ 1 })
33
46
  end
34
47
 
35
- def right()
48
+ def oddity
49
+ read_cell if @prefixes.empty?
50
+
51
+ push_prefix @prefixes.fix_params(->(n, m) { n % 2 })
52
+ end
53
+
54
+ def right
36
55
  @prefixes.fix_params ->(n, m) { m.inst_right(n) }
37
56
  end
38
57
 
39
- def left()
58
+ def left
40
59
  @prefixes.fix_params ->(n, m) { m.inst_left(n) }
41
60
  end
42
61
 
43
- def inc()
62
+ def inc
44
63
  @prefixes.fix_params ->(n, m) { m.inst_inc(n) }
45
64
  end
46
65
 
47
- def dec()
66
+ def dec
48
67
  @prefixes.fix_params ->(n, m) { m.inst_dec(n) }
49
68
  end
50
69
 
51
- def print()
70
+ def multiply
71
+ @prefixes.fix_params ->(n, m) { m.inst_multiply(n) }, 2
72
+ end
73
+
74
+ def divide
75
+ @prefixes.fix_params ->(n, m) { m.inst_divide(n) }, 2
76
+ end
77
+
78
+ def print
52
79
  if @prefixes.empty?
53
80
  ->(m) { m.inst_print_cell }
54
81
  else
@@ -56,7 +83,7 @@ module Braingasm
56
83
  end
57
84
  end
58
85
 
59
- def print_int()
86
+ def print_int
60
87
  if @prefixes.empty?
61
88
  ->(m) { m.inst_print_cell_int }
62
89
  else
@@ -64,18 +91,50 @@ module Braingasm
64
91
  end
65
92
  end
66
93
 
67
- def read()
94
+ def read
68
95
  if @prefixes.empty?
69
96
  ->(m) { m.inst_read_byte }
97
+ elsif @prefixes.first.is_a? String
98
+ string = @prefixes.first
99
+
100
+ @prefixes.fix_params ->(n, m) {
101
+ from, to = m.dp, m.dp + string.size
102
+
103
+ if m.tape_limit && to > m.tape_limit
104
+ limit = m.tape_limit
105
+ cutoff = to - limit
106
+
107
+ m.tape[from..limit] = string.bytes[0..cutoff]
108
+ m.tape[0...cutoff] = string.bytes[(cutoff+1)..-1]
109
+ else
110
+ m.tape[from...to] = string.bytes
111
+ end
112
+ }
70
113
  else
71
114
  @prefixes.fix_params ->(n, m) { m.cell = n }
72
115
  end
73
116
  end
74
117
 
75
- def read_int()
118
+ def read_int
76
119
  @prefixes.fix_params ->(n, m) { m.inst_read_int(n) }, 10
77
120
  end
78
121
 
122
+ def compare
123
+ ->(m) { m.inst_compare_cells }
124
+ end
125
+
126
+ def quit
127
+ @prefixes.fix_params ->(n, m) { m.inst_quit(n) }, 1
128
+ end
129
+
130
+ def tape_limit
131
+ if @prefixes.empty?
132
+ push_prefix ->(m) { x = m.pos; x + (x < 0 ? -1 : 1) }
133
+ end
134
+
135
+ @prefixes.fix_params ->(n, m) { m.limit_tape(n) }
136
+ end
137
+
79
138
  def jump(to)
80
139
  ->(m) { m.inst_jump(to) }
81
140
  end
@@ -26,4 +26,11 @@ module Braingasm
26
26
  @to = to
27
27
  end
28
28
  end
29
+
30
+ class ExitSignal < VMError
31
+ attr_reader :code
32
+ def initialize(code=0)
33
+ @code = code
34
+ end
35
+ end
29
36
  end
@@ -6,7 +6,8 @@ module Braingasm
6
6
  # A Machine keeps the state of a running program, and exposes various
7
7
  # operations to modify this state
8
8
  class Machine
9
- attr_accessor :tape, :dp, :program, :ip, :ctrl_stack, :last_write, :input, :output
9
+ attr_accessor :tape, :dp, :program, :ip, :ctrl_stack, :last_write, :input, :output,
10
+ :tape_limit
10
11
 
11
12
  def initialize
12
13
  @tape = Array.new(10) { 0 }
@@ -19,12 +20,7 @@ module Braingasm
19
20
  end
20
21
 
21
22
  def run
22
- return if @program.empty?
23
-
24
- loop do
25
- step()
26
- break unless @ip < @program.size
27
- end
23
+ step while @ip < @program.size
28
24
  end
29
25
 
30
26
  def step
@@ -47,8 +43,20 @@ module Braingasm
47
43
  @dp - @data_offset
48
44
  end
49
45
 
46
+ def calculate_new_dp(move)
47
+ if @tape_limit
48
+ if @tape_limit >= 0
49
+ (@dp + move) % @tape_limit
50
+ else
51
+ (@dp + move) % -@tape_limit
52
+ end
53
+ else
54
+ @dp + move
55
+ end
56
+ end
57
+
50
58
  def inst_right(n=1)
51
- new_dp = @dp + n
59
+ new_dp = calculate_new_dp(n)
52
60
  no_cells = @tape.length
53
61
 
54
62
  if new_dp >= no_cells
@@ -60,17 +68,17 @@ module Braingasm
60
68
  end
61
69
 
62
70
  def inst_left(n=1)
63
- new_dp = @dp - n
71
+ new_dp = calculate_new_dp(-n)
64
72
 
65
73
  if new_dp < 0
66
74
  new_cells = -new_dp
67
75
  new_cells.times { @tape.unshift 0 }
68
76
  @data_offset += new_cells
69
77
 
70
- new_dp = 0
78
+ @dp = 0
79
+ else
80
+ @dp = new_dp
71
81
  end
72
-
73
- @dp = new_dp
74
82
  end
75
83
 
76
84
  def inst_print_tape
@@ -78,13 +86,19 @@ module Braingasm
78
86
  end
79
87
 
80
88
  def inst_inc(n=1)
81
- @tape[@dp] += n
82
- trigger_cell_updated
89
+ self.cell += n
83
90
  end
84
91
 
85
92
  def inst_dec(n=1)
86
- @tape[@dp] -= n
87
- trigger_cell_updated
93
+ self.cell -= n
94
+ end
95
+
96
+ def inst_multiply(n=2)
97
+ self.cell *= n
98
+ end
99
+
100
+ def inst_divide(n=2)
101
+ self.cell /= n
88
102
  end
89
103
 
90
104
  def inst_jump(to)
@@ -105,12 +119,27 @@ module Braingasm
105
119
  ctrl_stack << x
106
120
  end
107
121
 
122
+ def print_bytes(b)
123
+ d,m = b.divmod(256)
124
+
125
+ if d > 0
126
+ print_bytes(d)
127
+ end
128
+
129
+ @output.putc(m)
130
+ end
131
+
108
132
  def inst_print(chr)
109
- @output.putc chr
133
+ case chr
134
+ when Integer
135
+ print_bytes(chr)
136
+ else
137
+ @output.print chr
138
+ end
110
139
  end
111
140
 
112
141
  def inst_print_cell
113
- @output.putc cell
142
+ print_bytes(cell)
114
143
  end
115
144
 
116
145
  def inst_print_int(n)
@@ -122,18 +151,30 @@ module Braingasm
122
151
  end
123
152
 
124
153
  def inst_read_byte
125
- @tape[@dp] = @input.getbyte || Options[:eof] || @tape[@dp]
126
- trigger_cell_updated
154
+ self.cell = @input.getbyte || Options[:eof] || @tape[@dp]
127
155
  end
128
156
 
129
157
  def inst_read_int(radix=10)
130
158
  return unless @input.gets =~ /\d+/
131
159
 
132
160
  @input.ungetc($')
133
- @tape[@dp] = $&.to_i(radix)
134
- trigger_cell_updated
161
+ self.cell = $&.to_i(radix)
135
162
  end
136
163
 
164
+ def inst_compare_cells
165
+ operand = @dp == 0 ? 0 : @tape[@dp-1]
166
+ @last_write = @tape[@dp] - operand
167
+ end
168
+
169
+ def inst_quit(value, code=0)
170
+ raise ExitSignal.new(code) unless value == 0
171
+ end
172
+
173
+ def limit_tape(cell_number)
174
+ @tape_limit = cell_number
175
+ end
176
+
177
+
137
178
  private
138
179
  def trigger_cell_updated
139
180
  @tape[@dp] %= Options[:cell_limit] if Options[:wrap_cells]
@@ -3,7 +3,7 @@ module Braingasm
3
3
  @options = {}
4
4
  @defaults = {
5
5
  eof: 0,
6
- wrap_cells: true,
6
+ wrap_cells: false,
7
7
  cell_limit: 256
8
8
  }.freeze
9
9
 
@@ -33,11 +33,12 @@ module Braingasm
33
33
  Options[:eof] = -1 if command_line_options[:negative]
34
34
  Options[:eof] = nil if command_line_options[:as_is]
35
35
 
36
- Options[:wrap_cells] = false if command_line_options[:unbound]
36
+ Options[:wrap_cells] = true if command_line_options[:bound]
37
37
 
38
38
  if command_line_options[:cell_size_given]
39
39
  cell_size = command_line_options[:cell_size]
40
40
  Options[:cell_limit] = 2**cell_size
41
+ Options[:wrap_cells] = true
41
42
  end
42
43
  end
43
44
  end
@@ -26,41 +26,54 @@ module Braingasm
26
26
  token = tokens.next
27
27
 
28
28
  case token
29
- when Integer
30
- @compiler.push_prefix token
31
- false
32
- when :hash
33
- @compiler.pos()
34
- false
35
- when :r
36
- @compiler.random()
37
- false
38
- when :z
39
- @compiler.zero()
40
- false
41
- when :p
42
- @compiler.parity()
43
- false
44
29
  when :right
45
- @compiler.right()
30
+ @compiler.right
46
31
  when :left
47
- @compiler.left()
48
- when :plus
49
- @compiler.inc()
50
- when :minus
51
- @compiler.dec()
52
- when :period
53
- @compiler.print()
54
- when :colon
55
- @compiler.print_int()
56
- when :comma
57
- @compiler.read()
58
- when :semicolon
59
- @compiler.read_int()
32
+ @compiler.left
33
+ when :increment
34
+ @compiler.inc
35
+ when :decrement
36
+ @compiler.dec
37
+ when :multiply
38
+ @compiler.multiply
39
+ when :divide
40
+ @compiler.divide
41
+ when :print
42
+ @compiler.print
43
+ when :output
44
+ @compiler.print_int
45
+ when :read
46
+ @compiler.read
47
+ when :input
48
+ @compiler.read_int
49
+ when :compare
50
+ @compiler.compare
51
+ when :quit
52
+ @compiler.quit
53
+ when :tape_limit
54
+ @compiler.tape_limit
60
55
  when :loop_start
61
56
  @compiler.loop_start(@program.size)
62
57
  when :loop_end
63
58
  @compiler.loop_end(@program.size)
59
+ else
60
+ case token
61
+ when Integer, String
62
+ @compiler.push_prefix token
63
+ when :position
64
+ @compiler.pos
65
+ when :random
66
+ @compiler.random
67
+ when :zero
68
+ @compiler.zero
69
+ when :signed
70
+ @compiler.signed
71
+ when :parity
72
+ @compiler.parity
73
+ when :oddity
74
+ @compiler.oddity
75
+ end
76
+ false
64
77
  end
65
78
  rescue BraingasmError => e
66
79
  raise_parsing_error(e.message)
@@ -5,7 +5,7 @@ module Braingasm
5
5
  class PrefixStack
6
6
  extend Forwardable
7
7
  attr_accessor :stack
8
- def_delegators :@stack, :empty?, :<<, :pop, :==
8
+ def_delegators :@stack, :empty?, :<<, :pop, :==, :first
9
9
 
10
10
  def initialize
11
11
  @stack = []
@@ -15,7 +15,7 @@ module Braingasm
15
15
  prefix = @stack.pop || default_param
16
16
 
17
17
  case prefix
18
- when Integer
18
+ when Integer, String
19
19
  function.curry.call(prefix)
20
20
  when Proc
21
21
  proc do |m|
@@ -35,22 +35,36 @@ module Braingasm
35
35
 
36
36
  private
37
37
  def read_token(scanner)
38
- return scanner.matched.to_i if scanner.scan(/\d+/)
39
- @@simple_tokens[scanner.scan(/\S/)] || :unknown
38
+ if scanner.scan(/\d+/)
39
+ scanner.matched.to_i
40
+ elsif scanner.scan(/"/)
41
+ s = scanner.scan(/[^"]*/)
42
+ scanner.skip(/"/)
43
+ s
44
+ else
45
+ @@simple_tokens[scanner.scan(/\S/)] || :unknown
46
+ end
40
47
  end
41
48
 
42
- @@simple_tokens = { '+' => :plus,
43
- '-' => :minus,
49
+ @@simple_tokens = { '+' => :increment,
50
+ '-' => :decrement,
51
+ '*' => :multiply,
52
+ '/' => :divide,
44
53
  '<' => :left,
45
54
  '>' => :right,
46
- '.' => :period,
47
- ':' => :colon,
48
- ',' => :comma,
49
- ';' => :semicolon,
50
- '#' => :hash,
51
- 'r' => :r,
52
- 'p' => :p,
53
- 'z' => :z,
55
+ '.' => :print,
56
+ ':' => :output,
57
+ ',' => :read,
58
+ ';' => :input,
59
+ 'C' => :compare,
60
+ '#' => :position,
61
+ 'r' => :random,
62
+ 'p' => :parity,
63
+ 'o' => :oddity,
64
+ 'z' => :zero,
65
+ 's' => :signed,
66
+ 'Q' => :quit,
67
+ 'L' => :tape_limit,
54
68
  '[' => :loop_start,
55
69
  ']' => :loop_end }
56
70
 
@@ -1,3 +1,3 @@
1
1
  module Braingasm
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: braingasm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Rødskog
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-09-24 00:00:00.000000000 Z
11
+ date: 2017-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: trollop
@@ -30,42 +30,42 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.13'
33
+ version: '1.14'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.13'
40
+ version: '1.14'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 11.2.2
47
+ version: '12'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 11.2.2
54
+ version: '12'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.5'
61
+ version: '3.6'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '3.5'
68
+ version: '3.6'
69
69
  description: braingasm combines the readability of brainfuck with the high-level functionality
70
70
  of assembly
71
71
  email: danielmero@gmail.com
@@ -86,6 +86,7 @@ files:
86
86
  - braingasm.gemspec
87
87
  - examples/io.b
88
88
  - examples/number_prefixes.bg
89
+ - examples/quine.bg
89
90
  - exe/braingasm
90
91
  - lib/braingasm.rb
91
92
  - lib/braingasm/compiler.rb
@@ -109,7 +110,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
109
110
  requirements:
110
111
  - - ">="
111
112
  - !ruby/object:Gem::Version
112
- version: '0'
113
+ version: '2.3'
113
114
  required_rubygems_version: !ruby/object:Gem::Requirement
114
115
  requirements:
115
116
  - - ">="
@@ -117,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
118
  version: '0'
118
119
  requirements: []
119
120
  rubyforge_project:
120
- rubygems_version: 2.5.1
121
+ rubygems_version: 2.6.12
121
122
  signing_key:
122
123
  specification_version: 4
123
124
  summary: It's liek brainfuck and assembly in one!