From 143b17a6c1593dc8936ea041d464b8df878c4232 Mon Sep 17 00:00:00 2001 From: ajet Date: Sat, 1 Nov 2025 15:01:22 -0900 Subject: [PATCH] init commit --- go.mod | 3 ++ hash_map/hash_map.go | 70 ++++++++++++++++++++++++++++++++++++++++++ keyword/keyword.go | 40 ++++++++++++++++++++++++ list/list.go | 72 ++++++++++++++++++++++++++++++++++++++++++++ main.go | 31 +++++++++++++++++++ symbol/symbol.go | 36 ++++++++++++++++++++++ utils/utils.go | 12 ++++++++ vector/vector.go | 51 +++++++++++++++++++++++++++++++ 8 files changed, 315 insertions(+) create mode 100644 go.mod create mode 100644 hash_map/hash_map.go create mode 100644 keyword/keyword.go create mode 100644 list/list.go create mode 100644 main.go create mode 100644 symbol/symbol.go create mode 100644 utils/utils.go create mode 100644 vector/vector.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f311a25 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module mal-go + +go 1.24.2 diff --git a/hash_map/hash_map.go b/hash_map/hash_map.go new file mode 100644 index 0000000..487020a --- /dev/null +++ b/hash_map/hash_map.go @@ -0,0 +1,70 @@ +package hash_map + +import ( + "mal-go/utils" + "maps" + "strings" +) + +// TODO: replace COW with proper HashArrayMappedTrie impelmentation of PersistentHashMap + +type HashMap struct { + _map map[any]any +} + +func New() *HashMap { + return new(HashMap).Init() +} + +func Init(this *HashMap) *HashMap { + this._map = make(map[any]any) + return this +} + +func (this *HashMap) Init() *HashMap { + return Init(this) +} + +func Clone(this *HashMap) *HashMap { + if this == nil { + return nil + } + + newMap := New().Init() + maps.Copy(newMap._map, this._map) + return newMap +} + +func (this *HashMap) Clone() *HashMap { + return Clone(this) +} + +func Conj(this *HashMap, key any, val any) *HashMap { + newMap := this.Clone() + newMap._map[key] = val + return newMap +} + +func (this *HashMap) Conj(key any, val any) *HashMap { + return Conj(this, key, val) +} + +func String(this *HashMap) string { + if this == nil { + return "{}" + } + var sb strings.Builder + sb.WriteRune('{') + for key, val := range this._map { + sb.WriteString(utils.Stringify(key)) + sb.WriteRune(' ') + sb.WriteString(utils.Stringify(val)) + sb.WriteRune(',') + sb.WriteRune(' ') + } + return sb.String()[:sb.Len()-2] + "}" +} + +func (this *HashMap) String() string { + return String(this) +} diff --git a/keyword/keyword.go b/keyword/keyword.go new file mode 100644 index 0000000..64be4ff --- /dev/null +++ b/keyword/keyword.go @@ -0,0 +1,40 @@ +package keyword + +var intern_map map[string]*Keyword + +type Keyword struct { + ns string + keyword string +} + +func Intern(keyword string) *Keyword { + return Intern_ns("", keyword) +} + +func Intern_ns(ns string, keyword string) *Keyword { + combined_str := ns + "/" + keyword + existingkeyword, ok := intern_map[combined_str] + if ok { + return existingkeyword + } else { + newkeyword := new(Keyword) + newkeyword.ns = ns + newkeyword.keyword = keyword + if intern_map == nil { + intern_map = make(map[string]*Keyword) + } + intern_map[combined_str] = newkeyword + return newkeyword + } +} + +func String(this *Keyword) string { + if this.ns == "" { + return ":" + this.keyword + } + return ":" + this.ns + "/" + this.keyword +} + +func (this *Keyword) String() string { + return String(this) +} diff --git a/list/list.go b/list/list.go new file mode 100644 index 0000000..0b7f7a6 --- /dev/null +++ b/list/list.go @@ -0,0 +1,72 @@ +package list + +import ( + "fmt" + "strings" +) + +type List struct { + Value any + next *List +} + +func New(val any) *List { + return new(List).Init(val) +} + +func (this *List) Init(val any) *List { + return Init(this, val) +} + +func Init(this *List, val any) *List { + this.Value = val + this.next = nil + return this +} + +func (this *List) Conj(val any) *List { + return Conj(this, val) +} + +func Conj(this *List, val any) *List { + new_head := New(val) + new_head.next = this + return new_head +} + +func Rest(this *List) *List { + if this == nil { + return nil + } + return this.next +} + +func (this *List) Rest() *List { + return Rest(this) +} + +func First(this *List) any { + return this.Value +} + +func (this *List) First() any { + return First(this) +} + +func String(this *List) string { + if this == nil { + return "{}" + } + var sb strings.Builder + sb.WriteRune('(') + // Iterate and print elements + for e := this; e != nil; e = Rest(e) { + sb.WriteString(fmt.Sprint(e.Value)) + sb.WriteRune(' ') + } + return sb.String()[:sb.Len()-1] + ")" +} + +func (this *List) String() string { + return String(this) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..f69d26d --- /dev/null +++ b/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "fmt" + "mal-go/hash_map" + "mal-go/keyword" + "mal-go/list" + "mal-go/symbol" + "mal-go/vector" +) + +func main() { + myList := list.New(5) + myList = myList.Conj(6) + plus := symbol.Intern("clojure.core", "+") + myList = myList.Conj(plus) + fmt.Println(myList) + + baz := keyword.Intern("baz") + myMap := hash_map.New() + myMap = myMap.Conj("foo", "bar") + myMap = myMap.Conj(baz, 42) + myMap = myMap.Conj("fizz", "buzz") + fmt.Println(myMap) + + myVec := vector.New() + myVec = myVec.Conj(plus) + myVec = myVec.Conj(1) + myVec = myVec.Conj(2) + fmt.Println(myVec) +} diff --git a/symbol/symbol.go b/symbol/symbol.go new file mode 100644 index 0000000..c03f78c --- /dev/null +++ b/symbol/symbol.go @@ -0,0 +1,36 @@ +package symbol + +var intern_map map[string]*Symbol + +type Symbol struct { + ns string + symbol string +} + +func Intern(ns string, symbol string) *Symbol { + combined_str := ns + "/" + symbol + existingSymbol, ok := intern_map[combined_str] + if ok { + return existingSymbol + } else { + newSymbol := new(Symbol) + newSymbol.ns = ns + newSymbol.symbol = symbol + if intern_map == nil { + intern_map = make(map[string]*Symbol) + } + intern_map[combined_str] = newSymbol + return newSymbol + } +} + +func String(this *Symbol) string { + if this.ns == "" { + return this.symbol + } + return this.ns + "/" + this.symbol +} + +func (this *Symbol) String() string { + return String(this) +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..9acae5d --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,12 @@ +package utils + +import "fmt" + +func Stringify(value any) string { + switch value.(type) { + case string: + return fmt.Sprintf("\"%s\"", value) + default: + return fmt.Sprint(value) + } +} diff --git a/vector/vector.go b/vector/vector.go new file mode 100644 index 0000000..09afa19 --- /dev/null +++ b/vector/vector.go @@ -0,0 +1,51 @@ +package vector + +import ( + "mal-go/utils" + "strings" +) + +// TODO: replace COW with proper HashArrayMappedTrie impelmentation of PersistentVector + +type Vector struct { + _slice []any +} + +func New() *Vector { + return new(Vector).Init() +} + +func (this *Vector) Init() *Vector { + this._slice = make([]any, 0) + return this +} + +func Conj(this *Vector, data any) *Vector { + newVec := New() + for _, el := range this._slice { + newVec._slice = append(newVec._slice, el) + } + newVec._slice = append(newVec._slice, data) + return newVec +} + +func (this *Vector) Conj(data any) *Vector { + return Conj(this, data) +} + +func String(this *Vector) string { + if this == nil { + return "[]" + } + var sb strings.Builder + sb.WriteRune('[') + for _, el := range this._slice { + sb.WriteString(utils.Stringify(el)) + sb.WriteRune(' ') + } + return sb.String()[:sb.Len()-1] + "]" +} + +func (this *Vector) String() string { + return String(this) +}