Skip to content

Add formatters for built-in values #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions builtin/expr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func TestDefExpr_Eval(t *testing.T) {
return DefExpr{Name: "foo"}, core.New(nil)
},
want: Symbol("foo"),
assert: func(t *testing.T, got core.Any, err error, env core.Env) {
assert: func(t *testing.T, got core.Any, _ error, env core.Env) {
v, err := env.Resolve("foo")
assert.NoError(t, err)
assert.Equal(t, Nil{}, v)
Expand All @@ -116,7 +116,7 @@ func TestDefExpr_Eval(t *testing.T) {
}, core.New(nil)
},
want: Symbol("foo"),
assert: func(t *testing.T, got core.Any, err error, env core.Env) {
assert: func(t *testing.T, got core.Any, _ error, env core.Env) {
v, err := env.Resolve("foo")
assert.NoError(t, err)
assert.Equal(t, 10, v)
Expand Down
12 changes: 6 additions & 6 deletions builtin/fn.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,12 @@ func (f Func) matchArity(args []core.Any) bool {
return argc == len(f.Params)
}

func (f Func) minArity() int {
if len(f.Params) > 0 && f.Variadic {
return len(f.Params) - 1
}
return len(f.Params)
}
// func (f Func) minArity() int {
// if len(f.Params) > 0 && f.Variadic {
// return len(f.Params) - 1
// }
// return len(f.Params)
// }

func (f *Func) compare(other Func) (bool, error) {
if f.Variadic != other.Variadic ||
Expand Down
8 changes: 0 additions & 8 deletions builtin/seq.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,6 @@ type LinkedList struct {
rest core.Seq
}

// SExpr returns a valid s-expression for LinkedList.
func (ll *LinkedList) SExpr() (string, error) {
if ll == nil {
return "()", nil
}
return core.SeqString(ll, "(", ")", " ")
}

// Conj returns a new list with all the items added at the head of the list.
func (ll *LinkedList) Conj(items ...core.Any) (res core.Seq, err error) {
res = ll
Expand Down
74 changes: 45 additions & 29 deletions builtin/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ var (
// Nil represents the Value 'nil'.
type Nil struct{}

// SExpr returns a valid s-expression representing Nil.
func (Nil) SExpr() (string, error) { return "nil", nil }

// Equals returns true IFF other is nil.
func (Nil) Equals(other core.Any) (bool, error) { return IsNil(other), nil }

Expand All @@ -44,9 +41,6 @@ func (Nil) String() string { return "nil" }
// Int64 represents a 64-bit integer Value.
type Int64 int64

// SExpr returns a valid s-expression representing Int64.
func (i64 Int64) SExpr() (string, error) { return i64.String(), nil }

// Comp performs comparison against another Int64.
func (i64 Int64) Comp(other core.Any) (int, error) {
if n, ok := other.(Int64); ok {
Expand All @@ -65,12 +59,24 @@ func (i64 Int64) Comp(other core.Any) (int, error) {

func (i64 Int64) String() string { return strconv.Itoa(int(i64)) }

func (i64 Int64) Format(s fmt.State, verb rune) {
switch verb {
case 'd':
fmt.Fprint(s, i64.String())

default:
if s.Flag('#') {
fmt.Fprintf(s, "%#v", int64(i64))
return
}

fmt.Fprintf(s, "%v", int64(i64))
}
}

// Float64 represents a 64-bit double precision floating point Value.
type Float64 float64

// SExpr returns a valid s-expression representing Float64.
func (f64 Float64) SExpr() (string, error) { return f64.String(), nil }

// Comp performs comparison against another Float64.
func (f64 Float64) Comp(other core.Any) (int, error) {
if n, ok := other.(Float64); ok {
Expand All @@ -88,18 +94,35 @@ func (f64 Float64) Comp(other core.Any) (int, error) {
}

func (f64 Float64) String() string {
if f64 == 0 {
return "0."
}

if math.Abs(float64(f64)) >= 1e16 {
return fmt.Sprintf("%e", f64)
return fmt.Sprintf("%e", float64(f64))
}

return fmt.Sprintf("%f", float64(f64))
}

func (f64 Float64) Format(s fmt.State, verb rune) {
switch verb {
case 'f':
fmt.Fprint(s, f64.String())

default:
if s.Flag('#') {
fmt.Fprintf(s, "%#v", float64(f64))
return
}

fmt.Fprintf(s, "%v", float64(f64))
}
return fmt.Sprintf("%f", f64)
}

// Bool represents a boolean Value.
type Bool bool

// SExpr returns a valid s-expression representing Bool.
func (b Bool) SExpr() (string, error) { return b.String(), nil }

// Equals returns true if 'other' is a boolean and has same logical Value.
func (b Bool) Equals(other core.Any) (bool, error) {
val, ok := other.(Bool)
Expand All @@ -116,11 +139,6 @@ func (b Bool) String() string {
// Char represents a Unicode character.
type Char rune

// SExpr returns a valid s-expression representing Char.
func (char Char) SExpr() (string, error) {
return fmt.Sprintf("\\%c", char), nil
}

// Equals returns true if the other Value is also a character and has same Value.
func (char Char) Equals(other core.Any) (bool, error) {
val, isChar := other.(Char)
Expand All @@ -132,23 +150,24 @@ func (char Char) String() string { return fmt.Sprintf("\\%c", char) }
// String represents a string of characters.
type String string

// SExpr returns a valid s-expression representing String.
func (str String) SExpr() (string, error) { return str.String(), nil }

// Equals returns true if 'other' is string and has same Value.
func (str String) Equals(other core.Any) (bool, error) {
otherStr, isStr := other.(String)
return isStr && (otherStr == str), nil
}

func (str String) String() string { return fmt.Sprintf("\"%s\"", string(str)) }
func (str String) Format(s fmt.State, verb rune) {
if verb == 's' && s.Flag('#') {
fmt.Fprintf(s, "%s", string(str))
return
}

fmt.Fprintf(s, "%#v", string(str))
}

// Symbol represents a lisp symbol Value.
type Symbol string

// SExpr returns a valid s-expression representing Symbol.
func (sym Symbol) SExpr() (string, error) { return string(sym), nil }

// Equals returns true if the other Value is also a symbol and has same Value.
func (sym Symbol) Equals(other core.Any) (bool, error) {
otherSym, isSym := other.(Symbol)
Expand All @@ -160,9 +179,6 @@ func (sym Symbol) String() string { return string(sym) }
// Keyword represents a keyword Value.
type Keyword string

// SExpr returns a valid s-expression representing Keyword.
func (kw Keyword) SExpr() (string, error) { return kw.String(), nil }

// Equals returns true if the other Value is keyword and has same Value.
func (kw Keyword) Equals(other core.Any) (bool, error) {
otherKW, isKeyword := other.(Keyword)
Expand Down
122 changes: 102 additions & 20 deletions builtin/value_test.go
Original file line number Diff line number Diff line change
@@ -1,52 +1,134 @@
package builtin
package builtin_test

import (
"errors"
"fmt"
"reflect"
"testing"

"github.com/spy16/slurp/builtin"
"github.com/spy16/slurp/core"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestNil(t *testing.T) {
n := Nil{}
n := builtin.Nil{}
assert.Equal(t, "nil", n.String())
testSExpr(t, n, "nil")
assertEq(t, builtin.Nil{}, n)
}

func TestInt64(t *testing.T) {
v := Int64(100)
v := builtin.Int64(100)
assert.Equal(t, "100", v.String())
testSExpr(t, v, "100")
assertEq(t, builtin.Int64(100), v)
testComp(t, v, 10, 0, core.ErrIncomparable)
testComp(t, v, v, 0, nil)
testComp(t, v, Int64(1), 1, nil)
testComp(t, v, Int64(10000), -1, nil)
testComp(t, v, builtin.Int64(1), 1, nil)
testComp(t, v, builtin.Int64(10000), -1, nil)
}

func TestFloat64(t *testing.T) {
assert.Equal(t, "1.000000e+19", Float64(1e19).String())
assert.Equal(t, "1.000000e+19", builtin.Float64(1e19).String())

v := Float64(100)
v := builtin.Float64(100)
assert.Equal(t, "100.000000", v.String())
testSExpr(t, v, "100.000000")
assertEq(t, builtin.Float64(100), v)
testComp(t, v, 10, 0, core.ErrIncomparable)
testComp(t, v, v, 0, nil)
testComp(t, v, Float64(1), 1, nil)
testComp(t, v, Float64(10000), -1, nil)
testComp(t, v, builtin.Float64(1), 1, nil)
testComp(t, v, builtin.Float64(10000), -1, nil)
}

func TestIsTruthy(t *testing.T) {
assert.True(t, IsTruthy(true))
assert.True(t, IsTruthy(10))
assert.False(t, IsTruthy(nil))
assert.False(t, IsTruthy(false))
assert.True(t, builtin.IsTruthy(true))
assert.True(t, builtin.IsTruthy(10))
assert.False(t, builtin.IsTruthy(nil))
assert.False(t, builtin.IsTruthy(false))
}

func testSExpr(t *testing.T, v core.SExpressable, want string) {
s, err := v.SExpr()
assert.NoError(t, err)
assert.Equal(t, want, s)
func TestFormat(t *testing.T) {
t.Parallel()

for _, tt := range []struct {
any core.Any
pretty, sxpr string
}{
{
any: builtin.Nil{},
pretty: "nil",
sxpr: "nil",
},
{
any: builtin.Int64(0),
pretty: "0",
sxpr: "0",
},
{
any: builtin.Float64(0),
pretty: "0",
sxpr: "0",
},
{
any: builtin.Float64(1e16),
pretty: "1e+16",
sxpr: "1e+16",
},
{
any: builtin.Float64(-.2),
pretty: "-0.2",
sxpr: "-0.2",
},
{
any: builtin.Bool(false),
pretty: "false",
sxpr: "false",
},
{
any: builtin.Bool(true),
pretty: "true",
sxpr: "true",
},
{
any: builtin.Char('🧠'),
pretty: "\\🧠",
sxpr: "\\🧠",
},
{
any: builtin.String("foo"),
pretty: "foo",
sxpr: "\"foo\"",
},
{
any: builtin.Symbol("foo"),
pretty: "foo",
sxpr: "foo",
},
{
any: builtin.NewList(builtin.Keyword("foo")),
pretty: "(:foo)",
sxpr: "(:foo)",
},
{
any: builtin.NewVector(builtin.Keyword("foo")),
pretty: "[:foo]",
sxpr: "[:foo]",
},
} {
t.Run(reflect.TypeOf(tt.any).String(), func(t *testing.T) {
assert.Equal(t, tt.sxpr, fmt.Sprintf("%s", tt.any),
"invalid symbolic expression")

assert.Equal(t, tt.pretty, fmt.Sprintf("%#s", tt.any),
"invalid pretty-print output")
})
}
}

func assertEq(t *testing.T, want, got core.Any, msgAndArgs ...interface{}) {
ok, err := core.Eq(want, got)
require.NoError(t, err, msgAndArgs...)
require.True(t, ok, msgAndArgs...)
}

func testComp(t *testing.T, v core.Comparable, other core.Any, want int, wantErr error) {
Expand Down
13 changes: 0 additions & 13 deletions builtin/vector.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,6 @@ func (v PersistentVector) Transient() *TransientVector {
// Count returns the number of elements contained in the Vector.
func (v PersistentVector) Count() (int, error) { return v.cnt, nil }

// SExpr returns a parsable s-expression for the Vector.
func (v PersistentVector) SExpr() (string, error) {
if v.cnt == 0 {
return "[]", nil
}

seq, _ := v.Seq()
return core.SeqString(seq, "[", "]", " ")
}

func (v PersistentVector) tailoff() int {
if v.cnt < width {
return 0
Expand Down Expand Up @@ -430,9 +420,6 @@ func (t TransientVector) Persistent() PersistentVector { return PersistentVector

func (t TransientVector) tailoff() int { return PersistentVector(t).tailoff() }

// SExpr returns an s-expression for the vector.
func (t TransientVector) SExpr() (string, error) { return PersistentVector(t).SExpr() }

// Count the number of elements in the vector.
func (t TransientVector) Count() (int, error) { return t.cnt, nil }

Expand Down
Loading