Skip to content

Commit

Permalink
Add update-row! and remove-row! and relevant tests
Browse files Browse the repository at this point in the history
also clean up some tests
  • Loading branch information
yumaikas committed Oct 5, 2024
1 parent 74a2d37 commit 6a8a8e0
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 15 deletions.
12 changes: 12 additions & 0 deletions env/spreadsheet.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ func NewSpreadsheetRow(values []any, uplink *Spreadsheet) *SpreadsheetRow {
return &nat
}

func SpreadsheetRowFromDict(dict Dict, uplink *Spreadsheet) (bool, string, *SpreadsheetRow) {
row := SpreadsheetRow{make([]any, len(uplink.Cols)), uplink}
for i, v := range uplink.Cols {
if val, ok := dict.Data[v]; ok {
row.Values[i] = val
} else {
return false, v, nil
}
}
return true, "", &row
}

type Spreadsheet struct {
Cols []string
Rows []SpreadsheetRow
Expand Down
42 changes: 36 additions & 6 deletions evaldo/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,32 @@ func MakeBuiltinError(env1 *env.ProgramState, msg string, fn string) *env.Error
return env.NewError(msg + " in builtin " + fn + ".")
}

func MakeArgError(env1 *env.ProgramState, N int, typ []env.Type, fn string) *env.Error {
env1.FailureFlag = true
func NameOfRyeType(t env.Type) string {
if t < 0 || int(t) >= len(env.NativeTypes) {
return "INVALID TYPE (" + strconv.FormatInt(int64(t), 10) + ")"
}
return env.NativeTypes[t]
}

func MakeArgErrorMessage(N int, allowedTypes []env.Type, fn string) string {
types := ""
for i, tt := range typ {
for i, tt := range allowedTypes {
if i > 0 {
types += ", "
}
types += env.NativeTypes[tt-1]
}
return env.NewError("builtin `" + fn + "` requires argument " + strconv.Itoa(N) + " to be: " + types + ".")
return "builtin `" + fn + "` requires argument " + strconv.Itoa(N) + " to be: " + types + "."
}

func MakeArgError(env1 *env.ProgramState, N int, typ []env.Type, fn string) *env.Error {
env1.FailureFlag = true
return env.NewError(MakeArgErrorMessage(N, typ, fn))
}

func MakeNeedsThawedArgError(env1 *env.ProgramState, fn string) *env.Error {
env1.FailureFlag = true
return env.NewError("builtin `" + fn + "` requires a thawed spreadsheet as the first argument")
}

func MakeNativeArgError(env1 *env.ProgramState, N int, knd []string, fn string) *env.Error {
Expand Down Expand Up @@ -6799,8 +6815,22 @@ var builtins = map[string]*env.Builtin{
return MakeBuiltinError(ps, fmt.Sprintf("String has less than %d elements.", num.Value), "nth")
}
return *env.NewString(string(str[num.Value-1 : num.Value]))
default:
return MakeArgError(ps, 1, []env.Type{env.BlockType}, "nth")
case env.Spreadsheet:
rows := s1.GetRows()
if num.Value > int64(len(rows)) {
return MakeBuiltinError(ps, fmt.Sprintf("Spreadhseet has less than %d rows.", num.Value), "nth")
}
return rows[num.Value-1]
case *env.Spreadsheet:
rows := s1.GetRows()
if num.Value > int64(len(rows)) {
return MakeBuiltinError(ps, fmt.Sprintf("Spreadhseet has less than %d rows.", num.Value), "nth")
}
return rows[num.Value-1]
default:
return MakeArgError(ps, 1,
[]env.Type{env.BlockType, env.ListType, env.StringType, env.SpreadsheetType},
"nth")
}
default:
return MakeArgError(ps, 2, []env.Type{env.IntegerType}, "nth")
Expand Down
75 changes: 73 additions & 2 deletions evaldo/builtins_spreadsheet.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,55 @@ var Builtins_spreadsheet = map[string]*env.Builtin{
}
},
},
"update-row!": {
Argsn: 3, // Spreadsheet, index function/dict
Doc: `Update the row at the given index. If given a dict or a spreadsheet row, replace the row with that.` +
`If given a function, pass the row, its index and replace the row with the return value from the function`,
Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
switch spr := arg0.(type) {
case *env.Spreadsheet:
switch idx := arg1.(type) {
case env.Integer:
if idx.Value < 1 || (idx.Value-1) > int64(len(spr.Rows)) {
errMsg := fmt.Sprintf("update-row! called with row index %i, but spreadsheet only has %i rows", idx.Value, len(spr.Rows))
return makeError(ps, errMsg)
}
switch updater := arg2.(type) {
case env.Function:
CallFunctionArgs4(updater, ps, spr.Rows[idx.Value-1], idx, nil, nil, ps.Ctx)
if !ps.ReturnFlag {
return makeError(ps, "Function given to update-row! should have returned a value, but didn't")
}
if ok, err, row := RyeValueToSpreadsheetRow(spr, ps.Res); ok {
spr.Rows[idx.Value-1] = *row
return spr
} else if len(err) > 0 {
return makeError(ps, err)
} else {
return makeError(ps, fmt.Sprintf(
"Function given to update-row! should have returned a Dict or a SpreadsheetRow, but returned a %s instead",
NameOfRyeType(ps.Res.Type()),
))
}

default:
if ok, err, row := RyeValueToSpreadsheetRow(spr, updater); ok {
spr.Rows[idx.Value-1] = *row
return ps.Res
} else if len(err) > 0 {
return makeError(ps, err)
}
return MakeArgError(ps, 3, []env.Type{env.DictType, env.SpreadsheetRowType}, "update-row")
}
default:
return MakeArgError(ps, 2, []env.Type{env.IntegerType}, "update-row!")
}
default:
return MakeNeedsThawedArgError(ps, "update-row!")
}

},
},
"remove-row!": {
Argsn: 2,
Doc: "Remove a row from a spreadsheet by index",
Expand All @@ -241,8 +290,12 @@ var Builtins_spreadsheet = map[string]*env.Builtin{
case *env.Spreadsheet:
switch data1 := arg1.(type) {
case env.Integer:
spr.RemoveRowByIndex(data1.Value)
return spr
if data1.Value > 0 && data1.Value <= int64(len(spr.Rows)) {
spr.RemoveRowByIndex(data1.Value - 1)
return spr
} else {
return makeError(ps, fmt.Sprintf("Spreadsheet had less then %d rows", data1.Value))
}
default:
return MakeArgError(ps, 2, []env.Type{env.BlockType, env.NativeType}, "remove-row!")
}
Expand Down Expand Up @@ -899,6 +952,24 @@ var Builtins_spreadsheet = map[string]*env.Builtin{
},
}

func RyeValueToSpreadsheetRow(spr *env.Spreadsheet, obj env.Object) (bool, string, *env.SpreadsheetRow) {
switch updater := obj.(type) {
case env.Dict:
success, missing, row := env.SpreadsheetRowFromDict(updater, spr)
if !success {
return false, "update-row! given a dict that is missing value for the " + missing + " column!", nil
} else {
return true, "", row

}
case env.SpreadsheetRow:
return true, "", &updater
default:
return false, "", nil
}

}

func DropColumnBlock(ps *env.ProgramState, s env.Spreadsheet, names env.Block) env.Object {
toDrop := make([]env.String, 0)
for _, obj := range names.Series.S {
Expand Down
4 changes: 2 additions & 2 deletions tests/main.rye
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ test-framework: context {
}
error: fn { test } {
; try { do\in root-ctx test }
try test :got \type? |= 'error \either { print join { " - OK: " mold got } } { inc! 'failed , print join { " - Error: expected error but got: " mold got } }
try test :got |type? |= 'error |either { print join { " - OK: " mold got } } { inc! 'failed , print join { " - Error: expected error but got: " mold got } }
}

equal: fn { test res } {
Expand Down Expand Up @@ -83,7 +83,7 @@ generate-doc-file: fn { filename menu } {
} |write* to-file filename + ".html"
}

menu: { "basics" "structures" "validation" "misc" "errors" }
menu: { "basics" "structures" "validation" "misc" "spreadsheet_mutations" }

switch arg {
"test" {
Expand Down
36 changes: 36 additions & 0 deletions tests/spreadsheet_mutations.rye
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
section "spreadsheet mutations"
"Functions that mutate thawed spreadsheets"
{

group "add-row!" "add-row!"
{ { block } }
{
equal {
spreadsheet { "title" "isbn" "price" "quantity" }
{
"Do Androids Dream of Electric Sheep?" "9781608866403" 12.99 4
"Animal Farm" "9780151002177" 2.49 10
"An Oxford Anthology of Shakespeare" "9780198129356" 19.99 10
"And Then There Were None" "9780062073488" 3.99 10
} |thaw ::cart
cart |nth 1 -> "title" } "Do Androids Dream of Electric Sheep?"
equal { cart |nth 3 -> "price" } 19.99

equal { cart .remove-row! 1 cart .nth 1 -> "title" } "Animal Farm"
equal {
rowdict: dict { "title" "Windowlight" "isbn" "0000000000000" "price" 0.99 "quantity" 1 }
probe rowdict
print rowdict .type?
cart .update-row! 1 rowdict
cart .nth 1 -> "isbn"
} "0000000000000"
equal {
updatefn: fn { row } { dict { "title" "I see the moon" "isbn" "0123456789012" "price" 0.99 "quantity" 1 } }
cart .update-row! 3 updatefn
cart .nth 3 -> "isbn"
} "0123456789012"

}

}

10 changes: 5 additions & 5 deletions tests/structures.rye
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ section "Spreadsheet related functions"
equal { spreadsheet { "name" "rating" "weight" }
{ "Enno" 4.3 120 "Enya" 6 132 "Shizuuchi" 7.2 168 "Kunishima" 2 68 } ::spr |type? } 'spreadsheet
equal { spr |length? } 4
equal { spr |columns? |length? } 3
equal { spr |header? |length? } 3
equal\todo { spr -> 2 -> 'name } "Enya"
equal { spr .first -> "name" } "Enno"
equal { spr .where-equal 'name "Enya" |length? } 1
Expand Down Expand Up @@ -618,8 +618,8 @@ section "Spreadsheet related functions"
equal { to-spreadsheet vals { dict { "a" 1 b 2 "c" 3 } dict { "a" 4 "b" 5 } } } spreadsheet { "a" "b" "c" } { 1 2 3 4 5 _ }
}

group "add-col"
mold\nowrap ?add-col!
group "add-column"
mold\nowrap ?add-column!
{ { block } }
{
equal { try { spreadsheet { "n" } [ 1 ] |add-col! 'm { x } { x } } |type? } 'error
Expand Down Expand Up @@ -674,12 +674,12 @@ section "Spreadsheet related functions"
mold\nowrap ?group-by
{ { block } }
{
equal { spreadsheet { "name" "val" } { "a" 1 "b" 2 } |group-by 'name { } |sort-by! 'name 'asc
equal { spreadsheet { "name" "val" } { "a" 1 "b" 2 } |group-by 'name { } |order-by! 'name 'asc
} spreadsheet { "name" } { "a" "b" }

equal { spreadsheet { "name" "val" } { "a" 1 "b" 6 "a" 5 "b" 10 "a" 7 }
|group-by 'name { 'name count 'val sum 'val min 'val max 'val avg }
|sort-by! 'name 'asc
|order-by! 'name 'asc
} spreadsheet { "name" "name_count" "val_sum" "val_min" "val_max" "val_avg" }
{
"a" 3 13.0 1.0 7.0 4.333333333333333
Expand Down

0 comments on commit 6a8a8e0

Please sign in to comment.