class Opal::Compiler


compiler.source_map # => #<SourceMap:>
compiler.compile
compiler = Opal::Compiler.new(“”)
@example Source Maps
compiler.result # => “javascript code”
compiler.compile
compiler = Opal::Compiler.new(“ruby_code”)
@example Accessing result
# => “javascript code”
Opal::Compiler.new(“ruby code”).compile
@example
javascript.
code, and then uses {Opal::Node} to step through the sexp to generate valid
This class uses {Opal::Parser} to gather the sexp syntax tree for the ruby
{Opal::Compiler} is the main class used to compile ruby to javascript code.

def self.compiler_option(name, default_value, options = {})

defines a compiler option, also creating method of form 'name?'
def self.compiler_option(name, default_value, options = {})
  mid          = options[:as]
  valid_values = options[: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

def compile

Returns:
  • (String) - javascript code
def compile
  @parser = Parser.new
  parsed = begin
    @parser.parse(@source, self.file)
  rescue => error
    raise SyntaxError, error.message, error.backtrace
  end
  @sexp = s(:top, parsed || s(:nil))
  @eof_content = @parser.lexer.eof_content
  @fragments = process(@sexp).flatten
  @result = @fragments.map(&:code).join('')
rescue Exception => error
  message = "An error occurred while compiling: #{self.file}\n#{error.message}"
  raise error.class, message, error.backtrace
end

def error(msg, line = nil)

the message and raises it.
method simply appends the filename and curent line number onto
This is called when a parsing/processing error occurs. This
def error(msg, line = nil)
  raise SyntaxError, "#{msg} :#{file}:#{line}"
end

def fragment(str, scope, sexp = nil)

def fragment(str, scope, sexp = nil)
  Fragment.new(str, scope, sexp)
end

def handle_block_given_call(sexp)

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 and scope.block_name
    fragment("(#{scope.block_name} !== nil)", scope, sexp)
  else
    fragment("false", scope, sexp)
  end
end

def handlers

def handlers
  @handlers ||= Opal::Nodes::Base.handlers
end

def has_break!

a `#has_break?(&block)` block.
Marks the current block has having detected a break, but only from inside
def has_break!
  @break_detected = true if @break_detected == false
end

def has_break?

Returns:
  • (Boolean) - whether a block has been detected
def has_break?
  return @break_detected unless block_given?
  @break_detected = false
  result = yield
  detected = @break_detected
  @break_detected = nil
  detected
end

def helper(name)

Use the given helper
def helper(name)
  self.helpers << name
end

def helpers

Returns:
  • (Set) -
def helpers
  @helpers ||= Set.new([:breaker, :slice])
end

def in_case

def in_case
  return unless block_given?
  old = @case_stmt
  @case_stmt = {}
  yield
  @case_stmt = old
end

def in_ensure

def in_ensure
  return unless block_given?
  @in_ensure = true
  result = yield
  @in_ensure = false
  result
end

def in_ensure?

def in_ensure?
  @in_ensure
end

def in_while

scope's while stack so we know how to handle break, next etc.
Used when we enter a while statement. This pushes onto the current
def in_while
  return unless block_given?
  @while_loop = @scope.push_while
  result = yield
  @scope.pop_while
  result
end

def in_while?

false otherwise.
Returns true if the parser is curently handling a while sexp,
def in_while?
  @scope.in_while?
end

def indent(&block)

code after reverting the indent.
adding an extra layer of indent, and then returning the resulting
To keep code blocks nicely indented, this will yield a block after
def indent(&block)
  indent = @indent
  @indent += INDENT
  @space = "\n#@indent"
  res = yield
  @indent = indent
  @space = "\n#@indent"
  res
end

def initialize(source, options = {})

def initialize(source, options = {})
  @source = source
  @indent = ''
  @unique = 0
  @options = options
end

def method_calls

Method calls made in this file
def method_calls
  @method_calls ||= Set.new
end

def operator_helpers

Operator helpers
def operator_helpers
  @operator_helpers ||= Set.new
end

def parser_indent

readable.
scope indent. The indent is used to keep generated code easily
Instances of `Scope` can use this to determine the current
def parser_indent
  @indent
end

def process(sexp, level = :expr)

and compiling it to fragments.
Process the given sexp by creating a node instance, based on its type,
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
    raise "Unsupported sexp: #{sexp.type}"
  end
end

def required_trees

(typically by calling #require_tree)
An array of trees required in this file
def required_trees
  @required_trees ||= []
end

def requires

An array of requires used in this file
def requires
  @requires ||= []
end

def returns(sexp)

default action if no special case is required.
sexps can just be added into a `s(:return) sexp`, so that is the
alterned/new sexps are returned and should be used instead. Most
Sexps that need to be returned are passed to this method, and the

need to be returned instead.
instead the "truthy" and "falsy" parts of the if statement both
example, `if` statemented cannot be returned in javascript, so
javascript any ruby, some sexps need to be handled specially. For
in the compiled javascript. Due to syntax differences between
The last sexps in method bodies, for example, need to be returned
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
    return_sexp = s(:js_return, sexp)
    return_sexp.source = sexp.source
    return_sexp
  end
end

def s(*parts)

of sexps does change.
returns an array, it must be used incase the internal structure
Create a new sexp using the given parts. Even though this just
def s(*parts)
  Sexp.new(parts)
end

def source_map(source_file = nil)

Returns:
  • (Opal::SourceMap) -

Parameters:
  • source_file (String) -- optional source_file to reference ruby source
def source_map(source_file = nil)
  Opal::SourceMap.new(@fragments, source_file || self.file)
end

def unique_temp

mainly to name method bodies for methods that use blocks.
Used to generate a unique id name per file. These are used
def unique_temp
  "TMP_#{@unique += 1}"
end

def warning(msg, line = nil)

the message and issues a warning.
method simply appends the filename and curent line number onto
This is called when a parsing/processing warning occurs. This
def warning(msg, line = nil)
  warn "WARNING: #{msg} -- #{file}:#{line}"
end

def with_temp(&block)

numbers of variables needed at runtime.
finished. Variables are queued once finished with to save the
while the block is yielding, and queue it back up once it is
generated code, and this method will assign (or reuse) on
Temporary varibales will be needed from time to time in the
def with_temp(&block)
  tmp = @scope.new_temp
  res = yield tmp
  @scope.queue_temp tmp
  res
end