1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
|
package msp
import (
"errors"
"strings"
)
type NodeType int // Types of node in the binary expression tree.
const (
NodeAnd NodeType = iota
NodeOr
)
func (t NodeType) Type() NodeType {
return t
}
type Layer struct {
Conditions []Condition
Operators []NodeType
}
type Raw struct { // Represents one node in the tree.
NodeType
Left Condition
Right Condition
}
func StringToRaw(r string) (out Raw, err error) {
// Automaton. Modification of Dijkstra's Two-Stack Algorithm for parsing
// infix notation. Reads one long unbroken expression (several operators and
// operands with no parentheses) at a time and parses it into a binary
// expression tree (giving AND operators precedence). Running time linear in
// the size of the predicate?
//
// Steps to the next (un)parenthesis.
// ( -> Push new queue onto staging stack
// value -> Push onto back of queue at top of staging stack.
// ) -> Pop queue off top of staging stack, build BET, and push tree
// onto the back of the top queue.
//
// To build the binary expression tree, for each type of operation we iterate
// through the (Condition, operator) lists compacting where that operation
// occurs into tree nodes.
//
// Staging stack is empty on initialization and should have exactly 1 node
// (the root node) at the end of the string.
r = "(" + r + ")"
min := func(a, b, c int) int { // Return smallest non-negative argument.
if a > b { // Sort {a, b, c}
a, b = b, a
}
if b > c {
b, c = c, b
}
if a > b {
a, b = b, a
}
if a != -1 {
return a
} else if b != -1 {
return b
} else {
return c
}
}
getNext := func(r string) (string, string) { // r -> (next, rest)
r = strings.TrimSpace(r)
if r[0] == '(' || r[0] == ')' || r[0] == '&' || r[0] == '|' {
return r[0:1], r[1:]
}
nextOper := min(
strings.Index(r, "&"),
strings.Index(r, "|"),
strings.Index(r, ")"),
)
if nextOper == -1 {
return r, ""
}
return strings.TrimSpace(r[0:nextOper]), r[nextOper:]
}
staging := []Layer{} // Stack of (Condition list, operator list)
indices := make(map[string]int, 0)
var nxt string
for len(r) > 0 {
nxt, r = getNext(r)
switch nxt {
case "(":
staging = append([]Layer{Layer{}}, staging...)
case ")":
if len(staging) < 1 { // Check 1
return out, errors.New("Invalid string: Illegal close parenthesis.")
}
top := staging[0] // Legal because of check 1.
staging = staging[1:]
if len(top.Conditions) != (len(top.Operators) + 1) { // Check 2
return out, errors.New("Invalid string: There needs to be an operator (& or |) for every pair of operands.")
}
for typ := NodeAnd; typ <= NodeOr; typ++ {
i := 0
for i < len(top.Operators) {
oper := top.Operators[i] // Legal because for loop condition.
// Copy left and right out of slice and THEN give a pointer for them!
left, right := top.Conditions[i], top.Conditions[i+1] // Legal because of check 2.
if oper == typ {
built := Raw{typ, left, right}
top.Conditions = append(
top.Conditions[:i],
append([]Condition{built}, top.Conditions[i+2:]...)...,
)
top.Operators = append(top.Operators[:i], top.Operators[i+1:]...) // Legal because for loop condition.
} else {
i++
}
}
}
if len(top.Conditions) != 1 || len(top.Operators) != 0 { // Check 3
return out, errors.New("Invalid string: Couldn't evaluate all of the operators.")
}
if len(staging) == 0 { // Check 4
if len(r) == 0 {
res, ok := top.Conditions[0].(Raw) // Legal because of check 3.
if !ok {
return out, errors.New("Invalid string: Only one condition was found?")
}
return res, nil
}
return out, errors.New("Invalid string: Can't parse anymore, but there's still data. Too many closing parentheses or too few opening parentheses?")
}
staging[0].Conditions = append(staging[0].Conditions, top.Conditions[0]) // Legal because of checks 3 and 4.
case "&":
// Legal because first operation is to add an empty layer to the stack.
// If the stack is ever empty again, the function tries to return or error.
staging[0].Operators = append(staging[0].Operators, NodeAnd)
case "|":
staging[0].Operators = append(staging[0].Operators, NodeOr) // Legal for same reason as case &.
default:
if _, there := indices[nxt]; !there {
indices[nxt] = 0
}
staging[0].Conditions = append(staging[0].Conditions, Name{nxt, indices[nxt]}) // Legal for same reason as case &.
indices[nxt]++
}
}
return out, errors.New("Invalid string: Not finished parsing, but out of data. Too many opening parentheses or too few closing parentheses?")
}
func (r Raw) String() string {
out := ""
switch left := r.Left.(type) {
case Name:
out += left.string
case Raw:
out += "(" + left.String() + ")"
}
if r.Type() == NodeAnd {
out += " & "
} else {
out += " | "
}
switch right := r.Right.(type) {
case Name:
out += right.string
case Raw:
out += "(" + right.String() + ")"
}
return out
}
func (r Raw) Formatted() (out Formatted) {
// Recursively maps a raw predicate to a formatted predicate by mapping AND
// gates to (2, A, B) treshold gates and OR gates to (1, A, B) gates.
if r.Type() == NodeAnd {
out.Min = 2
} else {
out.Min = 1
}
switch left := r.Left.(type) {
case Name:
out.Conds = []Condition{left}
case Raw:
out.Conds = []Condition{left.Formatted()}
}
switch right := r.Right.(type) {
case Name:
out.Conds = append(out.Conds, right)
case Raw:
out.Conds = append(out.Conds, right.Formatted())
}
out.Compress() // Small amount of predicate compression.
return
}
func (r Raw) Ok(db UserDatabase) bool {
if r.Type() == NodeAnd {
return r.Left.Ok(db) && r.Right.Ok(db)
} else {
return r.Left.Ok(db) || r.Right.Ok(db)
}
}
|