Last active
July 26, 2018 03:40
-
-
Save ppazos/5aaa4bc61dfaa176e4985ef0a7817364 to your computer and use it in GitHub Desktop.
Generates a logical expression from a plain structure, generating an intermediate AST binary tree
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class BinaryTree { | |
def tmp // temporal expression | |
def value | |
def left | |
def right | |
def parent // null for root | |
String toString() | |
{ | |
toStringIndent() | |
} | |
String toStringIndent(idnt = 0) | |
{ | |
if (left) | |
value +"\n"+ " ".multiply(idnt) +" |_ "+ left.toStringIndent(idnt+1) +"\n"+ " ".multiply(idnt) +" |_ "+ right.toStringIndent(idnt+1) | |
else value | |
} | |
// Know if tree is left or right child under it's parent | |
boolean isLeft() | |
{ | |
return this == this.parent.left | |
} | |
boolean isRight() | |
{ | |
return this == this.parent.right | |
} | |
BinaryTree sibling() | |
{ | |
if (this.isLeft()) return this.parent.right | |
return this.parent.left | |
} | |
boolean isComplex() | |
{ | |
return this.value == 'AND' || this.value == 'OR' | |
} | |
} | |
def process_tree(btree) | |
{ | |
try_divide(btree) | |
if (btree.left) process_tree(btree.left) | |
if (btree.right) process_tree(btree.right) | |
} | |
def try_divide(btree) | |
{ | |
def i, left_expr = [], right_expr = [] | |
i = btree.tmp.findIndexOf { it[2] } // first with post_group_op | |
if (i >= 0) // continue dividing tree, right grouping | |
{ | |
// the current root value should be the current post_group_op | |
// and the current post_group_op should be deleted to avoid reprocessing | |
btree.value = btree.tmp[i][2] | |
btree.tmp[i][2] = '' | |
btree.tmp.eachWithIndex { item, idx -> | |
if (idx <= i) left_expr << item | |
else right_expr << item | |
} | |
//println 'post left_expr '+ left_expr | |
//println 'post right_expr '+ right_expr | |
btree.left = new BinaryTree(tmp: left_expr, parent: btree) | |
btree.right = new BinaryTree(tmp: right_expr, parent: btree) | |
} | |
else // continue dividing tree, left grouping | |
{ | |
// last with prev_group_op (is last to group to the left, so a AND b AND c will be (a AND b) AND c) | |
// if findIndexOf is used, a AND b AND c will be a AND (b AND c), that is not the desired grouping | |
i = btree.tmp.findLastIndexOf { it[0] } | |
if (i < 0) // ends recursion, leaf node | |
{ | |
assert btree.tmp.size() == 1 | |
btree.value = btree.tmp[0][1] | |
return // no prev group_op found | |
} | |
// the current root value should be the current post_group_op | |
// and the current post_group_op should be deleted to avoid reprocessing | |
btree.value = btree.tmp[i][0] | |
btree.tmp[i][0] = '' | |
btree.tmp.eachWithIndex { item, idx -> | |
if (idx < i) left_expr << item | |
else right_expr << item | |
} | |
//println 'prev left_expr '+ left_expr | |
//println 'prev right_expr '+ right_expr | |
btree.left = new BinaryTree(tmp: left_expr, parent: btree) | |
btree.right = new BinaryTree(tmp: right_expr, parent: btree) | |
} | |
} | |
String toExpressionString(btree) | |
{ | |
def expr = '' | |
if (btree.left) | |
{ | |
def lexpr = toExpressionString(btree.left) | |
def rexpr = toExpressionString(btree.right) | |
expr = '('+ lexpr +' '+ btree.value +' '+ rexpr +')' | |
} | |
else | |
{ | |
expr = btree.value | |
} | |
return expr | |
} | |
// returns the leaves in the same order as they appear in the plain expression (left to right) | |
List getLeaves(btree, returnNode = false) | |
{ | |
List leaves = [] | |
if (btree.isComplex()) | |
{ | |
leaves += getLeaves(btree.left, returnNode) | |
leaves += getLeaves(btree.right, returnNode) | |
} | |
else | |
{ | |
if (returnNode) | |
leaves << btree | |
else | |
leaves << btree.value | |
} | |
return leaves | |
} | |
// same as getLeaves but returns the BinaryTree nodes, not their value. | |
List getLeavesBTree(btree) | |
{ | |
return getLeaves(btree, true) | |
} | |
// from a processed tree, get the expression back | |
def getExpression(btree) | |
{ | |
// creates empty expression structure with leaves on their rows, in row[1] | |
def leaves = getLeavesBTree(btree) | |
def expression = [] | |
leaves.each { leave -> | |
expression << ['', leave, ''] // leave is a BinaryTree node, not it's value, we need to put the value after finishing the process | |
} | |
def queue = [] as Queue // .offer() adds, .poll() removes | |
//getExpressionRecursive(btree, expression, queue, 0) | |
getExpressionIterative(expression, queue) | |
// transform the BinaryTrees in the current expression, back to their values | |
def simpleExpression = [] | |
expression.each { row -> | |
simpleExpression << [ (row[0]?row[0].value:''), (row[1]?row[1].value:''), (row[2]?row[2].value:'')] | |
} | |
return simpleExpression | |
} | |
def getExpressionIterative(expression, queue) | |
{ | |
def row, current = 0 | |
while (current < expression.size()-1) | |
{ | |
btree = expression[current][1] | |
if (btree.isLeft()) | |
{ | |
if (!btree.sibling().isComplex()) // sibling is simple | |
{ | |
// row of the sibling of the current node | |
row = expression.find { it[1] == btree.sibling() } | |
// left assoc = parent (this is BinaryTree needs to be transformed to it's value when finished) | |
row[0] = btree.parent | |
} | |
else | |
{ | |
// row of the current node | |
row = expression[current] | |
// right assoc = parent | |
row[2] = btree.parent | |
} | |
// add parent to queue | |
queue.offer(btree.parent) | |
} | |
// current = next | |
current ++ | |
} | |
while (!queue.isEmpty()) | |
{ | |
btree = queue.poll() | |
// avoid procesing root or right siblings | |
if (!btree.parent || btree.isRight()) continue | |
if (btree.sibling().isComplex()) | |
{ | |
// if sibling is complex, the current node should be on left assoc of some row | |
row = expression.find { it[0] == btree } // left assoc | |
// right assoc = parent | |
row[2] = btree.parent | |
} | |
else | |
{ | |
// if sibling is simple, it should be on the value space in the row, row[1] | |
row = expression.find { it[1] == btree.sibling() } | |
// left asoc = parent | |
row[0] = btree.parent | |
} | |
// add parent to queue | |
queue.offer(btree.parent) | |
} | |
} | |
def printBTreeExpression(e) | |
{ | |
e.each { row -> | |
println ((row[0] ? row[0].value : '') +', '+ (row[1] ? row[1].value : '') +', '+ (row[2] ? row[2].value : '')) | |
//println "row "+ row[0] //row[0].value. +", "+ row[1].value +", "+ row[2].value | |
} | |
} | |
/* | |
def expr = [ | |
['', 'C1', ''], | |
['AND', 'C2', 'OR'], | |
['', 'C3', ''], | |
['AND', 'C4', ''], | |
['AND', 'C5', 'OR'], | |
['', 'C6', ''] | |
] | |
*/ | |
/* | |
def expr = [ | |
['', 'C1', ''], | |
['AND', 'C2', 'OR'], | |
['', 'C3', ''], | |
['AND', 'C4', ''], | |
['AND', 'C5', ''], | |
['OR', 'C6', ''] // using the OR in the left association of the last node produces the same expression, because above says "C6 is at the rith of the rest" and this says "the rest is at the left of C6" | |
] | |
*/ | |
/* | |
def expr = [ | |
['', 'C1', ''], | |
['OR', 'C2', 'AND'], | |
['', 'C3', ''] | |
] | |
*/ | |
/* | |
def expr = [ | |
['', 'C1', ''], | |
['AND', 'C2', ''] | |
] | |
*/ | |
/* | |
def expr = [ | |
['', 'C1', ''] | |
] | |
*/ | |
def expr = [ | |
['', 'C3', 'AND'], | |
['', 'C4', ''], | |
['AND', 'C5', 'OR'], | |
['', 'C6', 'AND'], | |
['', 'C7', ''], | |
['OR', 'C8', ''] | |
] | |
def tree = new BinaryTree(tmp: expr) | |
process_tree(tree) | |
println tree | |
/* | |
OR | |
|_ AND | |
|_ C1 | |
|_ C2 | |
|_ OR | |
|_ AND | |
|_ AND | |
|_ C3 | |
|_ C4 | |
|_ C5 | |
|_ C6 | |
*/ | |
println toExpressionString(tree) | |
// ((C1 AND C2) OR (((C3 AND C4) AND C5) OR C6)) | |
// println tree.tmp // only the leaves left after the processing | |
// println getLeaves(tree) | |
// Get expression back from the tree structure, might not be 100% equal because there | |
// is an equivalent with the last complex node being and the right assoc of the n-1 | |
// row or at the left assoc of the n row. | |
def e = getExpression(tree) | |
//println e | |
def tree2 = new BinaryTree(tmp: e) | |
process_tree(tree2) | |
println tree2 | |
return | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment