Ditch IndexVarer interface in favor of IndexSetter.

This complicates indexing a little bit, but makes it easier to implement new
containers.
This commit is contained in:
Qi Xiao 2016-02-12 02:32:15 +01:00
parent 34fddcd100
commit 60a7f59ea9
3 changed files with 51 additions and 49 deletions

View File

@ -323,19 +323,38 @@ func (cp *compiler) indexingVar(n *parse.Indexing, msg string) VariableOp {
if variable == nil {
ec.errorf(p, "variable $%s does not exisit, compiler bug", varname)
}
for i, op := range indexOps {
indexer, ok := variable.Get().(IndexVarer)
if len(indexOps) == 0 {
// Just a variable, return directly.
return variable
}
// Indexing. Do Index up to the last but one index.
value := variable.Get()
n := len(indexOps)
for i, op := range indexOps[:n-1] {
indexer, ok := value.(Indexer)
if !ok {
ec.errorf( /* from p to */ indexBegins[i], "cannot be indexing for setting (type %T)", variable.Get())
}
values := op(ec)
if len(values) != 1 {
ec.errorf(indexBegins[i], "index must eval to a single Value (got %v)", values)
ec.errorf( /* from p to */ indexBegins[i], "cannot be indexed (value is %s, type %s)", value.Repr(), value.Type())
}
variable = indexer.IndexVar(values[0])
indicies := op(ec)
if len(indicies) != 1 {
ec.errorf(indexBegins[i], "index must eval to a single Value (got %v)", indicies)
}
value = indexer.Index(indicies[0])
}
return variable
// Now this must be an IndexSetter.
indexSetter, ok := value.(IndexSetter)
if !ok {
ec.errorf( /* from p to */ indexBegins[n-1], "cannot be indexed for setting (value is %s, type %s)", value.Repr(), value.Type())
}
// XXX Duplicate code.
indicies := indexOps[n-1](ec)
if len(indicies) != 1 {
ec.errorf(indexBegins[n-1], "index must eval to a single Value (got %v)", indicies)
}
return elemVariable{indexSetter, indicies[0]}
}
}

View File

@ -42,9 +42,10 @@ type Indexer interface {
Index(idx Value) Value
}
// IndexVarer is anything that can be indexed by a Value and yields a Variable.
type IndexVarer interface {
IndexVar(idx Value) Variable
// IndexSetter is a Value whose elements can be get as well as set.
type IndexSetter interface {
Indexer
IndexSet(idx Value, v Value)
}
// Caller is anything may be called on an evalCtx with a list of Value's.
@ -283,8 +284,15 @@ func (l List) Index(idx Value) Value {
return (*l.inner)[i]
}
func (l List) IndexVar(idx Value) Variable {
return listElem{l, intIndex(idx)}
func (l List) IndexSet(idxv Value, v Value) {
idx := intIndex(idxv)
if idx < 0 {
idx += len(*l.inner)
}
if idx < 0 || idx >= len(*l.inner) {
throw(ErrIndexOutOfRange)
}
(*l.inner)[idx] = v
}
// Map is a map from string to Value.
@ -328,8 +336,8 @@ func (m Map) Index(idx Value) Value {
return v
}
func (m Map) IndexVar(idx Value) Variable {
return mapElem{m, idx}
func (m Map) IndexSet(idx Value, v Value) {
(*m.inner)[idx] = v
}
// Closure is a closure.

View File

@ -24,43 +24,18 @@ func (iv ptrVariable) Get() Value {
return *iv.valuePtr
}
// listElem is the Variable generated by indexing a List.
type listElem struct {
parent List
idx int
// elemVariable is an element of a IndexSetter.
type elemVariable struct {
container IndexSetter
index Value
}
func (le listElem) Set(val Value) {
(*le.parent.inner)[le.index()] = val
func (ev elemVariable) Set(val Value) {
ev.container.IndexSet(ev.index, val)
}
func (le listElem) Get() Value {
return (*le.parent.inner)[le.index()]
}
func (le listElem) index() int {
idx := le.idx
if idx < 0 {
idx += len(*le.parent.inner)
}
if idx < 0 || idx >= len(*le.parent.inner) {
throw(ErrIndexOutOfRange)
}
return idx
}
// mapElem is the Variable generated by indexing a Map.
type mapElem struct {
parent Map
idx Value
}
func (me mapElem) Set(val Value) {
(*me.parent.inner)[me.idx] = val
}
func (me mapElem) Get() Value {
return (*me.parent.inner)[me.idx]
func (ev elemVariable) Get() Value {
return ev.container.Index(ev.index)
}
type envVariable struct {