burn 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/README.md +56 -569
  2. data/lib/burn.rb +5 -3
  3. data/lib/burn/cli.rb +160 -199
  4. data/lib/burn/config.rb +4 -0
  5. data/lib/burn/config/app.rb +15 -0
  6. data/lib/burn/config/config_base.rb +16 -0
  7. data/lib/burn/config/loader.rb +23 -0
  8. data/lib/burn/config/server.rb +11 -0
  9. data/lib/burn/fuel.rb +11 -0
  10. data/lib/burn/{dsl → fuel}/dsl_base.rb +1 -1
  11. data/lib/burn/fuel/rom/channel_stream.rb +106 -0
  12. data/lib/burn/fuel/rom/declare.rb +43 -0
  13. data/lib/burn/fuel/rom/music.rb +37 -0
  14. data/lib/burn/fuel/rom/note.rb +126 -0
  15. data/lib/burn/fuel/rom/scene.rb +279 -0
  16. data/lib/burn/fuel/rom/sound.rb +31 -0
  17. data/lib/burn/fuel/rom/sound_effect.rb +92 -0
  18. data/lib/burn/fuel/telnet/declare.rb +26 -0
  19. data/lib/burn/fuel/telnet/scene.rb +112 -0
  20. data/lib/burn/generator.rb +10 -3
  21. data/lib/burn/generator/rom/assembly_music.rb +49 -0
  22. data/lib/burn/generator/rom/assembly_sound_effect.rb +35 -0
  23. data/lib/burn/generator/rom/c_source.rb +55 -0
  24. data/lib/burn/generator/{template → rom/template}/mus_template.s +0 -0
  25. data/lib/burn/generator/{template → rom/template}/template.c +0 -0
  26. data/lib/burn/generator/rom_builder.rb +67 -0
  27. data/lib/burn/generator/telnet/jit_compiler.rb +41 -0
  28. data/lib/burn/generator/telnet/screen.rb +86 -0
  29. data/lib/burn/generator/telnet/sprite.rb +16 -0
  30. data/lib/burn/generator/telnet/user_input.rb +25 -0
  31. data/lib/burn/generator/telnet_vm.rb +50 -0
  32. data/lib/burn/pxes.rb +3 -0
  33. data/lib/burn/{util/pxes.rb → pxes/cc65_transpiler.rb} +8 -14
  34. data/lib/burn/pxes/cruby_transpiler.rb +189 -0
  35. data/lib/burn/pxes/transpiler_base.rb +14 -0
  36. data/lib/burn/server.rb +2 -0
  37. data/lib/burn/server/rom.rb +24 -0
  38. data/lib/burn/server/telnet.rb +120 -0
  39. data/lib/burn/util.rb +1 -2
  40. data/lib/burn/util/logo.rb +68 -0
  41. data/lib/burn/util/os.rb +1 -1
  42. data/lib/burn/version.rb +1 -1
  43. metadata +53 -19
  44. data/lib/burn/builder.rb +0 -65
  45. data/lib/burn/dsl.rb +0 -8
  46. data/lib/burn/dsl/channel_stream.rb +0 -105
  47. data/lib/burn/dsl/declare.rb +0 -42
  48. data/lib/burn/dsl/music.rb +0 -36
  49. data/lib/burn/dsl/note.rb +0 -125
  50. data/lib/burn/dsl/scene.rb +0 -278
  51. data/lib/burn/dsl/sound.rb +0 -30
  52. data/lib/burn/dsl/sound_effect.rb +0 -91
  53. data/lib/burn/generator/assembly_music.rb +0 -47
  54. data/lib/burn/generator/assembly_sound_effect.rb +0 -33
  55. data/lib/burn/generator/c_source.rb +0 -54
  56. data/lib/burn/util/server.rb +0 -21
data/lib/burn.rb CHANGED
@@ -7,7 +7,9 @@ require 'archive/tar/minitar'
7
7
  require 'burn/version'
8
8
  require 'burn/debug'
9
9
  require 'burn/util'
10
- require 'burn/dsl'
10
+ require 'burn/config'
11
+ require 'burn/pxes'
12
+ require 'burn/fuel'
11
13
  require 'burn/generator'
12
- require 'burn/cli'
13
- require 'burn/builder'
14
+ require 'burn/server'
15
+ require 'burn/cli'
data/lib/burn/cli.rb CHANGED
@@ -1,243 +1,204 @@
1
1
  module Burn
2
2
  class BurnTasks < Thor
3
3
  include Thor::Actions
4
+ class_option :debug, :type => :string, :aliases => '-d', :desc => "Debug mode"
5
+ class_option :verbose, :type => :boolean, :desc => "Print logs as much as possible", :default => false
6
+ default_task :fire
4
7
 
5
8
  def initialize(*args)
6
9
  super
7
10
  @workspace_root = Dir.getwd
11
+ @os = Util::Os.new
8
12
  end
9
13
 
10
- def self.source_root
11
- File.dirname(__FILE__) + "/.."
12
- end
13
-
14
- default_task :make
15
-
16
14
  desc "init", "Initialize environment"
17
- option :debug, :type => :string, :aliases => '-d', :desc => "Debug mode"
18
- option :verbose, :type => :boolean, :desc => "Print logs as much as possible", :default => false
19
15
  option :quick, :type=>:boolean, :desc=>"Make cc65 binaries available without gcc(however unstable. This option is not recommended.)"
20
16
  def init
21
- env = Burn::Util::Os.new
22
17
  base_path = "#{File.dirname(__FILE__)}/tools"
23
- remove_dir base_path+ "/"+env.os_name, :verbose => options[:verbose]
24
- Burn::Util::Unpack.new.unpack "#{base_path}/#{env.os_name}.tar.gz", base_path
25
- if !env.is_win? && !options[:quick] then
26
- Burn::Util::Unpack.new.unpack "#{base_path}/src.tar.gz", base_path
18
+ remove_dir base_path+ "/"+@os.name, :verbose => options[:verbose]
19
+ Util::Unpack.new.unpack "#{base_path}/#{@os.name}.tar.gz", base_path
20
+ if !@os.is_win? && !options[:quick] then
21
+ Util::Unpack.new.unpack "#{base_path}/src.tar.gz", base_path
27
22
  run "/bin/bash #{File.dirname(__FILE__)}/tools/make_exec.sh #{base_path}"
28
- copy_file "#{base_path}/src/cc65/cc65", "#{base_path}/#{env.os_name}/cc65/bin/cc65", :force => true
29
- copy_file "#{base_path}/src/ca65/ca65", "#{base_path}/#{env.os_name}/cc65/bin/ca65", :force => true
30
- copy_file "#{base_path}/src/ld65/ld65", "#{base_path}/#{env.os_name}/cc65/bin/ld65", :force => true
23
+ ["cc","ca","ld"].each do |pre|
24
+ copy_file "#{base_path}/src/#{pre}65/#{pre}65", "#{base_path}/#{@os.name}/cc65/bin/#{pre}65", :force => true
25
+ end
31
26
  end
32
- say "successfully finished. you've got ready to burn."
33
- end
34
-
35
- desc "make <filename>", "Compile and build application binary from Burn DSL file"
36
- option :preview, :type => :boolean, :aliases => '-p', :desc => "Preview .nes application right after compilation"
37
- option :debug, :type => :string, :aliases => '-d', :desc => "Debug mode"
38
- option :verbose, :type => :boolean, :desc => "Print logs as much as possible", :default => false
39
- option :chrome, :type => :boolean, :aliases => '-c', :desc => "Run emulator on chrome instead of firefox", :default => false
40
- def make(mainfile=nil)
41
- env = Burn::Util::Os.new
27
+ say <<-EOS
28
+ #{Util::Logo.new(VERSION).to_s}
29
+
30
+ successfully finished. you've got ready to burn.
31
+
32
+ EOS
33
+ end
34
+
35
+ desc "fire", "Create your application and Run them instantly"
36
+ option :chrome, :type => :boolean, :aliases => '-c', :desc => "[ROM mode only] Run emulator on chrome instead of firefox", :default => false
37
+ option :preview, :type => :boolean, :aliases => '-p', :desc => "[ROM mode only] Preview mode. By this, you can skip burning fuel DSL and focus on playing rom.", :default => false
38
+ option :rom_server, :type => :boolean, :desc=>"[NOT FOR USER] Run simple http server for emulator", :default => false
39
+ def fire(mainfile=nil)
42
40
  mainfile="main.rb" if mainfile.nil?
41
+ load_conf_and_options(mainfile)
42
+ if options[:rom_server] then
43
+ server "#{@workspace_root}/tmp/burn/release/js/", mainfile
44
+
45
+ else
46
+ if @conf.app.target==:rom then
47
+ make_and_play mainfile, options[:preview]
48
+
49
+ elsif @conf.app.target==:telnet then
50
+ say "starting telnet server #{@conf.server.ip_addr}:#{@conf.server.port}...."
51
+ Server::Telnet.new(File.read("#{@workspace_root}/#{mainfile}"), @conf).start
52
+ end
53
+ end
43
54
 
44
- if !File.exist?(mainfile) then
45
- help
55
+ end
56
+
57
+ desc "version", "Print version"
58
+ map %w(-v --version) => :version
59
+ def version
60
+ say "burn #{Burn::VERSION}"
61
+ end
62
+
63
+ # desc "release", "To be designed"
64
+
65
+ def self.source_root
66
+ File.dirname(__FILE__) + "/.."
67
+ end
68
+
69
+ no_tasks do
46
70
 
47
- elsif !File.exist?("#{File.dirname(__FILE__)}/tools/#{env.os_name}/cc65/bin/ld65#{".exe" if env.is_win?}") then
48
- say <<-EOS
71
+ def make_and_play(mainfile=nil, preview=true)
72
+ mainfile="main.rb" if mainfile.nil?
73
+
74
+ if !File.exist?(mainfile) then
75
+ help
76
+
77
+ elsif !File.exist?("#{File.dirname(__FILE__)}/tools/#{@os.name}/cc65/bin/ld65#{".exe" if @os.is_win?}") then
78
+ say <<-EOS
49
79
  [ERROR] you are not ready to burn, most probably you haven't execute burn init command yet.
50
80
  to fix this, try the following command:
51
81
 
52
- #{"sudo " if !env.is_win?}burn init
82
+ #{"sudo " if !@os.is_win?}burn init
53
83
 
54
84
  EOS
55
-
56
- else
57
-
58
- # init
59
- say "running burn v#{VERSION}..."
60
- remove_dir "#{@workspace_root}/tmp/burn", :verbose => options[:verbose]
61
- empty_directory "#{@workspace_root}/tmp/burn", :verbose => options[:verbose]
62
- #directory File.dirname(__FILE__) + "/workspace_default", "#{@workspace_root}/tmp/burn", :verbose => options[:verbose]
63
- Burn::Util::Unpack.new.unpack "#{File.dirname(__FILE__)}/tools/workspace_default.tar.gz", "#{@workspace_root}/tmp/burn"
64
-
65
- # compile and build .nes
66
- say "."
67
- builder = Builder.new(@workspace_root)
68
- builder.verbose options[:verbose]
69
- builder.load File.read("#{@workspace_root}/#{mainfile}")
70
- builder.generate
71
85
 
72
- # Prepare compilers
73
- say ".."
74
- directory File.dirname(__FILE__) + "/tools/#{env.os_name}", "#{@workspace_root}/tmp/burn", :verbose => options[:verbose]
75
-
76
- # Finally compile
77
- say "..."
78
- redirect = ""
79
- if env.is_win? then
80
- command = ""
81
- ext = "bat"
82
- redirect = " > nul" if !options[:verbose]
83
-
84
86
  else
85
- command = "/bin/bash "
86
- ext = "sh"
87
- redirect = " > /dev/null" if !options[:verbose]
88
-
89
- # Set permissions to execute compilers
90
- Dir::glob("#{@workspace_root}/tmp/burn/cc65/bin/*65").each do |f|
91
- File.chmod(0777, f)
92
- end
93
-
94
- end
95
- run "#{command}#{@workspace_root}/tmp/burn/scripts/compile.#{ext} #{@workspace_root}/tmp/burn #{redirect}", :verbose => options[:verbose]
96
-
97
- # prepare customized emulator
98
- say "...."
99
- require 'base64'
100
- File.write(
101
- "#{@workspace_root}/tmp/burn/release/js/emulator.html",
102
- File.read("#{@workspace_root}/tmp/burn/release/js/emulator.html")
103
- .gsub(/__@__TITLE__@__/, mainfile)
104
- .gsub(/__@__AUTHOR__@__/, "anonymous")
105
- .gsub(/__@__CREATED__@__/, Time.new.to_s)
106
- .gsub(/__@__ROM__@__/, mainfile)
107
- .gsub(/__@__ROMDATA__@__/,
108
- Base64::strict_encode64(
109
- File.binread("#{@workspace_root}/tmp/burn/main.nes")
110
- )
87
+ _v = @conf.app.verbose
88
+ if !preview then
89
+ app_root = "#{@workspace_root}/tmp/burn"
90
+
91
+ # init
92
+ say "running burn v#{VERSION}..."
93
+ remove_dir app_root, :verbose => _v
94
+ empty_directory app_root, :verbose => _v
95
+ Util::Unpack.new.unpack "#{File.dirname(__FILE__)}/tools/workspace_default.tar.gz", app_root
96
+
97
+ # compile and build .nes
98
+ say "."
99
+ builder = Generator::RomBuilder.new(@workspace_root)
100
+ builder.verbose _v
101
+ builder.load File.read("#{@workspace_root}/#{mainfile}")
102
+ builder.generate
103
+
104
+ # Prepare compilers
105
+ say ".."
106
+ directory File.dirname(__FILE__) + "/tools/#{@os.name}", app_root, :verbose => _v
107
+
108
+ # Finally compile
109
+ say "..."
110
+ redirect = ""
111
+ if @os.is_win? then
112
+ command = ""
113
+ ext = "bat"
114
+ redirect = " > nul" if !_v
115
+
116
+ else
117
+ command = "/bin/bash "
118
+ ext = "sh"
119
+ redirect = " > /dev/null" if !_v
120
+
121
+ # Set permissions to execute compilers
122
+ Dir::glob("#{app_root}/cc65/bin/*65").each do |f|
123
+ File.chmod(0777, f)
124
+ end
125
+
126
+ end
127
+ run "#{command}#{app_root}/scripts/compile.#{ext} #{app_root} #{redirect}", :verbose => _v
128
+
129
+ # prepare customized emulator
130
+ say "...."
131
+ require 'base64'
132
+ File.write(
133
+ "#{app_root}/release/js/emulator.html",
134
+ File.read("#{app_root}/release/js/emulator.html")
135
+ .gsub(/__@__TITLE__@__/, mainfile)
136
+ .gsub(/__@__AUTHOR__@__/, "anonymous")
137
+ .gsub(/__@__CREATED__@__/, Time.new.to_s)
138
+ .gsub(/__@__ROM__@__/, mainfile)
139
+ .gsub(/__@__ROMDATA__@__/,
140
+ Base64::strict_encode64(
141
+ File.binread("#{app_root}/main.nes")
142
+ )
143
+ )
111
144
  )
112
- )
113
- copy_file "#{@workspace_root}/tmp/burn/main.nes", "#{@workspace_root}/tmp/burn/release/js/main.nes", :verbose => options[:verbose]
114
-
115
- # used a icon from noun project. Thanks Jenny!
116
- # http://thenounproject.com/term/fire/24187/
117
- say <<-EOS
118
-
119
- `.::-`
120
- :hNMMNNds/.
121
- :dMMMMMMNdo.
122
- `oNMMMMMMMNh:
123
- /NMMMMMMMMNy.
124
- +MMMMMMMMMMm/
125
- dMMMMMMMMMMNo
126
- oMMMMMMMMMMMM+
127
- +MMMMMMMMMMMMN/
128
- sMMMMMMMMMMMMMm.
129
- `mMMMMMMMMMMMMMMs ::`
130
- /MMMMMMMMMMMMMMMN. `hh/`
131
- `dMMMMMMMMMMMMMMMMo -NMd/`
132
- sMMMMMMMMMMMMMMMMMd yMMMh-
133
- /MMMMMMMMMMMMMMMMMMN. /MMMMN+
134
- :NMMMMMMMMMMMMMMMMMMM- :MMMMMMo`
135
- -mMMMMMMMMMMMMMMMMMMMM/ oMMMMMMMo
136
- .dMMMMMMMMMMMMMMMMMMMMMo .mMMMMMMMN:
137
- .dMMMMMMMMMMMMMMMMMMMMMMm` .dMMMMMMMMMm`
138
- `dMMMMMMMMMMMMMMMMMMMMMMMMy.```/mMMMMMMMMMMMo
139
- `hMMMMMMMMMMMMMMMNNNMMMMMMMMNhhmMMMMMMMMMMMMMm`
140
- `hMMMMMMMMMMMMMMMN/`-/yNMMMMMMMMMMMMMMMMMMMMMMM:
141
- `yMMMMMMMMMMMMMMMMMN- .hMMMMMMMMMMMMMMMMMMMMMMy
142
- sMMMMMMMMMMMMMMMMMMMy sMMMMMMMMMMMMMMMMMMMMMd
143
- +MMMMMMMMMMMMMMMMMMMMd `hMMMMMMMMMMMMMMMMMMMMN`
144
- -NMMMMMMMMMMMMMMMMMMMMs .NMMMMMMMMMMMMMMMMMMMM-
145
- `dMMMMMMMMMMMMMMMMMMMMm. sMMMMMMMMMMMMMMMMMMMM:
146
- +MMMMMMMMMMMMMMMMMMMMN- -MMMMMMMMMMMMMMMMMMMM.
147
- `mMMMMMMMMMMMMMMMMMMMm- NMMMN+NMMMMMMMMMMMMN
148
- /MMMMMMMMMMMMMMMMMMMh. NMMMM--mMMMMMMMMMMMh
149
- yMMMMMMMMMMMMMMMMMN+` .MMMMM+ -mMMMMMMMMMM+
150
- `mMMMMMMMMMMMMMMMMh- +MMMMM: /MMMMMMMMMN.
151
- -MMMMMMMMMMMMMMMN+` yMMMMs hMMMMMMMMy
152
- :MMMMMMMMMMMMMMd- :hhy:` /MMMMMMMN.
153
- .NMMMMMMMMMMMMy. `` -MMMMMMMo
154
- dMMMMMMMMMMMs` .MMMMMMd`
155
- +MMMMMMMMMMy` :MMMMMN-
156
- `dMMMMMMMMN. sMMMMN/
157
- :NMMMMMMMh `mMMMN+
158
- +MMMMMMMs burn oMMMN+
159
- +NMMMMMh -NMMm:
160
- /mMMMMN- v#{VERSION} .dMNy.
161
- .hNMMMh` .dNh:
162
- `/mMMMh. -hy:`
163
- `+mMMm/` `:+.
164
- `/hNMd+. ``
165
- .+hNNy/.
166
- `-/oo:
167
-
168
-
145
+ copy_file "#{app_root}/main.nes", "#{app_root}/release/js/main.nes", :verbose => _v
146
+
147
+ say <<-EOS
169
148
 
170
149
 
171
150
  Successfully burned. Congratulations!
172
151
 
173
152
  The executable is available at:
174
153
 
175
- #{@workspace_root}/tmp/burn/main.nes
176
-
177
- EOS
178
-
179
- # run simulator
180
- if options[:preview] then
181
- play
182
- else
183
- say <<-EOS
184
- try following command to play the app:
185
-
186
- burn play
154
+ #{app_root}/main.nes
187
155
 
188
156
  EOS
189
-
157
+
158
+ end
159
+
160
+ # boot up webrick httpserver to download emulator script
161
+ command = "ruby " + File.dirname(__FILE__) + "/../../bin/burn fire #{mainfile} --rom-server " + (options[:debug] ? "-d" : "")
162
+ if @os.is_win? then
163
+ run "start #{command}", :verbose => _v
164
+ else
165
+ run "#{command} &", :verbose => _v
166
+ end
167
+
168
+ # wait for certain period of time to prevent browser from fetching game url too early
169
+ # *DEFINITELY* TO BE REFACTORED
170
+ # maybe better to use :StartCallback of WEBRICK?
171
+ sleep 1
172
+
173
+ # open up browser
174
+ uri = "http://#{@conf.server.ip_addr}:#{@conf.server.port}/emulator.html"
175
+ browser = options[:chrome] ? "chrome" : "firefox"
176
+ if @os.is_win? then
177
+ run "start #{browser} #{uri}", :verbose => _v
178
+ elsif @os.is_mac? then
179
+ browser = "\"/Applications/Google Chrome.app\"" if options[:chrome]
180
+ run "open -a #{browser} #{uri}", :verbose => _v
181
+ else
182
+ run "/usr/bin/#{browser} #{uri}", :verbose => _v
183
+ end
184
+
190
185
  end
191
186
  end
192
- end
193
-
194
- desc "server <document_root>", "Run simple http server for emulator. This is mainly used by burn rubygem itself, not by user."
195
- option :debug, :type => :string, :aliases => '-d', :desc => "Debug mode"
196
- def server(document_root=nil)
197
- raise Exception.new("document_root must be specified when you would like to run http server") if document_root.nil?
198
- server = Burn::Util::Server.new(document_root)
199
- server.start
200
- end
201
-
202
- desc "play <file>", "Invoke application simulator."
203
- option :debug, :type => :string, :aliases => '-d', :desc => "Debug mode"
204
- option :verbose, :type => :boolean, :desc => "Print logs as much as possible", :default => false
205
- option :chrome, :type => :boolean, :aliases => '-c', :desc => "Run emulator on chrome instead of firefox", :default => false
206
- def play(mainfile=nil)
207
- mainfile="main.nes" if mainfile.nil?
208
- env = Burn::Util::Os.new
209
187
 
210
- # boot up webrick httpserver to download emulator script
211
- command = "ruby " + File.dirname(__FILE__) + "/../../bin/burn server #{@workspace_root}/tmp/burn/release/js/ " + (options[:debug] ? "-d" : "")
212
- if env.is_win? then
213
- run "start #{command}", :verbose => options[:verbose]
214
- else
215
- run "#{command} &", :verbose => options[:verbose]
188
+ desc "server <document_root>", "Run simple http server for emulator. This is mainly used by burn rubygem itself, not by user."
189
+ def server(document_root=nil, mainfile)
190
+ raise Exception.new("document_root must be specified when you would like to run http server") if document_root.nil?
191
+ server = Server::Rom.new(document_root,@conf)
192
+ server.start
216
193
  end
217
194
 
218
- # wait for certain period of time to prevent browser from fetching game url too early
219
- sleep 1
220
-
221
- # open up browser
222
- uri = "http://127.0.0.1:17890/emulator.html"
223
- browser = options[:chrome] ? "chrome" : "firefox"
224
- if env.is_win? then
225
- run "start #{browser} #{uri}", :verbose => options[:verbose]
226
- elsif env.is_mac? then
227
- browser = "\"/Applications/Google Chrome.app\"" if options[:chrome]
228
- run "open -a #{browser} #{uri}", :verbose => options[:verbose]
229
- else
230
- run "/usr/bin/#{browser} #{uri}", :verbose => options[:verbose]
195
+ def load_conf_and_options(mainfile)
196
+ @conf = Configuration::Loader.new(File.read("#{@workspace_root}/#{mainfile}"))
197
+ @conf.app.debug options[:debug]
198
+ @conf.app.verbose options[:verbose]
231
199
  end
200
+
232
201
  end
233
202
 
234
- desc "version", "Print version"
235
- map %w(-v --version) => :version
236
- def version
237
- say "burn #{Burn::VERSION}"
238
- end
239
-
240
- # desc "release", "To be designed"
241
-
242
203
  end
243
204
  end
@@ -0,0 +1,4 @@
1
+ require 'burn/config/config_base'
2
+ require 'burn/config/app'
3
+ require 'burn/config/server'
4
+ require 'burn/config/loader'
@@ -0,0 +1,15 @@
1
+ module Burn
2
+ module Configuration
3
+ class App < ConfigBase
4
+ def initialize
5
+ @target = :rom
6
+ @width = 73
7
+ @height = 13
8
+ @frame_rate = :high
9
+ @user_input = :disable
10
+ @verbose = false
11
+ @debug = false
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module Burn
2
+ module Configuration
3
+ class ConfigBase
4
+ def method_missing(method_symbol, *args, &block)
5
+ if instance_variables.include?("@#{method_symbol}".to_sym) then
6
+ if args.count>0 then
7
+ self.instance_variable_set "@#{method_symbol}", args[0]
8
+ else
9
+ self.instance_variable_get "@#{method_symbol}"
10
+ end
11
+ end
12
+ end
13
+
14
+ end
15
+ end
16
+ end