Skip to content

Commit

Permalink
Merge pull request #246 from NeowayLabs/addIndexingToStrings
Browse files Browse the repository at this point in the history
Add indexing to strings
  • Loading branch information
i4ki authored Nov 11, 2017
2 parents b80e1ab + 99aed2a commit 13fafe7
Show file tree
Hide file tree
Showing 13 changed files with 503 additions and 128 deletions.
17 changes: 6 additions & 11 deletions internal/sh/builtin/len.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

type (
lenFn struct {
arg sh.Obj
arg sh.Collection
}
)

Expand All @@ -29,12 +29,7 @@ func lenresult(res int) []sh.Obj {
}

func (l *lenFn) Run(in io.Reader, out io.Writer, err io.Writer) ([]sh.Obj, error) {
if l.arg.Type() == sh.ListType {
arglist := l.arg.(*sh.ListObj)
return lenresult(len(arglist.List())), nil
}
argstr := l.arg.(*sh.StrObj)
return lenresult(len(argstr.Str())), nil
return lenresult(l.arg.Len()), nil
}

func (l *lenFn) SetArgs(args []sh.Obj) error {
Expand All @@ -43,11 +38,11 @@ func (l *lenFn) SetArgs(args []sh.Obj) error {
}

obj := args[0]

if obj.Type() != sh.ListType && obj.Type() != sh.StringType {
return errors.NewError("lenfn expects a list or a string, but a %s was provided", obj.Type())
col, err := sh.NewCollection(obj)
if err != nil {
return errors.NewError("len:error[%s]", err)
}

l.arg = obj
l.arg = col
return nil
}
109 changes: 48 additions & 61 deletions internal/sh/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,41 +529,37 @@ func (shell *Shell) ExecFile(path string) error {
}

func (shell *Shell) setvar(name *ast.NameNode, value sh.Obj) error {
finalObj := value

if name.Index != nil {
if list, ok := shell.Getvar(name.Ident); ok {
index, err := shell.evalIndex(name.Index)

if err != nil {
return err
}

if list.Type() != sh.ListType {
return errors.NewEvalError(shell.filename,
name, "Indexed assigment on non-list type: Variable %s is %s",
name.Ident,
list.Type())
}
if name.Index == nil {
shell.Setvar(name.Ident, value)
return nil
}

lobj := list.(*sh.ListObj)
lvalues := lobj.List()
obj, ok := shell.Getvar(name.Ident)
if !ok {
return errors.NewEvalError(shell.filename,
name, "Variable %s not found", name.Ident)
}

if index >= len(lvalues) {
return errors.NewEvalError(shell.filename,
name, "List out of bounds error. List has %d elements, but trying to access index %d",
len(lvalues), index)
}
index, err := shell.evalIndex(name.Index)
if err != nil {
return err
}

lvalues[index] = value
finalObj = sh.NewListObj(lvalues)
} else {
return errors.NewEvalError(shell.filename,
name, "Variable %s not found", name.Ident)
}
col, err := sh.NewWriteableCollection(obj)
if err != nil {
return errors.NewEvalError(shell.filename, name, err.Error())
}

shell.Setvar(name.Ident, finalObj)
err = col.Set(index, value)
if err != nil {
return errors.NewEvalError(
shell.filename,
name,
"error[%s] setting var",
err,
)
}
return nil
}

Expand Down Expand Up @@ -1486,27 +1482,21 @@ func (shell *Shell) evalIndexedVar(indexVar *ast.IndexExpr) (sh.Obj, error) {
return nil, err
}

if v.Type() != sh.ListType {
return nil, errors.NewEvalError(shell.filename, indexVar.Var,
"Invalid indexing of non-list variable: %s (%+v)", v.Type(), v)
col, err := sh.NewCollection(v)
if err != nil {
return nil, errors.NewEvalError(shell.filename, indexVar.Var, err.Error())
}

indexNum, err := shell.evalIndex(indexVar.Index)
if err != nil {
return nil, err
}

vlist := v.(*sh.ListObj)
values := vlist.List()

if indexNum < 0 || indexNum >= len(values) {
return nil, errors.NewEvalError(shell.filename,
indexVar.Var,
"Index out of bounds. len(%s) == %d, but given %d", indexVar.Var.Name,
len(values), indexNum)
val, err := col.Get(indexNum)
if err != nil {
return nil, errors.NewEvalError(shell.filename, indexVar.Var, err.Error())
}

return values[indexNum], nil
return val, nil
}

func (shell *Shell) evalArgIndexedVar(indexVar *ast.IndexExpr) ([]sh.Obj, error) {
Expand All @@ -1515,28 +1505,21 @@ func (shell *Shell) evalArgIndexedVar(indexVar *ast.IndexExpr) ([]sh.Obj, error)
return nil, err
}

if v.Type() != sh.ListType {
return nil, errors.NewEvalError(shell.filename, indexVar.Var,
"Invalid indexing of non-list variable: %s (%+v)", v.Type(), v)
col, err := sh.NewCollection(v)
if err != nil {
return nil, errors.NewEvalError(shell.filename, indexVar.Var, err.Error())
}

indexNum, err := shell.evalIndex(indexVar.Index)
if err != nil {
return nil, err
}

vlist := v.(*sh.ListObj)
values := vlist.List()

if indexNum < 0 || indexNum >= len(values) {
return nil, errors.NewEvalError(shell.filename,
indexVar.Var,
"Index out of bounds. len(%s) == %d, but given %d", indexVar.Var.Name,
len(values), indexNum)
retval, err := col.Get(indexNum)
if err != nil {
return nil, errors.NewEvalError(shell.filename, indexVar.Var, err.Error())
}

retval := values[indexNum]

if indexVar.IsVariadic {
if retval.Type() != sh.ListType {
return nil, errors.NewEvalError(shell.filename,
Expand All @@ -1545,7 +1528,7 @@ func (shell *Shell) evalArgIndexedVar(indexVar *ast.IndexExpr) ([]sh.Obj, error)
retlist := retval.(*sh.ListObj)
return retlist.List(), nil
}
return []sh.Obj{values[indexNum]}, nil
return []sh.Obj{retval}, nil
}

func (shell *Shell) evalVariable(a ast.Expr) (sh.Obj, error) {
Expand Down Expand Up @@ -2213,14 +2196,18 @@ func (shell *Shell) executeFor(n *ast.ForNode) ([]sh.Obj, error) {
return nil, err
}

if obj.Type() != sh.ListType {
col, err := sh.NewCollection(obj)
if err != nil {
return nil, errors.NewEvalError(shell.filename,
inExpr, "Invalid variable type in for range: %s", obj.Type())
inExpr, "error[%s] trying to iterate", err)
}

objlist := obj.(*sh.ListObj)

for _, val := range objlist.List() {
for i := 0; i < col.Len(); i++ {
val, err := col.Get(i)
if err != nil {
return nil, errors.NewEvalError(shell.filename,
inExpr, "unexpected error[%s] during iteration", err)
}
shell.Setvar(id, val)

objs, err := shell.executeTree(n.Tree(), false)
Expand Down
53 changes: 0 additions & 53 deletions internal/sh/shell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,59 +524,6 @@ func TestExecuteCmdMultipleAssignment(t *testing.T) {
}
}

// IFS *DO NOT* exists anymore.
// This tests only assure things works as expected (IFS has no power)
func TestExecuteCmdAssignmentIFSDontWork(t *testing.T) {
for _, test := range []execTestCase{
{
"ifs",
`IFS = (" ")
range <= echo 1 2 3 4 5 6 7 8 9 10
for i in $range {
echo "i = " + $i
}`,
"", "",
"<interactive>:4:9: Invalid variable type in for range: StringType",
},
{
"ifs",
`IFS = (";")
range <= echo "1;2;3;4;5;6;7;8;9;10"
for i in $range {
echo "i = " + $i
}`,
"", "",
"<interactive>:4:9: Invalid variable type in for range: StringType",
},
{
"ifs",
`IFS = (" " ";")
range <= echo "1;2;3;4;5;6 7;8;9;10"
for i in $range {
echo "i = " + $i
}`,
"", "",
"<interactive>:4:9: Invalid variable type in for range: StringType",
},
{
"ifs",
`IFS = (" " "-")
range <= echo "1;2;3;4;5;6;7-8;9;10"
for i in $range {
echo "i = " + $i
}`,
"", "",
"<interactive>:4:9: Invalid variable type in for range: StringType",
},
} {
testExec(t, test)
}
}

func TestExecuteRedirection(t *testing.T) {
f, teardown := setup(t)
defer teardown()
Expand Down
80 changes: 77 additions & 3 deletions sh/obj.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,72 @@ type (

StrObj struct {
objType
str string
runes []rune
}

Collection interface {
Len() int
Get(index int) (Obj, error)
}

WriteableCollection interface {
Set(index int, val Obj) error
}
)

func NewCollection(o Obj) (Collection, error) {
sizer, ok := o.(Collection)
if !ok {
return nil, fmt.Errorf(
"SizeError: trying to get size from type %s which is not a collection",
o.Type(),
)
}
return sizer, nil
}

func NewWriteableCollection(o Obj) (WriteableCollection, error) {
indexer, ok := o.(WriteableCollection)
if !ok {
return nil, fmt.Errorf(
"IndexError: trying to use a non write/indexable type %s to write on index: ",
o.Type(),
)
}
return indexer, nil
}

func (o objType) Type() objType {
return o
}

func NewStrObj(val string) *StrObj {
return &StrObj{
str: val,
runes: []rune(val),
objType: StringType,
}
}

func (o *StrObj) Str() string { return o.str }
func (o *StrObj) Str() string { return string(o.runes) }

func (o *StrObj) String() string { return o.Str() }

func (o *StrObj) Get(index int) (Obj, error) {
if index >= o.Len() {
return nil, fmt.Errorf(
"IndexError: Index[%d] out of range, string size[%d]",
index,
o.Len(),
)
}

return NewStrObj(string(o.runes[index])), nil
}

func (o *StrObj) Len() int {
return len(o.runes)
}

func NewFnObj(val FnDef) *FnObj {
return &FnObj{
fn: val,
Expand All @@ -66,6 +113,33 @@ func NewListObj(val []Obj) *ListObj {
}
}

func (o *ListObj) Len() int {
return len(o.list)
}

func (o *ListObj) Set(index int, value Obj) error {
if index >= len(o.list) {
return fmt.Errorf(
"IndexError: Index[%d] out of range, list size[%d]",
index,
len(o.list),
)
}
o.list[index] = value
return nil
}

func (o *ListObj) Get(index int) (Obj, error) {
if index >= len(o.list) {
return nil, fmt.Errorf(
"IndexError: Index out of bounds, index[%d] but list size[%d]",
index,
len(o.list),
)
}
return o.list[index], nil
}

func (o *ListObj) List() []Obj { return o.list }

func (o *ListObj) String() string {
Expand Down
3 changes: 3 additions & 0 deletions tests/cfg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package tests

const nashcmd = "../cmd/nash/nash"
Loading

0 comments on commit 13fafe7

Please sign in to comment.