package sexp
import (
"errors"
"strconv"
"strings"
"unicode"
)
type Sexp struct {
I interface{}
}
type QString string
type List []Sexp
func Parse(s string) (Sexp, error) {
s1, rem := ps2(s, -1)
if err, isErr := s1.I.(error); isErr {
return Sexp{}, err
}
if rem > "" {
return s1, errors.New("Left over text: " + rem)
}
return s1, nil
}
func ps2(s0 string, n int) (x Sexp, rem string) {
tok, s1 := gettok(s0)
switch t := tok.(type) {
case error:
return Sexp{tok}, s1
case nil: if n < 0 {
return Sexp{errors.New("blank input string")}, s0
} else {
return Sexp{errors.New("unmatched (")}, ""
}
case byte:
switch {
case t == '(':
x, s1 = ps2(s1, 0) if _, isErr := x.I.(error); isErr {
return x, s0
}
case n < 0:
return Sexp{errors.New("unmatched )")}, ""
default:
return Sexp{make(List, n)}, s1
}
default:
x = Sexp{tok} }
if n < 0 {
return x, s1
}
l, s1 := ps2(s1, n+1)
if _, isErr := l.I.(error); !isErr {
l.I.(List)[n] = x
}
return l, s1
}
func gettok(s string) (tok interface{}, rem string) {
s = strings.TrimSpace(s)
if s == "" {
return nil, ""
}
switch s[0] {
case '(', ')':
return s[0], s[1:]
case '"':
if i := strings.Index(s[1:], `"`); i >= 0 {
return QString(s[1 : i+1]), s[i+2:]
}
return errors.New(`unmatched "`), s
}
i := 1
for i < len(s) && s[i] != '(' && s[i] != ')' && s[i] != '"' &&
!unicode.IsSpace(rune(s[i])) {
i++
}
if j, err := strconv.Atoi(s[:i]); err == nil {
return j, s[i:]
}
if f, err := strconv.ParseFloat(s[:i], 64); err == nil {
return f, s[i:]
}
return s[:i], s[i:]
}