RewriteReturnsEditor.ceylon

import ceylon.ast.core {
    Block,
    Declaration,
    GroupedExpression,
    LIdentifier,
    AssigningExpression,
    Return,
    Specifier,
    Statement,
    ValueSpecification,
    Editor
}

"Rewrites all returns in a block to specifications of the [[named value|returnValueName]].
 
 For example,
 ~~~
 if (condition) {
     return a;
 } else {
     return b;
 }
 ~~~
 is rewritten to
 ~~~
 if (condition) {
     returnValue = a;
 } else {
     returnValue = b;
 }
 ~~~
 You should only use this if the block [[has no early returns|hasEarlyReturns]],
 otherwise this changes the semantics of the block.
 
 Usage:
 
     rewrittenBlock= someBlock.transform(RewriteReturnsEditor(returnValueName));"
shared class RewriteReturnsEditor(LIdentifier returnValueName) satisfies Editor {
    
    "Rewrites a single return statement to a value specification,
     or returns [[null]] if the return statement has no [[result|Return.result]]."
    ValueSpecification? rewriteReturn(Return ret) {
        if (exists result = ret.result) {
            AssigningExpression expression;
            if (is AssigningExpression result) {
                expression = result;
            } else {
                expression = GroupedExpression(result);
            }
            return ValueSpecification(returnValueName, Specifier(result));
        } else {
            return null;
        }
    }
    
    shared actual Block transformBlock(Block that)
            => that.copy(that.content.map((Declaration|Statement that) {
                if (is Return that) {
                    return rewriteReturn(that);
                } else {
                    assert (is Declaration|Statement ret = that.transform(this));
                    return ret;
                }
            }).coalesced.sequence());
}