Rewrite MSP parsers.

- Stop using container/list.  It requires casting which keeps causing panics.
- Justify all slice accesses.  Nil pointers also keep causing panics.
This commit is contained in:
Brendan McMillion
2015-12-04 11:29:12 -08:00
parent d89bb05295
commit 5e1cff9d9d
3 changed files with 79 additions and 49 deletions

View File

@@ -1,7 +1,6 @@
package msp
import (
"container/list"
"errors"
"fmt"
"strconv"
@@ -25,7 +24,7 @@ func StringToFormatted(f string) (out Formatted, err error) {
//
// Staging stack is empty on initialization and should have exactly 1 built
// threshold gate at the end of the string.
if f[0] != '(' || f[len(f)-1] != ')' {
if len(f) == 0 || f[0] != '(' || f[len(f)-1] != ')' {
return out, errors.New("Invalid string: Needs to begin and end with parentheses.")
}
@@ -54,7 +53,7 @@ func StringToFormatted(f string) (out Formatted, err error) {
return strings.TrimSpace(f[0:nextUnParen]), f[nextUnParen:]
}
staging := list.New()
staging := [][]Condition{}
indices := make(map[string]int, 0)
var nxt string
@@ -63,13 +62,22 @@ func StringToFormatted(f string) (out Formatted, err error) {
switch nxt {
case "(":
staging.PushFront(list.New())
staging = append([][]Condition{[]Condition{}}, staging...)
case ")":
top := staging.Remove(staging.Front()).(*list.List)
if len(staging) < 1 || len(staging[0]) < 1 { // Check 1
return out, errors.New("Invalid string: Illegal close parenthesis.")
}
top := staging[0] // Legal because of check 1.
staging = staging[1:]
var min int
minStr := top.Front()
min, err = strconv.Atoi(minStr.Value.(Name).string)
minStr, ok := top[0].(Name) // Legal because of check 1.
if !ok {
return out, errors.New("Invalid string: First argument wasn't a threshold!")
}
min, err = strconv.Atoi(minStr.string)
if err != nil {
return
}
@@ -79,25 +87,29 @@ func StringToFormatted(f string) (out Formatted, err error) {
Conds: []Condition{},
}
for cond := minStr.Next(); cond != nil; cond = cond.Next() {
built.Conds = append(built.Conds, cond.Value.(Condition))
for _, cond := range top[1:] {
built.Conds = append(built.Conds, cond)
}
if staging.Len() == 0 {
if len(staging) == 0 { // Check 2
if len(f) == 0 {
return built, nil
}
return built, errors.New("Invalid string: Can't parse anymore, but there's still data. Too many closing parentheses or too few opening parentheses?")
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.(*list.List).PushBack(built)
staging[0] = append(staging[0], built) // Legal because of check 2.
default:
if len(staging) < 1 {
return out, errors.New("Invalid string: Name is not encapsulated!")
}
if _, there := indices[nxt]; !there {
indices[nxt] = 0
}
staging.Front().Value.(*list.List).PushBack(Name{nxt, indices[nxt]})
staging[0] = append(staging[0], Name{nxt, indices[nxt]}) // Legal because of check above.
indices[nxt]++
}
}

View File

@@ -87,3 +87,17 @@ func TestFormatted(t *testing.T) {
t.Fatalf("Query #3 decoded wrong: %v %v", decQuery3.String(), err)
}
}
func TestBugs(t *testing.T) {
bugs := []string{
"(),)",
"((2, Alice, Bob), Bob, Carl)",
}
for _, bug := range bugs {
_, err := StringToFormatted(bug)
if err == nil {
t.Fatalf("Didn't panic or error on a malformed string: %v", bug)
}
}
}

View File

@@ -1,7 +1,6 @@
package msp
import (
"container/list"
"errors"
"strings"
)
@@ -17,6 +16,11 @@ func (t NodeType) Type() NodeType {
return t
}
type Layer struct {
Conditions []Condition
Operators []NodeType
}
type Raw struct { // Represents one node in the tree.
NodeType
@@ -84,7 +88,7 @@ func StringToRaw(r string) (out Raw, err error) {
return strings.TrimSpace(r[0:nextOper]), r[nextOper:]
}
staging := list.New() // Stack of (Condition list, operator list)
staging := []Layer{} // Stack of (Condition list, operator list)
indices := make(map[string]int, 0)
var nxt string
@@ -93,69 +97,69 @@ func StringToRaw(r string) (out Raw, err error) {
switch nxt {
case "(":
staging.PushFront([2]*list.List{list.New(), list.New()})
staging = append([]Layer{Layer{}}, staging...)
case ")":
top := staging.Remove(staging.Front()).([2]*list.List)
if top[0].Len() != (top[1].Len() + 1) {
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++ {
var step *list.Element
leftOperand := top[0].Front()
i := 0
for i < len(top.Operators) {
oper := top.Operators[i] // Legal because for loop condition.
for oper := top[1].Front(); oper != nil; oper = step {
step = oper.Next()
// 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}
if oper.Value.(NodeType) == typ {
left := leftOperand.Value.(Condition)
right := leftOperand.Next().Value.(Condition)
top.Conditions = append(
top.Conditions[:i],
append([]Condition{built}, top.Conditions[i+2:]...)...,
)
leftOperand.Next().Value = Raw{
NodeType: typ,
Left: &left,
Right: &right,
}
leftOperand = leftOperand.Next()
top[0].Remove(leftOperand.Prev())
top[1].Remove(oper)
top.Operators = append(top.Operators[:i], top.Operators[i+1:]...) // Legal because for loop condition.
} else {
leftOperand = leftOperand.Next()
i++
}
}
}
if top[0].Len() != 1 || top[1].Len() != 0 {
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 staging.Len() == 0 {
if len(staging) == 0 { // Check 4
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.")
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.Front().Value.([2]*list.List)[0].PushBack(top[0].Front().Value)
staging[0].Conditions = append(staging[0].Conditions, top.Conditions[0]) // Legal because of checks 3 and 4.
case "&":
staging.Front().Value.([2]*list.List)[1].PushBack(NodeAnd)
// 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.Front().Value.([2]*list.List)[1].PushBack(NodeOr)
staging[0].Operators = append(staging[0].Operators, NodeOr) // Legal for same reason as case &.
default:
if _, there := indices[nxt]; !there {
indices[nxt] = 0
}
staging.Front().Value.([2]*list.List)[0].PushBack(Name{nxt, indices[nxt]})
staging[0].Conditions = append(staging[0].Conditions, Name{nxt, indices[nxt]}) // Legal for same reason as case &.
indices[nxt]++
}
}