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 = {})
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
-
(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)
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!
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?
-
(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)
def helper(name) self.helpers << name end
def helpers
-
(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
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?
Returns true if the parser is curently handling a while sexp,
def in_while? @scope.in_while? end
def indent(&block)
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
def method_calls @method_calls ||= Set.new end
def operator_helpers
def operator_helpers @operator_helpers ||= Set.new end
def parser_indent
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)
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
An array of trees required in this file
def required_trees @required_trees ||= [] end
def requires
def requires @requires ||= [] end
def returns(sexp)
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)
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)
-
(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
Used to generate a unique id name per file. These are used
def unique_temp "TMP_#{@unique += 1}" end
def warning(msg, line = nil)
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)
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