From 92ea6c3eb02e05adcbdda0a5b39b67a6976314ba Mon Sep 17 00:00:00 2001 From: Catya3 <166070941+Catya3@users.noreply.github.com> Date: Tue, 11 Jun 2024 17:23:05 -0400 Subject: [PATCH 1/8] lists: Add an At op to access list elements --- internal/runtime/funcs/list_at.go | 72 ++++++++++++++++++++++++++++++ internal/runtime/funcs/registry.go | 1 + std/lists/lists.neva | 3 ++ 3 files changed, 76 insertions(+) create mode 100644 internal/runtime/funcs/list_at.go diff --git a/internal/runtime/funcs/list_at.go b/internal/runtime/funcs/list_at.go new file mode 100644 index 00000000..be1fcf77 --- /dev/null +++ b/internal/runtime/funcs/list_at.go @@ -0,0 +1,72 @@ +package funcs + +import ( + "context" + "errors" + + "github.com/nevalang/neva/internal/runtime" +) + +var errIndexOutOfBounds = errors.New("index out of bounds") + +type listAt struct{} + +func (listAt) Create(io runtime.FuncIO, _ runtime.Msg) (func(ctx context.Context), error) { + dataIn, err := io.In.Port("data") + if err != nil { + return nil, err + } + + idxIn, err := io.In.Port("idx") + if err != nil { + return nil, err + } + + resOut, err := io.Out.Port("res") + if err != nil { + return nil, err + } + + errOut, err := io.Out.Port("err") + if err != nil { + return nil, err + } + + return func(ctx context.Context) { + for { + var data []runtime.Msg + var idx int64 + + select { + case <-ctx.Done(): + return + case msg := <-dataIn: + data = msg.List() + } + + select { + case <-ctx.Done(): + return + case msg := <-idxIn: + idx = msg.Int() + } + + if idx < 0 || idx >= int64(len(data)) { + select { + case <-ctx.Done(): + return + case errOut <- runtime.NewMapMsg(map[string]runtime.Msg{ + "text": runtime.NewStrMsg(errIndexOutOfBounds.Error()), + }): + continue + } + } + + select { + case <-ctx.Done(): + return + case resOut <- data[idx]: + } + } + }, nil +} diff --git a/internal/runtime/funcs/registry.go b/internal/runtime/funcs/registry.go index 4753edd5..cf035093 100644 --- a/internal/runtime/funcs/registry.go +++ b/internal/runtime/funcs/registry.go @@ -63,6 +63,7 @@ func CreatorRegistry() map[string]runtime.FuncCreator { "regexp_submatch": regexpSubmatch{}, // list + "list_at": listAt{}, "index": index{}, "list_len": listlen{}, "list_push": listPush{}, diff --git a/std/lists/lists.neva b/std/lists/lists.neva index 0aab7bf0..6685888d 100644 --- a/std/lists/lists.neva +++ b/std/lists/lists.neva @@ -1,3 +1,6 @@ +#extern(list_at) +pub flow At(data list, idx int) (res T, err error) + pub flow For(data list) (sig any) { nodes { Iter From 6114373c589aab27dcb03044a642af96e1fb8b54 Mon Sep 17 00:00:00 2001 From: Catya3 <166070941+Catya3@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:57:38 -0400 Subject: [PATCH 2/8] strings: Add At command returning the n-th rune --- internal/runtime/funcs/registry.go | 1 + internal/runtime/funcs/string_at.go | 71 +++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 internal/runtime/funcs/string_at.go diff --git a/internal/runtime/funcs/registry.go b/internal/runtime/funcs/registry.go index cf035093..3c89f94c 100644 --- a/internal/runtime/funcs/registry.go +++ b/internal/runtime/funcs/registry.go @@ -74,6 +74,7 @@ func CreatorRegistry() map[string]runtime.FuncCreator { "time_sleep": timeSleep{}, // strings + "string_at": stringAt{}, "join": stringJoin{}, "split": stringSplit{}, "string_sort": listSortString{}, diff --git a/internal/runtime/funcs/string_at.go b/internal/runtime/funcs/string_at.go new file mode 100644 index 00000000..10999ac2 --- /dev/null +++ b/internal/runtime/funcs/string_at.go @@ -0,0 +1,71 @@ +package funcs + +import ( + "context" + "errors" + + "github.com/nevalang/neva/internal/runtime" + "golang.org/x/exp/utf8string" +) + +var errIndexOutOfBounds = errors.New("index out of bounds") + +type stringAt struct{} + +func (stringAt) Create(io runtime.FuncIO, _ runtime.Msg) (func(context.Context), error) { + dataIn, err := io.In.Port("data") + if err != nil { + return nil, err + } + + idxIn, err := io.In.Port("idx") + if err != nil { + return nil, err + } + + resOut, err := io.Out.Port("res") + if err != nil { + return nil, err + } + + errOut, err := io.Out.Port("err") + if err != nil { + return nil, err + } + + return func(ctx context.Context) { + for { + var data *utf8string.String + var idx int64 + + select { + case <-ctx.Done(): + return + case msg := <-dataIn: + data = utf8string.NewString(msg.Str()) + } + + select { + case <-ctx.Done(): + return + case msg := <-idxIn: + idx = msg.Int() + } + + if idx < 0 || idx >= int64(data.RuneCount()) { + select { + case <-ctx.Done(): + return + case errOut <- errorFromString(errIndexOutOfBounds.Error()): + continue + } + } + + select { + case <-ctx.Done(): + return + case resOut <- runtime.NewStrMsg(string(data.At(int(idx)))): + } + } + }, nil +} From 9eb427af114bc6cfea4bf7444c9d6be70936983b Mon Sep 17 00:00:00 2001 From: Catya3 <166070941+Catya3@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:59:18 -0400 Subject: [PATCH 3/8] builtin: Remove index flow op Replaced by strings and lists variants --- internal/runtime/funcs/index.go | 67 ------------------------------ internal/runtime/funcs/registry.go | 1 - 2 files changed, 68 deletions(-) delete mode 100644 internal/runtime/funcs/index.go diff --git a/internal/runtime/funcs/index.go b/internal/runtime/funcs/index.go deleted file mode 100644 index 6feeadb1..00000000 --- a/internal/runtime/funcs/index.go +++ /dev/null @@ -1,67 +0,0 @@ -package funcs - -import ( - "context" - - "github.com/nevalang/neva/internal/runtime" -) - -type index struct{} - -func (p index) Create(io runtime.FuncIO, _ runtime.Msg) (func(ctx context.Context), error) { - listIn, err := io.In.Port("data") - if err != nil { - return nil, err - } - - indexIn, err := io.In.Port("idx") - if err != nil { - return nil, err - } - - resOut, err := io.Out.Port("res") - if err != nil { - return nil, err - } - - errOut, err := io.Out.Port("err") - if err != nil { - return nil, err - } - - return func(ctx context.Context) { - var listMsg, idxMsg runtime.Msg - - for { - select { - case <-ctx.Done(): - return - case listMsg = <-listIn: - } - - select { - case <-ctx.Done(): - return - case idxMsg = <-indexIn: - } - - idx := idxMsg.Int() - list := listMsg.List() - - if idx < 0 || idx >= int64(len(list)) { - select { - case <-ctx.Done(): - return - case errOut <- runtime.NewStrMsg("Index out of bounds"): - continue - } - } - - select { - case <-ctx.Done(): - return - case resOut <- list[idx]: - } - } - }, nil -} diff --git a/internal/runtime/funcs/registry.go b/internal/runtime/funcs/registry.go index 3c89f94c..b655071c 100644 --- a/internal/runtime/funcs/registry.go +++ b/internal/runtime/funcs/registry.go @@ -64,7 +64,6 @@ func CreatorRegistry() map[string]runtime.FuncCreator { // list "list_at": listAt{}, - "index": index{}, "list_len": listlen{}, "list_push": listPush{}, "int_sort": listSortInt{}, From 81478f414daebfce9c7b69fa055b68c2579dcd74 Mon Sep 17 00:00:00 2001 From: Catya3 <166070941+Catya3@users.noreply.github.com> Date: Wed, 12 Jun 2024 12:03:31 -0400 Subject: [PATCH 4/8] strings: Remove duplicate definition of errIndexOutOfBounds --- internal/runtime/funcs/string_at.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/runtime/funcs/string_at.go b/internal/runtime/funcs/string_at.go index 10999ac2..2a933efd 100644 --- a/internal/runtime/funcs/string_at.go +++ b/internal/runtime/funcs/string_at.go @@ -2,13 +2,11 @@ package funcs import ( "context" - "errors" - "github.com/nevalang/neva/internal/runtime" "golang.org/x/exp/utf8string" -) -var errIndexOutOfBounds = errors.New("index out of bounds") + "github.com/nevalang/neva/internal/runtime" +) type stringAt struct{} From a270b231e10e17d487ee896df6b3bd9e991fefc3 Mon Sep 17 00:00:00 2001 From: Catya3 <166070941+Catya3@users.noreply.github.com> Date: Wed, 12 Jun 2024 12:08:43 -0400 Subject: [PATCH 5/8] builtin: Remove `Index` flow definition --- std/builtin/collections.neva | 6 ------ 1 file changed, 6 deletions(-) diff --git a/std/builtin/collections.neva b/std/builtin/collections.neva index 217a3492..55658d79 100644 --- a/std/builtin/collections.neva +++ b/std/builtin/collections.neva @@ -9,12 +9,6 @@ pub flow Len | map | string>(data T) (res int) #extern(stream_to_list) pub flow List(seq stream) (res list) -// Index returns the element at the given index in the ordered collection. -// If the index is out of bounds, it returns an error. -// The index is zero-based. -#extern(index) -pub flow Index | string>(data T, idx int) (res T, err error) - // Push creates new list with appended element. #extern(list_push) pub flow Push (lst list, data T) (res list) From e96b2e5e432c2702700dfb7cfe3cd58bda42c75a Mon Sep 17 00:00:00 2001 From: Catya3 <166070941+Catya3@users.noreply.github.com> Date: Wed, 12 Jun 2024 12:13:41 -0400 Subject: [PATCH 6/8] Examples: Update example for lists.At --- examples/list_index/main.neva | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/list_index/main.neva b/examples/list_index/main.neva index 7954f2ab..6669ef18 100644 --- a/examples/list_index/main.neva +++ b/examples/list_index/main.neva @@ -1,10 +1,12 @@ +import { lists } + const lst list = [1, 1, 5, 112, 69, 420] flow Main(start) (stop) { - nodes { Index>, Println } + nodes { lists.At, Println } :start -> [ - ($lst -> index:data), - (4 -> index:idx) + ($lst -> at:data), + (4 -> at:idx) ] - [index:res, index:err] -> println -> :stop -} \ No newline at end of file + [at:res, at:err] -> println -> :stop +} From 5ad6e4eb4e23056732fa15de73cc6e873580a881 Mon Sep 17 00:00:00 2001 From: Catya3 <166070941+Catya3@users.noreply.github.com> Date: Wed, 12 Jun 2024 12:31:38 -0400 Subject: [PATCH 7/8] strings: Add no dependency version of `At` --- internal/runtime/funcs/string_at.go | 31 +++++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/internal/runtime/funcs/string_at.go b/internal/runtime/funcs/string_at.go index 2a933efd..92702efc 100644 --- a/internal/runtime/funcs/string_at.go +++ b/internal/runtime/funcs/string_at.go @@ -3,8 +3,6 @@ package funcs import ( "context" - "golang.org/x/exp/utf8string" - "github.com/nevalang/neva/internal/runtime" ) @@ -33,14 +31,14 @@ func (stringAt) Create(io runtime.FuncIO, _ runtime.Msg) (func(context.Context), return func(ctx context.Context) { for { - var data *utf8string.String + var data string var idx int64 select { case <-ctx.Done(): return case msg := <-dataIn: - data = utf8string.NewString(msg.Str()) + data = msg.Str() } select { @@ -50,19 +48,30 @@ func (stringAt) Create(io runtime.FuncIO, _ runtime.Msg) (func(context.Context), idx = msg.Int() } - if idx < 0 || idx >= int64(data.RuneCount()) { - select { - case <-ctx.Done(): - return - case errOut <- errorFromString(errIndexOutOfBounds.Error()): - continue + if idx >= 0 && idx < int64(len(data)) { + var res rune + var found bool + for i, r := range data { + if int64(i) == idx { + res = r + found = true + break + } + } + if found { + select { + case <-ctx.Done(): + return + case resOut <- runtime.NewStrMsg(string(data.At(int(idx)))): + continue + } } } select { case <-ctx.Done(): return - case resOut <- runtime.NewStrMsg(string(data.At(int(idx)))): + case errOut <- errorFromString(errIndexOutOfBounds.Error()): } } }, nil From 523048a4b71ade1af50cd82796b2f94db8c286ff Mon Sep 17 00:00:00 2001 From: Catya3 <166070941+Catya3@users.noreply.github.com> Date: Thu, 13 Jun 2024 17:27:48 -0400 Subject: [PATCH 8/8] At: Support negative indexing and return typed errs --- internal/runtime/funcs/list_at.go | 13 +++++++++---- internal/runtime/funcs/string_at.go | 14 ++++++++++++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/internal/runtime/funcs/list_at.go b/internal/runtime/funcs/list_at.go index be1fcf77..a77cad65 100644 --- a/internal/runtime/funcs/list_at.go +++ b/internal/runtime/funcs/list_at.go @@ -7,7 +7,7 @@ import ( "github.com/nevalang/neva/internal/runtime" ) -var errIndexOutOfBounds = errors.New("index out of bounds") +var errListIndexOutOfBounds = errors.New("list index out of bounds") type listAt struct{} @@ -51,13 +51,18 @@ func (listAt) Create(io runtime.FuncIO, _ runtime.Msg) (func(ctx context.Context idx = msg.Int() } + if idx < 0 { + // Support negative indexing: + // $l = [1, 2, 3] + // $l[-1] // 3 + idx += int64(len(data)) + } + if idx < 0 || idx >= int64(len(data)) { select { case <-ctx.Done(): return - case errOut <- runtime.NewMapMsg(map[string]runtime.Msg{ - "text": runtime.NewStrMsg(errIndexOutOfBounds.Error()), - }): + case errOut <- errorFromString(errListIndexOutOfBounds.Error()): continue } } diff --git a/internal/runtime/funcs/string_at.go b/internal/runtime/funcs/string_at.go index 92702efc..eea3672e 100644 --- a/internal/runtime/funcs/string_at.go +++ b/internal/runtime/funcs/string_at.go @@ -2,10 +2,13 @@ package funcs import ( "context" + "errors" "github.com/nevalang/neva/internal/runtime" ) +var errStrIndexOutOfBounds = errors.New("string index out of bounds") + type stringAt struct{} func (stringAt) Create(io runtime.FuncIO, _ runtime.Msg) (func(context.Context), error) { @@ -48,6 +51,13 @@ func (stringAt) Create(io runtime.FuncIO, _ runtime.Msg) (func(context.Context), idx = msg.Int() } + if idx < 0 { + // Support negaitve indexing: + // $s = "abc" + // $s[-1] // "c" + idx += int64(len(data)) + } + if idx >= 0 && idx < int64(len(data)) { var res rune var found bool @@ -62,7 +72,7 @@ func (stringAt) Create(io runtime.FuncIO, _ runtime.Msg) (func(context.Context), select { case <-ctx.Done(): return - case resOut <- runtime.NewStrMsg(string(data.At(int(idx)))): + case resOut <- runtime.NewStrMsg(string(res)): continue } } @@ -71,7 +81,7 @@ func (stringAt) Create(io runtime.FuncIO, _ runtime.Msg) (func(context.Context), select { case <-ctx.Done(): return - case errOut <- errorFromString(errIndexOutOfBounds.Error()): + case errOut <- errorFromString(errStrIndexOutOfBounds.Error()): } } }, nil