feature/issue-1: built out the core libraries #3

Merged
lmika merged 8 commits from feature/issue-1 into main 2024-09-06 23:38:05 +00:00
3 changed files with 132 additions and 4 deletions
Showing only changes of commit efaf31c869 - Show all commits

View file

@ -102,6 +102,54 @@ func neBuiltin(ctx context.Context, args invocationArgs) (object, error) {
return boolObject(!objectsEqual(l, r)), nil return boolObject(!objectsEqual(l, r)), nil
} }
func ltBuiltin(ctx context.Context, args invocationArgs) (object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
isLess, err := objectsLessThan(args.args[0], args.args[1])
if err != nil {
return nil, err
}
return boolObject(isLess), nil
}
func leBuiltin(ctx context.Context, args invocationArgs) (object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
isLess, err := objectsLessThan(args.args[0], args.args[1])
if err != nil {
return nil, err
}
return boolObject(isLess || objectsEqual(args.args[0], args.args[1])), nil
}
func gtBuiltin(ctx context.Context, args invocationArgs) (object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
isGreater, err := objectsLessThan(args.args[1], args.args[0])
if err != nil {
return nil, err
}
return boolObject(isGreater), nil
}
func geBuiltin(ctx context.Context, args invocationArgs) (object, error) {
if err := args.expectArgn(2); err != nil {
return nil, err
}
isGreater, err := objectsLessThan(args.args[1], args.args[0])
if err != nil {
return nil, err
}
return boolObject(isGreater || objectsEqual(args.args[0], args.args[1])), nil
}
var errObjectsNotEqual = errors.New("objects not equal") var errObjectsNotEqual = errors.New("objects not equal")
func objectsEqual(l, r object) bool { func objectsEqual(l, r object) bool {
@ -169,6 +217,20 @@ func objectsEqual(l, r object) bool {
return false return false
} }
func objectsLessThan(l, r object) (bool, error) {
switch lv := l.(type) {
case strObject:
if rv, ok := r.(strObject); ok {
return lv < rv, nil
}
case intObject:
if rv, ok := r.(intObject); ok {
return lv < rv, nil
}
}
return false, errors.New("objects are not comparable")
}
func concatBuiltin(ctx context.Context, args invocationArgs) (object, error) { func concatBuiltin(ctx context.Context, args invocationArgs) (object, error) {
var sb strings.Builder var sb strings.Builder

View file

@ -62,6 +62,10 @@ func New(opts ...InstOption) *Inst {
rootEC.addCmd("eq", invokableFunc(eqBuiltin)) rootEC.addCmd("eq", invokableFunc(eqBuiltin))
rootEC.addCmd("ne", invokableFunc(neBuiltin)) rootEC.addCmd("ne", invokableFunc(neBuiltin))
rootEC.addCmd("gt", invokableFunc(gtBuiltin))
rootEC.addCmd("ge", invokableFunc(geBuiltin))
rootEC.addCmd("lt", invokableFunc(ltBuiltin))
rootEC.addCmd("le", invokableFunc(leBuiltin))
rootEC.addCmd("add", invokableFunc(addBuiltin)) rootEC.addCmd("add", invokableFunc(addBuiltin))

View file

@ -892,6 +892,66 @@ func TestBuiltins_Reduce(t *testing.T) {
} }
} }
func TestBuiltins_LtLeGtLe(t *testing.T) {
tests := []struct {
desc string
expr string
want bool
wantErr bool
}{
{desc: "str 1 - lt", expr: `lt "hello" "world"`, want: true},
{desc: "str 1 - le", expr: `le "hello" "world"`, want: true},
{desc: "str 1 - gt", expr: `gt "hello" "world"`, want: false},
{desc: "str 1 - ge", expr: `ge "hello" "world"`, want: false},
{desc: "str 2 - lt", expr: `lt "zzzzz" "world"`, want: false},
{desc: "str 2 - le", expr: `le "zzzzz" "world"`, want: false},
{desc: "str 2 - gt", expr: `gt "zzzzz" "world"`, want: true},
{desc: "str 2 - ge", expr: `ge "zzzzz" "world"`, want: true},
{desc: "str 3 - lt", expr: `lt "hello" "hello"`, want: false},
{desc: "str 3 - le", expr: `le "hello" "hello"`, want: true},
{desc: "str 3 - gt", expr: `gt "hello" "hello"`, want: false},
{desc: "str 3 - ge", expr: `ge "hello" "hello"`, want: true},
{desc: "int 1 - lt", expr: `lt 5 8`, want: true},
{desc: "int 1 - le", expr: `le 5 8`, want: true},
{desc: "int 1 - gt", expr: `gt 5 8`, want: false},
{desc: "int 1 - ge", expr: `ge 5 8`, want: false},
{desc: "int 2 - lt", expr: `lt 5 -8`, want: false},
{desc: "int 2 - le", expr: `le 5 -8`, want: false},
{desc: "int 2 - gt", expr: `gt 5 -8`, want: true},
{desc: "int 2 - ge", expr: `ge 5 -8`, want: true},
{desc: "int 3 - lt", expr: `lt 5 5`, want: false},
{desc: "int 3 - le", expr: `le 5 5`, want: true},
{desc: "int 3 - gt", expr: `gt 5 5`, want: false},
{desc: "int 3 - ge", expr: `ge 5 5`, want: true},
{desc: "not comparable 1", expr: `lt () ()`, wantErr: true},
{desc: "not comparable 2", expr: `lt $true $false`, wantErr: true},
{desc: "not comparable 3", expr: `lt [1 2 3] [2 3 4]`, wantErr: true},
{desc: "not comparable 4", expr: `lt ["1":2] ["2":3]`, wantErr: true},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
ctx := context.Background()
outW := bytes.NewBuffer(nil)
inst := New(WithOut(outW), WithTestBuiltin())
inst.SetVar("true", true)
inst.SetVar("false", false)
eqRes, err := inst.Eval(ctx, tt.expr)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.want, eqRes)
}
})
}
}
func TestBuiltins_EqNe(t *testing.T) { func TestBuiltins_EqNe(t *testing.T) {
tests := []struct { tests := []struct {
desc string desc string
@ -909,8 +969,8 @@ func TestBuiltins_EqNe(t *testing.T) {
{desc: "equal lists 3", expr: `eq [] []`, want: true}, {desc: "equal lists 3", expr: `eq [] []`, want: true},
{desc: "equal hashes 1", expr: `eq ["this":1 "that":"thing"] ["that":"thing" "this":1]`, want: true}, {desc: "equal hashes 1", expr: `eq ["this":1 "that":"thing"] ["that":"thing" "this":1]`, want: true},
{desc: "equal hashes 2", expr: `eq ["foo":"bar"] ["foo":"bar"]`, want: true}, {desc: "equal hashes 2", expr: `eq ["foo":"bar"] ["foo":"bar"]`, want: true},
{desc: "equal bools 1", expr: `eq true true`, want: true}, {desc: "equal bools 1", expr: `eq $true $true`, want: true},
{desc: "equal bools 2", expr: `eq false false`, want: true}, {desc: "equal bools 2", expr: `eq $false $false`, want: true},
{desc: "equal nil 1", expr: `eq () ()`, want: true}, {desc: "equal nil 1", expr: `eq () ()`, want: true},
{desc: "equal opaque 1", expr: `eq $hello $hello`, want: true}, {desc: "equal opaque 1", expr: `eq $hello $hello`, want: true},
{desc: "equal opaque 2", expr: `eq $world $world`, want: true}, {desc: "equal opaque 2", expr: `eq $world $world`, want: true},
@ -931,8 +991,8 @@ func TestBuiltins_EqNe(t *testing.T) {
{desc: "not equal types 2", expr: `eq 0 ""`, want: false}, {desc: "not equal types 2", expr: `eq 0 ""`, want: false},
{desc: "not equal types 3", expr: `eq [] [:]`, want: false}, {desc: "not equal types 3", expr: `eq [] [:]`, want: false},
{desc: "not equal types 4", expr: `eq ["23"] "23"`, want: false}, {desc: "not equal types 4", expr: `eq ["23"] "23"`, want: false},
{desc: "not equal types 5", expr: `eq true ()`, want: false}, {desc: "not equal types 5", expr: `eq $true ()`, want: false},
{desc: "not equal types 6", expr: `eq () false`, want: false}, {desc: "not equal types 6", expr: `eq () $false`, want: false},
{desc: "not equal types 7", expr: `eq () "yes"`, want: false}, {desc: "not equal types 7", expr: `eq () "yes"`, want: false},
{desc: "not equal types 8", expr: `eq () $world`, want: false}, {desc: "not equal types 8", expr: `eq () $world`, want: false},
} }
@ -949,6 +1009,8 @@ func TestBuiltins_EqNe(t *testing.T) {
inst := New(WithOut(outW), WithTestBuiltin()) inst := New(WithOut(outW), WithTestBuiltin())
inst.SetVar("hello", Opaque(testProxyObject{v: "hello"})) inst.SetVar("hello", Opaque(testProxyObject{v: "hello"}))
inst.SetVar("world", Opaque(testProxyObject{v: "world"})) inst.SetVar("world", Opaque(testProxyObject{v: "world"}))
inst.SetVar("true", true)
inst.SetVar("false", false)
eqRes, err := inst.Eval(ctx, tt.expr) eqRes, err := inst.Eval(ctx, tt.expr)
assert.NoError(t, err) assert.NoError(t, err)