Skip to content

Commit

Permalink
Some more node generation
Browse files Browse the repository at this point in the history
  • Loading branch information
adambeynon committed Oct 21, 2013
1 parent 000d364 commit 6b05c8d
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 71 deletions.
18 changes: 14 additions & 4 deletions lib/opal/nodes/base.rb
Expand Up @@ -36,9 +36,11 @@ def compile
raise "Not Implemented"
end

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

def unshift(str)
Expand All @@ -55,6 +57,10 @@ def fragment(str)
Opal::Parser::Fragment.new str, @sexp
end

def error(msg)
@parser.error msg
end

def scope
@parser.scope
end
Expand Down Expand Up @@ -116,7 +122,7 @@ def add_temp(temp)
end

def helper(name)
@parser.instance_variable_get(:@helpers)[name] = true
@parser.helper name
end

def with_temp(&block)
Expand All @@ -126,6 +132,10 @@ def with_temp(&block)
def in_while?
@parser.in_while?
end

def while_loop
@parser.instance_variable_get(:@while_loop)
end
end
end
end
60 changes: 60 additions & 0 deletions lib/opal/nodes/literal.rb
Expand Up @@ -125,5 +125,65 @@ def compile
push ", true)"
end
end

class HashNode < Node
def keys_and_values
keys, values = [], []

children.each_with_index do |obj, idx|
if idx.even?
keys << obj
else
values << obj
end
end

[keys, values]
end

def simple_keys?(keys)
keys.all? { |key| [:sym, :str].include? key.type }
end

def compile
keys, values = keys_and_values

if simple_keys? keys
compile_hash2 keys, values
else
compile_hash
end
end

def compile_hash
helper :hash

children.each_with_index do |child, idx|
push ', ' unless idx == 0
push expr(child)
end

wrap '$hash(', ')'
end

def compile_hash2(keys, values)
hash_obj, hash_keys = {}, []
helper :hash2

keys.size.times do |idx|
key = keys[idx][1].to_s.inspect
hash_keys << key unless hash_obj.include? key
hash_obj[key] = expr(values[idx])
end

hash_keys.each_with_index do |key, idx|
push ', ' unless idx == 0
push "#{key}: "
push hash_obj[key]
end

wrap "$hash2([#{hash_keys.join ', '}], {", "})"
end
end
end
end
31 changes: 29 additions & 2 deletions lib/opal/nodes/logic.rb
Expand Up @@ -13,6 +13,34 @@ def compile
end
end

class BreakNode < Node
children :value

def compile
if in_while?
compile_while
elsif scope.iter?
compile_iter
else
error "void value expression: cannot use break outside of iter/while"
end
end

def compile_while
if while_loop[:closure]
push "return ", expr_or_nil(value)
else
push "break;"
end
end

def compile_iter
error "break must be used as a statement" unless stmt?
push expr_or_nil(value)
wrap "return ($breaker.$v = ", ", $breaker)"
end
end

class NotNode < Node
children :value

Expand All @@ -35,8 +63,7 @@ def compile
if empty_splat?
push '[]'
elsif value.type == :sym
push expr(value)
wrap '[', ']'
push '[', expr(value), ']'
else
push recv(value)
end
Expand Down
76 changes: 11 additions & 65 deletions lib/opal/parser.rb
Expand Up @@ -52,6 +52,7 @@ def self.add_handler(klass, *types)
add_handler DynamicRegexpNode, :dregx
add_handler ExclusiveRangeNode, :dot2
add_handler InclusiveRangeNode, :dot3
add_handler HashNode, :hash

# variables
add_handler LocalVariableNode, :lvar
Expand All @@ -75,6 +76,7 @@ def self.add_handler(klass, *types)

# control flow
add_handler NextNode, :next
add_handler BreakNode, :break
add_handler NotNode, :not
add_handler SplatNode, :splat
add_handler OrNode, :or
Expand All @@ -101,11 +103,6 @@ def parse(str, options = {})
@indent = ''
@unique = 0

@helpers = {
:breaker => true,
:slice => true
}

# options
@file = options[:file] || '(file)'
@source_file = options[:source_file] || @file
Expand All @@ -115,6 +112,7 @@ def parse(str, options = {})
@irb_vars = (options[:irb] == true)

@method_calls = Set.new
@helpers = Set.new([:breaker, :slice])

@fragments = self.top(@sexp).flatten

Expand Down Expand Up @@ -220,6 +218,11 @@ def unique_temp
"TMP_#{@unique += 1}"
end

# Use the given helper
def helper(name)
@helpers << name
end

# Generate the code for the top level sexp, i.e. the root sexp
# for a file. This is used directly by `#parse`. It pushes a
# ":top" scope onto the stack and handles the passed in sexp.
Expand Down Expand Up @@ -247,7 +250,7 @@ def top(sexp, options = {})
"$scope = $opal",
"nil = $opal.nil"

@helpers.keys.each { |h| @scope.add_temp "$#{h} = $opal.#{h}" }
@helpers.to_a.each { |h| @scope.add_temp "$#{h} = $opal.#{h}" }

vars = [f(INDENT, sexp), @scope.to_vars, f("\n", sexp)]

Expand Down Expand Up @@ -945,7 +948,7 @@ def process_class(sexp, level)
body[1] = s(:nil) unless body[1]

code = []
@helpers[:klass] = true
helper :klass

if Symbol === cid or String === cid
base = process s(:self)
Expand Down Expand Up @@ -990,7 +993,7 @@ def process_class(sexp, level)
def process_module(sexp, level)
cid, body = sexp
code = []
@helpers[:module] = true
helper :module

if Symbol === cid or String === cid
base = process(s(:self))
Expand Down Expand Up @@ -1230,51 +1233,6 @@ def process_array(sexp, level)
code
end

# s(:hash, key1, val1, key2, val2...)
def process_hash(sexp, level)
keys = []
vals = []

sexp.each_with_index do |obj, idx|
if idx.even?
keys << obj
else
vals << obj
end
end

if keys.all? { |k| [:sym, :str].include? k[0] }
hash_obj = {}
hash_keys = []
keys.size.times do |i|
k = keys[i][1].to_s.inspect
hash_keys << k unless hash_obj.include? k
hash_obj[k] = process(vals[i])
end

result = []
@helpers[:hash2] = true

hash_keys.each do |k|
result << f(", ", sexp) unless result.empty?
result << f("#{k}: ", sexp)
result << hash_obj[k]
end

[f("$hash2([#{hash_keys.join ', '}], {", sexp), result, f("})", sexp)]
else
@helpers[:hash] = true
result = []

sexp.each do |p|
result << f(", ", p) unless result.empty?
result << process(p)
end

[f("$hash(", sexp), result, f(")", sexp)]
end
end

# s(:while, exp, block, true)
def process_while(sexp, level)
expr, stmt = sexp
Expand Down Expand Up @@ -1647,18 +1605,6 @@ def handle_yield_call(sexp, level)
end
end

def process_break(sexp, level)
val = sexp.empty? ? f('nil', sexp) : process(sexp[0])
if in_while?
@while_loop[:closure] ? [f("return ", sexp), val, f("", sexp)] : f("break;", sexp)
elsif @scope.iter?
error "break must be used as a statement" unless level == :stmt
[f("return ($breaker.$v = ", sexp), val, f(", $breaker)", sexp)]
else
error "void value expression: cannot use break outside of iter/while"
end
end

# s(:case, expr, when1, when2, ..)
def process_case(exp, level)
pre, code = [], []
Expand Down

0 comments on commit 6b05c8d

Please sign in to comment.