Compare commits
26 Commits
645fd64752
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0324724fb7 | |||
| 7e5a65e281 | |||
| ca2485b725 | |||
| f816fdc912 | |||
| 4ed27b8c0e | |||
| 656bd7dc27 | |||
| e906b007de | |||
| 465c959414 | |||
| ba00edc4db | |||
| a6f8502fbb | |||
| bfb8a2b0d8 | |||
| d573528f3b | |||
| 0c9e3aa20b | |||
| 72da3ec820 | |||
| df56597332 | |||
| 4deba4be78 | |||
| b7b7f79559 | |||
| 6155074b77 | |||
| 8dd6fd958d | |||
| 3b4c206003 | |||
| cfbddb3f39 | |||
| f5f7691de0 | |||
| b9b88d8fa0 | |||
| 5ac5acd6d1 | |||
| 413ab23904 | |||
| c044f452b5 |
@@ -1,2 +1,3 @@
|
|||||||
tmp
|
tmp
|
||||||
.air.toml
|
.air.toml
|
||||||
|
*/testdata/*
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ require (
|
|||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/stretchr/testify v1.11.1 // indirect
|
github.com/stretchr/testify v1.11.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
pgregory.net/rapid v1.2.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,3 +7,5 @@ github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD
|
|||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
||||||
|
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||||
|
|||||||
@@ -49,8 +49,13 @@ func (this *HashMap) Conj(key any, val any) *HashMap {
|
|||||||
return Conj(this, key, val)
|
return Conj(this, key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
func String(this *HashMap) string {
|
func (this *HashMap) IsEmpty() bool {
|
||||||
if this == nil {
|
return len(this._map) == 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HashMap) String() string {
|
||||||
|
if this.IsEmpty() {
|
||||||
return "{}"
|
return "{}"
|
||||||
}
|
}
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
@@ -63,8 +68,5 @@ func String(this *HashMap) string {
|
|||||||
sb.WriteRune(' ')
|
sb.WriteRune(' ')
|
||||||
}
|
}
|
||||||
return sb.String()[:sb.Len()-2] + "}"
|
return sb.String()[:sb.Len()-2] + "}"
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HashMap) String() string {
|
|
||||||
return String(this)
|
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-50
@@ -5,13 +5,21 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ICollection interface {
|
||||||
type IList interface {
|
|
||||||
Conj(data any) IList
|
Conj(data any) IList
|
||||||
|
IsEmpty() bool
|
||||||
|
String() string
|
||||||
|
Len() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ISeq interface {
|
||||||
First() any
|
First() any
|
||||||
Rest() IList
|
Rest() IList
|
||||||
String() string
|
}
|
||||||
IsEmpty() bool
|
|
||||||
|
type IList interface {
|
||||||
|
ISeq
|
||||||
|
ICollection
|
||||||
}
|
}
|
||||||
|
|
||||||
type EmptyList struct{}
|
type EmptyList struct{}
|
||||||
@@ -27,43 +35,29 @@ func Empty() IList {
|
|||||||
|
|
||||||
var emptyList = Empty()
|
var emptyList = Empty()
|
||||||
|
|
||||||
func New(val any) *List {
|
func New(val any) IList {
|
||||||
this := new(List)
|
this := new(List)
|
||||||
this.Value = val
|
this.Value = val
|
||||||
this.next = emptyList
|
this.next = emptyList
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *List) Conj(val any) IList {
|
func Cons(val any, rest IList) IList {
|
||||||
new_head := New(val)
|
this := new(List)
|
||||||
new_head.next = this
|
this.Value = val
|
||||||
return new_head
|
this.next = rest
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *List) Conj(val any) IList {
|
||||||
|
return Cons(val, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *EmptyList) Conj(val any) IList {
|
func (this *EmptyList) Conj(val any) IList {
|
||||||
return Conj(nil, val)
|
return New(val)
|
||||||
}
|
|
||||||
|
|
||||||
func Conj(this IList, val any) IList {
|
|
||||||
if this == nil {
|
|
||||||
return New(val)
|
|
||||||
} else {
|
|
||||||
return this.Conj(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Rest(this IList) IList {
|
|
||||||
if this == nil {
|
|
||||||
return emptyList
|
|
||||||
}
|
|
||||||
return this.Rest()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *List) Rest() IList {
|
func (this *List) Rest() IList {
|
||||||
if this == nil {
|
|
||||||
return emptyList
|
|
||||||
}
|
|
||||||
return this.next
|
return this.next
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,25 +65,14 @@ func (this *EmptyList) Rest() IList {
|
|||||||
return emptyList
|
return emptyList
|
||||||
}
|
}
|
||||||
|
|
||||||
func First(this *List) any {
|
|
||||||
if this == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return this.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *List) First() any {
|
func (this *List) First() any {
|
||||||
return First(this)
|
return this.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *EmptyList) First() any {
|
func (this *EmptyList) First() any {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsEmpty(this IList) bool {
|
|
||||||
return this == nil || this.IsEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *List) IsEmpty() bool {
|
func (this *List) IsEmpty() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -98,26 +81,26 @@ func (this *EmptyList) IsEmpty() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func String(this *List) string {
|
func (this *List) String() string {
|
||||||
if IsEmpty(this) {
|
|
||||||
return "()"
|
|
||||||
}
|
|
||||||
|
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
sb.WriteRune('(')
|
sb.WriteRune('(')
|
||||||
// Iterate and print elements
|
// Iterate and print elements
|
||||||
var e IList
|
var e IList
|
||||||
for e = this; !IsEmpty(e); e = Rest(e) {
|
for e = this; !e.IsEmpty(); e = e.Rest() {
|
||||||
sb.WriteString(fmt.Sprint(e.First()))
|
sb.WriteString(fmt.Sprint(e.First()))
|
||||||
sb.WriteRune(' ')
|
sb.WriteRune(' ')
|
||||||
}
|
}
|
||||||
return sb.String()[:sb.Len()-1] + ")"
|
return sb.String()[:sb.Len()-1] + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *List) String() string {
|
|
||||||
return String(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *EmptyList) String() string {
|
func (this *EmptyList) String() string {
|
||||||
return "()"
|
return "()"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *EmptyList) Len() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *List) Len() int {
|
||||||
|
return 1 + this.Rest().Len()
|
||||||
|
}
|
||||||
|
|||||||
+78
-21
@@ -1,34 +1,91 @@
|
|||||||
package list
|
package list
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"pgregory.net/rapid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReadString(t *testing.T) {
|
// intListGen returns a generator for either empty list or random ints
|
||||||
assert.Equal(t, "()", Empty().String(), "should insert at head")
|
func intListGen() *rapid.Generator[IList] {
|
||||||
|
return rapid.OneOf(rapid.Custom(func(t *rapid.T) IList {
|
||||||
|
myList := Empty()
|
||||||
|
t.Repeat(map[string]func(*rapid.T){
|
||||||
|
"conj": func(t *rapid.T) {
|
||||||
|
myList = myList.Conj(rapid.Int().Draw(t, "el"))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return myList
|
||||||
|
}), rapid.Just(Empty()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadConj(t *testing.T) {
|
// descSortedIntListGen returns a generator for list of at least several descending ints
|
||||||
var l IList
|
func descSortedIntListGen() *rapid.Generator[IList] {
|
||||||
assert.Equal(t, "()", Empty().String(), "should insert at head")
|
return rapid.Custom(func(t *rapid.T) IList {
|
||||||
l = New(5)
|
myList := New(0)
|
||||||
assert.Equal(t, "(5)", l.String(), "should insert at head")
|
i := 1
|
||||||
l = Empty().Conj(4)
|
t.Repeat(map[string]func(*rapid.T){
|
||||||
assert.Equal(t, "(4)", l.String(), "should insert at head")
|
"conj": func(t *rapid.T) {
|
||||||
|
myList = myList.Conj(i)
|
||||||
|
i += 1
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return myList
|
||||||
|
}).Filter(func(l IList) bool {
|
||||||
|
return l.Len() > 1
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadFirst(t *testing.T) {
|
func TestFirstIsNilOnlyWhenEmpty(t *testing.T) {
|
||||||
l := Empty().Conj(5).Conj(6).Conj(7)
|
assert.Equal(t, 1, New(3).Conj(2).Conj(1).First())
|
||||||
assert.Equal(t, 7, l.First(), "should return first element")
|
|
||||||
assert.Equal(t, nil, Empty().First(), "should return nil")
|
rapid.Check(t, func(t *rapid.T) {
|
||||||
|
myList := intListGen().Draw(t, "myList")
|
||||||
|
assert.Equal(t, myList.IsEmpty(), myList.First() == nil, "first is nil only when list is empty")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadRest(t *testing.T) {
|
func TestConj(t *testing.T) {
|
||||||
l := Empty().Conj(5).Conj(6).Conj(7)
|
rapid.Check(t, func(t *rapid.T) {
|
||||||
assert.Equal(t, "(6 5)", l.Rest().String(), "should return rest sublist")
|
myList := intListGen().Draw(t, "myList")
|
||||||
assert.Equal(t, "(5)", l.Rest().Rest().String(), "should return rest sublist")
|
newElem := rapid.Int().Draw(t, "newElem")
|
||||||
assert.Equal(t, "()", l.Rest().Rest().Rest().String(), "should return rest sublist")
|
myList2 := myList.Conj(newElem)
|
||||||
assert.Equal(t, Empty(), Empty().Rest(), "should return rest sublist")
|
assert.Equal(t, myList.Len()+1, myList2.Len(), "conj increases length")
|
||||||
assert.Equal(t, Empty(), Rest(Empty().Rest()), "should return rest sublist")
|
assert.Equal(t, newElem, myList2.First(), "first after conj returns inserted number")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRest(t *testing.T) {
|
||||||
|
rapid.Check(t, func(t *rapid.T) {
|
||||||
|
myList := intListGen().Draw(t, "myList")
|
||||||
|
if myList.IsEmpty() {
|
||||||
|
assert.Equal(t, myList.Len(), myList.Rest().Len(), "rest does not change length for empty list")
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, myList.Len()-1, myList.Rest().Len(), "rest decreases length of non-empty list")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
rapid.Check(t, func(t *rapid.T) {
|
||||||
|
myList := descSortedIntListGen().Draw(t, "myList")
|
||||||
|
assert.Greater(t, myList.First(), myList.Rest().First(), "rest decreases value in list head")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringifyIntList(t *testing.T) {
|
||||||
|
assert.Equal(t, "(1 2 3)", New(3).Conj(2).Conj(1).String())
|
||||||
|
|
||||||
|
rapid.Check(t, func(t *rapid.T) {
|
||||||
|
myList := intListGen().Draw(t, "myList")
|
||||||
|
s := myList.String()
|
||||||
|
r := regexp.MustCompile(`^\([\d\s-]*\)$`)
|
||||||
|
assert.True(t, r.Match([]byte(s)), s+" looks like a stringified list")
|
||||||
|
if !myList.IsEmpty() {
|
||||||
|
assert.Equal(t, myList.Len(), strings.Count(s, " ")+1, "number of spaces in string should match count of elements")
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, 0, strings.Count(s, " "), "no spaces in string for empty list")
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -1,6 +1,7 @@
|
|||||||
package read
|
package read
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"mal-go/hash_map"
|
"mal-go/hash_map"
|
||||||
"mal-go/symbol"
|
"mal-go/symbol"
|
||||||
"mal-go/vector"
|
"mal-go/vector"
|
||||||
@@ -47,6 +48,8 @@ func readForm(s string, pos int, ns string) (any, any, int) {
|
|||||||
res, err, pos = readVector(s, pos, ns)
|
res, err, pos = readVector(s, pos, ns)
|
||||||
case '(':
|
case '(':
|
||||||
res, err, pos = readList(s, pos, ns)
|
res, err, pos = readList(s, pos, ns)
|
||||||
|
default:
|
||||||
|
return nil, "unexpected char: '" + fmt.Sprint(c) + "'", pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res, err, pos
|
return res, err, pos
|
||||||
@@ -54,7 +57,10 @@ func readForm(s string, pos int, ns string) (any, any, int) {
|
|||||||
|
|
||||||
func readMap(s string, pos int, ns string) (any, any, int) {
|
func readMap(s string, pos int, ns string) (any, any, int) {
|
||||||
m := hash_map.New()
|
m := hash_map.New()
|
||||||
return m, "unimplemented", pos
|
if s[pos+1] == '}' {
|
||||||
|
return m, nil, pos + 2
|
||||||
|
}
|
||||||
|
return nil, "unimplemented", pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func readList(s string, pos int, ns string) (any, any, int) {
|
func readList(s string, pos int, ns string) (any, any, int) {
|
||||||
|
|||||||
+36
-1
@@ -1,10 +1,15 @@
|
|||||||
package read
|
package read
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
"fmt"
|
||||||
|
"mal-go/hash_map"
|
||||||
"mal-go/list"
|
"mal-go/list"
|
||||||
"mal-go/symbol"
|
"mal-go/symbol"
|
||||||
|
"mal-go/vector"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"pgregory.net/rapid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestReadInt(t *testing.T) {
|
func TestReadInt(t *testing.T) {
|
||||||
@@ -47,3 +52,33 @@ func TestReadList(t *testing.T) {
|
|||||||
assert.Equal(t, nil, err, "should read without error")
|
assert.Equal(t, nil, err, "should read without error")
|
||||||
assert.Equal(t, len(input), pos, "should read the whole string")
|
assert.Equal(t, len(input), pos, "should read the whole string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StringifiedFormGen() *rapid.Generator[string] {
|
||||||
|
return rapid.OneOf(rapid.Custom(func(t *rapid.T) string {
|
||||||
|
myList := list.Empty()
|
||||||
|
i := 0
|
||||||
|
t.Repeat(map[string]func(*rapid.T){
|
||||||
|
"conj": func(t *rapid.T) {
|
||||||
|
if i < 100 {
|
||||||
|
myList = myList.Conj(rapid.Int().Draw(t, "el"))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return myList.String()
|
||||||
|
}),
|
||||||
|
rapid.Just(list.Empty().String()),
|
||||||
|
rapid.Just(hash_map.New().String()),
|
||||||
|
rapid.Just(vector.New().String()),
|
||||||
|
rapid.Custom(func(t *rapid.T) string {
|
||||||
|
return fmt.Sprint(rapid.Int().Draw(t, "i"))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRead(t *testing.T) {
|
||||||
|
rapid.Check(t, func(t *rapid.T) {
|
||||||
|
input := StringifiedFormGen().Draw(t, "input")
|
||||||
|
_, err := ReadString(input)
|
||||||
|
assert.Nil(t, err, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
+7
-10
@@ -21,7 +21,7 @@ func (this *Vector) Init() *Vector {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
func Conj(this *Vector, data any) *Vector {
|
func (this *Vector) Conj(data any) *Vector {
|
||||||
newVec := New()
|
newVec := New()
|
||||||
for _, el := range this._slice {
|
for _, el := range this._slice {
|
||||||
newVec._slice = append(newVec._slice, el)
|
newVec._slice = append(newVec._slice, el)
|
||||||
@@ -30,12 +30,8 @@ func Conj(this *Vector, data any) *Vector {
|
|||||||
return newVec
|
return newVec
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Vector) Conj(data any) *Vector {
|
func (this *Vector) String() string {
|
||||||
return Conj(this, data)
|
if this.IsEmpty() {
|
||||||
}
|
|
||||||
|
|
||||||
func String(this *Vector) string {
|
|
||||||
if this == nil {
|
|
||||||
return "[]"
|
return "[]"
|
||||||
}
|
}
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
@@ -45,16 +41,17 @@ func String(this *Vector) string {
|
|||||||
sb.WriteRune(' ')
|
sb.WriteRune(' ')
|
||||||
}
|
}
|
||||||
return sb.String()[:sb.Len()-1] + "]"
|
return sb.String()[:sb.Len()-1] + "]"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Vector) String() string {
|
func (this *Vector) IsEmpty() bool {
|
||||||
return String(this)
|
return len(this._slice) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToList(this *Vector) list.IList {
|
func ToList(this *Vector) list.IList {
|
||||||
l := list.Empty()
|
l := list.Empty()
|
||||||
for i := len(this._slice) - 1; i >= 0; i-- {
|
for i := len(this._slice) - 1; i >= 0; i-- {
|
||||||
l = list.Conj(l, this._slice[i])
|
l = l.Conj(this._slice[i])
|
||||||
}
|
}
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user