<commit_before>import scala.reflect.macros.blackbox.Context

import DatatypeRepresentation._

trait DeclarationGenerator {
  /** @param datatype Representation of data type
    * @return AST of generated traits and case classes
    *
    * Caution: Classes involved in datatypes must *all* be uninheritable.
    * If this is the case, then we can make the fixed point type covariant
    * without sacrificing the functor instance inside Fix[+F[+_]], which
    * is necessary to obtain covariance in something like List[+A].
    */
  def generateDeclaration(c: Context)(datatype: Variant): c.Tree = {
    import c.universe._

    val template = mkTemplate(c)(datatype.name)

    if (datatype.cases.isEmpty)
      q"sealed trait $template"
    else {
      q"""
        sealed trait $template [..${generateCaseNames(c)(datatype.cases)}]
        ..${generateCases(c)(template, datatype.cases)}
      """
    }
  }

  /** create the name of the template trait by appending T */
  def mkTemplate(c: Context)(name: String): c.TypeName = c.universe.TypeName(name + "T")

  /** @param cases a bunch of named variants
    * @return their names as TypeName
    */
  def generateCaseNames(c: Context)(cases: Many[RecordOrHole]): Many[c.Tree] = {
    import c.universe._
    cases.map(record => covariantTypeParam(c)(record.name))
  }

  /** @param name name of type parameter
    * @return covariant type parameter of that name
    */
  def covariantTypeParam(c: Context)(name: String): c.Tree = {
    import c.universe._
    val q"type Dummy[$result]" = q"type Dummy[+${TypeName(name)}]"
    result
  }

  /** @param template TypeName of the template trait of the variant
    * @param cases cases of the variant
    * @return generated code for cases of the variant
    */
  def generateCases(c: Context)(template: c.TypeName, cases: Many[RecordOrHole]): Many[c.Tree] = {
    import c.universe._
    cases.zipWithIndex flatMap {
      case (Record(name, Many()), i) =>
        val typeName = TypeName(name)
        val termName = TermName(name)
        val params = namedParamsWithNothing(c)(typeName, i, cases.size)
        Many(
          q"sealed trait $typeName extends $template[..$params]",
          q"case object $termName extends $typeName")
    }
  }

  /** @param param type name at position `index`
    * @param index index of `tpe`
    * @param size  size of whole param list
    */
  def namedParamsWithNothing(c: Context)(param: c.TypeName, index: Int, size: Int): Many[c.TypeName] = {
    import c.universe._
    val nothing = TypeName("Nothing")
    Many.tabulate(size)(i => if (i == index) param else nothing)
  }
}

object DeclarationGenerator {
  /** test macros */
  object Tests extends DeclarationGenerator {
    import scala.language.experimental.macros
    import scala.annotation.StaticAnnotation

    def err(msg: String): Nothing = { System.err println msg ; sys error "got error" }

    def assertEqual(c: Context)(expected: c.Tree, actual: c.Tree): c.Expr[Any] = {
      import c.universe._
      // assert(actual.duplicate != actual) // this is actually true!
      // resorting to string comparison.
      // doesn't seem to have anything better.
      val eRaw = showRaw(expected)
      val aRaw = showRaw(actual)
      if (eRaw != aRaw)
        err(s"\nExpected:\n$eRaw\n\nActual:\n$aRaw\n")
      else<commit_msg>make error reporting private, prevent mis-import<commit_after>import scala.reflect.macros.blackbox.Context

import DatatypeRepresentation._

trait DeclarationGenerator {
  /** @param datatype Representation of data type
    * @return AST of generated traits and case classes
    *
    * Caution: Classes involved in datatypes must *all* be uninheritable.
    * If this is the case, then we can make the fixed point type covariant
    * without sacrificing the functor instance inside Fix[+F[+_]], which
    * is necessary to obtain covariance in something like List[+A].
    */
  def generateDeclaration(c: Context)(datatype: Variant): c.Tree = {
    import c.universe._

    val template = mkTemplate(c)(datatype.name)

    if (datatype.cases.isEmpty)
      q"sealed trait $template"
    else {
      q"""
        sealed trait $template [..${generateCaseNames(c)(datatype.cases)}]
        ..${generateCases(c)(template, datatype.cases)}
      """
    }
  }

  /** create the name of the template trait by appending T */
  def mkTemplate(c: Context)(name: String): c.TypeName = c.universe.TypeName(name + "T")

  /** @param cases a bunch of named variants
    * @return their names as TypeName
    */
  def generateCaseNames(c: Context)(cases: Many[RecordOrHole]): Many[c.Tree] = {
    import c.universe._
    cases.map(record => covariantTypeParam(c)(record.name))
  }

  /** @param name name of type parameter
    * @return covariant type parameter of that name
    */
  def covariantTypeParam(c: Context)(name: String): c.Tree = {
    import c.universe._
    val q"type Dummy[$result]" = q"type Dummy[+${TypeName(name)}]"
    result
  }

  /** @param template TypeName of the template trait of the variant
    * @param cases cases of the variant
    * @return generated code for cases of the variant
    */
  def generateCases(c: Context)(template: c.TypeName, cases: Many[RecordOrHole]): Many[c.Tree] = {
    import c.universe._
    cases.zipWithIndex flatMap {
      case (Record(name, Many()), i) =>
        val typeName = TypeName(name)
        val termName = TermName(name)
        val params = namedParamsWithNothing(c)(typeName, i, cases.size)
        Many(
          q"sealed trait $typeName extends $template[..$params]",
          q"case object $termName extends $typeName")
    }
  }

  /** @param param type name at position `index`
    * @param index index of `tpe`
    * @param size  size of whole param list
    */
  def namedParamsWithNothing(c: Context)(param: c.TypeName, index: Int, size: Int): Many[c.TypeName] = {
    import c.universe._
    val nothing = TypeName("Nothing")
    Many.tabulate(size)(i => if (i == index) param else nothing)
  }
}

object DeclarationGenerator {
  /** test macros */
  object Tests extends DeclarationGenerator {
    import scala.language.experimental.macros
    import scala.annotation.StaticAnnotation

    private[this] def err(msg: String): Nothing = { System.err println msg ; sys error "got error" }

    def assertEqual(c: Context)(expected: c.Tree, actual: c.Tree): c.Expr[Any] = {
      import c.universe._
      // assert(actual.duplicate != actual) // this is actually true!
      // resorting to string comparison.
      // doesn't seem to have anything better.
      val eRaw = showRaw(expected)
      val aRaw = showRaw(actual)
      if (eRaw != aRaw)
        err(s"\nExpected:\n$eRaw\n\nActual:\n$aRaw\n")
      else
{'fraction_non_alphanumeric': 0.11806691449814126, 'fraction_numerical': 0.0, 'mean_word_length': 3.111246943765281, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, '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': '2625232', 'n_tokens_mistral': 2057, 'n_tokens_neox': 1994, 'n_words': 619}