Permalink
Browse files

Flesh out catch clauses some more:

- The caught error object is bound and matched on.
- Multiple catch clauses are supported.

Still need to correctly handle continuing to unwind if no catch clause
matches, but it's getting there.
  • Loading branch information...
1 parent ac0acb0 commit 82088b74ab67390b46b9bb0d138b02d9be1053e8 @munificent committed Jun 13, 2012
Showing with 190 additions and 54 deletions.
  1. +42 −38 src/Compiler/Compiler.cpp
  2. +2 −0 src/Compiler/Compiler.h
  3. +13 −6 src/VM/Fiber.cpp
  4. +4 −4 test/and.mag
  5. +16 −0 test/calls/nested_param.mag
  6. +4 −4 test/or.mag
  7. +98 −2 test/throw/catch.mag
  8. +11 −0 test/throw/method_catch.mag
View
80 src/Compiler/Compiler.cpp
@@ -256,17 +256,18 @@ namespace magpie
compile(expr.body(), dest);
// Complete the catch handler.
- write(OP_EXIT_TRY);
+ write(OP_EXIT_TRY); // slot for caught value here?
// Jump past it if an exception is not thrown.
int jumpPastCatch = startJump();
+
endJump(enter, OP_ENTER_TRY);
- // Compile the catch handlers.
- // TODO(bob): Handle multiple catches, compile their patterns, pattern
- // match, etc. For now, just compile the body.
- ASSERT(expr.catches().count() == 1, "Multiple catch clauses not impl.");
- compile(expr.catches()[0].body(), dest);
+ // TODO(bob): Can we use "dest" here or does it need to be a temp?
+ // Write a pseudo-opcode so we know what slot to put the error in.
+ write(OP_MOVE, dest);
+
+ compileMatch(expr.catches(), dest);
endJump(jumpPastCatch, OP_JUMP);
}
@@ -320,39 +321,8 @@ namespace magpie
void Compiler::visit(MatchExpr& expr, int dest)
{
- // Compile the value.
compile(expr.value(), dest);
-
- Array<int> endJumps;
-
- // Compile each case.
- for (int i = 0; i < expr.cases().count(); i++)
- {
- const MatchClause& clause = expr.cases()[i];
- bool lastPattern = i == expr.cases().count() - 1;
-
- // Compile the pattern.
- PatternCompiler compiler(*this, !lastPattern);
- clause.pattern()->accept(compiler, dest);
-
- // Compile the body if the match succeeds.
- compile(clause.body(), dest);
-
- // Then jump past the other cases.
- if (!lastPattern)
- {
- endJumps.add(startJump());
-
- // If this pattern fails, make it jump to the next case.
- compiler.endJumps();
- }
- }
-
- // Patch all the jumps now that we know where the end is.
- for (int i = 0; i < endJumps.count(); i++)
- {
- endJump(endJumps[i], OP_JUMP);
- }
+ compileMatch(expr.cases(), dest);
}
void Compiler::visit(NameExpr& expr, int dest)
@@ -484,6 +454,40 @@ namespace magpie
compile(expr.pattern(), dest);
}
+ void Compiler::compileMatch(const Array<MatchClause>& clauses, int dest)
+ {
+ Array<int> endJumps;
+
+ // Compile each case.
+ for (int i = 0; i < clauses.count(); i++)
+ {
+ const MatchClause& clause = clauses[i];
+ bool lastPattern = i == clauses.count() - 1;
+
+ // Compile the pattern.
+ PatternCompiler compiler(*this, !lastPattern);
+ clause.pattern()->accept(compiler, dest);
+
+ // Compile the body if the match succeeds.
+ compile(clause.body(), dest);
+
+ // Then jump past the other cases.
+ if (!lastPattern)
+ {
+ endJumps.add(startJump());
+
+ // If this pattern fails, make it jump to the next case.
+ compiler.endJumps();
+ }
+ }
+
+ // Patch all the jumps now that we know where the end is.
+ for (int i = 0; i < endJumps.count(); i++)
+ {
+ endJump(endJumps[i], OP_JUMP);
+ }
+ }
+
int Compiler::compileExpressionOrConstant(gc<Expr> expr)
{
const NumberExpr* number = expr->asNumberExpr();
View
2 src/Compiler/Compiler.h
@@ -62,6 +62,8 @@ namespace magpie
virtual void visit(ThrowExpr& expr, int dest);
virtual void visit(VariableExpr& expr, int dest);
+ void compileMatch(const Array<MatchClause>& clauses, int dest);
+
// Compiles the given expr. If it's a constant expr, it adds the constant
// to the method table and returns the constant id (with the mask bit set).
// Otherwise, creates a temporary slot and compiles the expr to evaluate
View
19 src/VM/Fiber.cpp
@@ -381,10 +381,9 @@ namespace magpie
case OP_THROW:
{
// TODO(bob): Throw an actual value.
- if (!throwError(vm_.getBool(false)))
- {
- return FIBER_UNCAUGHT_ERROR;
- }
+ gc<Object> error = load(frame, GET_A(ins));
+ //std::cout << "throwing " << error << std::endl;
+ if (!throwError(error)) return FIBER_UNCAUGHT_ERROR;
break;
}
@@ -479,8 +478,16 @@ namespace magpie
callFrames_.truncate(nearestCatch_->callFrame() + 1);
// Jump to the catch handler.
- // TODO(bob): Insert thrown value into appropriate slot.
- callFrames_[-1].ip = nearestCatch_->offset();
+ CallFrame& frame = callFrames_[-1];
+ frame.ip = nearestCatch_->offset();
+
+ // The next instruction is a pseudo-op identifying where the error is.
+ instruction errorIns = frame.method->code()[frame.ip];
+ ASSERT(GET_OP(errorIns) == OP_MOVE,
+ "Expect pseudo-instruction at beginning of catch code.");
+ int errorSlot = GET_A(errorIns);
+ store(frame, errorSlot, error);
+ frame.ip++;
// Discard the try block now that we are outside of it.
nearestCatch_ = nearestCatch_->parent();
View
8 test/and.mag
@@ -15,6 +15,10 @@ print(true) and // expect: true
print(false) and // expect: false
print(false) // should not print
+// Swallow a trailing newline.
+print(true and
+ true) // expect: true
+
// Call 'true?' on the arguments to determine truthiness.
// TODO(bob): Support this.
/*
@@ -26,7 +30,3 @@ print(true) and // expect: true
right called shouldEqual(false)
end
*/
-
-// Swallow a trailing newline.
-print(true and
- true) // expect: true
View
16 test/calls/nested_param.mag
@@ -0,0 +1,16 @@
+def foo(a, b (c (d, e), f))
+ print(a)
+ print(b)
+ print(c)
+ print(d)
+ print(e)
+ print(f)
+end
+
+foo(1, ((2, 3), 4))
+// expect: 1
+// expect: ((2, 3), 4)
+// expect: (2, 3)
+// expect: 2
+// expect: 3
+// expect: 4
View
8 test/or.mag
@@ -15,6 +15,10 @@ print(false) or // expect: false
print(true) or // expect: true
print(true) // should not print
+// Swallow a trailing newline.
+print(true or
+ true) // expect: true
+
// Call 'true?' on the arguments to determine truthiness.
// TODO(bob): Support this.
/*
@@ -26,7 +30,3 @@ print(false) or // expect: false
right called shouldEqual(false)
end
*/
-
-// Swallow a trailing newline.
-print(true or
- true) // expect: true
View
100 test/throw/catch.mag
@@ -1,14 +1,110 @@
-// No error thrown.
+/*
+import spec.specify
+
+specify("A 'catch' clause in a block") with
+
+ it should("unwind past uncaught error types") with
+ var caught = false
+ do
+ do
+ throw "unwind error"
+ catch is Int then
+ fail("Should not be caught here.")
+ end
+ catch is String then
+ caught = true
+ end
+ caught shouldEqual(true)
+ end
+
+ it should("allow catch clauses in an if/then block") with
+ if true then
+ throw "error"
+ catch err is String then err shouldEqual("error")
+ end
+
+ it should("allow catch clauses in an else block") with
+ if false then
+ nothing
+ else
+ throw "error"
+ catch err is String then err shouldEqual("error")
+ end
+end
+*/
+
+// Do not execute the catch block if no error thrown.
do
print("no error") // expect: no error
catch err then
print("bad")
end
print("after") // expect: after
-// Catch.
+// Execute a catch block when an error is thrown.
do
throw "blah"
catch err then
print("caught") // expect: caught
end
+
+// Evaluate the throw expression.
+do
+ throw print("err") // expect: err
+catch err then
+ print("caught") // expect: caught
+end
+
+// Catch the thrown object.
+do
+ throw "err"
+catch err then
+ print(err) // expect: err
+end
+
+// Evaluate to the result of the catch block.
+do
+ val a = do
+ throw "err"
+ catch err then
+ "caught"
+ end
+ print(a) // expect: caught
+end
+
+// Allow a single line catch expression.
+do
+ throw "err"
+catch err then print("caught") // expect: caught
+
+// Allow catch clauses in an if/then block.
+if true then
+ throw "err"
+catch err then print("ok") // expect: ok
+
+// Allow catch clauses in an else block.
+if false then
+ nothing
+else
+ throw "err"
+catch err then print("ok") // expect: ok
+
+// Select the first matching catch clause.
+do
+ throw "err"
+catch "wrong" then
+ print("bad")
+catch "also wrong" then
+ print("bad")
+catch "err" then
+ print("good") // expect: good
+catch "err" then
+ print("bad")
+end
+
+
+// TODO(bob):
+// - catch patterns, destructuring, value patterns, etc.
+// - continue to unwind if no clause matches
+// - catches in other block expressions (for, while, etc.)
+
View
11 test/throw/method_catch.mag
@@ -0,0 +1,11 @@
+// A method body can have a catch clause.
+def method(shouldThrow)
+ print("method")
+ if shouldThrow then throw "err"
+catch err then
+ print("caught")
+end
+
+method(false) // expect: method
+method(true) // expect: method
+ // expect: caught

0 comments on commit 82088b7

Please sign in to comment.