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
4 changed files with 133 additions and 3 deletions
Showing only changes of commit 484631782e - Show all commits

View file

@ -231,6 +231,46 @@ func objectsLessThan(l, r object) (bool, error) {
return false, errors.New("objects are not comparable")
}
func strBuiltin(ctx context.Context, args invocationArgs) (object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
}
if args.args[0] == nil {
return strObject(""), nil
}
return strObject(args.args[0].String()), nil
}
func intBuiltin(ctx context.Context, args invocationArgs) (object, error) {
if err := args.expectArgn(1); err != nil {
return nil, err
}
if args.args[0] == nil {
return intObject(0), nil
}
switch v := args.args[0].(type) {
case intObject:
return v, nil
case strObject:
i, err := strconv.Atoi(string(v))
if err != nil {
return nil, errors.New("cannot convert to int")
}
return intObject(i), nil
case boolObject:
if v {
return intObject(1), nil
}
return intObject(0), nil
}
return nil, errors.New("cannot convert to int")
}
func concatBuiltin(ctx context.Context, args invocationArgs) (object, error) {
var sb strings.Builder

View file

@ -67,6 +67,9 @@ func New(opts ...InstOption) *Inst {
rootEC.addCmd("lt", invokableFunc(ltBuiltin))
rootEC.addCmd("le", invokableFunc(leBuiltin))
rootEC.addCmd("str", invokableFunc(strBuiltin))
rootEC.addCmd("int", invokableFunc(intBuiltin))
rootEC.addCmd("add", invokableFunc(addBuiltin))
rootEC.addCmd("cat", invokableFunc(concatBuiltin))

View file

@ -6,6 +6,7 @@ import (
"fmt"
"reflect"
"strconv"
"strings"
"github.com/lmika/gopkgs/fp/slices"
)
@ -51,7 +52,22 @@ func (s listObject) Index(i int) object {
type hashObject map[string]object
func (s hashObject) String() string {
return fmt.Sprintf("%v", map[string]object(s))
if len(s) == 0 {
return "[:]"
}
sb := strings.Builder{}
sb.WriteString("[")
for k, v := range s {
if sb.Len() != 1 {
sb.WriteString(" ")
}
sb.WriteString(k)
sb.WriteString(":")
sb.WriteString(v.String())
}
sb.WriteString("]")
return sb.String()
}
func (s hashObject) Truthy() bool {
@ -99,9 +115,9 @@ type boolObject bool
func (b boolObject) String() string {
if b {
return "(true)"
return "true"
}
return "(false))"
return "false"
}
func (b boolObject) Truthy() bool {

View file

@ -1022,3 +1022,74 @@ func TestBuiltins_EqNe(t *testing.T) {
})
}
}
func TestBuiltins_Str(t *testing.T) {
tests := []struct {
desc string
expr string
want string
}{
{desc: "str", expr: `str "hello"`, want: "hello"},
{desc: "int", expr: `str 123`, want: "123"},
{desc: "bool 1", expr: `str (eq 1 1)`, want: "true"},
{desc: "bool 2", expr: `str (eq 1 0)`, want: "false"},
{desc: "list 1", expr: `str [1 2 3]`, want: "[1 2 3]"},
{desc: "list 2", expr: `str []`, want: "[]"},
{desc: "dict 1", expr: `str ["hello":"world"]`, want: `[hello:world]`},
{desc: "dict 2", expr: `str [:]`, want: "[:]"},
{desc: "nil", expr: `str ()`, want: ""},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
ctx := context.Background()
outW := bytes.NewBuffer(nil)
inst := New(WithOut(outW), WithTestBuiltin())
eqRes, err := inst.Eval(ctx, tt.expr)
assert.NoError(t, err)
assert.Equal(t, tt.want, eqRes)
})
}
}
func TestBuiltins_Int(t *testing.T) {
tests := []struct {
desc string
expr string
want int
wantErr bool
}{
{desc: "str 1", expr: `int "123"`, want: 123},
{desc: "str 2", expr: `int "31452"`, want: 31452},
{desc: "str 3", expr: `int "-21"`, want: -21},
{desc: "int 1", expr: `int 123`, want: 123},
{desc: "int 2", expr: `int -21`, want: -21},
{desc: "bool 1", expr: `int (eq 1 1)`, want: 1},
{desc: "bool 2", expr: `int (eq 1 0)`, want: 0},
{desc: "nil", expr: `int ()`, want: 0},
{desc: "list 1", expr: `int [1 2 3]`, wantErr: true},
{desc: "list 2", expr: `int []`, wantErr: true},
{desc: "dict 1", expr: `int ["hello":"world"]`, wantErr: true},
{desc: "dict 2", expr: `int [:]`, 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())
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)
}
})
}
}