func (ss statements) Less(a, b int) bool {
	diffIndex := -1
	for i := range ss[a] {
		if len(ss[b]) < i+1 {
			return false
		}
		if ss[a][i] == ss[b][i] {
			continue
		}
		diffIndex = i
		break
	}
	if diffIndex == -1 {
		return true
	}
	ta := ss[a][diffIndex]
	tb := ss[b][diffIndex]
	if ta.typ == typEquals {
		return true
	}
	if tb.typ == typEquals {
		return false
	}
	if ta.typ == typNumericKey && tb.typ == typNumericKey {
		ia, _ := strconv.Atoi(ta.text)
		ib, _ := strconv.Atoi(tb.text)
		return ia < ib
	}
	if ta.typ != typNumber || tb.typ != typNumber {
		return ta.text < tb.text
	}
	na, _ := json.Number(ta.text).Float64()
	nb, _ := json.Number(tb.text).Float64()
	return na < nb
}
Less compares two statements for sort.Sort
Implements a natural sort to keep array indexes in order
package main
import (
	"encoding/json"
	"fmt"
	"io"
	"reflect"
	"strconv"
	"strings"
	"github.com/pkg/errors"
)
type statement []token
func (s statement) String() string {
	out := make([]string, 0, len(s)+2)
	for _, t := range s {
		out = append(out, t.format())
	}
	return strings.Join(out, "")
}
func (s statement) colorString() string {
	out := make([]string, 0, len(s)+2)
	for _, t := range s {
		out = append(out, t.formatColor())
	}
	return strings.Join(out, "")
}
type statementconv func(s statement) string
func statementToString(s statement) string {
	return s.String()
}
func statementToColorString(s statement) string {
	return s.colorString()
}
func (s statement) withBare(k string) statement {
	new := make(statement, len(s), len(s)+2)
	copy(new, s)
	return append(
		new,
		token{".", typDot},
		token{k, typBare},
	)
}
func (s statement) jsonify() (statement, error) {
	j := make(statement, 0, len(s)+1)
	if len(s) < 4 || s[0].typ != typBare || s[len(s)-3].typ != typEquals ||
		s[len(s)-1].typ != typSemi {
		return nil, errors.New("non-assignment statement")
	}
	j = append(j, token{"[", typLBrace})
	j = append(j, token{"[", typLBrace})
	for _, t := range s[1 : len(s)-3] {
		switch t.typ {
		case typNumericKey, typQuotedKey:
			j = append(j, t)
			j = append(j, token{",", typComma})
		case typBare:
			j = append(j, token{quoteString(t.text), typQuotedKey})
			j = append(j, token{",", typComma})
		}
	}
	if j[len(j)-1].typ == typComma {
		j = j[:len(j)-1]
	}
	j = append(j, token{"]", typLBrace})
	j = append(j, token{",", typComma})
	j = append(j, s[len(s)-2])
	j = append(j, token{"]", typLBrace})
	return j, nil
}
func (s statement) withQuotedKey(k string) statement {
	new := make(statement, len(s), len(s)+3)
	copy(new, s)
	return append(
		new,
		token{"[", typLBrace},
		token{quoteString(k), typQuotedKey},
		token{"]", typRBrace},
	)
}
func (s statement) withNumericKey(k int) statement {
	new := make(statement, len(s), len(s)+3)
	copy(new, s)
	return append(
		new,
		token{"[", typLBrace},
		token{strconv.Itoa(k), typNumericKey},
		token{"]", typRBrace},
	)
}
type statements []statement
func (ss *statements) addWithValue(path statement, value token) {
	s := make(statement, len(path), len(path)+3)
	copy(s, path)
	s = append(s, token{"=", typEquals}, value, token{";", typSemi})
	*ss = append(*ss, s)
}
func (ss *statements) add(s statement) {
	*ss = append(*ss, s)
}
func (ss statements) Len() int {
	return len(ss)
}
func (ss statements) Swap(i, j int) {
	ss[i], ss[j] = ss[j], ss[i]
}
type statementmaker func(str string) (statement, error)
func statementFromString(str string) statement {
	l := newLexer(str)
	s := l.lex()
	return s
}
func statementFromStringMaker(str string) (statement, error) {
	return statementFromString(str), nil
}
func statementFromJSONSpec(str string) (statement, error) {
	var a []interface{}
	var ok bool
	var v interface{}
	var s statement
	var t tokenTyp
	var nstr string
	var nbuf []byte
	err := json.Unmarshal([]byte(str), &a)
	if err != nil {
		return nil, err
	}
	if len(a) != 2 {
		goto out
	}
	v = a[1]
	a, ok = a[0].([]interface{})
	if !ok {
		goto out
	}
	s = make(statement, 0, 3*len(a)+4)
	s = append(s, token{"json", typBare})
	for _, e := range a {
		s = append(s, token{"[", typLBrace})
		switch e := e.(type) {
		case string:
			s = append(s, token{quoteString(e), typQuotedKey})
		case float64:
			nbuf, err = json.Marshal(e)
			if err != nil {
				return nil, errors.Wrap(err, "JSON internal error")
			}
			nstr = fmt.Sprintf("%s", nbuf)
			s = append(s, token{nstr, typNumericKey})
		default:
			ok = false
			goto out
		}
		s = append(s, token{"]", typRBrace})
	}
	s = append(s, token{"=", typEquals})
	switch v := v.(type) {
	case bool:
		if v {
			t = typTrue
		} else {
			t = typFalse
		}
	case float64:
		t = typNumber
	case string:
		t = typString
	case []interface{}:
		ok = (len(v) == 0)
		if !ok {
			goto out
		}
		t = typEmptyArray
	case map[string]interface{}:
		ok = (len(v) == 0)
		if !ok {
			goto out
		}
		t = typEmptyObject
	default:
		ok = (v == nil)
		if !ok {
			goto out
		}
		t = typNull
	}
	nbuf, err = json.Marshal(v)
	if err != nil {
		return nil, errors.Wrap(err, "JSON internal error")
	}
	nstr = fmt.Sprintf("%s", nbuf)
	s = append(s, token{nstr, t})
	s = append(s, token{";", typSemi})
out:
	if !ok {
		return nil, errors.New("invalid JSON layout")
	}
	return s, nil
}
func (ss statements) toInterface() (interface{}, error) {
	var parsed []interface{}
	for _, s := range ss {
		u, err := ungronTokens(s)
		switch err.(type) {
		case nil:
		case errRecoverable:
			continue
		default:
			return nil, errors.Wrapf(err, "ungron failed for `%s`", s)
		}
		parsed = append(parsed, u)
	}
	if len(parsed) == 0 {
		return nil, fmt.Errorf("no statements were parsed")
	}
	merged := parsed[0]
	for _, p := range parsed[1:] {
		m, err := recursiveMerge(merged, p)
		if err != nil {
			return nil, errors.Wrap(err, "failed to merge statements")
		}
		merged = m
	}
	return merged, nil
}