Some util functions to support bpfix

Change-Id: If53f696fbe937e007b902434f62d0d92435846dd
diff --git a/parser/ast.go b/parser/ast.go
index 19a79d7..52462f7 100644
--- a/parser/ast.go
+++ b/parser/ast.go
@@ -122,6 +122,40 @@
 	Eval() Expression
 }
 
+// ExpressionsAreSame tells whether the two values are the same Expression.
+// This includes the symbolic representation of each Expression but not their positions in the original source tree.
+// This does not apply any simplification to the expressions before comparing them
+// (for example, "!!a" wouldn't be deemed equal to "a")
+func ExpressionsAreSame(a Expression, b Expression) (equal bool, err error) {
+	return hackyExpressionsAreSame(a, b)
+}
+
+// TODO(jeffrygaston) once positions are removed from Expression stucts,
+// remove this function and have callers use reflect.DeepEqual(a, b)
+func hackyExpressionsAreSame(a Expression, b Expression) (equal bool, err error) {
+	if a.Type() != b.Type() {
+		return false, nil
+	}
+	left, err := hackyFingerprint(a)
+	if err != nil {
+		return false, nil
+	}
+	right, err := hackyFingerprint(b)
+	if err != nil {
+		return false, nil
+	}
+	areEqual := string(left) == string(right)
+	return areEqual, nil
+}
+
+func hackyFingerprint(expression Expression) (fingerprint []byte, err error) {
+	assignment := &Assignment{"a", noPos, expression, expression, noPos, "=", false}
+	module := &File{}
+	module.Defs = append(module.Defs, assignment)
+	p := newPrinter(module)
+	return p.Print()
+}
+
 type Type int
 
 const (
@@ -233,6 +267,31 @@
 
 func (x *Map) Type() Type { return MapType }
 
+// GetProperty looks for a property with the given name.
+// It resembles the bracket operator of a built-in Golang map.
+func (x *Map) GetProperty(name string) (Property *Property, found bool) {
+	prop, found, _ := x.getPropertyImpl(name)
+	return prop, found // we don't currently expose the index to callers
+}
+
+func (x *Map) getPropertyImpl(name string) (Property *Property, found bool, index int) {
+	for i, prop := range x.Properties {
+		if prop.Name == name {
+			return prop, true, i
+		}
+	}
+	return nil, false, -1
+}
+
+// GetProperty removes the property with the given name, if it exists.
+func (x *Map) RemoveProperty(propertyName string) (removed bool) {
+	_, found, index := x.getPropertyImpl(propertyName)
+	if found {
+		x.Properties = append(x.Properties[:index], x.Properties[index+1:]...)
+	}
+	return found
+}
+
 type List struct {
 	LBracePos scanner.Position
 	RBracePos scanner.Position