init reader for lists

This commit is contained in:
Adam Jeniski 2025-11-04 07:04:49 -10:00
parent f279d7c90a
commit 5200dfde00
4 changed files with 123 additions and 16 deletions

View File

@ -29,6 +29,9 @@ func (this *List) Conj(val any) *List {
}
func Conj(this *List, val any) *List {
if this == nil {
return New(val)
}
new_head := New(val)
new_head.next = this
return new_head
@ -46,6 +49,9 @@ func (this *List) Rest() *List {
}
func First(this *List) any {
if this == nil {
return nil
}
return this.Value
}
@ -53,6 +59,10 @@ func (this *List) First() any {
return First(this)
}
func IsEmpty(this *List) bool {
return this == nil
}
func String(this *List) string {
if this == nil {
return "{}"

View File

@ -1,39 +1,96 @@
package read
import (
"mal-go/list"
"mal-go/hash_map"
"mal-go/symbol"
"mal-go/vector"
"strconv"
"strings"
)
func ReadString(s string) (any, any) {
res, err, _ := readInternal(s, 0, nil, "user")
res, err, _ := readForm(s, 0, "user")
return res, err
}
func isSymbolChar(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-' || c == '/' || c == '\''
func isDigitChar(c byte) bool {
return c >= '0' && c <= '9'
}
func readInternal(s string, pos int, ctx *list.List, ns string) (any, any, int) {
func isSpaceChar(c byte) bool {
return c == ' ' || c == '\n' || c == ','
}
func isSymbolChar(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-' || c == '/' || c == '\'' || c == '+' || c == '-' || c == '*'
}
func readForm(s string, pos int, ns string) (any, any, int) {
if pos >= len(s) {
return ctx, nil, pos
return nil, "unterminated input", pos
}
c := s[pos]
var res, err any
if isSymbolChar(c) {
res, err, pos2 := readSymbol(s, pos, ns)
return res, err, pos2
if isSpaceChar(c) {
res, err, pos = readForm(s, pos+1, ns)
} else if isDigitChar(c) {
res, err, pos = readInt(s, pos, ns)
} else if isSymbolChar(c) {
res, err, pos = readSymbol(s, pos, ns)
} else {
switch c {
case '{':
res, err, pos = readMap(s, pos, ns)
case '[':
res, err, pos = readVector(s, pos, ns)
case '(':
res, err, pos = readList(s, pos, ns)
}
// switch c {
// case '{':
// res, err, _ = readInternal(s, pos+1, list.Conj(ctx, hash_map.New()), ns)
// case '[':
// case '(':
// }
return res, err, 0
}
return res, err, pos
}
func readMap(s string, pos int, ns string) (any, any, int) {
m := hash_map.New()
return m, "unimplemented", pos
}
func readList(s string, pos int, ns string) (any, any, int) {
pos += 1
forms := vector.New()
for rune(s[pos]) != ')' {
res, _, pos2 := readForm(s, pos, ns)
forms = forms.Conj(res)
pos = pos2
}
return forms.ToList(), nil, pos + 1
}
func readVector(s string, pos int, ns string) (any, any, int) {
pos += 1
forms := vector.New()
for rune(s[pos]) != ']' {
res, _, pos2 := readForm(s, pos, ns)
forms = forms.Conj(res)
pos = pos2
}
return forms, nil, pos + 1
}
func readInt(s string, pos int, ns string) (any, any, int) {
startingPos := pos
c := s[pos]
for isDigitChar(c) {
pos += 1
if pos >= len(s) {
break
}
c = s[pos]
}
res, _ := strconv.Atoi(s[startingPos:pos])
return res, nil, pos
}
func readSymbol(s string, pos int, ns string) (any, any, int) {

View File

@ -2,10 +2,19 @@ package read
import (
"github.com/stretchr/testify/assert"
"mal-go/list"
"mal-go/symbol"
"testing"
)
func TestReadInt(t *testing.T) {
input := "2"
sym, err, end_pos := readInt(input, 0, "user")
assert.Equal(t, 2, sym, "should read ")
assert.Equal(t, nil, err, "should read without error")
assert.Equal(t, len(input), end_pos, "should read the whole string")
}
func TestReadSymbol(t *testing.T) {
input := "foo"
sym, err, end_pos := readSymbol(input, 0, "user")
@ -21,3 +30,20 @@ func TestReadSymbolWithNamespace(t *testing.T) {
assert.Equal(t, err, nil, "should read without error")
assert.Equal(t, end_pos, len(input), "should read the whole string")
}
func TestReadListElem(t *testing.T) {
input := "(1223)"
f, err, pos := readForm(input, 1, "user")
assert.Equal(t, f, 1223, "should num list")
assert.Equal(t, err, nil, "should read without error")
assert.Equal(t, pos, len(input)-1, "should read the whole number")
assert.Equal(t, rune(input[pos]), ')', "should end pointing to ending list char")
}
func TestReadList(t *testing.T) {
input := "(1 2 3)"
l, err, pos := readList(input, 0, "user")
assert.Equal(t, list.New(3).Conj(2).Conj(1), l, "should read list")
assert.Equal(t, nil, err, "should read without error")
assert.Equal(t, len(input), pos, "should read the whole string")
}

View File

@ -1,6 +1,7 @@
package vector
import (
"mal-go/list"
"mal-go/utils"
"strings"
)
@ -49,3 +50,16 @@ func String(this *Vector) string {
func (this *Vector) String() string {
return String(this)
}
func ToList(this *Vector) *list.List {
var l *list.List
l = nil
for i := len(this._slice) - 1; i >= 0; i-- {
l = list.Conj(l, this._slice[i])
}
return l
}
func (this *Vector) ToList() *list.List {
return ToList(this)
}