Skip to content

Commit

Permalink
Compiler iters using compile nodes (needs a rewrite)
Browse files Browse the repository at this point in the history
  • Loading branch information
adambeynon committed Oct 23, 2013
1 parent b003431 commit e273b2a
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 115 deletions.
1 change: 1 addition & 0 deletions lib/opal/nodes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require 'opal/nodes/call'
require 'opal/nodes/call_special'
require 'opal/nodes/class'
require 'opal/nodes/iter'
require 'opal/nodes/if'
require 'opal/nodes/logic'
require 'opal/nodes/definitions'
Expand Down
118 changes: 118 additions & 0 deletions lib/opal/nodes/iter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
require 'opal/nodes/class'

module Opal
class Parser
# FIXME: this needs a rewrite very urgently
class IterNode < BaseScopeNode
children :args_sexp, :body_sexp

def compile
# opt args are last (if present) and are a s(:block)
if args.last.is_a?(Sexp) and args.last.type == :block
opt_args = args.pop
opt_args.shift
end

# does this iter define a block_pass
if args.last.is_a?(Sexp) and args.last.type == :block_pass
block_arg = args.pop
block_arg = block_arg[1][1].to_sym
end

# find any splat args
if args.last.is_a?(Sexp) and args.last.type == :splat
splat = args.last[1][1]
args.pop
len = args.length
end

params = args_to_params(args[1..-1])
params << splat if splat

to_vars = identity = nil

in_scope(:iter) do
identity = scope.identify!
add_temp "self = #{identity}._s || this"


args[1..-1].each_with_index do |arg, idx|
if arg.type == :lasgn
arg = arg[1]
arg = "#{arg}$" if Parser::RESERVED.include?(arg.to_s)

if opt_args and current_opt = opt_args.find { |s| s[1] == arg.to_sym }
push "if (#{arg} == null) #{arg} = ", expr(current_opt[2]), ";"
else
push "if (#{arg} == null) #{arg} = nil;"
end
elsif arg.type == :array
arg[1..-1].each_with_index do |_arg, _idx|
_arg = _arg[1]
_arg = "#{_arg}$" if Parser::RESERVED.include?(_arg.to_s)
push "#{_arg} = #{params[idx]}#{_idx};"
end
else
raise "Bad block arg type"
end
end

if splat
scope.add_arg splat
push "#{splat} = $slice.call(arguments, #{len - 1});"
end

if block_arg
scope.block_name = block_arg
scope.add_temp block_arg
scope_name = scope.identify!

line "#{block_arg} = #{scope_name}._p || nil, #{scope_name}._p = null;"
end

line stmt(body)
to_vars = scope.to_vars
end

unshift to_vars
unshift "function(#{params.join ', '}) {"
wrap "(#{identity} = ", "}, #{identity}._s = self, #{identity})"
end

def args
if Fixnum === args_sexp or args_sexp.nil?
s(:array)
elsif args_sexp.type == :lasgn
s(:array, args_sexp)
else
args_sexp[1]
end
end

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

# Maps block args into array of jsid. Adds $ suffix to invalid js
# identifiers.
#
# s(:args, parts...) => ["a", "b", "break$"]
def args_to_params(sexp)
result = []
sexp.each do |arg|
if arg[0] == :lasgn
ref = @parser.lvar_to_js(arg[1])
scope.add_arg ref
result << ref
elsif arg[0] == :array
result << scope.next_temp
else
raise "Bad js_block_arg: #{arg[0]}"
end
end

result
end
end
end
end
117 changes: 2 additions & 115 deletions lib/opal/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ def add_handler(klass, *types)
# if
add_handler IfNode, :if

add_handler IterNode, :iter

# rescue/ensure
add_handler EnsureNode, :ensure
add_handler RescueNode, :rescue
Expand Down Expand Up @@ -544,121 +546,6 @@ def js_block_given(sexp, level)
end
end

# A block/iter with embeded call. Compiles into function
# s(:iter, block_args [, body) => (function() { ... })
def process_iter(sexp, level)
args, body = sexp

body ||= s(:nil)
body = returns body
code = []
params = nil
scope_name = nil
identity = nil
to_vars = nil

args = nil if Fixnum === args # argh
args ||= s(:masgn, s(:array))
args = args.first == :lasgn ? s(:array, args) : args[1]

# opt args are last, if present, and are a [:block]
if args.last.is_a?(Sexp) and args.last[0] == :block
opt_args = args.pop
opt_args.shift
end

if args.last.is_a?(Sexp) and args.last[0] == :block_pass
block_arg = args.pop
block_arg = block_arg[1][1].to_sym
end

if args.last.is_a?(Sexp) and args.last[0] == :splat
splat = args.last[1][1]
args.pop
len = args.length
end

indent do
in_scope(:iter) do
identity = @scope.identify!
@scope.add_temp "self = #{identity}._s || this"

params = js_block_args(args[1..-1])

args[1..-1].each_with_index do |arg, idx|
if arg[0] == :lasgn
arg = arg[1]
arg = "#{arg}$" if RESERVED.include? arg.to_s

if opt_args and current_opt = opt_args.find { |s| s[1] == arg.to_sym }
code << [f("if (#{arg} == null) #{arg} = ", sexp), process(current_opt[2]), f(";\n#{@indent}", sexp)]
else
code << f("if (#{arg} == null) #{arg} = nil;\n#{@indent}", sexp)
end
elsif arg[0] == :array
arg[1..-1].each_with_index do |_arg, midx|
_arg = _arg[1]
_arg = "#{_arg}$" if RESERVED.include? _arg.to_s
code << f("#{_arg} = #{params[idx]}[#{midx}];\n#{@indent}")
end
else
raise "Bad block_arg type: #{arg[0]}"
end
end

if splat
@scope.add_arg splat
params << splat
code << f("#{splat} = $slice.call(arguments, #{len - 1});", sexp)
end

if block_arg
@scope.block_name = block_arg
@scope.add_temp block_arg
scope_name = @scope.identify!

blk = []
blk << f("\n#@indent#{block_arg} = #{scope_name}._p || nil, #{scope_name}._p = null;\n#@indent", sexp)

code.unshift blk
end

code << f("\n#@indent", sexp)
code << process(body, :stmt)

to_vars = [f("\n#@indent", sexp), @scope.to_vars, f("\n#@indent", sexp)]
end
end

itercode = [f("function(#{params.join ', '}) {\n", sexp), to_vars, code, f("\n#@indent}", sexp)]

itercode.unshift f("(#{identity} = ", sexp)
itercode << f(", #{identity}._s = self, #{identity})", sexp)

itercode
end

# Maps block args into array of jsid. Adds $ suffix to invalid js
# identifiers.
#
# s(:args, parts...) => ["a", "b", "break$"]
def js_block_args(sexp)
result = []
sexp.each do |arg|
if arg[0] == :lasgn
ref = lvar_to_js(arg[1])
@scope.add_arg ref
result << ref
elsif arg[0] == :array
result << @scope.next_temp
else
raise "Bad js_block_arg: #{arg[0]}"
end
end

result
end

# s(:call, recv, :mid, s(:arglist))
# s(:call, nil, :mid, s(:arglist))
def process_call(sexp, level)
Expand Down

0 comments on commit e273b2a

Please sign in to comment.