An alternative solution using regex.
The idea is to recursively parse the input string matching leaf nodes and expressions in paranthesis. Each invokation of the parse
method will match it's entire input and either return a node with the input value (if it is a leaf) or a node with two sub-nodes that are evaluated recursively (if it matches a pattern of "expression operator expression").
In its current form it requires highly regular input:
- a leaf must be on the form digit:upperCaseCharacter with no parenthesis
- one space between operator and expressions
- an expression is either a leaf or something contained in parenthesis
Whenever the regex matches, all matching groups are collected and null values are removed. There will be 8 non-null groups (since the number of grouping parenthesis are always the same) and the inner groups will always be number 2, 3 and 5, where 3 is the operator and 2 and 5 are the input for the left and right nodes respectively.
The toString
method simply writes out the expression in polish notation (op ex1 ex2).
class Node {
String value
Node left, right
static leaf = /(\d:[A-Z])/
static op = /([&|])/
static par = /\((.+)\)/
static parse(String text) {
if (text ==~ "^$leaf\$") {
return new Node(value: text)
}
def matcher = text =~ "^($leaf|$par) $op ($leaf|$par)\$"
if (matcher.matches()) {
def (l,o,r) = (matcher[0] - null)[2,3,5]
return new Node(left: parse(l), value: o, right: parse(r))
}
throw new IllegalArgumentException("Irregular input: $text")
}
String toString() {
left && right ? "$value $left $right" : value
}
}
Example:
println Node.parse('1:A')
println Node.parse('1:A & 2:B')
println Node.parse('(1:A & 2:B) | 3:C')
println Node.parse('(1:A & ((2:B | 3:C) & (4:D | 5:E))) | (6:F & 7:G)')
Produces the following polish notation output:
1:A
& 1:A 2:B
| & 1:A 2:B 3:C
| & 1:A & | 2:B 3:C | 4:D 5:E & 6:F 7:G