func (c *Config) WithFallback(fallback *Config) *Config {
	if current, ok := c.root.(Object); ok {
		if fallbackObject, ok := fallback.root.(Object); ok {
			resultConfig := fallbackObject.copy()
			mergeObjects(resultConfig, current)
			return resultConfig.toConfig()
		}
	}
	return c
}
WithFallback method returns a new *Config (or the current config, if the given fallback doesn't get used)
1. merges the values of the current and fallback *Configs, if the root of both of them are of type Object
for the same keys current values overrides the fallback values
2. if any of the *Configs has non-object root then returns the current *Config ignoring the fallback parameter
package hocon
import (
	"fmt"
	"strconv"
	"strings"
	"time"
)
type Type int
const (
	ObjectType Type = iota
	StringType
	ArrayType
	NumberType
	BooleanType
	NullType
	SubstitutionType
	ConcatenationType
)
type Config struct {
	root Value
}
func (c *Config) String() string { return c.root.String() }
func (c *Config) GetRoot() Value {
	return c.root
}
func (c *Config) GetObject(path string) Object {
	value := c.Get(path)
	if value == nil {
		return nil
	}
	return value.(Object)
}
func (c *Config) GetStringMap(path string) map[string]Value {
	return c.GetObject(path)
}
func (c *Config) GetStringMapString(path string) map[string]string {
	value := c.Get(path)
	if value == nil {
		return nil
	}
	object := value.(Object)
	var m = make(map[string]string, len(object))
	for k, v := range object {
		m[k] = v.String()
	}
	return m
}
func (c *Config) GetArray(path string) Array {
	value := c.Get(path)
	if value == nil {
		return nil
	}
	return value.(Array)
}
func (c *Config) GetIntSlice(path string) []int {
	value := c.Get(path)
	if value == nil {
		return nil
	}
	arr := value.(Array)
	slice := make([]int, 0, len(arr))
	for _, v := range arr {
		slice = append(slice, int(v.(Int)))
	}
	return slice
}
func (c *Config) GetStringSlice(path string) []string {
	value := c.Get(path)
	if value == nil {
		return nil
	}
	arr := value.(Array)
	slice := make([]string, 0, len(arr))
	for _, v := range arr {
		slice = append(slice, v.String())
	}
	return slice
}
func (c *Config) GetString(path string) string {
	value := c.Get(path)
	if value == nil {
		return ""
	}
	return value.String()
}
func (c *Config) GetInt(path string) int {
	value := c.Get(path)
	if value == nil {
		return 0
	}
	switch val := value.(type) {
	case Int:
		return int(val)
	case String:
		intValue, err := strconv.Atoi(string(val))
		if err != nil {
			panic(err)
		}
		return intValue
	default:
		panic("cannot parse value: " + val.String() + " to int!")
	}
}
func (c *Config) GetFloat32(path string) float32 {
	value := c.Get(path)
	if value == nil {
		return float32(0.0)
	}
	switch val := value.(type) {
	case Float32:
		return float32(val)
	case Float64:
		return float32(val)
	case String:
		floatValue, err := strconv.ParseFloat(string(val), 32)
		if err != nil {
			panic(err)
		}
		return float32(floatValue)
	default:
		panic("cannot parse value: " + val.String() + " to float32!")
	}
}
func (c *Config) GetFloat64(path string) float64 {
	value := c.Get(path)
	if value == nil {
		return 0.0
	}
	switch val := value.(type) {
	case Float64:
		return float64(val)
	case Float32:
		return float64(val)
	case String:
		floatValue, err := strconv.ParseFloat(string(val), 64)
		if err != nil {
			panic(err)
		}
		return floatValue
	default:
		panic("cannot parse value: " + val.String() + "to float64!")
	}
}
func (c *Config) GetBoolean(path string) bool {
	value := c.Get(path)
	if value == nil {
		return false
	}
	switch val := value.(type) {
	case Boolean:
		return bool(val)
	case String:
		switch val {
		case "true", "yes", "on":
			return true
		case "false", "no", "off":
			return false
		default:
			panic("cannot parse value: " + val + " to boolean!")
		}
	default:
		panic("cannot parse value: " + val.String() + " to boolean!")
	}
}
func (c *Config) GetDuration(path string) time.Duration {
	value := c.Get(path)
	if value == nil {
		return 0
	}
	return time.Duration(value.(Duration))
}
func (c *Config) Get(path string) Value {
	if c.root.Type() != ObjectType {
		return nil
	}
	return c.root.(Object).find(path)
}