aboutsummaryrefslogtreecommitdiff
path: root/go/src/reader
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-10-04 20:06:42 -0500
committerJoel Martin <github@martintribe.org>2014-10-04 20:06:42 -0500
commit45e1db6afbb0c63b1cd5d17e0996d7929803f37b (patch)
tree57aad492414d013e4d2b5b6eb81bc563244df02f /go/src/reader
parent1ac751b20c8c6efe924737b0f88b9de6805bd5b4 (diff)
downloadmal-45e1db6afbb0c63b1cd5d17e0996d7929803f37b.tar.gz
mal-45e1db6afbb0c63b1cd5d17e0996d7929803f37b.zip
go: reading of atoms and lists.
Diffstat (limited to 'go/src/reader')
-rw-r--r--go/src/reader/reader.go110
1 files changed, 110 insertions, 0 deletions
diff --git a/go/src/reader/reader.go b/go/src/reader/reader.go
new file mode 100644
index 0000000..3222d6f
--- /dev/null
+++ b/go/src/reader/reader.go
@@ -0,0 +1,110 @@
+package reader
+
+import (
+ "errors"
+ "regexp"
+ "strconv"
+ //"fmt"
+)
+
+import (
+ "types"
+)
+
+type Reader interface {
+ next() *string
+ peek() *string
+}
+
+type TokenReader struct {
+ tokens []string
+ position int
+}
+
+func (tr *TokenReader) next() *string {
+ if tr.position >= len(tr.tokens) { return nil }
+ token := tr.tokens[tr.position]
+ tr.position = tr.position + 1
+ return &token
+}
+
+func (tr *TokenReader) peek() *string {
+ if tr.position > len(tr.tokens) { return nil }
+ return &tr.tokens[tr.position]
+}
+
+
+
+func tokenize (str string) []string {
+ results := make([]string, 0, 1)
+ re := regexp.MustCompile(`[\s,]*(~@|[\[\]{}()'~^@]|"(?:\\.|[^\\"])*"|;.*|[^\s\[\]{}('",;)]*)`)
+ for _, group := range re.FindAllStringSubmatch(str, -1) {
+ if group[1] == "" { continue }
+ results = append(results, group[1])
+ }
+ return results
+}
+
+func read_atom(rdr Reader) (types.MalType, error) {
+ token := rdr.next()
+ if token == nil { return nil, errors.New("read_atom underflow") }
+ if match, _ := regexp.MatchString(`^-?[0-9]+$`, *token); match {
+ var i int
+ var e error
+ if i, e = strconv.Atoi(*token); e != nil {
+ return nil, errors.New("number parse error")
+ }
+ return i, nil
+ } else if (*token)[0] == '"' {
+ // TODO: unquote newline and quotes
+ return (*token)[1:len(*token)-1], nil
+ } else if *token == "nil" {
+ return nil, nil
+ } else if *token == "true" {
+ return true, nil
+ } else if *token == "false" {
+ return false, nil
+ } else {
+ return types.Symbol{*token}, nil
+ }
+ return token, nil
+}
+
+func read_list(rdr Reader) (types.MalType, error) {
+ token := rdr.next()
+ if token == nil { return nil, errors.New("read_list underflow") }
+
+ ast_list := []types.MalType{}
+ if *token != "(" {
+ return nil, errors.New("expected '('")
+ }
+ token = rdr.peek()
+ for ; token != nil && *token != ")" ; token = rdr.peek() {
+ if token == nil { return nil, errors.New("exepected ')', got EOF") }
+ f, e := read_form(rdr)
+ if e != nil { return nil, e }
+ ast_list = append(ast_list, f)
+ }
+ rdr.next()
+ return types.List{ast_list}, nil
+}
+
+func read_form(rdr Reader) (types.MalType, error) {
+ token := rdr.peek()
+ if token == nil { return nil, errors.New("read_form underflow") }
+ switch (*token) {
+ case ")": return nil, errors.New("unexpected ')'")
+ case "(": return read_list(rdr)
+ default: return read_atom(rdr)
+ }
+ return read_atom(rdr)
+}
+
+func Read_str(str string) (types.MalType, error) {
+ var tokens = tokenize(str);
+ if len(tokens) == 0 {
+ return nil, errors.New("<empty line>")
+ }
+
+ return read_form(&TokenReader{tokens: tokens, position: 0})
+}