<commit_before>import ../frontend/Token
import ControlStatement, Expression, Visitor, VariableDecl, Node,
       VariableAccess, VariableDecl, IntLiteral, Type, RangeLiteral,
       FunctionCall, Block, Scope, While, BinaryOp, BaseType, Tuple
import tinker/[Trail, Resolver, Response, Errors]

Foreach: class extends ControlStatement {

    indexVariable: Expression
    variable: Expression
    collection: Expression

    replaced := false
    _resolved? := false

    init: func ~_foreach (=variable, =collection, .token) {
        super(token)
    }

    isResolved: func -> Bool { _resolved? }

    clone: func -> This {
        copy := new(variable clone(), collection clone(), token)
        body list each(|stat| copy body add(stat clone()))
        copy
    }

    accept: func (visitor: Visitor) {
        visitor visitForeach(this)
    }

    replace: func (oldie, kiddo: Node) -> Bool {
        match oldie {
            case variable      => variable      = kiddo; replaced = true; return true
            case indexVariable => indexVariable = kiddo; replaced = true; return true
            case collection    => collection    = kiddo; return true
        }
        return super(oldie, kiddo)
    }

    _createDeclFromAccess: func (vAcc: VariableAccess) {
        varType: Type = null
        if(collection instanceOf?(RangeLiteral)) {
            varType = BaseType new("Int", vAcc token)
        }
        variable = VariableDecl new(varType, vAcc getName(), vAcc token)
    }

    resolve: func (trail: Trail, res: Resolver) -> Response {
        if (_resolved?) return Response OK

        match variable {
            case vAcc: VariableAccess =>
                if (!replaced) {
                    _createDeclFromAccess(vAcc)
                }
            case tuple: Tuple =>
                if (!replaced) {
                    (a, b) := (tuple[0], tuple[1])
                    match a {
                        case vAcc: VariableAccess =>
                            intType := BaseType new("Int", a token)
                            initialValue := IntLiteral new(-1, vAcc token)
                            indexVariable = VariableDecl new(intType, vAcc getName(), initialValue, a token)
                        case =>
                            res throwError(InvalidForeach new(a token, "Invalid element in foreach tuple, expected identifier"))
                            return Response OK
                    }

                    match b {
                        case vAcc: VariableAccess =>
                            _createDeclFromAccess(vAcc)
                        case =>
                            res throwError(InvalidForeach new(b token, "Invalid element in foreach tuple, expected identifier"))
                            return Response OK
                    }
                }
        }

        trail push(this)

        {
            response := variable resolve(trail, res)
            if(!response ok()) {
                trail pop(this)
                return response
            }
        }

        if (indexVariable) {
            response := indexVariable resolve(trail, res)
            if(!response ok()) {
                trail pop(this)
                return response
            }
        }

        {
            response := collection resolve(trail, res)
            if(!response ok()) {
                trail pop(this)
                return response
            }
        }

        trail pop(this)

        if(!collection instanceOf?(RangeLiteral)) {
            if(collection getType() == null) {
                res wholeAgain(this, "need collection type")
                return Response OK
            }

            // TODO/FIXME: Should 'this' be in the trail when resolving the collection type?
            // This could lead to some weird bugs but is most likely completely irrelevant since a type should not need to use the trail to resolve itself.
            collection getType() resolve(trail, res)

            match (collection getType()) {
                case baseType: BaseType =>
                    // I'm not sure this is the best idea, I don't think we have another way of checking against core types atm though.
                    if (baseType name == "Range") {
                        // If we have a range collection, just go ahead and turn it into a literal node for the backend to generate a C for loop directly.

                        // We will create an access to the range collection and make a range literal with its min and max values.
                        access: VariableAccess
                        match collection {
                            case va: VariableAccess =>
                                // We already have an access, keep that one
                                access = va
                            case =>
                                // We want to avoid side effects, so we create a new declaration and get an access to it.
                                vDecl := VariableDecl new(collection getType(), generateTempName("foreachRangeVar"), collection, collection token)
                                access = VariableAccess new(vDecl, token)

                                if (!trail addBeforeInScope(this, vDecl)) {
                                    res throwError(CouldntAddBeforeInScope new(token, this, vDecl, trail))
                                    return Response OK
                                }
                        }

                        // This is a pretty dirty hack.
                        // We basically mutate the foreache's state to a range literal foreach and force it to be resolved again.
                        // That way, it is not replaced by a while loop + iterator calls but is rather passed to the backend that directly generates a C for loop.

                        // This is our new collection
                        newCol := RangeLiteral new(VariableAccess new(access, "min", collection token),
                                                   VariableAccess new(access, "max", collection token), collection token)

                        // "Reset" our state
                        collection = newCol
                        replaced = false

                        // Rewind to a variable access, let resolve do what it needs to again
                        match variable {
                            case vDecl: VariableDecl =>
                                variable = VariableAccess new(vDecl name, vDecl token)
                        }

                        trail toString() println()

                        // Let's go again!
                        res wholeAgain(this, "replaced foreach range collection with equivalent range literal")
                        return Response OK
                    }
            }

            iterCall := FunctionCall new(collection, "iterator", token)

            response := Response LOOP
            while(response == Response LOOP) {
                response = iterCall resolve(trail, res)
            }

            iterType := iterCall getType()
            if(iterType == null) {
                if(res fatal) res throwError(InternalError new(token, "Couldn't resolve iterType of %s" format(toString())))
                res wholeAgain(this, "need iterType")
                return Response OK
            }
            iterType resolve(trail, res)
            if(!iterType isResolved()) {
                if(res fatal) res throwError(InternalError new(token, "Couldn't resolve iterType %s" format(iterType toString())))
                res wholeAgain(this, "need iterType")
                return Response OK
            }<commit_msg>Remove garbage variable declaration left by 'variable resolve'<commit_after>import ../frontend/Token
import ControlStatement, Expression, Visitor, VariableDecl, Node,
       VariableAccess, VariableDecl, IntLiteral, Type, RangeLiteral,
       FunctionCall, Block, Scope, While, BinaryOp, BaseType, Tuple
import tinker/[Trail, Resolver, Response, Errors]

Foreach: class extends ControlStatement {

    indexVariable: Expression
    variable: Expression
    collection: Expression

    replaced := false
    _resolved? := false

    init: func ~_foreach (=variable, =collection, .token) {
        super(token)
    }

    isResolved: func -> Bool { _resolved? }

    clone: func -> This {
        copy := new(variable clone(), collection clone(), token)
        body list each(|stat| copy body add(stat clone()))
        copy
    }

    accept: func (visitor: Visitor) {
        visitor visitForeach(this)
    }

    replace: func (oldie, kiddo: Node) -> Bool {
        match oldie {
            case variable      => variable      = kiddo; replaced = true; return true
            case indexVariable => indexVariable = kiddo; replaced = true; return true
            case collection    => collection    = kiddo; return true
        }
        return super(oldie, kiddo)
    }

    _createDeclFromAccess: func (vAcc: VariableAccess) {
        varType: Type = null
        if(collection instanceOf?(RangeLiteral)) {
            varType = BaseType new("Int", vAcc token)
        }
        variable = VariableDecl new(varType, vAcc getName(), vAcc token)
    }

    resolve: func (trail: Trail, res: Resolver) -> Response {
        if (_resolved?) return Response OK

        if (!replaced) match variable {
            case vAcc: VariableAccess =>
                _createDeclFromAccess(vAcc)
            case tuple: Tuple =>
                (a, b) := (tuple[0], tuple[1])
                match a {
                    case vAcc: VariableAccess =>
                        intType := BaseType new("Int", a token)
                        initialValue := IntLiteral new(-1, vAcc token)
                        indexVariable = VariableDecl new(intType, vAcc getName(), initialValue, a token)
                    case =>
                        res throwError(InvalidForeach new(a token, "Invalid element in foreach tuple, expected identifier"))
                        return Response OK
                }

                match b {
                    case vAcc: VariableAccess =>
                        _createDeclFromAccess(vAcc)
                    case =>
                        res throwError(InvalidForeach new(b token, "Invalid element in foreach tuple, expected identifier"))
                        return Response OK
                }
        }

        trail push(this)

        {
            response := variable resolve(trail, res)
            if(!response ok()) {
                trail pop(this)
                return response
            }
        }

        if (indexVariable) {
            response := indexVariable resolve(trail, res)
            if(!response ok()) {
                trail pop(this)
                return response
            }
        }

        {
            response := collection resolve(trail, res)
            if(!response ok()) {
                trail pop(this)
                return response
            }
        }

        trail pop(this)

        if(!collection instanceOf?(RangeLiteral)) {
            if(collection getType() == null) {
                res wholeAgain(this, "need collection type")
                return Response OK
            }

            // TODO/FIXME: Should 'this' be in the trail when resolving the collection type?
            // This could lead to some weird bugs but is most likely completely irrelevant since a type should not need to use the trail to resolve itself.
            collection getType() resolve(trail, res)

            match (collection getType()) {
                case baseType: BaseType =>
                    // I'm not sure this is the best idea, I don't think we have another way of checking against core types atm though.
                    if (baseType name == "Range") {
                        // If we have a range collection, just go ahead and turn it into a literal node for the backend to generate a C for loop directly.

                        // We will create an access to the range collection and make a range literal with its min and max values.
                        access: VariableAccess
                        match collection {
                            case va: VariableAccess =>
                                // We already have an access, keep that one
                                access = va
                            case =>
                                // We want to avoid side effects, so we create a new declaration and get an access to it.
                                vDecl := VariableDecl new(collection getType(), generateTempName("foreachRangeVar"), collection, collection token)
                                access = VariableAccess new(vDecl, token)

                                if (!trail addBeforeInScope(this, vDecl)) {
                                    res throwError(CouldntAddBeforeInScope new(token, this, vDecl, trail))
                                    return Response OK
                                }
                        }

                        // This is a pretty dirty hack.
                        // We basically mutate the foreache's state to a range literal foreach and force it to be resolved again.
                        // That way, it is not replaced by a while loop + iterator calls but is rather passed to the backend that directly generates a C for loop.

                        // This is our new collection
                        newCol := RangeLiteral new(VariableAccess new(access, "min", collection token),
                                                   VariableAccess new(access, "max", collection token), collection token)

                        collection = newCol
                        replaced = false

                        // We need to remove the unwrapped index vDecl from our parent scope
                        // because it's type is unknown and currently unresolvable.
                        scope := trail get(trail findScope()) as Scope

                        for (stmt in scope list) {
                            match stmt {
                                case vDecl: VariableDecl =>
                                    if (vDecl name == variable toString() && vDecl type == null) {
                                        scope remove(vDecl)
                                    }
                            }
                        }

                        // Let's go again!
                        res wholeAgain(this, "replaced foreach range collection with equivalent range literal")
                        return Response OK
                    }
            }

            iterCall := FunctionCall new(collection, "iterator", token)

            response := Response LOOP
            while(response == Response LOOP) {
                response = iterCall resolve(trail, res)
            }

            iterType := iterCall getType()
            if(iterType == null) {
                if(res fatal) res throwError(InternalError new(token, "Couldn't resolve iterType of %s" format(toString())))
                res wholeAgain(this, "need iterType")
                return Response OK
            }
            iterType resolve(trail, res)
            if(!iterType isResolved()) {
                if(res fatal) res throwError(InternalError new(token, "Couldn't resolve iterType %s" format(iterType toString())))
                res wholeAgain(this, "need iterType")
                return Response OK
            }
{'fraction_non_alphanumeric': 0.07181859060833815, 'fraction_numerical': 0.0003854307188282906, 'mean_word_length': 1.3738944800243977, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 17, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5501061', 'n_tokens_mistral': 3554, 'n_tokens_neox': 3322, 'n_words': 1271}