Class: Opal::Compiler
- Inherits:
-
Object
- Object
- Opal::Compiler
- Defined in:
- opal/lib/opal/compiler.rb
Overview
Constant Summary
- INDENT =
Generated code gets indented with two spaces on each scope
' '
- COMPARE =
All compare method nodes - used to optimize performance of math comparisons
%w[< > <= >=]
Instance Attribute Summary collapse
-
#case_stmt ⇒ Object
readonly
Current case_stmt.
-
#eof_content ⇒ Object
readonly
Any content in END special construct.
-
#fragments ⇒ Array
readonly
All [Opal::Fragment] used to produce result.
-
#result ⇒ String
readonly
The compiled ruby code.
-
#scope ⇒ Object
Current scope.
Class Method Summary collapse
-
.compiler_option(name, default_value, options = {}) ⇒ Object
defines a compiler option, also creating method of form 'name?'.
Instance Method Summary collapse
-
#arity_check? ⇒ Boolean
adds an arity check to every method definition.
-
#compile ⇒ String
Compile some ruby code to a string.
-
#dynamic_require_severity ⇒ Object
how to handle dynamic requires (:error, :warning, :ignore).
-
#error(msg, line = nil) ⇒ Object
This is called when a parsing/processing error occurs.
-
#file ⇒ String
The filename to use for compiling this code.
- #fragment(str, sexp = nil) ⇒ Object
-
#freezing? ⇒ Boolean
stubs out #freeze and #frozen?.
- #handle_block_given_call(sexp) ⇒ Object
- #handlers ⇒ Object
-
#helper(name) ⇒ Object
Use the given helper.
-
#helpers ⇒ Set<Symbol>
Any helpers required by this file.
- #in_case ⇒ Object
-
#in_while ⇒ Object
Used when we enter a while statement.
-
#in_while? ⇒ Boolean
Returns true if the parser is curently handling a while sexp, false otherwise.
-
#indent(&block) ⇒ Object
To keep code blocks nicely indented, this will yield a block after adding an extra layer of indent, and then returning the resulting code after reverting the indent.
-
#initialize(source, options = {}) ⇒ Compiler
constructor
A new instance of Compiler.
-
#inline_operators? ⇒ Object
are operators compiled inline.
-
#irb? ⇒ Object
compile top level local vars with support for irb style vars.
-
#method_calls ⇒ Object
Method calls made in this file.
-
#method_missing? ⇒ Boolean
adds method stubs for all used methods in file.
-
#operator_helpers ⇒ Object
Operator helpers.
-
#parser_indent ⇒ Object
Instances of
Scope
can use this to determine the current scope indent. -
#process(sexp, level = :expr) ⇒ Object
Process the given sexp by creating a node instance, based on its type, and compiling it to fragments.
-
#requirable? ⇒ Object
Prepare the code for future requires.
-
#required_trees ⇒ Object
An array of trees required in this file (typically by calling #require_tree).
-
#requires ⇒ Object
An array of requires used in this file.
-
#returns(sexp) ⇒ Object
The last sexps in method bodies, for example, need to be returned in the compiled javascript.
-
#s(*parts) ⇒ Object
Create a new sexp using the given parts.
-
#source_map(source_file = nil) ⇒ Opal::SourceMap
Returns a source map that can be used in the browser to map back to original ruby code.
-
#tainting? ⇒ Object
stubs out #taint, #untaint and #tainted?.
-
#unique_temp ⇒ Object
Used to generate a unique id name per file.
-
#warning(msg, line = nil) ⇒ Object
This is called when a parsing/processing warning occurs.
-
#with_temp(&block) ⇒ Object
Temporary varibales will be needed from time to time in the generated code, and this method will assign (or reuse) on while the block is yielding, and queue it back up once it is finished.
Constructor Details
#initialize(source, options = {}) ⇒ Compiler
Returns a new instance of Compiler
136 137 138 139 140 141 |
# File 'opal/lib/opal/compiler.rb', line 136 def initialize(source, = {}) @source = source @indent = '' @unique = 0 @options = end |
Instance Attribute Details
#case_stmt ⇒ Object (readonly)
Current case_stmt
131 132 133 |
# File 'opal/lib/opal/compiler.rb', line 131 def case_stmt @case_stmt end |
#eof_content ⇒ Object (readonly)
Any content in END special construct
134 135 136 |
# File 'opal/lib/opal/compiler.rb', line 134 def eof_content @eof_content end |
#fragments ⇒ Array (readonly)
Returns all [Opal::Fragment] used to produce result
125 126 127 |
# File 'opal/lib/opal/compiler.rb', line 125 def fragments @fragments end |
#result ⇒ String (readonly)
Returns The compiled ruby code
122 123 124 |
# File 'opal/lib/opal/compiler.rb', line 122 def result @result end |
#scope ⇒ Object
Current scope
128 129 130 |
# File 'opal/lib/opal/compiler.rb', line 128 def scope @scope end |
Class Method Details
.compiler_option(name, default_value, options = {}) ⇒ Object
defines a compiler option, also creating method of form 'name?'
52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'opal/lib/opal/compiler.rb', line 52 def self.compiler_option(name, default_value, = {}) mid = [:as] valid_values = [:valid_values] define_method(mid || name) do value = @options.fetch(name) { default_value } if valid_values and not(valid_values.include?(value)) raise ArgumentError, "invalid value #{value.inspect} for option #{name.inspect} "+ "(valid values: #{valid_values.inspect})" end value end end |
Instance Method Details
#arity_check? ⇒ Boolean
adds an arity check to every method definition
85 |
# File 'opal/lib/opal/compiler.rb', line 85 compiler_option :arity_check, false, :as => :arity_check? |
#compile ⇒ String
Compile some ruby code to a string.
146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'opal/lib/opal/compiler.rb', line 146 def compile @parser = Parser.new @sexp = s(:top, @parser.parse(@source, self.file) || s(:nil)) @eof_content = @parser.lexer.eof_content @fragments = process(@sexp).flatten @result = @fragments.map(&:code).join('') rescue => error = "An error occurred while compiling: #{self.file}\n#{error.}" raise error.class, , error.backtrace end |
#dynamic_require_severity ⇒ Object
how to handle dynamic requires (:error, :warning, :ignore)
107 |
# File 'opal/lib/opal/compiler.rb', line 107 compiler_option :dynamic_require_severity, :error, :valid_values => [:error, :warning, :ignore] |
#error(msg, line = nil) ⇒ Object
This is called when a parsing/processing error occurs. This method simply appends the filename and curent line number onto the message and raises it.
191 192 193 |
# File 'opal/lib/opal/compiler.rb', line 191 def error(msg, line = nil) raise SyntaxError, "#{msg} :#{file}:#{line}" end |
#file ⇒ String
The filename to use for compiling this code. Used for FILE directives as well as finding relative require()
71 |
# File 'opal/lib/opal/compiler.rb', line 71 compiler_option :file, '(file)' |
#fragment(str, sexp = nil) ⇒ Object
216 217 218 |
# File 'opal/lib/opal/compiler.rb', line 216 def fragment(str, sexp = nil) Fragment.new(str, sexp) end |
#freezing? ⇒ Boolean
stubs out #freeze and #frozen?
92 |
# File 'opal/lib/opal/compiler.rb', line 92 compiler_option :freezing, true, :as => :freezing? |
#handle_block_given_call(sexp) ⇒ Object
389 390 391 392 393 394 395 396 397 398 |
# File 'opal/lib/opal/compiler.rb', line 389 def handle_block_given_call(sexp) @scope.uses_block! if @scope.block_name fragment("(#{@scope.block_name} !== nil)", sexp) elsif scope = @scope.find_parent_def and scope.block_name fragment("(#{scope.block_name} !== nil)", sexp) else fragment("false", sexp) end end |
#handlers ⇒ Object
293 294 295 |
# File 'opal/lib/opal/compiler.rb', line 293 def handlers @handlers ||= Opal::Nodes::Base.handlers end |
#helper(name) ⇒ Object
Use the given helper
227 228 229 |
# File 'opal/lib/opal/compiler.rb', line 227 def helper(name) self.helpers << name end |
#helpers ⇒ Set<Symbol>
Any helpers required by this file. Used by Nodes::Top to reference runtime helpers that are needed. These are used to minify resulting javascript by keeping a reference to helpers used.
174 175 176 |
# File 'opal/lib/opal/compiler.rb', line 174 def helpers @helpers ||= Set.new([:breaker, :slice]) end |
#in_case ⇒ Object
267 268 269 270 271 272 273 |
# File 'opal/lib/opal/compiler.rb', line 267 def in_case return unless block_given? old = @case_stmt @case_stmt = {} yield @case_stmt = old end |
#in_while ⇒ Object
Used when we enter a while statement. This pushes onto the current scope's while stack so we know how to handle break, next etc.
258 259 260 261 262 263 264 265 |
# File 'opal/lib/opal/compiler.rb', line 258 def in_while return unless block_given? @while_loop = @scope.push_while result = yield @scope.pop_while result end |
#in_while? ⇒ Boolean
Returns true if the parser is curently handling a while sexp, false otherwise.
277 278 279 |
# File 'opal/lib/opal/compiler.rb', line 277 def in_while? @scope.in_while? end |
#indent(&block) ⇒ Object
To keep code blocks nicely indented, this will yield a block after adding an extra layer of indent, and then returning the resulting code after reverting the indent.
234 235 236 237 238 239 240 241 242 |
# File 'opal/lib/opal/compiler.rb', line 234 def indent(&block) indent = @indent @indent += INDENT @space = "\n#@indent" res = yield @indent = indent @space = "\n#@indent" res end |
#inline_operators? ⇒ Object
are operators compiled inline
117 |
# File 'opal/lib/opal/compiler.rb', line 117 compiler_option :inline_operators, true, :as => :inline_operators? |
#irb? ⇒ Object
compile top level local vars with support for irb style vars
102 |
# File 'opal/lib/opal/compiler.rb', line 102 compiler_option :irb, false, :as => :irb? |
#method_calls ⇒ Object
Method calls made in this file
184 185 186 |
# File 'opal/lib/opal/compiler.rb', line 184 def method_calls @method_calls ||= Set.new end |
#method_missing? ⇒ Boolean
adds method stubs for all used methods in file
78 |
# File 'opal/lib/opal/compiler.rb', line 78 compiler_option :method_missing, true, :as => :method_missing? |
#operator_helpers ⇒ Object
Operator helpers
179 180 181 |
# File 'opal/lib/opal/compiler.rb', line 179 def operator_helpers @operator_helpers ||= Set.new end |
#parser_indent ⇒ Object
Instances of Scope
can use this to determine the current
scope indent. The indent is used to keep generated code easily
readable.
205 206 207 |
# File 'opal/lib/opal/compiler.rb', line 205 def parser_indent @indent end |
#process(sexp, level = :expr) ⇒ Object
Process the given sexp by creating a node instance, based on its type, and compiling it to fragments.
283 284 285 286 287 288 289 290 291 |
# File 'opal/lib/opal/compiler.rb', line 283 def process(sexp, level = :expr) return fragment('') if sexp == nil if handler = handlers[sexp.type] return handler.new(sexp, level, self).compile_to_fragments else raise "Unsupported sexp: #{sexp.type}" end end |
#requirable? ⇒ Object
Prepare the code for future requires
112 |
# File 'opal/lib/opal/compiler.rb', line 112 compiler_option :requirable, false, :as => :requirable? |
#required_trees ⇒ Object
An array of trees required in this file (typically by calling #require_tree)
304 305 306 |
# File 'opal/lib/opal/compiler.rb', line 304 def required_trees @required_trees ||= [] end |
#requires ⇒ Object
An array of requires used in this file
298 299 300 |
# File 'opal/lib/opal/compiler.rb', line 298 def requires @requires ||= [] end |
#returns(sexp) ⇒ Object
The last sexps in method bodies, for example, need to be returned
in the compiled javascript. Due to syntax differences between
javascript any ruby, some sexps need to be handled specially. For
example, if
statemented cannot be returned in javascript, so
instead the "truthy" and "falsy" parts of the if statement both
need to be returned instead.
Sexps that need to be returned are passed to this method, and the
alterned/new sexps are returned and should be used instead. Most
sexps can just be added into a s(:return) sexp
, so that is the
default action if no special case is required.
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
# File 'opal/lib/opal/compiler.rb', line 319 def returns(sexp) return returns s(:nil) unless sexp case sexp.type # Undefs go from 1 ruby undef a,b,c to multiple JS Opal.udef() calls, so need to treat them as individual statements # and put the return on the last one when :undef last = sexp.pop sexp << returns(last) when :break, :next, :redo sexp when :yield sexp[0] = :returnable_yield sexp when :scope sexp[1] = returns sexp[1] sexp when :block if sexp.length > 1 sexp[-1] = returns sexp[-1] else sexp << returns(s(:nil)) end sexp when :when sexp[2] = returns(sexp[2]) sexp when :rescue sexp[1] = returns sexp[1] if sexp[2] and sexp[2][0] == :resbody if sexp[2][2] sexp[2][2] = returns sexp[2][2] else sexp[2][2] = returns s(:nil) end end sexp when :ensure sexp[1] = returns sexp[1] sexp when :begin sexp[1] = returns sexp[1] sexp when :rescue_mod sexp[1] = returns sexp[1] sexp[2] = returns sexp[2] sexp when :while # sexp[2] = returns(sexp[2]) sexp when :return, :js_return sexp when :xstr sexp[1] = "return #{sexp[1]};" unless /return|;/ =~ sexp[1] sexp when :dxstr sexp[1] = "return #{sexp[1]}" unless /return|;|\n/ =~ sexp[1] sexp when :if sexp[2] = returns(sexp[2] || s(:nil)) sexp[3] = returns(sexp[3] || s(:nil)) sexp else s(:js_return, sexp).tap { |s| s.source = sexp.source } end end |
#s(*parts) ⇒ Object
Create a new sexp using the given parts. Even though this just returns an array, it must be used incase the internal structure of sexps does change.
212 213 214 |
# File 'opal/lib/opal/compiler.rb', line 212 def s(*parts) Sexp.new(parts) end |
#source_map(source_file = nil) ⇒ Opal::SourceMap
Returns a source map that can be used in the browser to map back to original ruby code.
165 166 167 |
# File 'opal/lib/opal/compiler.rb', line 165 def source_map(source_file = nil) Opal::SourceMap.new(@fragments, source_file || self.file) end |
#tainting? ⇒ Object
stubs out #taint, #untaint and #tainted?
97 |
# File 'opal/lib/opal/compiler.rb', line 97 compiler_option :tainting, true, :as => :tainting? |
#unique_temp ⇒ Object
Used to generate a unique id name per file. These are used mainly to name method bodies for methods that use blocks.
222 223 224 |
# File 'opal/lib/opal/compiler.rb', line 222 def unique_temp "TMP_#{@unique += 1}" end |
#warning(msg, line = nil) ⇒ Object
This is called when a parsing/processing warning occurs. This method simply appends the filename and curent line number onto the message and issues a warning.
198 199 200 |
# File 'opal/lib/opal/compiler.rb', line 198 def warning(msg, line = nil) warn "WARNING: #{msg} -- #{file}:#{line}" end |
#with_temp(&block) ⇒ Object
Temporary varibales will be needed from time to time in the generated code, and this method will assign (or reuse) on while the block is yielding, and queue it back up once it is finished. Variables are queued once finished with to save the numbers of variables needed at runtime.
249 250 251 252 253 254 |
# File 'opal/lib/opal/compiler.rb', line 249 def with_temp(&block) tmp = @scope.new_temp res = yield tmp @scope.queue_temp tmp res end |