braingasm 0.2.1 → 0.3.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: f89bf346bd58ba90e981f96d4382b4483b142e26
4
- data.tar.gz: 11aa5d5055cd19509f50a0e8d7187dac5adc3d75
3
+ metadata.gz: dd80d14547d49b0c815eed05dccd712772b3bb58
4
+ data.tar.gz: 1664bf7fc54b68e9e830d497eccae10522663fce
5
5
  SHA512:
6
- metadata.gz: 9fc5b538410197b6fb03d05227ee7224860de4b2b6d235c61b1d5006765f48c81c13c4b4c6250376fe945ae4c38b9fc63038010f7952fa1c9ca9c654591ead49
7
- data.tar.gz: 6232090d60b2a027bceffdb4547025f214653180d4a4e7f6cecaa76b79a962f6b447fc022f5112de4865d97441db66c22c4894d4479b90c218c2abd6a89c8a9a
6
+ metadata.gz: d74db6070885068e3c7c844189e2fd72ba309018357495851cac96f53729350259e93b3017acf84cfd0020ff4c32aabb0aca543d6eb6aa90282e154f7aedcc93
7
+ data.tar.gz: 0228237b8fa8682463eb3ad40f83c9d31b544a1eb71da0b07f6ac7d88359df2d0ddfea83c9acc8c06b0d02482849cc637a48bdbb10d393418ed94cfdd675635f
data/braingasm.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.add_runtime_dependency "trollop", "~> 2.1.2"
23
23
 
24
- spec.add_development_dependency "bundler", "~> 1.12.5"
24
+ spec.add_development_dependency "bundler", "~> 1.13"
25
25
  spec.add_development_dependency "rake", "~> 11.2.2"
26
26
  spec.add_development_dependency "rspec", "~> 3.5"
27
27
  end
@@ -0,0 +1,3 @@
1
+ 10,
2
+ [5> #+5+ 2<#[1[<]] 2-] 5[>] 15+
3
+ 26[.+] 10.
data/exe/braingasm CHANGED
@@ -19,7 +19,7 @@ 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 aritrarily large cells"
22
+ opt :unbound, "Allow arbitrarily large cells"
23
23
  opt :cell_size, "Cell size in bits",
24
24
  :short => '-s',
25
25
  :default => 8
@@ -33,9 +33,8 @@ err "Error: No filname given" unless filename
33
33
  err "Error: No such file: #{filename}" unless File.exists?(filename)
34
34
 
35
35
  begin
36
- input = IO.read(filename)
37
- machine = Braingasm.initialize_machine(input)
38
- machine.run
36
+ code = IO.read(filename)
37
+ Braingasm.run(code)
39
38
  rescue Braingasm::BraingasmError => e
40
39
  err "#{e.type}: #{e.message}"
41
40
  end
data/lib/braingasm.rb CHANGED
@@ -1,12 +1,25 @@
1
1
  require "braingasm/tokenizer"
2
2
  require "braingasm/parser"
3
+ require "braingasm/compiler"
3
4
  require "braingasm/machine"
5
+ require "braingasm/io"
4
6
 
5
7
  module Braingasm
8
+
9
+ def self.run(code)
10
+ machine = self.initialize_machine(code)
11
+ machine.run()
12
+ end
13
+
6
14
  def self.initialize_machine(code)
7
- machine = Machine.new
8
15
  tokenizer = Tokenizer.new(code)
9
- machine.program = Parser.new(tokenizer).parse_program
16
+ compiler = Compiler.new
17
+ program = Parser.new(tokenizer, compiler).parse_program
18
+ machine = Machine.new
19
+
20
+ machine.input = InputBuffer.new($<)
21
+ machine.output = $>
22
+ machine.program = program
10
23
  machine
11
24
  end
12
25
  end
@@ -0,0 +1,122 @@
1
+ require "braingasm/prefixes"
2
+
3
+ module Braingasm
4
+ class Compiler
5
+ attr_accessor :prefixes, :loop_stack
6
+
7
+ def initialize
8
+ @prefixes = PrefixStack.new
9
+ @loop_stack = []
10
+ end
11
+
12
+ def push_prefix(prefix)
13
+ @prefixes << prefix
14
+ prefix
15
+ end
16
+
17
+ def pos
18
+ push_prefix ->(m) { m.pos }
19
+ end
20
+
21
+ def random
22
+ random = proc { |n, _| rand n }
23
+ return_max_value = proc { |_, _| Options[:cell_limit] }
24
+ push_prefix @prefixes.fix_params(random, return_max_value)
25
+ end
26
+
27
+ def zero
28
+ push_prefix ->(m) { m.last_write == 0 ? 1 : 0 }
29
+ end
30
+
31
+ def parity
32
+ push_prefix ->(m) { (m.last_write &.% 2) || 0 }
33
+ end
34
+
35
+ def right()
36
+ @prefixes.fix_params ->(n, m) { m.inst_right(n) }
37
+ end
38
+
39
+ def left()
40
+ @prefixes.fix_params ->(n, m) { m.inst_left(n) }
41
+ end
42
+
43
+ def inc()
44
+ @prefixes.fix_params ->(n, m) { m.inst_inc(n) }
45
+ end
46
+
47
+ def dec()
48
+ @prefixes.fix_params ->(n, m) { m.inst_dec(n) }
49
+ end
50
+
51
+ def print()
52
+ if @prefixes.empty?
53
+ ->(m) { m.inst_print_cell }
54
+ else
55
+ @prefixes.fix_params ->(n, m) { m.inst_print(n) }
56
+ end
57
+ end
58
+
59
+ def print_int()
60
+ if @prefixes.empty?
61
+ ->(m) { m.inst_print_cell_int }
62
+ else
63
+ @prefixes.fix_params ->(n, m) { m.inst_print_int(n) }
64
+ end
65
+ end
66
+
67
+ def read()
68
+ if @prefixes.empty?
69
+ ->(m) { m.inst_read_byte }
70
+ else
71
+ @prefixes.fix_params ->(n, m) { m.cell = n }
72
+ end
73
+ end
74
+
75
+ def read_int()
76
+ @prefixes.fix_params ->(n, m) { m.inst_read_int(n) }, 10
77
+ end
78
+
79
+ def jump(to)
80
+ ->(m) { m.inst_jump(to) }
81
+ end
82
+
83
+ def loop_start(start_index)
84
+ return prefixed_loop(start_index) unless @prefixes.empty?
85
+
86
+ new_loop = Loop.new
87
+ @loop_stack.push(new_loop)
88
+ new_loop.start_index = start_index
89
+ new_loop
90
+ end
91
+
92
+ def prefixed_loop(start_index)
93
+ new_loop = FixedLoop.new
94
+ @loop_stack.push(new_loop)
95
+ new_loop.start_index = start_index + 1
96
+ push_ctrl = @prefixes.fix_params ->(n, m) { m.inst_push_ctrl(n) }
97
+ [push_ctrl, new_loop]
98
+ end
99
+
100
+ def loop_end(index)
101
+ current_loop = @loop_stack.pop
102
+ raise BraingasmError, "Unmatched `]`" unless current_loop
103
+ current_loop.stop_index = index
104
+ jump(current_loop.start_index)
105
+ end
106
+
107
+ class Loop
108
+ attr_accessor :start_index, :stop_index
109
+
110
+ def call(machine)
111
+ machine.inst_jump_if_data_zero(stop_index + 1)
112
+ end
113
+ end
114
+
115
+ class FixedLoop < Loop
116
+ def call(machine)
117
+ machine.inst_jump_if_ctrl_zero(stop_index + 1)
118
+ end
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,41 @@
1
+ module Braingasm
2
+ class InputBuffer
3
+
4
+ def initialize(source)
5
+ @source = source
6
+ @buffer = StringIO.new
7
+ end
8
+
9
+ def ungetc(s)
10
+ case s
11
+ when String
12
+ @buffer.ungetc(s) if s.chomp.size > 0
13
+ when Integer
14
+ @buffer.ungetc(s) unless s == 10
15
+ end
16
+ end
17
+
18
+ def getbyte
19
+ @buffer.getbyte unless eof?
20
+ end
21
+
22
+ def gets
23
+ @buffer.gets unless eof?
24
+ end
25
+
26
+ def eof?
27
+ return true if @buffer.closed?
28
+ return false unless @buffer.eof?
29
+
30
+ s = @source.gets
31
+ if s
32
+ @buffer.string = s
33
+ false
34
+ else
35
+ @buffer.close
36
+ true
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -6,20 +6,24 @@ 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
9
+ attr_accessor :tape, :dp, :program, :ip, :ctrl_stack, :last_write, :input, :output
10
10
 
11
11
  def initialize
12
12
  @tape = Array.new(10) { 0 }
13
13
  @dp = 0 # data pointer
14
+ @data_offset = 0
14
15
  @ip = 0 # instruction pointer
16
+
17
+ @ctrl_stack = []
18
+ @last_write = 0
15
19
  end
16
20
 
17
21
  def run
18
22
  return if @program.empty?
19
23
 
20
24
  loop do
21
- continue = step
22
- break unless continue && @ip < @program.size
25
+ step()
26
+ break unless @ip < @program.size
23
27
  end
24
28
  end
25
29
 
@@ -30,6 +34,19 @@ module Braingasm
30
34
  @ip = jump.to
31
35
  end
32
36
 
37
+ def cell
38
+ @tape[@dp]
39
+ end
40
+
41
+ def cell=(new_value)
42
+ @tape[@dp] = new_value
43
+ trigger_cell_updated
44
+ end
45
+
46
+ def pos
47
+ @dp - @data_offset
48
+ end
49
+
33
50
  def inst_right(n=1)
34
51
  new_dp = @dp + n
35
52
  no_cells = @tape.length
@@ -46,9 +63,10 @@ module Braingasm
46
63
  new_dp = @dp - n
47
64
 
48
65
  if new_dp < 0
49
- n.times do
50
- @tape.unshift 0
51
- end
66
+ new_cells = -new_dp
67
+ new_cells.times { @tape.unshift 0 }
68
+ @data_offset += new_cells
69
+
52
70
  new_dp = 0
53
71
  end
54
72
 
@@ -61,33 +79,65 @@ module Braingasm
61
79
 
62
80
  def inst_inc(n=1)
63
81
  @tape[@dp] += n
64
- wrap_cell
82
+ trigger_cell_updated
65
83
  end
66
84
 
67
85
  def inst_dec(n=1)
68
86
  @tape[@dp] -= n
69
- wrap_cell
87
+ trigger_cell_updated
70
88
  end
71
89
 
72
90
  def inst_jump(to)
73
91
  raise JumpSignal.new(to)
74
92
  end
75
93
 
76
- def inst_jump_if_zero(to)
77
- raise JumpSignal.new(to) if @tape[@dp] == 0
94
+ def inst_jump_if_data_zero(to)
95
+ raise JumpSignal.new(to) if cell == 0
96
+ end
97
+
98
+ def inst_jump_if_ctrl_zero(to)
99
+ ctrl = ctrl_stack.pop
100
+ raise JumpSignal.new(to) if ctrl == 0
101
+ ctrl_stack << ctrl - 1
102
+ end
103
+
104
+ def inst_push_ctrl(x)
105
+ ctrl_stack << x
106
+ end
107
+
108
+ def inst_print(chr)
109
+ @output.putc chr
78
110
  end
79
111
 
80
112
  def inst_print_cell
81
- putc @tape[@dp]
113
+ @output.putc cell
114
+ end
115
+
116
+ def inst_print_int(n)
117
+ @output.print n
118
+ end
119
+
120
+ def inst_print_cell_int
121
+ @output.print cell
82
122
  end
83
123
 
84
124
  def inst_read_byte
85
- @tape[@dp] = ARGF.getbyte || Options[:eof] || @tape[@dp]
125
+ @tape[@dp] = @input.getbyte || Options[:eof] || @tape[@dp]
126
+ trigger_cell_updated
127
+ end
128
+
129
+ def inst_read_int(radix=10)
130
+ return unless @input.gets =~ /\d+/
131
+
132
+ @input.ungetc($')
133
+ @tape[@dp] = $&.to_i(radix)
134
+ trigger_cell_updated
86
135
  end
87
136
 
88
137
  private
89
- def wrap_cell
138
+ def trigger_cell_updated
90
139
  @tape[@dp] %= Options[:cell_limit] if Options[:wrap_cells]
140
+ @last_write = @tape[@dp]
91
141
  end
92
142
  end
93
143
  end
@@ -18,6 +18,10 @@ module Braingasm
18
18
  @options[option] = value
19
19
  end
20
20
 
21
+ def self.reset
22
+ @options = @defaults.dup
23
+ end
24
+
21
25
  private
22
26
  def self.check_defaults(option)
23
27
  raise ArgumentError, "Unknown option '#{option}'" unless @defaults.has_key?(option)
@@ -1,15 +1,16 @@
1
1
  require "braingasm/errors"
2
+ require "braingasm/compiler"
2
3
 
3
4
  module Braingasm
4
5
 
5
6
  # Takes some input code and generates the program
6
7
  class Parser
7
- attr_accessor :program, :loop_stack
8
+ attr_accessor :input, :program
8
9
 
9
- def initialize(input)
10
+ def initialize(input, compiler)
10
11
  @input = input
12
+ @compiler = compiler
11
13
  @program = []
12
- @loop_stack = []
13
14
  end
14
15
 
15
16
  def parse_program
@@ -17,88 +18,60 @@ module Braingasm
17
18
  push_instruction parse_next(@input)
18
19
  end
19
20
 
20
- raise_parsing_error("Unmatched `[`") unless @loop_stack.empty?
21
+ raise_parsing_error("Unmatched `[`") unless @compiler.loop_stack.empty?
21
22
  @program
22
23
  end
23
24
 
24
25
  def parse_next(tokens)
25
- case tokens.next
26
+ token = tokens.next
27
+
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
26
44
  when :right
27
- right()
45
+ @compiler.right()
28
46
  when :left
29
- left()
47
+ @compiler.left()
30
48
  when :plus
31
- inc()
49
+ @compiler.inc()
32
50
  when :minus
33
- dec()
51
+ @compiler.dec()
34
52
  when :period
35
- print()
53
+ @compiler.print()
54
+ when :colon
55
+ @compiler.print_int()
36
56
  when :comma
37
- read()
57
+ @compiler.read()
58
+ when :semicolon
59
+ @compiler.read_int()
38
60
  when :loop_start
39
- loop_start()
61
+ @compiler.loop_start(@program.size)
40
62
  when :loop_end
41
- loop_end()
63
+ @compiler.loop_end(@program.size)
42
64
  end
65
+ rescue BraingasmError => e
66
+ raise_parsing_error(e.message)
43
67
  end
44
68
 
45
69
  def push_instruction(instruction)
46
70
  return unless instruction
47
- @program.push instruction
71
+ @program.push(*instruction)
48
72
  @program.size - 1
49
73
  end
50
74
 
51
- def right(n=1)
52
- -> m { m.inst_right(n) }
53
- end
54
-
55
- def left(n=1)
56
- -> m { m.inst_left(n) }
57
- end
58
-
59
- def inc(n=1)
60
- -> m { m.inst_inc(n) }
61
- end
62
-
63
- def dec(n=1)
64
- -> m { m.inst_dec(n) }
65
- end
66
-
67
- def print()
68
- -> m { m.inst_print_cell }
69
- end
70
-
71
- def read()
72
- -> m { m.inst_read_byte }
73
- end
74
-
75
- def jump(to)
76
- -> m { m.inst_jump(to) }
77
- end
78
-
79
- def loop_start()
80
- new_loop = Loop.new
81
- @loop_stack.push(new_loop)
82
- new_loop.start_index = @program.size
83
- new_loop
84
- end
85
-
86
- def loop_end
87
- current_loop = @loop_stack.pop
88
- raise_parsing_error("Unmatched `]`") unless current_loop
89
- index = @program.size
90
- current_loop.stop_index = index
91
- jump(current_loop.start_index)
92
- end
93
-
94
- class Loop
95
- attr_accessor :start_index, :stop_index
96
-
97
- def call(machine)
98
- machine.inst_jump_if_zero(stop_index + 1)
99
- end
100
- end
101
-
102
75
  def raise_parsing_error(message)
103
76
  raise ParsingError.new(@input.line_numer, @input.column_number), message
104
77
  end
@@ -0,0 +1,30 @@
1
+ require "forwardable"
2
+
3
+ module Braingasm
4
+
5
+ class PrefixStack
6
+ extend Forwardable
7
+ attr_accessor :stack
8
+ def_delegators :@stack, :empty?, :<<, :pop, :==
9
+
10
+ def initialize
11
+ @stack = []
12
+ end
13
+
14
+ def fix_params(function, default_param=1)
15
+ prefix = @stack.pop || default_param
16
+
17
+ case prefix
18
+ when Integer
19
+ function.curry.call(prefix)
20
+ when Proc
21
+ proc do |m|
22
+ n = prefix.call(m)
23
+ function.call(n, m)
24
+ end
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -35,17 +35,25 @@ module Braingasm
35
35
 
36
36
  private
37
37
  def read_token(scanner)
38
- tokens = { '+' => :plus,
39
- '-' => :minus,
40
- '<' => :left,
41
- '>' => :right,
42
- '.' => :period,
43
- ',' => :comma,
44
- '[' => :loop_start,
45
- ']' => :loop_end }
46
- tokens[scanner.scan(/\S/)] || :unknown
38
+ return scanner.matched.to_i if scanner.scan(/\d+/)
39
+ @@simple_tokens[scanner.scan(/\S/)] || :unknown
47
40
  end
48
41
 
42
+ @@simple_tokens = { '+' => :plus,
43
+ '-' => :minus,
44
+ '<' => :left,
45
+ '>' => :right,
46
+ '.' => :period,
47
+ ':' => :colon,
48
+ ',' => :comma,
49
+ ';' => :semicolon,
50
+ '#' => :hash,
51
+ 'r' => :r,
52
+ 'p' => :p,
53
+ 'z' => :z,
54
+ '[' => :loop_start,
55
+ ']' => :loop_end }
56
+
49
57
  end
50
58
  end
51
59
 
@@ -1,3 +1,3 @@
1
1
  module Braingasm
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.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.2.1
4
+ version: 0.3.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-08-29 00:00:00.000000000 Z
11
+ date: 2016-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: trollop
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.12.5
33
+ version: '1.13'
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.12.5
40
+ version: '1.13'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -85,12 +85,16 @@ files:
85
85
  - bin/setup
86
86
  - braingasm.gemspec
87
87
  - examples/io.b
88
+ - examples/number_prefixes.bg
88
89
  - exe/braingasm
89
90
  - lib/braingasm.rb
91
+ - lib/braingasm/compiler.rb
90
92
  - lib/braingasm/errors.rb
93
+ - lib/braingasm/io.rb
91
94
  - lib/braingasm/machine.rb
92
95
  - lib/braingasm/options.rb
93
96
  - lib/braingasm/parser.rb
97
+ - lib/braingasm/prefixes.rb
94
98
  - lib/braingasm/tokenizer.rb
95
99
  - lib/braingasm/version.rb
96
100
  homepage: https://github.com/daniero/braingasm