nil 1.0.9 → 2.0.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.
@@ -1,3 +1,16 @@
1
+ # 2.0.0
2
+
3
+ - Total rewrite of the interpreter
4
+ * No more code generation, it actually interprets the code
5
+ * No more interpreter at all. Compiled code is run directly (NilCommands
6
+ class)
7
+
8
+ - Rewrite of MacroParser
9
+ * No more separate methods, everything is done right in the parse method
10
+ * Also called MacroCompiler now
11
+
12
+ - Add updated MacroCompiler stuff to executable
13
+
1
14
  # 1.0.9
2
15
  - Bug fixes
3
16
 
data/Gemfile CHANGED
@@ -4,5 +4,6 @@ gem 'trollop'
4
4
 
5
5
  group :development do
6
6
  gem 'pry'
7
+ gem 'pry-doc'
7
8
  gem 'riot'
8
9
  end
data/README.md CHANGED
@@ -56,27 +56,7 @@ default mode is `ascii`.
56
56
 
57
57
  ## Interpreter
58
58
 
59
- nil is also a brain**** interpreter. It does the interpreting in its own special
60
- way, though:
61
-
62
- 1. It uses the MacroCompiler to compile the code
63
- 2. It translates the brain**** to Ruby code
64
- 3. It then runs the Ruby code it generated.
65
-
66
- The interpreter tries to be smart about the way it translates the code, for
67
- instance:
68
-
69
- `+++++` will be translated to:
70
-
71
- sp += 5
72
-
73
- instead of
74
-
75
- sp += 1
76
- sp += 1
77
- sp += 1
78
- sp += 1
79
- sp += 1
59
+ nil also interprets brain**** code.
80
60
 
81
61
  ## Installing
82
62
 
@@ -99,13 +79,10 @@ require 'nil'
99
79
 
100
80
  code = "+4."
101
81
 
102
- mp = Nil::MacroParser.new
103
- code = mp.parse code
104
-
105
- interpreter = Nil::Interpreter.new
106
- interpreter.compile code
82
+ mc = Nil::MacroCompiler.new
83
+ commands = mp.compile code
107
84
 
108
- interpreter.run
85
+ commands.run
109
86
  ```
110
87
 
111
88
  Run `nil -h` for command line tool usage.
data/bin/nil CHANGED
@@ -14,18 +14,15 @@ Usage:
14
14
  where [options] are:
15
15
  EOS
16
16
 
17
- opt :compile_only, "Output compiled Ruby code to standard output instead of running it."
17
+ opt :compile_only, "Only compile macros in the code, do not run."
18
18
  end
19
19
 
20
20
  code = ARGF.read
21
- mp = Nil::MacroParser.new
22
- code = mp.parse code
23
- i = Nil::Interpreter.new
24
- i.compile code, mp.mode
21
+ mp = Nil::MacroCompiler.new
22
+ c = mp.compile code
25
23
 
26
24
  if opts[:compile_only]
27
- puts i.compiled_ruby
28
- exit
25
+ puts c.code
26
+ else
27
+ c.run
29
28
  end
30
-
31
- i.run
data/lib/nil.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'pp'
2
2
  require 'nil/version'
3
- require 'nil/interpreter'
3
+ require 'nil/nil_commands'
4
4
  require 'nil/macro'
5
5
 
6
6
  module Nil
@@ -1,15 +1,40 @@
1
1
  module Nil
2
- class MacroParser
3
- attr_accessor :original_code, :compiled_code, :mode
4
-
2
+ class MacroCompiler
5
3
  def initialize
6
- @mode = 'ascii'
7
4
  end
8
5
 
9
6
  def compile_subroutines(code)
7
+ end
8
+
9
+ def compile(code)
10
+ # Strip whitespace
11
+ code.gsub!(/\s/, '')
12
+
13
+
14
+ # Interpret the mdoe definition in the code
15
+ mode = 'ascii'
16
+ code.match /\.mode\((ascii|num)\)/i do |m|
17
+ $1 == 'num' ? mode = 'num' : nil
18
+ end
19
+ code.gsub!(/\.mode\((ascii|num)\)/i, '')
20
+
21
+ # Expand incldued files
22
+ included_code = ""
23
+ files = code.scan(/\.include\(([^\)]+)\)/).flatten
24
+ files.each do |f|
25
+ included_code << File.open(f, 'r').read.strip
26
+ end
27
+ code.gsub!(/\.include\(([^\)]+)\)/, '')
28
+ code = included_code + code
29
+
30
+ # Strip comments
31
+ code.gsub!(/;(.+);/, '')
32
+
33
+ # Compile subroutines
10
34
  subroutines = {}
11
35
  sub_def_regex = Regexp.new ':(?<sub-name>[a-zA-Z0-9_]+)\{(?<sub-code>[\[\]><\-\+.,A-Za-z0-9:]+)\}'
12
36
  sub_call_regex = Regexp.new '([a-zA-Z0-9_]+):'
37
+
13
38
  code.scan(sub_def_regex).map do |sub|
14
39
  subroutines[sub[0]] = sub[1]
15
40
  end
@@ -24,54 +49,14 @@ module Nil
24
49
  subroutines[$1]
25
50
  end
26
51
 
27
- code.gsub(sub_def_regex, '')
28
- end
29
-
30
- def interpret_mode_definition(code)
31
- code.match /\.mode\((ascii|num)\)/i do |m|
32
- $1 == 'num' ? @mode = 'num' : @mode = 'ascii'
33
- end
34
- code.gsub(/\.mode\((ascii|num)\)/i, '')
35
- end
36
-
37
- def expand_included_files(code)
38
- c = code
39
- include_regex = Regexp.new '\.include\(([^\)]+)\)'
40
- included_code = ""
41
- files = c.scan(include_regex).flatten
42
- files.each do |f|
43
- included_code << File.open(f, 'r').read.strip
44
- end
45
- c.gsub!(include_regex, '')
46
- c = included_code + c
47
- c
48
- end
49
-
50
- def strip_comments(code)
51
- code.gsub(/;(.+);/, '')
52
- end
53
-
54
- def parse(code)
55
- @original_code = code
56
-
57
- compiled_code = @original_code
58
-
59
- compiled_code.gsub!(/\s/, '')
60
-
61
- compiled_code = interpret_mode_definition(compiled_code)
62
-
63
- Nil::Interpreter.mode = @mode
64
-
65
- compiled_code = expand_included_files(compiled_code)
66
-
67
- compiled_code = strip_comments(compiled_code)
52
+ code.gsub!(sub_def_regex, '')
68
53
 
69
- compiled_code.gsub!(/([\+-<>])(\d+)/) do |m| # Parse the numerical commands.
54
+ # Parse numerical commands
55
+ code.gsub!(/([\+-<>])(\d+)/) do |m|
70
56
  $1 * $2.to_i
71
57
  end
72
58
 
73
- compiled_code = compile_subroutines(compiled_code)
74
- compiled_code
59
+ Nil::NilCommands.new(code, mode)
75
60
  end
76
61
  end
77
62
  end
@@ -0,0 +1,55 @@
1
+ module Nil
2
+ class NilCommands
3
+ attr_reader :sp, :stack, :code_pointer, :loop_begin_pointer,
4
+ :loop_end_pointer, :mode, :code
5
+
6
+
7
+ def initialize(code, mode = 'ascii')
8
+ @sp = 0
9
+ @stack = [0] * 10_000
10
+ @code_pointer = 0
11
+ @loop_begin_pointer = 0
12
+ @loop_end_pointer = 0
13
+
14
+ @code = code
15
+ @mode = mode
16
+ end
17
+
18
+ def run
19
+ while @code_pointer <= code.size
20
+ case code[@code_pointer]
21
+ when '.'
22
+ if @mode == 'num'
23
+ puts @stack[@sp]
24
+ else
25
+ print @stack[@sp].chr
26
+ end
27
+ when ','
28
+ if @mode == 'num'
29
+ @stack[@sp] = gets[0].to_i
30
+ else
31
+ @stack[@sp] = gets[0].ord
32
+ end
33
+ when '+'
34
+ @stack[@sp] += 1
35
+ when '-'
36
+ @stack[@sp] -= 1 unless @stack[@sp] == 0
37
+ when '>'
38
+ @sp += 1
39
+ when '<'
40
+ @sp -= 1
41
+ when '['
42
+ @loop_begin_pointer = @code_pointer
43
+
44
+ if @stack[@sp] <= 0
45
+ @code_pointer = @loop_end_pointer
46
+ end
47
+ when ']'
48
+ @loop_end_pointer = @code_pointer
49
+ @code_pointer = (@loop_begin_pointer - 1)
50
+ end
51
+ @code_pointer += 1
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,3 +1,3 @@
1
1
  module Nil
2
- VERSION = "1.0.9"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -1,125 +1,129 @@
1
1
  require 'nil'
2
+ require 'stringio'
2
3
  require 'riot'
3
4
 
4
5
  Riot.pretty_dots
5
6
 
6
- BOILERPLATE = <<RB
7
- sp = 0
8
- stack = [0] * 10_000
9
- RB
7
+ context "NilCommands" do
8
+ context "Adding" do
9
+ setup { Nil::NilCommands.new("+") }
10
10
 
11
- context "Interpreter" do
12
- setup { Nil::Interpreter.new }
11
+ asserts("adds value in stack at stack pointer") do
12
+ topic.run
13
+ topic.stack[topic.sp] == 1
14
+ end
15
+ end
13
16
 
14
- context "General code generation" do
15
- asserts("invalid characters in code cause failure") { topic.compile "a++", 'num' }.raises SyntaxError
17
+ context "Subtracting" do
18
+ setup { Nil::NilCommands.new("+-") }
16
19
 
17
- asserts("creates boilerplate code") { topic.compiled_ruby == BOILERPLATE }
20
+ asserts("subtracts value in stack at stack pointer") do
21
+ topic.run
22
+ topic.stack[topic.sp] == 0
23
+ end
18
24
  end
19
25
 
20
- context "Modes" do
21
- asserts("handles . in numerical mode") { topic.compile('.', 'num') == (BOILERPLATE + "puts stack[sp]\n") }
22
- asserts("handles . in ascii mode") { topic.compile('.', 'ascii') == (BOILERPLATE + "print stack[sp].chr\n") }
23
- end
26
+ context "Stack Pointer" do
27
+ asserts("stack pointer is incremented") do
28
+ nc = Nil::NilCommands.new(">")
29
+ nc.run
30
+ nc.sp == 1
31
+ end
24
32
 
25
- context "General operator handling" do
26
- asserts("handles ,") { topic.compile(',', 'num') == (BOILERPLATE + "stack[sp] = gets[0]\n") }
27
- asserts("handles >") { topic.compile('>', 'num') == (BOILERPLATE + "sp += 1\n") }
28
- asserts("handles <") { topic.compile('<', 'num') == (BOILERPLATE + "sp -= 1\n") }
29
- asserts("handles +") { topic.compile('+', 'num') == (BOILERPLATE + "stack[sp] += 1\n") }
30
- asserts("handles -") { topic.compile('-', 'num') == (BOILERPLATE + "stack[sp] -= 1\n") }
31
- asserts("handles [") { topic.compile('[', 'num') == (BOILERPLATE + "while stack[sp] != 0\n" ) }
32
- asserts("handles ]") { topic.compile(']', 'num') == (BOILERPLATE + "end\n" ) }
33
+ asserts("stack pointer is decremented") do
34
+ nc = Nil::NilCommands.new("><")
35
+ nc.run
36
+ nc.sp == 0
37
+ end
33
38
  end
34
39
 
35
- context "Advanced operator handling" do
36
- asserts("handles multiple operators smartly") do
37
- code = topic.compile "++++---->>>><<<<", 'num'
38
- code == BOILERPLATE + <<CODE
39
- stack[sp] += 4
40
- stack[sp] -= 4
41
- sp += 4
42
- sp -= 4
43
- CODE
44
- end
40
+ context "Loops" do
41
+ setup { Nil::NilCommands.new("++++[-]") }
45
42
 
46
- asserts("handles multiple printing statements") do
47
- code = "++++.>+++."
48
- code = topic.compile(code, 'num')
49
- code == BOILERPLATE + <<CODE
50
- stack[sp] += 4
51
- puts stack[sp]
52
- sp += 1
53
- stack[sp] += 3
54
- puts stack[sp]
55
- CODE
43
+ asserts("while loops work") do
44
+ topic.run
45
+ topic.stack[topic.sp] == 0
46
+ end
56
47
  end
57
- end
58
48
 
59
- context "Bug testing" do
60
- denies("error raised when brackets are inside subroutines") do
61
- code = ":sub{+++[-]}sub:"
62
- code = (Nil::MacroParser.new).parse code
63
- topic.compile(code)
64
- end.raises SyntaxError
65
- end
49
+ context "IO" do
50
+ context "Numerical output" do
51
+ setup { Nil::NilCommands.new("+++.", 'num') }
52
+
53
+ asserts("that numerical mode output works") do
54
+ out = StringIO.new
55
+ $stdout = out
56
+ topic.run
57
+ out.rewind
58
+ out.read == "3\n"
59
+ end
60
+ end
66
61
 
62
+ context "ASCII output" do
63
+ setup { Nil::NilCommands.new("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.", 'ascii') }
64
+
65
+ asserts("that ASCII output works") do
66
+ out = StringIO.new
67
+ old_out = $stdout
68
+ $stdout = out
69
+ topic.run
70
+ out.rewind
71
+ out.read == "d\n"
72
+ $stdout = old_out
73
+ end
74
+ end
75
+ end
67
76
  end
68
77
 
69
- context "MacroParser" do
70
- setup { Nil::MacroParser.new }
78
+ context "MacroCompiler" do
79
+ setup { Nil::MacroCompiler.new }
71
80
 
72
81
  context "Basics" do
73
- asserts("that the original code equals code passed to parser") do
74
- code = "++><>--" # Just some random stuff
75
- topic.parse code
76
- compiled_original_code = topic.original_code
77
- code == compiled_original_code
78
- end
82
+ asserts("that returned value is of NilCommands class") { topic.compile("").class == Nil::NilCommands }
79
83
  end
80
84
 
81
85
  context "Numbered commands" do
82
86
  asserts("that numbered commands are expanded") do
83
87
  code = "+5-5>5<5"
84
- compiled_code = topic.parse code
85
- compiled_code == "+++++----->>>>><<<<<"
88
+ compiled_code = topic.compile code
89
+ compiled_code.code == "+++++----->>>>><<<<<"
86
90
  end
87
91
  end
88
92
 
89
93
  context "Modes" do
90
94
  asserts("that numerical mode is correctly set") do
91
95
  code = ".mode(num)"
92
- compiled = topic.parse code
93
- Nil::Interpreter.mode == 'num'
96
+ compiled = topic.compile code
97
+ compiled.mode == 'num'
94
98
  end
95
99
 
96
100
  asserts("that ascii mode is correctly set") do
97
101
  code = ".mode(ascii)"
98
- compiled = topic.parse code
99
- Nil::Interpreter.mode == 'ascii'
102
+ compiled = topic.compile code
103
+ compiled.mode == 'ascii'
100
104
  end
101
105
 
102
106
  asserts("that ascii mode is set by default") do
103
107
  code = "+"
104
- compiled = topic.parse code
105
- Nil::Interpreter.mode == 'ascii'
108
+ compiled = topic.compile code
109
+ compiled.mode == 'ascii'
106
110
  end
107
111
  end
108
112
 
109
113
  context "Subroutines" do
110
114
  asserts("that subroutines are expanded") do
111
115
  code = ":test{++--><}++test:"
112
- compiled_code = topic.parse code
113
- compiled_code == "++++--><"
116
+ compiled_code = topic.compile code
117
+ compiled_code.code == "++++--><"
114
118
  end
115
119
 
116
120
  asserts("that subroutines contaning numbered commands are expanded") do
117
121
  code = ":test{+5}test:"
118
- compiled_code = topic.parse code
119
- compiled_code == "+++++"
122
+ compiled_code = topic.compile code
123
+ compiled_code.code == "+++++"
120
124
  end
121
125
 
122
- asserts("that recursive subroutines throw exceptions") { topic.parse ":sub{sub:}sub:" }.raises(SyntaxError)
126
+ asserts("that recursive subroutines throw exceptions") { topic.compile ":sub{sub:}sub:" }.raises(SyntaxError)
123
127
  end
124
128
 
125
129
  context "Whitespace & Comments" do
@@ -131,28 +135,28 @@ context "MacroParser" do
131
135
  }
132
136
  add:
133
137
  CODE
134
- compiled_code = topic.parse code
135
- compiled_code == "++--++++"
136
- end
138
+ compiled_code = topic.compile code
139
+ compiled_code.code == "++--++++"
140
+ end
137
141
 
138
142
  asserts("that comments are stripped") do
139
143
  code = ";this is a comment; ++--"
140
- compiled_code = topic.parse code
141
- compiled_code == "++--"
144
+ compiled_code = topic.compile code
145
+ compiled_code.code == "++--"
142
146
  end
143
147
  end
144
148
 
145
149
  context "File includes" do
146
150
  asserts("that other files are included") do
147
151
  code = ".include(test/test.nil)--"
148
- compiled_code = topic.parse code
149
- compiled_code == "++--"
152
+ compiled_code = topic.compile code
153
+ compiled_code.code == "++--"
150
154
  end
151
155
 
152
156
  asserts("that multiple files are included correctly") do
153
157
  code = ".include(test/test.nil).include(test/another.nil)--"
154
- compiled_code = topic.parse code
155
- compiled_code == "++>>--"
158
+ compiled_code = topic.compile code
159
+ compiled_code.code == "++>>--"
156
160
  end
157
161
  end
158
162
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nil
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.9
4
+ version: 2.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-05 00:00:00.000000000 Z
12
+ date: 2012-06-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: trollop
@@ -45,8 +45,8 @@ files:
45
45
  - TODO.md
46
46
  - bin/nil
47
47
  - lib/nil.rb
48
- - lib/nil/interpreter.rb
49
48
  - lib/nil/macro.rb
49
+ - lib/nil/nil_commands.rb
50
50
  - lib/nil/version.rb
51
51
  - nil.gemspec
52
52
  - test/another.nil
@@ -80,3 +80,4 @@ test_files:
80
80
  - test/another.nil
81
81
  - test/nil_test.rb
82
82
  - test/test.nil
83
+ has_rdoc:
@@ -1,87 +0,0 @@
1
- module Nil
2
- class Interpreter
3
- attr_reader :compiled_ruby
4
-
5
- def self.mode=(x)
6
- @@mode = x
7
- end
8
-
9
- def self.mode
10
- @@mode
11
- end
12
-
13
- def run
14
- eval(@compiled_ruby)
15
- end
16
-
17
- def initialize
18
- @@mode = 'ascii'
19
- end
20
-
21
- def handle_command(command, count)
22
- case command[0]
23
- when '+'
24
- "stack[sp] += #{count}\n"
25
- when '-'
26
- "stack[sp] -= #{count}\n"
27
- when '>'
28
- "sp += #{count}\n"
29
- when '<'
30
- "sp -= #{count}\n"
31
- when '['
32
- s = ""
33
- count.times do
34
- s << "while stack[sp] != 0\n"
35
- end
36
- s
37
- when ']'
38
- s = ""
39
- count.times do
40
- s << "end\n"
41
- end
42
- s
43
- when '.'
44
- s = ""
45
- count.times do
46
- if @@mode == 'num'
47
- s << "puts stack[sp]\n"
48
- else
49
- s << "print stack[sp].chr\n"
50
- end
51
- end
52
- s
53
- when ','
54
- s = ""
55
- count.times do
56
- s << "stack[sp] = gets[0]\n"
57
- end
58
- s
59
- end
60
- end
61
-
62
- def compile(code, mode)
63
- @@mode = mode
64
- @compiled_ruby = <<RB
65
- sp = 0
66
- stack = [0] * 10_000
67
- RB
68
-
69
- code.each_char do |c|
70
- if !COMMANDS.include? c
71
- raise SyntaxError, "Invalid characters in code."
72
- end
73
- end
74
-
75
- commands = code.scan( /((.)\2*)/ ).collect { |match| match.first }
76
-
77
- commands.each_index do |i|
78
- commands[i] = [commands[i], commands[i].length]
79
- end
80
-
81
- commands.each do |c|
82
- @compiled_ruby << handle_command(c[0], c[1])
83
- end
84
- @compiled_ruby
85
- end
86
- end
87
- end