diff options
| author | Joel Martin <github@martintribe.org> | 2014-10-04 20:06:42 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2014-10-04 20:06:42 -0500 |
| commit | 45e1db6afbb0c63b1cd5d17e0996d7929803f37b (patch) | |
| tree | 57aad492414d013e4d2b5b6eb81bc563244df02f /go/src/reader | |
| parent | 1ac751b20c8c6efe924737b0f88b9de6805bd5b4 (diff) | |
| download | mal-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.go | 110 |
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}) +} |
