From 4e6feca9b902e646edd3082b1e83cbd8563150d4 Mon Sep 17 00:00:00 2001 From: DeedleFake Date: Fri, 28 Jun 2019 11:37:12 -0400 Subject: [PATCH] std: rename `sub` to `set` and genericize (#190) * wdte: Rename `sub` to `set` and genercize. * wdte: Update to work with new `set` function. --- std/std.go | 63 ++++++++++++++++++++++++++++++---------------------- types.go | 48 ++++++++++++++++++++++++--------------- wdte.go | 17 +++++++++----- wdte_test.go | 15 ++++++++----- 4 files changed, 89 insertions(+), 54 deletions(-) diff --git a/std/std.go b/std/std.go index 09bda45..79b6f76 100644 --- a/std/std.go +++ b/std/std.go @@ -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 @@ -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 @@ -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), }) diff --git a/types.go b/types.go index 3c49c9b..4ae7f91 100644 --- a/types.go +++ b/types.go @@ -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 @@ -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 @@ -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 diff --git a/wdte.go b/wdte.go index 84f1c3b..c6a8850 100644 --- a/wdte.go +++ b/wdte.go @@ -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 @@ -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, } } diff --git a/wdte_test.go b/wdte_test.go index a99a12a..3308a43 100644 --- a/wdte_test.go +++ b/wdte_test.go @@ -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;`, @@ -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'}];`,