Skip to content

Commit

Permalink
Merge pull request #465 from refaktor/multiliner
Browse files Browse the repository at this point in the history
added sort\by intersection\by fixed contains, improved tests / reference
  • Loading branch information
refaktor authored Jan 18, 2025
2 parents a04a877 + 03d5fb2 commit fd2dae4
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 117 deletions.
203 changes: 176 additions & 27 deletions evaldo/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,71 @@ func (s RyeListSort) Less(i, j int) bool {
return greaterThanNew(env.ToRyeValue(s[j]), env.ToRyeValue(s[i]))
}

// Custom Sort object interface
type RyeBlockCustomSort struct {
data []env.Object
fn env.Function
ps *env.ProgramState
}

func (s RyeBlockCustomSort) Len() int {
return len(s.data)
}
func (s RyeBlockCustomSort) Swap(i, j int) {
s.data[i], s.data[j] = s.data[j], s.data[i]
}
func (s RyeBlockCustomSort) Less(i, j int) bool {
CallFunctionArgs2(s.fn, s.ps, s.data[i], s.data[j], nil)
return util.IsTruthy(s.ps.Res)
}

// Custom Sort object interface
type RyeListCustomSort struct {
data []any
fn env.Function
ps *env.ProgramState
}

func (s RyeListCustomSort) Len() int {
return len(s.data)
}
func (s RyeListCustomSort) Swap(i, j int) {
s.data[i], s.data[j] = s.data[j], s.data[i]
}
func (s RyeListCustomSort) Less(i, j int) bool {
CallFunctionArgs2(s.fn, s.ps, env.ToRyeValue(s.data[i]), env.ToRyeValue(s.data[j]), nil)
return util.IsTruthy(s.ps.Res)
}

func IntersectStringsCustom(a env.String, b env.String, ps *env.ProgramState, fn env.Function) string {
set := make(map[rune]bool)
var bu strings.Builder
for _, ch := range a.Value {
CallFunctionArgs2(fn, ps, b, *env.NewString(string(ch)), nil)
res := util.IsTruthy(ps.Res)
if res && !set[ch] {
bu.WriteRune(ch)
set[ch] = true
}
}
return bu.String()
}

func IntersectBlocksCustom(a env.Block, b env.Block, ps *env.ProgramState, fn env.Function) []env.Object {
set := make(map[string]bool)
res := make([]env.Object, 0)
for _, v := range a.Series.S {
CallFunctionArgs2(fn, ps, b, v, nil)
r := util.IsTruthy(ps.Res)
strv := v.Inspect(*ps.Idx)
if r && !set[strv] {
res = append(res, v)
set[strv] = true
}
}
return res
}

func LoadScriptLocalFile(ps *env.ProgramState, s1 env.Uri) (env.Object, string) {
var str string
fileIdx, _ := ps.Idx.GetIndex("file")
Expand Down Expand Up @@ -1432,14 +1497,14 @@ var builtins = map[string]*env.Builtin{
//contains block
case env.Block:
switch value := arg1.(type) {
case env.Integer:
case env.Object:
if util.ContainsVal(ps, s1.Series.S, value) {
return *env.NewInteger(1)
} else {
return *env.NewInteger(0)
}
default:
return MakeArgError(ps, 2, []env.Type{env.IntegerType}, "contains")
return MakeArgError(ps, 2, []env.Type{}, "contains")
}
// contains list
case env.List:
Expand Down Expand Up @@ -2779,6 +2844,43 @@ var builtins = map[string]*env.Builtin{
},
},

// Tests:
// equal { sort\by { 6 12 1 } fn { a b } { a < b } } { 1 6 12 }
// equal { sort\by { 6 12 1 } fn { a b } { a > b } } { 12 6 1 }
// equal { sort\by { { x 6 } { x 12 } { x 1 } } fn { a b } { second a |< second b } } { { x 1 } { x 6 } { x 12 } }
"sort\\by": { // TODO -- make sort (not in place) and decide if sort will only work on ref
Argsn: 2,
Doc: "Accepts a block or list and sorts in place in ascending order and returns it.",
Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
switch block := arg0.(type) {
case env.Block:
switch fn := arg1.(type) {
case env.Function:
copied := make([]env.Object, len(block.Series.S))
copy(copied, block.Series.S)
sorter := RyeBlockCustomSort{copied, fn, ps}
sort.Sort(sorter)
return *env.NewBlock(*env.NewTSeries(copied))
default:
return MakeArgError(ps, 1, []env.Type{env.BlockType, env.ListType}, "sort!")
}
case env.List:
switch fn := arg1.(type) {
case env.Function:
copied := make([]any, len(block.Data))
copy(copied, block.Data)
sorter := RyeListCustomSort{copied, fn, ps}
sort.Sort(sorter)
return *env.NewList(copied)
default:
return MakeArgError(ps, 1, []env.Type{env.BlockType, env.ListType}, "sort!")
}
default:
return MakeArgError(ps, 1, []env.Type{env.BlockType, env.ListType}, "sort!")
}
},
},

// Tests:
// equal { list { 3 2 3 5 3 2 } .unique |sort } list { 2 3 5 }
// equal { unique list { 1 1 2 2 3 } |sort } list { 1 2 3 }
Expand Down Expand Up @@ -3232,6 +3334,58 @@ var builtins = map[string]*env.Builtin{
},
},

// Tests:
// equal { intersection\by "foobar" "fbx" fn { a b } { a .contains b } } "fb"
// equal { intersection\by "fooBar" "Fbx" fn { a b } { a .to-lower .contains to-lower b } } "fB"
// equal { intersection\by { "foo" 33 } { 33 33 } fn { a b } { a .contains b } } { 33 }
// equal { intersection\by { "foo" "bar" 33 } { 42 } fn { a b } { map a { .type? } |contains b .type? } } { 33 }
// equal { intersection\by { { "foo" x } { "bar" y } } { { "bar" z } } fn { a b } { map a { .first } |contains first b } } { { "bar" y } }
"intersection\\by": {
Argsn: 3,
Doc: "Finds the intersection of two values by custom function.",
Pure: true,
Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
switch s1 := arg0.(type) {
case env.String:
switch s2 := arg1.(type) {
case env.String:
switch s3 := arg2.(type) {
case env.Function:
inter := IntersectStringsCustom(s1, s2, ps, s3)
return *env.NewString(inter)
default:
return MakeArgError(ps, 2, []env.Type{env.FunctionType}, "intersection\\by")
}
default:
return MakeArgError(ps, 2, []env.Type{env.StringType}, "intersection\\by")
}
case env.Block:
switch b2 := arg1.(type) {
case env.Block:
switch s3 := arg2.(type) {
case env.Function:
inter := IntersectBlocksCustom(s1, b2, ps, s3)
return *env.NewBlock(*env.NewTSeries(inter))
default:
return MakeArgError(ps, 3, []env.Type{env.FunctionType}, "intersection\\by")
}
default:
return MakeArgError(ps, 2, []env.Type{env.BlockType}, "intersection\\by")
}
/* case env.List:
switch l2 := arg1.(type) {
case env.List:
inter := util.IntersectLists(ps, s1, l2)
return *env.NewList(inter)
default:
return MakeArgError(ps, 2, []env.Type{env.ListType}, "intersection")
} */
default:
return MakeArgError(ps, 1, []env.Type{env.StringType, env.BlockType, env.ListType}, "intersection")
}
},
},

// Tests:
// equal { "abcde" .difference "cde" } "ab"
// equal { difference { 1 2 3 4 } { 2 4 } } { 1 3 }
Expand Down Expand Up @@ -4497,32 +4651,8 @@ var builtins = map[string]*env.Builtin{
},
},

/* "table": {
Argsn: 1,
Doc: "Constructs an empty table, accepts a block of column names",
Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
switch bloc := arg0.(type) {
case env.Block:
vv := bloc.Series.Peek()
switch vv.(type) {
case env.String:
cols := make([]string, bloc.Series.Len())
for i := 0; i < bloc.Series.Len(); i++ {
cols[i] = bloc.Series.Get(i).(env.String).Value
}
return *env.NewTable(cols)
case env.Word:
// TODO
}
return nil
}
return nil
},
}, */

//
// ##### Values & Types ##### ""
// ##### Values and Types ##### ""
//
// Tests:
// equal { to-word "test" } 'test
Expand Down Expand Up @@ -6647,6 +6777,8 @@ var builtins = map[string]*env.Builtin{
// equal { produce 5 0 { + 3 } } 15
// equal { produce 3 ">" { + "x>" } } ">x>x>x>"
// equal { produce 3 { } { .concat "x" } } { "x" "x" "x" }
// equal { produce 3 { } { ::x .concat length? x } } { 0 1 2 }
// equal { produce 5 { 2 } { ::acc .last ::x * x |concat* acc } } { 2 4 16 256 65536 4294967296 }
"produce": {
Argsn: 3,
Doc: "Accepts a number, initial value and a block of code. Does the block of code number of times, injecting the initial value or last result.",
Expand Down Expand Up @@ -8373,6 +8505,23 @@ var builtins = map[string]*env.Builtin{
},
},

"message?": { // **
AcceptFailure: true,
Argsn: 1,
Doc: "Returns the status code of the Error.", // TODO -- seems duplicate of status
Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
switch er := arg0.(type) {
case env.Error:
return *env.NewString(er.Message)
case *env.Error:
return *env.NewString(er.Message)
default:
ps.FailureFlag = true
return env.NewError("arg 0 not error")
}
},
},

"disarm": { // **
AcceptFailure: true,
Argsn: 1,
Expand Down
40 changes: 1 addition & 39 deletions evaldo/builtins_io.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"github.com/refaktor/rye/env"

"net/http"
"net/http/cgi"
// "net/http/cgi"

"github.com/jlaffaye/ftp"
)
Expand Down Expand Up @@ -913,44 +913,6 @@ var Builtins_io = map[string]*env.Builtin{
},
},

"serve-cgi": {
Argsn: 3,
Doc: "Serve CGI.",
Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
switch rword := arg0.(type) {
case env.Word:
switch wword := arg1.(type) {
case env.Word:
switch bloc := arg2.(type) {
case env.Block:
var rctx *env.RyeCtx
if err := cgi.Serve(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ser := ps.Ser
ctx := ps.Ctx
ps.Ser = bloc.Series
ps.Ctx = env.NewEnv(ps.Ctx) // make new context with no parent
ps.Ctx.Set(rword.Index, *env.NewNative(ps.Idx, w, "Go-server-response-writer"))
ps.Ctx.Set(wword.Index, *env.NewNative(ps.Idx, r, "Go-server-request"))
EvalBlock(ps)
rctx = ps.Ctx
ps.Ctx = ctx
ps.Ser = ser
})); err != nil {
return MakeBuiltinError(ps, err.Error(), "serve-cgi")
}
return *rctx
default:
return MakeArgError(ps, 3, []env.Type{env.BlockType}, "serve-cgi")
}
default:
return MakeArgError(ps, 2, []env.Type{env.WordType}, "serve-cgi")
}
default:
return MakeArgError(ps, 1, []env.Type{env.WordType}, "serve-cgi")
}
},
},

"ftp-schema//open": {
Argsn: 1,
Doc: "Open connection to FTP Server",
Expand Down
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ require (
github.com/thomasberger/parsemail v1.2.7
github.com/xuri/excelize/v2 v2.9.0
go.mongodb.org/mongo-driver v1.17.1
golang.org/x/crypto v0.31.0
golang.org/x/net v0.33.0
golang.org/x/crypto v0.32.0
golang.org/x/net v0.34.0
golang.org/x/sync v0.10.0
golang.org/x/term v0.27.0
golang.org/x/term v0.28.0
golang.org/x/text v0.21.0
)

Expand Down Expand Up @@ -119,7 +119,8 @@ require (
github.com/yhirose/go-peg v0.0.0-20210804202551-de25d6753cf1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.etcd.io/bbolt v1.3.7 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/tools v0.29.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
Expand Down
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,12 @@ go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHy
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
goji.io v2.0.2+incompatible h1:uIssv/elbKRLznFUy3Xj4+2Mz/qKhek/9aZQDUMae7c=
goji.io v2.0.2+incompatible/go.mod h1:sbqFwrtqZACxLBTQcdgVjFh54yGVCvwq8+w49MVMMIk=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -266,15 +266,15 @@ golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
Expand Down
Loading

0 comments on commit fd2dae4

Please sign in to comment.