package read import ( "mal-go/hash_map" "mal-go/symbol" "mal-go/vector" "strconv" "strings" ) func ReadString(s string) (any, any) { res, err, _ := readForm(s, 0, "user") return res, err } func isDigitChar(c byte) bool { return c >= '0' && c <= '9' } 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 nil, "unterminated input", pos } c := s[pos] var res, err any 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) } } 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) { startingPos := pos c := s[pos] for isSymbolChar(c) { pos += 1 if pos >= len(s) { break } c = s[pos] } sym := s[startingPos:pos] symNs := ns if strings.Contains(sym, "/") { splitIdx := strings.Index(s, "/") symNs = s[startingPos:splitIdx] sym = s[splitIdx+1 : pos] } return symbol.Intern(symNs, sym), nil, pos }