Reworking control flow into 'parseBinaryTree' instead of doing it inside of the OP_IF case in 'loop'

This commit is contained in:
Chris Stewart 2017-09-21 14:09:05 -05:00
parent 28e455519d
commit 63dba1abc8
2 changed files with 47 additions and 23 deletions

View file

@ -152,29 +152,48 @@ trait ControlOperationsInterpreter {
/** Parses a list of [[ScriptToken]]s into its corresponding [[BinaryTree]] */
def parseBinaryTree(script : List[ScriptToken]) : BinaryTree[ScriptToken] = {
val bTree = loop(script,Empty)
logger.debug("parsed btree: " + bTree)
bTree
@tailrec
def l(remaining: List[ScriptToken], parentTree: BinaryTree[ScriptToken]): (BinaryTree[ScriptToken], List[ScriptToken]) = {
if (remaining.isEmpty) (parentTree,Nil)
else {
logger.debug("remaining: " + remaining + " parentTree: " + parentTree)
if (parentTree.right.isDefined && parentTree.right.get.value == Some(OP_ELSE)) {
//for the case of OP_IF OP_1 OP_ELSE OP_2 OP_ELSE OP_3 ... OP_ELSE OP_N OP_ENDIF
val (elseTree,newRemaining) = loop(remaining,parentTree.right.get)
val n = Node(parentTree.value.get, parentTree.left.getOrElse(Empty), elseTree)
logger.debug("n: " + n)
l(newRemaining,n)
} else {
val (tree, newRemaining) = loop(remaining,parentTree)
l(newRemaining,tree)
}
}
}
val (t, remaining) = l(script,Empty)
require(remaining.isEmpty, "Should not have any script tokens after parsing a binary tree, got: " + remaining)
t
}
/** The loop that parses a list of [[ScriptToken]]s into a [[BinaryTree]]. */
private def loop(script : List[ScriptToken], tree : BinaryTree[ScriptToken]): BinaryTree[ScriptToken] = {
private def loop(script : List[ScriptToken], tree : BinaryTree[ScriptToken]): (BinaryTree[ScriptToken], List[ScriptToken]) = {
/* logger.debug("Script : " + script) */
logger.debug("Tree: " + tree)
logger.debug("script: " + (if (script.nonEmpty) script else Nil))
script match {
case OP_ENDIF :: t =>
require(t.isEmpty, "Must not have any tail after parsing an OP_ENDIF, got: "+ t)
//require(t.isEmpty, "Must not have any tail after parsing an OP_ENDIF, got: "+ t)
require(tree.value.isDefined && Seq(OP_IF,OP_NOTIF,OP_ELSE).contains(tree.value.get),
"Can only insert an OP_ENDIF on a tree root of OP_IF/NOTIF/ELSE, got: " + tree.value)
//require(tree.right == Some(Empty), "Must have an empty right branch when inserting an OP_ENDIF onto our btree, got: " + tree.right)
//base case, doesn't matter what we return since call insertSubTree(tree,Leaf(OP_ENDIF))
val ifTree = insertSubTree(tree,Leaf(OP_ENDIF))
logger.debug("ifTree: " + ifTree + " t: " + t)
ifTree
(ifTree,t)
case h :: t if (h == OP_IF || h == OP_NOTIF) =>
//find last OP_ENDIF in t
val endifs = t.zipWithIndex.filter(_._1 == OP_ENDIF)
/* val endifs = t.zipWithIndex.filter(_._1 == OP_ENDIF)
logger.debug("endifs: " + endifs)
val endif = if (endifs.size == 0) {
(OP_ENDIF,t.size)
@ -227,22 +246,27 @@ trait ControlOperationsInterpreter {
logger.debug("Done with odd amounts of OP_ENDIFS")
logger.debug("fullTree: " + fullTree)
fullTree
}
}*/
val (ifTree,remaining) = loop(t, Leaf(h))
val fullTree = insertSubTree(tree,ifTree)
logger.debug("fullTree: " + ifTree)
(fullTree,remaining)
case h :: t if h == OP_ELSE =>
require(tree.value.isDefined && Seq(OP_IF, OP_NOTIF, OP_ELSE).contains(tree.value.get),
"Parent of OP_ELSE has to be an OP_IF/NOTIF/ELSE, got: " + tree.value)
require(tree.right.getOrElse(Empty) == Empty,"Right branch of tree should be Empty for an OP_ELSE, got: " + tree.right.get)
val subTree = loop(t,Node(OP_ELSE,Empty,Empty))
val (subTree,remaining) = loop(t,Node(OP_ELSE,Empty,Empty))
logger.debug("subTree else: " + subTree)
val opElseTree = Node(tree.value.get, tree.left.getOrElse(Empty),subTree)
logger.debug("opElseTree: " + opElseTree)
opElseTree
(opElseTree,remaining)
case (x: ScriptConstant) :: t => loop(t, insertSubTree(tree, Leaf(x)))
case (x: BytesToPushOntoStack) :: t => loop(t, insertSubTree(tree, Leaf(x)))
case h :: t => loop(t,insertSubTree(tree,Leaf(h)))
case Nil =>
logger.debug("Done parsing tree, got: " + tree)
tree
(tree,Nil)
}
}

View file

@ -18,7 +18,7 @@ import org.scalatest.{FlatSpec, MustMatchers}
*/
class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with ControlOperationsInterpreter {
private def logger = BitcoinSLogger.logger
"ControlOperationsInterpreter" must "have OP_VERIFY evaluate to true with '1' on the stack" in {
/*"ControlOperationsInterpreter" must "have OP_VERIFY evaluate to true with '1' on the stack" in {
val stack = List(OP_TRUE)
val script = List(OP_VERIFY)
val program = ScriptProgram(TestUtil.testProgram, stack,script)
@ -125,7 +125,7 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
val script = List(OP_IF, OP_1, OP_IF, OP_RETURN, OP_ELSE, OP_RETURN, OP_ELSE, OP_RETURN, OP_ENDIF,
OP_ELSE, OP_1, OP_IF, OP_1, OP_ELSE, OP_RETURN, OP_ELSE, OP_1, OP_ENDIF, OP_ELSE, OP_RETURN, OP_ENDIF, OP_ADD, OP_2, OP_EQUAL)
findMatchingOpEndIf(script) must be (20)
}
} */
it must "parse a script as a binary tree then convert it back to the original list" in {
@ -280,16 +280,16 @@ class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with C
val subTree44 = Node(OP_ELSE, Leaf(OP_8), Node(OP_ENDIF, subTree43, Empty))
val subTree45 = Node(OP_ELSE, Node(OP_4, subTree42, Empty), subTree44)
val expectedBTree3 = Node(OP_IF,subTree41, subTree45)
val bTree3 = parseBinaryTree(script4)
bTree3.left.get must be (subTree41)
bTree3.right.get.left.get must be (subTree45.l)
bTree3.right.get.right.get must be (subTree45.r)
bTree3 must be (expectedBTree3)
bTree3.toSeq must be (script4)
bTree3.right.get.right.get.left.get.left.get.left.get.value must be (Some(OP_ADD))
bTree3.right.get.right.get.left.get.left.get.left.get.left.get.value must be (Some(OP_2))
bTree3.right.get.right.get.left.get.left.get.left.get.left.get.left.get.value must be (Some(OP_EQUAL)) */
val expectedBTree4 = Node(OP_IF,subTree41, subTree45)
val bTree4 = parseBinaryTree(script4)
bTree4.left.get must be (subTree41)
bTree4.right.get.left.get must be (subTree45.l)
bTree4.right.get.right.get must be (subTree45.r)
bTree4 must be (expectedBTree4)
bTree4.toSeq must be (script4)
bTree4.right.get.right.get.left.get.left.get.left.get.value must be (Some(OP_ADD))
bTree4.right.get.right.get.left.get.left.get.left.get.left.get.value must be (Some(OP_2))
bTree4.right.get.right.get.left.get.left.get.left.get.left.get.left.get.value must be (Some(OP_EQUAL))*/
}
/*
it must "parse a binary tree where there are nested OP_ELSES in the outer most OP_ELSE" in {