diff --git a/src/compiler.c b/src/compiler.c index cc7cac6..710ee54 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -260,7 +260,8 @@ static size_t addLocal(KrkToken name); static void string(int canAssign); static KrkToken decorator(size_t level, FunctionType type); static void call(int canAssign); -static void complexAssignment(size_t count, KrkScanner oldScanner, Parser oldParser, size_t targetCount); +static void complexAssignment(size_t count, KrkScanner oldScanner, Parser oldParser, size_t targetCount, int parenthesized); +static void complexAssignmentTargets(size_t count, KrkScanner oldScanner, Parser oldParser, size_t targetCount, int parenthesized); static void finishError(KrkToken * token) { size_t i = 0; @@ -2431,10 +2432,20 @@ static void grouping(int canAssign) { } else if (!maybeValidAssignment) { error("Can not assign to generator expression."); } else { - complexAssignment(chunkBefore, scannerBefore, parserBefore, argCount); + complexAssignment(chunkBefore, scannerBefore, parserBefore, argCount, 1); } } else if (canAssign == 2 && (check(TOKEN_EQUAL) || check(TOKEN_COMMA) || check(TOKEN_RIGHT_PAREN))) { - error("Assignment to nested parenthesized target list unsupported."); + if (!argCount) { + error("Can not assign to empty target list."); + } else if (!maybeValidAssignment) { + error("Can not assign to generator expression."); + } else { + currentChunk()->count = chunkBefore; + complexAssignmentTargets(chunkBefore, scannerBefore, parserBefore, argCount, 2); + if (!matchComplexEnd()) { + errorAtCurrent("Unexpected end of nested target list"); + } + } } } @@ -2665,21 +2676,14 @@ static void actualTernary(size_t count, KrkScanner oldScanner, Parser oldParser) parser = outParser; } -static void complexAssignment(size_t count, KrkScanner oldScanner, Parser oldParser, size_t targetCount) { - int parenthesizedTargets = (oldParser.previous.length && oldParser.previous.type == TOKEN_LEFT_PAREN); - - currentChunk()->count = count; - parsePrecedence(PREC_ASSIGNMENT); +static void complexAssignmentTargets(size_t count, KrkScanner oldScanner, Parser oldParser, size_t targetCount, int parenthesized) { emitBytes(OP_DUP, 0); + if (targetCount > 1) { EMIT_CONSTANT_OP(OP_UNPACK,targetCount); EMIT_CONSTANT_OP(OP_REVERSE,targetCount); } - /* Store end state */ - KrkScanner outScanner = krk_tellScanner(); - Parser outParser = parser; - /* Rewind */ krk_rewindScanner(oldScanner); parser = oldParser; @@ -2692,28 +2696,46 @@ static void complexAssignment(size_t count, KrkScanner oldScanner, Parser oldPar emitByte(OP_POP); if (checkTargetCount == targetCount && parser.previous.type == TOKEN_COMMA) { - if (!match(parenthesizedTargets ? TOKEN_RIGHT_PAREN : TOKEN_EQUAL)) { - errorAtCurrent("Expected to rescan end of target list, not '%.*s'", - (int)parser.current.length, parser.current.start); - break; + if (!match(parenthesized ? TOKEN_RIGHT_PAREN : TOKEN_EQUAL)) { + goto _errorAtCurrent; } } - if (checkTargetCount == targetCount && parenthesizedTargets) { - if (parser.previous.type != TOKEN_RIGHT_PAREN || !match(TOKEN_EQUAL)) { - errorAtCurrent("Expected to rescan closing paren, not '%.*s'", - (int)parser.current.length, parser.current.start); - break; + if (checkTargetCount == targetCount && parenthesized) { + if (parser.previous.type != TOKEN_RIGHT_PAREN) { + goto _errorAtCurrent; } } + if (checkTargetCount == targetCount && parenthesized) { + if (parenthesized == 1 && !match(TOKEN_EQUAL)) { + goto _errorAtCurrent; + } + } + + if (checkTargetCount == targetCount) return; + if (check(TOKEN_COMMA) || check(TOKEN_EQUAL) || check(TOKEN_RIGHT_PAREN)) { - error("Invalid complex assignment target (target #%d)", checkTargetCount+1); - break; + goto _errorAtCurrent; } } while (parser.previous.type != TOKEN_EQUAL && !parser.hadError); +_errorAtCurrent: + errorAtCurrent("Invalid complex assignment target"); +} + +static void complexAssignment(size_t count, KrkScanner oldScanner, Parser oldParser, size_t targetCount, int parenthesized) { + + currentChunk()->count = count; + parsePrecedence(PREC_ASSIGNMENT); + + /* Store end state */ + KrkScanner outScanner = krk_tellScanner(); + Parser outParser = parser; + + complexAssignmentTargets(count,oldScanner,oldParser,targetCount,parenthesized); + /* Restore end state */ krk_rewindScanner(outScanner); parser = outParser; @@ -2730,7 +2752,7 @@ static void actualComma(int canAssign, size_t count, KrkScanner oldScanner, Pars EMIT_CONSTANT_OP(OP_TUPLE,expressionCount); if (canAssign == 1 && match(TOKEN_EQUAL)) { - complexAssignment(count, oldScanner, oldParser, expressionCount); + complexAssignment(count, oldScanner, oldParser, expressionCount, 0); } } diff --git a/test/testNestedChainedMultipleAssignment.krk b/test/testNestedChainedMultipleAssignment.krk new file mode 100644 index 0000000..5899b1e --- /dev/null +++ b/test/testNestedChainedMultipleAssignment.krk @@ -0,0 +1,3 @@ +let x, y, a, b, c, d +a, (b, (c, d)) = x, y = 1, (2, '34') +print(a,b,c,d,x,y) diff --git a/test/testNestedChainedMultipleAssignment.krk.expect b/test/testNestedChainedMultipleAssignment.krk.expect new file mode 100644 index 0000000..6b08557 --- /dev/null +++ b/test/testNestedChainedMultipleAssignment.krk.expect @@ -0,0 +1 @@ +1 2 3 4 1 (2, '34')