<commit_before>package com.wavesplatform.lang

import cats.data.EitherT
import com.wavesplatform.lang.Common._
import com.wavesplatform.lang.Terms.Typed._
import com.wavesplatform.lang.Terms._
import com.wavesplatform.lang.TypeInfo._
import com.wavesplatform.lang.ctx._
import com.wavesplatform.lang.ctx.impl.PureContext
import com.wavesplatform.lang.testing.ScriptGen
import org.scalatest.prop.PropertyChecks
import org.scalatest.{Matchers, PropSpec}
import com.wavesplatform.lang.ctx.impl.PureContext._

class EvaluatorTest extends PropSpec with PropertyChecks with Matchers with ScriptGen with NoShrink {

  private def ev[T: TypeInfo](context: Context = PureContext.instance, expr: EXPR): Either[_, _] = Evaluator[T](context, expr)
  private def simpleDeclarationAndUsage(i: Int)                                                  = BLOCK(Some(LET("x", CONST_LONG(i))), REF("x", LONG), LONG)

  property("successful on very deep expressions (stack overflow check)") {
    val term = (1 to 100000).foldLeft[EXPR](CONST_LONG(0))((acc, _) => FUNCTION_CALL(sumLong.header, List(acc, CONST_LONG(1)), LONG))
    ev[Long](expr = term) shouldBe Right(100000)
  }

  property("successful on unused let") {
    ev[Long](
      expr = BLOCK(
        Some(LET("x", CONST_LONG(3))),
        CONST_LONG(3),
        LONG
      )) shouldBe Right(3)
  }

  property("successful on x = y") {
    ev[Long](
      expr = BLOCK(Some(LET("x", CONST_LONG(3))),
                   BLOCK(
                     Some(LET("y", REF("x", LONG))),
                     FUNCTION_CALL(sumLong.header, List(REF("x", LONG), REF("y", LONG)), LONG),
                     LONG
                   ),
                   LONG)) shouldBe Right(6)
  }

  property("successful on simple get") {
    ev[Long](expr = simpleDeclarationAndUsage(3)) shouldBe Right(3)
  }

  property("successful on get used further in expr") {
    ev[Boolean](
      expr = BLOCK(
        Some(LET("x", CONST_LONG(3))),
        FUNCTION_CALL(eqLong.header, List(REF("x", LONG), CONST_LONG(2)), BOOLEAN),
        BOOLEAN
      )) shouldBe Right(false)
  }

  property("successful on multiple lets") {
    ev[Boolean](
      expr = BLOCK(
        Some(LET("x", CONST_LONG(3))),
        BLOCK(Some(LET("y", CONST_LONG(3))), FUNCTION_CALL(eqLong.header, List(REF("x", LONG), REF("y", LONG)), BOOLEAN), BOOLEAN),
        BOOLEAN
      )) shouldBe Right(true)
  }

  property("successful on multiple lets with expression") {
    ev[Boolean](
      expr = BLOCK(
        Some(LET("x", CONST_LONG(3))),
        BLOCK(
          Some(LET("y", FUNCTION_CALL(sumLong.header, List(CONST_LONG(3), CONST_LONG(0)), LONG))),
          FUNCTION_CALL(eqLong.header, List(REF("x", LONG), REF("y", LONG)), BOOLEAN),
          BOOLEAN
        ),
        BOOLEAN
      )) shouldBe Right(true)
  }

  property("successful on deep type resolution") {
    ev[Long](expr = IF(FUNCTION_CALL(eqLong.header, List(CONST_LONG(1), CONST_LONG(2)), BOOLEAN), simpleDeclarationAndUsage(3), CONST_LONG(4), LONG)) shouldBe Right(
      4)
  }

  property("successful on same value names in different branches") {
    val expr =
      IF(FUNCTION_CALL(eqLong.header, List(CONST_LONG(1), CONST_LONG(2)), BOOLEAN), simpleDeclarationAndUsage(3), simpleDeclarationAndUsage(4), LONG)
    ev[Long](expr = expr) shouldBe Right(4)
  }

  property("fails if override") {
    ev[Long](
      expr = BLOCK(
        Some(LET("x", CONST_LONG(3))),
        BLOCK(
          Some(LET("x", FUNCTION_CALL(sumLong.header, List(CONST_LONG(3), CONST_LONG(0)), LONG))),
          FUNCTION_CALL(eqLong.header, List(REF("x", LONG), CONST_LONG(1)), LONG),
          LONG
        ),
        LONG
      )) should produce("already defined")
  }

  property("fails if definition not found") {
    ev[Long](expr = FUNCTION_CALL(sumLong.header, List(REF("x", LONG), CONST_LONG(2)), LONG)) should produce("A definition of 'x' is not found")
  }

  property("custom type field access") {
    val pointType     = PredefType("Point", List("X" -> LONG, "Y"                           -> LONG))
    val pointInstance = Obj(Map("X"                  -> LazyVal(LONG)(EitherT.pure(3)), "Y" -> LazyVal(LONG)(EitherT.pure(4))))
    ev[Long](
      context = Context(
        typeDefs = Map(pointType.name -> pointType),
        letDefs = Map(("p", LazyVal(TYPEREF(pointType.name))(EitherT.pure(pointInstance)))),
        functions = Map.empty
      ),
      expr = FUNCTION_CALL(sumLong.header, List(GETTER(REF("p", TYPEREF("Point")), "X", LONG), CONST_LONG(2)), LONG)
    ) shouldBe Right(5)
  }

  property("lazy let evaluation doesn't throw if not used") {
    val pointType     = PredefType("Point", List(("X", LONG), ("Y", LONG)))
    val pointInstance = Obj(Map(("X", LazyVal(LONG)(EitherT.pure(3))), ("Y", LazyVal(LONG)(EitherT.pure(4)))))
    val context = Context(
      typeDefs = Map((pointType.name, pointType)),
      letDefs = Map(("p", LazyVal(TYPEREF(pointType.name))(EitherT.pure(pointInstance))), ("badVal", LazyVal(LONG)(EitherT.leftT("Error")))),
      functions = Map.empty
    )
    ev[Long](
      context = context,
      expr = BLOCK(Some(LET("Z", REF("badVal", LONG))),
                   FUNCTION_CALL(sumLong.header, List(GETTER(REF("p", TYPEREF("Point")), "X", LONG), CONST_LONG(2)), LONG),
                   LONG)
    ) shouldBe Right(5)
  }

  property("field and value are evaluated maximum once") {
    var fieldCalculated = 0
    var valueCalculated = 0

    val pointType = PredefType("Point", List(("X", LONG), ("Y", LONG)))
    val pointInstance = Obj(Map(("X", LazyVal(LONG)(EitherT.pure {
      fieldCalculated = fieldCalculated + 1
      3
    })), ("Y", LazyVal(LONG)(EitherT.pure(4)))))
    val context = Context(
      typeDefs = Map((pointType.name, pointType)),
      letDefs = Map(("p", LazyVal(TYPEREF(pointType.name))(EitherT.pure(pointInstance))), ("h", LazyVal(LONG)(EitherT.pure {
        valueCalculated = valueCalculated + 1
        4
      }))),
      functions = Map.empty
    )
    ev[Long](
      context = context,
      expr = FUNCTION_CALL(sumLong.header, List(GETTER(REF("p", TYPEREF("Point")), "X", LONG), GETTER(REF("p", TYPEREF("Point")), "X", LONG)), LONG)
    ) shouldBe Right(6)

    ev[Long](
      context = context,
      expr = FUNCTION_CALL(sumLong.header, List(REF("h", LONG), REF("h", LONG)), LONG)
    ) shouldBe Right(8)

    fieldCalculated shouldBe 1
    valueCalculated shouldBe 1
  }

  property("let is evaluated maximum once") {
    var functionEvaluated = 0

    val f = PredefFunction("F", LONG, List(("_", LONG))) { _ =>
      functionEvaluated = functionEvaluated + 1
      Right(1L)
    }

    val context = Context(
      typeDefs = Map.empty,
      letDefs = Map.empty,
      functions = Map(f.header -> f)
    )
    ev[Long](
      context = context,
      expr = BLOCK(Some(LET("X", FUNCTION_CALL(f.header, List(CONST_LONG(1000)), LONG))),
                   FUNCTION_CALL(sumLong.header, List(REF("X", LONG), REF("X", LONG)), LONG),
                   LONG)
    ) shouldBe Right(2L)

    functionEvaluated shouldBe 1

  }

  property("successful on ref getter evaluation") {
    val fooType = PredefType("Foo", List(("bar", STRING), ("buz", LONG)))

    val fooInstance =
      Obj(Map("bar" -> LazyVal(STRING)(EitherT.pure("bAr")), "buz" -> LazyVal(LONG)(EitherT.pure(1L))))

    val context = Context(
      typeDefs = Map(fooType.name -> fooType),
      letDefs = Map("fooInstance" -> LazyVal(fooType.typeRef)(EitherT.pure(fooInstance))),
      functions = Map.empty
    )

    val expr = GETTER(REF("fooInstance", fooType.typeRef), "bar", STRING)<commit_msg>NODE-665: fixing tests after dropping Typed.BINARY_OP<commit_after>package com.wavesplatform.lang
import cats.syntax.semigroup._
import cats.data.EitherT
import com.wavesplatform.lang.Common._
import com.wavesplatform.lang.Terms.Typed._
import com.wavesplatform.lang.Terms._
import com.wavesplatform.lang.TypeInfo._
import com.wavesplatform.lang.ctx._
import com.wavesplatform.lang.ctx.Context._
import com.wavesplatform.lang.ctx.impl.PureContext
import com.wavesplatform.lang.testing.ScriptGen
import org.scalatest.prop.PropertyChecks
import org.scalatest.{Matchers, PropSpec}
import com.wavesplatform.lang.ctx.impl.PureContext._

class EvaluatorTest extends PropSpec with PropertyChecks with Matchers with ScriptGen with NoShrink {

  private def ev[T: TypeInfo](context: Context = PureContext.instance, expr: EXPR): Either[_, _] = Evaluator[T](context, expr)
  private def simpleDeclarationAndUsage(i: Int)                                                  = BLOCK(Some(LET("x", CONST_LONG(i))), REF("x", LONG), LONG)

  property("successful on very deep expressions (stack overflow check)") {
    val term = (1 to 100000).foldLeft[EXPR](CONST_LONG(0))((acc, _) => FUNCTION_CALL(sumLong.header, List(acc, CONST_LONG(1)), LONG))
    ev[Long](expr = term) shouldBe Right(100000)
  }

  property("successful on unused let") {
    ev[Long](
      expr = BLOCK(
        Some(LET("x", CONST_LONG(3))),
        CONST_LONG(3),
        LONG
      )) shouldBe Right(3)
  }

  property("successful on x = y") {
    ev[Long](
      expr = BLOCK(Some(LET("x", CONST_LONG(3))),
                   BLOCK(
                     Some(LET("y", REF("x", LONG))),
                     FUNCTION_CALL(sumLong.header, List(REF("x", LONG), REF("y", LONG)), LONG),
                     LONG
                   ),
                   LONG)) shouldBe Right(6)
  }

  property("successful on simple get") {
    ev[Long](expr = simpleDeclarationAndUsage(3)) shouldBe Right(3)
  }

  property("successful on get used further in expr") {
    ev[Boolean](
      expr = BLOCK(
        Some(LET("x", CONST_LONG(3))),
        FUNCTION_CALL(eqLong.header, List(REF("x", LONG), CONST_LONG(2)), BOOLEAN),
        BOOLEAN
      )) shouldBe Right(false)
  }

  property("successful on multiple lets") {
    ev[Boolean](
      expr = BLOCK(
        Some(LET("x", CONST_LONG(3))),
        BLOCK(Some(LET("y", CONST_LONG(3))), FUNCTION_CALL(eqLong.header, List(REF("x", LONG), REF("y", LONG)), BOOLEAN), BOOLEAN),
        BOOLEAN
      )) shouldBe Right(true)
  }

  property("successful on multiple lets with expression") {
    ev[Boolean](
      expr = BLOCK(
        Some(LET("x", CONST_LONG(3))),
        BLOCK(
          Some(LET("y", FUNCTION_CALL(sumLong.header, List(CONST_LONG(3), CONST_LONG(0)), LONG))),
          FUNCTION_CALL(eqLong.header, List(REF("x", LONG), REF("y", LONG)), BOOLEAN),
          BOOLEAN
        ),
        BOOLEAN
      )) shouldBe Right(true)
  }

  property("successful on deep type resolution") {
    ev[Long](expr = IF(FUNCTION_CALL(eqLong.header, List(CONST_LONG(1), CONST_LONG(2)), BOOLEAN), simpleDeclarationAndUsage(3), CONST_LONG(4), LONG)) shouldBe Right(
      4)
  }

  property("successful on same value names in different branches") {
    val expr =
      IF(FUNCTION_CALL(eqLong.header, List(CONST_LONG(1), CONST_LONG(2)), BOOLEAN), simpleDeclarationAndUsage(3), simpleDeclarationAndUsage(4), LONG)
    ev[Long](expr = expr) shouldBe Right(4)
  }

  property("fails if override") {
    ev[Long](
      expr = BLOCK(
        Some(LET("x", CONST_LONG(3))),
        BLOCK(
          Some(LET("x", FUNCTION_CALL(sumLong.header, List(CONST_LONG(3), CONST_LONG(0)), LONG))),
          FUNCTION_CALL(eqLong.header, List(REF("x", LONG), CONST_LONG(1)), LONG),
          LONG
        ),
        LONG
      )) should produce("already defined")
  }

  property("fails if definition not found") {
    ev[Long](expr = FUNCTION_CALL(sumLong.header, List(REF("x", LONG), CONST_LONG(2)), LONG)) should produce("A definition of 'x' is not found")
  }

  property("custom type field access") {
    val pointType     = PredefType("Point", List("X" -> LONG, "Y"                           -> LONG))
    val pointInstance = Obj(Map("X"                  -> LazyVal(LONG)(EitherT.pure(3)), "Y" -> LazyVal(LONG)(EitherT.pure(4))))
    ev[Long](
      context = PureContext.instance |+| Context(
        typeDefs = Map(pointType.name -> pointType),
        letDefs = Map(("p", LazyVal(TYPEREF(pointType.name))(EitherT.pure(pointInstance)))),
        functions = Map.empty
      ),
      expr = FUNCTION_CALL(sumLong.header, List(GETTER(REF("p", TYPEREF("Point")), "X", LONG), CONST_LONG(2)), LONG)
    ) shouldBe Right(5)
  }

  property("lazy let evaluation doesn't throw if not used") {
    val pointType     = PredefType("Point", List(("X", LONG), ("Y", LONG)))
    val pointInstance = Obj(Map(("X", LazyVal(LONG)(EitherT.pure(3))), ("Y", LazyVal(LONG)(EitherT.pure(4)))))
    val context = PureContext.instance |+| Context(
      typeDefs = Map((pointType.name, pointType)),
      letDefs = Map(("p", LazyVal(TYPEREF(pointType.name))(EitherT.pure(pointInstance))), ("badVal", LazyVal(LONG)(EitherT.leftT("Error")))),
      functions = Map.empty
    )
    ev[Long](
      context = context,
      expr = BLOCK(Some(LET("Z", REF("badVal", LONG))),
                   FUNCTION_CALL(sumLong.header, List(GETTER(REF("p", TYPEREF("Point")), "X", LONG), CONST_LONG(2)), LONG),
                   LONG)
    ) shouldBe Right(5)
  }

  property("field and value are evaluated maximum once") {
    var fieldCalculated = 0
    var valueCalculated = 0

    val pointType = PredefType("Point", List(("X", LONG), ("Y", LONG)))
    val pointInstance = Obj(Map(("X", LazyVal(LONG)(EitherT.pure {
      fieldCalculated = fieldCalculated + 1
      3
    })), ("Y", LazyVal(LONG)(EitherT.pure(4)))))
    val context = PureContext.instance |+| Context(
      typeDefs = Map((pointType.name, pointType)),
      letDefs = Map(("p", LazyVal(TYPEREF(pointType.name))(EitherT.pure(pointInstance))), ("h", LazyVal(LONG)(EitherT.pure {
        valueCalculated = valueCalculated + 1
        4
      }))),
      functions = Map.empty
    )
    ev[Long](
      context = context,
      expr = FUNCTION_CALL(sumLong.header, List(GETTER(REF("p", TYPEREF("Point")), "X", LONG), GETTER(REF("p", TYPEREF("Point")), "X", LONG)), LONG)
    ) shouldBe Right(6)

    ev[Long](
      context = context,
      expr = FUNCTION_CALL(sumLong.header, List(REF("h", LONG), REF("h", LONG)), LONG)
    ) shouldBe Right(8)

    fieldCalculated shouldBe 1
    valueCalculated shouldBe 1
  }

  property("let is evaluated maximum once") {
    var functionEvaluated = 0

    val f = PredefFunction("F", LONG, List(("_", LONG))) { _ =>
      functionEvaluated = functionEvaluated + 1
      Right(1L)
    }

    val context = PureContext.instance |+| Context(
      typeDefs = Map.empty,
      letDefs = Map.empty,
      functions = Map(f.header -> f)
    )
    ev[Long](
      context = context,
      expr = BLOCK(Some(LET("X", FUNCTION_CALL(f.header, List(CONST_LONG(1000)), LONG))),
                   FUNCTION_CALL(sumLong.header, List(REF("X", LONG), REF("X", LONG)), LONG),
                   LONG)
    ) shouldBe Right(2L)

    functionEvaluated shouldBe 1

  }

  property("successful on ref getter evaluation") {
    val fooType = PredefType("Foo", List(("bar", STRING), ("buz", LONG)))

    val fooInstance =
      Obj(Map("bar" -> LazyVal(STRING)(EitherT.pure("bAr")), "buz" -> LazyVal(LONG)(EitherT.pure(1L))))

    val context = Context(
      typeDefs = Map(fooType.name -> fooType),
      letDefs = Map("fooInstance" -> LazyVal(fooType.typeRef)(EitherT.pure(fooInstance))),
      functions = Map.empty
    )

    val expr = GETTER(REF("fooInstance", fooType.typeRef), "bar", STRING)
{'fraction_non_alphanumeric': 0.15798678585308978, 'fraction_numerical': 0.009651509262857884, 'mean_word_length': 3.1693221712125306, 'pattern_counts': {'":': 0, '<': 3, '<?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': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1088428', 'n_tokens_mistral': 5327, 'n_tokens_neox': 4996, 'n_words': 1056}