Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Clean up a load of compiler nodes
Browse files Browse the repository at this point in the history
adambeynon committed Oct 23, 2013
1 parent e273b2a commit f3b11ec
Showing 23 changed files with 484 additions and 431 deletions.
13 changes: 6 additions & 7 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -30,21 +30,21 @@ require 'opal-sprockets'
# do this at the top level). We figure out which file we are including, and
# add it to our require list
class ::Opal::Parser
alias_method :mspec_process_call, :process_call
alias_method :mspec_handle_call, :handle_call

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

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

return f("nil")
return fragment("nil")
end

mspec_process_call sexp, level
mspec_handle_call sexp
end
end

@@ -138,7 +138,6 @@ task :build_specs do

env = SpecEnvironment.new
env.build
env.build_min
end

desc <<-DESC
2 changes: 2 additions & 0 deletions lib/opal/nodes.rb
Original file line number Diff line number Diff line change
@@ -6,10 +6,12 @@
require 'opal/nodes/call_special'
require 'opal/nodes/class'
require 'opal/nodes/iter'
require 'opal/nodes/def'
require 'opal/nodes/if'
require 'opal/nodes/logic'
require 'opal/nodes/definitions'
require 'opal/nodes/yield'
require 'opal/nodes/rescue'
require 'opal/nodes/case'
require 'opal/nodes/super'
require 'opal/nodes/top'
38 changes: 23 additions & 15 deletions lib/opal/nodes/base.rb
Original file line number Diff line number Diff line change
@@ -13,10 +13,12 @@ def self.children(*names)
end
end

def initialize(sexp, level, parser)
attr_reader :compiler

def initialize(sexp, level, compiler)
@sexp = sexp
@level = level
@parser = parser
@compiler = @parser = compiler
end

def type
@@ -46,9 +48,11 @@ def push(*strs)
end
end

def unshift(str)
str = fragment(str) if str.is_a?(String)
@fragments.unshift str
def unshift(*strs)
strs.reverse.each do |str|
str = fragment(str) if str.is_a?(String)
@fragments.unshift str
end
end

def wrap(pre, post)
@@ -61,15 +65,15 @@ def fragment(str)
end

def error(msg)
@parser.error msg
@compiler.error msg
end

def scope
@parser.scope
@compiler.scope
end

def s(*args)
@parser.s(*args)
@compiler.s(*args)
end

def expr?
@@ -84,16 +88,20 @@ def stmt?
@level == :stmt
end

def process(sexp, level = :expr)
@compiler.process sexp, level
end

def expr(sexp)
@parser.process sexp, :expr
@compiler.process sexp, :expr
end

def recv(sexp)
@parser.process sexp, :recv
@compiler.process sexp, :recv
end

def stmt(sexp)
@parser.process sexp, :stmt
@compiler.process sexp, :stmt
end

def expr_or_nil(sexp)
@@ -113,19 +121,19 @@ def add_temp(temp)
end

def helper(name)
@parser.helper name
@compiler.helper name
end

def with_temp(&block)
@parser.with_temp(&block)
@compiler.with_temp(&block)
end

def in_while?
@parser.in_while?
@compiler.in_while?
end

def while_loop
@parser.instance_variable_get(:@while_loop)
@compiler.instance_variable_get(:@while_loop)
end
end
end
94 changes: 94 additions & 0 deletions lib/opal/nodes/call.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
require 'opal/nodes/base'

module Opal
class Parser
class CallNode < Node
children :recvr, :meth, :arglist, :iter

def compile
if handled = compiler.handle_call(@sexp)
push handled
return
end

mid = compiler.mid_to_jsid meth.to_s

compiler.method_calls << meth.to_sym

# trying to access an lvar in irb mode
if using_irb?
with_temp do |tmp|
lvar = meth.intern
lvar = "#{lvar}$" if Parser::RESERVED.include?(lvar)
call = s(:call, s(:self), meth.intern, s(:arglist))
push "((#{tmp} = $opal.irb_vars.#{lvar}) == null ? ", expr(call), " : #{tmp})"
end

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
block = expr(arglist.pop)
elsif iter
block = expr(iter)
end

tmpfunc = scope.new_temp if block
tmprecv = scope.new_temp if splat || tmpfunc

recv_code = recv(recv_sexp)
call_recv = s(:js_tmp, tmprecv || recv_code)

if tmpfunc and !splat
arglist.insert 1, call_recv
end

args = expr(arglist)

if tmprecv
push "(#{tmprecv} = ", recv_code, ")#{mid}"
else
push recv_code, mid
end

if tmpfunc
unshift "(#{tmpfunc} = "
push ", #{tmpfunc}._p = ", block, ", #{tmpfunc})"
end

if splat
push ".apply("
push(tmprecv || recv_code)
push ", ", args, ")"
elsif tmpfunc
push ".call(", args, ")"
else
push "(", args, ")"
end

scope.queue_temp tmpfunc if tmpfunc
end

def recv_sexp
recvr || s(:self)
end

def using_irb?
@compiler.irb_vars? and scope.top? and arglist == s(:arglist) and recvr.nil? and iter.nil?
end
end
end
end
4 changes: 2 additions & 2 deletions lib/opal/nodes/call_special.rb
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ class AttrAssignNode < Node

def compile
sexp = s(:call, recvr, mid, arglist)
push @parser.process(sexp, @level)
push process(sexp, @level)
end
end

@@ -20,7 +20,7 @@ class Match3Node < Node

def compile
sexp = s(:call, lhs, :=~, s(:arglist, rhs))
push @parser.process(sexp, @level)
push process(sexp, @level)
end
end

12 changes: 6 additions & 6 deletions lib/opal/nodes/case.rb
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ class CaseNode < Node
def compile
handled_else = false

@parser.in_case do
compiler.in_case do
if condition
case_stmt[:cond] = true
add_local '$case'
@@ -18,12 +18,12 @@ def compile

case_parts.each_with_index do |wen, idx|
if wen and wen.type == :when
@parser.returns(wen) if needs_closure?
compiler.returns(wen) if needs_closure?
push "else " unless idx == 0
push stmt(wen)
elsif wen # s(:else)
handled_else = true
wen = @parser.returns(wen) if needs_closure?
wen = compiler.returns(wen) if needs_closure?
push "else {", stmt(wen), "}"
end
end
@@ -46,7 +46,7 @@ def case_parts
end

def case_stmt
@parser.instance_variable_get(:@case_stmt)
compiler.case_stmt
end
end

@@ -73,15 +73,15 @@ def compile
end
end

push ") {", @parser.process(body_code, @level), "}"
push ") {", process(body_code, @level), "}"
end

def when_checks
whens.children
end

def case_stmt
@parser.instance_variable_get(:@case_stmt)
compiler.case_stmt
end

def body_code
4 changes: 2 additions & 2 deletions lib/opal/nodes/class.rb
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ module Opal
class Parser
class BaseScopeNode < Node
def in_scope(type, &block)
indent { @parser.in_scope(type, &block) }
indent { compiler.in_scope(type, &block) }
end
end

@@ -97,7 +97,7 @@ def super_code

def body_code
body[1] = s(:nil) unless body[1]
stmt(@parser.returns(body))
stmt(compiler.returns(body))
end
end
end
14 changes: 2 additions & 12 deletions lib/opal/nodes/constants.rb
Original file line number Diff line number Diff line change
@@ -5,13 +5,8 @@ class Parser
class ConstNode < Node
children :name

def initialize(*)
super
@const_missing = true
end

def compile
if @const_missing
if compiler.const_missing?
with_temp do |tmp|
push "((#{tmp} = $scope.#{name}) == null ? $opal.cm('#{name}') : #{tmp})"
end
@@ -45,13 +40,8 @@ def compile
class ConstGetNode < Node
children :base, :name

def initialize(*)
super
@const_missing = true
end

def compile
if @const_missing
if compiler.const_missing?
with_temp do |tmp|
push "((#{tmp} = ("
push expr(base)
188 changes: 188 additions & 0 deletions lib/opal/nodes/def.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
require 'opal/nodes/class'

module Opal
class Parser
# FIXME: needs rewrite
class DefNode < BaseScopeNode
children :recvr, :mid, :args, :stmts

def compile
jsid = compiler.mid_to_jsid mid.to_s
params = nil
scope_name = nil

# opt args if last arg is sexp
opt = args.pop if Sexp === args.last

argc = args.length - 1

# block name (&block)
if args.last.to_s.start_with? '&'
block_name = args.pop.to_s[1..-1].to_sym
argc -= 1
end

# splat args *splat
if args.last.to_s.start_with? '*'
uses_splat = true
if args.last == :*
argc -= 1
else
splat = args[-1].to_s[1..-1].to_sym
args[-1] = splat
argc -= 1
end
end

if compiler.arity_check?
arity_code = arity_check(args, opt, uses_splat, block_name, mid)
end

in_scope(:def) do
scope.mid = mid
scope.defs = true if recvr

if block_name
scope.uses_block!
scope.add_arg block_name
end

yielder = block_name || '$yield'
scope.block_name = yielder

params = process(args)
stmt_code = stmt(stmts)

add_temp 'self = this'

line "#{splat} = $slice.call(arguments, #{argc});" if splat

scope_name = scope.identity

if scope.uses_block?
add_temp "$iter = #{scope_name}._p"
add_temp "#{yielder} = $iter || nil"
end

opt[1..-1].each do |o|
next if o[2][2] == :undefined
line "if (#{compiler.lvar_to_js o[1]} == null) {"
line ' ', expr(o)
line "}"
end if opt

if scope.uses_block?
line "#{scope_name}._p = null;"
end

unshift "\n#{current_indent}", scope.to_vars
line stmt_code

unshift arity_code if arity_code

unshift "var $zuper = $slice.call(arguments, 0);" if scope.uses_zuper

if scope.catch_return
unshift "try {\n"
line "} catch ($returner) { if ($returner === $opal.returner) { return $returner.$v }"
push " throw $returner; }"
end
end

unshift ") {"
unshift(params)
unshift "function("
unshift "#{scope_name} = " if scope_name
line "}"

if recvr
unshift '$opal.defs(', recv(recvr), ", '$#{mid}', "
push ')'
elsif scope.class? and %w(Object BasicObject).include?(scope.name)
wrap "$opal.defn(self, '$#{mid}', ", ')'
elsif scope.class_scope?
scope.methods << "$#{mid}"
unshift "#{scope.proto}#{jsid} = "
elsif scope.iter?
wrap "$opal.defn(self, '$#{mid}', ", ')'
elsif scope.type == :sclass
unshift "self._proto#{jsid} = "
elsif scope.top?
unshift "$opal.Object._proto#{jsid} = "
else
unshift "def#{jsid} = "
end

wrap '(', ', nil)' if expr?
end

# Returns code used in debug mode to check arity of method call
def arity_check(args, opt, splat, block_name, mid)
meth = mid.to_s.inspect

arity = args.size - 1
arity -= (opt.size - 1) if opt
arity -= 1 if splat
arity = -arity - 1 if opt or splat

# $arity will point to our received arguments count
aritycode = "var $arity = arguments.length;"

if arity < 0 # splat or opt args
aritycode + "if ($arity < #{-(arity + 1)}) { $opal.ac($arity, #{arity}, this, #{meth}); }"
else
aritycode + "if ($arity !== #{arity}) { $opal.ac($arity, #{arity}, this, #{meth}); }"
end
end
end

# FIXME: needs rewrite
class ArglistNode < Node
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
26 changes: 13 additions & 13 deletions lib/opal/nodes/definitions.rb
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ class SvalueNode < Node
children :value

def compile
push @parser.process(value, @level)
push process(value, @level)
end
end

@@ -18,7 +18,7 @@ class ScopeNode < Node

def compile
body = self.body || s(:nil)
body = @parser.returns(body) unless scope.class_scope?
body = compiler.returns(body) unless scope.class_scope?
push stmt(body)
end
end
@@ -28,19 +28,19 @@ class UndefNode < Node

# FIXME: we should be setting method to a stub method here
def compile
push "delete #{scope.proto}#{@parser.mid_to_jsid mid[1].to_s}"
push "delete #{scope.proto}#{compiler.mid_to_jsid mid[1].to_s}"
end
end

class AliasNode < Node
children :new_name, :old_name

def new_mid
@parser.mid_to_jsid new_name[1].to_s
compiler.mid_to_jsid new_name[1].to_s
end

def old_mid
@parser.mid_to_jsid old_name[1].to_s
compiler.mid_to_jsid old_name[1].to_s
end

def compile
@@ -58,10 +58,10 @@ class BeginNode < Node

def compile
if !stmt? and body.type == :block
push stmt(@parser.returns(body))
push stmt(compiler.returns(body))
wrap '(function() {', '})()'
else
push @parser.process(body, @level)
push process(body, @level)
end
end
end
@@ -78,7 +78,7 @@ def compile

wrap '(', ')'
else
push @parser.process(body, @level)
push process(body, @level)
wrap '(', ')' unless stmt?
end
end
@@ -88,11 +88,11 @@ class RescueModNode < Node
children :lhs, :rhs

def body
stmt? ? lhs : @parser.returns(lhs)
stmt? ? lhs : compiler.returns(lhs)
end

def rescue_val
stmt? ? rhs : @parser.returns(rhs)
stmt? ? rhs : compiler.returns(rhs)
end

def compile
@@ -110,11 +110,11 @@ def compile
push stmt_join unless idx == 0

if yasgn = find_inline_yield(child)
push @parser.process(yasgn, @level)
push compiler.process(yasgn, @level)
push ";"
end

push @parser.process(child, @level)
push compiler.process(child, @level)
push ";" if child_is_expr?(child)
end
end
@@ -190,7 +190,7 @@ def compile
with_temp do |redo_var|
test_code = js_truthy(test)

@parser.in_while do
compiler.in_while do
while_loop[:closure] = true if wrap_in_closure?
while_loop[:redo_var] = redo_var

4 changes: 2 additions & 2 deletions lib/opal/nodes/helpers.rb
Original file line number Diff line number Diff line change
@@ -15,11 +15,11 @@ def variable(name)
end

def indent(&block)
@parser.indent(&block)
compiler.indent(&block)
end

def current_indent
@parser.parser_indent
compiler.parser_indent
end

def line(*strs)
4 changes: 2 additions & 2 deletions lib/opal/nodes/if.rb
Original file line number Diff line number Diff line change
@@ -43,11 +43,11 @@ def compile
end

def truthy
needs_wrapper? ? @parser.returns(true_body || s(:nil)) : true_body
needs_wrapper? ? compiler.returns(true_body || s(:nil)) : true_body
end

def falsy
needs_wrapper? ? @parser.returns(false_body || s(:nil)) : false_body
needs_wrapper? ? compiler.returns(false_body || s(:nil)) : false_body
end

def needs_wrapper?
4 changes: 2 additions & 2 deletions lib/opal/nodes/iter.rb
Original file line number Diff line number Diff line change
@@ -90,7 +90,7 @@ def args
end

def body
@parser.returns(body_sexp || s(:nil))
compiler.returns(body_sexp || s(:nil))
end

# Maps block args into array of jsid. Adds $ suffix to invalid js
@@ -101,7 +101,7 @@ def args_to_params(sexp)
result = []
sexp.each do |arg|
if arg[0] == :lasgn
ref = @parser.lvar_to_js(arg[1])
ref = compiler.lvar_to_js(arg[1])
scope.add_arg ref
result << ref
elsif arg[0] == :array
2 changes: 1 addition & 1 deletion lib/opal/nodes/literal.rb
Original file line number Diff line number Diff line change
@@ -281,7 +281,7 @@ def compile

child = child.to_sym
push ', ' unless idx == 0
child = @parser.lvar_to_js child
child = compiler.lvar_to_js child
scope.add_arg child
push child.to_s
end
4 changes: 2 additions & 2 deletions lib/opal/nodes/logic.rb
Original file line number Diff line number Diff line change
@@ -211,7 +211,7 @@ def compile
end

def compile_call
mid = @parser.mid_to_jsid value[2].to_s
mid = compiler.mid_to_jsid value[2].to_s
recv = value[1] ? expr(value[1]) : 'self'

push '(', recv, "#{mid} || ", recv
@@ -236,7 +236,7 @@ def compile_super
end

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

12 changes: 6 additions & 6 deletions lib/opal/nodes/rescue.rb
Original file line number Diff line number Diff line change
@@ -7,16 +7,16 @@ class EnsureNode < Node

def compile
push "try {"
line @parser.process(body_sexp, @level)
line compiler.process(body_sexp, @level)
line "} finally {"
line @parser.process(ensr_sexp, @level)
line compiler.process(ensr_sexp, @level)
line "}"

wrap '(function() {', '; })()' if wrap_in_closure?
end

def body_sexp
wrap_in_closure? ? @parser.returns(begn) : begn
wrap_in_closure? ? compiler.returns(begn) : begn
end

def ensr_sexp
@@ -35,14 +35,14 @@ def compile
handled_else = false

push "try {"
line(indent { @parser.process(body_code, @level) })
line(indent { process(body_code, @level) })
line "} catch ($err) {"

children[1..-1].each_with_index do |child, idx|
handled_else = true unless child.type == :resbody

push "else " unless idx == 0
push(indent { @parser.process(child, @level) })
push(indent { process(child, @level) })
end

# if no resbodys capture our error, then rethrow
@@ -82,7 +82,7 @@ def compile
push expr(variable), ';'
end

line @parser.process(rescue_body, @level)
line process(rescue_body, @level)
line "}"
end

63 changes: 63 additions & 0 deletions lib/opal/nodes/top.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
require 'opal/nodes/class'

module Opal
class Parser
# Generates code for an entire file, i.e. the base sexp
class TopNode < BaseScopeNode
def compile
push version_comment

line "(function($opal) {"

in_scope(:top) do
body_code = stmt(stmts)
body_code = [body_code] unless body_code.is_a?(Array)

add_temp 'self = $opal.top'
add_temp '$scope = $opal'
add_temp 'nil = $opal.nil'

add_used_helpers
line scope.to_vars

compile_method_stubs
compile_irb_vars

line body_code
end

line "})(Opal);\n"
end

def stmts
sexp = @sexp || s(:nil)
scope = s(:scope, sexp)
scope.line = sexp.line
scope
end

def compile_irb_vars
if compiler.irb_vars?
line "if (!$opal.irb_vars) { $opal.irb_vars = {}; }"
end
end

def add_used_helpers
helpers = compiler.helpers.to_a
helpers.to_a.each { |h| add_temp "$#{h} = $opal.#{h}" }
end

def compile_method_stubs
if compiler.method_missing?
calls = compiler.method_calls
stubs = calls.to_a.map { |k| "'$#{k}'" }.join(', ')
line "$opal.add_stubs([#{stubs}]);"
end
end

def version_comment
"/* Generated by Opal #{Opal::VERSION} */"
end
end
end
end
4 changes: 2 additions & 2 deletions lib/opal/nodes/variables.rb
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ class LocalVariableNode < Node
children :var_name

def using_irb?
@parser.instance_variable_get(:@irb_vars) and scope.top?
compiler.irb_vars? and scope.top?
end

def compile
@@ -23,7 +23,7 @@ class LocalAssignNode < Node
children :var_name, :value

def using_irb?
@parser.instance_variable_get(:@irb_vars) and scope.top?
compiler.irb_vars? and scope.top?
end

def compile
2 changes: 1 addition & 1 deletion lib/opal/nodes/yield.rb
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ def compile_call(children, level)
push expr(children.first)
wrap "$opal.$yield1(#{block_name}, ", ')'
else
push @parser.process_arglist(children, level)
push expr(s(:arglist, *children))

if uses_splat?(children)
wrap "$opal.$yieldX(#{block_name}, ", ')'
390 changes: 48 additions & 342 deletions lib/opal/parser.rb

Large diffs are not rendered by default.

19 changes: 11 additions & 8 deletions lib/opal/require_parser.rb
Original file line number Diff line number Diff line change
@@ -17,20 +17,23 @@ def parse source, options = {}
super source, options
end

def process_call sexp, level
if sexp[1] == :require
return handle_require sexp[2][1]
elsif sexp[1] == :autoload and @scope.class_scope?
return handle_require sexp[2][2]
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
super sexp
end

super sexp, level
end

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

def handle_require_sexp(sexp)
10 changes: 5 additions & 5 deletions lib/opal/target_scope.rb
Original file line number Diff line number Diff line change
@@ -133,19 +133,19 @@ def to_vars
result = str
end

f(result)
fragment(result)
end

def f(code, sexp = nil)
@parser.f code
def fragment(code, sexp = nil)
@parser.fragment code
end

# Generates code for this module to donate methods
def to_donate_methods
if should_donate? and !@methods.empty?
f("%s;$opal.donate(self, [%s]);" % [@parser.parser_indent, @methods.map(&:inspect).join(', ')])
fragment("%s;$opal.donate(self, [%s]);" % [@parser.parser_indent, @methods.map(&:inspect).join(', ')])
else
f("")
fragment("")
end
end

2 changes: 1 addition & 1 deletion opal.gemspec
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
s.add_development_dependency 'uglifier'
s.add_development_dependency 'rake'
s.add_development_dependency 'racc'
s.add_development_dependency 'opal-sprockets', '~> 0.1.1'
s.add_development_dependency 'opal-sprockets', '~> 0.2.1'
s.add_development_dependency 'rspec', '~> 2.14'
s.add_development_dependency 'octokit', '~> 2.4.0'
end

0 comments on commit f3b11ec

Please sign in to comment.