Skip to content

Commit

Permalink
Merge pull request #27 from tsandall/virtual-docs
Browse files Browse the repository at this point in the history
Fleshing out initial top down implementation
  • Loading branch information
tsandall committed May 2, 2016
2 parents ee681a9 + ec7efe1 commit fcc9a31
Show file tree
Hide file tree
Showing 11 changed files with 1,366 additions and 295 deletions.
10 changes: 7 additions & 3 deletions eval/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

package eval

import "sort"
import (
"fmt"
"sort"
)

// Compare returns 0 if a equals b, -1 if a is less than b, and 1 if b is than a.
//
Expand Down Expand Up @@ -117,7 +120,8 @@ func Compare(a, b interface{}) int {
return 1
}
}
panic("unreachable")

panic(fmt.Sprintf("illegal arguments of type %T and type %T", a, b))
}

const (
Expand All @@ -144,5 +148,5 @@ func sortOrder(v interface{}) int {
case map[string]interface{}:
return objectSort
}
panic("unreachable")
panic(fmt.Sprintf("illegal argument of type %T", v))
}
70 changes: 70 additions & 0 deletions eval/hashmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2016 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package eval

import "fmt"
import "strings"

import "github.com/open-policy-agent/opa/opalog"

type hashEntry struct {
k opalog.Value
v opalog.Value
next *hashEntry
}

type hashMap struct {
table map[int]*hashEntry
}

func newHashMap() *hashMap {
return &hashMap{make(map[int]*hashEntry)}
}

func (hm *hashMap) Get(k opalog.Value) opalog.Value {
hash := k.Hash()
for entry := hm.table[hash]; entry != nil; entry = entry.next {
if entry.k.Equal(k) {
return entry.v
}
}
return nil
}

// Iter invokes the iter function for each element in the hashMap.
// If the iter function returns true, iteration stops and the return value is true.
// If the iter function never returns true, iteration proceeds through all elements
// and the return value is false.
func (hm *hashMap) Iter(iter func(opalog.Value, opalog.Value) bool) bool {
for _, entry := range hm.table {
for ; entry != nil; entry = entry.next {
if iter(entry.k, entry.v) {
return true
}
}
}
return false
}

func (hm *hashMap) Put(k opalog.Value, v opalog.Value) {
hash := k.Hash()
head := hm.table[hash]
for entry := head; entry != nil; entry = entry.next {
if entry.k.Equal(k) {
entry.v = v
return
}
}
hm.table[hash] = &hashEntry{k: k, v: v, next: head}
}

func (hm *hashMap) String() string {
var buf []string
hm.Iter(func(k opalog.Value, v opalog.Value) bool {
buf = append(buf, fmt.Sprintf("%v: %v", k, v))
return false
})
return "{" + strings.Join(buf, ", ") + "}"
}
51 changes: 51 additions & 0 deletions eval/hashmap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2016 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package eval

import (
"fmt"
"reflect"
"testing"

"github.com/open-policy-agent/opa/opalog"
)

func TestHashmapOverwrite(t *testing.T) {
m := newHashMap()
key := opalog.String("hello")
expected := opalog.String("goodbye")
m.Put(key, opalog.String("world"))
m.Put(key, expected)
result := m.Get(key)
if result != expected {
t.Errorf("Expected existing value to be overwritten but got %v for key %v", result, key)
}
}

func TestIter(t *testing.T) {
m := newHashMap()
keys := []opalog.Number{opalog.Number(1), opalog.Number(2), opalog.Number(1.4)}
value := opalog.Null{}
for _, k := range keys {
m.Put(k, value)
}
// 1 and 1.4 should both hash to 1.
if len(m.table) != 2 {
panic(fmt.Sprintf("Expected collision: %v", m))
}
results := map[opalog.Value]opalog.Value{}
m.Iter(func(k opalog.Value, v opalog.Value) bool {
results[k] = v
return false
})
expected := map[opalog.Value]opalog.Value{
opalog.Number(1): value,
opalog.Number(2): value,
opalog.Number(1.4): value,
}
if !reflect.DeepEqual(results, expected) {
t.Errorf("Expected %v but got %v", expected, results)
}
}
6 changes: 4 additions & 2 deletions eval/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (store Storage) Lookup(path opalog.Ref) (interface{}, error) {

var node interface{} = store

for _, v := range path {
for i, v := range path {
switch n := node.(type) {
case Storage:
// The first element in a reference is always a Var so we have
Expand Down Expand Up @@ -106,8 +106,10 @@ func (store Storage) Lookup(path opalog.Ref) (interface{}, error) {
return nil, notFoundError("cannot find path %v in storage, path references array using negative index: %v", path, idx)
}
node = n[idx]
case *opalog.Rule:
return nil, notFoundError("cannot find path %v in storage, path references rule at index: %v", path, i-1)
default:
return nil, &StorageError{Code: StorageInternalErr, Message: fmt.Sprintf("cannot lookup object with non-string key: %v", path)}
return nil, notFoundError("cannot find path %v in storage, path references non-collection type at index: %v", path, i)
}
}

Expand Down
Loading

0 comments on commit fcc9a31

Please sign in to comment.