Skip to content

Commit

Permalink
add unary LogicalNot, Negation, BitwiseInversion and binary BinXor,
Browse files Browse the repository at this point in the history
BitOr, BitAnd, BitXor and numeric bit shift ops to typer;
  • Loading branch information
greenhat committed Jan 8, 2019
1 parent f9fc3e9 commit cb2adaa
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 12 deletions.
7 changes: 5 additions & 2 deletions src/main/scala/sigmastate/lang/SigmaBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,9 @@ trait SigmaBuilder {
def mkBitOr[T <: SNumericType](left: Value[T], right: Value[T]): Value[T]
def mkBitAnd[T <: SNumericType](left: Value[T], right: Value[T]): Value[T]
def mkBitXor[T <: SNumericType](left: Value[T], right: Value[T]): Value[T]
def mkBitShiftRight[T <: SNumericType](left: Value[T], right: Value[T]): Value[T]
def mkBitShiftLeft[T <: SNumericType](left: Value[T], right: Value[T]): Value[T]
def mkBitShiftRight[T <: SNumericType](bits: Value[T], shift: Value[T]): Value[T]
def mkBitShiftLeft[T <: SNumericType](bits: Value[T], shift: Value[T]): Value[T]
def mkBitShiftRightZeroed[T <: SNumericType](bits: Value[T], shift: Value[T]): Value[T]

def liftAny(v: Any): Nullable[SValue] = v match {
case arr: Array[Boolean] => Nullable(mkCollectionConstant[SBoolean.type](arr, SBoolean))
Expand Down Expand Up @@ -568,6 +569,8 @@ class StdSigmaBuilder extends SigmaBuilder {
override def mkBitShiftLeft[T <: SNumericType](bits: Value[T], shift: Value[T]): Value[T] =
BitOp(bits, shift, OpCodes.BitShiftLeftCode)

override def mkBitShiftRightZeroed[T <: SNumericType](bits: Value[T], shift: Value[T]): Value[T] =
BitOp(bits, shift, OpCodes.BitShiftRightZeroedCode)
}

trait TypeConstraintCheck {
Expand Down
46 changes: 39 additions & 7 deletions src/main/scala/sigmastate/lang/SigmaTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import sigmastate.Values._
import sigmastate._
import SCollection.SBooleanArray
import sigmastate.lang.Terms._
import sigmastate.lang.exceptions.{InvalidBinaryOperationParameters, MethodNotFound, TyperException, NonApplicableMethod}
import sigmastate.lang.exceptions._
import sigmastate.lang.SigmaPredef._
import sigmastate.serialization.OpCodes
import sigmastate.utxo._
Expand Down Expand Up @@ -203,10 +203,14 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe
throw new NonApplicableMethod(s"Unknown symbol $m, which is used as ($newObj) $m ($newArgs)")
}
case _: SNumericType => (m, newArgs) match {
case("+" | "*", Seq(r)) => r.tpe match {
case("+" | "*" | "^" | ">>" | "<<" | ">>>", Seq(r)) => r.tpe match {
case _: SNumericType => m match {
case "*" => bimap(env, "*", newObj.asNumValue, r.asNumValue)(mkMultiply)(tT, tT)
case "+" => bimap(env, "+", newObj.asNumValue, r.asNumValue)(mkPlus)(tT, tT)
case "^" => bimap(env, "^", newObj.asNumValue, r.asNumValue)(mkBitXor)(tT, tT)
case ">>" => bimap(env, ">>", newObj.asNumValue, r.asNumValue)(mkBitShiftRight)(tT, tT)
case "<<" => bimap(env, "<<", newObj.asNumValue, r.asNumValue)(mkBitShiftLeft)(tT, tT)
case ">>>" => bimap(env, ">>>", newObj.asNumValue, r.asNumValue)(mkBitShiftRightZeroed)(tT, tT)
}
case _ =>
throw new InvalidBinaryOperationParameters(s"Invalid argument type for $m, expected ${newObj.tpe} but was ${r.tpe}")
Expand All @@ -232,10 +236,13 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe
throw new NonApplicableMethod(s"Unknown symbol $m, which is used as ($newObj) $m ($newArgs)")
}
case SBoolean => (m, newArgs) match {
case ("||" | "&&", Seq(r)) => r.tpe match {
case SBoolean =>
val res = if (m == "||") mkBinOr(newObj.asBoolValue, r.asBoolValue) else mkBinAnd(newObj.asBoolValue, r.asBoolValue)
res
case ("||" | "&&" | "^", Seq(r)) => r.tpe match {
case SBoolean => m match {
case "||" => mkBinOr(newObj.asBoolValue, r.asBoolValue)
case "&&" => mkBinAnd(newObj.asBoolValue, r.asBoolValue)
case "^" => mkBinXor(newObj.asBoolValue, r.asBoolValue)

}
case SSigmaProp =>
val (a,b) = (newObj.asBoolValue, Select(r, SSigmaProp.IsProven, Some(SBoolean)).asBoolValue)
val res = if (m == "||") mkBinOr(a,b) else mkBinAnd(a,b)
Expand Down Expand Up @@ -320,6 +327,10 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe
case ArithOp(l, r, OpCodes.MinCode) => bimap(env, "min", l.asNumValue, r.asNumValue)(mkMin)(tT, tT)
case ArithOp(l, r, OpCodes.MaxCode) => bimap(env, "max", l.asNumValue, r.asNumValue)(mkMax)(tT, tT)

case BitOp(l, r, OpCodes.BitOrCode) => bimap(env, "|", l.asNumValue, r.asNumValue)(mkBitOr)(tT, tT)
case BitOp(l, r, OpCodes.BitAndCode) => bimap(env, "&", l.asNumValue, r.asNumValue)(mkBitAnd)(tT, tT)
case BitOp(l, r, OpCodes.BitXorCode) => bimap(env, "^", l.asNumValue, r.asNumValue)(mkBitXor)(tT, tT)

case Xor(l, r) => bimap(env, "|", l, r)(mkXor)(SByteArray, SByteArray)
case MultiplyGroup(l, r) => bimap(env, "*", l, r)(mkMultiplyGroup)(SGroupElement, SGroupElement)

Expand Down Expand Up @@ -358,6 +369,10 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe
error(s"Invalid operation ProofBytes: expected argument types ($SSigmaProp); actual: (${p.tpe})")
SigmaPropBytes(p1.asSigmaProp)

case LogicalNot(i) => unmap(env, "!", i.asBoolValue)(mkLogicalNot)(SBoolean)
case Negation(i) => unmap[SNumericType](env, "-", i.asNumValue)(mkNegation)(tT)
case BitInversion(i) => unmap[SNumericType](env, "~", i.asNumValue)(mkBitInversion)(tT)

case SomeValue(x) => SomeValue(assignType(env, x))
case v: NoneValue[_] => v

Expand Down Expand Up @@ -413,7 +428,10 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe
val r1 = assignType(env, r).asValue[T]
val safeMkNode = { (left: Value[T], right: Value[T]) =>
try {
mkNode(left, right)
val node = mkNode(left, right)
if (node.tpe == NoType)
error("No type can be assigned to expression")
node
} catch {
case e: Throwable =>
throw new InvalidBinaryOperationParameters(s"operation: $op: $e")
Expand Down Expand Up @@ -445,6 +463,20 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe
}
}

def unmap[T <: SType](env: Map[String, SType], op: String, i: Value[T])
(newNode: Value[T] => SValue)
(tArg: SType): SValue = {
val i1 = assignType(env, i).asValue[T]
if (!i1.tpe.isNumType && i1.tpe != tArg)
throw new InvalidUnaryOperationParameters(s"Invalid unary op $op: expected argument type $tArg, actual: ${i1.tpe}")
try {
newNode(i1)
} catch {
case e: Throwable =>
throw new InvalidUnaryOperationParameters(s"operation $op error: $e")
}
}

def typecheck(bound: SValue): SValue = {
val assigned = assignType(predefinedEnv, bound)
if (assigned.tpe == NoType) error(s"No type can be assigned to expression $assigned")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package sigmastate.lang.exceptions
final class InvalidBinaryOperationParameters(message: String, source: Option[SourceContext] = None)
extends TyperException(message, source)

final class InvalidUnaryOperationParameters(message: String, source: Option[SourceContext] = None)
extends TyperException(message, source)

final class MethodNotFound(message: String, source: Option[SourceContext] = None)
extends TyperException(message, source)

Expand Down
57 changes: 54 additions & 3 deletions src/test/scala/sigmastate/lang/SigmaCompilerTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ import sigmastate._
import sigmastate.helpers.SigmaTestingCommons
import sigmastate.interpreter.Interpreter.ScriptEnv
import sigmastate.lang.Terms.ZKProofBlock
import sigmastate.lang.exceptions.{InvalidArguments, TyperException}
import sigmastate.lang.exceptions.{CosterException, InvalidArguments, TyperException}
import sigmastate.lang.syntax.ParserException
import sigmastate.serialization.ValueSerializer
import sigmastate.serialization.generators.ValueGenerators
import sigmastate.utxo.{ByIndex, GetVar}

class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGenerators {
import CheckingSigmaBuilder._
implicit lazy val IR = new TestingIRContext

private def comp(env: ScriptEnv, x: String): Value[SType] = compileWithCosting(env, x)
private def comp(x: String): Value[SType] = compileWithCosting(env, x)
private def compWOCosting(x: String): Value[SType] = compile(env, x)
private def compWOCosting(env: ScriptEnv, x: String): Value[SType] = compile(env, x)

private def fail(env: ScriptEnv, x: String, index: Int, expected: Any): Unit = {
try {
Expand All @@ -37,6 +40,12 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen
}
}

private def testMissingCosting(script: String, expected: SValue): Unit = {
compWOCosting(script) shouldBe expected
// when implemented in coster this should be changed to a positive expectation
an [CosterException] should be thrownBy comp(env, script)
}

property("array indexed access") {
comp(env, "Coll(1)(0)") shouldBe
ByIndex(ConcreteCollection(IndexedSeq(IntConstant(1)))(SInt), 0)
Expand Down Expand Up @@ -92,8 +101,9 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen
comp("atLeast(2, Coll[SigmaProp](p1, p2))") shouldBe AtLeast(2, p1, p2)
}

ignore("ZKProof") { // costing is missing
comp("ZKProof { sigmaProp(HEIGHT > 1000) }") shouldBe ZKProofBlock(BoolToSigmaProp(GT(Height, IntConstant(1000))))
property("ZKProof") {
testMissingCosting("ZKProof { sigmaProp(HEIGHT > 1000) }",
ZKProofBlock(BoolToSigmaProp(GT(Height, IntConstant(1000)))))
}

property("sigmaProp") {
Expand Down Expand Up @@ -158,4 +168,45 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen
property("decodePoint") {
comp(env, "decodePoint(Coll[Byte](1.toByte))") shouldBe DecodePoint(ConcreteCollection(ByteConstant(1)))
}

property("logicalNot") {
testMissingCosting("!true", LogicalNot(TrueLeaf))
}

property("Negation") {
testMissingCosting("-HEIGHT", Negation(Height))
}

property("BitInversion") {
testMissingCosting("~1", BitInversion(IntConstant(1)))
}

property("LogicalXor") {
testMissingCosting("true ^ false", BinXor(TrueLeaf, FalseLeaf))
}

property("BitwiseOr") {
testMissingCosting("1 | 2", mkBitOr(IntConstant(1), IntConstant(2)))
}

property("BitwiseAnd") {
testMissingCosting("1 & 2", mkBitAnd(IntConstant(1), IntConstant(2)))
}

property("BitwiseXor") {
testMissingCosting("1 ^ 2", mkBitXor(IntConstant(1), IntConstant(2)))
}

property("BitShiftRight") {
testMissingCosting("1 >> 2", mkBitShiftRight(IntConstant(1), IntConstant(2)))
}

property("BitShiftLeft") {
testMissingCosting("1 << 2", mkBitShiftLeft(IntConstant(1), IntConstant(2)))
}

property("BitShiftRightZeroed") {
testMissingCosting("1 >>> 2", mkBitShiftRightZeroed(IntConstant(1), IntConstant(2)))
}

}
49 changes: 49 additions & 0 deletions src/test/scala/sigmastate/lang/SigmaTyperTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -528,4 +528,53 @@ class SigmaTyperTest extends PropSpec with PropertyChecks with Matchers with Lan
property("executeFromVar") {
typecheck(env, "executeFromVar[Boolean](1)") shouldBe SBoolean
}

property("LogicalNot") {
typecheck(env, "!true") shouldBe SBoolean
an [TyperException] should be thrownBy typecheck(env, "!getVar[SigmaProp](1).get")
}

property("Negation") {
typecheck(env, "-HEIGHT") shouldBe SInt
an [TyperException] should be thrownBy typecheck(env, "-true")
}

property("BitInversion") {
typecheck(env, "~1") shouldBe SInt
an [TyperException] should be thrownBy typecheck(env, "~true")
}

property("LogicalXor") {
typecheck(env, "true ^ false") shouldBe SBoolean
}

property("BitwiseOr") {
typecheck(env, "1 | 2") shouldBe SInt
an [TyperException] should be thrownBy typecheck(env, "true | false")
}

property("BitwiseAnd") {
typecheck(env, "1 & 2") shouldBe SInt
an [TyperException] should be thrownBy typecheck(env, "true & false")
}

property("BitwiseXor") {
typecheck(env, "1 ^ 2") shouldBe SInt
}

property("BitShiftRight") {
typecheck(env, "1 >> 2") shouldBe SInt
an [TyperException] should be thrownBy typecheck(env, "true >> false")
}

property("BitShiftLeft") {
typecheck(env, "1 << 2") shouldBe SInt
an [TyperException] should be thrownBy typecheck(env, "true << false")
}

property("BitShiftRightZeroed") {
typecheck(env, "1 >>> 2") shouldBe SInt
an [TyperException] should be thrownBy typecheck(env, "true >>> false")
}

}

0 comments on commit cb2adaa

Please sign in to comment.