From 77d12364b1acc6fc4a6524ed4fdf85422c81c4f6 Mon Sep 17 00:00:00 2001 From: rokostik Date: Fri, 22 Dec 2023 13:05:56 +0100 Subject: [PATCH] add equal function to object --- env/env.go | 25 +++ env/gen.go | 7 - env/object.go | 396 +++++++++++++++++++++++++++++++++ env/spreadsheet.go | 65 ++++++ evaldo/builtins.go | 22 +- evaldo/builtins_spreadsheet.go | 3 +- tests/structures.rye | 3 +- util/util.go | 14 +- 8 files changed, 501 insertions(+), 34 deletions(-) diff --git a/env/env.go b/env/env.go index bbfaccb7..73caa2e2 100755 --- a/env/env.go +++ b/env/env.go @@ -143,6 +143,31 @@ func (e RyeCtx) GetWords(idxs Idxs) Block { return *NewBlock(*NewTSeries(objs)) } +func (i RyeCtx) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oCtx := o.(RyeCtx) + if len(i.state) != len(oCtx.state) { + return false + } + for k, v := range i.state { + if !v.Equal(oCtx.state[k]) { + return false + } + } + if i.Parent != oCtx.Parent { + return false + } + if i.Kind != oCtx.Kind { + return false + } + if i.locked != oCtx.locked { + return false + } + return false +} + /*func (e *Env) Get(word int) (*Object, bool) { obj, exists := e.state[word] // recursively look at outer Environments ... diff --git a/env/gen.go b/env/gen.go index 5aa8427a..01bbc1f0 100755 --- a/env/gen.go +++ b/env/gen.go @@ -43,10 +43,3 @@ func (e *Gen) Set(kind int, word int, val Object) Object { e.dict[kind][word] = val return val } - -func (i Builtin) GetKind() int { - return int(BuiltinType) -} - -// TODO: Builtin is just temporary ... we need to make something else, that holds natives and user functions. Interface should be the same ... -// would it be better (faster) to have concrete type probably. diff --git a/env/object.go b/env/object.go index 93145803..6ac60d79 100644 --- a/env/object.go +++ b/env/object.go @@ -58,6 +58,7 @@ type Object interface { Probe(e Idxs) string Trace(msg string) GetKind() int + Equal(p Object) bool } // @@ -98,6 +99,13 @@ func (i Integer) GetKind() int { return int(IntegerType) } +func (i Integer) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Value == o.(Integer).Value +} + // // DECIMAL // @@ -136,6 +144,13 @@ func (i Decimal) GetKind() int { return int(DecimalType) } +func (i Decimal) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Value == o.(Decimal).Value +} + // // STRING // @@ -178,6 +193,13 @@ func (i String) GetKind() int { return int(StringType) } +func (i String) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Value == o.(String).Value +} + // // DATE // @@ -212,6 +234,13 @@ func (i Date) GetKind() int { return int(DateType) } +func (i Date) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Value == o.(Date).Value +} + // // URI // @@ -269,6 +298,14 @@ func (i Uri) GetKind() int { return i.Kind.Index } +func (i Uri) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oUri := o.(Uri) + return i.Path == oUri.Path && i.Scheme.Equal(oUri.Scheme) && i.Kind.Equal(oUri.Kind) +} + // // Email // @@ -305,6 +342,13 @@ func (i Email) GetKind() int { return int(EmailType) } +func (i Email) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Address == o.(Email).Address +} + // // BLOCK // @@ -369,6 +413,25 @@ func (i Block) GetKind() int { return int(BlockType) } +func (i Block) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oBlock := o.(Block) + if i.Series.Len() != oBlock.Series.Len() { + return false + } + if i.Mode != oBlock.Mode { + return false + } + for j := 0; j < i.Series.Len(); j += 1 { + if !i.Series.Get(j).Equal(oBlock.Series.Get(j)) { + return false + } + } + return true +} + // // WORD // @@ -407,6 +470,13 @@ func (i Word) GetKind() int { return int(WordType) } +func (i Word) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Index == o.(Word).Index +} + // // SETWORD // @@ -445,6 +515,13 @@ func (i Setword) GetKind() int { return int(SetwordType) } +func (i Setword) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Index == o.(Setword).Index +} + // // LSETWORD // @@ -483,6 +560,13 @@ func (i LSetword) GetKind() int { return int(LSetwordType) } +func (i LSetword) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Index == o.(LSetword).Index +} + // // OPWORD // @@ -526,6 +610,14 @@ func (i Opword) GetKind() int { return int(OpwordType) } +func (i Opword) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oOpword := o.(Opword) + return i.Index == oOpword.Index && i.Force == oOpword.Force +} + // // PIPEWORD // @@ -569,6 +661,14 @@ func (i Pipeword) GetKind() int { return int(PipewordType) } +func (i Pipeword) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oPipeword := o.(Pipeword) + return i.Index == oPipeword.Index && i.Force == oPipeword.Force +} + // // TAGWORD // @@ -611,6 +711,13 @@ func (i Tagword) GetKind() int { return int(TagwordType) } +func (i Tagword) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Index == o.(Tagword).Index +} + // // XWORD // @@ -653,6 +760,13 @@ func (i Xword) GetKind() int { return int(XwordType) } +func (i Xword) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Index == o.(Xword).Index +} + // // EXWORD // @@ -695,6 +809,13 @@ func (i EXword) GetKind() int { return int(EXwordType) } +func (i EXword) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Index == o.(EXword).Index +} + // // KINDWORD // @@ -737,6 +858,13 @@ func (i Kindword) GetKind() int { return int(KindwordType) } +func (i Kindword) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Index == o.(Kindword).Index +} + // // GETWORD // @@ -779,6 +907,13 @@ func (i Getword) GetKind() int { return int(GetwordType) } +func (i Getword) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Index == o.(Getword).Index +} + // // GENWORD // @@ -821,6 +956,13 @@ func (i Genword) GetKind() int { return int(GenwordType) } +func (i Genword) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Index == o.(Genword).Index +} + // // COMMA // @@ -851,6 +993,10 @@ func (i Comma) GetKind() int { return int(CommaType) } +func (i Comma) Equal(o Object) bool { + return i.Type() == o.Type() +} + // // VOID // @@ -881,6 +1027,10 @@ func (i Void) GetKind() int { return int(VoidType) } +func (i Void) Equal(o Object) bool { + return i.Type() == o.Type() +} + // // Function // @@ -964,6 +1114,26 @@ func (i Function) GetKind() int { return int(FunctionType) } +func (i Function) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oFunction := o.(Function) + if i.Argsn != oFunction.Argsn { + return false + } + if !i.Spec.Equal(oFunction.Spec) { + return false + } + if !i.Body.Equal(oFunction.Body) { + return false + } + if i.Pure != oFunction.Pure { + return false + } + return true +} + // // BuiltinFunction // @@ -973,6 +1143,8 @@ func (i Function) GetKind() int { type BuiltinFunction func(ps *ProgramState, arg0 Object, arg1 Object, arg2 Object, arg3 Object, arg4 Object) Object // Builtin represents a builtin function. +// TODO: Builtin is just temporary ... we need to make something else, that holds natives and user functions. Interface should be the same ... +// would it be better (faster) to have concrete type probably. type Builtin struct { Fn BuiltinFunction Argsn int @@ -1021,6 +1193,42 @@ func (i Builtin) Trace(msg string) { fmt.Println(i.Argsn) } +func (i Builtin) GetKind() int { + return int(BuiltinType) +} + +func (i Builtin) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oBuiltin := o.(Builtin) + if i.Argsn != oBuiltin.Argsn { + return false + } + if i.Cur0 != oBuiltin.Cur0 { + return false + } + if i.Cur1 != oBuiltin.Cur1 { + return false + } + if i.Cur2 != oBuiltin.Cur2 { + return false + } + if i.Cur3 != oBuiltin.Cur3 { + return false + } + if i.Cur4 != oBuiltin.Cur4 { + return false + } + if i.AcceptFailure != oBuiltin.AcceptFailure { + return false + } + if i.Pure != oBuiltin.Pure { + return false + } + return true +} + // // Error // @@ -1106,6 +1314,31 @@ func (i Error) GetKind() int { return int(IntegerType) } +func (i Error) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oError := o.(*Error) + if i.Status != oError.Status { + return false + } + if i.Message != oError.Message { + return false + } + if i.Parent != oError.Parent { + return false + } + if len(i.Values) != len(oError.Values) { + return false + } + for k, v := range i.Values { + if !v.Equal(oError.Values[k]) { + return false + } + } + return true +} + // // ARGWORD // @@ -1144,6 +1377,14 @@ func (i Argword) GetKind() int { return int(WordType) } +func (i Argword) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oArgword := o.(Argword) + return i.Name.Equal(oArgword.Name) && i.Kind.Equal(oArgword.Kind) +} + // // CPATH // @@ -1212,6 +1453,26 @@ func NewCPath3(w1 Word, w2 Word, w3 Word) *CPath { return &cp } +func (i CPath) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oCPath := o.(CPath) + if i.Cnt != oCPath.Cnt { + return false + } + if !i.Word1.Equal(oCPath.Word1) { + return false + } + if !i.Word2.Equal(oCPath.Word2) { + return false + } + if !i.Word3.Equal(oCPath.Word3) { + return false + } + return true +} + // // NATIVE // @@ -1252,6 +1513,22 @@ func (i Native) GetKind() int { return i.Kind.Index } +func (i Native) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oNative := o.(Native) + if !i.Kind.Equal(oNative.Kind) { + return false + } + if iValObj, ok := i.Value.(Object); ok { + if oValObj, ok := oNative.Value.(Object); ok { + return iValObj.Equal(oValObj) + } + } + return i.Value == oNative.Value +} + // // Dict -- nonindexed and unboxed map ... for example for params from request etc, so we don't neet to idex keys and it doesn't need boxed values // I think it should have option of having Kind too ... @@ -1332,6 +1609,33 @@ func (i Dict) GetKind() int { return i.Kind.Index } +func (i Dict) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oDict := o.(Dict) + if !i.Kind.Equal(oDict.Kind) { + return false + } + if len(i.Data) != len(oDict.Data) { + return false + } + for k, v := range i.Data { + if vObj, ok := v.(Object); ok { + if oObj, ok := oDict.Data[k].(Object); ok { + if !vObj.Equal(oObj) { + return false + } + } + } else { + if v != oDict.Data[k] { + return false + } + } + } + return true +} + // // List -- nonindexed and unboxed list (block) // @@ -1442,6 +1746,33 @@ func (i List) GetKind() int { return i.Kind.Index } +func (i List) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oList := o.(List) + if !i.Kind.Equal(oList.Kind) { + return false + } + if len(i.Data) != len(oList.Data) { + return false + } + for i, v := range i.Data { + if vObj, ok := v.(Object); ok { + if oObj, ok := oList.Data[i].(Object); ok { + if !vObj.Equal(oObj) { + return false + } + } + } else { + if v != oList.Data[i] { + return false + } + } + } + return true +} + // KIND Type // @@ -1498,6 +1829,28 @@ func (i Kind) HasConverter(from int) bool { } } +func (i Kind) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oKind := o.(Kind) + if !i.Kind.Equal(oKind.Kind) { + return false + } + if !i.Spec.Equal(oKind.Spec) { + return false + } + if len(i.Converters) != len(oKind.Converters) { + return false + } + for k, v := range i.Converters { + if !v.Equal(oKind.Converters[k]) { + return false + } + } + return true +} + // // Converter // @@ -1537,6 +1890,23 @@ func (i Converter) GetKind() int { return int(ConverterType) } +func (i Converter) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oConverter := o.(Converter) + if !i.From.Equal(oConverter.From) { + return false + } + if !i.To.Equal(oConverter.To) { + return false + } + if !i.Spec.Equal(oConverter.Spec) { + return false + } + return true +} + // // TIME // @@ -1574,6 +1944,13 @@ func (i Time) GetKind() int { return int(TimeType) } +func (i Time) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Value.Equal(o.(Time).Value) +} + // // VECTOR TYPE -- feture vector (uses govector) // @@ -1651,3 +2028,22 @@ func (i Vector) Trace(msg string) { func (i Vector) GetKind() int { return int(VectorType) } + +func (i Vector) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + oVector := o.(Vector) + if !i.Kind.Equal(oVector.Kind) { + return false + } + if i.Value.Len() != oVector.Value.Len() { + return false + } + for j := 0; j < i.Value.Len(); j++ { + if i.Value[j] != oVector.Value[j] { + return false + } + } + return true +} diff --git a/env/spreadsheet.go b/env/spreadsheet.go index 2acaf28d..5ae4bf16 100644 --- a/env/spreadsheet.go +++ b/env/spreadsheet.go @@ -256,6 +256,44 @@ func (s Spreadsheet) GetKind() int { return int(SpreadsheetType) } +func (s Spreadsheet) Equal(o Object) bool { + if s.Type() != o.Type() { + return false + } + oSpr := o.(Spreadsheet) + if len(s.Cols) != len(oSpr.Cols) { + return false + } + columnMapping := make(map[int]int, len(s.Cols)) + for i, v := range s.Cols { + idx := slices.Index[[]string](oSpr.Cols, v) + if idx == -1 { + return false + } + columnMapping[i] = idx + } + if len(s.Rows) != len(oSpr.Rows) { + return false + } + for i, row := range s.Rows { + for j, v := range row.Values { + o := oSpr.Rows[i].Values[columnMapping[j]] + if vObj, ok := v.(Object); ok { + if oObj, ok := o.(Object); ok { + if !vObj.Equal(oObj) { + return false + } + } + } else { + if v != o { + return false + } + } + } + } + return true +} + func (s SpreadsheetRow) GetKind() int { return int(0) } @@ -270,6 +308,33 @@ func (s SpreadsheetRow) Probe(e Idxs) string { return s.ToTxt() } +// Do not use when comparing a spreadsheet as a whole +// because column ordering is not guaranteed +func (s SpreadsheetRow) Equal(o Object) bool { + if s.Type() != o.Type() { + return false + } + oSprRow := o.(SpreadsheetRow) + if len(s.Values) != len(oSprRow.Values) { + return false + } + for i, v := range s.Values { + if vObj, ok := v.(Object); ok { + if oObj, ok := oSprRow.Values[i].(Object); ok { + if !vObj.Equal(oObj) { + return false + } + } + } else { + if v != oSprRow.Values[i] { + return false + } + } + } + + return true +} + func (s SpreadsheetRow) ToTxt() string { var bu strings.Builder bu.WriteString("[ ") diff --git a/evaldo/builtins.go b/evaldo/builtins.go index 0ee020ad..4c6da1d7 100644 --- a/evaldo/builtins.go +++ b/evaldo/builtins.go @@ -1123,7 +1123,7 @@ var builtins = map[string]*env.Builtin{ Pure: true, Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { var res int64 - if util.EqualValues(ps, arg0, arg1) { + if arg0.Equal(arg1) { res = 1 } else { res = 0 @@ -1149,7 +1149,7 @@ var builtins = map[string]*env.Builtin{ Doc: "Checks if first argument is greater or equal than the second.", Pure: true, Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { - if util.EqualValues(ps, arg0, arg1) || greaterThan(ps, arg0, arg1) { + if arg0.Equal(arg1) || greaterThan(ps, arg0, arg1) { return *env.NewInteger(1) } else { return *env.NewInteger(0) @@ -1173,7 +1173,7 @@ var builtins = map[string]*env.Builtin{ Doc: "Tests if Arg1 is lesser than Arg 2.", Pure: true, Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { - if util.EqualValues(ps, arg0, arg1) || lesserThan(ps, arg0, arg1) { + if arg0.Equal(arg1) || lesserThan(ps, arg0, arg1) { return *env.NewInteger(1) } else { return *env.NewInteger(0) @@ -3120,7 +3120,7 @@ var builtins = map[string]*env.Builtin{ if ps.ErrorFlag { return ps.Res } - newl = append(newl, ps.Res) + newl[i] = ps.Res ps.Ser.Reset() } @@ -3216,7 +3216,7 @@ var builtins = map[string]*env.Builtin{ if ps.ErrorFlag { return ps.Res } - newl = append(newl, ps.Res) + newl[i] = ps.Res ps.Ser.Reset() } @@ -3547,7 +3547,7 @@ var builtins = map[string]*env.Builtin{ if ps.ErrorFlag { return ps.Res } - if prevres == nil || util.EqualValues(ps, ps.Res, prevres) { + if prevres == nil || ps.Res.Equal(prevres) { subl = append(subl, curval) } else { newl = append(newl, env.NewBlock(*env.NewTSeries(subl))) @@ -3562,7 +3562,7 @@ var builtins = map[string]*env.Builtin{ for i := 0; i < l; i++ { curval := list.Series.Get(i) res := DirectlyCallBuiltin(ps, block, curval, nil) - if prevres == nil || util.EqualValues(ps, res, prevres) { + if prevres == nil || res.Equal(prevres) { subl = append(subl, curval) } else { newl = append(newl, env.NewBlock(*env.NewTSeries(subl))) @@ -3596,7 +3596,7 @@ var builtins = map[string]*env.Builtin{ if ps.ErrorFlag { return ps.Res } - if prevres == nil || util.EqualValues(ps, ps.Res, prevres) { + if prevres == nil || ps.Res.Equal(prevres) { subl = append(subl, curval) } else { newl = append(newl, env.NewList(subl)) @@ -3612,7 +3612,7 @@ var builtins = map[string]*env.Builtin{ curval := list.Data[i] curvalRye := env.ToRyeValue(list.Data[i]) res := DirectlyCallBuiltin(ps, block, curvalRye, nil) - if prevres == nil || util.EqualValues(ps, res, prevres) { + if prevres == nil || res.Equal(prevres) { subl = append(subl, curval) } else { newl = append(newl, env.NewList(subl)) @@ -3643,7 +3643,7 @@ var builtins = map[string]*env.Builtin{ if ps.ErrorFlag { return ps.Res } - if prevres == nil || util.EqualValues(ps, ps.Res, prevres) { + if prevres == nil || ps.Res.Equal(prevres) { subl.WriteRune(curval) } else { newl = append(newl, subl.String()) @@ -3658,7 +3658,7 @@ var builtins = map[string]*env.Builtin{ case env.Builtin: for _, curval := range list.Value { res := DirectlyCallBuiltin(ps, block, env.ToRyeValue(curval), nil) - if prevres == nil || util.EqualValues(ps, res, prevres) { + if prevres == nil || res.Equal(prevres) { subl.WriteRune(curval) } else { newl = append(newl, subl.String()) diff --git a/evaldo/builtins_spreadsheet.go b/evaldo/builtins_spreadsheet.go index bbbbf93e..6ea78d3c 100644 --- a/evaldo/builtins_spreadsheet.go +++ b/evaldo/builtins_spreadsheet.go @@ -11,7 +11,6 @@ import ( "strconv" "github.com/refaktor/rye/env" - "github.com/refaktor/rye/util" ) var Builtins_spreadsheet = map[string]*env.Builtin{ @@ -526,7 +525,7 @@ func WhereEquals(ps *env.ProgramState, s env.Spreadsheet, name string, val any) if len(row.Values) > idx { switch val2 := val.(type) { case env.Object: - if util.EqualValues(ps, val2, env.ToRyeValue(row.Values[idx])) { + if val2.Equal(env.ToRyeValue(row.Values[idx])) { nspr.AddRow(row) } } diff --git a/tests/structures.rye b/tests/structures.rye index 4e89ad85..62512a55 100644 --- a/tests/structures.rye +++ b/tests/structures.rye @@ -555,10 +555,9 @@ section "Spreadsheet related functions" mold\nowrap ?to-spreadsheet { { block } } { - equal { to-spreadsheet vals { dict { "a" 1 b 2 } dict { "a" 4 "b" 5 } } } spreadsheet { "a" "b" } { 1 2 3 4 } + equal { to-spreadsheet vals { dict { "a" 1 b 2 } dict { "a" 3 "b" 4 } } } spreadsheet { "a" "b" } { 1 2 3 4 } 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 _ } } - } diff --git a/util/util.go b/util/util.go index 5651131c..abde6af7 100644 --- a/util/util.go +++ b/util/util.go @@ -16,11 +16,6 @@ func PrintHeader() { fmt.Println("=-===============-===-===-=============-=") // Output: -3 } -// todo -- move to util -func equalValues(ps *env.ProgramState, arg0 env.Object, arg1 env.Object) bool { - return arg0.GetKind() == arg1.GetKind() && arg0.Inspect(*ps.Idx) == arg1.Inspect(*ps.Idx) -} - func IndexOfAt(s, sep string, n int) int { idx := strings.Index(s[n:], sep) if idx > -1 { @@ -31,7 +26,7 @@ func IndexOfAt(s, sep string, n int) int { func IndexOfSlice(ps *env.ProgramState, slice []env.Object, value env.Object) int { for i, v := range slice { - if equalValues(ps, v, value) { + if v.Equal(value) { return i } } @@ -246,18 +241,13 @@ func SplitMulti(s string, seps string) []string { func ContainsVal(ps *env.ProgramState, b []env.Object, val env.Object) bool { for _, a := range b { - if EqualValues(ps, a, val) { + if a.Equal(val) { return true } } return false } -// TODO move to this from various -func EqualValues(ps *env.ProgramState, arg0 env.Object, arg1 env.Object) bool { - return arg0.GetKind() == arg1.GetKind() && arg0.Inspect(*ps.Idx) == arg1.Inspect(*ps.Idx) -} - /* func Transpose(slice []env.Object) []env.Object { yl := len(slice) var xl int