|
@@ -0,0 +1,518 @@
|
|
|
+package sexp
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "reflect"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+)
|
|
|
+
|
|
|
+// The main and only AST structure. All fields are self explanatory, however
|
|
|
+// the way they are being formed needs explanation.
|
|
|
+//
|
|
|
+// A list node has empty value and non-nil children pointer, which is a
|
|
|
+// nil-terminated list of children nodes.
|
|
|
+//
|
|
|
+// A scalar node has nil children pointer.
|
|
|
+//
|
|
|
+// Take a look at this example:
|
|
|
+//
|
|
|
+// ((1 2) 3 4)
|
|
|
+//
|
|
|
+// will yield:
|
|
|
+//
|
|
|
+// Node{Children:
|
|
|
+// Node{Children:
|
|
|
+// Node{Value: "1", Next:
|
|
|
+// Node{Value: "2"}}, Next:
|
|
|
+// Node{Value: "3", Next:
|
|
|
+// Node{Value: "4"}}}}
|
|
|
+type Node struct {
|
|
|
+ Location SourceLoc
|
|
|
+ Value string
|
|
|
+ Children *Node
|
|
|
+ Next *Node
|
|
|
+}
|
|
|
+
|
|
|
+// Returns true if the node is a list (has children).
|
|
|
+func (n *Node) IsList() bool {
|
|
|
+ return n.Children != nil
|
|
|
+}
|
|
|
+
|
|
|
+// Return true if the node is a scalar (has no children).
|
|
|
+func (n *Node) IsScalar() bool {
|
|
|
+ return n.Children == nil
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) String() string {
|
|
|
+ return n.Value
|
|
|
+}
|
|
|
+
|
|
|
+// Returns the number of children nodes. Has O(N) complexity.
|
|
|
+func (n *Node) NumChildren() int {
|
|
|
+ i := 0
|
|
|
+ c := n.Children
|
|
|
+ for c != nil {
|
|
|
+ i++
|
|
|
+ c = c.Next
|
|
|
+ }
|
|
|
+ return i
|
|
|
+}
|
|
|
+
|
|
|
+// Returns Nth child node. If node is not a list, it will return an error.
|
|
|
+func (n *Node) Nth(num int) (*Node, error) {
|
|
|
+ if !n.IsList() {
|
|
|
+ return nil, NewUnmarshalError(n, nil, "node is not a list")
|
|
|
+ }
|
|
|
+
|
|
|
+ i := 0
|
|
|
+ for c := n.Children; c != nil; c = c.Next {
|
|
|
+ if i == num {
|
|
|
+ return c, nil
|
|
|
+ }
|
|
|
+ i++
|
|
|
+ }
|
|
|
+
|
|
|
+ num++
|
|
|
+ return nil, NewUnmarshalError(n, nil,
|
|
|
+ "cannot retrieve %d%s child node, %s",
|
|
|
+ num, number_suffix(num),
|
|
|
+ the_list_has_n_children(n.NumChildren()))
|
|
|
+}
|
|
|
+
|
|
|
+// Walk over children nodes, assuming they are key/value pairs. It returns error
|
|
|
+// if the iterable node is not a list or if any of its children is not a
|
|
|
+// key/value pair.
|
|
|
+func (n *Node) IterKeyValues(f func(k, v *Node) error) error {
|
|
|
+ for c := n.Children; c != nil; c = c.Next {
|
|
|
+ if !c.IsList() {
|
|
|
+ return NewUnmarshalError(c, nil,
|
|
|
+ "node is not a list, expected key/value pair")
|
|
|
+ }
|
|
|
+ // don't check for error here, because it's obvious that if the
|
|
|
+ // node is a list (and the definition of the list is `Children
|
|
|
+ // != nil`), it has at least one child
|
|
|
+ k, _ := c.Nth(0)
|
|
|
+ v, err := c.Nth(1)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ err = f(k, v)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+type Unmarshaler interface {
|
|
|
+ UnmarshalSexp(n *Node) error
|
|
|
+}
|
|
|
+
|
|
|
+// Unmarshals all children nodes of the node to pointer values. Applies the
|
|
|
+// same logic as Unmarshal. See description of the (*Node).Unmarshal method for
|
|
|
+// more details.
|
|
|
+func (n *Node) UnmarshalChildren(vals ...interface{}) (err error) {
|
|
|
+ if len(vals) == 0 {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // unmarshal all children of the node
|
|
|
+ i := 0
|
|
|
+ for c := n.Children; c != nil; c = c.Next {
|
|
|
+ if i >= len(vals) {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ if vals[i] == nil {
|
|
|
+ i++
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if err := c.unmarshal(vals[i]); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ i++
|
|
|
+ }
|
|
|
+
|
|
|
+ // did we fullfil all the arguments?
|
|
|
+ if i < len(vals) {
|
|
|
+ if i == 0 {
|
|
|
+ return NewUnmarshalError(n, nil,
|
|
|
+ "node has no children, %d was requested",
|
|
|
+ len(vals))
|
|
|
+ }
|
|
|
+ return NewUnmarshalError(n, nil,
|
|
|
+ "node has %d children, %d was requested",
|
|
|
+ i, len(vals))
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// Unmarshals the node and its siblings to pointer values.
|
|
|
+//
|
|
|
+// The function expects pointers to values with arbitrary types. If one of the
|
|
|
+// arguments is not a pointer it will panic.
|
|
|
+//
|
|
|
+// It supports unmarshaling to the following types:
|
|
|
+// - all number types: int{8,16,32,64}, uint{8,16,32,64}, float{32,64}
|
|
|
+// - bool
|
|
|
+// - string
|
|
|
+// - arrays and slices of all supported types
|
|
|
+// - empty interfaces{}
|
|
|
+// - maps
|
|
|
+// - structs
|
|
|
+// - pointers to any of the supported types (only one level of indirection)
|
|
|
+// - any type which implements Unmarshaler
|
|
|
+//
|
|
|
+// Here's some details on unmarshaling semantics:
|
|
|
+// (u)ints: unmarshaled using strconv.ParseInt/strconv.ParseUint with base 10
|
|
|
+// only
|
|
|
+// floats: unmarshaled using strconv.ParseFloat
|
|
|
+// bool: works strictly on two values "true" or "false"
|
|
|
+// string: unmarshaled as is (keep in mind that lexer supports escape sequences)
|
|
|
+// arrays: uses up to len(array) elements, if there is a smaller amount of
|
|
|
+// elements, the rest is zeroed
|
|
|
+// slices: uses all elements appending them to the slice, however if the slice
|
|
|
+// was bigger than the amount of elements, it will reslice it to the
|
|
|
+// appropriate length
|
|
|
+// iface: only empty interfaces are supported, it will unmarshal AST to
|
|
|
+// a []interface{} or a string
|
|
|
+// map: when unmarshaling to the map, assumes this AST form:
|
|
|
+// `((key value) (key value) (key value))`, doesn't clear the map
|
|
|
+// before appending all the key value pairs
|
|
|
+// struct: uses the same AST form as the map, where `key` means `field`,
|
|
|
+// supports `sexp` tags (see description below), will try to match
|
|
|
+// name specified in the tag, the field name and the field name
|
|
|
+// ignoring the case in that order
|
|
|
+//
|
|
|
+// Struct tags have the form: "name,opt,opt". Special tag "-" means "skip me".
|
|
|
+// Supported options:
|
|
|
+// siblings: will use sibling nodes instead of children for unmarshaling
|
|
|
+// to an array or a slice.
|
|
|
+//
|
|
|
+// Important note: If the type implements Unmarshaler interface, it will use it
|
|
|
+// instead of applying default unmarshaling strategies described above.
|
|
|
+func (n *Node) Unmarshal(vals ...interface{}) (err error) {
|
|
|
+ if len(vals) == 0 {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // unmarshal the node itself
|
|
|
+ if vals[0] != nil {
|
|
|
+ if err := n.unmarshal(vals[0]); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // unmarshal node's siblings
|
|
|
+ i := 1
|
|
|
+ for s := n.Next; s != nil; s = s.Next {
|
|
|
+ if i >= len(vals) {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ if vals[i] == nil {
|
|
|
+ i++
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if err := s.unmarshal(vals[i]); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ i++
|
|
|
+ }
|
|
|
+
|
|
|
+ // did we fullfil all the arguments?
|
|
|
+ if i < len(vals) {
|
|
|
+ if i == 1 {
|
|
|
+ return NewUnmarshalError(n, nil,
|
|
|
+ "node has no siblings, %d was requested",
|
|
|
+ len(vals)-1)
|
|
|
+ }
|
|
|
+ return NewUnmarshalError(n, nil,
|
|
|
+ "node has %d siblings, %d was requested",
|
|
|
+ i-1, len(vals)-1)
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+type UnmarshalError struct {
|
|
|
+ Type reflect.Type
|
|
|
+ Node *Node
|
|
|
+ message string
|
|
|
+}
|
|
|
+
|
|
|
+func NewUnmarshalError(n *Node, t reflect.Type, format string, args ...interface{}) *UnmarshalError {
|
|
|
+ if len(args) == 0 {
|
|
|
+ // simple hack to make it a bit faster in the case when no args
|
|
|
+ // were provided
|
|
|
+ return &UnmarshalError{
|
|
|
+ Type: t,
|
|
|
+ Node: n,
|
|
|
+ message: format,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return &UnmarshalError{
|
|
|
+ Type: t,
|
|
|
+ Node: n,
|
|
|
+ message: fmt.Sprintf(format, args...),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (e *UnmarshalError) Error() string {
|
|
|
+ args := []interface{}{e.message}
|
|
|
+ format := "%s"
|
|
|
+ if e.Node != nil {
|
|
|
+ if e.Node.IsList() {
|
|
|
+ format += " (list value)"
|
|
|
+ } else {
|
|
|
+ format += " (value: %q)"
|
|
|
+ args = append(args, e.Node.Value)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if e.Type != nil {
|
|
|
+ format += " (type: %s)"
|
|
|
+ args = append(args, e.Type)
|
|
|
+ }
|
|
|
+
|
|
|
+ return fmt.Sprintf(format, args...)
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) unmarshal_error(t reflect.Type, format string, args ...interface{}) {
|
|
|
+ panic(NewUnmarshalError(n, t, fmt.Sprintf(format, args...)))
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) unmarshal_unmarshaler(v reflect.Value) bool {
|
|
|
+ u, ok := v.Interface().(Unmarshaler)
|
|
|
+ if !ok {
|
|
|
+ // T doesn't work, try *T as well
|
|
|
+ if v.Kind() != reflect.Ptr && v.CanAddr() {
|
|
|
+ u, ok = v.Addr().Interface().(Unmarshaler)
|
|
|
+ if ok {
|
|
|
+ v = v.Addr()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ok && (v.Kind() != reflect.Ptr || !v.IsNil()) {
|
|
|
+ err := u.UnmarshalSexp(n)
|
|
|
+ if err != nil {
|
|
|
+ if ue, ok := err.(*UnmarshalError); ok {
|
|
|
+ panic(ue)
|
|
|
+ }
|
|
|
+ n.unmarshal_error(v.Type(), err.Error())
|
|
|
+ }
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) ensure_scalar(t reflect.Type) {
|
|
|
+ if n.IsScalar() {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ n.unmarshal_error(t, "scalar value required")
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) ensure_list(t reflect.Type) {
|
|
|
+ if n.IsList() {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ n.unmarshal_error(t, "list value required")
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) unmarshal_value(v reflect.Value, use_siblings bool) {
|
|
|
+ t := v.Type()
|
|
|
+ // we support one level of indirection at the moment
|
|
|
+ if v.Kind() == reflect.Ptr {
|
|
|
+ // if the pointer is nil, allocate a new element of the type it
|
|
|
+ // points to
|
|
|
+ if v.IsNil() {
|
|
|
+ v.Set(reflect.New(t.Elem()))
|
|
|
+ }
|
|
|
+ v = v.Elem()
|
|
|
+ }
|
|
|
+
|
|
|
+ // try Unmarshaler interface
|
|
|
+ if n.unmarshal_unmarshaler(v) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // fallback to default unmarshaling scheme
|
|
|
+ switch v.Kind() {
|
|
|
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
|
+ // TODO: more string -> int conversion options (hex, binary, octal, etc.)
|
|
|
+ n.ensure_scalar(t)
|
|
|
+ num, err := strconv.ParseInt(n.Value, 10, 64)
|
|
|
+ if err != nil {
|
|
|
+ n.unmarshal_error(t, err.Error())
|
|
|
+ }
|
|
|
+ if v.OverflowInt(num) {
|
|
|
+ n.unmarshal_error(t, "integer overflow")
|
|
|
+ }
|
|
|
+ v.SetInt(num)
|
|
|
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
|
+ // TODO: more string -> int conversion options (hex, binary, octal, etc.)
|
|
|
+ n.ensure_scalar(t)
|
|
|
+ num, err := strconv.ParseUint(n.Value, 10, 64)
|
|
|
+ if err != nil {
|
|
|
+ n.unmarshal_error(t, err.Error())
|
|
|
+ }
|
|
|
+ if v.OverflowUint(num) {
|
|
|
+ n.unmarshal_error(t, "integer overflow")
|
|
|
+ }
|
|
|
+ v.SetUint(num)
|
|
|
+ case reflect.Float32, reflect.Float64:
|
|
|
+ n.ensure_scalar(t)
|
|
|
+ num, err := strconv.ParseFloat(n.Value, 64)
|
|
|
+ if err != nil {
|
|
|
+ n.unmarshal_error(t, err.Error())
|
|
|
+ }
|
|
|
+ v.SetFloat(num)
|
|
|
+ case reflect.Bool:
|
|
|
+ n.ensure_scalar(t)
|
|
|
+ switch n.Value {
|
|
|
+ case "true":
|
|
|
+ v.SetBool(true)
|
|
|
+ case "false":
|
|
|
+ v.SetBool(false)
|
|
|
+ default:
|
|
|
+ n.unmarshal_error(t, "undefined boolean value, use true|false")
|
|
|
+ }
|
|
|
+ case reflect.String:
|
|
|
+ n.ensure_scalar(t)
|
|
|
+ v.SetString(n.Value)
|
|
|
+ case reflect.Array, reflect.Slice:
|
|
|
+ if !use_siblings {
|
|
|
+ n.ensure_list(t)
|
|
|
+ }
|
|
|
+ i := 0
|
|
|
+ c := n.Children
|
|
|
+ if use_siblings {
|
|
|
+ c = n
|
|
|
+ }
|
|
|
+ for ; c != nil; c = c.Next {
|
|
|
+ if i >= v.Len() {
|
|
|
+ if v.Kind() == reflect.Array {
|
|
|
+ break
|
|
|
+ } else {
|
|
|
+ v.Set(reflect.Append(v, reflect.Zero(t.Elem())))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ c.unmarshal_value(v.Index(i), false)
|
|
|
+ i++
|
|
|
+ }
|
|
|
+
|
|
|
+ if i < v.Len() {
|
|
|
+ if v.Kind() == reflect.Array {
|
|
|
+ z := reflect.Zero(t.Elem())
|
|
|
+ for n := v.Len(); i < n; i++ {
|
|
|
+ v.Index(i).Set(z)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ v.SetLen(i)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ case reflect.Interface:
|
|
|
+ if v.NumMethod() != 0 {
|
|
|
+ n.unmarshal_error(t, "unsupported type")
|
|
|
+ }
|
|
|
+
|
|
|
+ v.Set(reflect.ValueOf(n.unmarshal_as_interface()))
|
|
|
+ case reflect.Map:
|
|
|
+ n.ensure_list(t)
|
|
|
+ if v.IsNil() {
|
|
|
+ v.Set(reflect.MakeMap(t))
|
|
|
+ }
|
|
|
+
|
|
|
+ keyv := reflect.New(t.Key()).Elem()
|
|
|
+ valv := reflect.New(t.Elem()).Elem()
|
|
|
+ err := n.IterKeyValues(func(key, val *Node) error {
|
|
|
+ key.unmarshal_value(keyv, false)
|
|
|
+ val.unmarshal_value(valv, false)
|
|
|
+ v.SetMapIndex(keyv, valv)
|
|
|
+ return nil
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ n.unmarshal_error(t, "%s", err)
|
|
|
+ }
|
|
|
+ case reflect.Struct:
|
|
|
+ err := n.IterKeyValues(func(key, val *Node) error {
|
|
|
+ var f reflect.StructField
|
|
|
+ var ok bool
|
|
|
+ var opts tag_options
|
|
|
+ for i, n := 0, t.NumField(); i < n; i++ {
|
|
|
+ var tagname string
|
|
|
+ f = t.Field(i)
|
|
|
+ tag := f.Tag.Get("sexp")
|
|
|
+ if tag == "-" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if f.Anonymous {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ tagname, opts = parse_tag(tag)
|
|
|
+
|
|
|
+ ok = tagname == key.Value
|
|
|
+ if ok {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ ok = f.Name == key.Value
|
|
|
+ if ok {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ ok = strings.EqualFold(f.Name, key.Value)
|
|
|
+ if ok {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ok {
|
|
|
+ if f.PkgPath != "" {
|
|
|
+ n.unmarshal_error(t, "writing to an unexported field")
|
|
|
+ } else {
|
|
|
+ v := v.FieldByIndex(f.Index)
|
|
|
+ val.unmarshal_value(v, opts.contains("siblings"))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ n.unmarshal_error(t, "%s", err)
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ n.unmarshal_error(t, "unsupported type")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) unmarshal_as_interface() interface{} {
|
|
|
+ // interface parsing for sexp isn't really useful, the outcome is
|
|
|
+ // []interface{} or string
|
|
|
+ if n.IsList() {
|
|
|
+ var s []interface{}
|
|
|
+ for c := n.Children; c != nil; c = c.Next {
|
|
|
+ s = append(s, c.unmarshal_as_interface())
|
|
|
+ }
|
|
|
+ return s
|
|
|
+ }
|
|
|
+ return n.Value
|
|
|
+}
|
|
|
+
|
|
|
+func (n *Node) unmarshal(v interface{}) (err error) {
|
|
|
+ defer func() {
|
|
|
+ if e := recover(); e != nil {
|
|
|
+ if _, ok := e.(*UnmarshalError); ok {
|
|
|
+ err = e.(error)
|
|
|
+ } else {
|
|
|
+ panic(e)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }()
|
|
|
+
|
|
|
+ pv := reflect.ValueOf(v)
|
|
|
+ if pv.Kind() != reflect.Ptr || pv.IsNil() {
|
|
|
+ panic("Node.Unmarshal expects a non-nil pointer argument")
|
|
|
+ }
|
|
|
+ n.unmarshal_value(pv.Elem(), false)
|
|
|
+ return nil
|
|
|
+}
|