Skip to content

Commit

Permalink
Move require() and auotload() resolve logic into DependencyResolver c…
Browse files Browse the repository at this point in the history
…lass
  • Loading branch information
adambeynon committed Oct 24, 2013
1 parent 515efb9 commit 0a30293
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 125 deletions.
14 changes: 7 additions & 7 deletions Rakefile
Expand Up @@ -29,22 +29,22 @@ require 'opal-sprockets'
# We can't do this at runtime, so we hijack the method (and make sure we only
# do this at the top level). We figure out which file we are including, and
# add it to our require list
class Opal::Compiler
alias_method :mspec_handle_call, :handle_call
class Opal::Nodes::CallNode
alias_method :mspec_handle_special, :handle_special

def handle_call(sexp)
if sexp[2] == :language_version and @scope.top?
lang_type = sexp[3][2][1]
def handle_special
if meth == :language_version and scope.top?
lang_type = arglist[2][1]
target = "rubyspec/language/versions/#{lang_type}_1.9"

if File.exist?(target)
@requires << target
compiler.requires << target
end

return fragment("nil")
end

mspec_handle_call sexp
mspec_handle_special
end
end

Expand Down
4 changes: 4 additions & 0 deletions doc/home.md
@@ -0,0 +1,4 @@
# Opal

[Method Missing](method_missing.md)

66 changes: 1 addition & 65 deletions lib/opal/compiler.rb
Expand Up @@ -212,75 +212,11 @@ def handlers
@handlers ||= Opal::Nodes::Base.handlers
end

# Handle "special" method calls, e.g. require(). Subclasses can override
# this method. If this method returns nil, then the method will continue
# to be generated by CallNode.
def handle_call(sexp)
case sexp[2]
when :require
return handle_require(sexp[3][1])
when :autoload
if @scope.class_scope?
return handle_require(sexp[3][2])
end
else
nil
end
end

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

def handle_require(sexp)
str = handle_require_sexp sexp
requires << str unless str.nil?
fragment("", sexp)
end

def handle_require_sexp(sexp)
type = sexp.shift

if type == :str
return sexp[0]
elsif type == :call
recv, meth, args = sexp
parts = args[1..-1].map { |s| handle_require_sexp s }

if recv == [:const, :File]
if meth == :expand_path
return handle_expand_path(*parts)
elsif meth == :join
return handle_expand_path parts.join("/")
elsif meth == :dirname
return handle_expand_path parts[0].split("/")[0...-1].join("/")
end
end
end

case dynamic_require_severity
when :error
error "Cannot handle dynamic require"
when :warning
warning "Cannot handle dynamic require"
end
end

def handle_expand_path(path, base = '')
"#{base}/#{path}".split("/").inject([]) do |p, part|
if part == ''
# we had '//', so ignore
elsif part == '..'
p.pop
else
p << part
end

p
end.join "/"
end

# 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
Expand Down Expand Up @@ -357,7 +293,7 @@ def returns(sexp)
end
end

def js_block_given(sexp, level)
def handle_block_given_call(sexp)
@scope.uses_block!
if @scope.block_name
fragment("(#{@scope.block_name} !== nil)", sexp)
Expand Down
1 change: 1 addition & 0 deletions lib/opal/nodes.rb
Expand Up @@ -22,3 +22,4 @@
require 'opal/nodes/array'
require 'opal/nodes/defined'
require 'opal/nodes/masgn'
require 'opal/nodes/arglist'
56 changes: 56 additions & 0 deletions lib/opal/nodes/arglist.rb
@@ -0,0 +1,56 @@
require 'opal/nodes/base'

module Opal
module Nodes
# FIXME: needs rewrite
class ArglistNode < Base
handle :arglist

def compile
code, work = [], []

children.each do |current|
splat = current.first == :splat
arg = expr(current)

if splat
if work.empty?
if code.empty?
code << fragment("[].concat(")
code << arg
code << fragment(")")
else
code += ".concat(#{arg})"
end
else
if code.empty?
code << [fragment("["), work, fragment("]")]
else
code << [fragment(".concat(["), work, fragment("])")]
end

code << [fragment(".concat("), arg, fragment(")")]
end

work = []
else
work << fragment(", ") unless work.empty?
work << arg
end
end

unless work.empty?
join = work

if code.empty?
code = join
else
code << fragment(".concat(") << join << fragment(")")
end
end

push(*code)
end
end
end
end
135 changes: 83 additions & 52 deletions lib/opal/nodes/call.rb
Expand Up @@ -8,7 +8,7 @@ class CallNode < Base
children :recvr, :meth, :arglist, :iter

def compile
if handled = compiler.handle_call(@sexp)
if handled = self.handle_special
push handled
return
end
Expand All @@ -28,17 +28,6 @@ def compile
return
end

case meth
when :block_given?
return push @compiler.js_block_given(@sexp, @level)
when :__method__, :__callee__
if scope.def?
return push(scope.mid.to_s.inspect)
else
return push("nil")
end
end

splat = arglist[1..-1].any? { |a| a.first == :splat }

if Sexp === arglist.last and arglist.last.type == :block_pass
Expand Down Expand Up @@ -90,56 +79,98 @@ def recv_sexp
def using_irb?
@compiler.irb? and scope.top? and arglist == s(:arglist) and recvr.nil? and iter.nil?
end
end

# FIXME: needs rewrite
class ArglistNode < Base
handle :arglist
# Handle "special" method calls, e.g. require(). Subclasses can override
# this method. If this method returns nil, then the method will continue
# to be generated by CallNode.
def handle_special
case meth
when :require then handle_require
when :autoload then handle_autoload
when :block_given? then handle_block_given
when :__method__, :__callee__ then handle_callee
end
end

def compile
code, work = [], []

children.each do |current|
splat = current.first == :splat
arg = expr(current)

if splat
if work.empty?
if code.empty?
code << fragment("[].concat(")
code << arg
code << fragment(")")
else
code += ".concat(#{arg})"
end
else
if code.empty?
code << [fragment("["), work, fragment("]")]
else
code << [fragment(".concat(["), work, fragment("])")]
end
def handle_require
str = DependencyResolver.new(compiler, arglist[1]).resolve
compiler.requires << str unless str.nil?
fragment ''
end

code << [fragment(".concat("), arg, fragment(")")]
end
def handle_autoload
if scope.class_scope?
str = DependencyResolver.new(compiler, arglist[2]).resolve
compiler.requires << str unless str.nil?
fragment ''
end
end

work = []
else
work << fragment(", ") unless work.empty?
work << arg
end
def handle_block_given
compiler.handle_block_given_call @sexp
end

def handle_callee
if scope.def?
fragment scope.mid.to_s.inspect
else
fragment 'nil'
end
end

class DependencyResolver
def initialize(compiler, sexp)
@compiler = compiler
@sexp = sexp
end

unless work.empty?
join = work
def resolve
handle_part @sexp
end

def handle_part(sexp)
type = sexp.type

if type == :str
return sexp[1]
elsif type == :call
_, recv, meth, args = sexp

parts = args[1..-1].map { |s| handle_part s }

if code.empty?
code = join
else
code << fragment(".concat(") << join << fragment(")")
if recv == [:const, :File]
if meth == :expand_path
return expand_path(*parts)
elsif meth == :join
return expand_path parts.join('/')
elsif meth == :dirname
return expand_path parts[0].split('/')[0...-1].join('/')
end
end
end

msg = "Cannot handle dynamic require"
case @compiler.dynamic_require_severity
when :error
@compiler.error msg
when :warning
@compiler.warning msg
end
end

push(*code)
def expand_path(path, base = '')
"#{base}/#{path}".split("/").inject([]) do |p, part|
if part == ''
# we had '//', so ignore
elsif part == '..'
p.pop
else
p << part
end

p
end.join "/"
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/opal/nodes/defined.rb
Expand Up @@ -54,7 +54,7 @@ def compile_super
end

def compile_yield
push compiler.js_block_given(@sexp, @level)
push compiler.handle_block_given_call(@sexp)
wrap '((', ') != null ? "yield" : nil)'
end

Expand Down

0 comments on commit 0a30293

Please sign in to comment.