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

com.wavesplatform.lang.v1.testing.ScriptGen.scala Maven / Gradle / Ivy

The newest version!
package com.wavesplatform.lang.v1.testing

import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.common.utils.Base58
import com.wavesplatform.lang.v1.parser.BinaryOperation
import com.wavesplatform.lang.v1.parser.BinaryOperation.*
import com.wavesplatform.lang.v1.parser.Expressions.Pos.AnyPos
import com.wavesplatform.lang.v1.parser.Expressions.*
import com.wavesplatform.lang.v1.parser.Parser.{additionalV8Keywords, keywordsBeforeV8}
import org.scalacheck.*

import scala.reflect.ClassTag

trait ScriptGen {

  val allKeywords: Set[String] = keywordsBeforeV8 ++ additionalV8Keywords

  def CONST_LONGgen: Gen[(EXPR, Long)] = Gen.choose(Long.MinValue, Long.MaxValue).map(v => (CONST_LONG(AnyPos, v), v))

  def BOOLgen(gas: Int): Gen[(EXPR, Boolean)] =
    if (gas > 0) Gen.oneOf(GEgen(gas - 1), GTgen(gas - 1), EQ_INTgen(gas - 1), ANDgen(gas - 1), ORgen(gas - 1), IF_BOOLgen(gas - 1))
    else Gen.const((TRUE(AnyPos), true))

  def SUMgen(gas: Int): Gen[(EXPR, Long)] =
    for {
      (i1, v1) <- INTGen((gas - 2) / 2)
      (i2, v2) <- INTGen((gas - 2) / 2)
    } yield
      if ((BigInt(v1) + BigInt(v2)).isValidLong) {
        (BINARY_OP(AnyPos, i1, SUM_OP, i2), v1 + v2)
      } else {
        (BINARY_OP(AnyPos, i1, SUB_OP, i2), v1 - v2)
      }

  def SUBgen(gas: Int): Gen[(EXPR, Long)] =
    for {
      (i1, v1) <- INTGen((gas - 2) / 2)
      (i2, v2) <- INTGen((gas - 2) / 2)
    } yield
      if ((BigInt(v1) - BigInt(v2)).isValidLong) {
        (BINARY_OP(AnyPos, i1, SUB_OP, i2), v1 - v2)
      } else {
        (BINARY_OP(AnyPos, i1, SUM_OP, i2), v1 + v2)
      }

  def INTGen(gas: Int): Gen[(EXPR, Long)] =
    if (gas > 0)
      Gen.oneOf(
        CONST_LONGgen,
        SUMgen(gas - 1),
        SUBgen(gas - 1),
        IF_INTgen(gas - 1),
        INTGen(gas - 1).filter(v => (-BigInt(v._2)).isValidLong).map(e => (FUNCTION_CALL(AnyPos, PART.VALID(AnyPos, "-"), List(e._1)), -e._2))
      )
    else CONST_LONGgen

  def GEgen(gas: Int): Gen[(EXPR, Boolean)] =
    for {
      (i1, v1) <- INTGen((gas - 2) / 2)
      (i2, v2) <- INTGen((gas - 2) / 2)
    } yield (BINARY_OP(AnyPos, i1, GE_OP, i2), v1 >= v2)

  def GTgen(gas: Int): Gen[(EXPR, Boolean)] =
    for {
      (i1, v1) <- INTGen((gas - 2) / 2)
      (i2, v2) <- INTGen((gas - 2) / 2)
    } yield (BINARY_OP(AnyPos, i1, GT_OP, i2), v1 > v2)

  def EQ_INTgen(gas: Int): Gen[(EXPR, Boolean)] =
    for {
      (i1, v1) <- INTGen((gas - 2) / 2)
      (i2, v2) <- INTGen((gas - 2) / 2)
    } yield (BINARY_OP(AnyPos, i1, EQ_OP, i2), v1 == v2)

  def ANDgen(gas: Int): Gen[(EXPR, Boolean)] =
    for {
      (i1, v1) <- BOOLgen((gas - 2) / 2)
      (i2, v2) <- BOOLgen((gas - 2) / 2)
    } yield (BINARY_OP(AnyPos, i1, AND_OP, i2), v1 && v2)

  def ORgen(gas: Int): Gen[(EXPR, Boolean)] =
    for {
      (i1, v1) <- BOOLgen((gas - 2) / 2)
      (i2, v2) <- BOOLgen((gas - 2) / 2)
    } yield (BINARY_OP(AnyPos, i1, OR_OP, i2), v1 || v2)

  def IF_BOOLgen(gas: Int): Gen[(EXPR, Boolean)] =
    for {
      (cnd, vcnd) <- BOOLgen((gas - 3) / 3)
      (t, vt)     <- BOOLgen((gas - 3) / 3)
      (f, vf)     <- BOOLgen((gas - 3) / 3)
    } yield (
      IF(AnyPos, cnd, t, f),
      if (vcnd) { vt }
      else { vf }
    )

  def IF_INTgen(gas: Int): Gen[(EXPR, Long)] =
    for {
      (cnd, vcnd) <- BOOLgen((gas - 3) / 3)
      (t, vt)     <- INTGen((gas - 3) / 3)
      (f, vf)     <- INTGen((gas - 3) / 3)
    } yield (
      IF(AnyPos, cnd, t, f),
      if (vcnd) { vt }
      else { vf }
    )

  def STRgen: Gen[EXPR] =
    Gen.identifier.map(PART.VALID[String](AnyPos, _)).map(CONST_STRING(AnyPos, _))

  def LETgen(gas: Int): Gen[LET] =
    for {
      name       <- Gen.identifier.filter(!allKeywords(_))
      (value, _) <- BOOLgen((gas - 3) / 3)
    } yield LET(AnyPos, PART.VALID(AnyPos, name), value)

  def REFgen: Gen[EXPR] =
    Gen.identifier.filter(!allKeywords(_)).map(PART.VALID[String](AnyPos, _)).map(REF(AnyPos, _))

  def BLOCKgen(gas: Int): Gen[EXPR] =
    for {
      let  <- LETgen((gas - 3) / 3)
      body <- Gen.oneOf(BOOLgen((gas - 3) / 3).map(_._1), BLOCKgen((gas - 3) / 3)) // BLOCKGen wasn't add to BOOLGen since issue: NODE-700
    } yield BLOCK(AnyPos, let, body)

  private val spaceChars: Seq[Char] = " \t\n\r"

  val whitespaceChar: Gen[Char] = Gen.oneOf(spaceChars)
  val whitespaces: Gen[String] = for {
    n  <- Gen.choose(1, 5)
    xs <- Gen.listOfN(n, whitespaceChar)
  } yield xs.mkString

  def withWhitespaces(expr: String): Gen[String] =
    for {
      pred <- whitespaces
      post <- whitespaces
    } yield pred + expr + post

  private def toString[T](part: PART[T])(implicit ct: ClassTag[T]): String = part match {
    case PART.VALID(_, x: String)   => x
    case PART.VALID(_, xs: ByteStr) => Base58.encode(xs.arr)
    case _                          => throw new RuntimeException(s"Can't stringify $part")
  }

  def toString(expr: EXPR): Gen[String] = expr match {
    case CONST_LONG(_, x, _)    => withWhitespaces(s"$x")
    case REF(_, x, _, _)        => withWhitespaces(toString(x))
    case CONST_STRING(_, x, _)  => withWhitespaces(s"""\"${toString(x)}\"""")
    case CONST_BYTESTR(_, x, _) => withWhitespaces(s"""base58'${toString(x)}'""")
    case _: TRUE                => withWhitespaces("true")
    case _: FALSE               => withWhitespaces("false")
    case BINARY_OP(_, x, op: BinaryOperation, y, _, _) =>
      for {
        arg1 <- toString(x)
        arg2 <- toString(y)
      } yield s"($arg1${opsToFunctions(op)}$arg2)"
    case IF(_, cond, x, y, _, _) =>
      for {
        c <- toString(cond)
        t <- toString(x)
        f <- toString(y)
      } yield s"(if ($c) then $t else $f)"
    case BLOCK(_, let: LET, body, _, _) =>
      for {
        v         <- toString(let.value)
        b         <- toString(body)
        isNewLine <- Arbitrary.arbBool.arbitrary
        sep       <- if (isNewLine) Gen.const("\n") else withWhitespaces(";")
      } yield s"let ${toString(let.name)} = $v$sep$b"

    case FUNCTION_CALL(_, PART.VALID(_, "-"), List(CONST_LONG(_, v, _)), _, _) if v >= 0 =>
      s"-($v)"
    case FUNCTION_CALL(_, op, List(e), _, _) => toString(e).map(e => s"${toString(op)}$e")

    case x => throw new NotImplementedError(s"toString for ${x.getClass.getSimpleName}")
  }
}

trait ScriptGenParser extends ScriptGen {
  override def BOOLgen(gas: Int): Gen[(EXPR, Boolean)] = {
    if (gas > 0)
      Gen.oneOf(GEgen(gas - 1), GTgen(gas - 1), EQ_INTgen(gas - 1), ANDgen(gas - 1), ORgen(gas - 1), IF_BOOLgen(gas - 1), REFgen.map(r => (r, false)))
    else Gen.const((TRUE(AnyPos), true))
  }

  override def INTGen(gas: Int): Gen[(EXPR, Long)] =
    if (gas > 0) Gen.oneOf(CONST_LONGgen, SUMgen(gas - 1), IF_INTgen(gas - 1), REFgen.map(r => (r, 0L))) else CONST_LONGgen
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy