-
-
Notifications
You must be signed in to change notification settings - Fork 330
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add compiler nodes for rescue/ensure and case/when
- Loading branch information
1 parent
4e9d955
commit 6791fa8
Showing
4 changed files
with
211 additions
and
163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
require 'opal/nodes/base' | ||
|
||
module Opal | ||
class Parser | ||
class CaseNode < Node | ||
children :condition | ||
|
||
def compile | ||
handled_else = false | ||
|
||
@parser.in_case do | ||
if condition | ||
case_stmt[:cond] = true | ||
add_local '$case' | ||
|
||
push "$case = ", expr(condition), ";" | ||
end | ||
|
||
case_parts.each_with_index do |wen, idx| | ||
if wen and wen.type == :when | ||
@parser.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? | ||
push "else {", stmt(wen), "}" | ||
end | ||
end | ||
|
||
# if we are having a closure, we must return a usable value | ||
if needs_closure? and !handled_else | ||
push "else { return nil }" | ||
end | ||
|
||
wrap '(function() {', '})()' if needs_closure? | ||
end | ||
end | ||
|
||
def needs_closure? | ||
!stmt? | ||
end | ||
|
||
def case_parts | ||
children[1..-1] | ||
end | ||
|
||
def case_stmt | ||
@parser.instance_variable_get(:@case_stmt) | ||
end | ||
end | ||
|
||
class WhenNode < Node | ||
children :whens, :body | ||
|
||
def compile | ||
push "if (" | ||
|
||
when_checks.each_with_index do |check, idx| | ||
push ' || ' unless idx == 0 | ||
|
||
if check.type == :splat | ||
push "(function($splt) { for (var i = 0; i < $splt.length; i++) {" | ||
push "if ($splt[i]['$===']($case)) { return true; }" | ||
push "} return false; })(", expr(check[1]), ")" | ||
else | ||
if case_stmt[:cond] | ||
call = s(:call, check, :===, s(:arglist, s(:js_tmp, '$case'))) | ||
push expr(call) | ||
else | ||
push @parser.js_truthy(check) | ||
end | ||
end | ||
end | ||
|
||
push ") {", @parser.process(body_code, @level), "}" | ||
end | ||
|
||
def when_checks | ||
whens.children | ||
end | ||
|
||
def case_stmt | ||
@parser.instance_variable_get(:@case_stmt) | ||
end | ||
|
||
def body_code | ||
body || s(:nil) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
require 'opal/nodes/base' | ||
|
||
module Opal | ||
class Parser | ||
class EnsureNode < Node | ||
children :begn, :ensr | ||
|
||
def compile | ||
push "try {" | ||
line @parser.process(body_sexp, @level) | ||
line "} finally {" | ||
line @parser.process(ensr_sexp, @level) | ||
line "}" | ||
|
||
wrap '(function() {', '; })()' if wrap_in_closure? | ||
end | ||
|
||
def body_sexp | ||
wrap_in_closure? ? @parser.returns(begn) : begn | ||
end | ||
|
||
def ensr_sexp | ||
ensr || s(:nil) | ||
end | ||
|
||
def wrap_in_closure? | ||
recv? or expr? | ||
end | ||
end | ||
|
||
class RescueNode < Node | ||
children :body | ||
|
||
def compile | ||
handled_else = false | ||
|
||
push "try {" | ||
line(indent { @parser.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) }) | ||
end | ||
|
||
# if no resbodys capture our error, then rethrow | ||
unless handled_else | ||
push "else { throw $err; }" | ||
end | ||
|
||
line "}" | ||
|
||
wrap '(function() { ', '})()' if expr? | ||
end | ||
|
||
def body_code | ||
body.type == :resbody ? s(:nil) : body | ||
end | ||
end | ||
|
||
class ResBodyNode < Node | ||
children :args, :body | ||
|
||
def compile | ||
push "if (" | ||
|
||
rescue_classes.each_with_index do |cls, idx| | ||
push ', ' unless idx == 0 | ||
call = s(:call, cls, :===, s(:arglist, s(:js_tmp, '$err'))) | ||
push expr(call) | ||
end | ||
|
||
# if no classes are given, then catch all errors | ||
push "true" if rescue_classes.empty? | ||
|
||
push ") {" | ||
|
||
if variable = rescue_variable | ||
variable[2] = s(:js_tmp, '$err') | ||
push expr(variable), ';' | ||
end | ||
|
||
line @parser.process(rescue_body, @level) | ||
line "}" | ||
end | ||
|
||
def rescue_variable | ||
variable = args.last | ||
|
||
if Sexp === variable and [:lasgn, :iasgn].include?(variable.type) | ||
variable.dup | ||
end | ||
end | ||
|
||
def rescue_classes | ||
classes = args.children | ||
classes.pop if classes.last and classes.last.type != :const | ||
classes | ||
end | ||
|
||
def rescue_body | ||
body || s(:nil) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters