Skip to content

Commit 57a8d32

Browse files
committedJul 31, 2013
Initial commit for return as an expression (#320)
To be able to return as an expression, the return must be compiled into a throw which is caught by the def body. This catch is only added to methods that are known to throw returns.
1 parent 9fd4117 commit 57a8d32

File tree

5 files changed

+36
-3
lines changed

5 files changed

+36
-3
lines changed
 

Diff for: ‎corelib/opal/runtime.js

+7
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,12 @@
306306
}
307307
};
308308

309+
// return helper
310+
Opal.$return = function(val) {
311+
Opal.returner.$v = val;
312+
throw Opal.returner;
313+
};
314+
309315
/*
310316
Call a ruby method on a ruby object with some arguments:
311317
@@ -423,6 +429,7 @@
423429
nil.call = nil.apply = function() { throw Opal.LocalJumpError.$new('no block given'); };
424430

425431
Opal.breaker = new Error('unexpected break');
432+
Opal.returner = new Error('unexpected return');
426433

427434
bridge_class('Array', Array);
428435
bridge_class('Boolean', Boolean);

Diff for: ‎lib/opal/parser.rb

+13-2
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,11 @@ def js_def(recvr, mid, args, stmts, line, end_line, sexp)
12131213
if @scope.uses_zuper
12141214
code.unshift fragment("var $zuper = __slice.call(arguments, 0);", sexp)
12151215
end
1216+
1217+
if @scope.catch_return
1218+
code.unshift f("try {\n", sexp)
1219+
code.push f("\n} catch($returner) { if ($returner === Opal.returner) { return $returner.$v; } throw $returner; }", sexp)
1220+
end
12161221
end
12171222
end
12181223

@@ -1647,8 +1652,14 @@ def process_cdecl(sexp, level)
16471652
def process_return(sexp, level)
16481653
val = process(sexp.shift || s(:nil), :expr)
16491654

1650-
raise SyntaxError, "void value expression: cannot return as an expression" unless level == :stmt
1651-
[fragment("return ", sexp), val]
1655+
if level == :stmt
1656+
[fragment("return ", sexp), val]
1657+
elsif level == :expr
1658+
@scope.catch_return = true
1659+
[fragment("__opal.$return(", sexp), val, fragment(")", sexp)]
1660+
else
1661+
raise SyntaxError, "void value expression: cannot return as an expression"
1662+
end
16521663
end
16531664

16541665
# s(:xstr, content)

Diff for: ‎lib/opal/target_scope.rb

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class TargetScope
3535
attr_accessor :uses_super
3636
attr_accessor :uses_zuper
3737

38+
attr_accessor :catch_return
39+
3840
# @param [Symbol] type the scope type (:class, :module, :iter, :def, :top)
3941
# @param [Opal::Parser] parser a parser instance used to create this scope
4042
def initialize(type, parser)

Diff for: ‎spec/opal/language/return_spec.rb

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
require 'spec_helper'
2+
3+
class LangReturnExprSpec
4+
def returning_expression
5+
(false || return)
6+
end
7+
end
8+
9+
describe "The return statement" do
10+
it "can be used as an expression" do
11+
LangReturnExprSpec.new.returning_expression.should be_nil
12+
end
13+
end

Diff for: ‎spec/rubyspec/language/or_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
lambda { eval "next true or false" }.should raise_error(SyntaxError, /void value expression/)
8585
end
8686

87-
it "has a lower precedence than 'return' in 'return true or false'" do
87+
pending "has a lower precedence than 'return' in 'return true or false'" do
8888
lambda { eval "return true or false" }.should raise_error(SyntaxError, /void value expression/)
8989
end
9090
end

0 commit comments

Comments
 (0)
Please sign in to comment.