Skip to content

Commit

Permalink
std: rename sub to set and genericize (#190)
Browse files Browse the repository at this point in the history
* wdte: Rename `sub` to `set` and genercize.

* wdte: Update to work with new `set` function.
  • Loading branch information
DeedleFake committed Jun 28, 2019
1 parent 0aeaf58 commit 4e6feca
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 54 deletions.
63 changes: 37 additions & 26 deletions std/std.go
Expand Up @@ -467,17 +467,50 @@ func At(frame wdte.Frame, args ...wdte.Func) wdte.Func {
at := args[0].Call(frame).(wdte.Atter)
i := args[1].Call(frame)

ret, ok := at.At(i)
if !ok {
ret, err := at.At(i)
if err != nil {
return &wdte.Error{
Frame: frame,
Err: fmt.Errorf("index %v out of range", i),
Err: err,
}
}

return ret
}

// Set is a WDTE function with the following signatures:
//
// set con key val
// (set val) con key
// (set key val) con
//
// Set uses con's implementation of Setter to produce a new value from
// con with a key-val mapping applied to it. For example,
//
// set [1; 2; 3] 1 5
//
// returns a new Array containing [1; 5; 3].
func Set(frame wdte.Frame, args ...wdte.Func) wdte.Func {
frame = frame.Sub("set")

if len(args) < 3 {
return wdteutil.SaveArgsReverse(wdte.GoFunc(Set), args...)
}

s := args[0].Call(frame).(wdte.Setter)
k := args[1].Call(frame)
v := args[2]

r, err := s.Set(k, v)
if err != nil {
return &wdte.Error{
Frame: frame,
Err: err,
}
}
return r
}

// Collect is a WDTE function with the following signature:
//
// collect compound
Expand Down Expand Up @@ -522,28 +555,6 @@ func Known(frame wdte.Frame, args ...wdte.Func) wdte.Func {
return ret
}

// Sub is a WDTE function with the following signatures:
//
// sub scope id val
// (sub val) scope id
// (sub id val) scope
//
// Sub returns a subscope of scope with the value val bound to the ID
// id.
func Sub(frame wdte.Frame, args ...wdte.Func) wdte.Func {
if len(args) <= 2 {
return wdteutil.SaveArgsReverse(wdte.GoFunc(Sub), args...)
}

frame = frame.Sub("sub")

s := args[0].Call(frame).(*wdte.Scope)
id := wdte.ID(args[1].Call(frame).(wdte.String))
v := args[2]

return s.Add(id, v)
}

// Reflect is a WDTE function with the following signature:
//
// reflect v type
Expand Down Expand Up @@ -592,7 +603,7 @@ var Scope = wdte.S().Map(map[wdte.ID]wdte.Func{
"at": wdte.GoFunc(At),
"collect": wdte.GoFunc(Collect),
"known": wdte.GoFunc(Known),
"sub": wdte.GoFunc(Sub),
"set": wdte.GoFunc(Set),
"reflect": wdte.GoFunc(Reflect),
})

Expand Down
48 changes: 30 additions & 18 deletions types.go
Expand Up @@ -26,9 +26,15 @@ type Lenner interface {

// An Atter is a Func that can be indexed, like an array or a string.
type Atter interface {
// At returns the value at index i. If the index is out of range, it
// should return false as its second return value.
At(i Func) (Func, bool)
At(i Func) (Func, error)
}

// A Setter is a Func that can produce a new Func from itself with a
// key-value mapping applied in some way. For example, a scope can
// produce a subscope with a new variable added to it, or an array can
// produce a new array with an index modified.
type Setter interface {
Set(k, v Func) (Func, error)
}

// A Reflector is a Func that can determine if it can be treated as
Expand Down Expand Up @@ -85,16 +91,13 @@ func (s String) Len() int { // nolint
return len(s)
}

func (s String) At(i Func) (Func, bool) { // nolint
if i, ok := i.(Number); ok {
if (int(i) < 0) || (int(i) >= len(s)) {
return nil, false
}

return String(s[int(i)]), true
func (s String) At(index Func) (Func, error) { // nolint
i := int(index.(Number))
if (i < 0) || (i >= len(s)) {
return nil, fmt.Errorf("index %v is out of range [0,%v)", i, len(s))
}

return nil, false
return String(s[i]), nil
}

func (s String) Reflect(name string) bool { // nolint
Expand Down Expand Up @@ -158,16 +161,25 @@ func (a Array) Len() int { // nolint
return len(a)
}

func (a Array) At(i Func) (Func, bool) { // nolint
if i, ok := i.(Number); ok {
if (int(i) < 0) || (int(i) >= len(a)) {
return nil, false
}
func (a Array) At(index Func) (Func, error) { // nolint
i := int(index.(Number))
if (i < 0) || (i >= len(a)) {
return nil, fmt.Errorf("index %v is out of range [0,%v)", i, len(a))
}

return a[i], nil
}

return a[int(i)], true
func (a Array) Set(k, v Func) (Func, error) { // nolint
i := int(k.(Number))
if (i < 0) || (i >= len(a)) {
return nil, fmt.Errorf("index %v is out of bounds [0,%v]", i, len(a))
}

return nil, false
c := make(Array, len(a))
copy(c, a)
c[i] = v
return c, nil
}

func (a Array) String() string { // nolint
Expand Down
17 changes: 12 additions & 5 deletions wdte.go
Expand Up @@ -354,9 +354,16 @@ func (s *Scope) Call(frame Frame, args ...Func) Func { // nolint
return s
}

func (s *Scope) At(i Func) (Func, bool) { // nolint
func (s *Scope) At(i Func) (Func, error) { // nolint
v := s.Get(ID(i.(String)))
return v, v != nil
if v == nil {
return nil, fmt.Errorf("%v is not in scope", i)
}
return v, nil
}

func (s *Scope) Set(k, v Func) (Func, error) { // nolint
return s.Add(ID(k.(String)), v), nil
}

func (s *Scope) String() string { // nolint
Expand Down Expand Up @@ -864,10 +871,10 @@ func AssignPattern(frame Frame, scope *Scope, ids []ID, val Func) (*Scope, Func)
}) (*Scope, Func) {
m := make(map[ID]Func, len(ids))
for i, id := range ids {
v, ok := f.At(Number(i))
if !ok {
v, err := f.At(Number(i))
if err != nil {
return nil, &Error{
Err: errors.New("Atter shorter than pattern"),
Err: err,
Frame: frame,
}
}
Expand Down
15 changes: 10 additions & 5 deletions wdte_test.go
Expand Up @@ -442,6 +442,16 @@ func TestStd(t *testing.T) {
script: `let m => import 'math'; at m 'pi';`,
ret: wdte.Number(math.Pi),
},
{
name: "Set/Scope",
script: `let t => collect (let test => 3); let t => set t 'test2' 5; t.test2;`,
ret: wdte.Number(5),
},
{
name: "Set/Array",
script: `let t => [1; 2; 3]; set t 1 5;`,
ret: wdte.Array{wdte.Number(1), wdte.Number(5), wdte.Number(3)},
},
{
name: "Collect",
script: `let t => collect (let test => 3); t.test;`,
Expand All @@ -452,11 +462,6 @@ func TestStd(t *testing.T) {
script: `let t => collect (let test => 3; let other => 5); known t;`,
ret: wdte.Array{wdte.String("other"), wdte.String("test")},
},
{
name: "Sub",
script: `let t => collect (let test => 3); let t => sub t 'test2' 5; t.test2;`,
ret: wdte.Number(5),
},
{
name: "Reflect",
script: `[reflect 'string' 'String'; 'string' {reflect 'String' => 'test'}];`,
Expand Down

0 comments on commit 4e6feca

Please sign in to comment.