<commit_before>/// Scope.swift
///
/// Copyright 2017, The Silt Language Project.
///
/// This project is released under the MIT license, a copy of which is
/// available in the repository.

import Lithosphere

extension NameBinding {
  public func scopeCheckModule(_ module: ModuleDeclSyntax) -> DeclaredModule {
    let params = module.typedParameterList.map(self.scopeCheckTelescope)
    let filteredDecls = self.withScope(walkNotations(module)) { _ in
      return self.reparseDecls(module.declList)
    }
    return DeclaredModule(
      moduleName: self.activeScope.nameSpace.module,
      params: params,
      namespace: self.activeScope.nameSpace,
      decls: filteredDecls.map(self.scopeCheckDecl))
  }
  
  private func scopeCheckDecl(_ syntax: DeclSyntax) -> Decl {
    precondition(!(syntax is FunctionDeclSyntax ||
      syntax is FunctionClauseDeclSyntax))
    switch syntax {
    case let syntax as ReparsedFunctionDeclSyntax:
      return self.scopeCheckFunctionDecl(syntax)
    default:
      fatalError()
    }
  }

  private func scopeCheckExpr(_ syntax: ExprSyntax) -> Expr {
    switch syntax {
    case let syntax as NamedBasicExprSyntax:
      let n = QualifiedName(ast: syntax.name)
      let head : ApplyHead
      if self.isBoundVariable(n.name) {
        head = ApplyHead.variable(n.name)
      } else {
        guard case .definition(_)? = self.lookupFullyQualifiedName(n) else {
          fatalError()
        }
        head = ApplyHead.definition(n)
      }
      return .apply(head, [])
    case _ as TypeBasicExprSyntax:
      return .type
    case _ as UnderscoreExprSyntax:
      return .meta
    case let syntax as ParenthesizedExprSyntax:
      return self.scopeCheckExpr(syntax.expr)
    case let syntax as LambdaExprSyntax:
      return self.underScope { _ in
        let bindings = self.scopeCheckBindingList(syntax.bindingList)
        let ee = self.scopeCheckExpr(syntax.bodyExpr)
        return bindings.reversed().reduce(ee) { (acc, next) in
          return .lambda(next, acc)
        }
      }
    case let syntax as ApplicationExprSyntax:
      var elims = [Elimination]()
      let n = QualifiedName(ast: (syntax.exprs[0] as! NamedBasicExprSyntax).name)

      let head : ApplyHead
      if self.isBoundVariable(n.name) {
        head = ApplyHead.variable(n.name)
      } else {
        guard case .some(.definition(_)) = self.lookupFullyQualifiedName(n) else {
          fatalError()
        }
        head = ApplyHead.definition(n)
      }

      for i in 1..<syntax.exprs.count {
        let e = syntax.exprs[i]
        let elim = Elimination.apply(self.scopeCheckExpr(e))
        elims.append(elim)
      }
      return .apply(head, elims)
    default:
      print(type(of: syntax))
      fatalError()
    }
  }

  private func scopeCheckBindingList(
    _ syntax: BindingListSyntax) -> [([Name], Expr)] {
    var bs = [([Name], Expr)]()
    for i in 0..<syntax.count {
      let binding = syntax[i]
      switch binding {
      case let binding as NamedBindingSyntax:
        let name = QualifiedName(ast: binding.name).name
        guard let bindName = self.bindVariable(named: name) else {
          fatalError()
        }
        bs.append(([bindName], .meta))
      case let binding as TypedBindingSyntax:
        bs.append(self.scopeCheckTelescope(binding.parameter))
      default:
        fatalError()
      }
    }
    return bs
  }

  private func scopeCheckTelescope(
    _ syntax: TypedParameterSyntax) -> ([Name], Expr) {
    switch syntax {
    case let syntax as ExplicitTypedParameterSyntax:
      let tyExpr = self.scopeCheckExpr(syntax.ascription.typeExpr)
      var names = [Name]()
      for j in 0..<syntax.ascription.boundNames.count {
        let name = syntax.ascription.boundNames[j]
        guard let bindName = self.bindVariable(named: Name(name: name)) else {
          fatalError()
        }
        names.append(bindName)
      }
      return (names, tyExpr)
    case let syntax as ImplicitTypedParameterSyntax:
      let tyExpr = self.scopeCheckExpr(syntax.ascription.typeExpr)
      var names = [Name]()
      for j in 0..<syntax.ascription.boundNames.count {
        let name = syntax.ascription.boundNames[j]
        guard let bindName = self.bindVariable(named: Name(name: name)) else {
          fatalError()
        }
        names.append(bindName)
      }
      return (names, tyExpr)
    default:
      fatalError()
    }
  }

  private func scopeCheckPattern(_ syntax: BasicExprListSyntax) -> [Pattern] {
    assert(!syntax.isEmpty)

    let pats = syntax.map(self.exprToDeclaredPattern)
    var patterns = [Pattern]()
    patterns.reserveCapacity(pats.count)
    for p in pats.dropFirst() {
      switch p {
      case .wild:
        patterns.append(.wild)
      case .variable(let name):
        guard let (localName, _) = self.lookupLocalName(name) else {
          guard let name = self.bindVariable(named: name) else {
            fatalError()
          }
          patterns.append(.variable(QualifiedName(name: name)))
          continue
        }
        patterns.append(.variable(localName))
      default:
        fatalError()
      }
    }
    return patterns
  }

  private func scopeCheckFunctionDecl(
    _ syntax: ReparsedFunctionDeclSyntax) -> Decl {
    precondition(syntax.ascription.boundNames.count == 1)
    let funcName = Name(name: syntax.ascription.boundNames[0])
    guard let functionName = self.bindDefinition(named: funcName, 0) else {
      fatalError()
    }
    let clauses = syntax.clauseList.map(self.scopeCheckFunctionClause)
    return Decl.function(functionName, clauses)
  }

  private func scopeCheckFunctionClause(
    _ syntax: FunctionClauseDeclSyntax) -> Clause {
    switch syntax {
    case let syntax as NormalFunctionClauseDeclSyntax:
      return self.underScope { _ in
        let pattern = self.scopeCheckPattern(syntax.basicExprList)
        let body = self.scopeCheckExpr(syntax.rhsExpr)
        return Clause(patterns: pattern, body: .body(body, []))
      }
    case let syntax as WithRuleFunctionClauseDeclSyntax:<commit_msg>Fake it 'til you make it.<commit_after>/// Scope.swift
///
/// Copyright 2017, The Silt Language Project.
///
/// This project is released under the MIT license, a copy of which is
/// available in the repository.

import Lithosphere

extension NameBinding {
  public func scopeCheckModule(_ module: ModuleDeclSyntax) -> DeclaredModule {
    let params = module.typedParameterList.map(self.scopeCheckTelescope)
    let filteredDecls = self.withScope(walkNotations(module)) { _ in
      return self.reparseDecls(module.declList)
    }
    return DeclaredModule(
      moduleName: self.activeScope.nameSpace.module,
      params: params,
      namespace: self.activeScope.nameSpace,
      decls: filteredDecls.map(self.scopeCheckDecl))
  }
  
  private func scopeCheckDecl(_ syntax: DeclSyntax) -> Decl {
    precondition(!(syntax is FunctionDeclSyntax ||
      syntax is FunctionClauseDeclSyntax))
    switch syntax {
    case let syntax as ReparsedFunctionDeclSyntax:
      return self.scopeCheckFunctionDecl(syntax)
    default:
      fatalError()
    }
  }

  private func scopeCheckExpr(_ syntax: ExprSyntax) -> Expr {
    switch syntax {
    case let syntax as NamedBasicExprSyntax:
      let n = QualifiedName(ast: syntax.name)
      let head : ApplyHead
      if self.isBoundVariable(n.name) {
        head = ApplyHead.variable(n.name)
      } else {
        guard case .definition(_)? = self.lookupFullyQualifiedName(n) else {
          fatalError()
        }
        head = ApplyHead.definition(n)
      }
      return .apply(head, [])
    case _ as TypeBasicExprSyntax:
      return .type
    case _ as UnderscoreExprSyntax:
      return .meta
    case let syntax as ParenthesizedExprSyntax:
      return self.scopeCheckExpr(syntax.expr)
    case let syntax as LambdaExprSyntax:
      return self.underScope { _ in
        let bindings = self.scopeCheckBindingList(syntax.bindingList)
        let ee = self.scopeCheckExpr(syntax.bodyExpr)
        return bindings.reversed().reduce(ee) { (acc, next) in
          return .lambda(next, acc)
        }
      }
    case let syntax as ApplicationExprSyntax:
      var elims = [Elimination]()
      let n = QualifiedName(ast: (syntax.exprs[0] as! NamedBasicExprSyntax).name)

      let head : ApplyHead
      if self.isBoundVariable(n.name) {
        head = ApplyHead.variable(n.name)
      } else {
        guard case .some(.definition(_)) = self.lookupFullyQualifiedName(n) else {
          fatalError()
        }
        head = ApplyHead.definition(n)
      }

      for i in 1..<syntax.exprs.count {
        let e = syntax.exprs[i]
        let elim = Elimination.apply(self.scopeCheckExpr(e))
        elims.append(elim)
      }
      return .apply(head, elims)
    default:
      print(type(of: syntax))
      fatalError()
    }
  }

  private func scopeCheckBindingList(
    _ syntax: BindingListSyntax) -> [([Name], Expr)] {
    var bs = [([Name], Expr)]()
    for i in 0..<syntax.count {
      let binding = syntax[i]
      switch binding {
      case let binding as NamedBindingSyntax:
        let name = QualifiedName(ast: binding.name).name
        guard let bindName = self.bindVariable(named: name) else {
          fatalError()
        }
        bs.append(([bindName], .meta))
      case let binding as TypedBindingSyntax:
        bs.append(self.scopeCheckTelescope(binding.parameter))
      default:
        fatalError()
      }
    }
    return bs
  }

  private func scopeCheckTelescope(
    _ syntax: TypedParameterSyntax) -> ([Name], Expr) {
    switch syntax {
    case let syntax as ExplicitTypedParameterSyntax:
      let tyExpr = self.scopeCheckExpr(syntax.ascription.typeExpr)
      var names = [Name]()
      for j in 0..<syntax.ascription.boundNames.count {
        let name = syntax.ascription.boundNames[j]
        guard let bindName = self.bindVariable(named: Name(name: name)) else {
          fatalError()
        }
        names.append(bindName)
      }
      return (names, tyExpr)
    case let syntax as ImplicitTypedParameterSyntax:
      let tyExpr = self.scopeCheckExpr(syntax.ascription.typeExpr)
      var names = [Name]()
      for j in 0..<syntax.ascription.boundNames.count {
        let name = syntax.ascription.boundNames[j]
        guard let bindName = self.bindVariable(named: Name(name: name)) else {
          fatalError()
        }
        names.append(bindName)
      }
      return (names, tyExpr)
    default:
      fatalError()
    }
  }

  private func scopeCheckPattern(_ syntax: BasicExprListSyntax) -> [Pattern] {
    assert(!syntax.isEmpty)

    let pats = syntax.map(self.exprToDeclaredPattern)
    var patterns = [Pattern]()
    patterns.reserveCapacity(pats.count)
    for p in pats.dropFirst() {
      switch p {
      case .wild:
        patterns.append(.wild)
      case .variable(let name):
        guard let (localName, _) = self.lookupLocalName(name) else {
          guard let name = self.bindVariable(named: name) else {
            fatalError()
          }
          patterns.append(.variable(QualifiedName(name: name)))
          continue
        }
        patterns.append(.variable(localName))
      default:
        fatalError()
      }
    }
    return patterns
  }

  // FIXME: Remove this and really implement mixfix operators, dummy.
  private func rebindArrows(_ syntax: ExprSyntax) -> ExprSyntax {
    switch syntax {
    case let syntax as ParenthesizedExprSyntax:
      return syntax.withExpr(self.rebindArrows(syntax.expr))
    case let syntax as ApplicationExprSyntax:
      guard syntax.exprs.count > 1 else {
        return syntax.exprs[0]
      }

      var precExprs = [BasicExprSyntax]()
      for exprIdx in 0..<syntax.exprs.count {
        let expr = syntax.exprs[exprIdx]
        if
          let tokExpr = expr as? NamedBasicExprSyntax,
          QualifiedName(ast: tokExpr.name).string == "->"
        {
          let prevApp = ApplicationExprSyntax(exprs: BasicExprListSyntax(elements: precExprs))
          let prevExpr = ParenthesizedExprSyntax(
            leftParenToken: TokenSyntax(.leftParen),
            expr: self.rebindArrows(prevApp),
            rightParenToken: TokenSyntax(.rightParen)
          )

          let nextApp = ApplicationExprSyntax(exprs: BasicExprListSyntax(elements: Array(syntax.exprs[exprIdx+1..<syntax.exprs.endIndex])))
          let nextExpr = ParenthesizedExprSyntax(
            leftParenToken: TokenSyntax(.leftParen),
            expr: self.rebindArrows(nextApp),
            rightParenToken: TokenSyntax(.rightParen)
          )
          return ParenthesizedExprSyntax(
            leftParenToken: TokenSyntax(.leftParen),
            expr: ApplicationExprSyntax(exprs: BasicExprListSyntax(elements: [ tokExpr , prevExpr, nextExpr ])),
            rightParenToken: TokenSyntax(.rightParen)
          )
        } else {
          precExprs.append(expr)
        }
      }
      return syntax
    case let syntax as QuantifiedExprSyntax:
      return syntax.withOutputExpr(self.rebindArrows(syntax.outputExpr))
    default:
      print(type(of: syntax))
      fatalError()
    }
  }

  private func scopeCheckFunctionDecl(
    _ syntax: ReparsedFunctionDeclSyntax) -> Decl {
    precondition(syntax.ascription.boundNames.count == 1)
    let funcName = Name(name: syntax.ascription.boundNames[0])
    guard let functionName = self.bindDefinition(named: funcName, 0) else {
      fatalError()
    }
    let clauses = syntax.clauseList.map(self.scopeCheckFunctionClause)
    return Decl.function(functionName, clauses)
  }

  private func scopeCheckFunctionClause(
    _ syntax: FunctionClauseDeclSyntax) -> Clause {
    switch syntax {
    case let syntax as NormalFunctionClauseDeclSyntax:
      return self.underScope { _ in
        let pattern = self.scopeCheckPattern(syntax.basicExprList)
        let body = self.scopeCheckExpr(syntax.rhsExpr)
        return Clause(patterns: pattern, body: .body(body, []))
      }
    case let syntax as WithRuleFunctionClauseDeclSyntax:
{'fraction_non_alphanumeric': 0.0965143631049968, 'fraction_numerical': 0.001995865706750303, 'mean_word_length': 2.607611211108254, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 22, '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': '943338', 'n_tokens_mistral': 4134, 'n_tokens_neox': 4030, 'n_words': 956}