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[< > <= >=].freeze 
Instance Attribute Summary collapse
- 
  
    
      #case_stmt  ⇒ Object 
    
    
  
  
  
  
    
      readonly
    
    
  
  
  
  
  
  
    Current case_stmt. 
- 
  
    
      #comments  ⇒ Object 
    
    
  
  
  
  
    
      readonly
    
    
  
  
  
  
  
  
    Comments from the source code. 
- 
  
    
      #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?'. 
- .module_name(path) ⇒ Object
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). 
- 
  
    
      #enable_source_location?  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    Adds source_location for every method definition. 
- 
  
    
      #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, scope, sexp = nil) ⇒ Object
- #freezing? ⇒ Boolean deprecated Deprecated.
- #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  ⇒ 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. 
- #parse ⇒ Object
- 
  
    
      #parse_comments?  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    Adds comments for every method definition. 
- 
  
    
      #parser_indent  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    Instances of Scopecan 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. 
- #re_raise_with_location ⇒ Object
- 
  
    
      #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(type, *children)  ⇒ Object 
    
    
  
  
  
  
  
  
  
  
  
    Create a new sexp using the given parts. 
- 
  
    
      #source_map  ⇒ Opal::SourceMap 
    
    
  
  
  
  
  
  
  
  
  
    Returns a source map that can be used in the browser to map back to original ruby code. 
- #tainting? ⇒ Object deprecated Deprecated.
- 
  
    
      #unique_temp(name)  ⇒ 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  ⇒ 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
| 160 161 162 163 164 165 166 167 | # File 'opal/lib/opal/compiler.rb', line 160 def initialize(source, = {}) @source = source @indent = '' @unique = 0 @options = @comments = Hash.new([]) @case_stmt = nil end | 
Instance Attribute Details
#case_stmt ⇒ Object (readonly)
Current case_stmt
| 152 153 154 | # File 'opal/lib/opal/compiler.rb', line 152 def case_stmt @case_stmt end | 
#comments ⇒ Object (readonly)
Comments from the source code
| 158 159 160 | # File 'opal/lib/opal/compiler.rb', line 158 def comments @comments end | 
#eof_content ⇒ Object (readonly)
Any content in END special construct
| 155 156 157 | # File 'opal/lib/opal/compiler.rb', line 155 def eof_content @eof_content end | 
#fragments ⇒ Array (readonly)
Returns all [Opal::Fragment] used to produce result
| 146 147 148 | # File 'opal/lib/opal/compiler.rb', line 146 def fragments @fragments end | 
#result ⇒ String (readonly)
Returns The compiled ruby code
| 143 144 145 | # File 'opal/lib/opal/compiler.rb', line 143 def result @result end | 
#scope ⇒ Object
Current scope
| 149 150 151 | # File 'opal/lib/opal/compiler.rb', line 149 def scope @scope end | 
Class Method Details
.compiler_option(name, default_value, options = {}) ⇒ Object
defines a compiler option, also creating method of form 'name?'
| 61 62 63 64 65 66 67 68 69 70 71 72 | # File 'opal/lib/opal/compiler.rb', line 61 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 && !valid_values.include?(value) raise ArgumentError, "invalid value #{value.inspect} for option #{name.inspect} " \ "(valid values: #{valid_values.inspect})" end value end end | 
.module_name(path) ⇒ Object
| 55 56 57 58 | # File 'opal/lib/opal/compiler.rb', line 55 def self.module_name(path) path = File.join(File.dirname(path), File.basename(path).split('.').first) Pathname(path).cleanpath.to_s end | 
Instance Method Details
#arity_check? ⇒ Boolean
adds an arity check to every method definition
| 94 | # File 'opal/lib/opal/compiler.rb', line 94 compiler_option :arity_check, false, as: :arity_check? | 
#compile ⇒ String
Compile some ruby code to a string.
| 172 173 174 175 176 177 178 179 | # File 'opal/lib/opal/compiler.rb', line 172 def compile parse @fragments = re_raise_with_location { process(@sexp).flatten } @fragments << fragment("\n", nil, s(:newline)) unless @fragments.last.code.end_with?("\n") @result = @fragments.map(&:code).join('') end | 
#dynamic_require_severity ⇒ Object
how to handle dynamic requires (:error, :warning, :ignore)
| 118 | # File 'opal/lib/opal/compiler.rb', line 118 compiler_option :dynamic_require_severity, :ignore, valid_values: %i[error warning ignore] | 
#enable_source_location? ⇒ Object
Adds source_location for every method definition
| 135 | # File 'opal/lib/opal/compiler.rb', line 135 compiler_option :enable_source_location, false, as: :enable_source_location? | 
#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.
| 225 226 227 228 229 | # File 'opal/lib/opal/compiler.rb', line 225 def error(msg, line = nil) error = ::Opal::SyntaxError.new(msg) error.location = Opal::OpalBacktraceLocation.new(file, line) raise error end | 
#file ⇒ String
The filename to use for compiling this code. Used for FILE directives as well as finding relative require()
| 80 | # File 'opal/lib/opal/compiler.rb', line 80 compiler_option :file, '(file)' | 
#fragment(str, scope, sexp = nil) ⇒ Object
| 264 265 266 | # File 'opal/lib/opal/compiler.rb', line 264 def fragment(str, scope, sexp = nil) Fragment.new(str, scope, sexp) end | 
#freezing? ⇒ Boolean
stubs out #freeze and #frozen?
| 102 | # File 'opal/lib/opal/compiler.rb', line 102 compiler_option :freezing, true, as: :freezing? | 
#handle_block_given_call(sexp) ⇒ Object
| 457 458 459 460 461 462 463 464 465 466 | # File 'opal/lib/opal/compiler.rb', line 457 def handle_block_given_call(sexp) @scope.uses_block! if @scope.block_name fragment("(#{@scope.block_name} !== nil)", scope, sexp) elsif (scope = @scope.find_parent_def) && scope.block_name fragment("(#{scope.block_name} !== nil)", scope, sexp) else fragment('false', scope, sexp) end end | 
#handlers ⇒ Object
| 364 365 366 | # File 'opal/lib/opal/compiler.rb', line 364 def handlers @handlers ||= Opal::Nodes::Base.handlers end | 
#helper(name) ⇒ Object
Use the given helper
| 299 300 301 | # File 'opal/lib/opal/compiler.rb', line 299 def helper(name) 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.
| 208 209 210 | # File 'opal/lib/opal/compiler.rb', line 208 def helpers @helpers ||= Set.new(%i[breaker slice]) end | 
#in_case ⇒ Object
| 338 339 340 341 342 343 344 | # File 'opal/lib/opal/compiler.rb', line 338 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.
| 330 331 332 333 334 335 336 | # File 'opal/lib/opal/compiler.rb', line 330 def in_while return unless block_given? @while_loop = @scope.push_while result = indent { yield } @scope.pop_while result end | 
#in_while? ⇒ Boolean
Returns true if the parser is curently handling a while sexp, false otherwise.
| 348 349 350 | # File 'opal/lib/opal/compiler.rb', line 348 def in_while? @scope.in_while? end | 
#indent ⇒ 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.
| 306 307 308 309 310 311 312 313 314 | # File 'opal/lib/opal/compiler.rb', line 306 def indent indent = @indent @indent += INDENT @space = "\n#{@indent}" res = yield @indent = indent @space = "\n#{@indent}" res end | 
#inline_operators? ⇒ Object
are operators compiled inline
| 128 | # File 'opal/lib/opal/compiler.rb', line 128 compiler_option :inline_operators, true, as: :inline_operators? | 
#irb? ⇒ Object
compile top level local vars with support for irb style vars
| 113 | # File 'opal/lib/opal/compiler.rb', line 113 compiler_option :irb, false, as: :irb? | 
#method_calls ⇒ Object
Method calls made in this file
| 218 219 220 | # File 'opal/lib/opal/compiler.rb', line 218 def method_calls @method_calls ||= Set.new end | 
#method_missing? ⇒ Boolean
adds method stubs for all used methods in file
| 87 | # File 'opal/lib/opal/compiler.rb', line 87 compiler_option :method_missing, true, as: :method_missing? | 
#operator_helpers ⇒ Object
Operator helpers
| 213 214 215 | # File 'opal/lib/opal/compiler.rb', line 213 def operator_helpers @operator_helpers ||= Set.new end | 
#parse ⇒ Object
| 181 182 183 184 185 186 187 188 189 190 191 192 | # File 'opal/lib/opal/compiler.rb', line 181 def parse @buffer = ::Opal::Parser::SourceBuffer.new(file, 1) @buffer.source = @source @parser = Opal::Parser.default_parser sexp, comments, tokens = re_raise_with_location { @parser.tokenize(@buffer) } @sexp = s(:top, sexp || s(:nil)) @comments = ::Parser::Source::Comment.associate_locations(sexp, comments) @eof_content = EofContent.new(tokens, @source).eof end | 
#parse_comments? ⇒ Object
Adds comments for every method definition
| 140 | # File 'opal/lib/opal/compiler.rb', line 140 compiler_option :parse_comments, false, as: :parse_comments? | 
#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.
| 253 254 255 | # File 'opal/lib/opal/compiler.rb', line 253 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.
| 354 355 356 357 358 359 360 361 362 | # File 'opal/lib/opal/compiler.rb', line 354 def process(sexp, level = :expr) return fragment('', scope) if sexp.nil? if handler = handlers[sexp.type] return handler.new(sexp, level, self).compile_to_fragments else error "Unsupported sexp: #{sexp.type}" end end | 
#re_raise_with_location ⇒ Object
| 231 232 233 234 235 236 237 238 239 240 241 | # File 'opal/lib/opal/compiler.rb', line 231 def re_raise_with_location yield rescue StandardError, ::Opal::SyntaxError => error opal_location = ::Opal.opal_location_from_error(error) opal_location.path = file opal_location.label ||= @source.lines[opal_location.line.to_i - 1].strip new_error = ::Opal::SyntaxError.new(error.) new_error.set_backtrace error.backtrace ::Opal.add_opal_location_to_error(opal_location, new_error) raise new_error end | 
#requirable? ⇒ Object
Prepare the code for future requires
| 123 | # File 'opal/lib/opal/compiler.rb', line 123 compiler_option :requirable, false, as: :requirable? | 
#required_trees ⇒ Object
An array of trees required in this file (typically by calling #require_tree)
| 375 376 377 | # File 'opal/lib/opal/compiler.rb', line 375 def required_trees @required_trees ||= [] end | 
#requires ⇒ Object
An array of requires used in this file
| 369 370 371 | # File 'opal/lib/opal/compiler.rb', line 369 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.
| 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 | # File 'opal/lib/opal/compiler.rb', line 390 def returns(sexp) return returns s(:nil) unless sexp case sexp.type when :undef # undef :method_name always returns nil returns s(:begin, sexp, s(:nil)) when :break, :next, :redo sexp when :yield sexp.updated(:returnable_yield, nil) when :when *when_sexp, then_sexp = *sexp sexp.updated(nil, [*when_sexp, returns(then_sexp)]) when :rescue body_sexp, *resbodies, else_sexp = *sexp resbodies = resbodies.map do |resbody| returns(resbody) end if else_sexp else_sexp = returns(else_sexp) end sexp.updated( nil, [ returns(body_sexp), *resbodies, else_sexp ] ) when :resbody klass, lvar, body = *sexp sexp.updated(nil, [klass, lvar, returns(body)]) when :ensure rescue_sexp, ensure_body = *sexp sexp = sexp.updated(nil, [returns(rescue_sexp), ensure_body]) s(:js_return, sexp) when :begin, :kwbegin # Wrapping last expression with s(:js_return, ...) *rest, last = *sexp sexp.updated(nil, [*rest, returns(last)]) when :while, :until, :while_post, :until_post sexp when :return, :js_return, :returnable_yield sexp when :xstr sexp.updated(nil, [s(:js_return, *sexp.children)]) when :if cond, true_body, false_body = *sexp sexp.updated( nil, [ cond, returns(true_body), returns(false_body) ] ) else s(:js_return, sexp).updated( nil, nil, location: sexp.loc, ) end end | 
#s(type, *children) ⇒ 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.
| 260 261 262 | # File 'opal/lib/opal/compiler.rb', line 260 def s(type, *children) ::Opal::AST::Node.new(type, children) end | 
#source_map ⇒ Opal::SourceMap
Returns a source map that can be used in the browser to map back to original ruby code.
| 199 200 201 | # File 'opal/lib/opal/compiler.rb', line 199 def source_map ::Opal::SourceMap::File.new(@fragments, file, @source) end | 
#tainting? ⇒ Object
stubs out #taint, #untaint and #tainted?
| 108 | # File 'opal/lib/opal/compiler.rb', line 108 compiler_option :tainting, true, as: :tainting? | 
#unique_temp(name) ⇒ Object
Used to generate a unique id name per file. These are used mainly to name method bodies for methods that use blocks.
| 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | # File 'opal/lib/opal/compiler.rb', line 270 def unique_temp(name) name = name.to_s if name && !name.empty? name = name .to_s .gsub('<=>', '$lt_eq_gt') .gsub('===', '$eq_eq_eq') .gsub('==', '$eq_eq') .gsub('=~', '$eq_tilde') .gsub('!~', '$excl_tilde') .gsub('!=', '$not_eq') .gsub('<=', '$lt_eq') .gsub('>=', '$gt_eq') .gsub('=', '$eq') .gsub('?', '$ques') .gsub('!', '$excl') .gsub('/', '$slash') .gsub('%', '$percent') .gsub('+', '$plus') .gsub('-', '$minus') .gsub('<', '$lt') .gsub('>', '$gt') .gsub(/[^\w\$]/, '$') end unique = (@unique += 1) "#{'$' unless name.start_with?('$')}#{name}$#{unique}" 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.
| 246 247 248 | # File 'opal/lib/opal/compiler.rb', line 246 def warning(msg, line = nil) warn "warning: #{msg} -- #{file}:#{line}" end | 
#with_temp ⇒ 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.
| 321 322 323 324 325 326 | # File 'opal/lib/opal/compiler.rb', line 321 def with_temp tmp = @scope.new_temp res = yield tmp @scope.queue_temp tmp res end |