Add duplicate key check
This commit is contained in:
parent
a713d562c6
commit
6dbc004138
@ -41,13 +41,17 @@ func(st *State) Enter(input string) {
|
|||||||
st.CacheMap = make(map[string]string)
|
st.CacheMap = make(map[string]string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func(st *State) Add(k string, v string) error {
|
func(st *State) Add(key string, value string) error {
|
||||||
sz := st.checkCapacity(v)
|
checkFrame := st.frameOf(key)
|
||||||
|
if checkFrame > -1 {
|
||||||
|
return fmt.Errorf("key %v already defined in frame %v", key, checkFrame)
|
||||||
|
}
|
||||||
|
sz := st.checkCapacity(value)
|
||||||
if sz == 0 {
|
if sz == 0 {
|
||||||
return fmt.Errorf("Cache capacity exceeded %v of %v", st.CacheUseSize + sz, st.CacheSize)
|
return fmt.Errorf("Cache capacity exceeded %v of %v", st.CacheUseSize + sz, st.CacheSize)
|
||||||
}
|
}
|
||||||
log.Printf("add key %s value size %v", k, sz)
|
log.Printf("add key %s value size %v", key, sz)
|
||||||
st.Cache[len(st.Cache)-1][k] = v
|
st.Cache[len(st.Cache)-1][key] = value
|
||||||
st.CacheUseSize += sz
|
st.CacheUseSize += sz
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -61,7 +65,14 @@ func(st *State) Map(k string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func(st *State) Depth() uint8 {
|
||||||
|
return uint8(len(st.Cache))
|
||||||
|
}
|
||||||
|
|
||||||
func(st *State) Get() (map[string]string, error) {
|
func(st *State) Get() (map[string]string, error) {
|
||||||
|
if len(st.Cache) == 0 {
|
||||||
|
return nil, fmt.Errorf("get at top frame")
|
||||||
|
}
|
||||||
return st.Cache[len(st.Cache)-1], nil
|
return st.Cache[len(st.Cache)-1], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +92,28 @@ func (st *State) Exit() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func(st *State) Reset() error {
|
||||||
|
st.Cache = st.Cache[:1]
|
||||||
|
st.CacheUseSize = 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func(st *State) Check(key string) bool {
|
||||||
|
return st.frameOf(key) == -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func(st *State) frameOf(key string) int {
|
||||||
|
log.Printf("--- %s", key)
|
||||||
|
for i, m := range st.Cache {
|
||||||
|
for k, _ := range m {
|
||||||
|
if k == key {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
func(st *State) checkCapacity(v string) uint32 {
|
func(st *State) checkCapacity(v string) uint32 {
|
||||||
sz := uint32(len(v))
|
sz := uint32(len(v))
|
||||||
if st.CacheSize == 0 {
|
if st.CacheSize == 0 {
|
||||||
|
@ -73,3 +73,39 @@ func TestStateEnterExit(t *testing.T) {
|
|||||||
t.Errorf("expected out of top frame error")
|
t.Errorf("expected out of top frame error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStateReset(t *testing.T) {
|
||||||
|
st := NewState(17)
|
||||||
|
st.Enter("one")
|
||||||
|
err := st.Add("foo", "bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = st.Add("baz", "xyzzy")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
st.Enter("two")
|
||||||
|
st.Enter("three")
|
||||||
|
st.Reset()
|
||||||
|
if st.CacheUseSize != 0 {
|
||||||
|
t.Errorf("expected cache use size 0, got %v", st.CacheUseSize)
|
||||||
|
}
|
||||||
|
if st.Depth() != 1 {
|
||||||
|
t.Errorf("expected depth 1, got %v", st.Depth())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateLoadDup(t *testing.T) {
|
||||||
|
st := NewState(17)
|
||||||
|
st.Enter("one")
|
||||||
|
err := st.Add("foo", "bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
st.Enter("two")
|
||||||
|
err = st.Add("foo", "baz")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected fail on duplicate load")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
26
go/vm/vm.go
26
go/vm/vm.go
@ -37,7 +37,13 @@ func Run(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
func instructionSplit(b []byte) (string, []byte, error) {
|
func instructionSplit(b []byte) (string, []byte, error) {
|
||||||
|
if len(b) == 0 {
|
||||||
|
return "", nil, fmt.Errorf("argument is empty")
|
||||||
|
}
|
||||||
sz := uint8(b[0])
|
sz := uint8(b[0])
|
||||||
|
if sz == 0 {
|
||||||
|
return "", nil, fmt.Errorf("zero-length argument")
|
||||||
|
}
|
||||||
tailSz := uint8(len(b))
|
tailSz := uint8(len(b))
|
||||||
if tailSz - 1 < sz {
|
if tailSz - 1 < sz {
|
||||||
return "", nil, fmt.Errorf("corrupt instruction, len %v less than symbol length: %v", tailSz, sz)
|
return "", nil, fmt.Errorf("corrupt instruction, len %v less than symbol length: %v", tailSz, sz)
|
||||||
@ -61,10 +67,27 @@ func RunSink(instruction []byte, st state.State, rs resource.Fetcher, ctx contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RunCatch(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) {
|
func RunCatch(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) {
|
||||||
|
head, tail, err := instructionSplit(instruction)
|
||||||
|
if err != nil {
|
||||||
|
return st, err
|
||||||
|
}
|
||||||
|
r, err := rs.Get(head)
|
||||||
|
if err != nil {
|
||||||
|
return st, err
|
||||||
|
}
|
||||||
|
_ = tail
|
||||||
|
st.Add(head, r)
|
||||||
return st, nil
|
return st, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunCroak(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) {
|
func RunCroak(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) {
|
||||||
|
head, tail, err := instructionSplit(instruction)
|
||||||
|
if err != nil {
|
||||||
|
return st, err
|
||||||
|
}
|
||||||
|
_ = head
|
||||||
|
_ = tail
|
||||||
|
st.Reset()
|
||||||
return st, nil
|
return st, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +96,9 @@ func RunLoad(instruction []byte, st state.State, rs resource.Fetcher, ctx contex
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return st, err
|
return st, err
|
||||||
}
|
}
|
||||||
|
if !st.Check(head) {
|
||||||
|
return st, fmt.Errorf("key %v already loaded", head)
|
||||||
|
}
|
||||||
fn, err := rs.FuncFor(head)
|
fn, err := rs.FuncFor(head)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return st, err
|
return st, err
|
||||||
|
Loading…
Reference in New Issue
Block a user