All Downloads are FREE. Search and download functionalities are using the official Maven repository.

raw.compiler.base.Tree.scala Maven / Gradle / Ivy

There is a newer version: 0.33.11
Show newest version
/*
 * Copyright 2023 RAW Labs S.A.
 *
 * Use of this software is governed by the Business Source License
 * included in the file licenses/BSL.txt.
 *
 * As of the Change Date specified in that file, in accordance with
 * the Business Source License, use of this software will be governed
 * by the Apache License, Version 2.0, included in the file
 * licenses/APL.txt.
 */

package raw.compiler.base

import org.apache.commons.lang3.StringUtils
import org.bitbucket.inkytonik.kiama.rewriting.Rewriter.{everywhere, query}
import raw.compiler.base.source._
import raw.compiler.common.source.ErrorType
import raw.utils._

abstract class Tree[N <: BaseNode: Manifest, P <: N: Manifest, E <: N: Manifest](
    protected val originalRoot: P,
    ensureTree: Boolean
)(implicit programContext: ProgramContext)
    extends BaseTree[N, P, E](ensureTree) {

  protected def cloneWithPositions(): TreeWithPositions[N, P, E]

  def checkSyntaxAnalyzer(): Boolean = {
    // Ensure it re-parses properly.
    val newTree = cloneWithPositions()

    // Ensure new tree looks like the current one
    val sameAST = newTree.root == root
    if (!sameAST) {
      val msg = s"""Original pretty printed: $pretty
        |Parsed pretty printed:   ${newTree.pretty}
        |Original AST: $root
        |Parsed AST:   ${newTree.root}
        |Difference:   ${StringUtils.difference(root.toString, newTree.root.toString)}""".stripMargin
      if (messageTooBig(msg)) {
        val p = RawUtils.saveToTemporaryFileNoDeleteOnExit(msg, "deepcheck-", ".log")
        throw new AssertionError(s"""Tree parsed differently!
          |Details in ${p.toAbsolutePath.toString}""".stripMargin)
      } else {
        throw new AssertionError(s"""Tree parsed differently!
          |$msg""".stripMargin)
      }
    }
    sameAST
  }

  def checkSemanticAnalyzer(): Boolean = {
    if (!valid) {
      // Rebuild tree with tree positions.
      val treeWithPositions = cloneWithPositions()

      // Do valid check again, which (should?) retrigger same error, but which will now include error positions in the code.
      val clonedTreeValid = treeWithPositions.valid
      assert(!clonedTreeValid, "Cloned tree is valid but original tree was not!")
      false
    } else {
      true
    }
  }

  def checkTree(): Boolean = {
    var r = true
    if (programContext.settings.checkSyntaxAnalyzers) {
      r &= checkSyntaxAnalyzer()
    }
    r &= checkSemanticAnalyzer()
    r
  }

  override def isTreeValid: Boolean = {
    val isValid = super.isTreeValid
    if (programContext.settings.onTrainingWheels && isValid) {
      // Check if any ErrorType "accidentally" left in the tree
      val collectBugs = collectNodes[N, Seq, E] {
        case e: E if containsErrorType(analyzer.tipe(e)) => e
      }
      val bugs = collectBugs(root)

      if (bugs.nonEmpty) {
        val msg = bugs
          .map { e =>
            val t = analyzer.tipe(e)
            s"""Expression: $e
              |Type: $t""".stripMargin
          }
          .mkString("\n")
        throw new AssertionError(s"""Error types found on tree that has no errors reported!
          |This is the full (broken) tree:
          |$pretty
          |This is the expression(s) that typed with an error type:
          |(just above is a pretty-printed version of the full (broken) tree):
          |$msg""".stripMargin)
        // msb: The following lines are commented but can be useful to uncomment during debugging:
        //      I found that AssertionError was sometimes hard to debug: so I "let it go" by commenting out the assert
        //      and then the plan would fail at some later point (by "chance") and we'd have the pretty printed errors
        //      to help us out. Those pretty printed errors are not available via this assertion mechanism.
        //        logger.error(
        //          s"""Error types found on tree that has no errors reported!
        //             |$msg""".stripMargin)

      }
    }
    isValid
  }

  private def containsErrorType(t: Type): Boolean = {
    val check = everywhere(query[Type] { case _: ErrorType => return true })
    check(t)
    false
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy