Support multiple context managers in a 'with' statement
This commit is contained in:
parent
ee731257dd
commit
46dada92fc
@ -1435,6 +1435,7 @@ static void withStatement() {
|
||||
|
||||
/* We only need this for block() */
|
||||
size_t blockWidth = (parser.previous.type == TOKEN_INDENTATION) ? parser.previous.length : 0;
|
||||
KrkToken myPrevious = parser.previous;
|
||||
|
||||
/* Collect the with token that started this statement */
|
||||
advance();
|
||||
@ -1453,19 +1454,25 @@ static void withStatement() {
|
||||
markInitialized();
|
||||
}
|
||||
|
||||
consume(TOKEN_COLON, "Expected ':' after with statement");
|
||||
|
||||
/* Storage for return / exception */
|
||||
addLocal(syntheticToken(""));
|
||||
markInitialized();
|
||||
|
||||
/* Handler object */
|
||||
addLocal(syntheticToken(""));
|
||||
int withJump = emitJump(OP_PUSH_WITH);
|
||||
markInitialized();
|
||||
|
||||
if (check(TOKEN_COMMA)) {
|
||||
parser.previous = myPrevious;
|
||||
withStatement(); /* Keep nesting */
|
||||
} else {
|
||||
consume(TOKEN_COLON, "Expected ',' or ':' after with statement");
|
||||
|
||||
beginScope();
|
||||
block(blockWidth,"with");
|
||||
endScope();
|
||||
}
|
||||
|
||||
patchJump(withJump);
|
||||
emitByte(OP_CLEANUP_WITH);
|
||||
|
10
src/vm.c
10
src/vm.c
@ -300,7 +300,10 @@ inline void krk_push(KrkValue value) {
|
||||
inline KrkValue krk_pop() {
|
||||
krk_currentThread.stackTop--;
|
||||
if (unlikely(krk_currentThread.stackTop < krk_currentThread.stack)) {
|
||||
fprintf(stderr, "Fatal error: stack underflow detected in VM.\n");
|
||||
fprintf(stderr, "Fatal error: stack underflow detected in VM (krk_currentThread.stackTop = %p, krk_currentThread.stack = %p)\n",
|
||||
(void*)krk_currentThread.stackTop,
|
||||
(void*)krk_currentThread.stack);
|
||||
abort();
|
||||
return NONE_VAL();
|
||||
}
|
||||
return *krk_currentThread.stackTop;
|
||||
@ -738,8 +741,10 @@ _finishKwarg:
|
||||
/* We can't have had any kwargs. */
|
||||
if ((size_t)argCount > potentialPositionalArgs && closure->function->collectsArguments) {
|
||||
krk_push(NONE_VAL()); krk_push(NONE_VAL()); krk_pop(); krk_pop();
|
||||
startOfPositionals[offsetOfExtraArgs] = krk_list_of(argCount - potentialPositionalArgs,
|
||||
KrkValue tmp = krk_list_of(argCount - potentialPositionalArgs,
|
||||
&startOfPositionals[potentialPositionalArgs], 0);
|
||||
startOfPositionals = &krk_currentThread.stackTop[-argCount];
|
||||
startOfPositionals[offsetOfExtraArgs] = tmp;
|
||||
argCount = closure->function->requiredArgs + 1;
|
||||
argCountX = argCount - 1;
|
||||
while (krk_currentThread.stackTop > startOfPositionals + argCount) krk_pop();
|
||||
@ -2004,6 +2009,7 @@ _resumeHook: (void)0;
|
||||
krk_push(NONE_VAL());
|
||||
krk_push(NONE_VAL());
|
||||
krk_callSimple(OBJECT_VAL(type->_exit), 4, 0);
|
||||
if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) goto _finishException;
|
||||
}
|
||||
if (AS_HANDLER(handler).type != OP_RETURN) break;
|
||||
krk_pop(); /* handler */
|
||||
|
39
test/testMultipleWith.krk
Normal file
39
test/testMultipleWith.krk
Normal file
@ -0,0 +1,39 @@
|
||||
class Context:
|
||||
def __init__(self,name):
|
||||
self.name = name
|
||||
def __enter__(self):
|
||||
print("Entering",self.name)
|
||||
def __exit__(self, *args):
|
||||
print("Exiting",self.name,"with",[type(x) for x in args])
|
||||
|
||||
def nested():
|
||||
print("Before")
|
||||
with Context('a') as a:
|
||||
with Context('b') as b:
|
||||
with Context('c') as c:
|
||||
print("Hello, world.")
|
||||
print("After")
|
||||
|
||||
nested()
|
||||
|
||||
def simple():
|
||||
print("Before")
|
||||
with Context('a') as a, Context('b') as b, Context('c') as c:
|
||||
print("Hello, world.")
|
||||
print("After")
|
||||
|
||||
simple()
|
||||
|
||||
|
||||
def exceptional():
|
||||
print("Before")
|
||||
with Context('a') as a, Context('b') as b, Context('c') as c:
|
||||
print("Prior")
|
||||
raise ValueError("oh no")
|
||||
print("Shouldn't happen")
|
||||
print("After")
|
||||
|
||||
try:
|
||||
exceptional()
|
||||
except Exception as e:
|
||||
print("Raised",repr(e))
|
27
test/testMultipleWith.krk.expect
Normal file
27
test/testMultipleWith.krk.expect
Normal file
@ -0,0 +1,27 @@
|
||||
Before
|
||||
Entering a
|
||||
Entering b
|
||||
Entering c
|
||||
Hello, world.
|
||||
Exiting c with [<class 'NoneType'>, <class 'NoneType'>, <class 'NoneType'>]
|
||||
Exiting b with [<class 'NoneType'>, <class 'NoneType'>, <class 'NoneType'>]
|
||||
Exiting a with [<class 'NoneType'>, <class 'NoneType'>, <class 'NoneType'>]
|
||||
After
|
||||
Before
|
||||
Entering a
|
||||
Entering b
|
||||
Entering c
|
||||
Hello, world.
|
||||
Exiting c with [<class 'NoneType'>, <class 'NoneType'>, <class 'NoneType'>]
|
||||
Exiting b with [<class 'NoneType'>, <class 'NoneType'>, <class 'NoneType'>]
|
||||
Exiting a with [<class 'NoneType'>, <class 'NoneType'>, <class 'NoneType'>]
|
||||
After
|
||||
Before
|
||||
Entering a
|
||||
Entering b
|
||||
Entering c
|
||||
Prior
|
||||
Exiting c with [<class 'type'>, <class 'ValueError'>, <class 'list'>]
|
||||
Exiting b with [<class 'type'>, <class 'ValueError'>, <class 'list'>]
|
||||
Exiting a with [<class 'type'>, <class 'ValueError'>, <class 'list'>]
|
||||
Raised ValueError('oh no')
|
Loading…
Reference in New Issue
Block a user