<commit_before>import Foundation

public protocol SongExpressionLike {}

public enum SongExpression: SongExpressionLike, Equatable, Printable {

    
    case SongError(String)
    
    case SongUnit

    case SongInteger(Int)
    
    case SongString(String)
    
    case SongPair(SongExpressionLike, SongExpressionLike)
    
    case SongClosure(function: SongExpressionLike, context: SongContext)
    
    case SongLet(name: String, binding: SongExpressionLike, body: SongExpressionLike)
    
    case SongVariable(String)
    
    case SongFunction(name: String, parameters: [String], body: SongExpressionLike)
    
    case SongCall(closure: SongExpressionLike, arguments: [SongExpressionLike])
    
    
    public var description: String {
        switch self {
            
        case let .SongError(value):
            return "<\(value)>"

        case .SongUnit:
            return "#"

        case let .SongInteger(value):
            return "\(value)"
        
        case let .SongString(value):
            return "'\(value)'"
        
        case let .SongPair(first as SongExpression, second as SongExpression):
            return "(\(first), \(second))"
        
        case let .SongClosure(function as SongExpression, context):
            let contextList = contextDescription(context)
            return "[(\(contextList)) \(function)]"

        case let .SongLet(name, binding as SongExpression, body as SongExpression):
            return "let (\(name) = \(binding)) { \(body) }"
        
        case let .SongVariable(variable):
            return "\(variable)"
            
        case let .SongFunction(name, parameters, body as SongExpression):
            let parametersList = ", ".join(parameters)
            return "def \(name)(\(parametersList)) { \(body) }"
            
        case let .SongCall(closure as SongExpression, arguments):
            return descriptionSongCall(closure, arguments: arguments)
        
        default:
            return "<unknown>"
        }
    }
    
    func descriptionSongCall(closure: SongExpression, arguments: [SongExpressionLike]) -> String {
        var argumentStrings = Array<String>()
        for arg in arguments {
            argumentStrings.append("\(arg as SongExpression)")
        }
        let argumentsList = ", ".join(argumentStrings)
        return "\(closure)(\(argumentsList))"
    }
    
    public func evaluate() -> SongExpression {
        return evaluate(SongContext())
    }
    
    public func evaluate(context: SongContext) -> SongExpression {
        switch self {

        case let .SongLet(name, binding as SongExpression, body as SongExpression):
            return evaluateSongLet(name, binding, body, context)
            
        case let .SongVariable(variable):
            return evaluateSongVariable(variable, context)

        case let .SongFunction:
            return SongClosure(function: self, context: context)
            
        case let .SongCall(closure as SongExpression, arguments):
            return evaluateSongCallClosure(closure, arguments: arguments, callingContext: context)

        default:
            return self
        }
    }

    func evaluateSongLet(name: String, _ binding: SongExpression, _ body: SongExpression, _ context: SongContext) -> SongExpression {
        let letContext = extendContext(context, name: name, value: binding.evaluate(context))
        return body.evaluate(letContext)
    }
    
    func evaluateSongVariable(variable: String, _ context: SongContext) -> SongExpression {
        if let value = context[variable] {
            return value
        }
        return SongError("cannot evaluate \(variable)")
    }
    
    func evaluateSongCallClosure(closure: SongExpression, arguments: [SongExpressionLike], callingContext: SongContext) -> SongExpression {
        switch closure {
        case let .SongClosure(function as SongExpression, closureContext):
            return evaluateSongCallFunction(function, closureContext: closureContext, arguments: arguments, callingContext: callingContext)
        default:
            return SongError("can only call closure")
        }
    }
    
    func evaluateSongCallFunction(function: SongExpression, closureContext: SongContext, arguments: [SongExpressionLike], callingContext: SongContext) -> SongExpression {
        switch function {
        case let .SongFunction(name, parameters, body as SongExpression):
            if arguments.count < parameters.count {
                return SongExpression.SongError("not enough arguments")
            }
            if arguments.count > parameters.count {
                return SongExpression.SongError("too many arguments")
            }
            let extendedContext = extendContext(closureContext, parameters: parameters, arguments: arguments)
            return body.evaluate(extendedContext)
        default:
            return SongError("closure does not wrap function")
        }
    }
}

public func ==(lhs: SongExpression, rhs: SongExpression) -> Bool {
    switch (lhs, rhs) {
        
    case let (.SongError(lhsError), .SongError(rhsError)):
        return lhsError == rhsError

    case (.SongUnit, .SongUnit):
        return true
        
    case let (.SongInteger(lhsValue), .SongInteger(rhsValue)):
        return lhsValue == rhsValue
    
    case let (.SongString(lhsValue), .SongString(rhsValue)):<commit_msg>Tidy switch statement.<commit_after>import Foundation

public protocol SongExpressionLike {}

public enum SongExpression: SongExpressionLike, Equatable, Printable {

    
    case SongError(String)
    
    case SongUnit

    case SongInteger(Int)
    
    case SongString(String)
    
    case SongPair(SongExpressionLike, SongExpressionLike)
    
    case SongClosure(function: SongExpressionLike, context: SongContext)
    
    case SongLet(name: String, binding: SongExpressionLike, body: SongExpressionLike)
    
    case SongVariable(String)
    
    case SongFunction(name: String, parameters: [String], body: SongExpressionLike)
    
    case SongCall(closure: SongExpressionLike, arguments: [SongExpressionLike])
    
    
    public var description: String {
        switch self {
            
        case let .SongError(value):
            return "<\(value)>"

        case .SongUnit:
            return "#"

        case let .SongInteger(value):
            return "\(value)"
        
        case let .SongString(value):
            return "'\(value)'"
        
        case let .SongPair(first as SongExpression, second as SongExpression):
            return "(\(first), \(second))"
        
        case let .SongClosure(function as SongExpression, context):
            let contextList = contextDescription(context)
            return "[(\(contextList)) \(function)]"

        case let .SongLet(name, binding as SongExpression, body as SongExpression):
            return "let (\(name) = \(binding)) { \(body) }"
        
        case let .SongVariable(variable):
            return "\(variable)"
            
        case let .SongFunction(name, parameters, body as SongExpression):
            let parametersList = ", ".join(parameters)
            return "def \(name)(\(parametersList)) { \(body) }"
            
        case let .SongCall(closure as SongExpression, arguments):
            return descriptionSongCall(closure, arguments: arguments)
        
        default:
            return "<unknown>"
        }
    }
    
    func descriptionSongCall(closure: SongExpression, arguments: [SongExpressionLike]) -> String {
        var argumentStrings = Array<String>()
        for arg in arguments {
            argumentStrings.append("\(arg as SongExpression)")
        }
        let argumentsList = ", ".join(argumentStrings)
        return "\(closure)(\(argumentsList))"
    }
    
    public func evaluate() -> SongExpression {
        return evaluate(SongContext())
    }
    
    public func evaluate(context: SongContext) -> SongExpression {
        switch self {

        case let .SongLet(name, binding as SongExpression, body as SongExpression):
            return evaluateSongLet(name, binding, body, context)
            
        case let .SongVariable(variable):
            return evaluateSongVariable(variable, context)

        case let .SongFunction:
            return SongClosure(function: self, context: context)
            
        case let .SongCall(closure as SongExpression, arguments):
            return evaluateSongCallClosure(closure, arguments: arguments, callingContext: context)

        default:
            return self
        }
    }

    func evaluateSongLet(name: String, _ binding: SongExpression, _ body: SongExpression, _ context: SongContext) -> SongExpression {
        let letContext = extendContext(context, name: name, value: binding.evaluate(context))
        return body.evaluate(letContext)
    }
    
    func evaluateSongVariable(variable: String, _ context: SongContext) -> SongExpression {
        if let value = context[variable] {
            return value
        }
        return SongError("cannot evaluate \(variable)")
    }
    
    func evaluateSongCallClosure(closure: SongExpression, arguments: [SongExpressionLike], callingContext: SongContext) -> SongExpression {
        switch closure {
        case let .SongClosure(function as SongExpression, closureContext):
            return evaluateSongCallFunction(function, closureContext: closureContext, arguments: arguments, callingContext: callingContext)
        default:
            return SongError("can only call closure")
        }
    }
    
    func evaluateSongCallFunction(function: SongExpression, closureContext: SongContext, arguments: [SongExpressionLike], callingContext: SongContext) -> SongExpression {
        switch function {
        case let .SongFunction(_, parameters, body as SongExpression):
            if arguments.count < parameters.count {
                return SongExpression.SongError("not enough arguments")
            }
            if arguments.count > parameters.count {
                return SongExpression.SongError("too many arguments")
            }
            let extendedContext = extendContext(closureContext, parameters: parameters, arguments: arguments)
            return body.evaluate(extendedContext)
        default:
            return SongError("closure does not wrap function")
        }
    }
}

public func ==(lhs: SongExpression, rhs: SongExpression) -> Bool {
    switch (lhs, rhs) {
        
    case let (.SongError(lhsError), .SongError(rhsError)):
        return lhsError == rhsError

    case (.SongUnit, .SongUnit):
        return true
        
    case let (.SongInteger(lhsValue), .SongInteger(rhsValue)):
        return lhsValue == rhsValue
    
    case let (.SongString(lhsValue), .SongString(rhsValue)):
{'fraction_non_alphanumeric': 0.09079970381340244, 'fraction_numerical': 0.0, 'mean_word_length': 2.4476707083599236, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '336778', 'n_tokens_mistral': 2717, 'n_tokens_neox': 2578, 'n_words': 731}