nil 1.0.9 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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