package parser import ( "errors" "strconv" "strings" "github.com/hellerve/argos/ast" ) func withoutEmpty(input []string) []string { var r []string for _, str := range input { if str != "" { r = append(r, str) } } return r } func tokenize(input string) []string { return withoutEmpty(strings.Split(strings.Replace(strings.Replace(input, "(", " ( ", -1), ")", " ) ", -1), " ")) } func parseValue(input []string) (*ast.AST, error, []string) { var res ast.AST f, err := strconv.ParseFloat(input[0], 64) if err == nil { res = ast.AST{ast.Num, f} return &res, nil, input[1:] } if input[0][0] == '"' { var agg []string for { if len(input) == 0 { return nil, errors.New("Unmatched \""), input } token := input[0] input = input[1:] agg = append(agg, token) if token[len(token)-1] == '"' { break } } res = ast.AST{ast.String, strings.Join(agg, " ")} return &res, nil, input } res = ast.AST{ast.Symbol, input[0]} return &res, nil, input[1:] } func parseToken(input []string) (*ast.AST, error, []string) { if len(input) == 0 { return nil, errors.New("Unmatched '('"), input } if input[0][0] == '\'' { if (input[0] == "'") { input = input[1:] } else { input = append([]string{input[0][1:]}, input[1:]...) } tmp, err, input := parseToken(input) if err != nil { return nil, err, input } res := ast.AST{ast.List, []ast.AST{ast.AST{ast.Symbol, "quote"}, *tmp}} return &res, nil, input } switch input[0] { case "(": { var l []ast.AST input = input[1:] for input[0] != ")" { elem, err, newInput := parseToken(input) if err != nil { return nil, err, input } l = append(l, *elem) input = newInput } res := ast.AST{ast.List, l} return &res, nil, input[1:] } case ")": { return nil, errors.New("Unmatched ')'"), input } } return parseValue(input) } func Parse(input string) (*ast.AST, error, []string) { return parseToken(tokenize(input)) }