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]] */ /** Parses a list of [[ScriptToken]]s into its corresponding [[BinaryTree]] */
def parseBinaryTree(script : List[ScriptToken]) : BinaryTree[ScriptToken] = { def parseBinaryTree(script : List[ScriptToken]) : BinaryTree[ScriptToken] = {
val bTree = loop(script,Empty) @tailrec
logger.debug("parsed btree: " + bTree) def l(remaining: List[ScriptToken], parentTree: BinaryTree[ScriptToken]): (BinaryTree[ScriptToken], List[ScriptToken]) = {
bTree 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]]. */ /** 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("Script : " + script) */
logger.debug("Tree: " + tree) logger.debug("Tree: " + tree)
logger.debug("script: " + (if (script.nonEmpty) script else Nil)) logger.debug("script: " + (if (script.nonEmpty) script else Nil))
script match { script match {
case OP_ENDIF :: t => 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), 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) "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) //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)) //base case, doesn't matter what we return since call insertSubTree(tree,Leaf(OP_ENDIF))
val ifTree = insertSubTree(tree,Leaf(OP_ENDIF)) val ifTree = insertSubTree(tree,Leaf(OP_ENDIF))
logger.debug("ifTree: " + ifTree + " t: " + t) logger.debug("ifTree: " + ifTree + " t: " + t)
ifTree (ifTree,t)
case h :: t if (h == OP_IF || h == OP_NOTIF) => case h :: t if (h == OP_IF || h == OP_NOTIF) =>
//find last OP_ENDIF in t //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) logger.debug("endifs: " + endifs)
val endif = if (endifs.size == 0) { val endif = if (endifs.size == 0) {
(OP_ENDIF,t.size) (OP_ENDIF,t.size)
@ -227,22 +246,27 @@ trait ControlOperationsInterpreter {
logger.debug("Done with odd amounts of OP_ENDIFS") logger.debug("Done with odd amounts of OP_ENDIFS")
logger.debug("fullTree: " + fullTree) logger.debug("fullTree: " + 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 => case h :: t if h == OP_ELSE =>
require(tree.value.isDefined && Seq(OP_IF, OP_NOTIF, OP_ELSE).contains(tree.value.get), 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) "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) 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) logger.debug("subTree else: " + subTree)
val opElseTree = Node(tree.value.get, tree.left.getOrElse(Empty),subTree) val opElseTree = Node(tree.value.get, tree.left.getOrElse(Empty),subTree)
logger.debug("opElseTree: " + opElseTree) logger.debug("opElseTree: " + opElseTree)
opElseTree (opElseTree,remaining)
case (x: ScriptConstant) :: t => loop(t, insertSubTree(tree, Leaf(x))) case (x: ScriptConstant) :: t => loop(t, insertSubTree(tree, Leaf(x)))
case (x: BytesToPushOntoStack) :: 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 h :: t => loop(t,insertSubTree(tree,Leaf(h)))
case Nil => case Nil =>
logger.debug("Done parsing tree, got: " + tree) 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 { class ControlOperationsInterpreterTest extends FlatSpec with MustMatchers with ControlOperationsInterpreter {
private def logger = BitcoinSLogger.logger 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 stack = List(OP_TRUE)
val script = List(OP_VERIFY) val script = List(OP_VERIFY)
val program = ScriptProgram(TestUtil.testProgram, stack,script) 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, 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) 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) findMatchingOpEndIf(script) must be (20)
} } */
it must "parse a script as a binary tree then convert it back to the original list" in { 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 subTree44 = Node(OP_ELSE, Leaf(OP_8), Node(OP_ENDIF, subTree43, Empty))
val subTree45 = Node(OP_ELSE, Node(OP_4, subTree42, Empty), subTree44) val subTree45 = Node(OP_ELSE, Node(OP_4, subTree42, Empty), subTree44)
val expectedBTree3 = Node(OP_IF,subTree41, subTree45) val expectedBTree4 = Node(OP_IF,subTree41, subTree45)
val bTree3 = parseBinaryTree(script4) val bTree4 = parseBinaryTree(script4)
bTree3.left.get must be (subTree41) bTree4.left.get must be (subTree41)
bTree3.right.get.left.get must be (subTree45.l) bTree4.right.get.left.get must be (subTree45.l)
bTree3.right.get.right.get must be (subTree45.r) bTree4.right.get.right.get must be (subTree45.r)
bTree3 must be (expectedBTree3) bTree4 must be (expectedBTree4)
bTree3.toSeq must be (script4) bTree4.toSeq must be (script4)
bTree3.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.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)) bTree4.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)) */ 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 { it must "parse a binary tree where there are nested OP_ELSES in the outer most OP_ELSE" in {