Files
redoctober/msp/raw.go
2015-11-11 15:22:12 -08:00

226 lines
5.3 KiB
Go

package msp
import (
"container/list"
"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 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 := list.New() // 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.PushFront([2]*list.List{list.New(), list.New()})
case ")":
top := staging.Remove(staging.Front()).([2]*list.List)
if top[0].Len() != (top[1].Len() + 1) {
return out, errors.New("Invalid string: There needs to be an operator (& or |) for every pair of operands.")
}
for typ := NodeAnd; typ <= NodeOr; typ++ {
var step *list.Element
leftOperand := top[0].Front()
for oper := top[1].Front(); oper != nil; oper = step {
step = oper.Next()
if oper.Value.(NodeType) == typ {
left := leftOperand.Value.(Condition)
right := leftOperand.Next().Value.(Condition)
leftOperand.Next().Value = Raw{
NodeType: typ,
Left: &left,
Right: &right,
}
leftOperand = leftOperand.Next()
top[0].Remove(leftOperand.Prev())
top[1].Remove(oper)
} else {
leftOperand = leftOperand.Next()
}
}
}
if top[0].Len() != 1 || top[1].Len() != 0 {
return out, errors.New("Invalid string: Couldn't evaluate all of the operators.")
}
if staging.Len() == 0 {
if len(r) == 0 {
res := top[0].Front().Value
switch res.(type) {
case Raw:
return res.(Raw), nil
default:
return out, errors.New("Invalid string: Only one condition was found.")
}
}
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.Front().Value.([2]*list.List)[0].PushBack(top[0].Front().Value)
case "&":
staging.Front().Value.([2]*list.List)[1].PushBack(NodeAnd)
case "|":
staging.Front().Value.([2]*list.List)[1].PushBack(NodeOr)
default:
if _, there := indices[nxt]; !there {
indices[nxt] = 0
}
staging.Front().Value.([2]*list.List)[0].PushBack(String{nxt, indices[nxt]})
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 (*r.Left).(type) {
case String:
out += (*r.Left).(String).string
default:
out += "(" + (*r.Left).(Raw).String() + ")"
}
if r.Type() == NodeAnd {
out += " & "
} else {
out += " | "
}
switch (*r.Right).(type) {
case String:
out += (*r.Right).(String).string
default:
out += "(" + (*r.Right).(Raw).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 (*r.Left).(type) {
case String:
out.Conds = []Condition{(*r.Left).(String)}
default:
out.Conds = []Condition{(*r.Left).(Raw).Formatted()}
}
switch (*r.Right).(type) {
case String:
out.Conds = append(out.Conds, (*r.Right).(String))
default:
out.Conds = append(out.Conds, (*r.Right).(Raw).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)
}
}